diff --git a/package-lock.json b/package-lock.json index 0b11075cefd88d..fd198e3ca71536 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21006,6 +21006,16 @@ "node": ">=4" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -29055,6 +29065,267 @@ "node": ">=12" } }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -47969,6 +48240,7 @@ "@wordpress/validation-tools": "file:../../tools/validation", "esbuild": "^0.27.2", "esbuild-esm-loader": "^0.3.3", + "lightningcss": "^1.19.0", "prettier": "npm:wp-prettier@^3.0.3", "storybook": "^10.4.3", "stylelint": "^16.26.1" @@ -47980,6 +48252,7 @@ "peerDependencies": { "@types/react": "^18 || ^19", "esbuild": ">=0.27.2 <1.0.0", + "lightningcss": ">=1.19.0 <2.0.0", "postcss": "^8.0.0", "react": "^18 || ^19", "react-dom": "^18 || ^19", @@ -47993,6 +48266,9 @@ "esbuild": { "optional": true }, + "lightningcss": { + "optional": true + }, "postcss": { "optional": true }, diff --git a/packages/theme/CHANGELOG.md b/packages/theme/CHANGELOG.md index d607a29299ce7c..1310e65728c9bf 100644 --- a/packages/theme/CHANGELOG.md +++ b/packages/theme/CHANGELOG.md @@ -4,6 +4,7 @@ ### Enhancements +- Add a Lightning CSS visitor plugin that injects design system token fallbacks, available as `@wordpress/theme/lightningcss-plugins/lightningcss-ds-token-fallbacks`, with an optional peer range of `>=1.19.0 <2.0.0` ([#80401](https://github.com/WordPress/gutenberg/pull/80401)). - Widen optional peer dependency ranges so projects on newer tooling can install without peer resolution conflicts: Vite `^7 || ^8`, Stylelint `^16 || ^17`, and esbuild `>=0.27.2 <1.0.0` ([#80267](https://github.com/WordPress/gutenberg/pull/80267)). ### Documentation diff --git a/packages/theme/README.md b/packages/theme/README.md index 732e58c0a5760f..a82f9ad6cbe727 100644 --- a/packages/theme/README.md +++ b/packages/theme/README.md @@ -244,11 +244,12 @@ The build plugins inject generated fallbacks into bare `var(--wpds-*)` reference `@wordpress/build` already applies these plugins automatically when `@wordpress/theme` is installed. You only need to configure them manually for custom build setups. -| Export | Tool | Scope | -| ------------------------------------------------------------- | ------- | ----- | -| `@wordpress/theme/postcss-plugins/postcss-ds-token-fallbacks` | PostCSS | CSS | -| `@wordpress/theme/esbuild-plugins/esbuild-ds-token-fallbacks` | esbuild | JS/TS | -| `@wordpress/theme/vite-plugins/vite-ds-token-fallbacks` | Vite | JS/TS | +| Export | Tool | Scope | +| ----------------------------------------------------------------------- | ------------- | ----- | +| `@wordpress/theme/postcss-plugins/postcss-ds-token-fallbacks` | PostCSS | CSS | +| `@wordpress/theme/lightningcss-plugins/lightningcss-ds-token-fallbacks` | Lightning CSS | CSS | +| `@wordpress/theme/esbuild-plugins/esbuild-ds-token-fallbacks` | esbuild | JS/TS | +| `@wordpress/theme/vite-plugins/vite-ds-token-fallbacks` | Vite | JS/TS | Existing fallbacks are unchanged. An unknown token in a bare reference fails the build. @@ -263,6 +264,21 @@ export default { }; ``` +### Lightning CSS + +```js +import { transform, composeVisitors } from 'lightningcss'; +import dsTokenFallbacks from '@wordpress/theme/lightningcss-plugins/lightningcss-ds-token-fallbacks'; + +const { code } = transform( { + filename: 'styles.css', + code: Buffer.from( css ), + visitor: composeVisitors( [ dsTokenFallbacks ] ), +} ); +``` + +> **Note:** CSS Modules [`from global`](https://lightningcss.dev/css-modules.html#local-css-variables) references (for example `var(--wpds-dimension-gap-sm from global)` with `cssModules.dashedIdents`) are not yet supported. The visitor rebuilds `var()` from the token name alone, so `from` metadata is dropped and global custom properties may be incorrectly hashed. + ### esbuild ```js diff --git a/packages/theme/lightningcss-plugins/lightningcss-ds-token-fallbacks.mjs b/packages/theme/lightningcss-plugins/lightningcss-ds-token-fallbacks.mjs new file mode 100644 index 00000000000000..bb70c0fa8ab173 --- /dev/null +++ b/packages/theme/lightningcss-plugins/lightningcss-ds-token-fallbacks.mjs @@ -0,0 +1,32 @@ +import { addFallbackToVar } from '../postcss-plugins/ds-token-fallbacks.mjs'; + +/** + * Lightning CSS visitor that injects design-system token fallbacks into CSS. + * + * Replaces bare `var(--wpds-*)` references with `var(--wpds-*, )`. + * + * Existing fallbacks are left untouched. Unknown tokens throw. + * + * @type {import('lightningcss').Visitor} + */ +const plugin = { + /** @param {import('lightningcss').Variable} variable */ + Variable( variable ) { + // Leave existing fallbacks alone, including the valid empty fallback + // form `var(--token,)` which Lightning CSS parses as `fallback: []`. + if ( variable.fallback !== null ) { + return; + } + + const tokenName = variable.name.ident; + if ( ! tokenName.startsWith( '--wpds-' ) ) { + return; + } + + return { + raw: addFallbackToVar( `var(${ tokenName })` ), + }; + }, +}; + +export default plugin; diff --git a/packages/theme/package.json b/packages/theme/package.json index 6ae2783aca1188..575520325c46f4 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -51,6 +51,10 @@ "types": "./build-types/postcss-plugins/postcss-ds-token-fallbacks.d.mts", "default": "./postcss-plugins/postcss-ds-token-fallbacks.mjs" }, + "./lightningcss-plugins/lightningcss-ds-token-fallbacks": { + "types": "./build-types/lightningcss-plugins/lightningcss-ds-token-fallbacks.d.mts", + "default": "./lightningcss-plugins/lightningcss-ds-token-fallbacks.mjs" + }, "./esbuild-plugins/esbuild-ds-token-fallbacks": { "types": "./build-types/esbuild-plugins/esbuild-ds-token-fallbacks.d.mts", "default": "./esbuild-plugins/esbuild-ds-token-fallbacks.mjs" @@ -100,6 +104,7 @@ "@wordpress/validation-tools": "file:../../tools/validation", "esbuild": "^0.27.2", "esbuild-esm-loader": "^0.3.3", + "lightningcss": "^1.19.0", "prettier": "npm:wp-prettier@^3.0.3", "storybook": "^10.4.3", "stylelint": "^16.26.1" @@ -107,6 +112,7 @@ "peerDependencies": { "@types/react": "^18 || ^19", "esbuild": ">=0.27.2 <1.0.0", + "lightningcss": ">=1.19.0 <2.0.0", "postcss": "^8.0.0", "react": "^18 || ^19", "react-dom": "^18 || ^19", @@ -120,6 +126,9 @@ "esbuild": { "optional": true }, + "lightningcss": { + "optional": true + }, "postcss": { "optional": true }, diff --git a/packages/theme/src/postcss-plugins/test/add-fallback-to-var.test.ts b/packages/theme/src/postcss-plugins/test/add-fallback-to-var.test.ts index 4614848eb00937..51e3935374e37f 100644 --- a/packages/theme/src/postcss-plugins/test/add-fallback-to-var.test.ts +++ b/packages/theme/src/postcss-plugins/test/add-fallback-to-var.test.ts @@ -42,6 +42,12 @@ describe( 'addFallbackToVar', () => { ).toBe( 'var(--wpds-border-radius-sm, 999px)' ); } ); + it( 'leaves a var() with an empty fallback untouched', () => { + expect( + addFallbackToVar( 'var(--wpds-dimension-gap-sm,)', mockFallbacks ) + ).toBe( 'var(--wpds-dimension-gap-sm,)' ); + } ); + it( 'handles multiple var() calls in one value', () => { const input = 'var(--wpds-dimension-gap-sm) var(--wpds-dimension-gap-lg)'; diff --git a/packages/theme/src/postcss-plugins/test/ds-token-fallbacks.test.ts b/packages/theme/src/postcss-plugins/test/ds-token-fallbacks.test.ts index b27c053de37b24..de9e77c2d3be73 100644 --- a/packages/theme/src/postcss-plugins/test/ds-token-fallbacks.test.ts +++ b/packages/theme/src/postcss-plugins/test/ds-token-fallbacks.test.ts @@ -21,6 +21,12 @@ describe( 'addFallbackToVar', () => { ).toBe( 'var(--wpds-border-radius-sm, 999px)' ); } ); + it( 'leaves a var() with an empty fallback untouched', () => { + expect( addFallbackToVar( 'var(--wpds-dimension-gap-sm,)' ) ).toBe( + 'var(--wpds-dimension-gap-sm,)' + ); + } ); + it( 'leaves var() calls that are not design token properties', () => { expect( addFallbackToVar( 'var(--my-custom-prop)' ) ).toBe( 'var(--my-custom-prop)' diff --git a/packages/theme/src/test/__snapshots__/build-plugin-parity.test.ts.snap b/packages/theme/src/test/__snapshots__/build-plugin-parity.test.ts.snap index 2bac55364b7a1d..5f5b4f0d9746eb 100644 --- a/packages/theme/src/test/__snapshots__/build-plugin-parity.test.ts.snap +++ b/packages/theme/src/test/__snapshots__/build-plugin-parity.test.ts.snap @@ -25,3 +25,18 @@ exports[`design token fallback build plugin parity transforms supported CSS in s } " `; + +exports[`design token fallback build plugin parity transforms supported CSS with Lightning CSS in styles.css 1`] = ` +".fixture { + color: var(--wpds-color-foreground-content-neutral, #1e1e1e); + content: "var(--wpds-border-radius-sm)"; +} +" +`; + +exports[`design token fallback build plugin parity transforms supported CSS with Lightning CSS in styles.module.css 1`] = ` +".fixture { + gap: var(--wpds-dimension-gap-sm, 8px); +} +" +`; diff --git a/packages/theme/src/test/build-plugin-parity.test.ts b/packages/theme/src/test/build-plugin-parity.test.ts index 3d6dc83b96565d..917be39366106e 100644 --- a/packages/theme/src/test/build-plugin-parity.test.ts +++ b/packages/theme/src/test/build-plugin-parity.test.ts @@ -6,15 +6,27 @@ import type { OnLoadResult, PluginBuild, } from 'esbuild'; +import { transform as lightningcssTransform } from 'lightningcss'; import postcss from 'postcss'; import esbuildPlugin from '../../esbuild-plugins/esbuild-ds-token-fallbacks.mjs'; +import lightningcssPlugin from '../../lightningcss-plugins/lightningcss-ds-token-fallbacks.mjs'; import postcssPlugin from '../../postcss-plugins/postcss-ds-token-fallbacks.mjs'; import vitePlugin from '../../vite-plugins/vite-ds-token-fallbacks.mjs'; const fixturesDirectory = join( __dirname, 'fixtures/build-plugins' ); const validJsFixture = join( fixturesDirectory, 'source.ts' ); const unknownJsFixture = join( fixturesDirectory, 'unknown-token.ts' ); +const emptyFallbackCssFixture = join( fixturesDirectory, 'empty-fallback.css' ); +const emptyFallbackJsFixture = join( fixturesDirectory, 'empty-fallback.ts' ); + +function transformWithLightningcss( source: string, filename: string ) { + return lightningcssTransform( { + filename, + code: Buffer.from( source ), + visitor: lightningcssPlugin, + } ).code.toString(); +} type EsbuildOnLoad = ( args: OnLoadArgs @@ -74,6 +86,78 @@ describe( 'design token fallback build plugin parity', () => { } ); + it.each( [ 'styles.css', 'styles.module.css' ] )( + 'transforms supported CSS with Lightning CSS in %s', + async ( fixture ) => { + const filename = join( fixturesDirectory, fixture ); + const source = await readFile( filename, 'utf8' ); + const css = transformWithLightningcss( source, filename ); + + expect( css ).toMatchSnapshot(); + } + ); + + it( 'keeps PostCSS and Lightning CSS var() fallbacks aligned', async () => { + const filename = join( fixturesDirectory, 'styles.module.css' ); + const source = await readFile( filename, 'utf8' ); + const postcssResult = await postcss( [ postcssPlugin ] ).process( + source, + { from: filename } + ); + const lightningcssResult = transformWithLightningcss( + source, + filename + ); + + // styles.module.css only contains real var() references (no string + // contents), so both tools should inject the same fallback value. + expect( postcssResult.css ).toContain( + 'var(--wpds-dimension-gap-sm, 8px)' + ); + expect( lightningcssResult ).toContain( + 'var(--wpds-dimension-gap-sm, 8px)' + ); + } ); + + it( 'leaves an empty var() fallback untouched in PostCSS', async () => { + const source = await readFile( emptyFallbackCssFixture, 'utf8' ); + const result = await postcss( [ postcssPlugin ] ).process( source, { + from: emptyFallbackCssFixture, + } ); + + expect( result.css ).toContain( 'gap: var(--wpds-dimension-gap-sm,);' ); + } ); + + it( 'leaves an empty var() fallback untouched in Lightning CSS', async () => { + const source = await readFile( emptyFallbackCssFixture, 'utf8' ); + const result = transformWithLightningcss( + source, + emptyFallbackCssFixture + ); + + // Lightning CSS normalizes empty fallbacks with a space before `)`. + expect( result ).toContain( 'var(--wpds-dimension-gap-sm, )' ); + } ); + + it( 'leaves an empty var() fallback untouched in esbuild', async () => { + const result = await getEsbuildHook().transform( { + path: emptyFallbackJsFixture, + } as OnLoadArgs ); + + expect( result?.contents ).toContain( + 'gap: var(--wpds-dimension-gap-sm,);' + ); + } ); + + it( 'leaves an empty var() fallback untouched in Vite', async () => { + const source = await readFile( emptyFallbackJsFixture, 'utf8' ); + const result = getViteTransform()( source, emptyFallbackJsFixture ); + + expect( result?.code ).toContain( + 'gap: var(--wpds-dimension-gap-sm,);' + ); + } ); + it( 'keeps esbuild and Vite source-text transforms aligned', async () => { const source = await readFile( validJsFixture, 'utf8' ); const esbuildResult = await getEsbuildHook().transform( { @@ -133,6 +217,9 @@ describe( 'design token fallback build plugin parity', () => { from: unknownCssFixture, } ) ).rejects.toThrow( 'Unknown design token: --wpds-not-a-token' ); + expect( () => + transformWithLightningcss( cssSource, unknownCssFixture ) + ).toThrow( 'Unknown design token: --wpds-not-a-token' ); await expect( getEsbuildHook().transform( { path: unknownJsFixture, diff --git a/packages/theme/src/test/fixtures/build-plugins/empty-fallback.css b/packages/theme/src/test/fixtures/build-plugins/empty-fallback.css new file mode 100644 index 00000000000000..cddeeb51121873 --- /dev/null +++ b/packages/theme/src/test/fixtures/build-plugins/empty-fallback.css @@ -0,0 +1,4 @@ +.fixture { + /* stylelint-disable-next-line plugin-wpds/no-token-fallback-values -- Fixture verifies empty fallback handling. */ + gap: var(--wpds-dimension-gap-sm,); +} diff --git a/packages/theme/src/test/fixtures/build-plugins/empty-fallback.ts b/packages/theme/src/test/fixtures/build-plugins/empty-fallback.ts new file mode 100644 index 00000000000000..bc81eda5455e18 --- /dev/null +++ b/packages/theme/src/test/fixtures/build-plugins/empty-fallback.ts @@ -0,0 +1 @@ +export const styles = 'gap: var(--wpds-dimension-gap-sm,);'; diff --git a/packages/theme/tsconfig.plugins.json b/packages/theme/tsconfig.plugins.json index 488e89b87e14e4..868baecaca0429 100644 --- a/packages/theme/tsconfig.plugins.json +++ b/packages/theme/tsconfig.plugins.json @@ -9,6 +9,7 @@ }, "include": [ "esbuild-plugins/*.mjs", + "lightningcss-plugins/*.mjs", "postcss-plugins/*.mjs", "prebuilt/js/*.mjs", "stylelint-plugins/*.mjs",