Skip to content

Commit ddd18ef

Browse files
committed
fix: fail build when rolldown silently externalizes workspace packages
Rolldown silently externalizes imports it can't resolve, producing output with bare specifiers that crash at runtime. Add post-bundle validation to detect unbundled @voidzero-dev/* imports in dist/global/.
1 parent 16f71ad commit ddd18ef

3 files changed

Lines changed: 37 additions & 4 deletions

File tree

.github/actions/build-upstream/action.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,8 @@ runs:
4545
pnpm --filter @rolldown/pluginutils build
4646
pnpm --filter rolldown build-node
4747
pnpm --filter vite build-types
48-
pnpm --filter=@voidzero-dev/vite-plus-core build
49-
pnpm --filter=@voidzero-dev/vite-plus-test build
50-
pnpm --filter=vite-plus build-ts
48+
pnpm --filter "@voidzero-dev/*" build
49+
pnpm --filter vite-plus build-ts
5150
5251
# NAPI builds - only run on cache miss (slow, especially on Windows)
5352
# Must run before vite-plus TypeScript builds which depend on the bindings

packages/cli/build.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
*/
1818

1919
import { execSync } from 'node:child_process';
20-
import { existsSync, globSync, readdirSync, statSync } from 'node:fs';
20+
import { existsSync, globSync, readFileSync, readdirSync, statSync } from 'node:fs';
2121
import { copyFile, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
2222
import { dirname, join } from 'node:path';
2323
import { fileURLToPath } from 'node:url';
@@ -184,6 +184,39 @@ function buildGlobalModules() {
184184
cwd: projectDir,
185185
stdio: 'inherit',
186186
});
187+
validateGlobalBundleExternals();
188+
}
189+
190+
/**
191+
* Scan rolldown output for unbundled workspace package imports.
192+
*
193+
* Rolldown silently externalizes imports it can't resolve (no error, no warning).
194+
* If a workspace package's dist doesn't exist at bundle time (build order race,
195+
* clean checkout, etc.), the bare specifier stays in the output. Since these
196+
* packages are devDependencies — not installed in the global CLI's node_modules —
197+
* this causes a runtime ERR_MODULE_NOT_FOUND crash.
198+
*
199+
* Fail the build loudly instead of producing a broken install.
200+
*/
201+
function validateGlobalBundleExternals() {
202+
const globalDir = join(projectDir, 'dist/global');
203+
const files = globSync('*.js', { cwd: globalDir });
204+
const errors: string[] = [];
205+
206+
for (const file of files) {
207+
const content = readFileSync(join(globalDir, file), 'utf8');
208+
const matches = content.matchAll(/\bimport\s.*?from\s+["'](@voidzero-dev\/[^"']+)["']/g);
209+
for (const match of matches) {
210+
errors.push(` ${file}: unbundled import of "${match[1]}"`);
211+
}
212+
}
213+
214+
if (errors.length > 0) {
215+
throw new Error(
216+
`Rolldown failed to bundle workspace packages in dist/global/:\n${errors.join('\n')}\n` +
217+
`Ensure these packages are built before running the CLI build.`,
218+
);
219+
}
187220
}
188221

189222
/**

packages/cli/rolldown.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,6 @@ export default defineConfig({
3737
output: {
3838
format: 'esm',
3939
dir: './dist/global',
40+
cleanDir: true,
4041
},
4142
});

0 commit comments

Comments
 (0)