Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement vanilla / non-React styles, based on spike #1759

Draft
wants to merge 14 commits into
base: master
Choose a base branch
from
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,50 @@ describe('babel-plugin-strip-runtime with stylesheet extraction (extractStylesTo
});
});

describe('globalCss without JSX pragma', () => {
const code = `
import {cssMap, globalCss} from '@compiled/react';

const styles = cssMap({
base: {
color: 'red',
fontWeight: 'bold',
userSelect: 'none',
'.ProseMirror hr': {
userSelect: 'none',
background: 'red',
}
}
}, {global: true});

globalCss([styles.base]);
`;

it('adds styles to directory', () => {
const actual = transform(code, {
run: 'both',
runtime,
extractStylesToDirectory: { source: 'src/', dest: 'dist/' },
});

expect(actual).toMatchInlineSnapshot(`
"/* app.tsx generated by @compiled/babel-plugin v0.0.0 */
import './app.global.css';
import * as React from 'react';
import { ax, ix } from '@compiled/react/runtime';
const styles = {
base: '_170jb0f',
};
"
`);

expect(writeFileSync).toBeCalledWith(
expect.stringContaining('app.global.css'),
'._170jb0f .ProseMirror hr{-webkit-user-select:none;-moz-user-select:none;user-select:none;background-color:red}\n._170jb0f{color:red;font-weight:bold;-webkit-user-select:none;-moz-user-select:none;user-select:none}'
);
});
});

describe('with JSX pragma', () => {
it('extracts styles into app.compiled.css', () => {
const codeWithPragma = `
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}'
);
});
});
});
38 changes: 36 additions & 2 deletions packages/babel-plugin-strip-runtime/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +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 { 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 All @@ -22,6 +24,7 @@ export default declare<PluginPass>((api) => {
name: '@compiled/babel-plugin-strip-runtime',
pre() {
this.styleRules = [];
this.global = false;
},
visitor: {
Program: {
Expand Down Expand Up @@ -60,7 +63,9 @@ export default declare<PluginPass>((api) => {

if (this.opts.extractStylesToDirectory && this.styleRules.length > 0) {
// Build and sanitize filename of the css file
const cssFilename = `${parse(filename).name}.compiled.css`;
const cssFilename = this.global
? `${parse(filename).name}.global.css`
: `${parse(filename).name}.compiled.css`;

if (!file.opts.generatorOpts?.sourceFileName) {
throw new Error(`Source filename was not defined`);
Expand Down Expand Up @@ -104,7 +109,10 @@ export default declare<PluginPass>((api) => {
},

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

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

if (children.node.type !== 'ArrayExpression') {
return;
}

const globalStyleRules: string[] = [];
children.node.elements.forEach((element) => {
if (!t.isStringLiteral(element)) {
return;
}
globalStyleRules.push(element.value);
});

if (globalStyleRules.length > 0) {
if (isInjectGlobalCss(path.node)) {
this.global = true;
}
this.styleRules.push(...globalStyleRules);
}
// remove injectGlobalCss() call from the code
path.remove();
return;
}

if (isAutomaticRuntime(path.node, 'jsxs')) {
// We've found something that looks like _jsxs(...)
// Now we want to check if it's from the Compiled Runtime and if it is - replace with its children.
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'
);
};
Binary file not shown.
71 changes: 71 additions & 0 deletions packages/babel-plugin/src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -406,4 +406,75 @@ describe('babel plugin', () => {

expect(actual).toInclude('import { ac, ix, CC, CS } from "@compiled/react/runtime"');
});

it.only('should process cssMap even without a react component referencing the map', () => {
const actual = transform(
`
import {cssMap, globalCss} from '@compiled/react';

const styles = cssMap({
base: {
color: 'red',
fontWeight: 'bold',
'.ProseMirror hr': {
background: 'red',
}
},
icon: {
color: 'red',
fontWeight: 'bold',
'.ProseMirror .icon': {
background: 'red',
}
}
}, {global: true});

const styles2 = cssMap({
base: {
display: 'block',
background: 'red',
color: 'red',
fontWeight: 'bold',
userSelect: 'none',
}
});

globalCss([styles.base]);

<div css={styles2.base}></div>
`
);
expect(actual).toIncludeMultiple([
'._3wo5z1{color:red;font-weight:bold}',
'._3wo5z1 .ProseMirror hr{background-color:red}',
'._196cdez{color:red;font-weight:bold}',
'._196cdez .ProseMirror .icon{background-color:red}',
]);
});

it.only('should process cssMap even without a react component referencing the map and support autoprefix', () => {
const actual = transform(
`
import {cssMap, globalCss} from '@compiled/react';

const styles = cssMap({
base: {
color: 'red',
fontWeight: 'bold',
userSelect: 'none',
'.ProseMirror hr': {
userSelect: 'none',
background: 'red',
}
}
}, {global: true});

globalCss([styles.base]);
`
);
expect(actual).toIncludeMultiple([
'._170jb0f{color:red;font-weight:bold;-webkit-user-select:none;-moz-user-select:none;user-select:none}',
'._170jb0f .ProseMirror hr{-webkit-user-select:none;-moz-user-select:none;user-select:none;background-color:red}',
]);
});
});
Loading
Loading