Skip to content

Commit 357b294

Browse files
committed
Implement non-React / vanilla Compiled styles
1 parent 4607d30 commit 357b294

File tree

19 files changed

+536
-42
lines changed

19 files changed

+536
-42
lines changed

packages/babel-plugin-strip-runtime/src/__tests__/extract-styles.test.ts

-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ describe('babel-plugin-strip-runtime with stylesheet extraction (extractStylesTo
9090
extractStylesToDirectory: { source: 'src/', dest: 'dist/' },
9191
});
9292

93-
// TODO: remove injectCss after extraction
9493
expect(actual).toMatchInlineSnapshot(`
9594
"/* app.tsx generated by @compiled/babel-plugin v0.0.0 */
9695
import './app.global.css';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { writeFileSync } from 'fs';
2+
3+
import { transform } from './transform';
4+
5+
// Mock out FS to avoid writing to disk
6+
// We aren't processing the result anyway, so no need for specifying the response
7+
jest.mock('fs');
8+
9+
describe('babel-plugin-strip-runtime with vanillaCss', () => {
10+
describe('with the classic runtime', () => {
11+
const runtime = 'classic';
12+
13+
const styles = `{
14+
danger: {
15+
color: 'red',
16+
backgroundColor: 'red'
17+
},
18+
success: {
19+
color: 'green',
20+
backgroundColor: 'green'
21+
}
22+
}`;
23+
24+
it('should transform vanillaCss to ax', () => {
25+
const actual = transform(
26+
`
27+
import { cssMap, vanillaCss } from '@compiled/react';
28+
29+
const someStyles = cssMap(${styles});
30+
31+
function someFunctionCall(_obj) {}
32+
33+
export const bap = someFunctionCall({
34+
// node DOM constructor
35+
toDOM(node) {
36+
const { localId, state } = node.attrs;
37+
// injectCompiledCss should be added right before \`attrs\` at build time.
38+
const attrs = {
39+
'data-task-local-id': localId || 'local-task',
40+
'data-task-state': state || 'TODO',
41+
// vanillaCss function will hint Babel to inject styles on run time, and extract styles on build time
42+
class: vanillaCss([someStyles.base, state === "DONE" && someStyles.done]),
43+
};
44+
// construct a div node
45+
return ['div', attrs, 0];
46+
},
47+
});
48+
`,
49+
{
50+
run: 'both',
51+
runtime,
52+
extractStylesToDirectory: { source: 'src/', dest: 'dist/' },
53+
}
54+
);
55+
56+
expect(actual).toMatchInlineSnapshot(`
57+
"/* app.tsx generated by @compiled/babel-plugin v0.0.0 */
58+
import './app.compiled.css';
59+
import * as React from 'react';
60+
import { ax, ix } from '@compiled/react/runtime';
61+
const someStyles = {
62+
danger: '_syaz5scu _bfhk5scu',
63+
success: '_syazbf54 _bfhkbf54',
64+
};
65+
function someFunctionCall(_obj) {}
66+
export const bap = someFunctionCall({
67+
// node DOM constructor
68+
toDOM(node) {
69+
const { localId, state } = node.attrs;
70+
// injectCompiledCss should be added right before \`attrs\` at build time.
71+
const attrs = {
72+
'data-task-local-id': localId || 'local-task',
73+
'data-task-state': state || 'TODO',
74+
// vanillaCss function will hint Babel to inject styles on run time, and extract styles on build time
75+
class: ax([someStyles.base, state === 'DONE' && someStyles.done]),
76+
};
77+
// construct a div node
78+
return ['div', attrs, 0];
79+
},
80+
});
81+
"
82+
`);
83+
84+
expect(writeFileSync).toBeCalledWith(
85+
expect.stringContaining('app.compiled.css'),
86+
'._bfhk5scu{background-color:red}\n' +
87+
'._bfhkbf54{background-color:green}\n' +
88+
'._syaz5scu{color:red}\n' +
89+
'._syazbf54{color:green}'
90+
);
91+
});
92+
});
93+
});

packages/babel-plugin-strip-runtime/src/index.ts

+9-5
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import type { PluginPass, PluginOptions, BabelFileMetadata } from './types';
1212
import { isAutomaticRuntime } from './utils/is-automatic-runtime';
1313
import { isCCComponent } from './utils/is-cc-component';
1414
import { isCreateElement } from './utils/is-create-element';
15-
import { isInjectGlobalCss } from './utils/is-inject-globalcss';
15+
import { isInjectCompiledCss } from './utils/is-inject-css';
16+
import { isInjectGlobalCss } from './utils/is-inject-global-css';
1617
import { removeStyleDeclarations } from './utils/remove-style-declarations';
1718
import { toURIComponent } from './utils/to-uri-component';
1819

@@ -110,7 +111,7 @@ export default declare<PluginPass>((api) => {
110111
ImportSpecifier(path) {
111112
if (
112113
t.isIdentifier(path.node.imported) &&
113-
['CC', 'CS', 'injectCss'].includes(path.node.imported.name)
114+
['CC', 'CS', 'injectGlobalCss'].includes(path.node.imported.name)
114115
) {
115116
path.remove();
116117
}
@@ -166,7 +167,7 @@ export default declare<PluginPass>((api) => {
166167
return;
167168
}
168169

169-
if (isInjectGlobalCss(path.node)) {
170+
if (isInjectCompiledCss(path.node) || isInjectGlobalCss(path.node)) {
170171
const [children] = path.get('arguments');
171172

172173
if (children.node.type !== 'ArrayExpression') {
@@ -180,11 +181,14 @@ export default declare<PluginPass>((api) => {
180181
}
181182
globalStyleRules.push(element.value);
182183
});
184+
183185
if (globalStyleRules.length > 0) {
184-
this.global = true;
186+
if (isInjectGlobalCss(path.node)) {
187+
this.global = true;
188+
}
185189
this.styleRules.push(...globalStyleRules);
186190
}
187-
// remove injectCss() call from the code
191+
// remove injectGlobalCss() call from the code
188192
path.remove();
189193
return;
190194
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import * as t from '@babel/types';
2+
3+
/**
4+
* Return true if (and only if) the current node is a
5+
* `injectCompiledCss()` function call.
6+
*
7+
* @param node
8+
* @returns if the node is `injectCompiledCss()`
9+
*/
10+
export const isInjectCompiledCss = (node: t.Node): boolean => {
11+
return (
12+
// TODO: update other injectGlobalCss usages in other places
13+
t.isCallExpression(node) &&
14+
t.isIdentifier(node.callee) &&
15+
node.callee.name === 'injectCompiledCss'
16+
);
17+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import * as t from '@babel/types';
2+
3+
/**
4+
* Return true if (and only if) the current node is a
5+
* `injectGlobalCss()` function call.
6+
*
7+
* @param node
8+
* @returns if the node is `injectGlobalCss()`
9+
*/
10+
export const isInjectGlobalCss = (node: t.Node): boolean => {
11+
return (
12+
// TODO: update other injectGlobalCss usages in other places
13+
t.isCallExpression(node) &&
14+
t.isIdentifier(node.callee) &&
15+
node.callee.name === 'injectGlobalCss'
16+
);
17+
};

packages/babel-plugin-strip-runtime/src/utils/is-inject-globalcss.ts

-14
This file was deleted.

packages/babel-plugin/src/babel-plugin.ts

+34-17
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,12 @@ import {
3030
isCompiledStyledCallExpression,
3131
isCompiledStyledTaggedTemplateExpression,
3232
isCompiledCSSMapCallExpression,
33+
isCompiledVanillaCssCallExpression,
3334
} from './utils/is-compiled';
3435
import { isTransformedJsxFunction } from './utils/is-jsx-function';
3536
import { normalizePropsUsage } from './utils/normalize-props-usage';
3637
import { transformCssItems } from './utils/transform-css-items';
38+
import { visitVanillaCssPath } from './vanilla-css';
3739
import { visitXcssPropPath } from './xcss-prop';
3840

3941
// eslint-disable-next-line @typescript-eslint/no-var-requires
@@ -274,23 +276,31 @@ export default declare<State>((api) => {
274276
return;
275277
}
276278

277-
(['styled', 'ClassNames', 'css', 'keyframes', 'cssMap', 'globalCss'] as const).forEach(
278-
(apiName) => {
279-
if (
280-
state.compiledImports &&
281-
t.isIdentifier(specifier.node?.imported) &&
282-
specifier.node?.imported.name === apiName
283-
) {
284-
// Enable the API with the local name
285-
// @ts-expect-error
286-
const apiArray = state.compiledImports[apiName] || [];
287-
apiArray.push(specifier.node.local.name);
288-
// @ts-expect-error
289-
state.compiledImports[apiName] = apiArray;
290-
specifier.remove();
291-
}
279+
(
280+
[
281+
'styled',
282+
'ClassNames',
283+
'css',
284+
'keyframes',
285+
'cssMap',
286+
'globalCss',
287+
'vanillaCss',
288+
] as const
289+
).forEach((apiName) => {
290+
if (
291+
state.compiledImports &&
292+
t.isIdentifier(specifier.node?.imported) &&
293+
specifier.node?.imported.name === apiName
294+
) {
295+
// Enable the API with the local name
296+
// @ts-expect-error
297+
const apiArray = state.compiledImports[apiName] || [];
298+
apiArray.push(specifier.node.local.name);
299+
// @ts-expect-error
300+
state.compiledImports[apiName] = apiArray;
301+
specifier.remove();
292302
}
293-
);
303+
});
294304
});
295305

296306
if (path.node.specifiers.length === 0) {
@@ -317,11 +327,18 @@ Reasons this might happen:
317327
path.parentPath
318328
);
319329
}
330+
320331
if (isCompiledCSSMapCallExpression(path.node, state)) {
321332
visitCssMapPath(path, { context: 'root', state, parentPath: path });
322333
return;
323334
}
324335

336+
if (isCompiledVanillaCssCallExpression(path.node, state)) {
337+
// @ts-expect-error
338+
visitVanillaCssPath(path, { context: 'root', state, parentPath: path });
339+
return;
340+
}
341+
325342
const hasStyles =
326343
isCompiledCSSTaggedTemplateExpression(path.node, state) ||
327344
isCompiledStyledTaggedTemplateExpression(path.node, state) ||
@@ -359,7 +376,7 @@ Reasons this might happen:
359376
const cssOutput = buildCss(path.node.arguments[0], meta);
360377
// @ts-expect-error
361378
const { sheets } = transformCssItems(cssOutput.css, meta);
362-
const newNode = t.callExpression(t.identifier('injectCss'), [
379+
const newNode = t.callExpression(t.identifier('injectGlobalCss'), [
363380
t.arrayExpression(sheets.map((item) => t.stringLiteral(item))),
364381
]);
365382
path.parentPath.replaceWith(newNode);

packages/babel-plugin/src/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ export interface State extends PluginPass {
136136
keyframes?: string[];
137137
styled?: string[];
138138
cssMap?: string[];
139+
vanillaCss?: string[];
139140
};
140141

141142
usesXcss?: boolean;

packages/babel-plugin/src/utils/append-runtime-imports.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ const importSpecifier = (name: string, localName?: string): t.ImportSpecifier =>
1616
};
1717

1818
// Runtime function `ac` is less performant than `ax`, so we only want to import `ac` if classNameCompressionMap is provided.
19-
const COMPILED_RUNTIME_IMPORTS_WITH_COMPRESSION = ['ac', 'ix', 'CC', 'CS', 'injectCss'];
20-
const COMPILED_RUNTIME_IMPORTS_WITHOUT_COMPRESSION = ['ax', 'ix', 'CC', 'CS', 'injectCss'];
19+
const COMPILED_RUNTIME_IMPORTS_WITH_COMPRESSION = ['ac', 'ix', 'CC', 'CS', 'injectGlobalCss'];
20+
const COMPILED_RUNTIME_IMPORTS_WITHOUT_COMPRESSION = ['ax', 'ix', 'CC', 'CS', 'injectGlobalCss'];
2121
const COMPILED_RUNTIME_MODULE = '@compiled/react/runtime';
2222

2323
/**

packages/babel-plugin/src/utils/is-compiled.ts

+15
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,21 @@ export const isCompiledCSSMapCallExpression = (
6464
t.isIdentifier(node.callee) &&
6565
!!state.compiledImports?.cssMap?.includes(node.callee.name);
6666

67+
/**
68+
* TODO
69+
*
70+
* @param node {t.Node} The node that is being checked
71+
* @param state {State} Plugin state
72+
* @returns {boolean} Whether the node is a compiled vanillaCss
73+
*/
74+
export const isCompiledVanillaCssCallExpression = (
75+
node: t.Node,
76+
state: State
77+
): node is t.CallExpression =>
78+
t.isCallExpression(node) &&
79+
t.isIdentifier(node.callee) &&
80+
!!state.compiledImports?.vanillaCss?.includes(node.callee.name);
81+
6782
/**
6883
* Returns `true` if the node is using `keyframes` from `@compiled/react` as a tagged template expression
6984
*

0 commit comments

Comments
 (0)