diff --git a/.codesandbox/ci.json b/.codesandbox/ci.json index 29b9c5891..61f524d88 100644 --- a/.codesandbox/ci.json +++ b/.codesandbox/ci.json @@ -6,6 +6,7 @@ "packages/pigment-css-core", "packages/pigment-css-nextjs-plugin", "packages/pigment-css-react", + "packages/pigment-css-react-new", "packages/pigment-css-theme", "packages/pigment-css-unplugin", "packages/pigment-css-utils", diff --git a/.eslintignore b/.eslintignore index 1a9209a1e..3b7fdb074 100644 --- a/.eslintignore +++ b/.eslintignore @@ -11,6 +11,8 @@ /packages/pigment-css-react/tests/**/fixtures /packages/pigment-css-core/exports/ /packages/pigment-css-core/tests/**/fixtures +/packages/pigment-css-react-new/exports/ +/packages/pigment-css-react-new/tests/**/fixtures /packages/pigment-css-nextjs-plugin/loader.js # Ignore fixtures /packages-internal/scripts/typescript-to-proptypes/test/*/* diff --git a/.eslintrc.js b/.eslintrc.js index d36b0c245..c8de7cfe2 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -83,6 +83,8 @@ module.exports = { ], 'no-use-before-define': 'off', + 'react/react-in-jsx-scope': 'off', + // disabled type-aware linting due to performance considerations '@typescript-eslint/dot-notation': 'off', 'dot-notation': 'error', diff --git a/nx.json b/nx.json index 4734b4e6b..9009a8ec0 100644 --- a/nx.json +++ b/nx.json @@ -9,7 +9,14 @@ "build": { "cache": true, "dependsOn": ["copy-license", "^build"], - "outputs": ["{projectRoot}/build", "{projectRoot}/dist", "{projectRoot}/.next"] + "outputs": [ + "{projectRoot}/build", + "{projectRoot}/processors", + "{projectRoot}/runtime", + "{projectRoot}/dist", + "{projectRoot}/.next", + "{projectRoot}/exports" + ] }, "preview": { "dependsOn": ["^build"] diff --git a/package.json b/package.json index 8416d2c2b..9ffa5b60f 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,6 @@ "validate-declarations": "tsx scripts/validateTypescriptDeclarations.mts" }, "dependencies": { - "@pigment-css/react": "workspace:^", "globby": "^14.0.1" }, "devDependencies": { @@ -108,8 +107,6 @@ "prettier": "^3.3.3", "pretty-quick": "^4.0.0", "process": "^0.11.10", - "react": "18.3.1", - "react-dom": "18.3.1", "react-docgen": "^5.4.3", "remark": "^13.0.0", "rimraf": "^6.0.1", diff --git a/packages/pigment-css-core/package.json b/packages/pigment-css-core/package.json index 3f6adae33..747b9afa2 100644 --- a/packages/pigment-css-core/package.json +++ b/packages/pigment-css-core/package.json @@ -55,11 +55,11 @@ } }, "files": [ - "src", "build", "exports", "processors", "runtime", + "src", "package.json", "styles.css", "LICENSE" diff --git a/packages/pigment-css-core/src/css.ts b/packages/pigment-css-core/src/css.ts index 3086cb168..c4bb91618 100644 --- a/packages/pigment-css-core/src/css.ts +++ b/packages/pigment-css-core/src/css.ts @@ -19,8 +19,17 @@ export type CompoundVariant = VariantNames & { }; type CVAConfig = { + /** + * Documentation: https://pigment-css.com/features/styling#variants + */ variants?: V; + /** + * Documentation: https://pigment-css.com/features/styling#compound-variants + */ compoundVariants?: CompoundVariant[]; + /** + * Documentation: https://pigment-css.com/features/styling#default-variants + */ defaultVariants?: VariantNames; }; @@ -68,6 +77,9 @@ interface CssWithOption { (metadata: M): CssNoOption; } +/** + * Documentation: https://pigment-css.com/features/styling#css + */ const css: CssNoOption & CssWithOption = () => { throw new Error(generateErrorMessage('css')); }; diff --git a/packages/pigment-css-core/src/keyframes.ts b/packages/pigment-css-core/src/keyframes.ts index 4c2baf75f..52209c1cf 100644 --- a/packages/pigment-css-core/src/keyframes.ts +++ b/packages/pigment-css-core/src/keyframes.ts @@ -18,6 +18,9 @@ interface KeyframesWithOption { (metadata: M): KeyframesNoOption; } +/** + * Documentation: https://pigment-css.com/features/styling#keyframes + */ const keyframes: KeyframesWithOption & KeyframesNoOption = () => { throw new Error(generateErrorMessage('keyframes')); }; diff --git a/packages/pigment-css-core/src/processors/css.ts b/packages/pigment-css-core/src/processors/css.ts index 54a83e6fb..3e50bae31 100644 --- a/packages/pigment-css-core/src/processors/css.ts +++ b/packages/pigment-css-core/src/processors/css.ts @@ -10,7 +10,7 @@ * CssProcessor. */ -import { SourceLocation } from '@babel/types'; +import { SourceLocation, TemplateElement } from '@babel/types'; import { type TransformedInternalConfig, type StyleObjectReturn, @@ -21,7 +21,7 @@ import { serializeStyles, valueToLiteral, evaluateClassNameArg, - getCSSVar, + transformProbableCssVar, } from '@pigment-css/utils'; import { CallParam, @@ -119,6 +119,99 @@ export type CssTailProcessorParams = BaseCssProcessorConstructorParams extends [ ? T : never; +function handleTemplateElementOrSimilar( + templateParams: (TemplateElement | ExpressionValue)[], + values: ValueCache, + processor: BaseCssProcessor, +) { + const { themeArgs = {}, pigmentFeatures: { useLayer = true } = {} } = + processor.options as TransformedInternalConfig; + // @ts-ignore @TODO - Fix this. No idea how to initialize a Tagged String array. + const templateStrs: string[] = []; + // @ts-ignore @TODO - Fix this. No idea how to initialize a Tagged String array. + templateStrs.raw = []; + const templateExpressions: Primitive[] = []; + let paramsToIterate = templateParams; + const [firstArg, ...restArgs] = templateParams; + if ('kind' in firstArg && firstArg.kind === ValueType.LAZY) { + const value = values.get(firstArg.ex.name) as string[]; + templateStrs.push(...value); + // @ts-ignore @TODO - Fix this. No idea how to initialize a Tagged String array. + templateStrs.raw.push(...value); + paramsToIterate = restArgs; + } + paramsToIterate.forEach((param) => { + if ('kind' in param) { + switch (param.kind) { + case ValueType.FUNCTION: { + const value = values.get(param.ex.name) as TemplateCallback; + templateExpressions.push(value(themeArgs)); + break; + } + case ValueType.CONST: { + if (typeof param.value === 'string') { + templateExpressions.push(transformProbableCssVar(param.value)); + } else { + templateExpressions.push(param.value); + } + break; + } + case ValueType.LAZY: { + const evaluatedValue = values.get(param.ex.name); + if (typeof evaluatedValue === 'function') { + templateExpressions.push(evaluatedValue(themeArgs)); + } else if (typeof evaluatedValue === 'string') { + templateExpressions.push(transformProbableCssVar(evaluatedValue)); + } else { + templateExpressions.push(evaluatedValue as Primitive); + } + break; + } + default: + break; + } + } else if ('type' in param && param.type === 'TemplateElement') { + templateStrs.push(param.value.cooked as string); + // @ts-ignore + templateStrs.raw.push(param.value.raw); + } + }); + const { styles } = serializeStyles( + templateExpressions.length > 0 ? [templateStrs, ...templateExpressions] : [templateStrs], + ); + + const cssText = useLayer + ? `@layer pigment.base{${processor.wrapStyle(styles, '')}}` + : processor.wrapStyle(styles, ''); + const className = processor.getClassName(); + const rules: Rules = { + [`.${className}`]: { + className, + cssText, + displayName: processor.displayName, + start: processor.location?.start ?? null, + }, + }; + const location = processor.location; + const sourceMapReplacements: Replacements = [ + { + length: cssText.length, + original: { + start: { + column: location?.start.column ?? 0, + line: location?.start.line ?? 0, + }, + end: { + column: location?.end.column ?? 0, + line: location?.end.line ?? 0, + }, + }, + }, + ]; + processor.classNames.push(className); + processor.artifacts.push(['css', [rules, sourceMapReplacements]]); +} + /** * Only deals with css`` or css(metadata)`` calls. */ @@ -138,84 +231,8 @@ export class CssTaggedTemplateProcessor extends BaseCssProcessor { } build(values: ValueCache): void { - const { themeArgs, pigmentFeatures: { useLayer = true } = {} } = this - .options as TransformedInternalConfig; const [, templateParams] = this.templateParam; - // @ts-ignore @TODO - Fix this. No idea how to initialize a Tagged String array. - const templateStrs: string[] = []; - // @ts-ignore @TODO - Fix this. No idea how to initialize a Tagged String array. - templateStrs.raw = []; - const templateExpressions: Primitive[] = []; - templateParams.forEach((param) => { - if ('kind' in param) { - switch (param.kind) { - case ValueType.FUNCTION: { - const value = values.get(param.ex.name) as TemplateCallback; - templateExpressions.push(value(themeArgs)); - break; - } - case ValueType.CONST: { - templateExpressions.push(param.value); - break; - } - case ValueType.LAZY: { - const evaluatedValue = values.get(param.ex.name); - if (typeof evaluatedValue === 'function') { - templateExpressions.push(evaluatedValue(themeArgs)); - } else if ( - typeof evaluatedValue === 'object' && - evaluatedValue && - (evaluatedValue as unknown as Record).isThemeVar - ) { - templateExpressions.push(getCSSVar(evaluatedValue.toString(), true)); - } else { - templateExpressions.push(evaluatedValue as Primitive); - } - break; - } - default: - break; - } - } else if ('type' in param && param.type === 'TemplateElement') { - templateStrs.push(param.value.cooked as string); - // @ts-ignore - templateStrs.raw.push(param.value.raw); - } - }); - const { styles } = serializeStyles( - templateExpressions.length > 0 ? [templateStrs, ...templateExpressions] : [templateStrs], - ); - - const cssText = useLayer - ? `@layer pigment.base{${this.wrapStyle(styles, '')}}` - : this.wrapStyle(styles, ''); - const className = this.getClassName(); - const rules: Rules = { - [`.${className}`]: { - className, - cssText, - displayName: this.displayName, - start: this.location?.start ?? null, - }, - }; - const location = this.location; - const sourceMapReplacements: Replacements = [ - { - length: cssText.length, - original: { - start: { - column: location?.start.column ?? 0, - line: location?.start.line ?? 0, - }, - end: { - column: location?.end.column ?? 0, - line: location?.end.line ?? 0, - }, - }, - }, - ]; - this.classNames.push(className); - this.artifacts.push(['css', [rules, sourceMapReplacements]]); + handleTemplateElementOrSimilar(templateParams, values, this); } } @@ -232,21 +249,44 @@ export class CssObjectProcessor extends BaseCssProcessor { getDependencies(): ExpressionValue[] { const [, ...params] = this.callParam; - return params.flat().filter((param) => 'kind' in param); + return params.flat().filter((param) => 'kind' in param && param.kind !== ValueType.CONST); + } + + isMaybeTransformedTemplateLiteral(values: ValueCache): boolean { + const [, firstArg, ...restArgs] = this.callParam; + if (!('kind' in firstArg) || firstArg.kind === ValueType.CONST) { + return false; + } + const firstArgVal = values.get(firstArg.ex.name); + if (Array.isArray(firstArgVal) && restArgs.length === firstArgVal.length - 1) { + return true; + } + return false; + } + + private buildForTransformedTemplateTag(values: ValueCache) { + const [, ...templateParams] = this.callParam; + handleTemplateElementOrSimilar(templateParams, values, this); } build(values: ValueCache): void { + if (this.isMaybeTransformedTemplateLiteral(values)) { + this.buildForTransformedTemplateTag(values); + return; + } const [, ...callParams] = this.callParam; const { themeArgs, pigmentFeatures: { useLayer = true } = {} } = this .options as TransformedInternalConfig; - const evaluatedValues = (callParams as (LazyValue | FunctionValue)[]).map((param) => - values.get(param.ex.name), + const evaluatedValues = (callParams as ExpressionValue[]).map((param) => + param.kind === ValueType.CONST ? param.value : values.get(param.ex.name), ); let stylesList: (object | Function)[]; // let metadata: any; // check for css(metadata, [styles]) or css(metadata, style) call const locations: (SourceLocation | null | undefined)[] = []; + // Remove this condition as this supports an older API that has since been + // removed from TS support. if ( evaluatedValues.length === 2 && evaluatedValues[0] && diff --git a/packages/pigment-css-core/tests/css/fixtures/css.input.js b/packages/pigment-css-core/tests/css/fixtures/css.input.js index 7780b2813..c9641849f 100644 --- a/packages/pigment-css-core/tests/css/fixtures/css.input.js +++ b/packages/pigment-css-core/tests/css/fixtures/css.input.js @@ -165,3 +165,10 @@ export const cls6 = css(({ theme }) => ({ }, ], })); + +export const cls7 = css( + { + color: '$palette.main', + }, + `display: black`, +); diff --git a/packages/pigment-css-core/tests/css/fixtures/css.output.css b/packages/pigment-css-core/tests/css/fixtures/css.output.css index e4b080848..f5e610214 100644 --- a/packages/pigment-css-core/tests/css/fixtures/css.output.css +++ b/packages/pigment-css-core/tests/css/fixtures/css.output.css @@ -22,4 +22,6 @@ @layer pigment.variants{.c1qxs0o9-palette-primary{color:red;border-color:pink;}} @layer pigment.variants{.c1qxs0o9-palette-secondary{color:red;border-color:pink;}} @layer pigment.compoundvariants{.c1qxs0o9-cv{border-width:1px;}} -/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9wYWNrYWdlcy9waWdtZW50LWNzcy1jb3JlL3Rlc3RzL2Nzcy9maXh0dXJlcy9jc3MuaW5wdXQuanMiXSwibmFtZXMiOlsiLmdxdmMzd2UiLCIuY3gzenRwZSIsIi5jMWFxOTlvNiIsIi5jMWFxOTlvNi0xIiwiLlRlc3QtY2xhc3MiLCIuVGVzdC1jbGFzczIiLCIuVGVzdC1jbGFzczItMSIsIi5UZXN0LWNsYXNzMi0yIiwiLlRlc3QtY2xhc3MyLTMiLCIuVGVzdC1jbGFzczItc2l6ZS1zbWFsbC0xIiwiLlRlc3QtY2xhc3MyLXNpemUtbWVkaXVtLTEiLCIuVGVzdC1jbGFzczItc2l6ZS1sYXJnZS0xIiwiLlRlc3QtY2xhc3MzIiwiLlRlc3QtY2xhc3MzLXNpemUtc21hbGwiLCIuVGVzdC1jbGFzczMtc2l6ZS1tZWRpdW0iLCIuVGVzdC1jbGFzczMtc2l6ZS1sYXJnZSIsIi5UZXN0LWNsYXNzMy1jb2xvci1wcmltYXJ5IiwiLlRlc3QtY2xhc3MzLWNvbG9yLXNlY29uZGFyeSIsIi5UZXN0LWNsYXNzMy1jdiIsIi5UZXN0LWNsYXNzMy1jdi0xIiwiLmMxcXhzMG85IiwiLmMxcXhzMG85LXBhbGV0dGUtcHJpbWFyeSIsIi5jMXF4czBvOS1wYWxldHRlLXNlY29uZGFyeSIsIi5jMXF4czBvOS1jdiJdLCJtYXBwaW5ncyI6IkFBS21DQTtBQU1mQztBQVNsQkM7QUFLQUM7QUFPa0JDO0FBVWxCQztBQU1BQztBQTJCQUM7QUFNQUM7QUF2Q0FDO0FBTUFDO0FBMkJBQztBQWNvREM7QUFBQUM7QUFBQUM7QUFBQUM7QUFBQUM7QUFBQUM7QUFBQUM7QUFBQUM7QUFxRDlCQztBQUFBQztBQUFBQztBQUFBQyIsImZpbGUiOiIvcGFja2FnZXMvcGlnbWVudC1jc3MtY29yZS90ZXN0cy9jc3MvZml4dHVyZXMvY3NzLmlucHV0LmNzcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGNzcywga2V5ZnJhbWVzIH0gZnJvbSAnQHBpZ21lbnQtY3NzL2NvcmUnO1xuaW1wb3J0IHsgdCB9IGZyb20gJ0BwaWdtZW50LWNzcy90aGVtZSc7XG5cbmNvbnN0IGFiID0gJ2FsaWNlYmx1ZSc7XG5cbmNvbnN0IGdyYWRpZW50S2V5ZnJhbWUgPSBrZXlmcmFtZXMoKHsgdGhlbWUgfSkgPT4gKHtcbiAgJzUwJSc6IHtcbiAgICBiYWNrZ3JvdW5kOiAnZ3JlZW4nLFxuICB9LFxufSkpO1xuXG5leHBvcnQgY29uc3QgY2xzMSA9IGNzc2BcbiAgLS0tZmxleDogMTtcbiAgY29sb3I6ICR7KHsgdGhlbWUgfSkgPT4gdGhlbWUucGFsZXR0ZS5wcmltYXJ5Lm1haW59O1xuICBmb250LXNpemU6ICR7KHsgdGhlbWUgfSkgPT4gdGhlbWUuc2l6ZS5mb250LmgxfTtcbiAgYW5pbWF0aW9uLW5hbWU6ICR7Z3JhZGllbnRLZXlmcmFtZX07XG4gIGJhY2tncm91bmQtY29sb3I6ICR7YWJ9O1xuYDtcblxuZXhwb3J0IGNvbnN0IGNsczIgPSBjc3MoXG4gIHtcbiAgICAkJGZsZXg6IDIxLFxuICAgICQkdGVzdFZhcjogJ3JlZCcsXG4gICAgYm9yZGVyOiAnMXB4IHNvbGlkICQkdGVzdFZhcicsXG4gIH0sXG4gIHtcbiAgICAkJGZsZXg6IDIyLFxuICAgICQkdGVzdFZhcjE6ICdyZWQnLFxuICAgIGJvcmRlcjogJzFweCBzb2xpZCAkJHRlc3RWYXIxJyxcbiAgfSxcbik7XG5cbmV4cG9ydCBjb25zdCBjbHMzID0gY3NzKHtcbiAgY2xhc3NOYW1lOiAnVGVzdC1jbGFzcycsXG59KWBcbiAgLS0tZmxleDogMztcbiAgY29sb3I6ICR7KHsgdGhlbWUgfSkgPT4gdGhlbWUucGFsZXR0ZS5wcmltYXJ5Lm1haW59O1xuICBmb250LXNpemU6ICR7KHsgdGhlbWUgfSkgPT4gdGhlbWUuc2l6ZS5mb250LmgxfTtcbiAgYmFja2dyb3VuZC1jb2xvcjogJHthYn07XG5gO1xuXG5leHBvcnQgY29uc3QgY2xzNCA9IGNzcyh7IGNsYXNzTmFtZTogJ1Rlc3QtY2xhc3MyJyB9KShcbiAge1xuICAgICQkZmxleDogNDEsXG4gICAgJCR0ZXN0VmFyOiAncmVkJyxcbiAgICBiYWNrZ3JvdW5kQ29sb3I6ICckJHRlc3RWYXInLFxuICAgIGJvcmRlcjogYDFweCBzb2xpZCAke3QoJyRwYWxldHRlLnByaW1hcnkubWFpbicpfWAsXG4gIH0sXG4gICh7IHRoZW1lIH0pID0+ICh7XG4gICAgJCRmbGV4OiA0MixcbiAgICBjb2xvcjogdGhlbWUucGFsZXR0ZS5wcmltYXJ5Lm1haW4sXG4gICAgZm9udFNpemU6IHRoZW1lLnNpemUuZm9udC5oMSxcbiAgICBiYWNrZ3JvdW5kQ29sb3I6IGFiLFxuICAgIHZhcmlhbnRzOiB7XG4gICAgICBzaXplOiB7XG4gICAgICAgIHNtYWxsOiB7XG4gICAgICAgICAgJCRmbGV4OiA0MjEsXG4gICAgICAgICAgcGFkZGluZzogMCxcbiAgICAgICAgICBtYXJnaW46IDAsXG4gICAgICAgICAgYm9yZGVyQ29sb3I6IHRoZW1lLnBhbGV0dGUucHJpbWFyeS5tYWluLFxuICAgICAgICB9LFxuICAgICAgICBtZWRpdW06IHtcbiAgICAgICAgICAkJGZsZXg6IDQyMixcbiAgICAgICAgICBwYWRkaW5nOiA1LFxuICAgICAgICB9LFxuICAgICAgICBsYXJnZToge1xuICAgICAgICAgICQkZmxleDogNDIzLFxuICAgICAgICAgIHBhZGRpbmc6IDEwLFxuICAgICAgICB9LFxuICAgICAgfSxcbiAgICB9LFxuICAgIGRlZmF1bHRWYXJpYW50czoge1xuICAgICAgc2l6ZTogJ21lZGl1bScsXG4gICAgfSxcbiAgfSksXG4gICh7IHRoZW1lIH0pID0+IGBcbiAgICAtLS1mbGV4OiA0MztcbiAgICBjb2xvcjogJHt0aGVtZS5wYWxldHRlLnByaW1hcnkubWFpbn07XG4gICAgZm9udC1zaXplOiAke3RoZW1lLnNpemUuZm9udC5oMX07XG4gICAgYmFja2dyb3VuZC1jb2xvcjogJHthYn07XG4gIGAsXG4gIGBcbiAgICAtLS1mbGV4OiA0NDtcbiAgICBjb2xvcjogcmVkO1xuICAgIGZvbnQtc2l6ZTogMXJlbTtcbiAgICBiYWNrZ3JvdW5kLWNvbG9yOiAke2FifTtcbiAgYCxcbik7XG5cbmV4cG9ydCBjb25zdCBjbHM1ID0gY3NzKHsgY2xhc3NOYW1lOiAnVGVzdC1jbGFzczMnIH0pKCh7IHRoZW1lIH0pID0+ICh7XG4gICQkZmxleDogNTEsXG4gICQkdGVzdFZhcjogJ3JlZCcsXG4gIGJhY2tncm91bmRDb2xvcjogJyQkdGVzdFZhcicsXG4gIGJvcmRlcjogYDFweCBzb2xpZCAke3QoJyRwYWxldHRlLnByaW1hcnkubWFpbicpfWAsXG4gIHZhcmlhbnRzOiB7XG4gICAgc2l6ZToge1xuICAgICAgc21hbGw6IHtcbiAgICAgICAgJCRmbGV4OiA1MixcbiAgICAgICAgcGFkZGluZzogMCxcbiAgICAgICAgbWFyZ2luOiAwLFxuICAgICAgICBib3JkZXJDb2xvcjogdGhlbWUucGFsZXR0ZS5wcmltYXJ5Lm1haW4sXG4gICAgICB9LFxuICAgICAgbWVkaXVtOiB7XG4gICAgICAgICQkZmxleDogNTMsXG4gICAgICAgIHBhZGRpbmc6IDUsXG4gICAgICB9LFxuICAgICAgbGFyZ2U6IHtcbiAgICAgICAgJCRmbGV4OiA1NCxcbiAgICAgICAgcGFkZGluZzogMTAsXG4gICAgICB9LFxuICAgIH0sXG4gICAgY29sb3I6IHtcbiAgICAgIHByaW1hcnk6IHtcbiAgICAgICAgJCRmbGV4OiA1NSxcbiAgICAgICAgY29sb3I6ICdncmVlbicsXG4gICAgICB9LFxuICAgICAgc2Vjb25kYXJ5OiB7XG4gICAgICAgICQkZmxleDogNTYsXG4gICAgICAgIGNvbG9yOiAnYmx1ZScsXG4gICAgICB9LFxuICAgIH0sXG4gIH0sXG4gIGNvbXBvdW5kVmFyaWFudHM6IFtcbiAgICB7XG4gICAgICBzaXplOiAnc21hbGwnLFxuICAgICAgY29sb3I6ICdwcmltYXJ5JyxcbiAgICAgIGNzczoge1xuICAgICAgICAkJGZsZXg6IDU3LFxuICAgICAgICBib3JkZXJSYWRpdXM6ICcxMDAlJyxcbiAgICAgIH0sXG4gICAgfSxcbiAgICB7XG4gICAgICBzaXplOiAnbGFyZ2UnLFxuICAgICAgY29sb3I6ICdwcmltYXJ5JyxcbiAgICAgIGNzczoge1xuICAgICAgICAkJGZsZXg6IDU4LFxuICAgICAgICBib3JkZXJSYWRpdXM6ICcxMDAlJyxcbiAgICAgIH0sXG4gICAgfSxcbiAgXSxcbn0pKTtcblxuZXhwb3J0IGNvbnN0IGNsczYgPSBjc3MoKHsgdGhlbWUgfSkgPT4gKHtcbiAgY29sb3I6ICckcGFsZXR0ZS5tYWluJyxcbiAgYmFja2dyb3VuZENvbG9yOiB0aGVtZS5wYWxldHRlLm1haW4xLFxuICBib3JkZXI6IGAxcHggc29saWQgJHt0KCckcGFsZXR0ZS5tYWluJyl9YCxcbiAgdmFyaWFudHM6IHtcbiAgICBwYWxldHRlOiB7XG4gICAgICBwcmltYXJ5OiB7XG4gICAgICAgIGNvbG9yOiAncmVkJyxcbiAgICAgICAgYm9yZGVyQ29sb3I6ICdwaW5rJyxcbiAgICAgIH0sXG4gICAgICBzZWNvbmRhcnk6IHtcbiAgICAgICAgY29sb3I6ICdyZWQnLFxuICAgICAgICBib3JkZXJDb2xvcjogJ3BpbmsnLFxuICAgICAgfSxcbiAgICB9LFxuICB9LFxuICBjb21wb3VuZFZhcmlhbnRzOiBbXG4gICAge1xuICAgICAgcGFsZXR0ZTogJ3NlY29uZGFyeScsXG4gICAgICBjc3M6IHtcbiAgICAgICAgYm9yZGVyV2lkdGg6IDEsXG4gICAgICB9LFxuICAgIH0sXG4gIF0sXG59KSk7XG4iXX0=*/ \ No newline at end of file +@layer pigment.base{.c10qevxu{color:var(--palette-main);}} +@layer pigment.base{.c10qevxu-1{display:black;}} +/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9wYWNrYWdlcy9waWdtZW50LWNzcy1jb3JlL3Rlc3RzL2Nzcy9maXh0dXJlcy9jc3MuaW5wdXQuanMiXSwibmFtZXMiOlsiLmdxdmMzd2UiLCIuY3gzenRwZSIsIi5jMWFxOTlvNiIsIi5jMWFxOTlvNi0xIiwiLlRlc3QtY2xhc3MiLCIuVGVzdC1jbGFzczIiLCIuVGVzdC1jbGFzczItMSIsIi5UZXN0LWNsYXNzMi0yIiwiLlRlc3QtY2xhc3MyLTMiLCIuVGVzdC1jbGFzczItc2l6ZS1zbWFsbC0xIiwiLlRlc3QtY2xhc3MyLXNpemUtbWVkaXVtLTEiLCIuVGVzdC1jbGFzczItc2l6ZS1sYXJnZS0xIiwiLlRlc3QtY2xhc3MzIiwiLlRlc3QtY2xhc3MzLXNpemUtc21hbGwiLCIuVGVzdC1jbGFzczMtc2l6ZS1tZWRpdW0iLCIuVGVzdC1jbGFzczMtc2l6ZS1sYXJnZSIsIi5UZXN0LWNsYXNzMy1jb2xvci1wcmltYXJ5IiwiLlRlc3QtY2xhc3MzLWNvbG9yLXNlY29uZGFyeSIsIi5UZXN0LWNsYXNzMy1jdiIsIi5UZXN0LWNsYXNzMy1jdi0xIiwiLmMxcXhzMG85IiwiLmMxcXhzMG85LXBhbGV0dGUtcHJpbWFyeSIsIi5jMXF4czBvOS1wYWxldHRlLXNlY29uZGFyeSIsIi5jMXF4czBvOS1jdiIsIi5jMTBxZXZ4dSIsIi5jMTBxZXZ4dS0xIl0sIm1hcHBpbmdzIjoiQUFLbUNBO0FBTWZDO0FBU2xCQztBQUtBQztBQU9rQkM7QUFVbEJDO0FBTUFDO0FBMkJBQztBQU1BQztBQXZDQUM7QUFNQUM7QUEyQkFDO0FBY29EQztBQUFBQztBQUFBQztBQUFBQztBQUFBQztBQUFBQztBQUFBQztBQUFBQztBQXFEOUJDO0FBQUFDO0FBQUFDO0FBQUFDO0FBMkJ0QkM7QUFHQUMiLCJmaWxlIjoiL3BhY2thZ2VzL3BpZ21lbnQtY3NzLWNvcmUvdGVzdHMvY3NzL2ZpeHR1cmVzL2Nzcy5pbnB1dC5jc3MiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBjc3MsIGtleWZyYW1lcyB9IGZyb20gJ0BwaWdtZW50LWNzcy9jb3JlJztcbmltcG9ydCB7IHQgfSBmcm9tICdAcGlnbWVudC1jc3MvdGhlbWUnO1xuXG5jb25zdCBhYiA9ICdhbGljZWJsdWUnO1xuXG5jb25zdCBncmFkaWVudEtleWZyYW1lID0ga2V5ZnJhbWVzKCh7IHRoZW1lIH0pID0+ICh7XG4gICc1MCUnOiB7XG4gICAgYmFja2dyb3VuZDogJ2dyZWVuJyxcbiAgfSxcbn0pKTtcblxuZXhwb3J0IGNvbnN0IGNsczEgPSBjc3NgXG4gIC0tLWZsZXg6IDE7XG4gIGNvbG9yOiAkeyh7IHRoZW1lIH0pID0+IHRoZW1lLnBhbGV0dGUucHJpbWFyeS5tYWlufTtcbiAgZm9udC1zaXplOiAkeyh7IHRoZW1lIH0pID0+IHRoZW1lLnNpemUuZm9udC5oMX07XG4gIGFuaW1hdGlvbi1uYW1lOiAke2dyYWRpZW50S2V5ZnJhbWV9O1xuICBiYWNrZ3JvdW5kLWNvbG9yOiAke2FifTtcbmA7XG5cbmV4cG9ydCBjb25zdCBjbHMyID0gY3NzKFxuICB7XG4gICAgJCRmbGV4OiAyMSxcbiAgICAkJHRlc3RWYXI6ICdyZWQnLFxuICAgIGJvcmRlcjogJzFweCBzb2xpZCAkJHRlc3RWYXInLFxuICB9LFxuICB7XG4gICAgJCRmbGV4OiAyMixcbiAgICAkJHRlc3RWYXIxOiAncmVkJyxcbiAgICBib3JkZXI6ICcxcHggc29saWQgJCR0ZXN0VmFyMScsXG4gIH0sXG4pO1xuXG5leHBvcnQgY29uc3QgY2xzMyA9IGNzcyh7XG4gIGNsYXNzTmFtZTogJ1Rlc3QtY2xhc3MnLFxufSlgXG4gIC0tLWZsZXg6IDM7XG4gIGNvbG9yOiAkeyh7IHRoZW1lIH0pID0+IHRoZW1lLnBhbGV0dGUucHJpbWFyeS5tYWlufTtcbiAgZm9udC1zaXplOiAkeyh7IHRoZW1lIH0pID0+IHRoZW1lLnNpemUuZm9udC5oMX07XG4gIGJhY2tncm91bmQtY29sb3I6ICR7YWJ9O1xuYDtcblxuZXhwb3J0IGNvbnN0IGNsczQgPSBjc3MoeyBjbGFzc05hbWU6ICdUZXN0LWNsYXNzMicgfSkoXG4gIHtcbiAgICAkJGZsZXg6IDQxLFxuICAgICQkdGVzdFZhcjogJ3JlZCcsXG4gICAgYmFja2dyb3VuZENvbG9yOiAnJCR0ZXN0VmFyJyxcbiAgICBib3JkZXI6IGAxcHggc29saWQgJHt0KCckcGFsZXR0ZS5wcmltYXJ5Lm1haW4nKX1gLFxuICB9LFxuICAoeyB0aGVtZSB9KSA9PiAoe1xuICAgICQkZmxleDogNDIsXG4gICAgY29sb3I6IHRoZW1lLnBhbGV0dGUucHJpbWFyeS5tYWluLFxuICAgIGZvbnRTaXplOiB0aGVtZS5zaXplLmZvbnQuaDEsXG4gICAgYmFja2dyb3VuZENvbG9yOiBhYixcbiAgICB2YXJpYW50czoge1xuICAgICAgc2l6ZToge1xuICAgICAgICBzbWFsbDoge1xuICAgICAgICAgICQkZmxleDogNDIxLFxuICAgICAgICAgIHBhZGRpbmc6IDAsXG4gICAgICAgICAgbWFyZ2luOiAwLFxuICAgICAgICAgIGJvcmRlckNvbG9yOiB0aGVtZS5wYWxldHRlLnByaW1hcnkubWFpbixcbiAgICAgICAgfSxcbiAgICAgICAgbWVkaXVtOiB7XG4gICAgICAgICAgJCRmbGV4OiA0MjIsXG4gICAgICAgICAgcGFkZGluZzogNSxcbiAgICAgICAgfSxcbiAgICAgICAgbGFyZ2U6IHtcbiAgICAgICAgICAkJGZsZXg6IDQyMyxcbiAgICAgICAgICBwYWRkaW5nOiAxMCxcbiAgICAgICAgfSxcbiAgICAgIH0sXG4gICAgfSxcbiAgICBkZWZhdWx0VmFyaWFudHM6IHtcbiAgICAgIHNpemU6ICdtZWRpdW0nLFxuICAgIH0sXG4gIH0pLFxuICAoeyB0aGVtZSB9KSA9PiBgXG4gICAgLS0tZmxleDogNDM7XG4gICAgY29sb3I6ICR7dGhlbWUucGFsZXR0ZS5wcmltYXJ5Lm1haW59O1xuICAgIGZvbnQtc2l6ZTogJHt0aGVtZS5zaXplLmZvbnQuaDF9O1xuICAgIGJhY2tncm91bmQtY29sb3I6ICR7YWJ9O1xuICBgLFxuICBgXG4gICAgLS0tZmxleDogNDQ7XG4gICAgY29sb3I6IHJlZDtcbiAgICBmb250LXNpemU6IDFyZW07XG4gICAgYmFja2dyb3VuZC1jb2xvcjogJHthYn07XG4gIGAsXG4pO1xuXG5leHBvcnQgY29uc3QgY2xzNSA9IGNzcyh7IGNsYXNzTmFtZTogJ1Rlc3QtY2xhc3MzJyB9KSgoeyB0aGVtZSB9KSA9PiAoe1xuICAkJGZsZXg6IDUxLFxuICAkJHRlc3RWYXI6ICdyZWQnLFxuICBiYWNrZ3JvdW5kQ29sb3I6ICckJHRlc3RWYXInLFxuICBib3JkZXI6IGAxcHggc29saWQgJHt0KCckcGFsZXR0ZS5wcmltYXJ5Lm1haW4nKX1gLFxuICB2YXJpYW50czoge1xuICAgIHNpemU6IHtcbiAgICAgIHNtYWxsOiB7XG4gICAgICAgICQkZmxleDogNTIsXG4gICAgICAgIHBhZGRpbmc6IDAsXG4gICAgICAgIG1hcmdpbjogMCxcbiAgICAgICAgYm9yZGVyQ29sb3I6IHRoZW1lLnBhbGV0dGUucHJpbWFyeS5tYWluLFxuICAgICAgfSxcbiAgICAgIG1lZGl1bToge1xuICAgICAgICAkJGZsZXg6IDUzLFxuICAgICAgICBwYWRkaW5nOiA1LFxuICAgICAgfSxcbiAgICAgIGxhcmdlOiB7XG4gICAgICAgICQkZmxleDogNTQsXG4gICAgICAgIHBhZGRpbmc6IDEwLFxuICAgICAgfSxcbiAgICB9LFxuICAgIGNvbG9yOiB7XG4gICAgICBwcmltYXJ5OiB7XG4gICAgICAgICQkZmxleDogNTUsXG4gICAgICAgIGNvbG9yOiAnZ3JlZW4nLFxuICAgICAgfSxcbiAgICAgIHNlY29uZGFyeToge1xuICAgICAgICAkJGZsZXg6IDU2LFxuICAgICAgICBjb2xvcjogJ2JsdWUnLFxuICAgICAgfSxcbiAgICB9LFxuICB9LFxuICBjb21wb3VuZFZhcmlhbnRzOiBbXG4gICAge1xuICAgICAgc2l6ZTogJ3NtYWxsJyxcbiAgICAgIGNvbG9yOiAncHJpbWFyeScsXG4gICAgICBjc3M6IHtcbiAgICAgICAgJCRmbGV4OiA1NyxcbiAgICAgICAgYm9yZGVyUmFkaXVzOiAnMTAwJScsXG4gICAgICB9LFxuICAgIH0sXG4gICAge1xuICAgICAgc2l6ZTogJ2xhcmdlJyxcbiAgICAgIGNvbG9yOiAncHJpbWFyeScsXG4gICAgICBjc3M6IHtcbiAgICAgICAgJCRmbGV4OiA1OCxcbiAgICAgICAgYm9yZGVyUmFkaXVzOiAnMTAwJScsXG4gICAgICB9LFxuICAgIH0sXG4gIF0sXG59KSk7XG5cbmV4cG9ydCBjb25zdCBjbHM2ID0gY3NzKCh7IHRoZW1lIH0pID0+ICh7XG4gIGNvbG9yOiAnJHBhbGV0dGUubWFpbicsXG4gIGJhY2tncm91bmRDb2xvcjogdGhlbWUucGFsZXR0ZS5tYWluMSxcbiAgYm9yZGVyOiBgMXB4IHNvbGlkICR7dCgnJHBhbGV0dGUubWFpbicpfWAsXG4gIHZhcmlhbnRzOiB7XG4gICAgcGFsZXR0ZToge1xuICAgICAgcHJpbWFyeToge1xuICAgICAgICBjb2xvcjogJ3JlZCcsXG4gICAgICAgIGJvcmRlckNvbG9yOiAncGluaycsXG4gICAgICB9LFxuICAgICAgc2Vjb25kYXJ5OiB7XG4gICAgICAgIGNvbG9yOiAncmVkJyxcbiAgICAgICAgYm9yZGVyQ29sb3I6ICdwaW5rJyxcbiAgICAgIH0sXG4gICAgfSxcbiAgfSxcbiAgY29tcG91bmRWYXJpYW50czogW1xuICAgIHtcbiAgICAgIHBhbGV0dGU6ICdzZWNvbmRhcnknLFxuICAgICAgY3NzOiB7XG4gICAgICAgIGJvcmRlcldpZHRoOiAxLFxuICAgICAgfSxcbiAgICB9LFxuICBdLFxufSkpO1xuXG5leHBvcnQgY29uc3QgY2xzNyA9IGNzcyhcbiAge1xuICAgIGNvbG9yOiAnJHBhbGV0dGUubWFpbicsXG4gIH0sXG4gIGBkaXNwbGF5OiBibGFja2AsXG4pO1xuIl19*/ \ No newline at end of file diff --git a/packages/pigment-css-core/tests/css/fixtures/css.output.js b/packages/pigment-css-core/tests/css/fixtures/css.output.js index b83256194..e295ff5c6 100644 --- a/packages/pigment-css-core/tests/css/fixtures/css.output.js +++ b/packages/pigment-css-core/tests/css/fixtures/css.output.js @@ -5,6 +5,7 @@ import { css as _css4, css as _css5, css as _css6, + css as _css7, } from '@pigment-css/core/runtime'; export const cls1 = /*#__PURE__*/ _css({ classes: 'cx3ztpe', @@ -113,3 +114,6 @@ export const cls6 = /*#__PURE__*/ _css6({ }, ], }); +export const cls7 = /*#__PURE__*/ _css7({ + classes: 'c10qevxu c10qevxu-1', +}); diff --git a/packages/pigment-css-react-new/.gitignore b/packages/pigment-css-react-new/.gitignore new file mode 100644 index 000000000..5a1c9bc1b --- /dev/null +++ b/packages/pigment-css-react-new/.gitignore @@ -0,0 +1,2 @@ +processors/ +runtime/ diff --git a/packages/pigment-css-react-new/README.md b/packages/pigment-css-react-new/README.md new file mode 100644 index 000000000..f00cca524 --- /dev/null +++ b/packages/pigment-css-react-new/README.md @@ -0,0 +1,29 @@ +# Pigment CSS + +Pigment CSS is a zero-runtime CSS-in-JS library that extracts the colocated styles to their own CSS files at build time. + +## Getting started + +Pigment CSS supports Next.js and Vite with support for more bundlers in the future. + +### Why choose Pigment CSS + +Thanks to recent advancements in CSS (like CSS variables and `color-mix()`), "traditional" CSS-in-JS solutions that process styles at runtime are no longer required for unlocking features like color transformations and theme variables which are necessary for maintaining a sophisticated design system. + +Pigment CSS addresses the needs of the modern React developer by providing a zero-runtime CSS-in-JS styling solution as a successor to tools like Emotion and styled-components. + +Compared to its predecessors, Pigment CSS offers improved DX and runtime performance (though at the cost of increased build time) while also being compatible with React Server Components. +Pigment CSS is built on top of [WyW-in-JS](https://wyw-in-js.dev/), enabling to provide the smoothest possible experience for Material UI users when migrating from Emotion in v5 to Pigment CSS in v6. + +### Installation + + + +```bash +npm install @pigment-css/core +npm install --save-dev @pigment-css/nextjs-plugin +``` + + + +For more information and getting started guide, check the [repository README.md](https://github.com/mui/pigment-css). diff --git a/packages/pigment-css-react-new/exports/css.js b/packages/pigment-css-react-new/exports/css.js new file mode 100644 index 000000000..4707c749b --- /dev/null +++ b/packages/pigment-css-react-new/exports/css.js @@ -0,0 +1,5 @@ +Object.defineProperty(exports, '__esModule', { + value: true, +}); + +exports.default = require('../processors/css').StyledCssProcessor; diff --git a/packages/pigment-css-react-new/exports/styled.js b/packages/pigment-css-react-new/exports/styled.js new file mode 100644 index 000000000..937d484a8 --- /dev/null +++ b/packages/pigment-css-react-new/exports/styled.js @@ -0,0 +1,5 @@ +Object.defineProperty(exports, '__esModule', { + value: true, +}); + +exports.default = require('../processors/styled').StyledProcessor; diff --git a/packages/pigment-css-react-new/package.json b/packages/pigment-css-react-new/package.json new file mode 100644 index 000000000..b7f59d0a6 --- /dev/null +++ b/packages/pigment-css-react-new/package.json @@ -0,0 +1,152 @@ +{ + "name": "@pigment-css/react-new", + "version": "0.0.27", + "main": "build/index.js", + "module": "build/index.mjs", + "types": "build/index.d.ts", + "author": "MUI Team", + "description": "A zero-runtime CSS-in-JS library to be used with React.", + "repository": { + "type": "git", + "url": "git+https://github.com/mui/pigment-css.git", + "directory": "packages/pigment-css-react-new" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/mui/pigment-css/issues" + }, + "homepage": "https://github.com/mui/pigment-css/tree/master/README.md", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "scripts": { + "clean": "rimraf build", + "watch": "tsup --watch --clean false", + "copy-license": "node ../../scripts/pigment-license.mjs", + "build": "tsup", + "test": "cd ../../ && cross-env NODE_ENV=test BABEL_ENV=coverage nyc --reporter=text mocha 'packages/pigment-css-react-new/**/*.test.{js,ts,tsx}'", + "test:update": "cd ../../ && cross-env NODE_ENV=test UPDATE_FIXTURES=true mocha 'packages/pigment-css-react-new/**/*.test.{js,ts,tsx}'", + "test:ci": "cd ../../ && cross-env NODE_ENV=test BABEL_ENV=coverage nyc --reporter=lcov --report-dir=./coverage/pigment-css-react-new mocha 'packages/pigment-css-react-new/**/*.test.{js,ts,tsx}'", + "typescript": "tsc --noEmit -p ." + }, + "dependencies": { + "@babel/plugin-syntax-jsx": "^7.25.9", + "@babel/types": "^7.25.8", + "@emotion/is-prop-valid": "^1.3.1", + "@pigment-css/core": "workspace:*", + "@pigment-css/utils": "workspace:*", + "@pigment-css/theme": "workspace:^", + "@wyw-in-js/processor-utils": "^0.6.0", + "@wyw-in-js/shared": "^0.6.0", + "@wyw-in-js/transform": "^0.6.0", + "csstype": "^3.1.3" + }, + "peerDependencies": { + "react": "^17 || ^18 || ^19 || ^19.0.0-rc" + }, + "devDependencies": { + "@testing-library/react": "^16.2.0", + "@types/react": "^19.0.2", + "@types/chai": "^4.3.14", + "@types/mocha": "^10.0.6", + "chai": "^4.4.1", + "prettier": "^3.3.3", + "react": "^19.0.0" + }, + "sideEffects": false, + "publishConfig": { + "access": "public" + }, + "wyw-in-js": { + "tags": { + "styled": "./exports/styled.js", + "keyframes": "@pigment-css/core/exports/keyframes", + "css": "./exports/css.js" + } + }, + "files": [ + "build", + "exports", + "processors", + "runtime", + "src", + "package.json", + "styles.css", + "LICENSE" + ], + "exports": { + ".": { + "require": { + "types": "./build/index.d.ts", + "default": "./build/index.js" + }, + "import": { + "types": "./build/index.d.mts", + "default": "./build/index.mjs" + } + }, + "./package.json": "./package.json", + "./styles.css": "./styles.css", + "./processors/css": { + "require": { + "types": "./processors/css.d.ts", + "default": "./processors/css.js" + }, + "import": { + "types": "./processors/css.d.mts", + "default": "./processors/css.mjs" + } + }, + "./processors/styled": { + "require": { + "types": "./processors/styled.d.ts", + "default": "./processors/styled.js" + }, + "import": { + "types": "./processors/styled.d.mts", + "default": "./processors/styled.mjs" + } + }, + "./exports/*": { + "default": "./exports/*.js" + }, + "./runtime": { + "require": { + "types": "./runtime/index.d.ts", + "default": "./runtime/index.js" + }, + "import": { + "types": "./runtime/index.d.mts", + "default": "./runtime/index.mjs" + } + } + }, + "nx": { + "targets": { + "test": { + "cache": false, + "dependsOn": [ + "build" + ] + }, + "test:update": { + "cache": false, + "dependsOn": [ + "build" + ] + }, + "test:ci": { + "cache": false, + "dependsOn": [ + "build" + ] + }, + "build": { + "outputs": [ + "{projectRoot}/build" + ] + } + } + } +} diff --git a/packages/pigment-css-react-new/src/index.ts b/packages/pigment-css-react-new/src/index.ts new file mode 100644 index 000000000..5cb0efb9b --- /dev/null +++ b/packages/pigment-css-react-new/src/index.ts @@ -0,0 +1,2 @@ +export * from '@pigment-css/core'; +export * from './styled'; diff --git a/packages/pigment-css-react-new/src/processors/css.ts b/packages/pigment-css-react-new/src/processors/css.ts new file mode 100644 index 000000000..7d9832ae4 --- /dev/null +++ b/packages/pigment-css-react-new/src/processors/css.ts @@ -0,0 +1,5 @@ +import { CssProcessor } from '@pigment-css/core/processors/css'; + +export class StyledCssProcessor extends CssProcessor { + basePath = `${process.env.PACKAGE_NAME}/runtime`; +} diff --git a/packages/pigment-css-react-new/src/processors/styled.ts b/packages/pigment-css-react-new/src/processors/styled.ts new file mode 100644 index 000000000..f4dd7e434 --- /dev/null +++ b/packages/pigment-css-react-new/src/processors/styled.ts @@ -0,0 +1,117 @@ +import { Identifier } from '@babel/types'; +import { evaluateClassNameArg } from '@pigment-css/utils'; +import { + type CallParam, + type Expression, + type MemberParam, + type Params, + type TailProcessorParams, + validateParams, +} from '@wyw-in-js/processor-utils'; +import { ValueType } from '@wyw-in-js/shared'; +import { CssProcessor } from '@pigment-css/core/processors/css'; +import { BaseInterface } from '@pigment-css/core/css'; + +export type TemplateCallback = (params: Record | undefined) => string | number; + +type WrappedNode = + | string + | { + node: Identifier; + source: string; + }; + +const REACT_COMPONENT = '$$reactComponent'; + +export class StyledProcessor extends CssProcessor { + tagName: WrappedNode = ''; + + // eslint-disable-next-line class-methods-use-this + get packageName() { + return process.env.PACKAGE_NAME as string; + } + + basePath = `${this.packageName}/runtime`; + + constructor(params: Params, ...args: TailProcessorParams) { + const [callee, callOrMember, callOrTemplate] = params; + super([callee, callOrTemplate], ...args); + + if (params.length === 3) { + validateParams( + params, + ['callee', ['call', 'member'], ['call', 'template']], + `Invalid use of ${this.tagSource.imported} function.`, + ); + + this.setTagName(callOrMember as CallParam | MemberParam); + } else { + throw new Error(`${this.packageName} Invalid call to ${this.tagSource.imported} function.`); + } + } + + private setTagName(param: CallParam | MemberParam) { + if (param[0] === 'member') { + this.tagName = param[1]; + } else { + const [, element, callOpt] = param; + switch (element.kind) { + case ValueType.CONST: { + if (typeof element.value === 'string') { + this.tagName = element.value; + } + break; + } + case ValueType.LAZY: { + this.tagName = { + node: element.ex, + source: element.source, + }; + this.dependencies.push(element); + break; + } + case ValueType.FUNCTION: { + this.tagName = REACT_COMPONENT; + break; + } + default: + break; + } + + if (callOpt) { + this.processor.staticClass = evaluateClassNameArg(callOpt.source) as BaseInterface; + } + } + } + + getBaseClass(): string { + return this.className; + } + + get asSelector(): string { + return this.processor.getBaseClass(); + } + + get value(): Expression { + return this.astService.stringLiteral(`.${this.processor.getBaseClass()}`); + } + + createReplacement() { + const t = this.astService; + const callId = t.addNamedImport('styled', this.getImportPath()); + const elementOrComponent = (() => { + if (typeof this.tagName === 'string') { + if (this.tagName === REACT_COMPONENT) { + return t.arrowFunctionExpression([], t.blockStatement([])); + } + return t.stringLiteral(this.tagName); + } + if (this.tagName?.node) { + return t.callExpression(t.identifier(this.tagName.node.name), []); + } + return t.nullLiteral(); + })(); + const firstCall = t.callExpression(callId, [elementOrComponent]); + return t.callExpression(firstCall, [this.getStyleArgs()]); + } +} diff --git a/packages/pigment-css-react-new/src/runtime/index.ts b/packages/pigment-css-react-new/src/runtime/index.ts new file mode 100644 index 000000000..85fa192f1 --- /dev/null +++ b/packages/pigment-css-react-new/src/runtime/index.ts @@ -0,0 +1,2 @@ +export * from '@pigment-css/core/runtime'; +export * from './styled'; diff --git a/packages/pigment-css-react-new/src/runtime/styled.tsx b/packages/pigment-css-react-new/src/runtime/styled.tsx new file mode 100644 index 000000000..14afbdcaa --- /dev/null +++ b/packages/pigment-css-react-new/src/runtime/styled.tsx @@ -0,0 +1,148 @@ +import type { Primitive } from '@pigment-css/core'; +import { type ClassInfo, css } from '@pigment-css/core/runtime'; +import * as React from 'react'; +import isPropValid from '@emotion/is-prop-valid'; + +type StyledInfo = ClassInfo & { + displayName?: string; + vars?: Record Primitive, boolean]>; +}; + +function isHtmlTag(tag: unknown): tag is string { + return ( + typeof tag === 'string' && + // 96 is one less than the char code + // for "a" so this is checking that + // it's a lowercase character + tag.charCodeAt(0) > 96 + ); +} + +function defaultShouldForwardProp(propName: string): boolean { + // if first character is $ + if (propName.charCodeAt(0) === 36) { + return false; + } + if (propName === 'as') { + return false; + } + return true; +} + +function shouldForwardProp(propName: string) { + if (defaultShouldForwardProp(propName)) { + return isPropValid(propName); + } + return false; +} + +function getStyle(props: ClassInfo['defaultVariants'], vars: StyledInfo['vars']) { + const newStyle: Record = {}; + if (!props || !vars) { + return newStyle; + } + // eslint-disable-next-line no-restricted-syntax + for (const key in vars) { + if (!vars.hasOwnProperty(key)) { + continue; + } + const [variableFunction, isUnitLess] = vars[key]; + const value = variableFunction(props); + if (typeof value === 'undefined') { + continue; + } + if (typeof value === 'string' || isUnitLess) { + newStyle[key] = value; + } else { + newStyle[key] = `${value}px`; + } + } + return newStyle; +} + +export function styled(tag: T) { + if (process.env.NODE_ENV === 'development') { + if (tag === undefined) { + throw new Error( + 'You are trying to create a styled element with an undefined component.\nYou may have forgotten to import it.', + ); + } + } + const shouldForwardPropLocal = + typeof tag === 'string' ? shouldForwardProp : defaultShouldForwardProp; + let shouldUseAs = !shouldForwardPropLocal('as'); + + // @ts-expect-error + // eslint-disable-next-line no-underscore-dangle + if (typeof tag !== 'string' && tag.__styled_by_pigment_css) { + // If the tag is a Pigment styled component, + // render the styled component and pass the `as` prop down + shouldUseAs = false; + } + + function scopedStyled({ + classes, + variants = [], + defaultVariants = {}, + vars, + displayName = '', + }: StyledInfo) { + const cssFn = css({ + classes, + variants, + defaultVariants, + }); + const baseClasses = cssFn(); + + const StyledComponent = React.forwardRef< + React.ComponentRef, + React.ComponentPropsWithoutRef & { + as?: React.ElementType; + className?: string; + style?: React.CSSProperties; + } + >(function render(props, ref) { + const newProps: Record = {}; + const Component = (shouldUseAs && props.as) || tag; + const propClass = props.className; + const propStyle = props.style; + let shouldForwardPropComponent = shouldForwardPropLocal; + + // Reassign `shouldForwardProp` if incoming `as` prop is a React component + if (!isHtmlTag(Component)) { + shouldForwardPropComponent = defaultShouldForwardProp; + } + + // eslint-disable-next-line no-restricted-syntax + for (const key in props) { + if (shouldForwardPropComponent(key)) { + newProps[key] = props[key]; + } + } + newProps.className = variants.length ? cssFn(props) : baseClasses; + if (propClass) { + newProps.className = `${newProps.className} ${propClass}`; + } + newProps.style = { + ...propStyle, + ...getStyle(props, vars), + }; + + return ; + }); + + if (displayName) { + StyledComponent.displayName = displayName; + } else { + StyledComponent.displayName = `Styled(${typeof tag === 'string' ? tag : 'Pigment'})`; + } + + // @ts-expect-error No TS check required + // eslint-disable-next-line no-underscore-dangle + StyledComponent.__styled_by_pigment_css = true; + + return StyledComponent; + } + + return scopedStyled; +} diff --git a/packages/pigment-css-react-new/src/styled.ts b/packages/pigment-css-react-new/src/styled.ts new file mode 100644 index 000000000..74acd83d0 --- /dev/null +++ b/packages/pigment-css-react-new/src/styled.ts @@ -0,0 +1,88 @@ +import type * as React from 'react'; +import { + Variants, + BaseInterface, + CssArg, + VariantNames, + Primitive, + generateErrorMessage, +} from '@pigment-css/core'; + +export type NoInfer = [T][T extends any ? 0 : never]; +type FastOmit = { + [K in keyof T as K extends U ? never : K]: T[K]; +}; + +export type Substitute = FastOmit & B; + +export interface RequiredProps { + className?: string; + style?: React.CSSProperties; +} + +export type PolymorphicComponentProps< + Props extends {}, + AsTarget extends React.ElementType | undefined, + AsTargetProps extends object = AsTarget extends React.ElementType + ? React.ComponentPropsWithRef + : {}, +> = NoInfer, 'as'>> & { + as?: AsTarget; + children?: React.ReactNode; +}; + +export interface PolymorphicComponent + extends React.ForwardRefExoticComponent { + ( + props: PolymorphicComponentProps, + ): React.JSX.Element; +} + +type StyledArgument = CssArg; + +interface StyledComponent extends PolymorphicComponent { + defaultProps?: Partial | undefined; + toString: () => string; +} + +interface StyledOptions extends BaseInterface {} + +export interface CreateStyledComponent< + Component extends React.ElementType, + OuterProps extends object, +> { + ( + arg: TemplateStringsArray, + ...templateArgs: (StyledComponent | Primitive | ((props: Props) => Primitive))[] + ): StyledComponent> & (Component extends string ? {} : Component); + + ( + ...styles: Array> + ): StyledComponent : V>> & + (Component extends string ? {} : Component); +} + +export interface CreateStyled { + < + TagOrComponent extends React.ElementType, + FinalProps extends {} = React.ComponentPropsWithRef, + >( + tag: TagOrComponent, + options?: StyledOptions, + ): CreateStyledComponent; +} + +export type CreateStyledIndex = { + [Key in keyof React.JSX.IntrinsicElements]: CreateStyledComponent< + Key, + React.JSX.IntrinsicElements[Key] + >; +}; + +/** + * Documentation: https://pigment-css.com/features/styling#styled + */ +// @ts-expect-error The implementation is is different than the user API +export const styled: CreateStyled & CreateStyledIndex = () => { + throw new Error(generateErrorMessage('styled')); +}; diff --git a/packages/pigment-css-react-new/src/sx.d.ts b/packages/pigment-css-react-new/src/sx.d.ts new file mode 100644 index 000000000..ba4394ece --- /dev/null +++ b/packages/pigment-css-react-new/src/sx.d.ts @@ -0,0 +1,10 @@ +import type { CSSObjectNoCallback, ThemeArgs } from '@pigment-css/core'; + +type GetTheme = Argument extends { theme: infer Theme } ? Theme : never; + +export type SxProp = + | CSSObjectNoCallback + | ((theme: GetTheme) => CSSObjectNoCallback) + | ReadonlyArray) => CSSObjectNoCallback)>; + +export default function sx(arg: SxProp, componentClass?: string): string; diff --git a/packages/pigment-css-react-new/styles.css b/packages/pigment-css-react-new/styles.css new file mode 100644 index 000000000..87774e547 --- /dev/null +++ b/packages/pigment-css-react-new/styles.css @@ -0,0 +1,3 @@ +/** + * Placeholder css file where theme contents will be injected by the bundler + */ diff --git a/packages/pigment-css-react-new/tests/styled/fixtures/dummy-component.fixture.js b/packages/pigment-css-react-new/tests/styled/fixtures/dummy-component.fixture.js new file mode 100644 index 000000000..ade8d6738 --- /dev/null +++ b/packages/pigment-css-react-new/tests/styled/fixtures/dummy-component.fixture.js @@ -0,0 +1,3 @@ +export function TestComponent() { + return

