Skip to content

Commit 4bacd90

Browse files
authored
fix(cjs): wrong cjs default exports (#144)
* fix(cjs): wrong cjs default exports * chore: cleanup
1 parent ebb6696 commit 4bacd90

File tree

2 files changed

+108
-15
lines changed

2 files changed

+108
-15
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "vite-plugin-inspect",
33
"type": "module",
44
"version": "10.2.1",
5-
"packageManager": "pnpm@10.3.0",
5+
"packageManager": "pnpm@10.4.0",
66
"description": "Inspect the intermediate state of Vite plugins",
77
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
88
"license": "MIT",

scripts/postbuild.ts

+107-14
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,111 @@
1-
import { readFileSync, writeFileSync } from 'node:fs'
2-
import fsp from 'node:fs/promises'
3-
import { resolve } from 'node:path'
4-
5-
function patchCjs(cjsModulePath: string, name: string) {
6-
const cjsModule = readFileSync(cjsModulePath, 'utf-8')
7-
writeFileSync(
8-
cjsModulePath,
9-
cjsModule
10-
.replace(`module.exports = ${name};`, `exports = ${name};`),
11-
{ encoding: 'utf-8' },
1+
import { readFile, writeFile } from 'node:fs/promises'
2+
// import { readFileSync, writeFileSync } from 'node:fs'
3+
import { findExports, findStaticImports, parseStaticImport } from 'mlly'
4+
5+
const regexp = /export\s*\{([^}]*)\}/
6+
const defaultExportRegexp = /\s*as\s+default\s*/
7+
const typeExportRegexp = /\s*type\s+/
8+
9+
// Temporal fix for unbuild cjs plugin: will work only here.
10+
// The current unbuild cjs plugin fixing only some default exports in d.cts files.
11+
// This script will not fix export { default } from '<some-specifier>'.
12+
async function fixDefaultCJSExports(path: string) {
13+
const code = await readFile(path, 'utf-8')
14+
15+
const defaultExport = findExports(code).find(e =>
16+
e.names.includes('default'),
17+
)
18+
19+
if (!defaultExport) {
20+
return
21+
}
22+
23+
const match = defaultExport.code.match(regexp)
24+
if (!match?.length) {
25+
return
26+
}
27+
28+
let defaultAlias: string | undefined
29+
const exportsEntries: string[] = []
30+
for (const exp of match[1].split(',').map(e => e.trim())) {
31+
const m = exp.match(defaultExportRegexp)
32+
if (m) {
33+
defaultAlias = exp.replace(m[0], '')
34+
}
35+
else {
36+
exportsEntries.push(exp)
37+
}
38+
}
39+
40+
if (!defaultAlias) {
41+
// handle default export like:
42+
// import defaultExport from '<some-identifier>'
43+
// export default defaultExport
44+
// dts plugin will generate code like:
45+
// import defaultExport from '<some-identifier>';
46+
// export { default } from '<some-identifier>';
47+
const defaultStaticImport = findStaticImports(code).find(i => i.specifier === defaultExport.specifier)
48+
const defaultImport = defaultStaticImport ? parseStaticImport(defaultStaticImport).defaultImport : undefined
49+
if (!defaultExport) {
50+
return
51+
}
52+
// this will generate the following code:
53+
// import defaultExport from '<some-identifier>';
54+
// export = defaultExport;
55+
await writeFile(
56+
path,
57+
code.replace(
58+
defaultExport.code,
59+
`export = ${defaultImport}`,
60+
),
61+
'utf-8',
62+
)
63+
return
64+
}
65+
66+
let exportStatement = exportsEntries.length > 0 ? undefined : ''
67+
68+
// replace export { type A, type B, type ... } with export type { A, B, ... }
69+
// that's, if all remaining exports are type exports, replace export {} with export type {}
70+
if (exportStatement === undefined) {
71+
const imports = findStaticImports(code).map(i => i.imports)
72+
let someExternalExport = false
73+
const allRemainingExports = exportsEntries.map((exp) => {
74+
if (someExternalExport) {
75+
return [exp, ''] as const
76+
}
77+
if (!imports.includes(exp)) {
78+
const m = exp.match(typeExportRegexp)
79+
if (m) {
80+
const name = exp.replace(m[0], '').trim()
81+
if (!imports.includes(name)) {
82+
return [exp, name] as const
83+
}
84+
}
85+
}
86+
someExternalExport = true
87+
return [exp, ''] as const
88+
})
89+
exportStatement = someExternalExport
90+
? `;\nexport { ${allRemainingExports.map(([e, _]) => e).join(', ')} }`
91+
: `;\nexport type { ${allRemainingExports.map(([_, t]) => t).join(', ')} }`
92+
}
93+
94+
await writeFile(
95+
path,
96+
code.replace(
97+
defaultExport.code,
98+
`export = ${defaultAlias}${exportStatement}`,
99+
),
100+
'utf-8',
12101
)
13102
}
14103

15-
patchCjs(resolve('./dist/nuxt.cjs'), 'nuxt')
16-
patchCjs(resolve('./dist/index.cjs'), 'index.PluginInspect')
104+
function mapDualPaths(files: string[]) {
105+
return files.map(name => [
106+
fixDefaultCJSExports(`dist/${name}.d.ts`),
107+
fixDefaultCJSExports(`dist/${name}.d.cts`),
108+
])
109+
}
17110

18-
await fsp.cp('src/client/public/assets/fonts', 'dist/client/assets/fonts', { recursive: true })
111+
await Promise.all(mapDualPaths(['nuxt', 'index']))

0 commit comments

Comments
 (0)