diff --git a/change/@griffel-postcss-syntax-466b35ec-0bf1-4ddb-9e0e-381079132a98.json b/change/@griffel-postcss-syntax-466b35ec-0bf1-4ddb-9e0e-381079132a98.json new file mode 100644 index 000000000..23deeff3d --- /dev/null +++ b/change/@griffel-postcss-syntax-466b35ec-0bf1-4ddb-9e0e-381079132a98.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: return location of the makeStyles/makeResetStyles call expression when it is not called with object expression", + "packageName": "@griffel/postcss-syntax", + "email": "yuanboxue@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/packages/postcss-syntax/src/location-preset.ts b/packages/postcss-syntax/src/location-preset.ts index 81551a13b..a65eedf99 100644 --- a/packages/postcss-syntax/src/location-preset.ts +++ b/packages/postcss-syntax/src/location-preset.ts @@ -14,6 +14,7 @@ export type ResetCommentDirectivesByHookDeclarator = Record; export interface LocationPluginState extends PluginPass { + callExpressionLocations?: Record; locations?: LocationsByHookDeclarator; commentDirectives?: CommentDirectivesByHookDeclarator; @@ -22,11 +23,12 @@ export interface LocationPluginState extends PluginPass { } export interface LocationPluginMetadata { - locations: Record>; + callExpressionLocations: Record; + locations: LocationsByHookDeclarator; commentDirectives: CommentDirectivesByHookDeclarator; resetCommentDirectives: ResetCommentDirectivesByHookDeclarator; - resetLocations: Record; + resetLocations: ResetLocationsByHookDeclarator; } // eslint-disable-next-line @typescript-eslint/no-empty-interface @@ -57,6 +59,7 @@ const plugin = declare>((a name: '@griffel/slot-location-plugin', pre() { + this.callExpressionLocations = {}; this.locations = {}; this.resetLocations = {}; this.commentDirectives = {}; @@ -67,6 +70,7 @@ const plugin = declare>((a Program: { exit() { Object.assign(this.file.metadata, { + callExpressionLocations: this.callExpressionLocations, locations: this.locations, resetLocations: this.resetLocations, commentDirectives: this.commentDirectives, @@ -94,6 +98,13 @@ const plugin = declare>((a // but since we only collect locations, the plugin is idempotent and we // it's safe enough to avoid doing that check if (functionKinds.includes(callee.node.name)) { + if (path.node.loc) { + state.callExpressionLocations ??= {}; + state.callExpressionLocations[declaratorId] = { + ...path.node.loc, + }; + } + const locations = path.get('arguments')[0]; if (!locations.isObjectExpression()) { return; @@ -130,6 +141,13 @@ const plugin = declare>((a } if (resetFunctionKinds.includes(callee.node.name)) { + if (path.node.loc) { + state.callExpressionLocations ??= {}; + state.callExpressionLocations[declaratorId] = { + ...path.node.loc, + }; + } + state.resetLocations ??= {}; const resetStyles = path.get('arguments')[0]; if (!resetStyles.isObjectExpression()) { diff --git a/packages/postcss-syntax/src/parse.test.ts b/packages/postcss-syntax/src/parse.test.ts index e37a78606..963605248 100644 --- a/packages/postcss-syntax/src/parse.test.ts +++ b/packages/postcss-syntax/src/parse.test.ts @@ -154,6 +154,38 @@ export const useStyles = makeStyles({ }); }); + it('should map style locations to makeStyles call when it is not called with object expressions', () => { + const fixture = ` +import { makeStyles } from "@griffel/react"; + +const styles = { + slot1: { + color: "red", + }, + slot2: { + backgroundColor: "green", + }, +}; + +export const useStyles = makeStyles(styles); +`; + const root = parse(fixture, { from: 'fixture.styles.ts' }); + + expect(root.toString()).toMatchInlineSnapshot(` + ".fe3e8s9{color:red;} + .fcnqdeg{background-color:green;}" + `); + + root.walk(node => { + const slot = node.raw(GRIFFEL_SLOT_RAW); + expect(['slot1', 'slot2']).toContain(slot); + expect(node.raw(GRIFFEL_SLOT_LOCATION_RAW)).toEqual({ + start: { line: 13, column: 25, index: 173 }, + end: { line: 13, column: 43, index: 191 }, + }); + }); + }); + it('should hold original source in document raw field', () => { const fixture = ` import { makeStyles } from '@griffel/react'; @@ -334,6 +366,31 @@ export const useResetStyles2 = makeResetStyles({ }); }); + it('should map style locations to makeResetStyles call when it is not called with object expressions', () => { + const fixture = ` +import { makeResetStyles } from "@griffel/react"; + +const styles = { + color: "red", + backgroundColor: "green", +}; + +export const useResetStyles = makeResetStyles(styles); +`; + const root = parse(fixture, { from: 'fixture.styles.ts' }); + + expect(root.toString()).toMatchInlineSnapshot(`".rbe9p1m{color:red;background-color:green;}"`); + + root.walk(node => { + const declarator = node.raw(GRIFFEL_DECLARATOR_RAW); + expect(declarator).toEqual('useResetStyles'); + expect(node.raw(GRIFFEL_DECLARATOR_LOCATION_RAW)).toEqual({ + start: { line: 9, column: 30, index: 147 }, + end: { line: 9, column: 53, index: 170 }, + }); + }); + }); + it('should hold original source in document raw field', () => { const fixture = ` import { makeResetStyles } from '@griffel/react'; diff --git a/packages/postcss-syntax/src/parse.ts b/packages/postcss-syntax/src/parse.ts index b36fa3743..c8432195f 100644 --- a/packages/postcss-syntax/src/parse.ts +++ b/packages/postcss-syntax/src/parse.ts @@ -31,8 +31,15 @@ export const parse = (css: string | { toString(): string }, opts?: ParserOptions }, }); - const { cssEntries, cssResetEntries, resetLocations, locations, commentDirectives, resetCommentDirectives } = - metadata; + const { + cssEntries, + cssResetEntries, + callExpressionLocations, + resetLocations, + locations, + commentDirectives, + resetCommentDirectives, + } = metadata; const cssRuleSlotNames: string[] = []; const cssRules: string[] = []; @@ -74,9 +81,9 @@ export const parse = (css: string | { toString(): string }, opts?: ParserOptions node.raws[GRIFFEL_DECLARATOR_RAW] = declarator; if (slot) { node.raws[GRIFFEL_SLOT_RAW] = slot; - node.raws[GRIFFEL_SLOT_LOCATION_RAW] = locations[declarator][slot]; + node.raws[GRIFFEL_SLOT_LOCATION_RAW] = locations[declarator]?.[slot] ?? callExpressionLocations[declarator]; } else { - node.raws[GRIFFEL_DECLARATOR_LOCATION_RAW] = resetLocations[declarator]; + node.raws[GRIFFEL_DECLARATOR_LOCATION_RAW] = resetLocations[declarator] ?? callExpressionLocations[declarator]; } }); diff --git a/packages/postcss-syntax/src/transform-sync.test.ts b/packages/postcss-syntax/src/transform-sync.test.ts index c886e4495..3c80f8d2e 100644 --- a/packages/postcss-syntax/src/transform-sync.test.ts +++ b/packages/postcss-syntax/src/transform-sync.test.ts @@ -152,4 +152,147 @@ describe('transformSync', () => { } `); }); + + it('should return location of makeStyles call expression', () => { + const sourceCode = ` + import type { GriffelStyle } from "@griffel/react"; + import { makeStyles } from "@griffel/react"; + + const mixin = (): GriffelStyle => ({ + marginTop: "4px", + }); + + const styles = { + root: { + color: "red", + backgroundColor: "green", + ...mixin(), + }, + }; + + export const useStyles1 = makeStyles(styles); + export const useStyles2 = makeStyles(styles); + `; + const options: TransformOptions = { + filename: 'test.styles.ts', + pluginOptions: { + babelOptions: { + presets: ['@babel/preset-typescript'], + }, + generateMetadata: true, + }, + }; + + const result = transformSync(sourceCode, options); + + expect(result.metadata.cssEntries).toMatchInlineSnapshot(` + Object { + "useStyles1": Object { + "root": Array [ + ".fe3e8s9{color:red;}", + ".fcnqdeg{background-color:green;}", + ".fvjh0tl{margin-top:4px;}", + ], + }, + "useStyles2": Object { + "root": Array [ + ".fe3e8s9{color:red;}", + ".fcnqdeg{background-color:green;}", + ".fvjh0tl{margin-top:4px;}", + ], + }, + } + `); + expect(result.metadata.callExpressionLocations).toEqual({ + useStyles1: { + end: { + column: 50, + index: 383, + line: 17, + }, + start: { + column: 32, + index: 365, + line: 17, + }, + }, + useStyles2: { + end: { + column: 50, + index: 435, + line: 18, + }, + start: { + column: 32, + index: 417, + line: 18, + }, + }, + }); + }); + + it('should return location of makeResetStyles call expression', () => { + const sourceCode = ` + import type { GriffelStyle } from "@griffel/react"; + import { makeResetStyles } from "@griffel/react"; + const mixin = (): GriffelStyle => ({ + marginTop: "4px", + }); + const styles = { + color: "red", + backgroundColor: "green", + ...mixin(), + }; + export const useResetStyles1 = makeResetStyles(styles); + export const useResetStyles2 = makeResetStyles(styles); + `; + const options: TransformOptions = { + filename: 'test.styles.ts', + pluginOptions: { + babelOptions: { + presets: ['@babel/preset-typescript'], + }, + generateMetadata: true, + }, + }; + + const result = transformSync(sourceCode, options); + + expect(result.metadata.cssResetEntries).toMatchInlineSnapshot(` + Object { + "useResetStyles1": Array [ + ".rv6h41g{color:red;background-color:green;margin-top:4px;}", + ], + "useResetStyles2": Array [ + ".rv6h41g{color:red;background-color:green;margin-top:4px;}", + ], + } + `); + expect(result.metadata.callExpressionLocations).toEqual({ + useResetStyles1: { + end: { + column: 60, + index: 362, + line: 12, + }, + start: { + column: 37, + index: 339, + line: 12, + }, + }, + useResetStyles2: { + end: { + column: 60, + index: 424, + line: 13, + }, + start: { + column: 37, + index: 401, + line: 13, + }, + }, + }); + }); });