From 11803694160fc08ff3a8d45c4e0d1ff4487f7ae3 Mon Sep 17 00:00:00 2001 From: Ben Word Date: Wed, 11 Mar 2026 14:20:58 -0500 Subject: [PATCH 1/5] =?UTF-8?q?=F0=9F=90=9B=20Fix=20CSS=20variable=20extra?= =?UTF-8?q?ction=20skipping=20every=20other=20value=20in=20minified=20CSS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The regex patterns for extracting --color-*, --font-*, --text-*, and --radius-* variables included a trailing [;}]? that consumed the delimiter. In minified CSS (single line), this starved the next match's (?:^|[;{}]) anchor, causing every other variable to be skipped — e.g. only 6 of 11 color shades were extracted. Drop the trailing [;}]? since [^;}]+ already stops before the delimiter. Co-Authored-By: Claude Opus 4.6 --- src/index.ts | 8 +-- tests/build.test.ts | 119 +++++++++++++++++++++++++++++++++++++++ tests/fixture/app.css | 1 + tests/fixture/theme.json | 14 +++++ 4 files changed, 138 insertions(+), 4 deletions(-) create mode 100644 tests/build.test.ts create mode 100644 tests/fixture/app.css create mode 100644 tests/fixture/theme.json diff --git a/src/index.ts b/src/index.ts index 77c088a..0a6dd1d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1164,13 +1164,13 @@ export function wordpressThemeJson(config: ThemeJsonConfig = {}): VitePlugin { }; const patterns = { - COLOR: /(?:^|[;{}])\s*--color-([^:]+):\s*([^;}]+)[;}]?/gm, + COLOR: /(?:^|[;{}])\s*--color-([^:]+):\s*([^;}]+)/gm, FONT_FAMILY: - /(?:^|[;{}])\s*--font-([^:]+):\s*([^;}]+)[;}]?/gm, + /(?:^|[;{}])\s*--font-([^:]+):\s*([^;}]+)/gm, FONT_SIZE: - /(?:^|[;{}])\s*--text-([^:]+):\s*([^;}]+)[;}]?/gm, + /(?:^|[;{}])\s*--text-([^:]+):\s*([^;}]+)/gm, BORDER_RADIUS: - /(?:^|[;{}])\s*--radius-([^:]+):\s*([^;}]+)[;}]?/gm, + /(?:^|[;{}])\s*--radius-([^:]+):\s*([^;}]+)/gm, } as const; // Process colors from either @theme block or Tailwind config diff --git a/tests/build.test.ts b/tests/build.test.ts new file mode 100644 index 0000000..bfcc461 --- /dev/null +++ b/tests/build.test.ts @@ -0,0 +1,119 @@ +import { describe, expect, it, beforeAll, afterAll } from 'vitest'; +import { build } from 'vite'; +import tailwindcss from '@tailwindcss/vite'; +import { wordpressThemeJson } from '../src/index.js'; +import fs from 'fs'; +import path from 'path'; + +const fixtureDir = path.resolve(__dirname, 'fixture'); +const outDir = path.join(fixtureDir, 'dist'); + +async function runBuild(pluginOptions = {}) { + await build({ + plugins: [ + tailwindcss(), + wordpressThemeJson({ + baseThemeJsonPath: path.join(fixtureDir, 'theme.json'), + outputPath: 'theme.json', + ...pluginOptions, + }), + ], + build: { + rollupOptions: { + input: path.join(fixtureDir, 'app.css'), + }, + outDir, + emptyOutDir: true, + write: true, + }, + logLevel: 'silent', + }); + + return JSON.parse(fs.readFileSync(path.join(outDir, 'theme.json'), 'utf8')); +} + +describe('vite build integration', () => { + afterAll(() => { + fs.rmSync(outDir, { recursive: true, force: true }); + }); + + describe('colors', () => { + it('should extract all 11 shades for each color', async () => { + const themeJson = await runBuild(); + const palette = themeJson.settings.color.palette; + + const redColors = palette.filter((c: any) => c.slug.startsWith('red-')); + expect(redColors).toHaveLength(11); + + const expectedShades = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950]; + for (const shade of expectedShades) { + expect(palette).toContainEqual( + expect.objectContaining({ + name: `Red (${shade})`, + slug: `red-${shade}`, + }) + ); + } + }); + + it('should include all Tailwind color families', async () => { + const themeJson = await runBuild(); + const palette = themeJson.settings.color.palette; + const slugs = palette.map((c: any) => c.slug); + + const expectedFamilies = [ + 'red', 'orange', 'amber', 'yellow', 'lime', 'green', + 'emerald', 'teal', 'cyan', 'sky', 'blue', 'indigo', + 'violet', 'purple', 'fuchsia', 'pink', 'rose', + 'slate', 'gray', 'zinc', 'neutral', 'stone', + ]; + + for (const family of expectedFamilies) { + expect(slugs.some((s: string) => s.startsWith(`${family}-`))).toBe(true); + } + }); + + it('should not include colors when disabled', async () => { + const themeJson = await runBuild({ disableTailwindColors: true }); + expect(themeJson.settings.color.palette).toBeUndefined(); + }); + }); + + describe('fonts', () => { + it('should extract font families', async () => { + const themeJson = await runBuild(); + const fonts = themeJson.settings.typography.fontFamilies; + + expect(fonts).toContainEqual( + expect.objectContaining({ slug: 'sans' }) + ); + expect(fonts).toContainEqual( + expect.objectContaining({ slug: 'mono' }) + ); + }); + }); + + describe('font sizes', () => { + it('should extract all font sizes', async () => { + const themeJson = await runBuild(); + const sizes = themeJson.settings.typography.fontSizes; + + const expectedSizes = ['xs', 'sm', 'base', 'lg', 'xl', '2xl', '3xl', '4xl', '5xl', '6xl', '7xl', '8xl', '9xl']; + for (const size of expectedSizes) { + expect(sizes).toContainEqual( + expect.objectContaining({ slug: size }) + ); + } + }); + }); + + describe('border radius', () => { + it('should extract border radius sizes', async () => { + const themeJson = await runBuild({ disableTailwindBorderRadius: false }); + const radiusSizes = themeJson.settings.border?.radiusSizes; + + expect(radiusSizes).toBeDefined(); + expect(radiusSizes!.length).toBeGreaterThan(0); + }); + }); +}); diff --git a/tests/fixture/app.css b/tests/fixture/app.css new file mode 100644 index 0000000..d9182b9 --- /dev/null +++ b/tests/fixture/app.css @@ -0,0 +1 @@ +@import "tailwindcss" theme(static); diff --git a/tests/fixture/theme.json b/tests/fixture/theme.json new file mode 100644 index 0000000..ba768d9 --- /dev/null +++ b/tests/fixture/theme.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://schemas.wp.org/trunk/theme.json", + "version": 3, + "settings": { + "color": { + "custom": false, + "defaultPalette": false + }, + "typography": { + "defaultFontSizes": false, + "customFontSize": false + } + } +} From 6c2bfff0641731f3bfb5cd45f97d7c712e1393f6 Mon Sep 17 00:00:00 2001 From: Ben Word Date: Wed, 11 Mar 2026 14:23:29 -0500 Subject: [PATCH 2/5] =?UTF-8?q?=F0=9F=90=9B=20Fix=20eslint=20errors=20in?= =?UTF-8?q?=20build=20integration=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 --- tests/build.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/build.test.ts b/tests/build.test.ts index bfcc461..90ceb9c 100644 --- a/tests/build.test.ts +++ b/tests/build.test.ts @@ -1,4 +1,5 @@ -import { describe, expect, it, beforeAll, afterAll } from 'vitest'; +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { describe, expect, it, afterAll } from 'vitest'; import { build } from 'vite'; import tailwindcss from '@tailwindcss/vite'; import { wordpressThemeJson } from '../src/index.js'; From d9927912ec2ca0d979734b8e3be1887b9c0775b5 Mon Sep 17 00:00:00 2001 From: Ben Word Date: Wed, 11 Mar 2026 14:25:28 -0500 Subject: [PATCH 3/5] Add @tailwindcss/vite as devDependency for build integration tests Co-Authored-By: Claude Opus 4.6 --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index ce1c83f..87f89b3 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "esbuild": "0.25.6", "eslint": "^9.0.0", "globals": "^16.3.0", + "@tailwindcss/vite": "^4.1.11", "tailwindcss": "^4.1.11", "typescript": "^5.0.0", "vite": "^7.0.0", From 1fe229a29ea836a22a4f4494c759fc97f5de42c6 Mon Sep 17 00:00:00 2001 From: Ben Word Date: Wed, 11 Mar 2026 14:29:03 -0500 Subject: [PATCH 4/5] Rebuild tailwindcss native bindings in CI Works around npm optional dependency resolution bug (npm/cli#4828) that causes @tailwindcss/oxide platform binaries to be missing when installing without a lock file. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 702dfd2..d8b4788 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -27,7 +27,7 @@ jobs: node-version: ${{ matrix.node-version }} - name: Install dependencies - run: npm install + run: npm install && npm rebuild tailwindcss - name: ESLint run: npm run lint From 959369f3974c1c8ded59c8077aa560dbfaa7ed85 Mon Sep 17 00:00:00 2001 From: Ben Word Date: Wed, 11 Mar 2026 14:32:05 -0500 Subject: [PATCH 5/5] Fix CI: drop Node 18, add @tailwindcss/oxide devDependency Node 18 is unsupported by Tailwind v4. Adding @tailwindcss/oxide as an explicit devDependency ensures npm installs the platform-specific native binaries (works around npm/cli#4828). Co-Authored-By: Claude Opus 4.6 --- .github/workflows/tests.yml | 4 ++-- package.json | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d8b4788..6c39689 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,7 +15,7 @@ jobs: strategy: matrix: - node-version: [18.x, 20.x, 22.x, latest] + node-version: [20.x, 22.x, latest] steps: - name: Checkout code @@ -27,7 +27,7 @@ jobs: node-version: ${{ matrix.node-version }} - name: Install dependencies - run: npm install && npm rebuild tailwindcss + run: npm install - name: ESLint run: npm run lint diff --git a/package.json b/package.json index 87f89b3..078a2ca 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "esbuild": "0.25.6", "eslint": "^9.0.0", "globals": "^16.3.0", + "@tailwindcss/oxide": "^4.1.11", "@tailwindcss/vite": "^4.1.11", "tailwindcss": "^4.1.11", "typescript": "^5.0.0",