diff --git a/change/@fluentui-babel-make-styles-483339d1-5946-49b0-81d4-f0b5d9cc738e.json b/change/@fluentui-babel-make-styles-483339d1-5946-49b0-81d4-f0b5d9cc738e.json new file mode 100644 index 0000000000000..3215ed6bec5fa --- /dev/null +++ b/change/@fluentui-babel-make-styles-483339d1-5946-49b0-81d4-f0b5d9cc738e.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "simplify module evaluation", + "packageName": "@fluentui/babel-make-styles", + "email": "olfedias@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/packages/babel-make-styles/__fixtures__/commonjs/code.ts b/packages/babel-make-styles/__fixtures__/commonjs/code.ts index d534afd06be7a..aad7775c7aca4 100644 --- a/packages/babel-make-styles/__fixtures__/commonjs/code.ts +++ b/packages/babel-make-styles/__fixtures__/commonjs/code.ts @@ -1,13 +1,9 @@ -import { Theme } from '@fluentui/react-theme'; const react_make_styles_1 = require('@fluentui/react-make-styles'); -const useStyles = react_make_styles_1.makeStyles({ - root: function (theme: Theme) { - return { - fontSize: theme.fontSizeBase300, - lineHeight: theme.lineHeightBase300, - fontWeight: theme.fontWeightRegular, - }; +export const useStyles = react_make_styles_1.makeStyles({ + root: { + fontSize: '14px', + lineHeight: 1, }, }); diff --git a/packages/babel-make-styles/__fixtures__/commonjs/output.ts b/packages/babel-make-styles/__fixtures__/commonjs/output.ts index 538e54dfd3357..d3f779da56bca 100644 --- a/packages/babel-make-styles/__fixtures__/commonjs/output.ts +++ b/packages/babel-make-styles/__fixtures__/commonjs/output.ts @@ -1,22 +1,14 @@ -import { Theme } from '@fluentui/react-theme'; - const react_make_styles_1 = require('@fluentui/react-make-styles'); -const useStyles = react_make_styles_1.__styles( +export const useStyles = react_make_styles_1.__styles( { root: { - Be2twd7: 'fkhj508', - Bg96gwp: 'f1i3iumi', - Bhrd7zp: 'figsok6', + Be2twd7: 'fses1vf', + Bg96gwp: 'fp6vxd', }, }, { - d: [ - '.fkhj508{font-size:var(--fontSizeBase300);}', - '.f1i3iumi{line-height:var(--lineHeightBase300);}', - '.figsok6{font-weight:var(--fontWeightRegular);}', - ], + d: ['.fses1vf{font-size:14px;}', '.fp6vxd{line-height:1;}'], }, ); - console.log(useStyles); diff --git a/packages/babel-make-styles/__fixtures__/config-evaluation-rules/code.ts b/packages/babel-make-styles/__fixtures__/config-evaluation-rules/code.ts index 34635cc22f0ab..27a1add145cba 100644 --- a/packages/babel-make-styles/__fixtures__/config-evaluation-rules/code.ts +++ b/packages/babel-make-styles/__fixtures__/config-evaluation-rules/code.ts @@ -1,13 +1,8 @@ import { makeStyles } from '@fluentui/react-make-styles'; - -const func = () => { - // This assignment has no sense, but it will prevent us from evaluation in AST - // This fixture uses "sampleEvaluator.js" in plugin's config so input we should get a different color - const color = 'red'; - - return { color }; -}; +import { colorRed } from './consts'; export const useStyles = makeStyles({ - root: func(), + // This import has no sense, but it will prevent us from evaluation in AST by Babel + // This fixture uses "sampleEvaluator.js" in plugin's config so input we should get a different color + root: { color: colorRed }, }); diff --git a/packages/babel-make-styles/__fixtures__/config-evaluation-rules/consts.ts b/packages/babel-make-styles/__fixtures__/config-evaluation-rules/consts.ts new file mode 100644 index 0000000000000..90717adddbc4b --- /dev/null +++ b/packages/babel-make-styles/__fixtures__/config-evaluation-rules/consts.ts @@ -0,0 +1 @@ +export const colorRed = 'red'; diff --git a/packages/babel-make-styles/__fixtures__/config-evaluation-rules/output.ts b/packages/babel-make-styles/__fixtures__/config-evaluation-rules/output.ts index 5b1d05eb386f7..1ff69eba1839d 100644 --- a/packages/babel-make-styles/__fixtures__/config-evaluation-rules/output.ts +++ b/packages/babel-make-styles/__fixtures__/config-evaluation-rules/output.ts @@ -1,14 +1,5 @@ import { __styles } from '@fluentui/react-make-styles'; - -const func = () => { - // This assignment has no sense, but it will prevent us from evaluation in AST - // This fixture uses "sampleEvaluator.js" in plugin's config so input we should get a different color - const color = 'red'; - return { - color, - }; -}; - +import { colorRed } from './consts'; export const useStyles = __styles( { root: { diff --git a/packages/babel-make-styles/__fixtures__/config-evaluation-rules/sampleEvaluator.js b/packages/babel-make-styles/__fixtures__/config-evaluation-rules/sampleEvaluator.js index 633346eb53d12..8871cf21f659a 100644 --- a/packages/babel-make-styles/__fixtures__/config-evaluation-rules/sampleEvaluator.js +++ b/packages/babel-make-styles/__fixtures__/config-evaluation-rules/sampleEvaluator.js @@ -4,7 +4,7 @@ const sampleEvaluator = () => { // Evaluators transform input code to something that will be evaluated by Node later. In evaluatePathsInVM() we expect // that results will be available as "exports.__mkPreval", this evaluator mocks it - const result = `exports.__mkPreval = [{ color: 'blue' }]`; + const result = `exports.__mkPreval = [{ root: { color: 'blue' } }]`; return [result, null]; }; diff --git a/packages/babel-make-styles/__fixtures__/error-style-method/fixture.js b/packages/babel-make-styles/__fixtures__/error-style-method/fixture.js deleted file mode 100644 index d4ee6c91e2de4..0000000000000 --- a/packages/babel-make-styles/__fixtures__/error-style-method/fixture.js +++ /dev/null @@ -1,6 +0,0 @@ -import { makeStyles } from '@fluentui/react-make-styles'; - -// This file is .js intentionally to avoid TS compiler errors -export const useStyles = makeStyles({ - root() {}, -}); diff --git a/packages/babel-make-styles/__fixtures__/error-style-property/fixture.js b/packages/babel-make-styles/__fixtures__/error-style-property/fixture.js deleted file mode 100644 index 96f63b22b4df7..0000000000000 --- a/packages/babel-make-styles/__fixtures__/error-style-property/fixture.js +++ /dev/null @@ -1,8 +0,0 @@ -import { makeStyles } from '@fluentui/react-make-styles'; - -// This file is .js intentionally to avoid TS compiler errors -export const useStyles = makeStyles({ - root: { - color() {}, - }, -}); diff --git a/packages/babel-make-styles/__fixtures__/non-existing-module-call/code.tsx b/packages/babel-make-styles/__fixtures__/non-existing-module-call/code.tsx index 3a5895aaf2132..733e967248c8b 100644 --- a/packages/babel-make-styles/__fixtures__/non-existing-module-call/code.tsx +++ b/packages/babel-make-styles/__fixtures__/non-existing-module-call/code.tsx @@ -2,11 +2,8 @@ import { makeStyles } from '@fluentui/react-make-styles'; import { createModule } from './module'; export const useStyles = makeStyles({ - container: theme => { - // This assignment has no sense, but it will prevent us from evaluation in AST - const color = theme.colorNeutralStroke1; - - return { color }; + container: { + color: 'red', }, }); diff --git a/packages/babel-make-styles/__fixtures__/non-existing-module-call/output.tsx b/packages/babel-make-styles/__fixtures__/non-existing-module-call/output.tsx index 1a1fd5d766734..cae7e7abf7f9f 100644 --- a/packages/babel-make-styles/__fixtures__/non-existing-module-call/output.tsx +++ b/packages/babel-make-styles/__fixtures__/non-existing-module-call/output.tsx @@ -3,11 +3,11 @@ import { createModule } from './module'; export const useStyles = __styles( { container: { - sj55zd: 'fq2l63j', + sj55zd: 'fe3e8s9', }, }, { - d: ['.fq2l63j{color:var(--colorNeutralStroke1);}'], + d: ['.fe3e8s9{color:red;}'], }, ); createModule().baz(); diff --git a/packages/babel-make-styles/src/constants.ts b/packages/babel-make-styles/src/constants.ts deleted file mode 100644 index 0250b204fe78e..0000000000000 --- a/packages/babel-make-styles/src/constants.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const UNHANDLED_CASE_ERROR = - 'We met an unhandled case, this is a bug, please include a stacktrace and report it at ' + - 'https://github.com/microsoft/fluentui'; diff --git a/packages/babel-make-styles/src/plugin.test.ts b/packages/babel-make-styles/src/plugin.test.ts index a6460949978ad..98fc342701a75 100644 --- a/packages/babel-make-styles/src/plugin.test.ts +++ b/packages/babel-make-styles/src/plugin.test.ts @@ -31,7 +31,6 @@ pluginTester({ fixture: path.resolve(fixturesDir, 'error-argument-count', 'fixture.js'), error: /function accepts only a single param/, }, - { title: 'errors: throws on invalid config', fixture: path.resolve(fixturesDir, 'error-config-babel-options', 'fixture.ts'), @@ -42,17 +41,6 @@ pluginTester({ }, error: /Validation failed for passed config/, }, - - { - title: 'errors: throws on invalid slot', - fixture: path.resolve(fixturesDir, 'error-style-method', 'fixture.js'), - error: /Object methods are not supported for defining styles/, - }, - { - title: 'errors: throws on invalid property', - fixture: path.resolve(fixturesDir, 'error-style-property', 'fixture.js'), - error: /Object methods are not supported for defining styles/, - }, ], plugin, diff --git a/packages/babel-make-styles/src/plugin.ts b/packages/babel-make-styles/src/plugin.ts index 8d43d73335fef..e6ea7b361f0d4 100644 --- a/packages/babel-make-styles/src/plugin.ts +++ b/packages/babel-make-styles/src/plugin.ts @@ -6,77 +6,34 @@ import { resolveStyleRulesForSlots, CSSRulesByBucket, StyleBucketName, MakeStyle import { astify } from './utils/astify'; import { evaluatePaths } from './utils/evaluatePaths'; -import { UNHANDLED_CASE_ERROR } from './constants'; import { BabelPluginOptions } from './types'; import { validateOptions } from './validateOptions'; -type AstStyleNode = - | { kind: 'PURE_OBJECT'; nodePath: NodePath } - | { - kind: 'LAZY_OBJECT'; - nodePath: NodePath; - lazyPaths: NodePath[]; - } - | { kind: 'LAZY_FUNCTION'; nodePath: NodePath } - | { kind: 'LAZY_EXPRESSION_CALL'; nodePath: NodePath } - | { kind: 'LAZY_SEQUENCE_EXPRESSION'; nodePath: NodePath } - | { kind: 'LAZY_MEMBER'; nodePath: NodePath } - | { kind: 'LAZY_IDENTIFIER'; nodePath: NodePath } - | { kind: 'SPREAD'; nodePath: NodePath; spreadPath: NodePath }; - type BabelPluginState = PluginPass & { importDeclarationPath?: NodePath; requireDeclarationPath?: NodePath; - /** Contains all paths to calls of makeStyles(). */ + definitionPaths?: NodePath[]; calleePaths?: NodePath[]; - - /** Contains AST nodes with that should be resolved. */ - styleNodes?: AstStyleNode[]; }; -/** - * Parses a MemberExpression that defines token usage for extract parts that are needed to generate a CSS variable. - * - * @example - * getTokenParts(theme.alias.color.green.foreground1) - * // results in - * ['alias', 'color', 'green', 'foreground'] - */ -function getTokenParts(path: NodePath, result: string[] = []): string[] { - const objectPath = path.get('object'); - const propertyPath = path.get('property'); - - if (objectPath.isMemberExpression()) { - getTokenParts(objectPath, result); - } else if (objectPath.isIdentifier()) { - // We ignore this part as it will return a first id (i.e. "theme") - } else { - throw objectPath.buildCodeFrameError(UNHANDLED_CASE_ERROR); - } +function getDefinitionPathFromMakeStylesCallExpression( + callExpression: NodePath, +): NodePath { + const argumentPaths = callExpression.get('arguments') as NodePath[]; + const hasValidArguments = Array.isArray(argumentPaths) && argumentPaths.length === 1; - if (propertyPath.isIdentifier()) { - result.push(propertyPath.node.name); + if (!hasValidArguments) { + throw new Error('makeStyles() function accepts only a single param'); } - return result; -} - -/** - * Gets an Identifier from a MemberExpression that represents theme variable name. - */ -function getMemberExpressionIdentifier(expressionPath: NodePath): NodePath { - const objectPath = expressionPath.get('object'); + const definitionsPath = argumentPaths[0]; - if (objectPath.isIdentifier()) { - return objectPath; - } - - if (objectPath.isMemberExpression()) { - return getMemberExpressionIdentifier(objectPath); + if (!definitionsPath.isObjectExpression()) { + throw definitionsPath.buildCodeFrameError('makeStyles() function accepts only an object as a param'); } - throw objectPath.buildCodeFrameError(UNHANDLED_CASE_ERROR); + return definitionsPath; } /** @@ -126,335 +83,6 @@ function isRequireDeclarator(path: NodePath): boolean { return false; } -function namesToCssVariable(names: string[]): string { - return `var(--${names.join('-')})`; -} - -/** - * Processes an object (makeStyles argument) and collects smallest possible paths for evaluation later. - */ -function processDefinitions( - argumentsPaths: NodePath[], - state: BabelPluginState, -): void { - const hasValidArguments = Array.isArray(argumentsPaths) && argumentsPaths.length === 1; - - if (!hasValidArguments) { - throw new Error('makeStyles() function accepts only a single param'); - } - - const definitionsPath = argumentsPaths[0]; - - if (!definitionsPath.isObjectExpression()) { - throw definitionsPath.buildCodeFrameError('makeStyles() function accepts only an object as a param'); - } - - const styleSlots = definitionsPath.get('properties'); - - styleSlots.forEach((styleSlotPath: NodePath) => { - if (styleSlotPath.isObjectMethod()) { - throw styleSlotPath.buildCodeFrameError('Object methods are not supported for defining styles'); - } - - /** - * Needs lazy evaluation anyway. - * - * @example makeStyles({ ...SOME_STYLES }) - */ - if (styleSlotPath.isSpreadElement()) { - // Heads up! - // - // Babel can't evaluate spreads so we will fallback into VM execution anyway. Evaluation in VM replaces evaluated - // paths, this means that we will loose a "nodePath" as an identifier and will not be able to replace this path - // later. - // There we are cloning an existing spread and creating a wrapping spread: - // "{ ...fooBar }" becomes "{ ...{ ...forBar } }" - // ^ this spread will be evaluated in VM - // ^ this spread will be used for replacement later - const spreadArgumentPath = styleSlotPath.get('argument'); - const clonedArgumentNode = t.cloneNode(spreadArgumentPath.node); - const wrappingSpreadArgumentNode = t.objectExpression([t.spreadElement(clonedArgumentNode)]); - - spreadArgumentPath.replaceWith(wrappingSpreadArgumentNode); - state.styleNodes?.push({ - kind: 'SPREAD', - nodePath: styleSlotPath, - spreadPath: styleSlotPath.get('argument.properties.0') as NodePath, - }); - return; - } - - if (styleSlotPath.isObjectProperty()) { - const stylesPath = styleSlotPath.get('value'); - - /** - * Needs lazy evaluation anyway. - * - * @example makeStyles({ root: SOME_VARIABLE }) - */ - if (stylesPath.isIdentifier()) { - state.styleNodes?.push({ - kind: 'LAZY_IDENTIFIER', - nodePath: stylesPath, - }); - return; - } - - /** - * May need lazy evaluation in less optimistic scenarios. - * - * @example - * makeStyles({ root: { color: 'red' } }) // ✔ can be resolved in AST - * makeStyles({ root: { color: SOME_VARIABLE } }) // ❌ lazy evaluation - * makeStyles({ ...sharedStyles }) // ❌ lazy evaluation - */ - if (stylesPath.isObjectExpression()) { - const propertiesPaths = stylesPath.get('properties'); - const lazyPaths: NodePath[] = []; - - propertiesPaths.forEach(propertyPath => { - if (propertyPath.isObjectMethod()) { - throw propertyPath.buildCodeFrameError('Object methods are not supported for defining styles'); - } - - if (propertyPath.isObjectProperty()) { - const keyPath = propertyPath.get('key'); - const valuePath = propertyPath.get('value'); - - /** - * Computed properties may require lazy evaluation. - * - * @example - * makeStyles({ [var]: { color: 'red' } }) - * makeStyles({ [`${var}`]: { color: SOME_VARIABLE } }) - */ - if (propertyPath.node.computed) { - lazyPaths.push(keyPath); - } - - if (valuePath.isStringLiteral() || valuePath.isNullLiteral() || valuePath.isNumericLiteral()) { - return; - } - - // TODO: properly support this case to avoid useless lazy evaluations (as they should be slower) - // We should use recursive lookup there - // - // if (valuePath.isObjectExpression()) { - // return; - // } - - if (valuePath.isExpression()) { - lazyPaths.push(valuePath); - return; - } - - throw valuePath.buildCodeFrameError(UNHANDLED_CASE_ERROR); - } - - if (propertyPath.isSpreadElement()) { - lazyPaths.push(propertyPath); - return; - } - - throw propertyPath.buildCodeFrameError(UNHANDLED_CASE_ERROR); - }); - - if (lazyPaths.length === 0) { - state.styleNodes?.push({ - kind: 'PURE_OBJECT', - nodePath: stylesPath, - }); - return; - } - - state.styleNodes?.push({ - kind: 'LAZY_OBJECT', - nodePath: stylesPath, - lazyPaths, - }); - return; - } - - /** - * A scenario when slots styles are represented by functions, in the worst case fallbacks to lazy - * evaluation. - * - * @example - * // ✔ can be resolved in AST - * makeStyles({ root: (t) => ({ color: t.red }) }) - * makeStyles({ root: () => ({ padding: '12px' }) }) - * // ❌ lazy evaluation - * makeStyles({ root: (t) => ({ color: SOME_VARIABLE }) }) - * // ❌ lazy evaluation, the worst case as function contains body - * makeStyles({ root: (t) => { return { color: SOME_VARIABLE } } }) - */ - if (stylesPath.isArrowFunctionExpression()) { - const paramError = 'A function in makeStyles() can only have a single param (for tokens/theme)'; - if (stylesPath.get('params').length > 1) { - throw stylesPath.buildCodeFrameError(paramError); - } - - const paramsPath = stylesPath.get('params.0') as NodePath | undefined; - - if (paramsPath && !paramsPath.isIdentifier()) { - throw stylesPath.buildCodeFrameError(`${paramError} and it should be a valid identifier`); - } - - const bodyPath = stylesPath.get('body'); - - /** - * Optimistic case, we may not need to use lazy evaluation 🚀 - * - * @example - * // ✔ can be resolved in AST - * makeStyles({ root: (t) => ({ color: t.red }) }) - * makeStyles({ root: () => ({ padding: '12px' }) }) - * // ❌ lazy evaluation - * makeStyles({ root: (t) => ({ color: SOME_VARIABLE }) }) - */ - if (bodyPath.isObjectExpression()) { - const propertiesPaths = bodyPath.get('properties'); - const lazyPaths: NodePath[] = []; - - propertiesPaths.forEach(propertyPath => { - if (propertyPath.isObjectMethod()) { - throw propertyPath.buildCodeFrameError('Object methods are not supported for defining styles'); - } - - if (propertyPath.isObjectProperty()) { - const valuePath = propertyPath.get('value'); - - if (valuePath.isStringLiteral() || valuePath.isNullLiteral() || valuePath.isNumericLiteral()) { - return; - } - - // This condition resolves "theme.aliasColorGreenForeground1" to CSS variable - if (valuePath.isMemberExpression()) { - const identifierPath = getMemberExpressionIdentifier(valuePath); - const paramsName = paramsPath?.node.name; - - if (paramsName && identifierPath.isIdentifier({ name: paramsName })) { - const cssVariable = namesToCssVariable(getTokenParts(valuePath)); - - valuePath.replaceWith(t.stringLiteral(cssVariable)); - return; - } - - lazyPaths.push(valuePath); - return; - } - - if (valuePath.isExpression()) { - lazyPaths.push(valuePath); - return; - } - - throw valuePath.buildCodeFrameError(UNHANDLED_CASE_ERROR); - } - - if (propertyPath.isSpreadElement()) { - lazyPaths.push(propertyPath); - return; - } - - throw propertyPath.buildCodeFrameError(UNHANDLED_CASE_ERROR); - }); - - if (lazyPaths.length === 0) { - stylesPath.replaceWith(bodyPath); - state.styleNodes?.push({ - kind: 'PURE_OBJECT', - // 👇 as we replaced an arrow function with its body, we can cast typings - nodePath: (stylesPath as unknown) as NodePath, - }); - return; - } - - state.styleNodes?.push({ - kind: 'LAZY_FUNCTION', - nodePath: stylesPath, - }); - return; - - // TODO: this can be lazy object once we will implement nested lookup, see object path for reference - - // stylesPath.replaceWith(bodyPath); - // state.styleNodes?.push({ - // kind: 'LAZY_OBJECT', - // // 👇 as we replaced an arrow function with its body, we can cast typings - // nodePath: (stylesPath as unknown) as NodePath, - // lazyPaths, - // }); - // - // return; - } - - state.styleNodes?.push({ - kind: 'LAZY_FUNCTION', - nodePath: stylesPath, - }); - return; - } - - /** - * A scenario when slots styles are represented by functions with body, fallbacks to lazy evaluation. - * - * @example - * // ❌ lazy evaluation - * makeStyles({ root: function (t) { return { color: SOME_VARIABLE } } }) - */ - if (stylesPath.isFunctionExpression()) { - state.styleNodes?.push({ - kind: 'LAZY_FUNCTION', - nodePath: stylesPath, - }); - return; - } - - /** - * A scenario when slots styles are represented by a function call. - * - * @example - * // ❌ lazy evaluation - * makeStyles({ root: display() }) - * makeStyles({ root: typography.display() }) - */ - if (stylesPath.isCallExpression()) { - state.styleNodes?.push({ kind: 'LAZY_EXPRESSION_CALL', nodePath: stylesPath }); - return; - } - - /** - * A scenario when slots styles are represented by an object. - * - * @example - * // ❌ lazy evaluation - * makeStyles({ root: typography.display }) - */ - if (stylesPath.isMemberExpression()) { - state.styleNodes?.push({ kind: 'LAZY_MEMBER', nodePath: stylesPath }); - return; - } - - /** - * A scenario when slots styles are represented by a sequence expression. - * - * @example - * // ❌ lazy evaluation - * makeStyles({ root: (_a: { color: 'red' }, _a.backgroundColor: 'blue', _a ) }) - * makeStyles({ root: (_a: { color: SOME_VARIABLE }, _a.backgroundColor: 'blue', _a) }) - * makeStyles({ root: (_a: { color: 'red' }, _a[`. ${some_className}`]: { color: 'blue' }, _a) }) - */ - if (stylesPath.isSequenceExpression()) { - state.styleNodes?.push({ kind: 'LAZY_SEQUENCE_EXPRESSION', nodePath: stylesPath }); - return; - } - } - - throw styleSlotPath.buildCodeFrameError(UNHANDLED_CASE_ERROR); - }); -} - /** * Rules that are returned by `resolveStyles()` are not deduplicated. * It's critical to filter out duplicates for build-time transform to avoid duplicated rules in a bundle. @@ -497,8 +125,8 @@ export const plugin = declare, PluginObj, PluginObj[]>( - (acc, styleNode) => { - if ( - styleNode.kind === 'LAZY_IDENTIFIER' || - styleNode.kind === 'LAZY_FUNCTION' || - styleNode.kind === 'LAZY_MEMBER' || - styleNode.kind === 'LAZY_EXPRESSION_CALL' || - styleNode.kind === 'LAZY_SEQUENCE_EXPRESSION' - ) { - return [...acc, styleNode.nodePath]; - } + if (state.definitionPaths) { + // Runs Babel AST processing or module evaluation for Node once for all arguments of makeStyles() calls once + evaluatePaths(path, state.file.opts.filename!, state.definitionPaths, pluginOptions); - if (styleNode.kind === 'LAZY_OBJECT') { - return [...acc, ...styleNode.lazyPaths]; - } - - if (styleNode.kind === 'PURE_OBJECT') { - return acc; - } - - if (styleNode.kind === 'SPREAD') { - return [...acc, styleNode.spreadPath]; - } - - throw new Error( - `We don't support "styleNode.kind=${ - (styleNode as AstStyleNode).kind - }", this is a bug, please report it`, - ); - }, - [], - ); - - if (pathsToEvaluate.length > 0) { - evaluatePaths(path, state.file.opts.filename!, pathsToEvaluate, pluginOptions); - } - - state.styleNodes?.forEach(styleNode => { - if (styleNode.kind === 'SPREAD') { - const nodePath = styleNode.nodePath; - const evaluationResult = (nodePath.get('argument') as NodePath).evaluate(); + state.definitionPaths.forEach(definitionPath => { + const callExpressionPath = definitionPath.findParent(parentPath => + parentPath.isCallExpression(), + ) as NodePath; + const evaluationResult = definitionPath.evaluate(); if (!evaluationResult.confident) { - // This is undocumented but shows the specific statement that failed // eslint-disable-next-line @typescript-eslint/no-explicit-any const deoptPath = (evaluationResult as any).deopt as NodePath | undefined; - throw (deoptPath || nodePath).buildCodeFrameError( + throw (deoptPath || definitionPath).buildCodeFrameError( 'Evaluation of a code fragment failed, this is a bug, please report it', ); } - const stylesBySlots: Record = evaluationResult.value; - - nodePath.replaceWithMultiple((astify(stylesBySlots) as t.ObjectExpression).properties); - } - }); - - state.calleePaths?.forEach(calleePath => { - const callExpressionPath = calleePath.findParent(parentPath => - parentPath.isCallExpression(), - ) as NodePath; - const argumentPath = callExpressionPath.get('arguments.0') as NodePath; + const stylesBySlots: Record = evaluationResult.value; + const [classnamesMapping, cssRules] = resolveStyleRulesForSlots(stylesBySlots); - const evaluationResult = argumentPath.evaluate(); - - if (!evaluationResult.confident) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const deoptPath = (evaluationResult as any).deopt as NodePath | undefined; - throw (deoptPath || argumentPath).buildCodeFrameError( - 'Evaluation of a code fragment failed, this is a bug, please report it', - ); - } - - const stylesBySlots: Record = evaluationResult.value; - const [classnamesMapping, cssRules] = resolveStyleRulesForSlots(stylesBySlots); - - // TODO: find a better way to replace arguments - callExpressionPath.node.arguments = [astify(classnamesMapping), astify(dedupeCSSRules(cssRules))]; - }); + // TODO: find a better way to replace arguments + callExpressionPath.node.arguments = [astify(classnamesMapping), astify(dedupeCSSRules(cssRules))]; + }); + } if (state.importDeclarationPath) { const specifiers = state.importDeclarationPath.get('specifiers'); @@ -617,8 +191,8 @@ export const plugin = declare, PluginObj 0) { - state.calleePaths!.forEach(calleePath => { + if (state.calleePaths) { + state.calleePaths.forEach(calleePath => { calleePath.replaceWith(t.identifier('__styles')); }); } @@ -636,12 +210,16 @@ export const plugin = declare, PluginObj, PluginObj, PluginObj); }, },