diff --git a/.yarn/patches/@parcel-transformer-js-npm-2.14.0-9521af387e.patch b/.yarn/patches/@parcel-transformer-js-npm-2.14.0-9521af387e.patch new file mode 100644 index 00000000000..2be536cf585 --- /dev/null +++ b/.yarn/patches/@parcel-transformer-js-npm-2.14.0-9521af387e.patch @@ -0,0 +1,16 @@ +diff --git a/lib/JSTransformer.js b/lib/JSTransformer.js +index 1e0991fc8f9c231b70eefd7e177119a06162eeca..e5be5a0901ebc97cbbbb7bc0c774787de36fa2b5 100644 +--- a/lib/JSTransformer.js ++++ b/lib/JSTransformer.js +@@ -446,6 +446,11 @@ var _default = exports.default = new (_plugin().Transformer)({ + specifierType: 'esm' + }); + }, ++ loc: { ++ filePath: asset.filePath, ++ line: loc.line, ++ col: loc.col ++ }, + invalidateOnFileChange(filePath) { + asset.invalidateOnFileChange(filePath); + }, diff --git a/eslint.config.mjs b/eslint.config.mjs index 7a94bf740f3..d43d06d328d 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -497,4 +497,12 @@ export default [{ rules: { "react/react-in-jsx-scope": OFF, }, +}, { + files: ["packages/dev/style-macro-chrome-plugin/**"], + languageOptions: { + globals: { + ...globals.webextensions, + ...globals.browser + } + } }]; \ No newline at end of file diff --git a/package.json b/package.json index 2ca9dad9daf..5560b459421 100644 --- a/package.json +++ b/package.json @@ -221,6 +221,7 @@ "@babel/preset-env": "7.24.4", "@babel/traverse": "7.24.1", "@babel/types": "7.24.0", + "@parcel/config-webextension": "2.14.0", "postcss": "8.4.24", "postcss-custom-properties": "13.2.0", "postcss-import": "15.1.0", @@ -238,7 +239,8 @@ "@types/node@npm:>= 8": "^22", "micromark-extension-mdxjs": "patch:micromark-extension-mdxjs@npm%3A1.0.0#~/.yarn/patches/micromark-extension-mdxjs-npm-1.0.0-d2b6b69e4a.patch", "remark-mdx": "patch:remark-mdx@npm%3A2.0.0-rc.2#~/.yarn/patches/remark-mdx-npm-2.0.0-rc.2-7a71234e1f.patch", - "remark-parse": "patch:remark-parse@npm%3A10.0.1#~/.yarn/patches/remark-parse-npm-10.0.1-e654d7df78.patch" + "remark-parse": "patch:remark-parse@npm%3A10.0.1#~/.yarn/patches/remark-parse-npm-10.0.1-e654d7df78.patch", + "@parcel/transformer-js": "patch:@parcel/transformer-js@npm%3A2.14.0#~/.yarn/patches/@parcel-transformer-js-npm-2.14.0-9521af387e.patch" }, "@parcel/transformer-css": { "cssModules": { diff --git a/packages/@react-spectrum/s2/chromatic/CardView.stories.tsx b/packages/@react-spectrum/s2/chromatic/CardView.stories.tsx index c8e89b63397..218d90a5f8b 100644 --- a/packages/@react-spectrum/s2/chromatic/CardView.stories.tsx +++ b/packages/@react-spectrum/s2/chromatic/CardView.stories.tsx @@ -41,7 +41,7 @@ const cardViewStyles = style({ } }); -export const Empty = (args: CardViewProps, {viewMode}) => { +export const Empty = (args: CardViewProps, {viewMode} = {viewMode: ''}) => { return ( {(renderProps) => (<> - {variant === 'genai' || variant === 'premium' + {variant === 'genai' || variant === 'premium' ? ( , {viewMode}) => { +export const Example = (args: CardViewProps, {viewMode} = {viewMode: ''}) => { let list = useAsyncList({ async load({signal, cursor, items}) { let page = cursor || 1; @@ -157,7 +157,7 @@ Example.args = { selectionMode: 'multiple' }; -export const Empty = (args: CardViewProps, {viewMode}) => { +export const Empty = (args: CardViewProps, {viewMode} = {viewMode: ''}) => { return ( , {viewMode}) => { +export const CollectionCards = (args: CardViewProps, {viewMode} = {viewMode: ''}) => { let list = useAsyncList({ async load({signal, cursor}) { let page = cursor || 1; diff --git a/packages/@react-spectrum/s2/style/__tests__/mergeStyles.test.js b/packages/@react-spectrum/s2/style/__tests__/mergeStyles.test.js index 00376090225..3f2da3a4b99 100644 --- a/packages/@react-spectrum/s2/style/__tests__/mergeStyles.test.js +++ b/packages/@react-spectrum/s2/style/__tests__/mergeStyles.test.js @@ -13,13 +13,17 @@ import {mergeStyles} from '../runtime'; import {style} from '../spectrum-theme'; +function stripMacro(css) { + return css.replaceAll(/-macro\$[0-9a-zA-Z]{6}[ ]?/gi, ''); +} + describe('mergeStyles', () => { it('should merge styles', () => { let a = style({backgroundColor: 'red-1000', color: 'pink-100'}); let b = style({fontSize: 'body-xs', backgroundColor: 'gray-50'}); let expected = style({backgroundColor: 'gray-50', color: 'pink-100', fontSize: 'body-xs'}); let merged = mergeStyles(a, b); - expect(merged).toBe(expected); + expect(stripMacro(merged)).toBe(stripMacro(expected.toString())); }); it('should merge with arbitrary values', () => { @@ -27,6 +31,6 @@ describe('mergeStyles', () => { let b = style({fontSize: '[15px]', backgroundColor: 'gray-50'}); let expected = style({backgroundColor: 'gray-50', color: '[hotpink]', fontSize: '[15px]'}); let merged = mergeStyles(a, b); - expect(merged).toBe(expected); + expect(stripMacro(merged)).toBe(stripMacro(expected.toString())); }); }); diff --git a/packages/@react-spectrum/s2/style/__tests__/style-macro.test.js b/packages/@react-spectrum/s2/style/__tests__/style-macro.test.js index 1fa22b97fdf..9332e16d65a 100644 --- a/packages/@react-spectrum/s2/style/__tests__/style-macro.test.js +++ b/packages/@react-spectrum/s2/style/__tests__/style-macro.test.js @@ -22,7 +22,8 @@ function testStyle(...args) { }, args ); - return {css, js}; + + return {css, js: typeof js === 'function' ? js : js.toString()}; } describe('style-macro', () => { @@ -55,7 +56,7 @@ describe('style-macro', () => { " `); - expect(js).toMatchInlineSnapshot('" Jbs9 Jbpv9"'); + expect(js).toMatchInlineSnapshot('" Jbs9 Jbpv9 -macro$M74Bxe"'); }); it('should support self references', () => { @@ -118,7 +119,7 @@ describe('style-macro', () => { `); expect(js).toMatchInlineSnapshot( - '" _kc9 hc9 mCPFGYc9 lc9 SMBFGYc9 Rv9 ZjUQgKd9 -m_-mc9 -S_-Sv9"' + '" _kc9 hc9 mCPFGYc9 lc9 SMBFGYc9 Rv9 ZjUQgKd9 -m_-mc9 -S_-Sv9 -macro$wNXlmc"' ); }); @@ -136,9 +137,13 @@ describe('style-macro', () => { color: 'green-400' }); - expect(js()).toMatchInlineSnapshot('" gw9 pg9"'); - expect(overrides).toMatchInlineSnapshot('" g8tmWqb9 pHJ3AUd9"'); - expect(js({}, overrides)).toMatchInlineSnapshot('" g8tmWqb9 pg9"'); + expect(js()).toMatchInlineSnapshot('" gw9 pg9 -macro$7ucvu4"'); + expect(overrides).toMatchInlineSnapshot( + '" g8tmWqb9 pHJ3AUd9 -macro$sFRgme"' + ); + expect(js({}, overrides)).toMatchInlineSnapshot( + '" g8tmWqb9 -macro$sFRgme pg9 -macro$nt261r"' + ); }); it('should support allowed overrides for properties that expand into multiple', () => { @@ -153,9 +158,13 @@ describe('style-macro', () => { translateX: 40 }); - expect(js()).toMatchInlineSnapshot('" -_7PloMd-B9 __Ya9"'); - expect(overrides).toMatchInlineSnapshot('" -_7PloMd-D9 __Ya9"'); - expect(js({}, overrides)).toMatchInlineSnapshot('" -_7PloMd-D9 __Ya9"'); + expect(js()).toMatchInlineSnapshot('" -_7PloMd-B9 __Ya9 -macro$hzmozh"'); + expect(overrides).toMatchInlineSnapshot( + '" -_7PloMd-D9 __Ya9 -macro$f5PRU"' + ); + expect(js({}, overrides)).toMatchInlineSnapshot( + '" -_7PloMd-D9 __Ya9 -macro$f5PRU -macro$1vw5vro"' + ); }); it('should support allowed overrides for shorthands', () => { @@ -170,9 +179,11 @@ describe('style-macro', () => { padding: 40 }); - expect(js()).toMatchInlineSnapshot('" Tk9 Qk9 Sk9 Rk9"'); - expect(overrides).toMatchInlineSnapshot('" Tm9 Qm9 Sm9 Rm9"'); - expect(js({}, overrides)).toMatchInlineSnapshot('" Tm9 Qm9 Sm9 Rm9"'); + expect(js()).toMatchInlineSnapshot('" Tk9 Qk9 Sk9 Rk9 -macro$1jmmjz3"'); + expect(overrides).toMatchInlineSnapshot('" Tm9 Qm9 Sm9 Rm9 -macro$hWyGab"'); + expect(js({}, overrides)).toMatchInlineSnapshot( + '" Tm9 Qm9 Sm9 Rm9 -macro$hWyGab -macro$vku4y4"' + ); }); it("should support allowed overrides for values that aren't defined", () => { @@ -187,9 +198,11 @@ describe('style-macro', () => { minWidth: 32 }); - expect(js()).toMatchInlineSnapshot('" gE9"'); - expect(overrides).toMatchInlineSnapshot('" Nk9"'); - expect(js({}, overrides)).toMatchInlineSnapshot('" Nk9 gE9"'); + expect(js()).toMatchInlineSnapshot('" gE9 -macro$2v7u7e"'); + expect(overrides).toMatchInlineSnapshot('" Nk9 -macro$hsZrrc"'); + expect(js({}, overrides)).toMatchInlineSnapshot( + '" Nk9 -macro$hsZrrc gE9 -macro$emy58b"' + ); }); it('should support runtime conditions', () => { @@ -243,9 +256,13 @@ describe('style-macro', () => { " `); - expect(js({})).toMatchInlineSnapshot('" gH9 pt9"'); - expect(js({isHovered: true})).toMatchInlineSnapshot('" gF9 po9"'); - expect(js({isPressed: true})).toMatchInlineSnapshot('" gE9 pm9"'); + expect(js({})).toMatchInlineSnapshot('" gH9 pt9 -macro$1cgd07e"'); + expect(js({isHovered: true})).toMatchInlineSnapshot( + '" gF9 po9 -macro$1b5rdyb"' + ); + expect(js({isPressed: true})).toMatchInlineSnapshot( + '" gE9 pm9 -macro$1aigku8"' + ); }); it('should support nested runtime conditions', () => { @@ -286,11 +303,15 @@ describe('style-macro', () => { " `); - expect(js({})).toMatchInlineSnapshot('" gH9"'); - expect(js({isHovered: true})).toMatchInlineSnapshot('" gF9"'); - expect(js({isSelected: true})).toMatchInlineSnapshot('" g_h9"'); + expect(js({})).toMatchInlineSnapshot('" gH9 -macro$2v7ua5"'); + expect(js({isHovered: true})).toMatchInlineSnapshot( + '" gF9 -macro$2v7u8b"' + ); + expect(js({isSelected: true})).toMatchInlineSnapshot( + '" g_h9 -macro$nl39vw"' + ); expect(js({isSelected: true, isHovered: true})).toMatchInlineSnapshot( - '" g39"' + '" g39 -macro$2v7tqw"' ); }); @@ -305,9 +326,15 @@ describe('style-macro', () => { } }); - expect(js({variant: 'accent'})).toMatchInlineSnapshot('" gY9"'); - expect(js({variant: 'primary'})).toMatchInlineSnapshot('" gjQquMe9"'); - expect(js({variant: 'secondary'})).toMatchInlineSnapshot('" gw9"'); + expect(js({variant: 'accent'})).toMatchInlineSnapshot( + '" gY9 -macro$2v7upq"' + ); + expect(js({variant: 'primary'})).toMatchInlineSnapshot( + '" gjQquMe9 -macro$1xbmgag"' + ); + expect(js({variant: 'secondary'})).toMatchInlineSnapshot( + '" gw9 -macro$2v7vh8"' + ); }); it('supports runtime conditions nested inside css conditions', () => { @@ -341,8 +368,10 @@ describe('style-macro', () => { " `); - expect(js({})).toMatchInlineSnapshot('" plb9"'); - expect(js({isSelected: true})).toMatchInlineSnapshot('" ple9"'); + expect(js({})).toMatchInlineSnapshot('" plb9 -macro$nlai7w"'); + expect(js({isSelected: true})).toMatchInlineSnapshot( + '" ple9 -macro$nlaian"' + ); }); it('should expand shorthand properties to longhands', () => { @@ -350,7 +379,7 @@ describe('style-macro', () => { padding: 24 }); - expect(js).toMatchInlineSnapshot('" Th9 Qh9 Sh9 Rh9"'); + expect(js).toMatchInlineSnapshot('" Th9 Qh9 Sh9 Rh9 -macro$tgBsxe"'); expect(css).toMatchInlineSnapshot(` "@layer _.a; diff --git a/packages/@react-spectrum/s2/style/runtime.ts b/packages/@react-spectrum/s2/style/runtime.ts index 5b567e2ab56..9bbbfc982ff 100644 --- a/packages/@react-spectrum/s2/style/runtime.ts +++ b/packages/@react-spectrum/s2/style/runtime.ts @@ -39,16 +39,23 @@ import {StyleString} from './types'; export function mergeStyles(...styles: (StyleString | null | undefined)[]): StyleString { let definedStyles = styles.filter(Boolean) as StyleString[]; if (definedStyles.length === 1) { - return definedStyles[0]; + let first = definedStyles[0]; + if (typeof first !== 'string') { + // static macro has a toString method so that we generate the style macro map for the entry + // it's automatically called in other places, but for our merging, we have to call it ourselves + return (first as StyleString).toString() as StyleString; + } + return first; } - let map = new Map(); + let map = new Map(); for (let style of definedStyles) { - for (let [k, v] of parse(style)) { + // must call toString here for the static macro + for (let [k, v] of parse(style.toString())) { map.set(k, v); } } - + let res = ''; for (let value of map.values()) { res += value; diff --git a/packages/@react-spectrum/s2/style/style-macro.ts b/packages/@react-spectrum/s2/style/style-macro.ts index d41d6aaeee6..db060370eea 100644 --- a/packages/@react-spectrum/s2/style/style-macro.ts +++ b/packages/@react-spectrum/s2/style/style-macro.ts @@ -176,7 +176,7 @@ export function parseArbitraryValue(value: Value): string | undefined { return value.slice(1, -1); } else if ( typeof value === 'string' && ( - /^(var|calc|min|max|clamp|round|mod|rem|sin|cos|tan|asin|acos|atan|atan2|pow|sqrt|hypot|log|exp|abs|sign)\(.+\)$/.test(value) || + /^(var|calc|min|max|clamp|round|mod|rem|sin|cos|tan|asin|acos|atan|atan2|pow|sqrt|hypot|log|exp|abs|sign)\(.+\)$/.test(value) || /^(inherit|initial|unset)$/.test(value) ) ) { @@ -205,6 +205,8 @@ interface MacroContext { addAsset(asset: {type: string, content: string}): void } +let isCompilingDependencies: boolean | null | string = false; + export function createTheme(theme: T): StyleFunction, 'default' | Extract> { let properties = new Map>(Object.entries(theme.properties).map(([k, v]) => { if (!Array.isArray(v) && v.cssProperties) { @@ -280,8 +282,11 @@ export function createTheme(theme: T): StyleFunction(theme: T): StyleFunction(); - let js = 'let rules = " ";\n'; + let js = 'let rules = " ", currentRules = {};\n'; if (allowedOverrides?.length) { for (let property of allowedOverrides) { let shorthand = theme.shorthands[property]; @@ -315,7 +320,7 @@ export function createTheme(theme: T): StyleFunction(theme: T): StyleFunction classNamePrefix(p, p)).join('|')})[^\\s]+/g`; + let regex = `/(?:^|\\s)(${[...allowedOverridesSet].map(p => classNamePrefix(p, p)).join('|')}|-macro\\$)[^\\s]+/g`; if (loop) { - js += `let matches = (overrides || '').matchAll(${regex});\n`; + js += `let matches = String(overrides || '').matchAll(${regex});\n`; js += 'for (let p of matches) {\n'; js += loop; js += ' rules += p[0];\n'; js += '}\n'; } else { - js += `rules += ((overrides || '').match(${regex}) || []).join('')\n`; + js += `rules += (String(overrides || '').match(${regex}) || []).join('')\n`; } } @@ -383,10 +388,24 @@ export function createTheme(theme: T): StyleFunction(theme: T): StyleFunction(theme: T): StyleFunction(theme: T): StyleFunction, indent?: string): string } +let conditionStack: string[] = []; + /** A CSS style rule. */ class StyleRule implements Rule { className: string; pseudos: string; property: string; value: string; + themeProperty: string | undefined; + themeValue: Value | undefined; - constructor(className: string, property: string, value: string) { + constructor(className: string, property: string, value: string, themeProperty: string, themeValue) { this.className = className; this.pseudos = ''; this.property = property; this.value = value; + if (isCompilingDependencies !== null) { + this.themeProperty = themeProperty; + this.themeValue = themeValue; + } } addPseudo(prelude: string) { @@ -687,6 +715,21 @@ class StyleRule implements Rule { res += `${indent}if (!${this.property.replace('--', '__')}) `; } res += `${indent}rules += ' ${this.className}';`; + if (this.themeProperty) { + let name = this.themeProperty; + if (this.pseudos) { + conditionStack.push(this.pseudos); + } + if (conditionStack.length) { + // name += ` (${conditionStack.join(', ')})`; + res += ` currentRules[${JSON.stringify(name)}] = typeof currentRules[${JSON.stringify(name)}] === 'object' ? currentRules[${JSON.stringify(name)}] : {"default": currentRules[${JSON.stringify(name)}]}; currentRules[${JSON.stringify(name)}][${JSON.stringify(conditionStack.join(' && '))}] = ${JSON.stringify(this.themeValue)};`; + } else { + res += ` currentRules[${JSON.stringify(name)}] = ${JSON.stringify(this.themeValue)};`; + } + if (this.pseudos) { + conditionStack.pop(); + } + } return res; } } @@ -739,10 +782,12 @@ class GroupRule implements Rule { /** A rule that applies conditionally in CSS (e.g. @media). */ class AtRule extends GroupRule { prelude: string; + themeCondition: string | null; - constructor(rules: Rule[], prelude: string, layer: string) { + constructor(rules: Rule[], prelude: string, layer: string, themeCondition: string | null) { super(rules, layer); this.prelude = prelude; + this.themeCondition = themeCondition; } toCSS(rulesByLayer: Map, preludes: string[] = [], layer?: string): void { @@ -750,6 +795,13 @@ class AtRule extends GroupRule { super.toCSS(rulesByLayer, preludes, layer); preludes?.pop(); } + + toJS(allowedOverridesSet: Set, indent?: string): string { + conditionStack.push(this.themeCondition || this.prelude); + let res = super.toJS(allowedOverridesSet, indent); + conditionStack.pop(); + return res; + } } /** A rule that applies conditionally at runtime. */ @@ -766,7 +818,10 @@ class ConditionalRule extends GroupRule { } toJS(allowedOverridesSet: Set, indent = ''): string { - return `${indent}if (props.${this.condition}) {\n${super.toJS(allowedOverridesSet, indent + ' ')}\n${indent}}`; + conditionStack.push(this.condition); + let res = `${indent}if (props.${this.condition}) {\n${super.toJS(allowedOverridesSet, indent + ' ')}\n${indent}}`; + conditionStack.pop(); + return res; } } diff --git a/packages/dev/style-macro-chrome-plugin/.parcelrc b/packages/dev/style-macro-chrome-plugin/.parcelrc new file mode 100644 index 00000000000..f497a196e5f --- /dev/null +++ b/packages/dev/style-macro-chrome-plugin/.parcelrc @@ -0,0 +1,9 @@ +{ + "extends": "@parcel/config-webextension", + "transformers": { + "*.{js,mjs,jsx,cjs,ts,tsx}": [ + "@parcel/transformer-js", + "@parcel/transformer-react-refresh-wrap" + ] + } +} diff --git a/packages/dev/style-macro-chrome-plugin/README.md b/packages/dev/style-macro-chrome-plugin/README.md new file mode 100644 index 00000000000..b58aa54e91f --- /dev/null +++ b/packages/dev/style-macro-chrome-plugin/README.md @@ -0,0 +1,43 @@ +# style-macro-chrome-plugin + +This is a chrome plugin to assist in debugging the styles applied by our Style Macro. + +## Local development + +From the root of our monopackage, run + +``` +yarn +yarn workspace style-macro-chrome-plugin start +``` + +This will create a dist directory in the directory `packages/dev/style-macro-chrome-plugin` which will update anytime the code changes and results in a rebuild. + +Next, open Chrome and go to [chrome://extensions/](chrome://extensions/). + +Load an unpacked extension, it's a button in the top left, and navigate to the dist directory. + +The extension is now registered in Chrome and you can go to storybook or docs, wherever you are working. + +Inspect an element on the page to open dev tools and go to the Style Macro panel. + +## Troubleshooting + +If the panel isn't updating with styles, try closing the dev tools and reopening it. + +If the extension doesn't appear to have the latest code, try closing the dev tools and reopening it. + +If every tab you have open (or many of them) reload when you make local changes to the extension, then go into the extension settings and limit it to `localhost` or something appropriate. + +## ToDos + +- [ ] Work with RSC +- [ ] Would be pretty cool if we could match a style condition to trigger it, like hover +- [ ] Eventually add to https://github.com/astahmer/atomic-css-devtools ?? +- [ ] Our own UI +- [ ] Filtering +- [ ] Resolve css variables inline +- [ ] Link to file on the side instead of grouping by filename? +- [ ] Add classname that is applying style? +- [ ] Work in MFE's +- [ ] Don't memory leak the global object diff --git a/packages/dev/style-macro-chrome-plugin/package.json b/packages/dev/style-macro-chrome-plugin/package.json new file mode 100644 index 00000000000..11098f97b87 --- /dev/null +++ b/packages/dev/style-macro-chrome-plugin/package.json @@ -0,0 +1,25 @@ +{ + "name": "style-macro-chrome-plugin", + "version": "0.1.0", + "scripts": { + "start": "parcel watch src/manifest.json --host localhost --config .parcelrc", + "build": "parcel build src/manifest.json --config .parcelrc" + }, + "devDependencies": { + "@parcel/config-default": "^2.14.0", + "@parcel/config-webextension": "^2.14.0", + "@parcel/core": "^2.14.0", + "@parcel/transformer-js": "^2.14.0", + "parcel": "^2.14.0" + }, + "rsp": { + "type": "cli" + }, + "repository": { + "type": "git", + "url": "https://github.com/adobe/react-spectrum" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/dev/style-macro-chrome-plugin/src/content-script.js b/packages/dev/style-macro-chrome-plugin/src/content-script.js new file mode 100644 index 00000000000..7111571a444 --- /dev/null +++ b/packages/dev/style-macro-chrome-plugin/src/content-script.js @@ -0,0 +1,25 @@ + +if (window.__macrosLoaded) { + return; +} +window.__macrosLoaded = true; + +window.addEventListener('message', function (event) { + // Only accept messages from the same frame + if (event.source !== window) { + return; + } + + var message = event.data; + + // Only accept messages that we know are ours. Note that this is not foolproof + // and the page can easily spoof messages if it wants to. + if (message !== 'update-macros') { + return; + } + // if this script is run multiple times on the page, then only handle it once + event.stopImmediatePropagation(); + event.stopPropagation(); + + chrome.runtime.sendMessage(message); +}); diff --git a/packages/dev/style-macro-chrome-plugin/src/devtool.js b/packages/dev/style-macro-chrome-plugin/src/devtool.js new file mode 100644 index 00000000000..eeff5601b62 --- /dev/null +++ b/packages/dev/style-macro-chrome-plugin/src/devtool.js @@ -0,0 +1,55 @@ + + +chrome.devtools.panels.elements.createSidebarPane('Style Macros', (sidebar) => { + sidebar.setObject({}); + let update = () => { + chrome.devtools.inspectedWindow.eval('$0.getAttribute("class")', async (className) => { + if (typeof className !== 'string') { + sidebar.setObject({}); + return; + } + + let macros = className.matchAll(/-macro\$([^\s]+)/g); + let matches = []; + for (let macro of macros) { + await new Promise(resolve => { + chrome.devtools.inspectedWindow.eval(`$0.ownerDocument.defaultView.__macros?.[${JSON.stringify(macro[1])}]`, (result, x) => { + if (result) { + matches.push(result); + } + resolve(); + }); + }); + } + + if (matches.length === 0) { + sidebar.setObject({}); + } else if (matches.length === 1) { + sidebar.setObject(matches[0].style ?? {}, matches[0].loc); + } else { + let seenProperties = new Set(); + for (let i = matches.length - 1; i >= 0; i--) { + for (let key in matches[i].style) { + if (seenProperties.has(key)) { + delete matches[i].style[key]; + } else { + seenProperties.add(key); + } + } + } + + let res = {}; + for (let match of matches) { + res[match.loc] = match.style; + } + sidebar.setObject(res); + } + }); + }; + + chrome.devtools.panels.elements.onSelectionChanged.addListener(() => { + update(); + }); + + chrome.runtime.onMessage.addListener(() => update()); +}); diff --git a/packages/dev/style-macro-chrome-plugin/src/devtools.html b/packages/dev/style-macro-chrome-plugin/src/devtools.html new file mode 100644 index 00000000000..6f5f400e09b --- /dev/null +++ b/packages/dev/style-macro-chrome-plugin/src/devtools.html @@ -0,0 +1,7 @@ + + + + Devtools! + + + diff --git a/packages/dev/style-macro-chrome-plugin/src/icons/128.png b/packages/dev/style-macro-chrome-plugin/src/icons/128.png new file mode 100644 index 00000000000..a0a1377e1a9 Binary files /dev/null and b/packages/dev/style-macro-chrome-plugin/src/icons/128.png differ diff --git a/packages/dev/style-macro-chrome-plugin/src/icons/16.png b/packages/dev/style-macro-chrome-plugin/src/icons/16.png new file mode 100644 index 00000000000..41a22d0a311 Binary files /dev/null and b/packages/dev/style-macro-chrome-plugin/src/icons/16.png differ diff --git a/packages/dev/style-macro-chrome-plugin/src/icons/32.png b/packages/dev/style-macro-chrome-plugin/src/icons/32.png new file mode 100644 index 00000000000..493110735d2 Binary files /dev/null and b/packages/dev/style-macro-chrome-plugin/src/icons/32.png differ diff --git a/packages/dev/style-macro-chrome-plugin/src/icons/48.png b/packages/dev/style-macro-chrome-plugin/src/icons/48.png new file mode 100644 index 00000000000..6794ef2da83 Binary files /dev/null and b/packages/dev/style-macro-chrome-plugin/src/icons/48.png differ diff --git a/packages/dev/style-macro-chrome-plugin/src/icons/96.png b/packages/dev/style-macro-chrome-plugin/src/icons/96.png new file mode 100644 index 00000000000..b12d2c4a2f0 Binary files /dev/null and b/packages/dev/style-macro-chrome-plugin/src/icons/96.png differ diff --git a/packages/dev/style-macro-chrome-plugin/src/manifest.json b/packages/dev/style-macro-chrome-plugin/src/manifest.json new file mode 100644 index 00000000000..c2cab5a5a2c --- /dev/null +++ b/packages/dev/style-macro-chrome-plugin/src/manifest.json @@ -0,0 +1,19 @@ +{ + "manifest_version": 3, + "name": "Style Macro DevTools", + "version": "0.0.1", + "devtools_page": "devtools.html", + "content_scripts": [{ + "matches": ["*://*/*"], + "js": ["content-script.js"], + "all_frames": true, + "run_at": "document_start" + }], + "icons": { + "16": "icons/16.png", + "32": "icons/32.png", + "48": "icons/48.png", + "96": "icons/96.png", + "128": "icons/128.png" + } +} diff --git a/yarn.lock b/yarn.lock index 56957f5e18d..a604d8bc635 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4429,6 +4429,21 @@ __metadata: languageName: node linkType: hard +"@parcel/config-webextension@npm:2.14.0": + version: 2.14.0 + resolution: "@parcel/config-webextension@npm:2.14.0" + dependencies: + "@parcel/config-default": "npm:2.14.0" + "@parcel/packager-webextension": "npm:2.14.0" + "@parcel/runtime-webextension": "npm:2.14.0" + "@parcel/transformer-raw": "npm:2.14.0" + "@parcel/transformer-webextension": "npm:2.14.0" + peerDependencies: + "@parcel/core": ^2.14.0 + checksum: 10c0/3348bcfb3143aba2bd684b4a39c3eccfbeaacb154f332a58c0f3f3e9a737433110724ca703f3d7b183480e2b0f4a072857f6df602a17beb9a390ab1459cf6290 + languageName: node + linkType: hard + "@parcel/core@npm:2.14.0, @parcel/core@npm:^2.13.1, @parcel/core@npm:^2.14.0": version: 2.14.0 resolution: "@parcel/core@npm:2.14.0" @@ -4767,6 +4782,17 @@ __metadata: languageName: node linkType: hard +"@parcel/packager-webextension@npm:2.14.0": + version: 2.14.0 + resolution: "@parcel/packager-webextension@npm:2.14.0" + dependencies: + "@parcel/plugin": "npm:2.14.0" + "@parcel/utils": "npm:2.14.0" + nullthrows: "npm:^1.1.1" + checksum: 10c0/85b893868b7bfa9db350112de2e5736dcdb303cb07bd4269ce7c75d5f38b83e999f38fb518e7871dd691c696011463fc67a90ab05b561f2636c6510d93c9cf96 + languageName: node + linkType: hard + "@parcel/plugin@npm:2.14.0, @parcel/plugin@npm:^2.0.0, @parcel/plugin@npm:^2.10.2, @parcel/plugin@npm:^2.14.0": version: 2.14.0 resolution: "@parcel/plugin@npm:2.14.0" @@ -4903,6 +4929,17 @@ __metadata: languageName: node linkType: hard +"@parcel/runtime-webextension@npm:2.14.0": + version: 2.14.0 + resolution: "@parcel/runtime-webextension@npm:2.14.0" + dependencies: + "@parcel/plugin": "npm:2.14.0" + "@parcel/utils": "npm:2.14.0" + nullthrows: "npm:^1.1.1" + checksum: 10c0/19a26d2054b6d8acf0ef5a7e793bc9a1231370607cf691bacc54ede5859fe2a25e61915d175718eb8b0ce279d496ab8cde94a1e8e798ee2a199f06a23b1f653e + languageName: node + linkType: hard + "@parcel/rust@npm:2.14.0": version: 2.14.0 resolution: "@parcel/rust@npm:2.14.0" @@ -5004,7 +5041,7 @@ __metadata: languageName: node linkType: hard -"@parcel/transformer-js@npm:2.14.0, @parcel/transformer-js@npm:^2.13.1": +"@parcel/transformer-js@npm:2.14.0": version: 2.14.0 resolution: "@parcel/transformer-js@npm:2.14.0" dependencies: @@ -5025,6 +5062,27 @@ __metadata: languageName: node linkType: hard +"@parcel/transformer-js@patch:@parcel/transformer-js@npm%3A2.14.0#~/.yarn/patches/@parcel-transformer-js-npm-2.14.0-9521af387e.patch": + version: 2.14.0 + resolution: "@parcel/transformer-js@patch:@parcel/transformer-js@npm%3A2.14.0#~/.yarn/patches/@parcel-transformer-js-npm-2.14.0-9521af387e.patch::version=2.14.0&hash=773e96" + dependencies: + "@parcel/diagnostic": "npm:2.14.0" + "@parcel/plugin": "npm:2.14.0" + "@parcel/rust": "npm:2.14.0" + "@parcel/source-map": "npm:^2.1.1" + "@parcel/utils": "npm:2.14.0" + "@parcel/workers": "npm:2.14.0" + "@swc/helpers": "npm:^0.5.0" + browserslist: "npm:^4.6.6" + nullthrows: "npm:^1.1.1" + regenerator-runtime: "npm:^0.14.1" + semver: "npm:^7.5.2" + peerDependencies: + "@parcel/core": ^2.14.0 + checksum: 10c0/ac714d500c6ceb36290d6605a4de9724bcfc51965f7e496eb279943e73a7d0df5109bdb1dacec03966e5482b64c283e0583066f0272b8a4c860bd76923c55cf1 + languageName: node + linkType: hard + "@parcel/transformer-json@npm:2.14.0": version: 2.14.0 resolution: "@parcel/transformer-json@npm:2.14.0" @@ -5156,6 +5214,19 @@ __metadata: languageName: node linkType: hard +"@parcel/transformer-webextension@npm:2.14.0": + version: 2.14.0 + resolution: "@parcel/transformer-webextension@npm:2.14.0" + dependencies: + "@mischnic/json-sourcemap": "npm:^0.1.0" + "@parcel/diagnostic": "npm:2.14.0" + "@parcel/plugin": "npm:2.14.0" + "@parcel/utils": "npm:2.14.0" + content-security-policy-parser: "npm:^0.6.0" + checksum: 10c0/b83c02346da27b0a4b5ec22a9d304724c41fe576f3c6b9ebe43e7c0d40ddf6dc80dbb9ca6086d97f4301181eb516e6bbfb98b166195325b8a223e83637769f41 + languageName: node + linkType: hard + "@parcel/ts-utils@npm:2.14.0": version: 2.14.0 resolution: "@parcel/ts-utils@npm:2.14.0" @@ -14859,6 +14930,13 @@ __metadata: languageName: node linkType: hard +"content-security-policy-parser@npm:^0.6.0": + version: 0.6.0 + resolution: "content-security-policy-parser@npm:0.6.0" + checksum: 10c0/f494e69020e2320179eab47ad2cdafb09752ed63ca4fb5445071381e392d19edd110c0c3ec43f135d27c34b49dbab851b7fcf188dd2ba30cacd6e1107b15b674 + languageName: node + linkType: hard + "content-type@npm:~1.0.4, content-type@npm:~1.0.5": version: 1.0.5 resolution: "content-type@npm:1.0.5" @@ -30386,6 +30464,18 @@ __metadata: languageName: node linkType: hard +"style-macro-chrome-plugin@workspace:packages/dev/style-macro-chrome-plugin": + version: 0.0.0-use.local + resolution: "style-macro-chrome-plugin@workspace:packages/dev/style-macro-chrome-plugin" + dependencies: + "@parcel/config-default": "npm:^2.14.0" + "@parcel/config-webextension": "npm:^2.14.0" + "@parcel/core": "npm:^2.14.0" + "@parcel/transformer-js": "npm:^2.14.0" + parcel: "npm:^2.14.0" + languageName: unknown + linkType: soft + "style-to-object@npm:^0.3.0": version: 0.3.0 resolution: "style-to-object@npm:0.3.0"