From 1c1533096cab4486b42fd980683ea764f0b8c775 Mon Sep 17 00:00:00 2001 From: "Charles Graham, SWT" Date: Tue, 18 Aug 2026 01:03:26 -0500 Subject: [PATCH] Fix Vite 8 library React imports --- .github/workflows/react-compatibility.yml | 3 ++ package-lock.json | 51 +++++++++++++++++++++++ package.json | 2 + scripts/verify-library-bundle.js | 33 +++++++++++++++ vite.config.js | 13 ++++-- 5 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 scripts/verify-library-bundle.js diff --git a/.github/workflows/react-compatibility.yml b/.github/workflows/react-compatibility.yml index 9c72362..7394249 100644 --- a/.github/workflows/react-compatibility.yml +++ b/.github/workflows/react-compatibility.yml @@ -35,5 +35,8 @@ jobs: - name: Build library run: npm run build-lib + - name: Verify library bundle + run: npm run verify-lib + - name: Build docs run: npm run build-docs diff --git a/package-lock.json b/package-lock.json index b3e1249..a467fa3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "redux-bundler-react": "^1.2.0" }, "devDependencies": { + "@babel/parser": "^7.29.8", "@lhci/cli": "^0.15.1", "@types/react": "^18.3.0", "@types/react-dom": "^18.3.0", @@ -53,6 +54,56 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@emnapi/core": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", diff --git a/package.json b/package.json index dccf85e..c00f386 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "dev": "vite", "build": "npm run build-lib && npm run build-docs", "build-lib": "vite build -m lib", + "verify-lib": "node scripts/verify-library-bundle.js", "build-docs": "vite build", "lhci:prep": "node scripts/extract-routes.js", "lhci:run": "rmdir /s /q .lighthouseci 2>nul & npx @lhci/cli autorun --config=.lighthouserc.gen.json --verbose & node scripts/lhci-index.js", @@ -67,6 +68,7 @@ "react-dom": "^18.2.0 || ^19.0.0" }, "devDependencies": { + "@babel/parser": "^7.29.8", "@lhci/cli": "^0.15.1", "@types/react": "^18.3.0", "@types/react-dom": "^18.3.0", diff --git a/scripts/verify-library-bundle.js b/scripts/verify-library-bundle.js new file mode 100644 index 0000000..bcb56a6 --- /dev/null +++ b/scripts/verify-library-bundle.js @@ -0,0 +1,33 @@ +import fs from "node:fs"; + +const bundlePath = new URL("../dist/groundwork.es.js", import.meta.url); + +if (!fs.existsSync(bundlePath)) { + throw new Error("Library bundle not found. Run `npm run build-lib` first."); +} + +const bundle = fs.readFileSync(bundlePath, "utf8"); +const forbiddenPatterns = [ + { + description: "a browser-side CommonJS require fallback", + pattern: "Calling `require` for", + }, + { + description: "a CommonJS React require call", + pattern: /\brequire\((['"`])react(?:\/jsx-runtime)?\1\)/, + }, +]; + +for (const { description, pattern } of forbiddenPatterns) { + if ( + typeof pattern === "string" + ? bundle.includes(pattern) + : pattern.test(bundle) + ) { + throw new Error(`ES module bundle contains ${description}.`); + } +} + +console.log( + "Verified the ES module bundle uses browser-compatible React imports.", +); diff --git a/vite.config.js b/vite.config.js index 14a275d..def1544 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,4 +1,4 @@ -import { defineConfig } from "vite"; +import { defineConfig, esmExternalRequirePlugin } from "vite"; import react from "@vitejs/plugin-react"; import tailwindcss from "tailwindcss"; import pkg from "./package.json"; @@ -36,7 +36,13 @@ export default defineConfig(({ mode }) => { if (mode === "lib") { console.log("Building library"); return { - plugins: [react(), tailwindcss()], + plugins: [ + react(), + tailwindcss(), + esmExternalRequirePlugin({ + external: ["react", "react-dom", "react/jsx-runtime"], + }), + ], publicDir: false, build: { lib: { @@ -44,8 +50,7 @@ export default defineConfig(({ mode }) => { fileName: (format) => `groundwork.${format}.js`, entry: "lib/index.jsx", }, - rollupOptions: { - external: ["react", "react-dom", "react/jsx-runtime"], + rolldownOptions: { output: { assetFileNames: (assetInfo) => { if (assetInfo.name?.endsWith(".css")) {