Skip to content

feat: Style macro devtool #8305

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .yarn/patches/@parcel-transformer-js-npm-2.14.0-9521af387e.patch
Original file line number Diff line number Diff line change
@@ -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);
},
8 changes: 8 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
}];
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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": {
Expand Down
2 changes: 1 addition & 1 deletion packages/@react-spectrum/s2/chromatic/CardView.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const cardViewStyles = style({
}
});

export const Empty = (args: CardViewProps<any>, {viewMode}) => {
export const Empty = (args: CardViewProps<any>, {viewMode} = {viewMode: ''}) => {
return (
<CardView
aria-label="Assets"
Expand Down
2 changes: 1 addition & 1 deletion packages/@react-spectrum/s2/src/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ export const Button = forwardRef(function Button(props: ButtonProps, ref: Focusa
isStaticColor: !!staticColor
}, props.styles)}>
{(renderProps) => (<>
{variant === 'genai' || variant === 'premium'
{variant === 'genai' || variant === 'premium'
? (
<span
className={gradient({
Expand Down
6 changes: 3 additions & 3 deletions packages/@react-spectrum/s2/stories/CardView.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ function PhotoCard({item, layout}: {item: Item, layout: string}) {
);
}

export const Example = (args: CardViewProps<any>, {viewMode}) => {
export const Example = (args: CardViewProps<any>, {viewMode} = {viewMode: ''}) => {
let list = useAsyncList<Item, number | null>({
async load({signal, cursor, items}) {
let page = cursor || 1;
Expand Down Expand Up @@ -157,7 +157,7 @@ Example.args = {
selectionMode: 'multiple'
};

export const Empty = (args: CardViewProps<any>, {viewMode}) => {
export const Empty = (args: CardViewProps<any>, {viewMode} = {viewMode: ''}) => {
return (
<CardView
aria-label="Assets"
Expand Down Expand Up @@ -202,7 +202,7 @@ function TopicCard({topic}: {topic: Topic}) {
);
}

export const CollectionCards = (args: CardViewProps<any>, {viewMode}) => {
export const CollectionCards = (args: CardViewProps<any>, {viewMode} = {viewMode: ''}) => {
let list = useAsyncList<Topic, number | null>({
async load({signal, cursor}) {
let page = cursor || 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,24 @@
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', () => {
let a = style({backgroundColor: 'red-1000', color: '[hotpink]'});
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()));
});
});
85 changes: 57 additions & 28 deletions packages/@react-spectrum/s2/style/__tests__/style-macro.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ function testStyle(...args) {
},
args
);
return {css, js};

return {css, js: typeof js === 'function' ? js : js.toString()};
}

describe('style-macro', () => {
Expand Down Expand Up @@ -55,7 +56,7 @@ describe('style-macro', () => {

"
`);
expect(js).toMatchInlineSnapshot('" Jbs9 Jbpv9"');
expect(js).toMatchInlineSnapshot('" Jbs9 Jbpv9 -macro$M74Bxe"');
});

it('should support self references', () => {
Expand Down Expand Up @@ -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"'
);
});

Expand All @@ -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', () => {
Expand All @@ -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', () => {
Expand All @@ -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", () => {
Expand All @@ -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', () => {
Expand Down Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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"'
);
});

Expand All @@ -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', () => {
Expand Down Expand Up @@ -341,16 +368,18 @@ 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', () => {
let {js, css} = testStyle({
padding: 24
});

expect(js).toMatchInlineSnapshot('" Th9 Qh9 Sh9 Rh9"');
expect(js).toMatchInlineSnapshot('" Th9 Qh9 Sh9 Rh9 -macro$tgBsxe"');
expect(css).toMatchInlineSnapshot(`
"@layer _.a;

Expand Down
15 changes: 11 additions & 4 deletions packages/@react-spectrum/s2/style/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, string>();
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;
Expand Down
Loading