[devtools-extension] fix panel CSS, dedupe rules, add metadata export#1721
Conversation
- Fix unstyled panel: the StyleX unplugin merges/renames the CSS asset (e.g. style.css -> style2.css) and only rewrites JS references, leaving the static panel.html <link> pointing at a missing file. The copy-static plugin now rewrites the stylesheet href to the actual emitted CSS name. - Dedupe declarations in collectStylexDebugData so an identical atomic rule injected into multiple stylesheets (HMR/StrictMode/multi-bundle) no longer renders as duplicate rows. - Parameterize collectStylexDebugData with an optional `target` (element or CSS selector), defaulting to `$0`, so the scraper can run outside DevTools. - Add a "Copy metadata" button + exportMetadata serializer that copies the selected element's StyleX context (resolved declarations, sources, overrides) as markdown.
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
There was a problem hiding this comment.
Pull request overview
This PR updates the StyleX DevTools extension to ensure the panel reliably loads its emitted CSS, reduces duplicate declaration rows during inspection, and adds a new “Copy metadata” capability to export StyleX inspection details as markdown.
Changes:
- Rewrite copied HTML
<link href>to point at the actual emitted CSS filename during the Rollup build. - Extend
collectStylexDebugDatawith an optionaltargetselector/element and dedupe identical declaration tuples per class. - Add metadata export utilities and a new panel header button to copy markdown to the clipboard.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/@stylexjs/devtools-extension/src/utils/exportMetadata.js | Adds a markdown serializer for StyleX debug data (“Copy metadata” output). |
| packages/@stylexjs/devtools-extension/src/panel/components/CopyMetadataButton.js | Adds UI + clipboard-copy logic for exporting metadata from the panel. |
| packages/@stylexjs/devtools-extension/src/panel/App.jsx | Wires the new copy button into the panel header. |
| packages/@stylexjs/devtools-extension/src/inspected/collectStylexDebugData.js | Adds target support and dedupes duplicate declaration rows per class. |
| packages/@stylexjs/devtools-extension/rollup.config.mjs | Updates static HTML copying to rewrite CSS links to the emitted CSS asset name. |
| const [label, setLabel] = useState('Copy metadata'); | ||
| const timeoutRef = useRef<?TimeoutID>(null); | ||
|
|
| const dedupKey = [ | ||
| decl.property, | ||
| decl.value, | ||
| decl.important ? '!' : '', | ||
| decl.condition ?? '', | ||
| decl.pseudoElement ?? '', | ||
| ].join('::'); |
| export function collectStylexDebugData( | ||
| target?: HTMLElement | string | null, | ||
| ): StylexDebugData { |
| // Tracks which (property/value/important/condition/pseudoElement) tuples we have | ||
| // already recorded per class, so an identical atomic rule appearing in multiple | ||
| // stylesheets (common in dev: HMR re-injection, StrictMode, multiple bundles) | ||
| // does not render as duplicate rows. | ||
| const seenDeclKeys = new Map<string, Set<string>>(); |
workflow: benchmarks/sizeComparison of minified (terser) and compressed (brotli) size results, measured in bytes. Smaller is better.
|
workflow: benchmarks/perfComparison of performance test results, measured in operations per second. Larger is better.
|
…nup, tests - Use JSON.stringify for the per-class dedup key so values containing the delimiter (e.g. pseudoElement '::before') can't collide. - Clear the pending label-reset timeout on CopyMetadataButton unmount. - Add tests: cross-stylesheet dedup, target by selector, target by element, and invalid-selector handling.
Summary
A few fixes and one new feature for the StyleX DevTools extension.
Fix: panel renders with no styles (when built from current
main)panel.htmlhas a hardcoded<link href="./assets/style.css">. At build time two things feed that asset:cssBundlerollup plugin emits the importedindex.css(reset) asassets/style.css..cssasset.This used to work: when the extension was added (#1401), the unplugin merged its CSS in place, keeping the filename
style.css, so the<link>resolved. The currently-published Store build predates the change below and is styled correctly.Since #1444 ("[unplugin] Bun support and refactor"), the unplugin emits a new asset for the merged CSS instead of editing in place. Because
assetFileNameshas no content hash andstyle.cssis already taken by step 1, Rollup deduplicates the new file tostyle2.cssand deletes the original. The unplugin rewrites references inside the JS bundle, butpanel.htmlis a static file copied verbatim (not part of the bundle), so its link still pointed atstyle.css→ 404 → the panel loaded completely unstyled.The
copy-staticplugin now reads the actual emitted CSS filename from the bundle and rewrites the stylesheethrefin copied HTML to match (name-agnostic, so it works forstyle.css,style2.css, or hashed names).Fix: duplicate declaration rows
collectStylexDebugDatawalked every stylesheet and pushed a row per matching rule occurrence. When an identical atomic rule was injected into multiple stylesheets (common in dev: HMR re-injection, React StrictMode, multiple bundles) the same declaration rendered 2–3×. It now dedupes on(property, value, important, condition, pseudoElement)per class.Scraper can run outside DevTools (additive groundwork)
collectStylexDebugDatanow accepts an optionaltarget— an element or a CSS selector — defaulting to the DevTools-selected element ($0). Passing nothing preserves the existing panel behavior, so this is fully backward compatible. This keeps a single canonical scraper that also works when run outside DevTools (e.g. evaluated by browser automation, where there is no$0).Feature: "Copy metadata"
New header button +
exportMetadataserializer that copies the selected element's StyleX context (resolved declarations grouped by property/condition,data-style-srcsources, and overrides) as compact markdown.Test plan
Tested on Facebook via loading unpacked plugin
yarn build(clean) emits the CSS andpanel.htmllinks the actual file.yarn jest packages/@stylexjs/devtools-extension --watchman=falsepasses.yarn flow check packages/@stylexjs/devtools-extension→ no errors.