From 0cea295f541ba336ff4b8ae98d5ce3f6042806b4 Mon Sep 17 00:00:00 2001 From: Khalid Abdi Date: Tue, 13 Oct 2026 15:50:40 +0300 Subject: [PATCH] fix(docs): let the Vercel build install what the web components import The site is built on Vercel from apps/docs, and an install run from there brings only the docs app's own dependencies. Its pages import the web components' source by path, and that source imports tailwind-variants, which the docs app had never declared. CI installs every workspace from the root, so it was there in CI and missing on Vercel: the first deploy with the web collection failed at "Module not found: tailwind-variants" with every check green. The docs app now depends on panelui-web by name, which brings that package's dependencies with it. Checked by cloning the repository fresh, installing from apps/docs and building, which is what Vercel does. A contract test keeps the dependency there, and checks that the web package declares everything its source imports. CLAUDE.md says how the two installs differ and how to reproduce Vercel's. The lockfile gains one line, the new edge. No version moved. Co-Authored-By: Claude Opus 5.5 --- CLAUDE.md | 9 +++++++ apps/docs/package.json | 1 + apps/docs/test/web-registry.test.mjs | 36 ++++++++++++++++++++++++++++ package-lock.json | 1 + 4 files changed, 47 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index a9474cdb..8fd73643 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -670,6 +670,15 @@ Two pieces make the current arrangement, and they have to stay in step: `main`. Deploy hooks are unaffected by `deploymentEnabled: false` — that setting gates the Git integration, not the hook. +**Vercel installs from `apps/docs`, and CI does not.** An `npm install` run inside a workspace +brings only what that workspace depends on, directly or through another workspace it names. CI +runs `npm ci` at the root, which brings everything, so a package the docs build needs and +`apps/docs` never declared is present in CI and missing on Vercel. The first deploy of the web +collection failed this way, on `tailwind-variants`, with every check green. The docs app +therefore depends on `panelui-web` by name, because its pages import that source by path, and a +contract test holds it there. To see what Vercel will see, clone the repository fresh, then +`cd apps/docs && npm install && npm run build`. + **A documentation change that is not part of a release does not reach panelui.dev on its own.** New previews, a corrected props table and a typo fix all sit on `main` until the next release. To publish them now, run the **Deploy docs** workflow by hand from the Actions tab — that is what the diff --git a/apps/docs/package.json b/apps/docs/package.json index df91899a..2854557d 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -34,6 +34,7 @@ "lucide-react": "^1.25.0", "next": "^16.3.0", "next-themes": "^0.4.6", + "panelui-web": "*", "react": "^19.2.3", "react-day-picker": "^10.0.1", "react-dom": "^19.2.3", diff --git a/apps/docs/test/web-registry.test.mjs b/apps/docs/test/web-registry.test.mjs index 5d22ed6b..357073b5 100644 --- a/apps/docs/test/web-registry.test.mjs +++ b/apps/docs/test/web-registry.test.mjs @@ -114,3 +114,39 @@ test('every component has a thumb for the index, and the index shows the gallery } assert.match(read('content/docs/web/components/index.mdx'), //); }); + +/* + * The site is built on Vercel from `apps/docs`, and an install run from there + * brings only what the docs app depends on, directly or through a workspace + * it names. The pages import the web components' source by path, so that + * source's own imports have to arrive the same way. CI installs every + * workspace from the root and cannot see one that is missing: the first + * deploy of the web collection failed on exactly this, with every check green. + */ +test('the docs app can install what the web components import', () => { + const web = path.resolve(docs, '../../packages/web'); + const webPackage = JSON.parse(fs.readFileSync(path.join(web, 'package.json'), 'utf8')); + const docsPackage = JSON.parse(read('package.json')); + + assert.ok( + docsPackage.dependencies?.['panelui-web'], + 'apps/docs must depend on panelui-web: its pages import that source, and Vercel installs from apps/docs' + ); + + // And the package says everything its source reaches for, so naming the package is enough. + const declared = new Set([ + ...Object.keys(webPackage.dependencies ?? {}), + ...Object.keys(webPackage.peerDependencies ?? {}), + ]); + const sources = fs + .readdirSync(path.join(web, 'src'), { recursive: true }) + .filter((file) => /\.tsx?$/.test(String(file))); + for (const file of sources) { + const source = fs.readFileSync(path.join(web, 'src', String(file)), 'utf8'); + for (const [, specifier] of source.matchAll(/from\s+['"]([^'".][^'"]*)['"]/g)) { + // `@scope/name/sub/path` and `name/sub/path` both belong to the package before the sub path. + const name = specifier.startsWith('@') ? specifier.split('/').slice(0, 2).join('/') : specifier.split('/')[0]; + assert.ok(declared.has(name), `${file} imports ${specifier}, which packages/web/package.json does not declare`); + } + } +}); diff --git a/package-lock.json b/package-lock.json index 09001f56..b75e7c9c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,7 @@ "lucide-react": "^1.25.0", "next": "^16.3.0", "next-themes": "^0.4.6", + "panelui-web": "*", "react": "^19.2.3", "react-day-picker": "^10.0.1", "react-dom": "^19.2.3",