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'); /** Returns the CSS content written to app.compiled.css by the last transform call. */ const getExtractedCss = (): string => { // writeFileSync(filePath, cssContent) — find the call that wrote to app.compiled.css const match = (writeFileSync as jest.Mock).mock.calls.find(([filePath]) => (filePath as string).includes('app.compiled.css') ); const [, cssContent] = match ?? []; return cssContent as string; }; describe('babel-plugin-strip-runtime with stylesheet extraction (extractStylesToDirectory)', () => { describe('with the classic runtime', () => { const runtime = 'classic'; describe('without JSX pragma', () => { const code = ` import '@compiled/react'; const Component = () => (
hello world
); `; 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.compiled.css'; import * as React from 'react'; import { ax, ix } from '@compiled/react/runtime'; const Component = () => /*#__PURE__*/ React.createElement( 'div', { className: ax(['_4ya3eErjyG _1UtDYzynoA']), }, 'hello world' ); " `); expect(writeFileSync).toBeCalledWith( expect.stringContaining('app.compiled.css'), '._1UtDYzynoA{color:blue}\n._4ya3eErjyG{font-size:12px}' ); }); it('error when source directory is not found', () => { expect(() => transform(code, { run: 'both', runtime, extractStylesToDirectory: { source: 'not-existing-src/', dest: 'dist/' }, }) ).toThrowWithMessage( Error, `/base/src/app.tsx: Source directory 'not-existing-src/' was not found relative to source file ('../src/app.tsx')` ); }); }); describe('with JSX pragma', () => { it('extracts styles into app.compiled.css', () => { const codeWithPragma = ` /** @jsx myJsx */ import { css, jsx as myJsx } from '@compiled/react'; const Component = () => (
hello world 2
); const Component2 = () => (
hello world 2
); `; const actual = transform(codeWithPragma, { run: 'both', runtime: 'classic', 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 Component = () => /*#__PURE__*/ React.createElement( 'div', { className: ax(['_4ya3eErjyG _1UtDYzynoA']), }, 'hello world 2' ); const Component2 = () => /*#__PURE__*/ React.createElement( 'div', { className: ax(['_4ya3eErjyG _1UtDYzy8mA']), }, 'hello world 2' ); " `); }); }); }); describe('with cssMapScoped (non-atomic)', () => { it('extracts atomic cssMap styles into app.compiled.css', () => { (writeFileSync as jest.Mock).mockClear(); const code = ` /** @jsxImportSource @compiled/react */ import { cssMap } from '@compiled/react'; const styles = cssMap({ panel: { color: 'blue', padding: '8px' }, danger: { color: 'red' }, }); const Component = ({ isDanger }) => (
); `; const actual = transform(code, { run: 'both', runtime: 'automatic', extractStylesToDirectory: { source: 'src/', dest: 'dist/' }, }); // JS output: atomic _xxx class names expect(actual).toMatchInlineSnapshot(` "/* app.tsx generated by @compiled/babel-plugin v0.0.0 */ import './app.compiled.css'; import { ax, ix } from '@compiled/react/runtime'; import { jsxs as _jsxs, jsx as _jsx } from 'react/jsx-runtime'; const styles = { panel: '_0Of8r2Jg58 _1ZnuxbJg58 _1wydGWJg58 _2Zuz6QJg58 _1UtDYzynoA', danger: '_1UtDYzGowl', }; const Component = ({ isDanger }) => /*#__PURE__*/ _jsx('div', { className: ax([styles.panel, isDanger && styles.danger]), }); " `); // CSS output: atomic rules lexically sorted in the compiled.css file expect(getExtractedCss()).toMatchInlineSnapshot(` "._0Of8r2Jg58{padding-top:8px} ._1UtDYzGowl{color:red} ._1UtDYzynoA{color:blue} ._1ZnuxbJg58{padding-right:8px} ._1wydGWJg58{padding-bottom:8px} ._2Zuz6QJg58{padding-left:8px}" `); }); it('extracts mixed atomic cssMap and non-atomic cssMapScoped styles into app.compiled.css', () => { const code = ` /** @jsxImportSource @compiled/react */ import { cssMap, cssMapScoped } from '@compiled/react'; const atomicStyles = cssMap({ base: { color: 'blue' }, }); const scopedStyles = cssMapScoped({ panel: { '.panel': { padding: '8px' } }, }); const Component = ({ variant }) => (
); `; const actual = transform(code, { run: 'both', runtime: 'automatic', extractStylesToDirectory: { source: 'src/', dest: 'dist/' }, }); // JS output: both _xxx atomic and cc- non-atomic class names expect(actual).toMatchInlineSnapshot(` "/* app.tsx generated by @compiled/babel-plugin v0.0.0 */ import './app.compiled.css'; import { ax, ix } from '@compiled/react/runtime'; import { jsxs as _jsxs, jsx as _jsx } from 'react/jsx-runtime'; const atomicStyles = { base: '_1UtDYzynoA', }; const scopedStyles = { panel: 'cc-1m0qvev', }; const Component = ({ variant }) => /*#__PURE__*/ _jsx('div', { className: ax([atomicStyles.base, scopedStyles.panel]), }); " `); // CSS output: both atomic and cc- scoped rules expect(writeFileSync).toBeCalledWith( expect.stringContaining('app.compiled.css'), expect.stringContaining('._') ); expect(writeFileSync).toBeCalledWith( expect.stringContaining('app.compiled.css'), expect.stringContaining('.cc-') ); }); it('preserves non-atomic rule source order in extracted CSS', () => { (writeFileSync as jest.Mock).mockClear(); // Use variable names that produce hashes where the override (zzOverride) // lexically sorts BEFORE the base (aaBase) — proving that source order // is preserved even when lexical sort would reorder them. const code = ` /** @jsxImportSource @compiled/react */ import { cssMapScoped } from '@compiled/react'; const aaBase = cssMapScoped({ default: { '.editor .panel': { backgroundColor: 'gray' } }, }); const zzOverride = cssMapScoped({ default: { '.editor .panel': { backgroundColor: 'pink' } }, }); const Component = () => (
); `; transform(code, { run: 'both', runtime: 'automatic', extractStylesToDirectory: { source: 'src/', dest: 'dist/' }, }); // Snapshot the extracted CSS to lock down rule ordering. // baseStyles (gray) must appear BEFORE overrideStyles (pink). expect(getExtractedCss()).toMatchInlineSnapshot(` ".cc-z3r7a0 .editor .panel{background-color:gray} .cc-qr51l7 .editor .panel{background-color:pink}" `); }); it('extracts cc- scoped styles into app.compiled.css', () => { const code = ` /** @jsxImportSource @compiled/react */ import { cssMapScoped } from '@compiled/react'; const styles = cssMapScoped({ panel: { '.panel': { color: 'blue', padding: '8px' } }, danger: { '.panel': { color: 'red' } }, }); const Component = ({ isDanger }) => (
); `; const actual = transform(code, { run: 'both', runtime: 'automatic', extractStylesToDirectory: { source: 'src/', dest: 'dist/' }, }); // JS output: cc- class names, not atomic _xxx classes expect(actual).toMatchInlineSnapshot(` "/* app.tsx generated by @compiled/babel-plugin v0.0.0 */ import './app.compiled.css'; import { ax, ix } from '@compiled/react/runtime'; import { jsxs as _jsxs, jsx as _jsx } from 'react/jsx-runtime'; const styles = { panel: 'cc-16etyda', danger: 'cc-1fdnvw3', }; const Component = ({ isDanger }) => /*#__PURE__*/ _jsx('div', { className: ax([styles.panel, isDanger && styles.danger]), }); " `); // CSS output: cc- scoped rules written to the compiled.css file expect(writeFileSync).toBeCalledWith( expect.stringContaining('app.compiled.css'), expect.stringContaining('.cc-') ); }); }); describe('with the automatic runtime', () => { describe('with JSX pragma', () => { it('extracts styles into app.compiled.css', () => { const codeWithPragma = ` /** @jsxImportSource @compiled/react */ import { css } from '@compiled/react'; const Component = () => (
hello world 2
); const Component2 = () => (
hello world 2
); `; const actual = transform(codeWithPragma, { run: 'both', runtime: 'automatic', extractStylesToDirectory: { source: 'src/', dest: 'dist/' }, }); expect(actual).toMatchInlineSnapshot(` "/* app.tsx generated by @compiled/babel-plugin v0.0.0 */ import './app.compiled.css'; import { ax, ix } from '@compiled/react/runtime'; import { jsxs as _jsxs, jsx as _jsx } from 'react/jsx-runtime'; const Component = () => /*#__PURE__*/ _jsx('div', { className: ax(['_4ya3eErjyG _1UtDYzynoA']), children: 'hello world 2', }); const Component2 = () => /*#__PURE__*/ _jsx('div', { className: ax(['_4ya3eErjyG _1UtDYzy8mA']), children: 'hello world 2', }); " `); }); }); }); });