Skip to content

Commit

Permalink
Implement non-React / vanilla Compiled styles
Browse files Browse the repository at this point in the history
  • Loading branch information
dddlr committed Dec 17, 2024
1 parent d24bc12 commit 13c1af5
Show file tree
Hide file tree
Showing 19 changed files with 536 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ describe('babel-plugin-strip-runtime with stylesheet extraction (extractStylesTo
extractStylesToDirectory: { source: 'src/', dest: 'dist/' },
});

// TODO: remove injectCss after extraction
expect(actual).toMatchInlineSnapshot(`
"/* app.tsx generated by @compiled/babel-plugin v0.0.0 */
import './app.global.css';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { writeFileSync } from 'fs';

import { transform } from './transform';

// Mock out FS to avoid writing to disk
// We aren't processing the result anyway, so no need for specifying the response
jest.mock('fs');

describe('babel-plugin-strip-runtime with vanillaCss', () => {
describe('with the classic runtime', () => {
const runtime = 'classic';

const styles = `{
danger: {
color: 'red',
backgroundColor: 'red'
},
success: {
color: 'green',
backgroundColor: 'green'
}
}`;

it('should transform vanillaCss to ax', () => {
const actual = transform(
`
import { cssMap, vanillaCss } from '@compiled/react';
const someStyles = cssMap(${styles});
function someFunctionCall(_obj) {}
export const bap = someFunctionCall({
// node DOM constructor
toDOM(node) {
const { localId, state } = node.attrs;
// injectCompiledCss should be added right before \`attrs\` at build time.
const attrs = {
'data-task-local-id': localId || 'local-task',
'data-task-state': state || 'TODO',
// vanillaCss function will hint Babel to inject styles on run time, and extract styles on build time
class: vanillaCss([someStyles.base, state === "DONE" && someStyles.done]),
};
// construct a div node
return ['div', attrs, 0];
},
});
`,
{
run: 'both',
runtime,
extractStylesToDirectory: { source: 'src/', dest: 'dist/' },
}
);

expect(actual).toMatchInlineSnapshot(`
"/* app.tsx generated by @compiled/babel-plugin v0.0.0 */
import './app.compiled.css';
import * as React from 'react';
import { ax, ix } from '@compiled/react/runtime';
const someStyles = {
danger: '_syaz5scu _bfhk5scu',
success: '_syazbf54 _bfhkbf54',
};
function someFunctionCall(_obj) {}
export const bap = someFunctionCall({
// node DOM constructor
toDOM(node) {
const { localId, state } = node.attrs;
// injectCompiledCss should be added right before \`attrs\` at build time.
const attrs = {
'data-task-local-id': localId || 'local-task',
'data-task-state': state || 'TODO',
// vanillaCss function will hint Babel to inject styles on run time, and extract styles on build time
class: ax([someStyles.base, state === 'DONE' && someStyles.done]),
};
// construct a div node
return ['div', attrs, 0];
},
});
"
`);

expect(writeFileSync).toBeCalledWith(
expect.stringContaining('app.compiled.css'),
'._bfhk5scu{background-color:red}\n' +
'._bfhkbf54{background-color:green}\n' +
'._syaz5scu{color:red}\n' +
'._syazbf54{color:green}'
);
});
});
});
14 changes: 9 additions & 5 deletions packages/babel-plugin-strip-runtime/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import type { PluginPass, PluginOptions, BabelFileMetadata } from './types';
import { isAutomaticRuntime } from './utils/is-automatic-runtime';
import { isCCComponent } from './utils/is-cc-component';
import { isCreateElement } from './utils/is-create-element';
import { isInjectGlobalCss } from './utils/is-inject-globalcss';
import { isInjectCompiledCss } from './utils/is-inject-css';
import { isInjectGlobalCss } from './utils/is-inject-global-css';
import { removeStyleDeclarations } from './utils/remove-style-declarations';
import { toURIComponent } from './utils/to-uri-component';

Expand Down Expand Up @@ -110,7 +111,7 @@ export default declare<PluginPass>((api) => {
ImportSpecifier(path) {
if (
t.isIdentifier(path.node.imported) &&
['CC', 'CS', 'injectCss'].includes(path.node.imported.name)
['CC', 'CS', 'injectGlobalCss'].includes(path.node.imported.name)
) {
path.remove();
}
Expand Down Expand Up @@ -166,7 +167,7 @@ export default declare<PluginPass>((api) => {
return;
}

if (isInjectGlobalCss(path.node)) {
if (isInjectCompiledCss(path.node) || isInjectGlobalCss(path.node)) {
const [children] = path.get('arguments');

if (children.node.type !== 'ArrayExpression') {
Expand All @@ -180,11 +181,14 @@ export default declare<PluginPass>((api) => {
}
globalStyleRules.push(element.value);
});

if (globalStyleRules.length > 0) {
this.global = true;
if (isInjectGlobalCss(path.node)) {
this.global = true;
}
this.styleRules.push(...globalStyleRules);
}
// remove injectCss() call from the code
// remove injectGlobalCss() call from the code
path.remove();
return;
}
Expand Down
17 changes: 17 additions & 0 deletions packages/babel-plugin-strip-runtime/src/utils/is-inject-css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as t from '@babel/types';

/**
* Return true if (and only if) the current node is a
* `injectCompiledCss()` function call.
*
* @param node
* @returns if the node is `injectCompiledCss()`
*/
export const isInjectCompiledCss = (node: t.Node): boolean => {
return (
// TODO: update other injectGlobalCss usages in other places
t.isCallExpression(node) &&
t.isIdentifier(node.callee) &&
node.callee.name === 'injectCompiledCss'
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as t from '@babel/types';

/**
* Return true if (and only if) the current node is a
* `injectGlobalCss()` function call.
*
* @param node
* @returns if the node is `injectGlobalCss()`
*/
export const isInjectGlobalCss = (node: t.Node): boolean => {
return (
// TODO: update other injectGlobalCss usages in other places
t.isCallExpression(node) &&
t.isIdentifier(node.callee) &&
node.callee.name === 'injectGlobalCss'
);
};

This file was deleted.

51 changes: 34 additions & 17 deletions packages/babel-plugin/src/babel-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@ import {
isCompiledStyledCallExpression,
isCompiledStyledTaggedTemplateExpression,
isCompiledCSSMapCallExpression,
isCompiledVanillaCssCallExpression,
} from './utils/is-compiled';
import { isTransformedJsxFunction } from './utils/is-jsx-function';
import { normalizePropsUsage } from './utils/normalize-props-usage';
import { transformCssItems } from './utils/transform-css-items';
import { visitVanillaCssPath } from './vanilla-css';
import { visitXcssPropPath } from './xcss-prop';

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

(['styled', 'ClassNames', 'css', 'keyframes', 'cssMap', 'globalCss'] as const).forEach(
(apiName) => {
if (
state.compiledImports &&
t.isIdentifier(specifier.node?.imported) &&
specifier.node?.imported.name === apiName
) {
// Enable the API with the local name
// @ts-expect-error
const apiArray = state.compiledImports[apiName] || [];
apiArray.push(specifier.node.local.name);
// @ts-expect-error
state.compiledImports[apiName] = apiArray;
specifier.remove();
}
(
[
'styled',
'ClassNames',
'css',
'keyframes',
'cssMap',
'globalCss',
'vanillaCss',
] as const
).forEach((apiName) => {
if (
state.compiledImports &&
t.isIdentifier(specifier.node?.imported) &&
specifier.node?.imported.name === apiName
) {
// Enable the API with the local name
// @ts-expect-error
const apiArray = state.compiledImports[apiName] || [];
apiArray.push(specifier.node.local.name);
// @ts-expect-error
state.compiledImports[apiName] = apiArray;
specifier.remove();
}
);
});
});

if (path.node.specifiers.length === 0) {
Expand All @@ -317,11 +327,18 @@ Reasons this might happen:
path.parentPath
);
}

if (isCompiledCSSMapCallExpression(path.node, state)) {
visitCssMapPath(path, { context: 'root', state, parentPath: path });
return;
}

if (isCompiledVanillaCssCallExpression(path.node, state)) {
// @ts-expect-error
visitVanillaCssPath(path, { context: 'root', state, parentPath: path });
return;
}

const hasStyles =
isCompiledCSSTaggedTemplateExpression(path.node, state) ||
isCompiledStyledTaggedTemplateExpression(path.node, state) ||
Expand Down Expand Up @@ -359,7 +376,7 @@ Reasons this might happen:
const cssOutput = buildCss(path.node.arguments[0], meta);
// @ts-expect-error
const { sheets } = transformCssItems(cssOutput.css, meta);
const newNode = t.callExpression(t.identifier('injectCss'), [
const newNode = t.callExpression(t.identifier('injectGlobalCss'), [
t.arrayExpression(sheets.map((item) => t.stringLiteral(item))),
]);
path.parentPath.replaceWith(newNode);
Expand Down
1 change: 1 addition & 0 deletions packages/babel-plugin/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ export interface State extends PluginPass {
keyframes?: string[];
styled?: string[];
cssMap?: string[];
vanillaCss?: string[];
};

usesXcss?: boolean;
Expand Down
4 changes: 2 additions & 2 deletions packages/babel-plugin/src/utils/append-runtime-imports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ const importSpecifier = (name: string, localName?: string): t.ImportSpecifier =>
};

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

/**
Expand Down
15 changes: 15 additions & 0 deletions packages/babel-plugin/src/utils/is-compiled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,21 @@ export const isCompiledCSSMapCallExpression = (
t.isIdentifier(node.callee) &&
!!state.compiledImports?.cssMap?.includes(node.callee.name);

/**
* TODO
*
* @param node {t.Node} The node that is being checked
* @param state {State} Plugin state
* @returns {boolean} Whether the node is a compiled vanillaCss
*/
export const isCompiledVanillaCssCallExpression = (
node: t.Node,
state: State
): node is t.CallExpression =>
t.isCallExpression(node) &&
t.isIdentifier(node.callee) &&
!!state.compiledImports?.vanillaCss?.includes(node.callee.name);

/**
* Returns `true` if the node is using `keyframes` from `@compiled/react` as a tagged template expression
*
Expand Down
Loading

0 comments on commit 13c1af5

Please sign in to comment.