Hello

; +} diff --git a/packages/pigment-css-react-new/tests/styled/fixtures/styled-import-replacement.input.js b/packages/pigment-css-react-new/tests/styled/fixtures/styled-import-replacement.input.js new file mode 100644 index 000000000..26a8765ab --- /dev/null +++ b/packages/pigment-css-react-new/tests/styled/fixtures/styled-import-replacement.input.js @@ -0,0 +1,37 @@ +import { styled, keyframes } from '@pigment-css/react-new'; + +function TestComponent() { + return

Hello World

; +} + +const rotateKeyframe = keyframes({ + from: { + transform: 'translateX(0%)', + }, + to: { + transform: 'translateX(100%)', + }, +}); + +const Component = styled.div({ + animation: `${rotateKeyframe} 2s ease-out 0s infinite`, + marginLeft: 10, +}); + +export const SliderRail = styled(TestComponent, { + name: 'MuiSlider', + slot: 'Rail', +})` + display: block; + position: absolute; + left: 0; + top: 0; + border-top-left-radius: 3px; +`; + +const SliderRail2 = styled.span` + ${SliderRail} { + padding-inline-start: none; + margin: 0px 10px 10px 30px; + } +`; diff --git a/packages/pigment-css-react-new/tests/styled/fixtures/styled-import-replacement.output.css b/packages/pigment-css-react-new/tests/styled/fixtures/styled-import-replacement.output.css new file mode 100644 index 000000000..ad7f38c22 --- /dev/null +++ b/packages/pigment-css-react-new/tests/styled/fixtures/styled-import-replacement.output.css @@ -0,0 +1,5 @@ +@layer pigment.base{@keyframes r1gkqk0p{from{transform:translateX(0%);}to{transform:translateX(100%);}}} +@layer pigment.base{.cor4ri8{animation:r1gkqk0p 2s ease-out 0s infinite;margin-left:10px;}} +@layer pigment.base{.sn659j3{display:block;position:absolute;left:0;top:0;border-top-left-radius:3px;}} +@layer pigment.base{.s9z6p60 .sn659j3{padding-inline-start:none;margin:0px 10px 10px 30px;}} +/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9wYWNrYWdlcy9waWdtZW50LWNzcy1yZWFjdC1uZXcvdGVzdHMvc3R5bGVkL2ZpeHR1cmVzL3N0eWxlZC1pbXBvcnQtcmVwbGFjZW1lbnQuaW5wdXQuanMiXSwibmFtZXMiOlsiLnIxZ2txazBwIiwiLmNvcjRyaTgiLCIuc242NTlqMyIsIi5zOXo2cDYwIl0sIm1hcHBpbmdzIjoiQUFNaUNBO0FBU0pDO0FBS0hDO0FBV05DIiwiZmlsZSI6Ii9wYWNrYWdlcy9waWdtZW50LWNzcy1yZWFjdC1uZXcvdGVzdHMvc3R5bGVkL2ZpeHR1cmVzL3N0eWxlZC1pbXBvcnQtcmVwbGFjZW1lbnQuaW5wdXQuY3NzIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgc3R5bGVkLCBrZXlmcmFtZXMgfSBmcm9tICdAcGlnbWVudC1jc3MvcmVhY3QtbmV3JztcblxuZnVuY3Rpb24gVGVzdENvbXBvbmVudCgpIHtcbiAgcmV0dXJuIDxoMT5IZWxsbyBXb3JsZDwvaDE+O1xufVxuXG5jb25zdCByb3RhdGVLZXlmcmFtZSA9IGtleWZyYW1lcyh7XG4gIGZyb206IHtcbiAgICB0cmFuc2Zvcm06ICd0cmFuc2xhdGVYKDAlKScsXG4gIH0sXG4gIHRvOiB7XG4gICAgdHJhbnNmb3JtOiAndHJhbnNsYXRlWCgxMDAlKScsXG4gIH0sXG59KTtcblxuY29uc3QgQ29tcG9uZW50ID0gc3R5bGVkLmRpdih7XG4gIGFuaW1hdGlvbjogYCR7cm90YXRlS2V5ZnJhbWV9IDJzIGVhc2Utb3V0IDBzIGluZmluaXRlYCxcbiAgbWFyZ2luTGVmdDogMTAsXG59KTtcblxuZXhwb3J0IGNvbnN0IFNsaWRlclJhaWwgPSBzdHlsZWQoVGVzdENvbXBvbmVudCwge1xuICBuYW1lOiAnTXVpU2xpZGVyJyxcbiAgc2xvdDogJ1JhaWwnLFxufSlgXG4gIGRpc3BsYXk6IGJsb2NrO1xuICBwb3NpdGlvbjogYWJzb2x1dGU7XG4gIGxlZnQ6IDA7XG4gIHRvcDogMDtcbiAgYm9yZGVyLXRvcC1sZWZ0LXJhZGl1czogM3B4O1xuYDtcblxuY29uc3QgU2xpZGVyUmFpbDIgPSBzdHlsZWQuc3BhbmBcbiAgJHtTbGlkZXJSYWlsfSB7XG4gICAgcGFkZGluZy1pbmxpbmUtc3RhcnQ6IG5vbmU7XG4gICAgbWFyZ2luOiAwcHggMTBweCAxMHB4IDMwcHg7XG4gIH1cbmA7XG4iXX0=*/ \ No newline at end of file diff --git a/packages/pigment-css-react-new/tests/styled/fixtures/styled-import-replacement.output.js b/packages/pigment-css-react-new/tests/styled/fixtures/styled-import-replacement.output.js new file mode 100644 index 000000000..784ed810a --- /dev/null +++ b/packages/pigment-css-react-new/tests/styled/fixtures/styled-import-replacement.output.js @@ -0,0 +1,14 @@ +import { styled as _styled, styled as _styled2, styled as _styled3 } from '@my-lib/react/styled'; +function TestComponent() { + return

Hello World

; +} +const Component = /*#__PURE__*/ _styled('div')({ + classes: 'cor4ri8', +}); +const _exp3 = /*#__PURE__*/ () => TestComponent; +export const SliderRail = /*#__PURE__*/ _styled2(_exp3())({ + classes: 'sn659j3', +}); +const SliderRail2 = /*#__PURE__*/ _styled3('span')({ + classes: 's9z6p60', +}); diff --git a/packages/pigment-css-react-new/tests/styled/fixtures/styled-no-layer.input.js b/packages/pigment-css-react-new/tests/styled/fixtures/styled-no-layer.input.js new file mode 100644 index 000000000..a538e091a --- /dev/null +++ b/packages/pigment-css-react-new/tests/styled/fixtures/styled-no-layer.input.js @@ -0,0 +1,108 @@ +import { styled, keyframes } from '@pigment-css/react-new'; + +const rotateKeyframe = keyframes({ + from: { + transform: 'rotate(360deg)', + }, + to: { + transform: 'rotate(0deg)', + }, +}); + +function TestComponent() { + return

Hello

; +} + +const StyledTest = styled(TestComponent)({ + // gets converted to css variable -> ---id: 0px + $$id: 0, + display: 'block', + position: 'absolute', + borderRadius: 'inherit', + color(props) { + return props.size === 'small' ? 'red' : 'blue'; + }, + variants: { + size: { + small: { + $$id: '01', + padding: 0, + margin: 0, + borderColor: 'red', + }, + medium: { + $$id: '02', + padding: 5, + }, + large: { + $$id: '03', + padding: 10, + }, + }, + }, +}); + +export const SliderRail3 = styled('span', { + name: 'MuiSlider', + slot: 'Rail', +})({ + $$id: 1, + display: 'block', + position: 'absolute', + borderRadius: 'inherit', + backgroundColor: 'currentColor', + opacity: 0.38, +}); + +export const SliderRail = styled('span', { + name: 'MuiSlider', + slot: 'Rail', +})` + ---id: 2; + display: block; + position: absolute; + border-radius: inherit; + background-color: currentColor; + opacity: 0.38; +`; + +const SliderRail5 = styled.span({ + display: 'block', + opacity: 0.38, + [SliderRail]: { + display: 'none', + }, +}); + +const Component = styled.div({ + $$id: 3, + color: '#ff5252', + animation: `${rotateKeyframe} 2s ease-out 0s infinite`, +}); + +const SliderRail2 = styled('span')` + ---id: 4; + display: block; + opacity: 0.38; + ${SliderRail} { + display: none; + } +`; + +const SliderRail4 = styled.span` + ---id: 5; + display: block; + opacity: 0.38; + ${SliderRail} { + display: none; + } +`; + +export function App() { + return ( + + + + + ); +} diff --git a/packages/pigment-css-react-new/tests/styled/fixtures/styled-no-layer.output.css b/packages/pigment-css-react-new/tests/styled/fixtures/styled-no-layer.output.css new file mode 100644 index 000000000..748f1f761 --- /dev/null +++ b/packages/pigment-css-react-new/tests/styled/fixtures/styled-no-layer.output.css @@ -0,0 +1,12 @@ +@keyframes rl9f3t2 {from{transform:rotate(360deg);}to{transform:rotate(0deg);}} +.s1k22dj{---id:0;display:block;position:absolute;border-radius:inherit;color:var(--s1k22dj-1);} +.s1k22dj-size-small{---id:01;padding:0;margin:0;border-color:red;} +.s1k22dj-size-medium{---id:02;padding:5px;} +.s1k22dj-size-large{---id:03;padding:10px;} +.scx6lci{---id:1;display:block;position:absolute;border-radius:inherit;background-color:currentColor;opacity:0.38;} +.s1uutepx{---id:2;display:block;position:absolute;border-radius:inherit;background-color:currentColor;opacity:0.38;} +.s1czomkm{display:block;opacity:0.38;}.s1czomkm .s1uutepx{display:none;} +.camj2o9{---id:3;color:#ff5252;animation:rl9f3t2 2s ease-out 0s infinite;} +.s9jspa8{---id:4;display:block;opacity:0.38;}.s9jspa8 .s1uutepx{display:none;} +.sg47azp{---id:5;display:block;opacity:0.38;}.sg47azp .s1uutepx{display:none;} +/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9wYWNrYWdlcy9waWdtZW50LWNzcy1yZWFjdC1uZXcvdGVzdHMvc3R5bGVkL2ZpeHR1cmVzL3N0eWxlZC1uby1sYXllci5pbnB1dC5qcyJdLCJuYW1lcyI6WyIucmw5ZjN0MiIsIi5zMWsyMmRqIiwiLnMxazIyZGotc2l6ZS1zbWFsbCIsIi5zMWsyMmRqLXNpemUtbWVkaXVtIiwiLnMxazIyZGotc2l6ZS1sYXJnZSIsIi5zY3g2bGNpIiwiLnMxdXV0ZXB4IiwiLnMxY3pvbWttIiwiLmNhbWoybzkiLCIuczlqc3BhOCIsIi5zZzQ3YXpwIl0sIm1hcHBpbmdzIjoiQUFFaUNBO0FBYVFDO0FBQUFDO0FBQUFDO0FBQUFDO0FBZ0N0Q0M7QUFTdUJDO0FBWU1DO0FBUUhDO0FBTVRDO0FBU0FDIiwiZmlsZSI6Ii9wYWNrYWdlcy9waWdtZW50LWNzcy1yZWFjdC1uZXcvdGVzdHMvc3R5bGVkL2ZpeHR1cmVzL3N0eWxlZC1uby1sYXllci5pbnB1dC5jc3MiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBzdHlsZWQsIGtleWZyYW1lcyB9IGZyb20gJ0BwaWdtZW50LWNzcy9yZWFjdC1uZXcnO1xuXG5jb25zdCByb3RhdGVLZXlmcmFtZSA9IGtleWZyYW1lcyh7XG4gIGZyb206IHtcbiAgICB0cmFuc2Zvcm06ICdyb3RhdGUoMzYwZGVnKScsXG4gIH0sXG4gIHRvOiB7XG4gICAgdHJhbnNmb3JtOiAncm90YXRlKDBkZWcpJyxcbiAgfSxcbn0pO1xuXG5mdW5jdGlvbiBUZXN0Q29tcG9uZW50KCkge1xuICByZXR1cm4gPGgxPkhlbGxvPC9oMT47XG59XG5cbmNvbnN0IFN0eWxlZFRlc3QgPSBzdHlsZWQoVGVzdENvbXBvbmVudCkoe1xuICAvLyBnZXRzIGNvbnZlcnRlZCB0byBjc3MgdmFyaWFibGUgLT4gLS0taWQ6IDBweFxuICAkJGlkOiAwLFxuICBkaXNwbGF5OiAnYmxvY2snLFxuICBwb3NpdGlvbjogJ2Fic29sdXRlJyxcbiAgYm9yZGVyUmFkaXVzOiAnaW5oZXJpdCcsXG4gIGNvbG9yKHByb3BzKSB7XG4gICAgcmV0dXJuIHByb3BzLnNpemUgPT09ICdzbWFsbCcgPyAncmVkJyA6ICdibHVlJztcbiAgfSxcbiAgdmFyaWFudHM6IHtcbiAgICBzaXplOiB7XG4gICAgICBzbWFsbDoge1xuICAgICAgICAkJGlkOiAnMDEnLFxuICAgICAgICBwYWRkaW5nOiAwLFxuICAgICAgICBtYXJnaW46IDAsXG4gICAgICAgIGJvcmRlckNvbG9yOiAncmVkJyxcbiAgICAgIH0sXG4gICAgICBtZWRpdW06IHtcbiAgICAgICAgJCRpZDogJzAyJyxcbiAgICAgICAgcGFkZGluZzogNSxcbiAgICAgIH0sXG4gICAgICBsYXJnZToge1xuICAgICAgICAkJGlkOiAnMDMnLFxuICAgICAgICBwYWRkaW5nOiAxMCxcbiAgICAgIH0sXG4gICAgfSxcbiAgfSxcbn0pO1xuXG5leHBvcnQgY29uc3QgU2xpZGVyUmFpbDMgPSBzdHlsZWQoJ3NwYW4nLCB7XG4gIG5hbWU6ICdNdWlTbGlkZXInLFxuICBzbG90OiAnUmFpbCcsXG59KSh7XG4gICQkaWQ6IDEsXG4gIGRpc3BsYXk6ICdibG9jaycsXG4gIHBvc2l0aW9uOiAnYWJzb2x1dGUnLFxuICBib3JkZXJSYWRpdXM6ICdpbmhlcml0JyxcbiAgYmFja2dyb3VuZENvbG9yOiAnY3VycmVudENvbG9yJyxcbiAgb3BhY2l0eTogMC4zOCxcbn0pO1xuXG5leHBvcnQgY29uc3QgU2xpZGVyUmFpbCA9IHN0eWxlZCgnc3BhbicsIHtcbiAgbmFtZTogJ011aVNsaWRlcicsXG4gIHNsb3Q6ICdSYWlsJyxcbn0pYFxuICAtLS1pZDogMjtcbiAgZGlzcGxheTogYmxvY2s7XG4gIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgYm9yZGVyLXJhZGl1czogaW5oZXJpdDtcbiAgYmFja2dyb3VuZC1jb2xvcjogY3VycmVudENvbG9yO1xuICBvcGFjaXR5OiAwLjM4O1xuYDtcblxuY29uc3QgU2xpZGVyUmFpbDUgPSBzdHlsZWQuc3Bhbih7XG4gIGRpc3BsYXk6ICdibG9jaycsXG4gIG9wYWNpdHk6IDAuMzgsXG4gIFtTbGlkZXJSYWlsXToge1xuICAgIGRpc3BsYXk6ICdub25lJyxcbiAgfSxcbn0pO1xuXG5jb25zdCBDb21wb25lbnQgPSBzdHlsZWQuZGl2KHtcbiAgJCRpZDogMyxcbiAgY29sb3I6ICcjZmY1MjUyJyxcbiAgYW5pbWF0aW9uOiBgJHtyb3RhdGVLZXlmcmFtZX0gMnMgZWFzZS1vdXQgMHMgaW5maW5pdGVgLFxufSk7XG5cbmNvbnN0IFNsaWRlclJhaWwyID0gc3R5bGVkKCdzcGFuJylgXG4gIC0tLWlkOiA0O1xuICBkaXNwbGF5OiBibG9jaztcbiAgb3BhY2l0eTogMC4zODtcbiAgJHtTbGlkZXJSYWlsfSB7XG4gICAgZGlzcGxheTogbm9uZTtcbiAgfVxuYDtcblxuY29uc3QgU2xpZGVyUmFpbDQgPSBzdHlsZWQuc3BhbmBcbiAgLS0taWQ6IDU7XG4gIGRpc3BsYXk6IGJsb2NrO1xuICBvcGFjaXR5OiAwLjM4O1xuICAke1NsaWRlclJhaWx9IHtcbiAgICBkaXNwbGF5OiBub25lO1xuICB9XG5gO1xuXG5leHBvcnQgZnVuY3Rpb24gQXBwKCkge1xuICByZXR1cm4gKFxuICAgIDxDb21wb25lbnQ+XG4gICAgICA8U2xpZGVyUmFpbCAvPlxuICAgICAgPFNsaWRlclJhaWwyIC8+XG4gICAgPC9Db21wb25lbnQ+XG4gICk7XG59XG4iXX0=*/ \ No newline at end of file diff --git a/packages/pigment-css-react-new/tests/styled/fixtures/styled-no-layer.output.js b/packages/pigment-css-react-new/tests/styled/fixtures/styled-no-layer.output.js new file mode 100644 index 000000000..ca3e3e5c6 --- /dev/null +++ b/packages/pigment-css-react-new/tests/styled/fixtures/styled-no-layer.output.js @@ -0,0 +1,70 @@ +import { + styled as _styled, + styled as _styled2, + styled as _styled3, + styled as _styled4, + styled as _styled5, + styled as _styled6, + styled as _styled7, +} from '@pigment-css/react-new/runtime'; +function TestComponent() { + return

Hello

; +} +const _exp2 = /*#__PURE__*/ () => TestComponent; +const StyledTest = /*#__PURE__*/ _styled(_exp2())({ + classes: 's1k22dj', + variants: [ + { + $$cls: 's1k22dj-size-small', + props: { + size: 'small', + }, + }, + { + $$cls: 's1k22dj-size-medium', + props: { + size: 'medium', + }, + }, + { + $$cls: 's1k22dj-size-large', + props: { + size: 'large', + }, + }, + ], + vars: { + '--s1k22dj-1': [ + (props) => { + return props.size === 'small' ? 'red' : 'blue'; + }, + 0, + ], + }, +}); +export const SliderRail3 = /*#__PURE__*/ _styled2('span')({ + classes: 'scx6lci', +}); +export const SliderRail = /*#__PURE__*/ _styled3('span')({ + classes: 's1uutepx', +}); +const SliderRail5 = /*#__PURE__*/ _styled4('span')({ + classes: 's1czomkm', +}); +const Component = /*#__PURE__*/ _styled5('div')({ + classes: 'camj2o9', +}); +const SliderRail2 = /*#__PURE__*/ _styled6('span')({ + classes: 's9jspa8', +}); +const SliderRail4 = /*#__PURE__*/ _styled7('span')({ + classes: 'sg47azp', +}); +export function App() { + return ( + + + + + ); +} diff --git a/packages/pigment-css-react-new/tests/styled/fixtures/styled-swc-transformed-tagged-string.input.js b/packages/pigment-css-react-new/tests/styled/fixtures/styled-swc-transformed-tagged-string.input.js new file mode 100644 index 000000000..47723d4bc --- /dev/null +++ b/packages/pigment-css-react-new/tests/styled/fixtures/styled-swc-transformed-tagged-string.input.js @@ -0,0 +1,118 @@ +/** + * This is a pre-transformed file for testing. + */ +import { _ as _tagged_template_literal } from '@swc/helpers/_/_tagged_template_literal'; +import { styled, keyframes } from '@pigment-css/react-new'; + +function _templateObject() { + const data = _tagged_template_literal([ + '\n 0% {\n transform: scale(0);\n opacity: 0.1;\n }\n\n 100% {\n transform: scale(1);\n opacity: 0.3;\n }\n', + ]); + _templateObject = function () { + return data; + }; + return data; +} +function _templateObject1() { + const data = _tagged_template_literal([ + '\n 0% {\n opacity: 1;\n }\n\n 100% {\n opacity: 0;\n }\n', + ]); + _templateObject1 = function () { + return data; + }; + return data; +} +function _templateObject2() { + const data = _tagged_template_literal([ + '\n 0% {\n transform: scale(1);\n }\n\n 50% {\n transform: scale(0.92);\n }\n\n 100% {\n transform: scale(1);\n }\n', + ]); + _templateObject2 = function () { + return data; + }; + return data; +} +function _templateObject3() { + const data = _tagged_template_literal([ + '\n opacity: 0;\n position: absolute;\n\n &.', + ' {\n opacity: 0.3;\n transform: scale(1);\n animation-name: ', + ';\n animation-duration: ', + 'ms;\n animation-timing-function: ', + ';\n }\n\n &.', + ' {\n animation-duration: ', + 'ms;\n }\n\n & .', + ' {\n opacity: 1;\n display: block;\n width: 100%;\n height: 100%;\n border-radius: 50%;\n background-color: currentColor;\n }\n\n & .', + ' {\n opacity: 0;\n animation-name: ', + ';\n animation-duration: ', + 'ms;\n animation-timing-function: ', + ';\n }\n\n & .', + ' {\n position: absolute;\n /* @noflip */\n left: 0px;\n top: 0;\n animation-name: ', + ';\n animation-duration: 2500ms;\n animation-timing-function: ', + ';\n animation-iteration-count: infinite;\n animation-delay: 200ms;\n }\n', + ]); + _templateObject3 = function () { + return data; + }; + return data; +} + +const touchRippleClasses = { + rippleVisible: 'MuiTouchRipple.rippleVisible', + ripplePulsate: 'MuiTouchRipple.ripplePulsate', + child: 'MuiTouchRipple.child', + childLeaving: 'MuiTouchRipple.childLeaving', + childPulsate: 'MuiTouchRipple.childPulsate', +}; + +const enterKeyframe = keyframes(_templateObject()); +const exitKeyframe = keyframes(_templateObject1()); +const pulsateKeyframe = keyframes(_templateObject2()); + +export const TouchRippleRoot = styled('span', { + name: 'MuiTouchRipple', + slot: 'Root', +})({ + overflow: 'hidden', + pointerEvents: 'none', + position: 'absolute', + zIndex: 0, + top: 0, + right: 0, + bottom: 0, + left: 0, + borderRadius: 'inherit', +}); + +// This `styled()` function invokes keyframes. `styled-components` only supports keyframes +// in string templates. Do not convert these styles in JS object as it will break. +export const TouchRippleRipple = styled(Ripple, { + name: 'MuiTouchRipple', + slot: 'Ripple', +})( + _templateObject3(), + touchRippleClasses.rippleVisible, + enterKeyframe, + DURATION, + (param) => { + let { theme } = param; + return theme.transitions.easing.easeInOut; + }, + touchRippleClasses.ripplePulsate, + (param) => { + let { theme } = param; + return theme.transitions.duration.shorter; + }, + touchRippleClasses.child, + touchRippleClasses.childLeaving, + exitKeyframe, + DURATION, + (param) => { + let { theme } = param; + return theme.transitions.easing.easeInOut; + }, + touchRippleClasses.childPulsate, + pulsateKeyframe, + (param) => { + let { theme } = param; + return theme.transitions.easing.easeInOut; + }, +); diff --git a/packages/pigment-css-react-new/tests/styled/fixtures/styled-swc-transformed-tagged-string.output.css b/packages/pigment-css-react-new/tests/styled/fixtures/styled-swc-transformed-tagged-string.output.css new file mode 100644 index 000000000..fa309eaba --- /dev/null +++ b/packages/pigment-css-react-new/tests/styled/fixtures/styled-swc-transformed-tagged-string.output.css @@ -0,0 +1,36 @@ +@keyframes edi25uv { + 0% { + transform: scale(0); + opacity: 0.1; + } + + 100% { + transform: scale(1); + opacity: 0.3; + } +} +@keyframes elx6tt { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + } +} +@keyframes prw41fh { + 0% { + transform: scale(1); + } + + 50% { + transform: scale(0.92); + } + + 100% { + transform: scale(1); + } +} +.tcavtwv{overflow:hidden;pointer-events:none;position:absolute;z-index:0;top:0;right:0;bottom:0;left:0;border-radius:inherit;} +.tblmtgc{opacity:0;position:absolute;}.tblmtgc.MuiTouchRipple.rippleVisible{opacity:0.3;transform:scale(1);animation-name:edi25uv;animation-duration:ms;animation-timing-function:cubic-bezier(0.4, 0, 0.2, 1);}.tblmtgc.MuiTouchRipple.ripplePulsate{animation-duration:200ms;}.tblmtgc .MuiTouchRipple.child{opacity:1;display:block;width:100%;height:100%;border-radius:50%;background-color:currentColor;}.tblmtgc .MuiTouchRipple.childLeaving{opacity:0;animation-name:elx6tt;animation-duration:ms;animation-timing-function:cubic-bezier(0.4, 0, 0.2, 1);}.tblmtgc .MuiTouchRipple.childPulsate{position:absolute;left:0px;top:0;animation-name:prw41fh;animation-duration:2500ms;animation-timing-function:cubic-bezier(0.4, 0, 0.2, 1);animation-iteration-count:infinite;animation-delay:200ms;} +/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9wYWNrYWdlcy9waWdtZW50LWNzcy1yZWFjdC1uZXcvdGVzdHMvc3R5bGVkL2ZpeHR1cmVzL3N0eWxlZC1zd2MtdHJhbnNmb3JtZWQtdGFnZ2VkLXN0cmluZy5pbnB1dC5qcyJdLCJuYW1lcyI6WyIuZWRpMjV1diIsIi5lbHg2dHQiLCIucHJ3NDFmaCIsIi50Y2F2dHd2IiwiLnRibG10Z2MiXSwibWFwcGluZ3MiOiJBQWlFc0JBO0FBQ0RDO0FBQ0dDO0FBS3JCQztBQWM4QkMiLCJmaWxlIjoiL3BhY2thZ2VzL3BpZ21lbnQtY3NzLXJlYWN0LW5ldy90ZXN0cy9zdHlsZWQvZml4dHVyZXMvc3R5bGVkLXN3Yy10cmFuc2Zvcm1lZC10YWdnZWQtc3RyaW5nLmlucHV0LmNzcyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogVGhpcyBpcyBhIHByZS10cmFuc2Zvcm1lZCBmaWxlIGZvciB0ZXN0aW5nLlxuICovXG5pbXBvcnQgeyBfIGFzIF90YWdnZWRfdGVtcGxhdGVfbGl0ZXJhbCB9IGZyb20gJ0Bzd2MvaGVscGVycy9fL190YWdnZWRfdGVtcGxhdGVfbGl0ZXJhbCc7XG5pbXBvcnQgeyBzdHlsZWQsIGtleWZyYW1lcyB9IGZyb20gJ0BwaWdtZW50LWNzcy9yZWFjdC1uZXcnO1xuXG5mdW5jdGlvbiBfdGVtcGxhdGVPYmplY3QoKSB7XG4gIGNvbnN0IGRhdGEgPSBfdGFnZ2VkX3RlbXBsYXRlX2xpdGVyYWwoW1xuICAgICdcXG4gIDAlIHtcXG4gICAgdHJhbnNmb3JtOiBzY2FsZSgwKTtcXG4gICAgb3BhY2l0eTogMC4xO1xcbiAgfVxcblxcbiAgMTAwJSB7XFxuICAgIHRyYW5zZm9ybTogc2NhbGUoMSk7XFxuICAgIG9wYWNpdHk6IDAuMztcXG4gIH1cXG4nLFxuICBdKTtcbiAgX3RlbXBsYXRlT2JqZWN0ID0gZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiBkYXRhO1xuICB9O1xuICByZXR1cm4gZGF0YTtcbn1cbmZ1bmN0aW9uIF90ZW1wbGF0ZU9iamVjdDEoKSB7XG4gIGNvbnN0IGRhdGEgPSBfdGFnZ2VkX3RlbXBsYXRlX2xpdGVyYWwoW1xuICAgICdcXG4gIDAlIHtcXG4gICAgb3BhY2l0eTogMTtcXG4gIH1cXG5cXG4gIDEwMCUge1xcbiAgICBvcGFjaXR5OiAwO1xcbiAgfVxcbicsXG4gIF0pO1xuICBfdGVtcGxhdGVPYmplY3QxID0gZnVuY3Rpb24gKCkge1xuICAgIHJldHVybiBkYXRhO1xuICB9O1xuICByZXR1cm4gZGF0YTtcbn1cbmZ1bmN0aW9uIF90ZW1wbGF0ZU9iamVjdDIoKSB7XG4gIGNvbnN0IGRhdGEgPSBfdGFnZ2VkX3RlbXBsYXRlX2xpdGVyYWwoW1xuICAgICdcXG4gIDAlIHtcXG4gICAgdHJhbnNmb3JtOiBzY2FsZSgxKTtcXG4gIH1cXG5cXG4gIDUwJSB7XFxuICAgIHRyYW5zZm9ybTogc2NhbGUoMC45Mik7XFxuICB9XFxuXFxuICAxMDAlIHtcXG4gICAgdHJhbnNmb3JtOiBzY2FsZSgxKTtcXG4gIH1cXG4nLFxuICBdKTtcbiAgX3RlbXBsYXRlT2JqZWN0MiA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gZGF0YTtcbiAgfTtcbiAgcmV0dXJuIGRhdGE7XG59XG5mdW5jdGlvbiBfdGVtcGxhdGVPYmplY3QzKCkge1xuICBjb25zdCBkYXRhID0gX3RhZ2dlZF90ZW1wbGF0ZV9saXRlcmFsKFtcbiAgICAnXFxuICBvcGFjaXR5OiAwO1xcbiAgcG9zaXRpb246IGFic29sdXRlO1xcblxcbiAgJi4nLFxuICAgICcge1xcbiAgICBvcGFjaXR5OiAwLjM7XFxuICAgIHRyYW5zZm9ybTogc2NhbGUoMSk7XFxuICAgIGFuaW1hdGlvbi1uYW1lOiAnLFxuICAgICc7XFxuICAgIGFuaW1hdGlvbi1kdXJhdGlvbjogJyxcbiAgICAnbXM7XFxuICAgIGFuaW1hdGlvbi10aW1pbmctZnVuY3Rpb246ICcsXG4gICAgJztcXG4gIH1cXG5cXG4gICYuJyxcbiAgICAnIHtcXG4gICAgYW5pbWF0aW9uLWR1cmF0aW9uOiAnLFxuICAgICdtcztcXG4gIH1cXG5cXG4gICYgLicsXG4gICAgJyB7XFxuICAgIG9wYWNpdHk6IDE7XFxuICAgIGRpc3BsYXk6IGJsb2NrO1xcbiAgICB3aWR0aDogMTAwJTtcXG4gICAgaGVpZ2h0OiAxMDAlO1xcbiAgICBib3JkZXItcmFkaXVzOiA1MCU7XFxuICAgIGJhY2tncm91bmQtY29sb3I6IGN1cnJlbnRDb2xvcjtcXG4gIH1cXG5cXG4gICYgLicsXG4gICAgJyB7XFxuICAgIG9wYWNpdHk6IDA7XFxuICAgIGFuaW1hdGlvbi1uYW1lOiAnLFxuICAgICc7XFxuICAgIGFuaW1hdGlvbi1kdXJhdGlvbjogJyxcbiAgICAnbXM7XFxuICAgIGFuaW1hdGlvbi10aW1pbmctZnVuY3Rpb246ICcsXG4gICAgJztcXG4gIH1cXG5cXG4gICYgLicsXG4gICAgJyB7XFxuICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTtcXG4gICAgLyogQG5vZmxpcCAqL1xcbiAgICBsZWZ0OiAwcHg7XFxuICAgIHRvcDogMDtcXG4gICAgYW5pbWF0aW9uLW5hbWU6ICcsXG4gICAgJztcXG4gICAgYW5pbWF0aW9uLWR1cmF0aW9uOiAyNTAwbXM7XFxuICAgIGFuaW1hdGlvbi10aW1pbmctZnVuY3Rpb246ICcsXG4gICAgJztcXG4gICAgYW5pbWF0aW9uLWl0ZXJhdGlvbi1jb3VudDogaW5maW5pdGU7XFxuICAgIGFuaW1hdGlvbi1kZWxheTogMjAwbXM7XFxuICB9XFxuJyxcbiAgXSk7XG4gIF90ZW1wbGF0ZU9iamVjdDMgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIGRhdGE7XG4gIH07XG4gIHJldHVybiBkYXRhO1xufVxuXG5jb25zdCB0b3VjaFJpcHBsZUNsYXNzZXMgPSB7XG4gIHJpcHBsZVZpc2libGU6ICdNdWlUb3VjaFJpcHBsZS5yaXBwbGVWaXNpYmxlJyxcbiAgcmlwcGxlUHVsc2F0ZTogJ011aVRvdWNoUmlwcGxlLnJpcHBsZVB1bHNhdGUnLFxuICBjaGlsZDogJ011aVRvdWNoUmlwcGxlLmNoaWxkJyxcbiAgY2hpbGRMZWF2aW5nOiAnTXVpVG91Y2hSaXBwbGUuY2hpbGRMZWF2aW5nJyxcbiAgY2hpbGRQdWxzYXRlOiAnTXVpVG91Y2hSaXBwbGUuY2hpbGRQdWxzYXRlJyxcbn07XG5cbmNvbnN0IGVudGVyS2V5ZnJhbWUgPSBrZXlmcmFtZXMoX3RlbXBsYXRlT2JqZWN0KCkpO1xuY29uc3QgZXhpdEtleWZyYW1lID0ga2V5ZnJhbWVzKF90ZW1wbGF0ZU9iamVjdDEoKSk7XG5jb25zdCBwdWxzYXRlS2V5ZnJhbWUgPSBrZXlmcmFtZXMoX3RlbXBsYXRlT2JqZWN0MigpKTtcblxuZXhwb3J0IGNvbnN0IFRvdWNoUmlwcGxlUm9vdCA9IHN0eWxlZCgnc3BhbicsIHtcbiAgbmFtZTogJ011aVRvdWNoUmlwcGxlJyxcbiAgc2xvdDogJ1Jvb3QnLFxufSkoe1xuICBvdmVyZmxvdzogJ2hpZGRlbicsXG4gIHBvaW50ZXJFdmVudHM6ICdub25lJyxcbiAgcG9zaXRpb246ICdhYnNvbHV0ZScsXG4gIHpJbmRleDogMCxcbiAgdG9wOiAwLFxuICByaWdodDogMCxcbiAgYm90dG9tOiAwLFxuICBsZWZ0OiAwLFxuICBib3JkZXJSYWRpdXM6ICdpbmhlcml0Jyxcbn0pO1xuXG4vLyBUaGlzIGBzdHlsZWQoKWAgZnVuY3Rpb24gaW52b2tlcyBrZXlmcmFtZXMuIGBzdHlsZWQtY29tcG9uZW50c2Agb25seSBzdXBwb3J0cyBrZXlmcmFtZXNcbi8vIGluIHN0cmluZyB0ZW1wbGF0ZXMuIERvIG5vdCBjb252ZXJ0IHRoZXNlIHN0eWxlcyBpbiBKUyBvYmplY3QgYXMgaXQgd2lsbCBicmVhay5cbmV4cG9ydCBjb25zdCBUb3VjaFJpcHBsZVJpcHBsZSA9IHN0eWxlZChSaXBwbGUsIHtcbiAgbmFtZTogJ011aVRvdWNoUmlwcGxlJyxcbiAgc2xvdDogJ1JpcHBsZScsXG59KShcbiAgX3RlbXBsYXRlT2JqZWN0MygpLFxuICB0b3VjaFJpcHBsZUNsYXNzZXMucmlwcGxlVmlzaWJsZSxcbiAgZW50ZXJLZXlmcmFtZSxcbiAgRFVSQVRJT04sXG4gIChwYXJhbSkgPT4ge1xuICAgIGxldCB7IHRoZW1lIH0gPSBwYXJhbTtcbiAgICByZXR1cm4gdGhlbWUudHJhbnNpdGlvbnMuZWFzaW5nLmVhc2VJbk91dDtcbiAgfSxcbiAgdG91Y2hSaXBwbGVDbGFzc2VzLnJpcHBsZVB1bHNhdGUsXG4gIChwYXJhbSkgPT4ge1xuICAgIGxldCB7IHRoZW1lIH0gPSBwYXJhbTtcbiAgICByZXR1cm4gdGhlbWUudHJhbnNpdGlvbnMuZHVyYXRpb24uc2hvcnRlcjtcbiAgfSxcbiAgdG91Y2hSaXBwbGVDbGFzc2VzLmNoaWxkLFxuICB0b3VjaFJpcHBsZUNsYXNzZXMuY2hpbGRMZWF2aW5nLFxuICBleGl0S2V5ZnJhbWUsXG4gIERVUkFUSU9OLFxuICAocGFyYW0pID0+IHtcbiAgICBsZXQgeyB0aGVtZSB9ID0gcGFyYW07XG4gICAgcmV0dXJuIHRoZW1lLnRyYW5zaXRpb25zLmVhc2luZy5lYXNlSW5PdXQ7XG4gIH0sXG4gIHRvdWNoUmlwcGxlQ2xhc3Nlcy5jaGlsZFB1bHNhdGUsXG4gIHB1bHNhdGVLZXlmcmFtZSxcbiAgKHBhcmFtKSA9PiB7XG4gICAgbGV0IHsgdGhlbWUgfSA9IHBhcmFtO1xuICAgIHJldHVybiB0aGVtZS50cmFuc2l0aW9ucy5lYXNpbmcuZWFzZUluT3V0O1xuICB9LFxuKTtcbiJdfQ==*/ \ No newline at end of file diff --git a/packages/pigment-css-react-new/tests/styled/fixtures/styled-swc-transformed-tagged-string.output.js b/packages/pigment-css-react-new/tests/styled/fixtures/styled-swc-transformed-tagged-string.output.js new file mode 100644 index 000000000..ce6a834ce --- /dev/null +++ b/packages/pigment-css-react-new/tests/styled/fixtures/styled-swc-transformed-tagged-string.output.js @@ -0,0 +1,14 @@ +import { styled as _styled, styled as _styled2 } from '@pigment-css/react-new/runtime'; +/** + * This is a pre-transformed file for testing. + */ +export const TouchRippleRoot = /*#__PURE__*/ _styled('span')({ + classes: 'tcavtwv', +}); + +// This `styled()` function invokes keyframes. `styled-components` only supports keyframes +// in string templates. Do not convert these styles in JS object as it will break. +const _exp6 = /*#__PURE__*/ () => Ripple; +export const TouchRippleRipple = /*#__PURE__*/ _styled2(_exp6())({ + classes: 'tblmtgc', +}); diff --git a/packages/pigment-css-react-new/tests/styled/fixtures/styled.input.js b/packages/pigment-css-react-new/tests/styled/fixtures/styled.input.js new file mode 100644 index 000000000..1255e9d76 --- /dev/null +++ b/packages/pigment-css-react-new/tests/styled/fixtures/styled.input.js @@ -0,0 +1,141 @@ +import { t } from '@pigment-css/theme'; +import { styled, keyframes, css } from '@pigment-css/react-new'; +import { TestComponent } from './dummy-component.fixture'; + +const cls1 = css({ + color: 'red', +}); + +export const rotateKeyframe = keyframes({ className: 'rotate' })({ + from: { + transform: 'rotate(360deg)', + }, + to: { + transform: 'rotate(0deg)', + }, +}); + +const StyledTest = styled(TestComponent, { + className: 'StyledTest', +})({ + $$id: 0, + display: 'block', + position: 'absolute', + borderRadius: 'inherit', + [`.${cls1}`]: { + color: 'blue', + }, + color(props) { + return props.size === 'small' ? 'red' : 'blue'; + }, + variants: { + size: { + small: { + $$id: '01', + padding: 0, + margin: 0, + borderColor: 'red', + }, + medium: { + $$id: '02', + padding: 5, + }, + large: { + $$id: '03', + padding: 10, + }, + }, + }, +}); + +export const SliderRail3 = styled('span', { + name: 'MuiSlider', + slot: 'Rail', +})({ + $$id: 1, + display: 'block', + position: 'absolute', + borderRadius: 'inherit', + backgroundColor: 'currentColor', + opacity: 0.38, +}); + +export const SliderRail = styled('span', { + name: 'MuiSlider', + slot: 'Rail', +})` + ---id: 2; + display: block; + position: absolute; + border-radius: inherit; + background-color: currentColor; + opacity: 0.38; +`; + +const SliderRail5 = styled.span({ + display: 'block', + opacity: 0.38, + [SliderRail]: { + display: 'none', + }, +}); + +const Component = styled.div({ + $$id: 3, + color: '#ff5252', + animation: `${rotateKeyframe} 2s ease-out 0s infinite`, +}); + +const SliderRail2 = styled('span')` + ---id: 4; + display: block; + opacity: 0.38; + ${SliderRail} { + display: none; + } +`; + +const SliderRail4 = styled.span` + ---id: 5; + display: block; + opacity: 0.38; + ${SliderRail} { + display: none; + } +`; + +const ViewPort = styled(SliderRail4)` + max-height: 100vh; + padding-top: 0.75rem; + padding-bottom: 3rem; + padding-left: 1.5rem; + padding-right: calc( + var(--sideNavScrollbarGapLeft) + var(--sideNavScrollbarWidth) / 2 + + var(--sideNavScrollbarThumbWidth) / 2 + ); + + /* Scroll containers are focusable */ + outline: 0; + + .Root:has(&:focus-visible)::before { + content: ''; + inset: 0; + pointer-events: none; + position: absolute; + outline: 2px solid ${t('$color.blue')}; + outline-offset: -2px; + /* Don't inset the outline on the right */ + right: -2px; + } +`; + +export function App() { + return ( + + + + + ); +} + +App.displayName = 'App'; diff --git a/packages/pigment-css-react-new/tests/styled/fixtures/styled.output.css b/packages/pigment-css-react-new/tests/styled/fixtures/styled.output.css new file mode 100644 index 000000000..aac9e2309 --- /dev/null +++ b/packages/pigment-css-react-new/tests/styled/fixtures/styled.output.css @@ -0,0 +1,17 @@ +@layer pigment.base{.c1dgsgnh{color:red;}} +@layer pigment.base{@keyframes rotate{from{transform:rotate(360deg);}to{transform:rotate(0deg);}}} +@layer pigment.base{.StyledTest{---id:0;display:block;position:absolute;border-radius:inherit;color:var(--StyledTest-1);}.StyledTest .c1dgsgnh{color:blue;}} +@layer pigment.variants{.StyledTest-size-small{---id:01;padding:0;margin:0;border-color:red;}} +@layer pigment.variants{.StyledTest-size-medium{---id:02;padding:5px;}} +@layer pigment.variants{.StyledTest-size-large{---id:03;padding:10px;}} +@layer pigment.base{.s4ekdda{---id:1;display:block;position:absolute;border-radius:inherit;background-color:currentColor;opacity:0.38;}} +@layer pigment.base{.sqpzee{---id:2;display:block;position:absolute;border-radius:inherit;background-color:currentColor;opacity:0.38;}} +@layer pigment.base{.s1o48m17{display:block;opacity:0.38;}.s1o48m17 .sqpzee{display:none;}} +@layer pigment.base{.c13e7k7c{---id:3;color:#ff5252;animation:rotate 2s ease-out 0s infinite;}} +@layer pigment.base{.sqzgjb7{---id:4;display:block;opacity:0.38;}.sqzgjb7 .sqpzee{display:none;}} +@layer pigment.base{.sxcjuwu{---id:5;display:block;opacity:0.38;}.sxcjuwu .sqpzee{display:none;}} +@layer pigment.base{.v1x90zfp{max-height:100vh;padding-top:0.75rem;padding-bottom:3rem;padding-left:1.5rem;padding-right:calc( + var(--sideNavScrollbarGapLeft) + var(--sideNavScrollbarWidth) / 2 + + var(--sideNavScrollbarThumbWidth) / 2 + );outline:0;}.Root:has(.v1x90zfp:focus-visible)::before{content:'';inset:0;pointer-events:none;position:absolute;outline:2px solid var(--color-blue);outline-offset:-2px;right:-2px;}} +/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9wYWNrYWdlcy9waWdtZW50LWNzcy1yZWFjdC1uZXcvdGVzdHMvc3R5bGVkL2ZpeHR1cmVzL3N0eWxlZC5pbnB1dC5qcyJdLCJuYW1lcyI6WyIuYzFkZ3NnbmgiLCIucm90YXRlIiwiLlN0eWxlZFRlc3QiLCIuU3R5bGVkVGVzdC1zaXplLXNtYWxsIiwiLlN0eWxlZFRlc3Qtc2l6ZS1tZWRpdW0iLCIuU3R5bGVkVGVzdC1zaXplLWxhcmdlIiwiLnM0ZWtkZGEiLCIuc3FwemVlIiwiLnMxbzQ4bTE3IiwiLmMxM2U3azdjIiwiLnNxemdqYjciLCIuc3hjanV3dSIsIi52MXg5MHpmcCJdLCJtYXBwaW5ncyI6IkFBSWlCQTtBQUlnREM7QUFXOURDO0FBQUFDO0FBQUFDO0FBQUFDO0FBa0NBQztBQVN1QkM7QUFZTUM7QUFRSEM7QUFNVEM7QUFTQUM7QUFTSEMiLCJmaWxlIjoiL3BhY2thZ2VzL3BpZ21lbnQtY3NzLXJlYWN0LW5ldy90ZXN0cy9zdHlsZWQvZml4dHVyZXMvc3R5bGVkLmlucHV0LmNzcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHQgfSBmcm9tICdAcGlnbWVudC1jc3MvdGhlbWUnO1xuaW1wb3J0IHsgc3R5bGVkLCBrZXlmcmFtZXMsIGNzcyB9IGZyb20gJ0BwaWdtZW50LWNzcy9yZWFjdC1uZXcnO1xuaW1wb3J0IHsgVGVzdENvbXBvbmVudCB9IGZyb20gJy4vZHVtbXktY29tcG9uZW50LmZpeHR1cmUnO1xuXG5jb25zdCBjbHMxID0gY3NzKHtcbiAgY29sb3I6ICdyZWQnLFxufSk7XG5cbmV4cG9ydCBjb25zdCByb3RhdGVLZXlmcmFtZSA9IGtleWZyYW1lcyh7IGNsYXNzTmFtZTogJ3JvdGF0ZScgfSkoe1xuICBmcm9tOiB7XG4gICAgdHJhbnNmb3JtOiAncm90YXRlKDM2MGRlZyknLFxuICB9LFxuICB0bzoge1xuICAgIHRyYW5zZm9ybTogJ3JvdGF0ZSgwZGVnKScsXG4gIH0sXG59KTtcblxuY29uc3QgU3R5bGVkVGVzdCA9IHN0eWxlZChUZXN0Q29tcG9uZW50LCB7XG4gIGNsYXNzTmFtZTogJ1N0eWxlZFRlc3QnLFxufSkoe1xuICAkJGlkOiAwLFxuICBkaXNwbGF5OiAnYmxvY2snLFxuICBwb3NpdGlvbjogJ2Fic29sdXRlJyxcbiAgYm9yZGVyUmFkaXVzOiAnaW5oZXJpdCcsXG4gIFtgLiR7Y2xzMX1gXToge1xuICAgIGNvbG9yOiAnYmx1ZScsXG4gIH0sXG4gIGNvbG9yKHByb3BzKSB7XG4gICAgcmV0dXJuIHByb3BzLnNpemUgPT09ICdzbWFsbCcgPyAncmVkJyA6ICdibHVlJztcbiAgfSxcbiAgdmFyaWFudHM6IHtcbiAgICBzaXplOiB7XG4gICAgICBzbWFsbDoge1xuICAgICAgICAkJGlkOiAnMDEnLFxuICAgICAgICBwYWRkaW5nOiAwLFxuICAgICAgICBtYXJnaW46IDAsXG4gICAgICAgIGJvcmRlckNvbG9yOiAncmVkJyxcbiAgICAgIH0sXG4gICAgICBtZWRpdW06IHtcbiAgICAgICAgJCRpZDogJzAyJyxcbiAgICAgICAgcGFkZGluZzogNSxcbiAgICAgIH0sXG4gICAgICBsYXJnZToge1xuICAgICAgICAkJGlkOiAnMDMnLFxuICAgICAgICBwYWRkaW5nOiAxMCxcbiAgICAgIH0sXG4gICAgfSxcbiAgfSxcbn0pO1xuXG5leHBvcnQgY29uc3QgU2xpZGVyUmFpbDMgPSBzdHlsZWQoJ3NwYW4nLCB7XG4gIG5hbWU6ICdNdWlTbGlkZXInLFxuICBzbG90OiAnUmFpbCcsXG59KSh7XG4gICQkaWQ6IDEsXG4gIGRpc3BsYXk6ICdibG9jaycsXG4gIHBvc2l0aW9uOiAnYWJzb2x1dGUnLFxuICBib3JkZXJSYWRpdXM6ICdpbmhlcml0JyxcbiAgYmFja2dyb3VuZENvbG9yOiAnY3VycmVudENvbG9yJyxcbiAgb3BhY2l0eTogMC4zOCxcbn0pO1xuXG5leHBvcnQgY29uc3QgU2xpZGVyUmFpbCA9IHN0eWxlZCgnc3BhbicsIHtcbiAgbmFtZTogJ011aVNsaWRlcicsXG4gIHNsb3Q6ICdSYWlsJyxcbn0pYFxuICAtLS1pZDogMjtcbiAgZGlzcGxheTogYmxvY2s7XG4gIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgYm9yZGVyLXJhZGl1czogaW5oZXJpdDtcbiAgYmFja2dyb3VuZC1jb2xvcjogY3VycmVudENvbG9yO1xuICBvcGFjaXR5OiAwLjM4O1xuYDtcblxuY29uc3QgU2xpZGVyUmFpbDUgPSBzdHlsZWQuc3Bhbih7XG4gIGRpc3BsYXk6ICdibG9jaycsXG4gIG9wYWNpdHk6IDAuMzgsXG4gIFtTbGlkZXJSYWlsXToge1xuICAgIGRpc3BsYXk6ICdub25lJyxcbiAgfSxcbn0pO1xuXG5jb25zdCBDb21wb25lbnQgPSBzdHlsZWQuZGl2KHtcbiAgJCRpZDogMyxcbiAgY29sb3I6ICcjZmY1MjUyJyxcbiAgYW5pbWF0aW9uOiBgJHtyb3RhdGVLZXlmcmFtZX0gMnMgZWFzZS1vdXQgMHMgaW5maW5pdGVgLFxufSk7XG5cbmNvbnN0IFNsaWRlclJhaWwyID0gc3R5bGVkKCdzcGFuJylgXG4gIC0tLWlkOiA0O1xuICBkaXNwbGF5OiBibG9jaztcbiAgb3BhY2l0eTogMC4zODtcbiAgJHtTbGlkZXJSYWlsfSB7XG4gICAgZGlzcGxheTogbm9uZTtcbiAgfVxuYDtcblxuY29uc3QgU2xpZGVyUmFpbDQgPSBzdHlsZWQuc3BhbmBcbiAgLS0taWQ6IDU7XG4gIGRpc3BsYXk6IGJsb2NrO1xuICBvcGFjaXR5OiAwLjM4O1xuICAke1NsaWRlclJhaWx9IHtcbiAgICBkaXNwbGF5OiBub25lO1xuICB9XG5gO1xuXG5jb25zdCBWaWV3UG9ydCA9IHN0eWxlZChTbGlkZXJSYWlsNClgXG4gIG1heC1oZWlnaHQ6IDEwMHZoO1xuICBwYWRkaW5nLXRvcDogMC43NXJlbTtcbiAgcGFkZGluZy1ib3R0b206IDNyZW07XG4gIHBhZGRpbmctbGVmdDogMS41cmVtO1xuICBwYWRkaW5nLXJpZ2h0OiBjYWxjKFxuICAgIHZhcigtLXNpZGVOYXZTY3JvbGxiYXJHYXBMZWZ0KSArIHZhcigtLXNpZGVOYXZTY3JvbGxiYXJXaWR0aCkgLyAyICtcbiAgICAgIHZhcigtLXNpZGVOYXZTY3JvbGxiYXJUaHVtYldpZHRoKSAvIDJcbiAgKTtcblxuICAvKiBTY3JvbGwgY29udGFpbmVycyBhcmUgZm9jdXNhYmxlICovXG4gIG91dGxpbmU6IDA7XG5cbiAgLlJvb3Q6aGFzKCY6Zm9jdXMtdmlzaWJsZSk6OmJlZm9yZSB7XG4gICAgY29udGVudDogJyc7XG4gICAgaW5zZXQ6IDA7XG4gICAgcG9pbnRlci1ldmVudHM6IG5vbmU7XG4gICAgcG9zaXRpb246IGFic29sdXRlO1xuICAgIG91dGxpbmU6IDJweCBzb2xpZCAke3QoJyRjb2xvci5ibHVlJyl9O1xuICAgIG91dGxpbmUtb2Zmc2V0OiAtMnB4O1xuICAgIC8qIERvbid0IGluc2V0IHRoZSBvdXRsaW5lIG9uIHRoZSByaWdodCAqL1xuICAgIHJpZ2h0OiAtMnB4O1xuICB9XG5gO1xuXG5leHBvcnQgZnVuY3Rpb24gQXBwKCkge1xuICByZXR1cm4gKFxuICAgIDxDb21wb25lbnQ+XG4gICAgICA8U2xpZGVyUmFpbCAvPlxuICAgICAgPFNsaWRlclJhaWwyIC8+XG4gICAgPC9Db21wb25lbnQ+XG4gICk7XG59XG5cbkFwcC5kaXNwbGF5TmFtZSA9ICdBcHAnO1xuIl19*/ \ No newline at end of file diff --git a/packages/pigment-css-react-new/tests/styled/fixtures/styled.output.js b/packages/pigment-css-react-new/tests/styled/fixtures/styled.output.js new file mode 100644 index 000000000..2c4031f35 --- /dev/null +++ b/packages/pigment-css-react-new/tests/styled/fixtures/styled.output.js @@ -0,0 +1,76 @@ +import { + css as _css, + styled as _styled, + styled as _styled2, + styled as _styled3, + styled as _styled4, + styled as _styled5, + styled as _styled6, + styled as _styled7, + styled as _styled8, +} from '@pigment-css/react-new/runtime'; +import { TestComponent } from './dummy-component.fixture'; +export const rotateKeyframe = 'rotate'; +const _exp4 = /*#__PURE__*/ () => TestComponent; +const StyledTest = /*#__PURE__*/ _styled(_exp4())({ + classes: 'StyledTest', + variants: [ + { + $$cls: 'StyledTest-size-small', + props: { + size: 'small', + }, + }, + { + $$cls: 'StyledTest-size-medium', + props: { + size: 'medium', + }, + }, + { + $$cls: 'StyledTest-size-large', + props: { + size: 'large', + }, + }, + ], + vars: { + '--StyledTest-1': [ + (props) => { + return props.size === 'small' ? 'red' : 'blue'; + }, + 0, + ], + }, +}); +export const SliderRail3 = /*#__PURE__*/ _styled2('span')({ + classes: 's4ekdda', +}); +export const SliderRail = /*#__PURE__*/ _styled3('span')({ + classes: 'sqpzee', +}); +const SliderRail5 = /*#__PURE__*/ _styled4('span')({ + classes: 's1o48m17', +}); +const Component = /*#__PURE__*/ _styled5('div')({ + classes: 'c13e7k7c', +}); +const SliderRail2 = /*#__PURE__*/ _styled6('span')({ + classes: 'sqzgjb7', +}); +const SliderRail4 = /*#__PURE__*/ _styled7('span')({ + classes: 'sxcjuwu', +}); +const _exp14 = /*#__PURE__*/ () => SliderRail4; +const ViewPort = /*#__PURE__*/ _styled8(_exp14())({ + classes: 'v1x90zfp', +}); +export function App() { + return ( + + + + + ); +} +App.displayName = 'App'; diff --git a/packages/pigment-css-react-new/tests/styled/styled-runtime.test.tsx b/packages/pigment-css-react-new/tests/styled/styled-runtime.test.tsx new file mode 100644 index 000000000..241f538d0 --- /dev/null +++ b/packages/pigment-css-react-new/tests/styled/styled-runtime.test.tsx @@ -0,0 +1,172 @@ +import * as React from 'react'; +import { expect } from 'chai'; +import { render } from '@testing-library/react'; + +import { styled } from '../../src/runtime'; + +describe('styled - runtime', () => { + it('should return base classes when there are no variants or runtime props', async () => { + const Component = styled('div')({ + classes: 'hello world', + }); + const screen = render(Hello); + const component = await screen.findByTestId('component'); + expect(component.className).to.equal('hello world'); + }); + + it('should return base and variant classes as per prop value', async () => { + const Component = styled('div')({ + classes: 'hello', + defaultVariants: { + variant: 'primary', + size: 'medium', + }, + variants: [ + { + $$cls: 'v-primary', + props: { + variant: 'primary', + }, + }, + { + $$cls: 'v-secondary', + props: { + variant: 'secondary', + }, + }, + { + $$cls: 's-small', + props: { + size: 'small', + }, + }, + { + $$cls: 's-medium', + props: { + size: 'medium', + }, + }, + { + $$cls: 's-large', + props: { + size: 'large', + }, + }, + ], + }) as React.FC< + { size?: string; variant?: string; as?: string } & React.JSX.IntrinsicElements['div'] + >; + const screen = render(Hello); + let component = await screen.findByTestId('component'); + expect(component.className).to.equal('hello v-primary s-medium'); + + screen.rerender( + + Hello + , + ); + expect(component.className).to.equal('hello v-primary s-medium'); + + screen.rerender( + + Hello + , + ); + expect(component.className).to.equal('hello v-primary s-small'); + + screen.rerender( + + Hello + , + ); + expect(component.className).to.equal('hello v-secondary s-medium'); + + screen.rerender( + + Hello + , + ); + expect(component.className).to.equal('hello v-secondary s-large'); + + screen.rerender( + // @ts-expect-error type is forwardable to button + + Hello + , + ); + component = await screen.findByRole('button'); + expect(component.tagName).to.equal('BUTTON'); + expect(component.getAttribute('type')).to.equal('button'); + }); + + it('default prop filtering for native html tag', async () => { + const Link = styled('a')({ + classes: 'green', + }); + const other = { m: [3], pt: [4] }; + + const screen = render( + + hello world + , + ); + const component = await screen.findByTestId('component'); + expect(component.getAttribute('href')).to.equal('link'); + expect(component.getAttribute('aria-label')).to.equal('some label'); + expect(component.getAttribute('data-wow')).to.equal('value'); + expect(component.getAttribute('is')).to.equal('true'); + + expect(component.hasAttribute('a')).to.equal(false); + expect(component.hasAttribute('b')).to.equal(false); + expect(component.hasAttribute('wow')).to.equal(false); + expect(component.hasAttribute('prop')).to.equal(false); + expect(component.hasAttribute('cool')).to.equal(false); + expect(component.hasAttribute('filtering')).to.equal(false); + }); + + describe('as', () => { + it("child's classes still propagate to its parent", () => { + const StyledChild = styled('span')({ + classes: 'child', + }); + + const StyledParent = styled(StyledChild)({ + classes: 'parent', + }); + + const { getByTestId } = render(); + expect(getByTestId('component').className).to.equal('child parent'); + }); + + it('use component forward prop if provided `as` is a component', () => { + const StyledDiv = styled('div')({ + classes: 'root', + }); + + function Component({ TagComponent = 'span', ...props }) { + return ; + } + + const { getByTestId } = render( + // @ts-expect-error + , + ); + + expect(getByTestId('component').tagName).to.equal('BUTTON'); + }); + }); +}); diff --git a/packages/pigment-css-react-new/tests/styled/styled.spec.tsx b/packages/pigment-css-react-new/tests/styled/styled.spec.tsx new file mode 100644 index 000000000..86f4acb8e --- /dev/null +++ b/packages/pigment-css-react-new/tests/styled/styled.spec.tsx @@ -0,0 +1,52 @@ +import { t } from '@pigment-css/theme'; +import { styled } from '../../src/styled'; + +declare module '@pigment-css/theme' { + interface Theme { + palette: { + main: string; + }; + } +} + +const Button = styled('button')({ + color: 'red', + variants: { + btnSize: { + small: { + padding: 0, + }, + medium: { + padding: '1rem', + }, + large: { + padding: '2rem', + }, + }, + }, +}); + +const Div1 = styled.div<{ $size?: 'small' | 'medium' | 'large' }>` + color: red; + padding: ${({ $size }) => ($size === 'small' ? 2 : 4)}; +`; + + undefined}> +