Skip to content

Commit e985b7f

Browse files
committed
feat: provide cjs pkg entrypoints
relates google#340 google#399 google#282 google#197
1 parent bc2a08c commit e985b7f

File tree

11 files changed

+189
-33
lines changed

11 files changed

+189
-33
lines changed

.github/workflows/test.yml

+21
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,24 @@ jobs:
7878
timeout-minutes: 1
7979
env:
8080
FORCE_COLOR: 3
81+
82+
smoke-node:
83+
runs-on: ubuntu-latest
84+
needs: build
85+
strategy:
86+
matrix:
87+
node-version: [ 12, 14, 16, 18, 20 ]
88+
steps:
89+
- uses: actions/checkout@v4
90+
- name: Use Node.js ${{ matrix.node-version }}
91+
uses: actions/setup-node@v4
92+
with:
93+
node-version: ${{ matrix.node-version }}
94+
- uses: actions/download-artifact@v4
95+
with:
96+
name: build
97+
- name: cjs smoke test
98+
run: npm run test:smoke:cjs
99+
- name: mjs smoke test
100+
if: matrix.node-version != '12'
101+
run: npm run test:smoke:mjs

package-lock.json

+41-7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+35-9
Original file line numberDiff line numberDiff line change
@@ -22,31 +22,53 @@
2222
}
2323
},
2424
"exports": {
25-
".": "./build/index.js",
26-
"./globals": "./build/globals.js",
27-
"./cli": "./build/cli.js",
28-
"./core": "./build/core.js",
25+
".": {
26+
"import": "./build/index.js",
27+
"require": "./build/index.cjs",
28+
"types": "./build/index.d.ts",
29+
"default": "./build/index.js"
30+
},
31+
"./globals": {
32+
"import": "./build/globals.js",
33+
"require": "./build/globals.cjs",
34+
"types": "./build/globals.d.ts",
35+
"default": "./build/globals.js"
36+
},
37+
"./cli": {
38+
"import": "./build/cli.js",
39+
"require": "./build/cli.cjs",
40+
"types": "./build/cli.d.ts",
41+
"default": "./build/cli.js"
42+
},
43+
"./core": {
44+
"import": "./build/core.js",
45+
"require": "./build/core.cjs",
46+
"types": "./build/core.d.ts",
47+
"default": "./build/core.js"
48+
},
2949
"./package.json": "./package.json"
3050
},
3151
"bin": {
3252
"zx": "./build/cli.js"
3353
},
3454
"engines": {
35-
"node": ">= 16.0.0"
55+
"node": ">= 12.0.0"
3656
},
3757
"scripts": {
3858
"fmt": "prettier --write .",
3959
"fmt:check": "prettier --check .",
4060
"build": "npm run build:js && npm run build:dts",
4161
"build:check": "tsc",
42-
"build:js": "node scripts/build-js.mjs --format=esm --entry=src/*.ts && npm run build:vendor",
43-
"build:vendor": "node scripts/build-js.mjs --format=esm --entry=src/vendor.ts --bundle=all --banner",
62+
"build:js": "node scripts/build-js.mjs --format=cjs --hybrid --entry=src/*.ts && npm run build:vendor",
63+
"build:vendor": "node scripts/build-js.mjs --format=cjs --entry=src/vendor.ts --bundle=all",
4464
"build:dts": "tsc --project tsconfig.prod.json && node scripts/build-dts.mjs",
4565
"test": "npm run build && npm run test:unit && npm run test:types",
4666
"test:unit": "node ./test/all.test.js",
4767
"test:types": "tsd",
4868
"test:smoke:bun": "bun test ./test/smoke/bun.test.js",
4969
"test:smoke:win32": "node ./test/smoke/win32.test.js",
70+
"test:smoke:cjs": "node ./test/smoke/node.test.cjs",
71+
"test:smoke:mjs": "node ./test/smoke/node.test.mjs",
5072
"coverage": "c8 -x build/vendor.js -x 'test/**' -x scripts --check-coverage npm test",
5173
"circular": "madge --circular src/*",
5274
"version": "cat package.json | fx .version"
@@ -60,27 +82,31 @@
6082
"@types/minimist": "^1.2.5",
6183
"@types/node": ">=20.11.30",
6284
"@types/which": "^3.0.3",
63-
"@webpod/ps": "^0.0.0-beta.3",
6485
"@webpod/ingrid": "^0.0.0-beta.3",
86+
"@webpod/ps": "^0.0.0-beta.3",
6587
"c8": "^9.1.0",
6688
"chalk": "^5.3.0",
89+
"create-require": "^1.1.1",
6790
"depseek": "^0.4.1",
6891
"dts-bundle-generator": "^9.3.1",
6992
"esbuild": "^0.20.2",
7093
"esbuild-node-externals": "^1.13.0",
7194
"esbuild-plugin-entry-chunks": "^0.1.12",
95+
"esbuild-plugin-hybrid-export": "^0.2.0",
96+
"esbuild-plugin-transform-hook": "^0.0.0",
7297
"fs-extra": "^11.2.0",
7398
"fx": "*",
7499
"globby": "^14.0.1",
75100
"madge": "^6.1.0",
76101
"minimist": "^1.2.8",
102+
"node-abort-controller": "^3.1.1",
77103
"node-fetch-native": "^1.6.4",
78104
"prettier": "^3.2.5",
79105
"tsd": "^0.31.0",
80106
"typescript": "^5.4.4",
81107
"which": "^4.0.0",
82108
"yaml": "^2.4.1",
83-
"zurk": "^0.1.2"
109+
"zurk": "^0.1.4"
84110
},
85111
"publishConfig": {
86112
"registry": "https://wombat-dressing-room.appspot.com"

scripts/build-js.mjs

+33-7
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import path from 'node:path'
1818
import esbuild from 'esbuild'
1919
import { nodeExternalsPlugin } from 'esbuild-node-externals'
2020
import { entryChunksPlugin } from 'esbuild-plugin-entry-chunks'
21+
import { hybridExportPlugin } from 'esbuild-plugin-hybrid-export'
22+
import { transformHookPlugin } from 'esbuild-plugin-transform-hook'
2123
import minimist from 'minimist'
2224
import glob from 'fast-glob'
2325

@@ -33,7 +35,7 @@ const argv = minimist(process.argv.slice(2), {
3335
target: 'node12',
3436
cwd: process.cwd(),
3537
},
36-
boolean: ['minify', 'sourcemap', 'banner'],
38+
boolean: ['minify', 'sourcemap', 'banner', 'hybrid'],
3739
string: ['entry', 'external', 'bundle', 'license', 'format', 'map', 'cwd'],
3840
})
3941
const {
@@ -44,19 +46,29 @@ const {
4446
sourcemap,
4547
license,
4648
format,
49+
hybrid,
4750
cwd: _cwd,
4851
} = argv
4952

50-
const plugins = []
53+
const plugins = [transformHookPlugin({
54+
hooks: [{
55+
on: 'end',
56+
pattern: /\.cjs/,
57+
transform(contents) {
58+
return contents
59+
.replaceAll('"node:', '"')
60+
.replaceAll('require("stream/promises")', 'require("stream").promises')
61+
.replaceAll('require("fs/promises")', 'require("fs").promises')
62+
.replaceAll('}).prototype', '}).prototype || {}')
63+
}
64+
}]
65+
})]
5166
const cwd = Array.isArray(_cwd) ? _cwd[_cwd.length - 1] : _cwd
5267
const entries = entry.split(/,\s?/)
5368
const entryPoints = entry.includes('*')
5469
? await glob(entries, { absolute: false, onlyFiles: true, cwd, root: cwd })
5570
: entries.map((p) => path.relative(cwd, path.resolve(cwd, p)))
5671

57-
console.log('cwd=', cwd)
58-
console.log('entryPoints=', entryPoints)
59-
6072
const _bundle = bundle !== 'none' && !process.argv.includes('--no-bundle')
6173
const _external = _bundle ? external.split(',') : undefined // https://github.com/evanw/esbuild/issues/1466
6274

@@ -70,6 +82,14 @@ if (bundle === 'src') {
7082
plugins.push(nodeExternalsPlugin())
7183
}
7284

85+
if (hybrid) {
86+
plugins.push(hybridExportPlugin({
87+
loader: 'import',
88+
to: 'build',
89+
toExt: '.js'
90+
}))
91+
}
92+
7393
const formats = format.split(',')
7494
const banner =
7595
argv.banner && bundle === 'all'
@@ -95,7 +115,7 @@ const esmConfig = {
95115
target: 'esnext',
96116
format: 'esm',
97117
outExtension: {
98-
// '.js': '.mjs'
118+
'.js': '.mjs'
99119
},
100120
plugins,
101121
legalComments: license,
@@ -111,12 +131,18 @@ const cjsConfig = {
111131
format: 'cjs',
112132
banner: {},
113133
outExtension: {
114-
// '.js': '.cjs'
134+
'.js': '.cjs'
135+
},
136+
// https://github.com/evanw/esbuild/issues/1633
137+
define: {
138+
'import.meta.url': 'import_meta_url'
115139
},
140+
inject: ['./scripts/import.meta.url-polyfill.js']
116141
}
117142

118143
for (const format of formats) {
119144
const config = format === 'cjs' ? cjsConfig : esmConfig
145+
console.log('config=', config)
120146

121147
await esbuild.build(config).catch(() => process.exit(1))
122148
}

scripts/import.meta.url-polyfill.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const import_meta_url =
2+
typeof document === 'undefined' ? new (require('url'.replace('', '')).URL)('file:' + __filename).href :
3+
(document.currentScript && document.currentScript.src || new URL('main.js', document.baseURI).href)

src/cli.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
// See the License for the specific language governing permissions and
1515
// limitations under the License.
1616

17-
import { createRequire } from 'node:module'
1817
import { basename, dirname, extname, join, resolve } from 'node:path'
1918
import url from 'node:url'
2019
import {
@@ -26,8 +25,9 @@ import {
2625
minimist,
2726
fs,
2827
} from './index.js'
29-
import { randomId } from './util.js'
3028
import { installDeps, parseDeps } from './deps.js'
29+
import { randomId } from './util.js'
30+
import { createRequire } from './vendor.js'
3131

3232
function printUsage() {
3333
// language=txt
@@ -56,9 +56,9 @@ const argv = minimist(process.argv.slice(2), {
5656
boolean: ['version', 'help', 'quiet', 'verbose', 'install', 'repl'],
5757
alias: { e: 'eval', i: 'install', v: 'version', h: 'help' },
5858
stopEarly: true,
59-
})
59+
});
6060

61-
await (async function main() {
61+
(async function main() {
6262
const globals = './globals.js'
6363
await import(globals)
6464
if (argv.verbose) $.verbose = true

src/vendor.ts

+8
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,17 @@ import {
2828
import * as yaml from 'yaml'
2929
import * as _fs from 'fs-extra'
3030
import type { fetch } from 'node-fetch-native'
31+
import { AbortController } from 'node-abort-controller'
3132

3233
export { exec, buildCmd } from 'zurk/spawn'
3334

35+
import _createRequire from 'create-require'
36+
import { URL } from 'url'
37+
38+
export const createRequire = (_createRequire) as unknown as (filename: string | URL) => NodeRequire
39+
40+
global.AbortController = global.AbortController || AbortController
41+
3442
export { fetch as nodeFetch } from 'node-fetch-native'
3543
export type RequestInfo = Parameters<typeof fetch>[0]
3644
export type RequestInit = Parameters<typeof fetch>[1]

test/extra.test.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,14 @@
1515
import assert from 'node:assert'
1616
import fs from 'node:fs/promises'
1717
import { test, describe } from 'node:test'
18-
import { globby } from 'globby'
18+
import { globby } from '../build/index.js'
1919

2020
describe('extra', () => {
2121
test('every file should have a license', async () => {
22-
const files = await globby(['**/*.{js,mjs,ts}'], {
22+
const files = await globby([
23+
'**/*.{js,mjs,ts}',
24+
'!**/*-polyfill.js'
25+
], {
2326
gitignore: true,
2427
onlyFiles: true,
2528
cwd: process.cwd(),

0 commit comments

Comments
 (0)