Commit 086b500
Changed files (12)
.github
workflows
test
.github/workflows/test.yml
@@ -78,3 +78,24 @@ jobs:
timeout-minutes: 1
env:
FORCE_COLOR: 3
+
+ smoke-node:
+ runs-on: ubuntu-latest
+ needs: build
+ strategy:
+ matrix:
+ node-version: [12, 14, 16, 18, 20]
+ steps:
+ - uses: actions/checkout@v4
+ - name: Use Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@v4
+ with:
+ node-version: ${{ matrix.node-version }}
+ - uses: actions/download-artifact@v4
+ with:
+ name: build
+ - name: cjs smoke test
+ run: npm run test:smoke:cjs
+ - name: mjs smoke test
+ if: matrix.node-version != '12'
+ run: npm run test:smoke:mjs
scripts/build-js.mjs
@@ -18,6 +18,9 @@ import path from 'node:path'
import esbuild from 'esbuild'
import { nodeExternalsPlugin } from 'esbuild-node-externals'
import { entryChunksPlugin } from 'esbuild-plugin-entry-chunks'
+import { hybridExportPlugin } from 'esbuild-plugin-hybrid-export'
+import { transformHookPlugin } from 'esbuild-plugin-transform-hook'
+import { extractHelpersPlugin } from 'esbuild-plugin-extract-helpers'
import minimist from 'minimist'
import glob from 'fast-glob'
@@ -33,7 +36,7 @@ const argv = minimist(process.argv.slice(2), {
target: 'node12',
cwd: process.cwd(),
},
- boolean: ['minify', 'sourcemap', 'banner'],
+ boolean: ['minify', 'sourcemap', 'banner', 'hybrid'],
string: ['entry', 'external', 'bundle', 'license', 'format', 'map', 'cwd'],
})
const {
@@ -44,6 +47,7 @@ const {
sourcemap,
license,
format,
+ hybrid,
cwd: _cwd,
} = argv
@@ -54,9 +58,6 @@ const entryPoints = entry.includes('*')
? await glob(entries, { absolute: false, onlyFiles: true, cwd, root: cwd })
: entries.map((p) => path.relative(cwd, path.resolve(cwd, p)))
-console.log('cwd=', cwd)
-console.log('entryPoints=', entryPoints)
-
const _bundle = bundle !== 'none' && !process.argv.includes('--no-bundle')
const _external = _bundle ? external.split(',') : undefined // https://github.com/evanw/esbuild/issues/1466
@@ -70,6 +71,50 @@ if (bundle === 'src') {
plugins.push(nodeExternalsPlugin())
}
+if (hybrid) {
+ plugins.push(
+ hybridExportPlugin({
+ loader: 'import',
+ to: 'build',
+ toExt: '.js',
+ })
+ )
+}
+
+plugins.push(
+ transformHookPlugin({
+ hooks: [
+ {
+ on: 'end',
+ pattern: new RegExp(
+ '(' +
+ entryPoints.map((e) => path.parse(e).name).join('|') +
+ ')\\.cjs$'
+ ),
+ transform(contents) {
+ return contents
+ .toString()
+ .replaceAll('"node:', '"')
+ .replaceAll(
+ 'require("stream/promises")',
+ 'require("stream").promises'
+ )
+ .replaceAll('require("fs/promises")', 'require("fs").promises')
+ .replaceAll('}).prototype', '}).prototype || {}')
+ .replace(
+ /\/\/ Annotate the CommonJS export names for ESM import in node:/,
+ ($0) => `/* c8 ignore next 100 */\n${$0}`
+ )
+ },
+ },
+ ],
+ }),
+ extractHelpersPlugin({
+ cwd: 'build',
+ include: /\.cjs/,
+ })
+)
+
const formats = format.split(',')
const banner =
argv.banner && bundle === 'all'
@@ -95,7 +140,7 @@ const esmConfig = {
target: 'esnext',
format: 'esm',
outExtension: {
- // '.js': '.mjs'
+ '.js': '.mjs',
},
plugins,
legalComments: license,
@@ -111,12 +156,18 @@ const cjsConfig = {
format: 'cjs',
banner: {},
outExtension: {
- // '.js': '.cjs'
+ '.js': '.cjs',
+ },
+ // https://github.com/evanw/esbuild/issues/1633
+ define: {
+ 'import.meta.url': 'import_meta_url',
},
+ inject: ['./scripts/import.meta.url-polyfill.js'],
}
for (const format of formats) {
const config = format === 'cjs' ? cjsConfig : esmConfig
+ console.log('config=', config)
await esbuild.build(config).catch(() => process.exit(1))
}
scripts/import.meta.url-polyfill.js
@@ -0,0 +1,5 @@
+export const import_meta_url =
+ typeof document === 'undefined'
+ ? new (require('url'.replace('', '')).URL)('file:' + __filename).href
+ : (document.currentScript && document.currentScript.src) ||
+ new URL('main.js', document.baseURI).href
src/cli.ts
@@ -14,7 +14,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import { createRequire } from 'node:module'
import { basename, dirname, extname, join, resolve } from 'node:path'
import url from 'node:url'
import {
@@ -26,8 +25,9 @@ import {
minimist,
fs,
} from './index.js'
-import { randomId } from './util.js'
import { installDeps, parseDeps } from './deps.js'
+import { randomId } from './util.js'
+import { createRequire } from './vendor.js'
function printUsage() {
// language=txt
@@ -58,7 +58,7 @@ const argv = minimist(process.argv.slice(2), {
stopEarly: true,
})
-await (async function main() {
+;(async function main() {
const globals = './globals.js'
await import(globals)
if (argv.verbose) $.verbose = true
src/vendor.ts
@@ -28,9 +28,19 @@ import {
import * as yaml from 'yaml'
import * as _fs from 'fs-extra'
import type { fetch } from 'node-fetch-native'
+import { AbortController } from 'node-abort-controller'
export { exec, buildCmd } from 'zurk/spawn'
+import _createRequire from 'create-require'
+import { URL } from 'url'
+
+export const createRequire = _createRequire as unknown as (
+ filename: string | URL
+) => NodeRequire
+
+global.AbortController = global.AbortController || AbortController
+
export { fetch as nodeFetch } from 'node-fetch-native'
export type RequestInfo = Parameters<typeof fetch>[0]
export type RequestInit = Parameters<typeof fetch>[1]
test/fixtures/js-project/package-lock.json
@@ -23,26 +23,31 @@
"@webpod/ps": "^0.0.0-beta.3",
"c8": "^9.1.0",
"chalk": "^5.3.0",
+ "create-require": "^1.1.1",
"depseek": "^0.4.1",
- "dts-bundle-generator": "^9.3.1",
+ "dts-bundle-generator": "^9.5.1",
"esbuild": "^0.20.2",
"esbuild-node-externals": "^1.13.0",
"esbuild-plugin-entry-chunks": "^0.1.12",
+ "esbuild-plugin-extract-helpers": "^0.0.3",
+ "esbuild-plugin-hybrid-export": "^0.2.1",
+ "esbuild-plugin-transform-hook": "^0.0.1",
"fs-extra": "^11.2.0",
"fx": "*",
"globby": "^14.0.1",
"madge": "^6.1.0",
"minimist": "^1.2.8",
+ "node-abort-controller": "^3.1.1",
"node-fetch-native": "^1.6.4",
"prettier": "^3.2.5",
"tsd": "^0.31.0",
"typescript": "^5.4.4",
"which": "^4.0.0",
"yaml": "^2.4.1",
- "zurk": "^0.1.2"
+ "zurk": "^0.1.4"
},
"engines": {
- "node": ">= 16.0.0"
+ "node": ">= 12.0.0"
},
"optionalDependencies": {
"@types/fs-extra": "^11.0.4",
test/smoke/node.test.cjs
@@ -0,0 +1,31 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+const assert = require('assert')
+require('zx/globals')
+;(async () => {
+ // smoke test
+ {
+ const p = await $`echo foo`
+ assert.match(p.stdout, /foo/)
+ }
+
+ // captures err stack
+ {
+ const p = await $({ nothrow: true })`echo foo; exit 3`
+ assert.match(p.message, /exit code: 3/)
+ }
+})()
+
+console.log('smoke cjs: ok')
test/smoke/node-esm.test.js → test/smoke/node.test.mjs
@@ -13,7 +13,7 @@
// limitations under the License.
import assert from 'assert'
-import '../../build/globals.js'
+import 'zx/globals'
;(async () => {
// smoke test
{
@@ -28,4 +28,4 @@ import '../../build/globals.js'
}
})()
-console.log('smoke: ok')
+console.log('smoke mjs: ok')
test/all.test.js
@@ -15,7 +15,6 @@
import './cli.test.js'
import './core.test.js'
import './deps.test.js'
-import './extra.test.js'
import './global.test.js'
import './goods.test.js'
import './index.test.js'
test/extra.test.js
@@ -15,11 +15,11 @@
import assert from 'node:assert'
import fs from 'node:fs/promises'
import { test, describe } from 'node:test'
-import { globby } from 'globby'
+import { globby } from '../build/index.js'
describe('extra', () => {
test('every file should have a license', async () => {
- const files = await globby(['**/*.{js,mjs,ts}'], {
+ const files = await globby(['**/*.{js,mjs,ts}', '!**/*-polyfill.js'], {
gitignore: true,
onlyFiles: true,
cwd: process.cwd(),
package-lock.json
@@ -1,12 +1,12 @@
{
"name": "zx",
- "version": "8.0.0",
+ "version": "8.0.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "zx",
- "version": "8.0.0",
+ "version": "8.0.2",
"license": "Apache-2.0",
"bin": {
"zx": "build/cli.js"
@@ -20,26 +20,31 @@
"@webpod/ps": "^0.0.0-beta.3",
"c8": "^9.1.0",
"chalk": "^5.3.0",
+ "create-require": "^1.1.1",
"depseek": "^0.4.1",
- "dts-bundle-generator": "^9.3.1",
+ "dts-bundle-generator": "^9.5.1",
"esbuild": "^0.20.2",
"esbuild-node-externals": "^1.13.0",
"esbuild-plugin-entry-chunks": "^0.1.12",
+ "esbuild-plugin-extract-helpers": "^0.0.3",
+ "esbuild-plugin-hybrid-export": "^0.2.1",
+ "esbuild-plugin-transform-hook": "^0.0.1",
"fs-extra": "^11.2.0",
"fx": "*",
"globby": "^14.0.1",
"madge": "^6.1.0",
"minimist": "^1.2.8",
+ "node-abort-controller": "^3.1.1",
"node-fetch-native": "^1.6.4",
"prettier": "^3.2.5",
"tsd": "^0.31.0",
"typescript": "^5.4.4",
"which": "^4.0.0",
"yaml": "^2.4.1",
- "zurk": "^0.1.2"
+ "zurk": "^0.1.4"
},
"engines": {
- "node": ">= 16.0.0"
+ "node": ">= 12.0.0"
},
"optionalDependencies": {
"@types/fs-extra": "^11.0.4",
@@ -835,6 +840,12 @@
"safe-buffer": "~5.1.1"
}
},
+ "node_modules/create-require": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+ "dev": true
+ },
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -1199,9 +1210,9 @@
}
},
"node_modules/dts-bundle-generator": {
- "version": "9.3.1",
- "resolved": "https://registry.npmjs.org/dts-bundle-generator/-/dts-bundle-generator-9.3.1.tgz",
- "integrity": "sha512-1/nMT7LFOkXbrL1ZvLpzrjNbfX090LZ64nLIXVmet557mshFCGP/oTiQiZenafJZ6GsmRQLTYKSlQnkxK8tsTw==",
+ "version": "9.5.1",
+ "resolved": "https://registry.npmjs.org/dts-bundle-generator/-/dts-bundle-generator-9.5.1.tgz",
+ "integrity": "sha512-DxpJOb2FNnEyOzMkG11sxO2dmxPjthoVWxfKqWYJ/bI/rT1rvTMktF5EKjAYrRZu6Z6t3NhOUZ0sZ5ZXevOfbA==",
"dev": true,
"dependencies": {
"typescript": ">=5.0.2",
@@ -1302,6 +1313,36 @@
"esbuild": ">=0.19.0"
}
},
+ "node_modules/esbuild-plugin-extract-helpers": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/esbuild-plugin-extract-helpers/-/esbuild-plugin-extract-helpers-0.0.3.tgz",
+ "integrity": "sha512-pD9p+WHsrN8iyRRFuI8on1grE0KQhiOmueYQV/PLHWzydY1fliNfydiRl5yemE+0S7keJ/ga2F9SCLUzS4H4tw==",
+ "dev": true,
+ "dependencies": {
+ "esbuild-plugin-transform-hook": "0.0.1"
+ },
+ "peerDependencies": {
+ "esbuild": ">=0.19.0"
+ }
+ },
+ "node_modules/esbuild-plugin-hybrid-export": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/esbuild-plugin-hybrid-export/-/esbuild-plugin-hybrid-export-0.2.1.tgz",
+ "integrity": "sha512-vdFLmsekj+lDihz7LAr61ApDsEQtVdUvwlGc+asnBlZbSG5DtCSlBkFR0FlJ+L4fjsQ62Supo3tAEd7K/oq48w==",
+ "dev": true,
+ "peerDependencies": {
+ "esbuild": ">=0.19.0"
+ }
+ },
+ "node_modules/esbuild-plugin-transform-hook": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/esbuild-plugin-transform-hook/-/esbuild-plugin-transform-hook-0.0.1.tgz",
+ "integrity": "sha512-OurhRFH6FYJcMKEw73/aAyOTSH+weEoHNuAuRnITR13u6BnSeXbuVdMS+Zuq1Mc/oGuk9hFIjm6MMsHNb+42kw==",
+ "dev": true,
+ "peerDependencies": {
+ "esbuild": ">=0.19.0"
+ }
+ },
"node_modules/escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
@@ -2743,6 +2784,12 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
+ "node_modules/node-abort-controller": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz",
+ "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==",
+ "dev": true
+ },
"node_modules/node-fetch-native": {
"version": "1.6.4",
"resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.4.tgz",
@@ -4384,9 +4431,9 @@
}
},
"node_modules/zurk": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/zurk/-/zurk-0.1.2.tgz",
- "integrity": "sha512-iulDU6SgS+YvvPqLaHrBzDMXxPj72JKCHzbx3oo+38zcyMcF/kwK4vClnHwRZ8UqtQB35Lj1cRXRqoXKvRUdqg==",
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/zurk/-/zurk-0.1.4.tgz",
+ "integrity": "sha512-sAUhRgP6vqTxVhcjSMlukAZ9l2TJSK3HWOnzAIUYInkuToS07LdAb878xtNc/Dgj7sve43chedsXc/k5ft/WyQ==",
"dev": true
}
}
package.json
@@ -22,32 +22,55 @@
}
},
"exports": {
- ".": "./build/index.js",
- "./globals": "./build/globals.js",
- "./cli": "./build/cli.js",
- "./core": "./build/core.js",
+ ".": {
+ "import": "./build/index.js",
+ "require": "./build/index.cjs",
+ "types": "./build/index.d.ts",
+ "default": "./build/index.js"
+ },
+ "./globals": {
+ "import": "./build/globals.js",
+ "require": "./build/globals.cjs",
+ "types": "./build/globals.d.ts",
+ "default": "./build/globals.js"
+ },
+ "./cli": {
+ "import": "./build/cli.js",
+ "require": "./build/cli.cjs",
+ "types": "./build/cli.d.ts",
+ "default": "./build/cli.js"
+ },
+ "./core": {
+ "import": "./build/core.js",
+ "require": "./build/core.cjs",
+ "types": "./build/core.d.ts",
+ "default": "./build/core.js"
+ },
"./package.json": "./package.json"
},
"bin": {
"zx": "./build/cli.js"
},
"engines": {
- "node": ">= 16.0.0"
+ "node": ">= 12.0.0"
},
"scripts": {
"fmt": "prettier --write .",
"fmt:check": "prettier --check .",
"build": "npm run build:js && npm run build:dts",
"build:check": "tsc",
- "build:js": "node scripts/build-js.mjs --format=esm --entry=src/*.ts && npm run build:vendor",
- "build:vendor": "node scripts/build-js.mjs --format=esm --entry=src/vendor.ts --bundle=all --banner",
+ "build:js": "node scripts/build-js.mjs --format=cjs --hybrid --entry=src/*.ts && npm run build:vendor",
+ "build:vendor": "node scripts/build-js.mjs --format=cjs --entry=src/vendor.ts --bundle=all",
"build:dts": "tsc --project tsconfig.prod.json && node scripts/build-dts.mjs",
- "test": "npm run build && npm run test:unit && npm run test:types",
+ "test": "npm run build && npm run test:unit && npm run test:types && npm run test:license",
"test:unit": "node ./test/all.test.js",
"test:types": "tsd",
+ "test:license": "node ./test/extra.test.js",
"test:smoke:bun": "bun test ./test/smoke/bun.test.js",
"test:smoke:win32": "node ./test/smoke/win32.test.js",
- "coverage": "c8 -x build/vendor.js -x 'test/**' -x scripts --check-coverage npm test",
+ "test:smoke:cjs": "node ./test/smoke/node.test.cjs",
+ "test:smoke:mjs": "node ./test/smoke/node.test.mjs",
+ "coverage": "c8 -x build/vendor.cjs -x build/esblib.cjs -x 'test/**' -x scripts --check-coverage npm test",
"circular": "madge --circular src/*",
"version": "cat package.json | fx .version"
},
@@ -60,27 +83,32 @@
"@types/minimist": "^1.2.5",
"@types/node": ">=20.11.30",
"@types/which": "^3.0.3",
- "@webpod/ps": "^0.0.0-beta.3",
"@webpod/ingrid": "^0.0.0-beta.3",
+ "@webpod/ps": "^0.0.0-beta.3",
"c8": "^9.1.0",
"chalk": "^5.3.0",
+ "create-require": "^1.1.1",
"depseek": "^0.4.1",
- "dts-bundle-generator": "^9.3.1",
+ "dts-bundle-generator": "^9.5.1",
"esbuild": "^0.20.2",
"esbuild-node-externals": "^1.13.0",
"esbuild-plugin-entry-chunks": "^0.1.12",
+ "esbuild-plugin-hybrid-export": "^0.2.1",
+ "esbuild-plugin-transform-hook": "^0.0.1",
+ "esbuild-plugin-extract-helpers": "^0.0.3",
"fs-extra": "^11.2.0",
"fx": "*",
"globby": "^14.0.1",
"madge": "^6.1.0",
"minimist": "^1.2.8",
+ "node-abort-controller": "^3.1.1",
"node-fetch-native": "^1.6.4",
"prettier": "^3.2.5",
"tsd": "^0.31.0",
"typescript": "^5.4.4",
"which": "^4.0.0",
"yaml": "^2.4.1",
- "zurk": "^0.1.2"
+ "zurk": "^0.1.4"
},
"publishConfig": {
"registry": "https://wombat-dressing-room.appspot.com"