Skip to content

Commit 632f215

Browse files
Brooooooklynclaude
andcommitted
fix(cli): externalize postcss/lightningcss from the dts bundle
`vp pack --dts` inlined postcss's `.d.ts` into the declaration bundle and failed with 61 MISSING_EXPORT errors for postcss type names. postcss exposes its public types via `declare namespace postcss { export {...} }` + `export = postcss` — a CJS namespace shape the dts bundler cannot map named imports onto. `externalDtsTypeOnlyPlugin` previously rewrote postcss/lightningcss imports to type-only via regex, which neither parsed reliably for default+named imports nor addressed the `export =` mismatch. Replace it with a `resolveId` hook that marks postcss/lightningcss external whenever imported from a `.d.ts` file, so emitted declarations keep `import type { X } from 'postcss'` instead of inlining postcss's `.d.ts` — standard dts-bundler treatment of third-party packages. Runtime bundling is unaffected (the hook only fires for declaration-file importers). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 9b451f4 commit 632f215

1 file changed

Lines changed: 29 additions & 41 deletions

File tree

packages/cli/src/pack-bin.ts

Lines changed: 29 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -13,53 +13,42 @@ import { cac } from 'cac';
1313

1414
import { resolveViteConfig } from './resolve-vite-config.ts';
1515

16+
// Matches a `.d.ts` / `.d.mts` / `.d.cts` importer.
17+
const RE_DTS = /\.d\.[cm]?ts$/;
18+
// Bare specifier for postcss / lightningcss (root or any subpath).
19+
const EXTERNAL_DTS_PKG_RE = /^(?:postcss|lightningcss)(?:\/|$)/;
20+
1621
/**
17-
* Rolldown plugin that transforms value imports/exports to type-only in external
18-
* packages' .d.ts files. Some packages (e.g. postcss, lightningcss) use
19-
* `import { X }` and `export { X } from` instead of their type-only equivalents,
20-
* which causes MISSING_EXPORT warnings from the DTS bundler.
22+
* Rolldown plugin that keeps `postcss` / `lightningcss` external to the DTS
23+
* bundle.
24+
*
25+
* The DTS bundler resolves these packages and tries to inline their `.d.ts`
26+
* files. postcss ships its public types as a CJS `export = postcss` over a
27+
* `declare namespace postcss { export { AtRule, ... } }` (see
28+
* postcss/lib/postcss.d.ts), and its ESM types entry (postcss.d.mts) re-exports
29+
* those names with `export { AtRule, ... } from './postcss.js'`. The bundler
30+
* cannot map named imports onto an `export =`'d namespace's members, so every
31+
* consumer `import type { AtRule } from 'postcss'` becomes a MISSING_EXPORT
32+
* error.
2133
*
22-
* Since .d.ts files contain only type information, all imports/exports are
23-
* inherently type-only, so this transformation is always safe.
34+
* Marking these packages external for `.d.ts` importers leaves the import
35+
* untouched in the emitted declarations (`import type { AtRule } from 'postcss'`),
36+
* which is how third-party packages should be treated in a DTS bundle anyway.
37+
* They are only externalized when imported *from a declaration file*, so runtime
38+
* bundling is unaffected.
2439
*/
25-
const EXTERNAL_DTS_INTERNAL_RE = /node_modules\/(postcss|lightningcss)\/.*\.d\.(ts|mts|cts)$/;
26-
// Match consumer .d.ts files that import from postcss/lightningcss.
27-
// In CI (installed from tgz): node_modules/vite-plus-core/dist/...
28-
// In local development (symlinked workspace): packages/core/dist/...
29-
const EXTERNAL_DTS_CONSUMER_RE =
30-
/(?:vite-plus-core|packages\/core)\/.*lightningcssOptions\.d\.ts$|(?:vite-plus-core|packages\/core)\/dist\/.*\.d\.ts$/;
31-
const EXTERNAL_DTS_FIX_RE = new RegExp(
32-
`${EXTERNAL_DTS_INTERNAL_RE.source}|${EXTERNAL_DTS_CONSUMER_RE.source}`,
33-
);
34-
3540
function externalDtsTypeOnlyPlugin() {
3641
return {
3742
name: 'vite-plus:external-dts-type-only',
38-
transform: {
39-
filter: { id: { include: [EXTERNAL_DTS_FIX_RE] } },
40-
handler(code: string, rawId: string) {
43+
resolveId: {
44+
order: 'pre' as const,
45+
handler(id: string, importer: string | undefined) {
4146
// Normalize Windows backslash paths to forward slashes for regex matching
42-
const id = rawId.replaceAll('\\', '/');
43-
if (EXTERNAL_DTS_INTERNAL_RE.test(id)) {
44-
// postcss/lightningcss internal files: transform imports only
45-
// (exports may include value re-exports like `export const Features`).
46-
// A type-only import statement cannot mix a default binding with named
47-
// bindings, so fold `import D, { N } from 'x'` into a single named
48-
// clause (`{ default as D, N }`) before the generic prepend runs.
49-
return code
50-
.replace(/^import\s+(\w+)\s*,\s*\{/gm, 'import type { default as $1,')
51-
.replace(/^(import\s+)(?!type\s)/gm, 'import type ');
47+
const normalizedImporter = importer?.replaceAll('\\', '/');
48+
if (normalizedImporter && RE_DTS.test(normalizedImporter) && EXTERNAL_DTS_PKG_RE.test(id)) {
49+
return { id, external: true };
5250
}
53-
// Consumer files: only transform imports from postcss/lightningcss
54-
return code
55-
.replace(
56-
/^import\s+(\w+)\s*,\s*\{([^}]*)\}\s*from\s+(['"](?:postcss|lightningcss)['"])/gm,
57-
'import type { default as $1,$2} from $3',
58-
)
59-
.replace(
60-
/^(import\s+)(?!type\s)(.+from\s+['"](?:postcss|lightningcss)['"])/gm,
61-
'import type $2',
62-
);
51+
return undefined;
6352
},
6453
},
6554
};
@@ -153,8 +142,7 @@ cli
153142
: [viteConfig.pack ?? {}];
154143
for (const packConfig of packConfigs) {
155144
const merged = { ...packConfig, ...flags };
156-
// Inject plugin to fix MISSING_EXPORT warnings from external .d.ts files
157-
// (postcss, lightningcss use `import`/`export` instead of `import type`/`export type`)
145+
// Keep postcss/lightningcss external to the dts bundle (see plugin doc)
158146
if (merged.dts) {
159147
const existingPlugins = Array.isArray(merged.plugins) ? merged.plugins : [];
160148
merged.plugins = [...existingPlugins, externalDtsTypeOnlyPlugin()];

0 commit comments

Comments
 (0)