Skip to content

Commit 155b9ef

Browse files
author
Brijesh Bittu
committed
Handle pre-transformed tagged-template literal by swc
1 parent 35f443e commit 155b9ef

File tree

9 files changed

+361
-82
lines changed

9 files changed

+361
-82
lines changed

packages/pigment-css-core/src/processors/css.ts

Lines changed: 117 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
* CssProcessor.
1111
*/
1212

13-
import { SourceLocation } from '@babel/types';
13+
import { SourceLocation, TemplateElement } from '@babel/types';
1414
import {
1515
type TransformedInternalConfig,
1616
type StyleObjectReturn,
@@ -21,7 +21,7 @@ import {
2121
serializeStyles,
2222
valueToLiteral,
2323
evaluateClassNameArg,
24-
getCSSVar,
24+
transformProbableCssVar,
2525
} from '@pigment-css/utils';
2626
import {
2727
CallParam,
@@ -119,6 +119,99 @@ export type CssTailProcessorParams = BaseCssProcessorConstructorParams extends [
119119
? T
120120
: never;
121121

122+
function handleTemplateElementOrSimilar(
123+
templateParams: (TemplateElement | ExpressionValue)[],
124+
values: ValueCache,
125+
processor: BaseCssProcessor,
126+
) {
127+
const { themeArgs = {}, pigmentFeatures: { useLayer = true } = {} } =
128+
processor.options as TransformedInternalConfig;
129+
// @ts-ignore @TODO - Fix this. No idea how to initialize a Tagged String array.
130+
const templateStrs: string[] = [];
131+
// @ts-ignore @TODO - Fix this. No idea how to initialize a Tagged String array.
132+
templateStrs.raw = [];
133+
const templateExpressions: Primitive[] = [];
134+
let paramsToIterate = templateParams;
135+
const [firstArg, ...restArgs] = templateParams;
136+
if ('kind' in firstArg && firstArg.kind === ValueType.LAZY) {
137+
const value = values.get(firstArg.ex.name) as string[];
138+
templateStrs.push(...value);
139+
// @ts-ignore @TODO - Fix this. No idea how to initialize a Tagged String array.
140+
templateStrs.raw.push(...value);
141+
paramsToIterate = restArgs;
142+
}
143+
paramsToIterate.forEach((param) => {
144+
if ('kind' in param) {
145+
switch (param.kind) {
146+
case ValueType.FUNCTION: {
147+
const value = values.get(param.ex.name) as TemplateCallback;
148+
templateExpressions.push(value(themeArgs));
149+
break;
150+
}
151+
case ValueType.CONST: {
152+
if (typeof param.value === 'string') {
153+
templateExpressions.push(transformProbableCssVar(param.value));
154+
} else {
155+
templateExpressions.push(param.value);
156+
}
157+
break;
158+
}
159+
case ValueType.LAZY: {
160+
const evaluatedValue = values.get(param.ex.name);
161+
if (typeof evaluatedValue === 'function') {
162+
templateExpressions.push(evaluatedValue(themeArgs));
163+
} else if (typeof evaluatedValue === 'string') {
164+
templateExpressions.push(transformProbableCssVar(evaluatedValue));
165+
} else {
166+
templateExpressions.push(evaluatedValue as Primitive);
167+
}
168+
break;
169+
}
170+
default:
171+
break;
172+
}
173+
} else if ('type' in param && param.type === 'TemplateElement') {
174+
templateStrs.push(param.value.cooked as string);
175+
// @ts-ignore
176+
templateStrs.raw.push(param.value.raw);
177+
}
178+
});
179+
const { styles } = serializeStyles(
180+
templateExpressions.length > 0 ? [templateStrs, ...templateExpressions] : [templateStrs],
181+
);
182+
183+
const cssText = useLayer
184+
? `@layer pigment.base{${processor.wrapStyle(styles, '')}}`
185+
: processor.wrapStyle(styles, '');
186+
const className = processor.getClassName();
187+
const rules: Rules = {
188+
[`.${className}`]: {
189+
className,
190+
cssText,
191+
displayName: processor.displayName,
192+
start: processor.location?.start ?? null,
193+
},
194+
};
195+
const location = processor.location;
196+
const sourceMapReplacements: Replacements = [
197+
{
198+
length: cssText.length,
199+
original: {
200+
start: {
201+
column: location?.start.column ?? 0,
202+
line: location?.start.line ?? 0,
203+
},
204+
end: {
205+
column: location?.end.column ?? 0,
206+
line: location?.end.line ?? 0,
207+
},
208+
},
209+
},
210+
];
211+
processor.classNames.push(className);
212+
processor.artifacts.push(['css', [rules, sourceMapReplacements]]);
213+
}
214+
122215
/**
123216
* Only deals with css`` or css(metadata)`` calls.
124217
*/
@@ -138,84 +231,8 @@ export class CssTaggedTemplateProcessor extends BaseCssProcessor {
138231
}
139232

140233
build(values: ValueCache): void {
141-
const { themeArgs, pigmentFeatures: { useLayer = true } = {} } = this
142-
.options as TransformedInternalConfig;
143234
const [, templateParams] = this.templateParam;
144-
// @ts-ignore @TODO - Fix this. No idea how to initialize a Tagged String array.
145-
const templateStrs: string[] = [];
146-
// @ts-ignore @TODO - Fix this. No idea how to initialize a Tagged String array.
147-
templateStrs.raw = [];
148-
const templateExpressions: Primitive[] = [];
149-
templateParams.forEach((param) => {
150-
if ('kind' in param) {
151-
switch (param.kind) {
152-
case ValueType.FUNCTION: {
153-
const value = values.get(param.ex.name) as TemplateCallback;
154-
templateExpressions.push(value(themeArgs));
155-
break;
156-
}
157-
case ValueType.CONST: {
158-
templateExpressions.push(param.value);
159-
break;
160-
}
161-
case ValueType.LAZY: {
162-
const evaluatedValue = values.get(param.ex.name);
163-
if (typeof evaluatedValue === 'function') {
164-
templateExpressions.push(evaluatedValue(themeArgs));
165-
} else if (
166-
typeof evaluatedValue === 'object' &&
167-
evaluatedValue &&
168-
(evaluatedValue as unknown as Record<string, boolean>).isThemeVar
169-
) {
170-
templateExpressions.push(getCSSVar(evaluatedValue.toString(), true));
171-
} else {
172-
templateExpressions.push(evaluatedValue as Primitive);
173-
}
174-
break;
175-
}
176-
default:
177-
break;
178-
}
179-
} else if ('type' in param && param.type === 'TemplateElement') {
180-
templateStrs.push(param.value.cooked as string);
181-
// @ts-ignore
182-
templateStrs.raw.push(param.value.raw);
183-
}
184-
});
185-
const { styles } = serializeStyles(
186-
templateExpressions.length > 0 ? [templateStrs, ...templateExpressions] : [templateStrs],
187-
);
188-
189-
const cssText = useLayer
190-
? `@layer pigment.base{${this.wrapStyle(styles, '')}}`
191-
: this.wrapStyle(styles, '');
192-
const className = this.getClassName();
193-
const rules: Rules = {
194-
[`.${className}`]: {
195-
className,
196-
cssText,
197-
displayName: this.displayName,
198-
start: this.location?.start ?? null,
199-
},
200-
};
201-
const location = this.location;
202-
const sourceMapReplacements: Replacements = [
203-
{
204-
length: cssText.length,
205-
original: {
206-
start: {
207-
column: location?.start.column ?? 0,
208-
line: location?.start.line ?? 0,
209-
},
210-
end: {
211-
column: location?.end.column ?? 0,
212-
line: location?.end.line ?? 0,
213-
},
214-
},
215-
},
216-
];
217-
this.classNames.push(className);
218-
this.artifacts.push(['css', [rules, sourceMapReplacements]]);
235+
handleTemplateElementOrSimilar(templateParams, values, this);
219236
}
220237
}
221238

@@ -235,7 +252,28 @@ export class CssObjectProcessor extends BaseCssProcessor {
235252
return params.flat().filter((param) => 'kind' in param);
236253
}
237254

255+
isMaybeTransformedTemplateLiteral(values: ValueCache): boolean {
256+
const [, firstArg, ...restArgs] = this.callParam;
257+
if (!('kind' in firstArg) || firstArg.kind === ValueType.CONST) {
258+
return false;
259+
}
260+
const firstArgVal = values.get(firstArg.ex.name);
261+
if (Array.isArray(firstArgVal) && restArgs.length === firstArgVal.length - 1) {
262+
return true;
263+
}
264+
return false;
265+
}
266+
267+
private buildForTransformedTemplateTag(values: ValueCache) {
268+
const [, ...templateParams] = this.callParam;
269+
handleTemplateElementOrSimilar(templateParams, values, this);
270+
}
271+
238272
build(values: ValueCache): void {
273+
if (this.isMaybeTransformedTemplateLiteral(values)) {
274+
this.buildForTransformedTemplateTag(values);
275+
return;
276+
}
239277
const [, ...callParams] = this.callParam;
240278
const { themeArgs, pigmentFeatures: { useLayer = true } = {} } = this
241279
.options as TransformedInternalConfig;
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/**
2+
* This is a pre-transformed file for testing.
3+
*/
4+
import { _ as _tagged_template_literal } from '@swc/helpers/_/_tagged_template_literal';
5+
import { styled, keyframes } from '@pigment-css/react-new';
6+
7+
function _templateObject() {
8+
const data = _tagged_template_literal([
9+
'\n 0% {\n transform: scale(0);\n opacity: 0.1;\n }\n\n 100% {\n transform: scale(1);\n opacity: 0.3;\n }\n',
10+
]);
11+
_templateObject = function () {
12+
return data;
13+
};
14+
return data;
15+
}
16+
function _templateObject1() {
17+
const data = _tagged_template_literal([
18+
'\n 0% {\n opacity: 1;\n }\n\n 100% {\n opacity: 0;\n }\n',
19+
]);
20+
_templateObject1 = function () {
21+
return data;
22+
};
23+
return data;
24+
}
25+
function _templateObject2() {
26+
const data = _tagged_template_literal([
27+
'\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',
28+
]);
29+
_templateObject2 = function () {
30+
return data;
31+
};
32+
return data;
33+
}
34+
function _templateObject3() {
35+
const data = _tagged_template_literal([
36+
'\n opacity: 0;\n position: absolute;\n\n &.',
37+
' {\n opacity: 0.3;\n transform: scale(1);\n animation-name: ',
38+
';\n animation-duration: ',
39+
'ms;\n animation-timing-function: ',
40+
';\n }\n\n &.',
41+
' {\n animation-duration: ',
42+
'ms;\n }\n\n & .',
43+
' {\n opacity: 1;\n display: block;\n width: 100%;\n height: 100%;\n border-radius: 50%;\n background-color: currentColor;\n }\n\n & .',
44+
' {\n opacity: 0;\n animation-name: ',
45+
';\n animation-duration: ',
46+
'ms;\n animation-timing-function: ',
47+
';\n }\n\n & .',
48+
' {\n position: absolute;\n /* @noflip */\n left: 0px;\n top: 0;\n animation-name: ',
49+
';\n animation-duration: 2500ms;\n animation-timing-function: ',
50+
';\n animation-iteration-count: infinite;\n animation-delay: 200ms;\n }\n',
51+
]);
52+
_templateObject3 = function () {
53+
return data;
54+
};
55+
return data;
56+
}
57+
58+
const touchRippleClasses = {
59+
rippleVisible: 'MuiTouchRipple.rippleVisible',
60+
ripplePulsate: 'MuiTouchRipple.ripplePulsate',
61+
child: 'MuiTouchRipple.child',
62+
childLeaving: 'MuiTouchRipple.childLeaving',
63+
childPulsate: 'MuiTouchRipple.childPulsate',
64+
};
65+
66+
const enterKeyframe = keyframes(_templateObject());
67+
const exitKeyframe = keyframes(_templateObject1());
68+
const pulsateKeyframe = keyframes(_templateObject2());
69+
70+
export const TouchRippleRoot = styled('span', {
71+
name: 'MuiTouchRipple',
72+
slot: 'Root',
73+
})({
74+
overflow: 'hidden',
75+
pointerEvents: 'none',
76+
position: 'absolute',
77+
zIndex: 0,
78+
top: 0,
79+
right: 0,
80+
bottom: 0,
81+
left: 0,
82+
borderRadius: 'inherit',
83+
});
84+
85+
// This `styled()` function invokes keyframes. `styled-components` only supports keyframes
86+
// in string templates. Do not convert these styles in JS object as it will break.
87+
export const TouchRippleRipple = styled(Ripple, {
88+
name: 'MuiTouchRipple',
89+
slot: 'Ripple',
90+
})(
91+
_templateObject3(),
92+
touchRippleClasses.rippleVisible,
93+
enterKeyframe,
94+
DURATION,
95+
(param) => {
96+
let { theme } = param;
97+
return theme.transitions.easing.easeInOut;
98+
},
99+
touchRippleClasses.ripplePulsate,
100+
(param) => {
101+
let { theme } = param;
102+
return theme.transitions.duration.shorter;
103+
},
104+
touchRippleClasses.child,
105+
touchRippleClasses.childLeaving,
106+
exitKeyframe,
107+
DURATION,
108+
(param) => {
109+
let { theme } = param;
110+
return theme.transitions.easing.easeInOut;
111+
},
112+
touchRippleClasses.childPulsate,
113+
pulsateKeyframe,
114+
(param) => {
115+
let { theme } = param;
116+
return theme.transitions.easing.easeInOut;
117+
},
118+
);

packages/pigment-css-react-new/tests/styled/fixtures/styled-swc-transformed-tagged-string.output.css

Lines changed: 36 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)