From 0047e707245e4d2b8e2b901994bf135315e620fe Mon Sep 17 00:00:00 2001 From: Ivor Zhou Date: Fri, 28 Aug 2026 15:02:01 -0700 Subject: [PATCH 1/3] [compiler-conformance] add implementation-neutral golden fixtures Extracted from #1623 (2f2281bc) without the Rust/SWC plugin, so the behavioral contract can land independently of the Rust implementation. packages/compiler-conformance holds eight fixtures as plain data: a manifest saying how to compile the entry file, and a recorded result pinning the generated JavaScript, StyleX metadata, generated CSS and the diagnostics. Nothing in it is Babel-specific, so a compiler written in any language can read the fixtures and check itself against the same contract. The Babel plugin is the first consumer, through an adapter that keeps parser options, console capture and rule processing out of the shared package. Generated JavaScript is compared as a normalized syntax tree, so how an implementation prints a program is not part of the contract. Metadata, CSS and diagnostics are compared exactly, after absolute paths are tokenized and implementation-specific diagnostic decoration is removed. define-vars-theme exercises a successful defineVars/createTheme transform. In #1623 it expected a module-resolution error, which pinned a misconfiguration rather than behavior; failed transforms are still covered by validation-error. --- .../__tests__/golden-fixtures-test.js | 50 +++++ packages/@stylexjs/babel-plugin/package.json | 1 + .../test-utils/babel-conformance-adapter.js | 91 ++++++++ packages/compiler-conformance/README.md | 174 +++++++++++++++ .../__tests__/conformance-harness-test.js | 209 ++++++++++++++++++ .../fixtures/create-basic/expected.json | 25 +++ .../fixtures/create-basic/input.js | 15 ++ .../fixtures/create-basic/manifest.json | 4 + .../fixtures/define-vars-theme/expected.json | 33 +++ .../define-vars-theme/input.stylex.js | 21 ++ .../fixtures/define-vars-theme/manifest.json | 9 + .../fixtures/keyframes-and-when/expected.json | 41 ++++ .../fixtures/keyframes-and-when/input.js | 25 +++ .../fixtures/keyframes-and-when/manifest.json | 4 + .../fixtures/options-warning/expected.json | 10 + .../fixtures/options-warning/input.js | 8 + .../fixtures/options-warning/manifest.json | 7 + .../fixtures/props-and-merge/expected.json | 25 +++ .../fixtures/props-and-merge/input.jsx | 24 ++ .../fixtures/props-and-merge/manifest.json | 5 + .../rewrite-theme-extension/expected.json | 8 + .../fixtures/rewrite-theme-extension/input.js | 10 + .../rewrite-theme-extension/manifest.json | 10 + .../rewrite-theme-extension/tokens.stylex.js | 10 + .../fixtures/runtime-injection/expected.json | 17 ++ .../fixtures/runtime-injection/input.js | 14 ++ .../fixtures/runtime-injection/manifest.json | 7 + .../fixtures/validation-error/expected.json | 8 + .../fixtures/validation-error/input.js | 10 + .../fixtures/validation-error/manifest.json | 4 + packages/compiler-conformance/jest.config.cjs | 17 ++ packages/compiler-conformance/package.json | 18 ++ packages/compiler-conformance/src/ast.js | 121 ++++++++++ packages/compiler-conformance/src/fixtures.js | 89 ++++++++ packages/compiler-conformance/src/index.js | 53 +++++ .../compiler-conformance/src/normalize.js | 124 +++++++++++ packages/compiler-conformance/src/result.js | 78 +++++++ 37 files changed, 1379 insertions(+) create mode 100644 packages/@stylexjs/babel-plugin/__tests__/golden-fixtures-test.js create mode 100644 packages/@stylexjs/babel-plugin/test-utils/babel-conformance-adapter.js create mode 100644 packages/compiler-conformance/README.md create mode 100644 packages/compiler-conformance/__tests__/conformance-harness-test.js create mode 100644 packages/compiler-conformance/fixtures/create-basic/expected.json create mode 100644 packages/compiler-conformance/fixtures/create-basic/input.js create mode 100644 packages/compiler-conformance/fixtures/create-basic/manifest.json create mode 100644 packages/compiler-conformance/fixtures/define-vars-theme/expected.json create mode 100644 packages/compiler-conformance/fixtures/define-vars-theme/input.stylex.js create mode 100644 packages/compiler-conformance/fixtures/define-vars-theme/manifest.json create mode 100644 packages/compiler-conformance/fixtures/keyframes-and-when/expected.json create mode 100644 packages/compiler-conformance/fixtures/keyframes-and-when/input.js create mode 100644 packages/compiler-conformance/fixtures/keyframes-and-when/manifest.json create mode 100644 packages/compiler-conformance/fixtures/options-warning/expected.json create mode 100644 packages/compiler-conformance/fixtures/options-warning/input.js create mode 100644 packages/compiler-conformance/fixtures/options-warning/manifest.json create mode 100644 packages/compiler-conformance/fixtures/props-and-merge/expected.json create mode 100644 packages/compiler-conformance/fixtures/props-and-merge/input.jsx create mode 100644 packages/compiler-conformance/fixtures/props-and-merge/manifest.json create mode 100644 packages/compiler-conformance/fixtures/rewrite-theme-extension/expected.json create mode 100644 packages/compiler-conformance/fixtures/rewrite-theme-extension/input.js create mode 100644 packages/compiler-conformance/fixtures/rewrite-theme-extension/manifest.json create mode 100644 packages/compiler-conformance/fixtures/rewrite-theme-extension/tokens.stylex.js create mode 100644 packages/compiler-conformance/fixtures/runtime-injection/expected.json create mode 100644 packages/compiler-conformance/fixtures/runtime-injection/input.js create mode 100644 packages/compiler-conformance/fixtures/runtime-injection/manifest.json create mode 100644 packages/compiler-conformance/fixtures/validation-error/expected.json create mode 100644 packages/compiler-conformance/fixtures/validation-error/input.js create mode 100644 packages/compiler-conformance/fixtures/validation-error/manifest.json create mode 100644 packages/compiler-conformance/jest.config.cjs create mode 100644 packages/compiler-conformance/package.json create mode 100644 packages/compiler-conformance/src/ast.js create mode 100644 packages/compiler-conformance/src/fixtures.js create mode 100644 packages/compiler-conformance/src/index.js create mode 100644 packages/compiler-conformance/src/normalize.js create mode 100644 packages/compiler-conformance/src/result.js diff --git a/packages/@stylexjs/babel-plugin/__tests__/golden-fixtures-test.js b/packages/@stylexjs/babel-plugin/__tests__/golden-fixtures-test.js new file mode 100644 index 000000000..7d0f70f78 --- /dev/null +++ b/packages/@stylexjs/babel-plugin/__tests__/golden-fixtures-test.js @@ -0,0 +1,50 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +'use strict'; + +import { + exactPart, + getFixtureNames, + isJsEquivalent, + jsPart, + loadFixture, + normalizeResult, + readExpected, + writeExpected, +} from 'compiler-conformance'; + +import { babelAdapter } from '../test-utils/babel-conformance-adapter'; + +// Re-records every `expected.json` from the current Babel output. Only use it +// after reviewing the resulting diff. +const UPDATE_GOLDEN = process.env.STYLEX_UPDATE_GOLDEN === '1'; + +describe('@stylexjs/babel-plugin golden fixtures', () => { + test.each(getFixtureNames())('%s', (fixtureName) => { + const fixture = loadFixture(fixtureName); + const rawResult = babelAdapter.transform(fixture); + + if (UPDATE_GOLDEN) { + writeExpected(fixtureName, rawResult, fixture); + } + + const actual = normalizeResult(fixture, rawResult); + const expected = readExpected(fixtureName, fixture); + + // Transform status, StyleX metadata, generated CSS and diagnostics have to + // match exactly once normalized. + expect(exactPart(actual)).toEqual(exactPart(expected)); + + // Generated JavaScript is compared as a normalized AST so that an + // implementation is free to print it differently. When the programs really + // do differ, assert on the sources to get a readable diff. + if (!isJsEquivalent(jsPart(expected), jsPart(actual), fixture.syntax)) { + expect(jsPart(actual)).toBe(jsPart(expected)); + } + }); +}); diff --git a/packages/@stylexjs/babel-plugin/package.json b/packages/@stylexjs/babel-plugin/package.json index 27659dcd7..4e3c682f5 100644 --- a/packages/@stylexjs/babel-plugin/package.json +++ b/packages/@stylexjs/babel-plugin/package.json @@ -35,6 +35,7 @@ "@rollup/plugin-node-resolve": "^15.3.0", "@rollup/plugin-replace": "^6.0.1", "babel-plugin-syntax-hermes-parser": "^0.36.1", + "compiler-conformance": "0.19.0", "path-browserify": "^1.0.1", "rollup": "^4.59.0", "scripts": "0.19.0" diff --git a/packages/@stylexjs/babel-plugin/test-utils/babel-conformance-adapter.js b/packages/@stylexjs/babel-plugin/test-utils/babel-conformance-adapter.js new file mode 100644 index 000000000..3d63dde23 --- /dev/null +++ b/packages/@stylexjs/babel-plugin/test-utils/babel-conformance-adapter.js @@ -0,0 +1,91 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +'use strict'; + +import { transformFileSync } from '@babel/core'; + +import stylexPlugin from '../src/index'; + +/** + * Adapts the StyleX Babel plugin to the `compiler-conformance` adapter + * contract. Everything Babel-specific lives here: parser configuration, the + * shape of the transform result, and capturing diagnostics off the console. + * + * See `packages/compiler-conformance/README.md` for the contract itself. + */ + +function formatDiagnostic(args) { + return args + .map((value) => + typeof value === 'string' ? value : JSON.stringify(value, null, 2), + ) + .join(' '); +} + +// The fixture `syntax` list uses the same names as `@babel/parser` plugins. +function toParserOpts(syntax) { + return { plugins: [...syntax] }; +} + +function transform(fixture) { + const warnings = []; + const errors = []; + const originalWarn = console.warn; + const originalError = console.error; + + console.warn = (...args) => { + warnings.push(formatDiagnostic(args)); + }; + console.error = (...args) => { + errors.push(formatDiagnostic(args)); + }; + + try { + const result = transformFileSync(fixture.entryPath, { + babelrc: false, + configFile: false, + filename: fixture.entryPath, + parserOpts: toParserOpts(fixture.syntax), + plugins: [[stylexPlugin, fixture.pluginOptions]], + }); + + if (result == null) { + throw new Error( + `Babel produced no result for fixture "${fixture.name}".`, + ); + } + + const metadata = result.metadata?.stylex ?? []; + + return { + css: stylexPlugin.processStylexRules(metadata, fixture.processOptions), + errors, + js: result.code, + metadata, + status: 'ok', + warnings, + }; + } catch (error) { + return { + error: { + message: error instanceof Error ? error.message : String(error), + }, + errors, + status: 'error', + warnings, + }; + } finally { + console.warn = originalWarn; + console.error = originalError; + } +} + +export const babelAdapter = { + name: 'babel', + transform, +}; diff --git a/packages/compiler-conformance/README.md b/packages/compiler-conformance/README.md new file mode 100644 index 000000000..3fafd5def --- /dev/null +++ b/packages/compiler-conformance/README.md @@ -0,0 +1,174 @@ +# compiler-conformance + +Golden fixtures that describe what a StyleX compiler must do, independently of +how it is implemented. + +Each fixture pins the four observable outputs of compiling one file: the +generated JavaScript, the StyleX metadata, the generated CSS, and the +diagnostics. Fixtures are plain data — an implementation written in any language +can read them and check itself against the same contract. + +The StyleX Babel plugin is the first consumer, via the adapter in +`packages/@stylexjs/babel-plugin/test-utils/babel-conformance-adapter.js` and +the test in `packages/@stylexjs/babel-plugin/__tests__/golden-fixtures-test.js`. + +## Layout + +``` +fixtures// + manifest.json how to compile the fixture + input.js the entry file (name set by `entry`) + expected.json the recorded result + ... any extra files the entry imports +``` + +## `manifest.json` + +| Field | Type | Default | Meaning | +| ---------------- | ---------- | ----------------------------------------------------- | ------------------------------------------------------------ | +| `description` | `string` | `""` | What behavior this fixture pins down. | +| `entry` | `string` | `"input.js"` | File in the fixture directory to compile. | +| `syntax` | `string[]` | `["flow"]` | Source syntax the entry uses, e.g. `["flow", "jsx"]`. | +| `pluginOptions` | `object` | `{}` | StyleX compiler options. | +| `processOptions` | `object` | `{"useLayers": false, "enableLTRRTLComments": false}` | Options for the step that assembles rules into a stylesheet. | + +`pluginOptions` and `processOptions` are StyleX's own documented options, so +they carry over unchanged between implementations. `syntax` is deliberately +abstract: an adapter maps it onto whatever its parser needs. + +Avoid options that take an absolute path. Fixture output must not depend on +where the repository is checked out. + +## `expected.json` + +For a fixture that compiles successfully: + +```json +{ + "status": "ok", + "js": "…generated JavaScript…", + "metadata": [ + ["x1e2nbdu", { "ltr": ".x1e2nbdu{color:red}", "rtl": null }, 3000] + ], + "css": ".x1e2nbdu{color:red}", + "warnings": [], + "errors": [] +} +``` + +For a fixture whose transform is expected to fail: + +```json +{ + "status": "error", + "error": { "message": "create() can only accept an object." }, + "warnings": [], + "errors": [] +} +``` + +`warnings` and `errors` are non-fatal diagnostics, split by the channel they +were reported on. A fixture can succeed and still report diagnostics — +`options-warning` does exactly that. `error` is the fatal one that ended the +transform. + +## How results are compared + +**`js` is compared semantically**, as a normalized syntax tree. Ignored are +whitespace, indentation, semicolons, comments, quote style and numeric literal +spelling, how a non-computed property key is written (`{a: 1}`, `{'a': 1}` and +`{1: x}` name the same properties as `{"a": 1}`, `{a: 1}` and `{"1": x}`), and +property shorthand (`{a}` equals `{a: a}`). So an implementation only has to +agree on the program it emits, not on how it prints it. The string stored in +`expected.json` is one valid printing of that program; reformatting it does not +change the outcome. When the programs genuinely differ, the Babel test falls +back to asserting on the sources so the failure shows a readable diff. + +**`metadata`, `css`, `warnings`, `errors` and `status` are compared exactly**, +after normalization: + +- Line endings are normalized to `\n`, and trailing whitespace is stripped from + each CSS line. +- Absolute paths become `` and ``. +- In diagnostics, a leading `/: ` location prefix is + dropped, a trailing source excerpt (code frame) is dropped, and a leading + `[implementation-name]` tag is collapsed to `[stylex]`. What is compared is + the message itself, not how an implementation decorates it. +- Object keys are sorted, because key order carries no meaning in JSON. **Array + order is preserved**, because the order of emitted rules is part of the + contract. + +## Consuming this suite from another implementation + +Implement an adapter — compile one fixture, return its result in the shape below +— and the shared runner does the rest: + +```js +{ + status: 'ok', + js: '…', // generated JavaScript + metadata: [...], // StyleX metadata + css: '…', // stylesheet assembled from the metadata + warnings: [...], // non-fatal diagnostics + errors: [...], +} +``` + +or, when the transform fails: + +```js +{ status: 'error', error: { message: '…' }, warnings: [...], errors: [...] } +``` + +From JavaScript, drive it with the helpers this package exports: + +```js +const { + exactPart, + getFixtureNames, + isJsEquivalent, + jsPart, + loadFixture, + normalizeResult, + readExpected, +} = require('compiler-conformance'); + +for (const name of getFixtureNames()) { + const fixture = loadFixture(name); + const actual = normalizeResult(fixture, myAdapter.transform(fixture)); + const expected = readExpected(name, fixture); + + assert.deepStrictEqual(exactPart(actual), exactPart(expected)); + assert.ok(isJsEquivalent(jsPart(expected), jsPart(actual), fixture.syntax)); +} +``` + +From another language, read `manifest.json` and `expected.json` directly and +apply the rules in [How results are compared](#how-results-are-compared). The +fixtures are the contract; these helpers are only a convenience. + +Two things to be aware of when porting: + +- Some fixtures depend on their own location. `define-vars-theme` uses + `commonJS` module resolution, which names a file + `:` — here + `compiler-conformance:fixtures/define-vars-theme/input.stylex.js`. That name + is hashed into the generated CSS variables, so it is stable across checkouts + but changes if the fixture is moved or the package is renamed. +- `props-and-merge` is the only fixture that needs JSX. + +## Updating the recorded output + +Re-record every `expected.json` from the current Babel output and review the +diff: + +``` +STYLEX_UPDATE_GOLDEN=1 npx jest golden-fixtures +``` + +from `packages/@stylexjs/babel-plugin`. A change here is a change to the +behavioral contract, so the diff deserves the same scrutiny as the code that +caused it. + +The comparison rules above are themselves tested, independently of any compiler +— run `npm test` in this package. diff --git a/packages/compiler-conformance/__tests__/conformance-harness-test.js b/packages/compiler-conformance/__tests__/conformance-harness-test.js new file mode 100644 index 000000000..f6d424244 --- /dev/null +++ b/packages/compiler-conformance/__tests__/conformance-harness-test.js @@ -0,0 +1,209 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +'use strict'; + +const fs = require('node:fs'); + +const { + exactPart, + getExpectedPath, + getFixtureNames, + isJsEquivalent, + jsPart, + loadFixture, + normalizeCss, + normalizeData, + normalizeDiagnostic, + normalizePaths, + normalizeResult, +} = require('../src/index'); + +const FIXTURE = { + dir: '/repo/packages/compiler-conformance/fixtures/demo', + entry: 'input.js', + repoRoot: '/repo', +}; +const CONTEXT = { + entry: FIXTURE.entry, + fixtureDir: FIXTURE.dir, + repoRoot: FIXTURE.repoRoot, +}; + +describe('normalizePaths', () => { + test('replaces the fixture directory before the repository root', () => { + expect( + normalizePaths(`${FIXTURE.dir}/input.js imports /repo/other.js`, CONTEXT), + ).toBe('/input.js imports /other.js'); + }); + + test('normalizes line endings', () => { + expect(normalizePaths('a\r\nb', CONTEXT)).toBe('a\nb'); + }); +}); + +describe('normalizeDiagnostic', () => { + test('drops the location prefix and the source excerpt', () => { + const message = [ + `${FIXTURE.dir}/input.js: create() can only accept an object.`, + ' 1 | import * as stylex from "@stylexjs/stylex";', + '> 2 | export const styles = stylex.create(1);', + ' | ^^^^^^^^^^^^^^^^', + ].join('\n'); + + expect(normalizeDiagnostic(message, CONTEXT)).toBe( + 'create() can only accept an object.', + ); + }); + + test('collapses the implementation tag', () => { + expect( + normalizeDiagnostic('[@stylexjs/babel-plugin] Expected a boolean.', { + ...CONTEXT, + }), + ).toBe('[stylex] Expected a boolean.'); + }); + + test('leaves a message that merely opens with a bracket alone', () => { + expect(normalizeDiagnostic('[1, 2] is not a valid value.', CONTEXT)).toBe( + '[1, 2] is not a valid value.', + ); + }); + + test('leaves a pipe that is not a source excerpt alone', () => { + expect( + normalizeDiagnostic('bad value\n|start| is reserved.', CONTEXT), + ).toBe('bad value\n|start| is reserved.'); + }); +}); + +describe('normalizeCss', () => { + test('strips trailing whitespace from every line and trims', () => { + expect( + normalizeCss('\n.a{color:red} \n.b{color:blue}\t\n\n', CONTEXT), + ).toBe('.a{color:red}\n.b{color:blue}'); + }); +}); + +describe('normalizeData', () => { + test('sorts object keys', () => { + expect(JSON.stringify(normalizeData({ b: 1, a: 2 }, CONTEXT))).toBe( + '{"a":2,"b":1}', + ); + }); + + test('preserves array order, because rule order is part of the contract', () => { + expect(normalizeData(['z', 'a'], CONTEXT)).toEqual(['z', 'a']); + }); +}); + +describe('isJsEquivalent', () => { + test('ignores formatting, quote style and comments', () => { + expect( + isJsEquivalent( + 'export const a = {x: 1};', + '// a comment\nexport const a = {\n "x": 1,\n}\n', + ['flow'], + ), + ).toBe(true); + }); + + test('ignores how a property key is spelled', () => { + expect(isJsEquivalent('({a: 1});', '({"a": 1});', ['flow'])).toBe(true); + expect(isJsEquivalent('({1: x});', '({"1": x});', ['flow'])).toBe(true); + }); + + test('ignores property shorthand', () => { + expect(isJsEquivalent('({a});', '({a: a});', ['flow'])).toBe(true); + }); + + test('reports a genuine difference', () => { + expect( + isJsEquivalent('export const a = 1;', 'export const a = 2;', ['flow']), + ).toBe(false); + }); + + test('still reports a differently named property', () => { + expect(isJsEquivalent('({a: 1});', '({b: 1});', ['flow'])).toBe(false); + }); + + test('still reports a computed key', () => { + expect(isJsEquivalent('({a: 1});', '({[a]: 1});', ['flow'])).toBe(false); + }); + + test('honors the requested syntax', () => { + expect( + isJsEquivalent('const a =
;', 'const a =
;', [ + 'flow', + 'jsx', + ]), + ).toBe(true); + }); + + test('reports a difference when only one side produced output', () => { + expect(isJsEquivalent(null, 'export const a = 1;', ['flow'])).toBe(false); + }); + + test('fails loudly on unparseable output', () => { + expect(() => + isJsEquivalent('export const a = 1;', 'const = ;', ['flow']), + ).toThrow(/Failed to parse the actual output as JavaScript/); + }); +}); + +describe('normalizeResult', () => { + test('normalizes a successful transform', () => { + const result = normalizeResult(FIXTURE, { + css: '.a{color:red} ', + js: ' export const a = 1; ', + metadata: [['a', { rtl: null, ltr: '.a{color:red}' }, 3000]], + status: 'ok', + warnings: ['[@stylexjs/babel-plugin] heads up'], + }); + + expect(result).toEqual({ + css: '.a{color:red}', + errors: [], + js: 'export const a = 1;', + metadata: [['a', { ltr: '.a{color:red}', rtl: null }, 3000]], + status: 'ok', + warnings: ['[stylex] heads up'], + }); + expect(exactPart(result)).not.toHaveProperty('js'); + expect(jsPart(result)).toBe('export const a = 1;'); + }); + + test('normalizes a failed transform', () => { + const result = normalizeResult(FIXTURE, { + error: { + message: `${FIXTURE.dir}/input.js: create() can only accept an object.\n> 1 | x\n | ^`, + }, + status: 'error', + }); + + expect(result).toEqual({ + error: { message: 'create() can only accept an object.' }, + errors: [], + status: 'error', + warnings: [], + }); + expect(jsPart(result)).toBeNull(); + }); +}); + +describe('fixtures', () => { + test('every fixture ships an entry file and a recorded result', () => { + const names = getFixtureNames(); + expect(names.length).toBeGreaterThan(0); + + for (const name of names) { + const fixture = loadFixture(name); + expect(fs.existsSync(fixture.entryPath)).toBe(true); + expect(fs.existsSync(getExpectedPath(name))).toBe(true); + } + }); +}); diff --git a/packages/compiler-conformance/fixtures/create-basic/expected.json b/packages/compiler-conformance/fixtures/create-basic/expected.json new file mode 100644 index 000000000..fc6bdd2c0 --- /dev/null +++ b/packages/compiler-conformance/fixtures/create-basic/expected.json @@ -0,0 +1,25 @@ +{ + "css": ".xrkmrrc{background-color:red}\n.xju2f9n{color:blue}", + "errors": [], + "js": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport * as stylex from '@stylexjs/stylex';\nexport const styles = {\n root: {\n kWkggS: \"xrkmrrc\",\n kMwMTN: \"xju2f9n\",\n $$css: true\n }\n};", + "metadata": [ + [ + "xrkmrrc", + { + "ltr": ".xrkmrrc{background-color:red}", + "rtl": null + }, + 3000 + ], + [ + "xju2f9n", + { + "ltr": ".xju2f9n{color:blue}", + "rtl": null + }, + 3000 + ] + ], + "status": "ok", + "warnings": [] +} diff --git a/packages/compiler-conformance/fixtures/create-basic/input.js b/packages/compiler-conformance/fixtures/create-basic/input.js new file mode 100644 index 000000000..7014bc7a9 --- /dev/null +++ b/packages/compiler-conformance/fixtures/create-basic/input.js @@ -0,0 +1,15 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as stylex from '@stylexjs/stylex'; + +export const styles = stylex.create({ + root: { + backgroundColor: 'red', + color: 'blue', + }, +}); diff --git a/packages/compiler-conformance/fixtures/create-basic/manifest.json b/packages/compiler-conformance/fixtures/create-basic/manifest.json new file mode 100644 index 000000000..cc7c695b2 --- /dev/null +++ b/packages/compiler-conformance/fixtures/create-basic/manifest.json @@ -0,0 +1,4 @@ +{ + "description": "stylex.create() with static declarations compiles to a style object and two atomic CSS rules.", + "entry": "input.js" +} diff --git a/packages/compiler-conformance/fixtures/define-vars-theme/expected.json b/packages/compiler-conformance/fixtures/define-vars-theme/expected.json new file mode 100644 index 000000000..f95273d6f --- /dev/null +++ b/packages/compiler-conformance/fixtures/define-vars-theme/expected.json @@ -0,0 +1,33 @@ +{ + "css": ":root, .xnsr5hh{--xkoxvr5:red;--x19047mj:white;}\n@media (prefers-color-scheme: dark){:root, .xnsr5hh{--x19047mj:black;}}\n.x19gg9ko.x19gg9ko, .x19gg9ko.x19gg9ko:root{--x19047mj:yellow;--xkoxvr5:blue;}", + "errors": [], + "js": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport * as stylex from '@stylexjs/stylex';\nexport const vars = {\n fg: \"var(--xkoxvr5)\",\n bg: \"var(--x19047mj)\",\n __varGroupHash__: \"xnsr5hh\"\n};\nexport const theme = {\n xnsr5hh: \"x19gg9ko xnsr5hh\",\n $$css: true\n};", + "metadata": [ + [ + "xnsr5hh", + { + "ltr": ":root, .xnsr5hh{--xkoxvr5:red;--x19047mj:white;}", + "rtl": null + }, + 0.1 + ], + [ + "xnsr5hh-1lveb7", + { + "ltr": "@media (prefers-color-scheme: dark){:root, .xnsr5hh{--x19047mj:black;}}", + "rtl": null + }, + 0.2 + ], + [ + "x19gg9ko", + { + "ltr": ".x19gg9ko, .x19gg9ko:root{--x19047mj:yellow;--xkoxvr5:blue;}", + "rtl": null + }, + 0.5 + ] + ], + "status": "ok", + "warnings": [] +} diff --git a/packages/compiler-conformance/fixtures/define-vars-theme/input.stylex.js b/packages/compiler-conformance/fixtures/define-vars-theme/input.stylex.js new file mode 100644 index 000000000..3b1c47060 --- /dev/null +++ b/packages/compiler-conformance/fixtures/define-vars-theme/input.stylex.js @@ -0,0 +1,21 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as stylex from '@stylexjs/stylex'; + +export const vars = stylex.defineVars({ + fg: 'red', + bg: { + default: 'white', + '@media (prefers-color-scheme: dark)': 'black', + }, +}); + +export const theme = stylex.createTheme(vars, { + fg: 'blue', + bg: 'yellow', +}); diff --git a/packages/compiler-conformance/fixtures/define-vars-theme/manifest.json b/packages/compiler-conformance/fixtures/define-vars-theme/manifest.json new file mode 100644 index 000000000..1075071b5 --- /dev/null +++ b/packages/compiler-conformance/fixtures/define-vars-theme/manifest.json @@ -0,0 +1,9 @@ +{ + "description": "stylex.defineVars() and stylex.createTheme() emit CSS custom properties, including a media-query override. The entry is a `.stylex.js` file so that `commonJS` module resolution derives a canonical name for it; that name is `:`, so the generated hashes depend on the fixture's location inside this package but not on where the repository is checked out.", + "entry": "input.stylex.js", + "pluginOptions": { + "unstable_moduleResolution": { + "type": "commonJS" + } + } +} diff --git a/packages/compiler-conformance/fixtures/keyframes-and-when/expected.json b/packages/compiler-conformance/fixtures/keyframes-and-when/expected.json new file mode 100644 index 000000000..231d3a0b1 --- /dev/null +++ b/packages/compiler-conformance/fixtures/keyframes-and-when/expected.json @@ -0,0 +1,41 @@ +{ + "css": "@keyframes xekv6nw-B{0%{opacity:0;}100%{opacity:1;}}\n.x127lhb5:not(#\\#){animation-name:xekv6nw-B}\n.x1e2nbdu:not(#\\#){color:red}\n.x1tavwxn.x1tavwxn:where(.x-default-marker:focus *):not(#\\#){color:blue}", + "errors": [], + "js": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport * as stylex from '@stylexjs/stylex';\nexport const styles = {\n root: {\n kKVMdj: \"x127lhb5\",\n kMwMTN: \"x1e2nbdu x1tavwxn\",\n $$css: true\n }\n};", + "metadata": [ + [ + "xekv6nw-B", + { + "ltr": "@keyframes xekv6nw-B{0%{opacity:0;}100%{opacity:1;}}", + "rtl": null + }, + 0 + ], + [ + "x127lhb5", + { + "ltr": ".x127lhb5{animation-name:xekv6nw-B}", + "rtl": null + }, + 3000 + ], + [ + "x1e2nbdu", + { + "ltr": ".x1e2nbdu{color:red}", + "rtl": null + }, + 3000 + ], + [ + "x1tavwxn", + { + "ltr": ".x1tavwxn.x1tavwxn:where(.x-default-marker:focus *){color:blue}", + "rtl": null + }, + 3011.5 + ] + ], + "status": "ok", + "warnings": [] +} diff --git a/packages/compiler-conformance/fixtures/keyframes-and-when/input.js b/packages/compiler-conformance/fixtures/keyframes-and-when/input.js new file mode 100644 index 000000000..3dbfb8ee5 --- /dev/null +++ b/packages/compiler-conformance/fixtures/keyframes-and-when/input.js @@ -0,0 +1,25 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as stylex from '@stylexjs/stylex'; + +export const styles = stylex.create({ + root: { + animationName: stylex.keyframes({ + '0%': { + opacity: 0, + }, + '100%': { + opacity: 1, + }, + }), + color: { + default: 'red', + [stylex.when.ancestor(':focus')]: 'blue', + }, + }, +}); diff --git a/packages/compiler-conformance/fixtures/keyframes-and-when/manifest.json b/packages/compiler-conformance/fixtures/keyframes-and-when/manifest.json new file mode 100644 index 000000000..b00fdc7f3 --- /dev/null +++ b/packages/compiler-conformance/fixtures/keyframes-and-when/manifest.json @@ -0,0 +1,4 @@ +{ + "description": "stylex.keyframes() emits an @keyframes rule at priority 0 and stylex.when.ancestor() emits a conditional rule with raised specificity.", + "entry": "input.js" +} diff --git a/packages/compiler-conformance/fixtures/options-warning/expected.json b/packages/compiler-conformance/fixtures/options-warning/expected.json new file mode 100644 index 000000000..7e9460a62 --- /dev/null +++ b/packages/compiler-conformance/fixtures/options-warning/expected.json @@ -0,0 +1,10 @@ +{ + "css": "", + "errors": [ + "[stylex] Expected (options.enableFontSizePxToRem) to be a boolean, but got `\"true\"`." + ], + "js": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nexport const value = 1;", + "metadata": [], + "status": "ok", + "warnings": [] +} diff --git a/packages/compiler-conformance/fixtures/options-warning/input.js b/packages/compiler-conformance/fixtures/options-warning/input.js new file mode 100644 index 000000000..b1fb6b659 --- /dev/null +++ b/packages/compiler-conformance/fixtures/options-warning/input.js @@ -0,0 +1,8 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +export const value = 1; diff --git a/packages/compiler-conformance/fixtures/options-warning/manifest.json b/packages/compiler-conformance/fixtures/options-warning/manifest.json new file mode 100644 index 000000000..468c9e31f --- /dev/null +++ b/packages/compiler-conformance/fixtures/options-warning/manifest.json @@ -0,0 +1,7 @@ +{ + "description": "An option of the wrong type reports a non-fatal diagnostic on the error channel while the transform still succeeds.", + "entry": "input.js", + "pluginOptions": { + "enableFontSizePxToRem": "true" + } +} diff --git a/packages/compiler-conformance/fixtures/props-and-merge/expected.json b/packages/compiler-conformance/fixtures/props-and-merge/expected.json new file mode 100644 index 000000000..b1863f4b0 --- /dev/null +++ b/packages/compiler-conformance/fixtures/props-and-merge/expected.json @@ -0,0 +1,25 @@ +{ + "css": ".x1e2nbdu{color:red}\n.x17z2mba:hover{color:blue}", + "errors": [], + "js": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport * as stylex from '@stylexjs/stylex';\nimport React from 'react';\nexport function Button(props) {\n return