Skip to content

Commit 336886a

Browse files
committed
feat: add require exports & esm -> cjs conversion
1 parent f6c62c3 commit 336886a

File tree

4 files changed

+192
-5
lines changed

4 files changed

+192
-5
lines changed

deno.lock

Lines changed: 77 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,28 @@
1212
"exports": {
1313
"./access": {
1414
"types": "./access.d.ts",
15-
"default": "./access.mjs"
15+
"import": "./access.mjs",
16+
"require": "./access.js"
1617
},
1718
"./find": {
1819
"types": "./find.d.ts",
19-
"default": "./find.mjs"
20+
"import": "./find.mjs",
21+
"require": "./find.js"
2022
},
2123
"./package": {
2224
"types": "./package.d.ts",
23-
"default": "./package.mjs"
25+
"import": "./package.mjs",
26+
"require": "./package.js"
2427
},
2528
"./resolve": {
2629
"types": "./resolve.d.ts",
27-
"default": "./resolve.mjs"
30+
"import": "./resolve.mjs",
31+
"require": "./resolve.js"
2832
},
2933
"./walk": {
3034
"types": "./walk.d.ts",
31-
"default": "./walk.mjs"
35+
"import": "./walk.mjs",
36+
"require": "./walk.js"
3237
},
3338
"./package.json": "./package.json"
3439
},

scripts/build.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { join, resolve } from 'node:path';
88

99
import oxc from 'npm:[email protected]';
1010
import { minify } from 'npm:[email protected]';
11+
import * as CommonJS from './commonjs.ts';
1112

1213
const Quiet = Deno.args.includes('--quiet');
1314

@@ -59,6 +60,7 @@ async function transform(filename: string) {
5960
}
6061

6162
let rgx = /\.tsx?$/;
63+
let cjs = filename.replace(rgx, '.js');
6264
let esm = filename.replace(rgx, '.mjs');
6365
let dts = filename.replace(rgx, '.d.ts');
6466

@@ -70,6 +72,12 @@ async function transform(filename: string) {
7072
log('> writing "%s" file', esm);
7173
await Deno.writeTextFile(outfile, xform.code);
7274

75+
// esm -> cjs
76+
outfile = join(outdir, cjs);
77+
log('> writing "%s" file', cjs);
78+
let tmp = CommonJS.transform(xform.code);
79+
await Deno.writeTextFile(outfile, tmp);
80+
7381
try {
7482
let min = minify(esm, xform.code, {
7583
mangle: { toplevel: true },

scripts/commonjs.ts

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import MagicString from 'npm:[email protected]';
2+
import { type Function, parseSync } from 'npm:[email protected]';
3+
import { walk } from 'npm:[email protected]';
4+
5+
type Location = {
6+
start: number;
7+
end: number;
8+
};
9+
10+
export function transform(esm: string) {
11+
let s = new MagicString(esm);
12+
let ast = parseSync('file.js', esm).program;
13+
14+
walk(ast.body, {
15+
ImportDeclaration(n) {
16+
let { start, end } = n as unknown as Location;
17+
let src = n.source as unknown as {
18+
type: 'StringLiteral';
19+
value: string;
20+
start: number;
21+
end: number;
22+
};
23+
24+
let from = src.value;
25+
26+
// NOTE: remove "node:" for Node < 14
27+
// https://nodejs.org/api/esm.html#node-imports
28+
// if (from.startsWith('node:')) {
29+
// from = from.substring(5);
30+
// }
31+
32+
let $locals: string[] = [];
33+
let $default: string | undefined;
34+
35+
let i = 0, arr = n.specifiers;
36+
let tmp: typeof arr[number];
37+
38+
for (; i < arr.length; i++) {
39+
tmp = arr[i];
40+
41+
switch (tmp.type) {
42+
case 'ImportDefaultSpecifier':
43+
case 'ImportNamespaceSpecifier': {
44+
if ($default) throw new Error('Double `default` exports!');
45+
$default = tmp.local.name;
46+
break;
47+
}
48+
49+
case 'ImportSpecifier': {
50+
let { imported, local } = tmp;
51+
if (imported.name !== local.name) {
52+
$locals.push(`${imported.name}: ${local.name}`);
53+
} else {
54+
$locals.push(local.name);
55+
}
56+
break;
57+
}
58+
}
59+
}
60+
61+
let stmt = 'const ';
62+
if ($default) {
63+
stmt += $default;
64+
}
65+
if ($locals.length > 0) {
66+
if ($default) stmt += ', ';
67+
stmt += '{ ' + $locals.join(', ') + ' }';
68+
}
69+
70+
let qq = s.snip(src.start, src.start + 1);
71+
stmt += ` = require(${qq + from + qq});`;
72+
s.overwrite(start, end, stmt);
73+
},
74+
ExportDefaultDeclaration(n) {
75+
let start = (n as unknown as Location).start;
76+
s.overwrite(start, start + 'export default'.length, 'module.exports =');
77+
},
78+
ExportNamedDeclaration(n) {
79+
let start = (n as unknown as Location).start;
80+
let type = n.declaration?.type;
81+
let key: string | undefined;
82+
83+
if (type === 'FunctionDeclaration') {
84+
key = (n.declaration as Function).id?.name;
85+
} else {
86+
console.log('EXPORT NAMED TYPE?', n.declaration);
87+
}
88+
89+
if (key) {
90+
s.remove(start, start + 'export '.length);
91+
s.append(`\nexports.${key} = ${key};`);
92+
}
93+
},
94+
});
95+
96+
return s.toString();
97+
}

0 commit comments

Comments
 (0)