Commit f4b0328
Changed files (21)
.github
workflows
scripts
test
.github/workflows/test.yml
@@ -215,7 +215,7 @@ jobs:
- name: Install deps
run: npm ci
- name: Install TypeScript ${{ matrix.ts }}
- run: npm i --force --no-package-lock typescript@${{ matrix.ts }}
+ run: npm i --force --no-package-lock --no-fund typescript@${{ matrix.ts }}
- uses: actions/download-artifact@v4
with:
name: build
scripts/build-dts.mjs
@@ -110,6 +110,7 @@ for (const dts of await glob(['build/**/*.d.ts', '!build/vendor-*.d.ts'])) {
const contents =
(pkgEntries.some((e) => dts.includes(e)) ? prefix : '') +
(await fs.readFile(dts, 'utf8'))
+ .replaceAll(".ts';", ".js';")
.split('\n')
.filter((line) => !line.startsWith('/// <reference types'))
.join('\n')
src/cli.ts
@@ -28,11 +28,12 @@ import {
path,
stdin,
VERSION,
-} from './index.js'
-import { installDeps, parseDeps } from './deps.js'
-import { startRepl } from './repl.js'
-import { randomId, bufToString } from './util.js'
-import { createRequire, type minimist } from './vendor.js'
+} from './index.ts'
+import { installDeps, parseDeps } from './deps.ts'
+import { startRepl } from './repl.ts'
+import { randomId, bufToString } from './util.ts'
+import { transformMarkdown } from './md.ts'
+import { createRequire, type minimist } from './vendor.ts'
const EXT = '.mjs'
const EXT_RE = /^\.[mc]?[jt]sx?$/
@@ -206,6 +207,8 @@ async function readScriptFromHttp(remote: string): Promise<string> {
return res.text()
}
+export { transformMarkdown }
+
export function injectGlobalRequire(origin: string): void {
const __filename = path.resolve(origin)
const __dirname = path.dirname(__filename)
@@ -213,79 +216,6 @@ export function injectGlobalRequire(origin: string): void {
Object.assign(globalThis, { __filename, __dirname, require })
}
-export function transformMarkdown(buf: Buffer | string): string {
- const output = []
- const tabRe = /^( +|\t)/
- const codeBlockRe =
- /^(?<fence>(`{3,20}|~{3,20}))(?:(?<js>(js|javascript|ts|typescript))|(?<bash>(sh|shell|bash))|.*)$/
- let state = 'root'
- let codeBlockEnd = ''
- let prevLineIsEmpty = true
- for (const line of bufToString(buf).split(/\r?\n/)) {
- switch (state) {
- case 'root':
- if (tabRe.test(line) && prevLineIsEmpty) {
- output.push(line)
- state = 'tab'
- continue
- }
- const { fence, js, bash } = line.match(codeBlockRe)?.groups || {}
- if (!fence) {
- prevLineIsEmpty = line === ''
- output.push('// ' + line)
- continue
- }
- codeBlockEnd = fence
- if (js) {
- state = 'js'
- output.push('')
- } else if (bash) {
- state = 'bash'
- output.push('await $`')
- } else {
- state = 'other'
- output.push('')
- }
- break
- case 'tab':
- if (line === '') {
- output.push('')
- } else if (tabRe.test(line)) {
- output.push(line)
- } else {
- output.push('// ' + line)
- state = 'root'
- }
- break
- case 'js':
- if (line === codeBlockEnd) {
- output.push('')
- state = 'root'
- } else {
- output.push(line)
- }
- break
- case 'bash':
- if (line === codeBlockEnd) {
- output.push('`')
- state = 'root'
- } else {
- output.push(line)
- }
- break
- case 'other':
- if (line === codeBlockEnd) {
- output.push('')
- state = 'root'
- } else {
- output.push('// ' + line)
- }
- break
- }
- }
- return output.join('\n')
-}
-
export function isMain(
metaurl: string = import.meta.url,
scriptpath: string = process.argv[1]
src/core.ts
@@ -31,7 +31,7 @@ import {
formatExitMessage,
getCallerLocation,
getExitCodeInfo,
-} from './error.js'
+} from './error.ts'
import {
exec,
buildCmd,
@@ -41,7 +41,7 @@ import {
VoidStream,
type ChalkInstance,
type TSpawnStore,
-} from './vendor-core.js'
+} from './vendor-core.ts'
import {
type Duration,
log,
@@ -60,9 +60,9 @@ import {
toCamelCase,
randomId,
bufArrJoin,
-} from './util.js'
+} from './util.ts'
-export { log, type LogEntry } from './util.js'
+export { log, type LogEntry } from './util.ts'
const CWD = Symbol('processCwd')
const SYNC = Symbol('syncExec')
src/deps.ts
@@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import { $ } from './core.js'
-import { spinner } from './goods.js'
-import { depseek } from './vendor.js'
+import { $ } from './core.ts'
+import { spinner } from './goods.ts'
+import { depseek } from './vendor.ts'
/**
* Install npm dependencies
src/globals.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import * as _ from './index.js'
+import * as _ from './index.ts'
Object.assign(globalThis, _)
// TODO: global types not working with jsr.io
src/goods.ts
@@ -14,7 +14,7 @@
import assert from 'node:assert'
import { createInterface } from 'node:readline'
-import { $, within, ProcessOutput } from './core.js'
+import { $, within, ProcessOutput } from './core.ts'
import {
type Duration,
identity,
@@ -22,13 +22,13 @@ import {
parseBool,
parseDuration,
toCamelCase,
-} from './util.js'
+} from './util.ts'
import {
type RequestInfo,
type RequestInit,
nodeFetch,
minimist,
-} from './vendor.js'
+} from './vendor.ts'
export { default as path } from 'node:path'
export * as os from 'node:os'
src/index.ts
@@ -12,11 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import { ProcessPromise } from './core.js'
-import { fs } from './vendor.js'
+import { ProcessPromise } from './core.ts'
+import { fs } from './vendor.ts'
-export * from './core.js'
-export * from './goods.js'
+export * from './core.ts'
+export * from './goods.ts'
export {
minimist,
chalk,
@@ -27,7 +27,7 @@ export {
ps,
glob,
glob as globby,
-} from './vendor.js'
+} from './vendor.ts'
export const VERSION: string = fs.readJsonSync(
new URL('../package.json', import.meta.url)
@@ -42,7 +42,7 @@ export {
tempdir as tmpdir,
tempfile,
tempfile as tmpfile,
-} from './util.js'
+} from './util.ts'
/**
* @deprecated Use $`cmd`.nothrow() instead.
src/md.ts
@@ -0,0 +1,88 @@
+// Copyright 2025 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.
+
+import { bufToString } from './util.ts'
+
+export function transformMarkdown(buf: Buffer | string): string {
+ const output = []
+ const tabRe = /^( +|\t)/
+ const codeBlockRe =
+ /^(?<fence>(`{3,20}|~{3,20}))(?:(?<js>(js|javascript|ts|typescript))|(?<bash>(sh|shell|bash))|.*)$/
+ let state = 'root'
+ let codeBlockEnd = ''
+ let prevLineIsEmpty = true
+ for (const line of bufToString(buf).split(/\r?\n/)) {
+ switch (state) {
+ case 'root':
+ if (tabRe.test(line) && prevLineIsEmpty) {
+ output.push(line)
+ state = 'tab'
+ continue
+ }
+ const { fence, js, bash } = line.match(codeBlockRe)?.groups || {}
+ if (!fence) {
+ prevLineIsEmpty = line === ''
+ output.push('// ' + line)
+ continue
+ }
+ codeBlockEnd = fence
+ if (js) {
+ state = 'js'
+ output.push('')
+ } else if (bash) {
+ state = 'bash'
+ output.push('await $`')
+ } else {
+ state = 'other'
+ output.push('')
+ }
+ break
+ case 'tab':
+ if (line === '') {
+ output.push('')
+ } else if (tabRe.test(line)) {
+ output.push(line)
+ } else {
+ output.push('// ' + line)
+ state = 'root'
+ }
+ break
+ case 'js':
+ if (line === codeBlockEnd) {
+ output.push('')
+ state = 'root'
+ } else {
+ output.push(line)
+ }
+ break
+ case 'bash':
+ if (line === codeBlockEnd) {
+ output.push('`')
+ state = 'root'
+ } else {
+ output.push(line)
+ }
+ break
+ case 'other':
+ if (line === codeBlockEnd) {
+ output.push('')
+ state = 'root'
+ } else {
+ output.push('// ' + line)
+ }
+ break
+ }
+ }
+ return output.join('\n')
+}
src/repl.ts
@@ -16,8 +16,8 @@ import os from 'node:os'
import path from 'node:path'
import repl from 'node:repl'
import { inspect } from 'node:util'
-import { ProcessOutput, defaults } from './core.js'
-import { chalk } from './vendor-core.js'
+import { ProcessOutput, defaults } from './core.ts'
+import { chalk } from './vendor-core.ts'
const HISTORY =
process.env.ZX_REPL_HISTORY ?? path.join(os.homedir(), '.zx_repl_history')
src/util.ts
@@ -20,10 +20,10 @@ import {
type RequestInfo,
type RequestInit,
type TSpawnStoreChunks,
-} from './vendor-core.js'
+} from './vendor-core.ts'
import { inspect } from 'node:util'
-export { isStringLiteral } from './vendor-core.js'
+export { isStringLiteral } from './vendor-core.ts'
export function tempdir(
prefix: string = `zx-${randomId()}`,
src/vendor.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import { bus } from './vendor-core.js'
+import { bus } from './vendor-core.ts'
import {
depseek as _depseek,
dotenv as _dotenv,
@@ -22,10 +22,10 @@ import {
YAML as _YAML,
glob as _glob,
nodeFetch as _nodeFetch,
-} from './vendor-extra.js'
+} from './vendor-extra.ts'
-export * from './vendor-core.js'
-export { createRequire } from './vendor-extra.js'
+export * from './vendor-core.ts'
+export { createRequire } from './vendor-extra.ts'
const { wrap } = bus
export const depseek: typeof _depseek = wrap('depseek', _depseek)
test/smoke/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "compilerOptions": {
+ "target": "ES2021",
+ "lib": ["ES2021"],
+ "moduleResolution": "NodeNext",
+ "module": "NodeNext",
+ "strict": true,
+ "outDir": "./build",
+ "declaration": true,
+ "emitDeclarationOnly": true,
+ "types": []
+ },
+ "include": ["./src/**/*"],
+ "exclude": ["./src/globals.ts"]
+}
test/all.test.js
@@ -20,6 +20,7 @@ import './export.test.js'
import './global.test.js'
import './goods.test.js'
import './index.test.js'
+import './md.test.ts'
import './package.test.js'
import './util.test.js'
import './vendor.test.js'
test/cli.test.js
@@ -18,7 +18,7 @@ import { fileURLToPath } from 'node:url'
import net from 'node:net'
import getPort from 'get-port'
import { $, path, tmpfile, tmpdir, fs } from '../build/index.js'
-import { isMain, normalizeExt, transformMarkdown } from '../build/cli.js'
+import { isMain, normalizeExt } from '../build/cli.js'
const __filename = fileURLToPath(import.meta.url)
const spawn = $.spawn
@@ -349,43 +349,6 @@ describe('cli', () => {
}
})
- test('transformMarkdown()', () => {
- // prettier-ignore
- assert.equal(transformMarkdown(`
-# Title
-
-~~~js
-await $\`echo "js"\`
-~~~
-
-typescript code block
-~~~~~ts
-await $\`echo "ts"\`
-~~~~~
-
-~~~
-unknown code block
-~~~
-
-`), `//
-// # Title
-//
-
-await $\`echo "js"\`
-
-//
-// typescript code block
-
-await $\`echo "ts"\`
-
-//
-
-// unknown code block
-
-//
-// `)
- })
-
test('normalizeExt()', () => {
assert.equal(normalizeExt('.ts'), '.ts')
assert.equal(normalizeExt('ts'), '.ts')
test/md.test.ts
@@ -0,0 +1,72 @@
+// Copyright 2025 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.
+
+import { test, describe } from 'node:test'
+import assert from 'node:assert'
+import { transformMarkdown } from '../src/md.ts'
+
+describe('md', () => {
+ test('transformMarkdown()', () => {
+ assert.equal(transformMarkdown('\n'), '// \n// ')
+ assert.equal(transformMarkdown(' \n '), ' \n ')
+ assert.equal(
+ transformMarkdown(`
+\t~~~js
+console.log('js')`),
+ `// \n\t~~~js\n// console.log('js')`
+ )
+ // prettier-ignore
+ assert.equal(transformMarkdown(`
+# Title
+
+~~~js
+await $\`echo "js"\`
+~~~
+
+typescript code block
+~~~~~ts
+await $\`echo "ts"\`
+~~~~~
+
+~~~
+unknown code block
+~~~
+
+~~~sh
+echo foo
+~~~
+
+`), `//
+// # Title
+//
+
+await $\`echo "js"\`
+
+//
+// typescript code block
+
+await $\`echo "ts"\`
+
+//
+
+// unknown code block
+
+//
+await $\`
+echo foo
+\`
+//
+// `)
+ })
+})
test/package.test.js
@@ -62,6 +62,7 @@ describe('package', () => {
'build/index.cjs',
'build/index.d.ts',
'build/index.js',
+ 'build/md.d.ts',
'build/util.cjs',
'build/util.d.ts',
'build/util.js',
.nycrc
@@ -2,5 +2,14 @@
"reporter": ["html", "text"],
"lines": 98,
"branches": "90",
- "statements": "98"
+ "statements": "98",
+ "exclude": [
+ "build/deno.js",
+ "build/vendor-extra.cjs",
+ "build/vendor-core.cjs",
+ "build/esblib.cjs",
+ "test/**",
+ "scripts",
+ "src/util.ts"
+ ]
}
.size-limit.json
@@ -30,7 +30,7 @@
{
"name": "all",
"path": "build/*",
- "limit": "850.2 kB",
+ "limit": "850.25 kB",
"brotli": false,
"gzip": false
}
package.json
@@ -63,7 +63,7 @@
"fmt": "prettier --write .",
"fmt:check": "prettier --check .",
"build": "npm run build:js && npm run build:dts && npm run build:tests",
- "build:js": "node scripts/build-js.mjs --format=cjs --hybrid --entry=src/*.ts:!src/error.ts:!src/repl.ts && npm run build:vendor",
+ "build:js": "node scripts/build-js.mjs --format=cjs --hybrid --entry=src/*.ts:!src/error.ts:!src/repl.ts:!src/md.ts && npm run build:vendor",
"build:vendor": "node scripts/build-js.mjs --format=cjs --entry=src/vendor-*.ts --bundle=all",
"build:tests": "node scripts/build-tests.mjs",
"build:dts": "tsc --project tsconfig.json && rm build/error.d.ts build/repl.d.ts && node scripts/build-dts.mjs",
@@ -75,7 +75,7 @@
"test:it": "node ./test/it/build.test.js",
"test:jsr": "node ./test/it/build-jsr.test.js",
"test:unit": "node --experimental-strip-types ./test/all.test.js",
- "test:coverage": "c8 -x build/deno.js -x build/vendor-extra.cjs -x build/vendor-core.cjs -x build/esblib.cjs -x 'test/**' -x scripts --check-coverage npm run test:unit",
+ "test:coverage": "c8 -c .nycrc --check-coverage npm run test:unit",
"test:circular": "madge --circular src/*",
"test:types": "tsd",
"test:license": "node ./test/extra.test.js",
@@ -84,7 +84,7 @@
"test:smoke:strip-types": "node --experimental-strip-types test/smoke/ts.test.ts",
"test:smoke:tsx": "tsx test/smoke/ts.test.ts",
"test:smoke:tsc": "cd test/smoke && mkdir -p node_modules && ln -s ../../../ ./node_modules/zx; ../../node_modules/typescript/bin/tsc -v && ../../node_modules/typescript/bin/tsc --esModuleInterop --module node16 --rootDir . --outdir ./temp ts.test.ts && node ./temp/ts.test.js",
- "test:smoke:ts-node": "node --loader ts-node/esm test/smoke/ts.test.ts",
+ "test:smoke:ts-node": "cd test/smoke && node --loader ts-node/esm ts.test.ts",
"test:smoke:bun": "bun test ./test/smoke/bun.test.js && bun ./test/smoke/node.test.mjs",
"test:smoke:win32": "node ./test/smoke/win32.test.js",
"test:smoke:cjs": "node ./test/smoke/node.test.cjs",
tsconfig.json
@@ -8,6 +8,7 @@
"outDir": "./build",
"declaration": true,
"emitDeclarationOnly": true,
+ "allowImportingTsExtensions": true,
"types": []
},
"include": ["./src/**/*"],