Skip to content

Commit 9959c79

Browse files
committed
feat(commonjs): add named exports option
1 parent 70ae949 commit 9959c79

File tree

6 files changed

+123
-6
lines changed

6 files changed

+123
-6
lines changed

packages/vite-plugin-commonjs/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ export default {
3030
- `include: string[]`
3131
Dependencies that only need to be transform.
3232

33+
- `addNamedExports: boolean`
34+
add named exports.
35+
3336
### CommonJS module in node_modules
3437
```js
3538
import { esbuildCommonjs } from '@originjs/vite-plugin-commonjs'
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`transform named exports 1`] = `
4+
"var __getOwnPropNames = Object.getOwnPropertyNames;
5+
var __commonJS = (cb, mod) => function __require() {
6+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
7+
};
8+
var require_stdin = __commonJS({
9+
\\"<stdin>\\"(exports, module) {
10+
const a = \\"a\\";
11+
const obj = {
12+
o1: 1,
13+
o2: 2
14+
};
15+
module.exports = {
16+
a,
17+
b: \\"b\\",
18+
...obj
19+
};
20+
exports.c = \\"c\\";
21+
}
22+
});
23+
24+
var __named_exports_for_vite = require_stdin();
25+
26+
var __named_exports_for_vite__a = __named_exports_for_vite['a'];
27+
var __named_exports_for_vite__b = __named_exports_for_vite['b'];
28+
var __named_exports_for_vite__o1 = __named_exports_for_vite['o1'];
29+
var __named_exports_for_vite__o2 = __named_exports_for_vite['o2'];
30+
var __named_exports_for_vite__c = __named_exports_for_vite['c'];
31+
32+
export {__named_exports_for_vite__a as a,__named_exports_for_vite__b as b,__named_exports_for_vite__o1 as o1,__named_exports_for_vite__o2 as o2,__named_exports_for_vite__c as c};
33+
34+
export default __named_exports_for_vite;
35+
36+
"
37+
`;

packages/vite-plugin-commonjs/__tests__/transform.spec.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { transformRequire, isCommonJS } from "../src/lib";
1+
import { transformRequire, isCommonJS, transformCommonJS } from "../src/lib";
22

33
test('transform require', () => {
44
//general require
@@ -67,6 +67,30 @@ test('isCommonJS', () => {
6767
expect(isCommonJS(`exports['some key'] = 1`)).toBeTruthy();
6868
});
6969

70+
test('transform named exports', () => {
71+
72+
let code = `
73+
const a = 'a';
74+
75+
const obj = {
76+
o1: 1,
77+
o2: 2
78+
};
79+
80+
module.exports = {
81+
a,
82+
b: 'b',
83+
...obj
84+
};
85+
86+
exports.c = 'c';
87+
`;
88+
89+
const transformResult = transformCommonJS(code, true).code;
90+
expect(transformResult).toMatch(/export {.*__named_exports_for_vite__a as a,.*}/);
91+
expect(transformResult).toMatchSnapshot();
92+
});
93+
7094
test('Both url and comments are present', () => {
7195
//singleline comments
7296
let code = `dosomething () {

packages/vite-plugin-commonjs/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
},
2525
"homepage": "https://github.com/originjs/vite-plugins/tree/main/packages/vite-plugin-commonjs",
2626
"dependencies": {
27+
"cjs-esm-exports": "^0.6.1",
2728
"esbuild": "^0.14.14"
2829
},
2930
"devDependencies": {

packages/vite-plugin-commonjs/src/index.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { transformSync, TransformResult } from "esbuild";
2-
import { transformRequire, isCommonJS } from "./lib";
1+
import { TransformResult } from "esbuild";
2+
import { transformRequire, transformCommonJS, isCommonJS } from "./lib";
33
import * as fs from "fs";
44
import { Plugin } from "vite";
55
import createFilter from "./filter";
@@ -8,10 +8,11 @@ export type Options = {
88
include?: string | string[] | undefined;
99
exclude?: string | string[] | undefined;
1010
skipPreBuild?: boolean;
11+
addNamedExports?: boolean;
1112
};
1213

1314
export function viteCommonjs(
14-
options: Options = { skipPreBuild: false }
15+
options: Options = { skipPreBuild: false, addNamedExports: false }
1516
): Plugin {
1617
const filter = createFilter(options.include, options.exclude);
1718
return {
@@ -28,7 +29,7 @@ export function viteCommonjs(
2829
let result = transformRequire(code, id);
2930

3031
if (id.indexOf("/node_modules/.vite/") == -1 && isCommonJS(code)) {
31-
return transformSync(result.code, { format: "esm" });
32+
return transformCommonJS(result.code, options.addNamedExports);
3233
}
3334

3435
if (result.replaced) {

packages/vite-plugin-commonjs/src/lib.ts

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import { parse as parseCjs } from 'cjs-esm-exports';
2+
import { transformSync } from 'esbuild';
3+
14
const commonJSRegex: RegExp = /\b(module\.exports|exports\.\w+|exports\s*=\s*|exports\s*\[.*\]\s*=\s*)/;
25
const requireRegex: RegExp = /(?<!\.)\b_{0,2}require\s*\(\s*(["'].*?["'])\s*\)/g;
36
const IMPORT_STRING_PREFIX: String = "__require_for_vite";
@@ -50,6 +53,54 @@ export function isCommonJS(code: string): boolean {
5053
return commonJSRegex.test(code);
5154
}
5255

56+
export function transformCommonJS(code: string, addNamedExports = false) {
57+
const transformResult = transformSync(code, { format: "esm" });
58+
59+
if (!addNamedExports) {
60+
return transformResult;
61+
}
62+
63+
const esmCode = transformResult.code;
64+
const defaultExportRegex = /export default (.*?);/;
65+
const defaultExport = esmCode.match(defaultExportRegex)?.[1];
66+
67+
if (!defaultExport) {
68+
return transformResult;
69+
}
70+
71+
const { exports } = parseCjs('', code);
72+
if (!exports || !exports.length) {
73+
return transformResult;
74+
}
75+
76+
const namedKey = '__named_exports_for_vite';
77+
const namedExportsMap: Record<string, string> = exports.reduce(
78+
(map: Record<string, string>, key: string) => {
79+
map[key] = `${namedKey}['${key}']`;
80+
return map;
81+
},
82+
{},
83+
);
84+
const namedKeys = Object.keys(namedExportsMap);
85+
86+
const namedExportsCode = `
87+
var ${namedKey} = ${defaultExport};
88+
89+
${namedKeys.map(key => {
90+
return `var ${namedKey}__${key} = ${namedExportsMap[key]};`
91+
}).join('\n')}
92+
93+
export {${namedKeys.map(key => `${namedKey}__${key} as ${key}`).join(',')}};
94+
95+
export default ${namedKey};
96+
`;
97+
98+
return {
99+
...transformResult,
100+
code: esmCode.replace(defaultExportRegex, namedExportsCode),
101+
};
102+
}
103+
53104
function removeComments(
54105
code: string,
55106
exp: RegExp,
@@ -86,4 +137,4 @@ function isString(text: string) {
86137
} catch (err) {
87138
return false;
88139
}
89-
}
140+
}

0 commit comments

Comments
 (0)