From b61073600a61584054a7d45f2be1a454c657d95e Mon Sep 17 00:00:00 2001 From: Anton Golub Date: Sat, 21 Dec 2024 15:59:55 +0300 Subject: [PATCH] refactor: separate error helpers closes #964 --- package.json | 6 +- src/core.ts | 42 ++------- src/error.ts | 226 +++++++++++++++++++++++++++++++++++++++++++++ src/util.ts | 181 ------------------------------------ test/all.test.js | 1 + test/core.test.js | 21 +++++ test/error.test.ts | 80 ++++++++++++++++ test/util.test.js | 126 +++++++------------------ tsconfig.json | 5 +- 9 files changed, 378 insertions(+), 310 deletions(-) create mode 100644 src/error.ts create mode 100644 test/error.test.ts diff --git a/package.json b/package.json index 0fb10481e4..037f1b9d7a 100644 --- a/package.json +++ b/package.json @@ -63,10 +63,10 @@ "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 && npm run build:vendor", + "build:js": "node scripts/build-js.mjs --format=cjs --hybrid --entry=src/*.ts:!src/error.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.prod.json && node scripts/build-dts.mjs", + "build:dts": "tsc --project tsconfig.prod.json && rm build/error.d.ts && node scripts/build-dts.mjs", "docs:dev": "vitepress dev docs", "docs:build": "vitepress build docs", "docs:preview": "vitepress preview docs", @@ -74,7 +74,7 @@ "test": "npm run test:size && npm run fmt:check && npm run test:unit && npm run test:types && npm run test:license", "test:it": "node ./test/it/build.test.js", "test:jsr": "node ./test/it/build-jsr.test.js", - "test:unit": "node ./test/all.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:circular": "madge --circular src/*", "test:types": "tsd", diff --git a/src/core.ts b/src/core.ts index a24cec9a58..c2cd84c803 100644 --- a/src/core.ts +++ b/src/core.ts @@ -25,6 +25,12 @@ import { type Readable, type Writable } from 'node:stream' import { inspect } from 'node:util' import { EOL as _EOL } from 'node:os' import { EventEmitter } from 'node:events' +import { + formatErrorMessage, + formatExitMessage, + getCallerLocation, + getExitCodeInfo, +} from './error.js' import { exec, buildCmd, @@ -39,10 +45,7 @@ import { } from './vendor-core.js' import { type Duration, - errnoMessage, - exitCodeInfo, formatCmd, - getCallerLocation, isString, isStringLiteral, noop, @@ -720,34 +723,9 @@ export class ProcessOutput extends Error { return this._duration } - static getExitMessage( - code: number | null, - signal: NodeJS.Signals | null, - stderr: string, - from: string - ): string { - let message = `exit code: ${code}` - if (code != 0 || signal != null) { - message = `${stderr || '\n'} at ${from}` - message += `\n exit code: ${code}${ - exitCodeInfo(code) ? ' (' + exitCodeInfo(code) + ')' : '' - }` - if (signal != null) { - message += `\n signal: ${signal}` - } - } - - return message - } + static getExitMessage = formatExitMessage - static getErrorMessage(err: NodeJS.ErrnoException, from: string): string { - return ( - `${err.message}\n` + - ` errno: ${err.errno} (${errnoMessage(err.errno)})\n` + - ` code: ${err.code}\n` + - ` at ${from}` - ) - } + static getErrorMessage = formatErrorMessage; [inspect.custom](): string { let stringify = (s: string, c: ChalkInstance) => @@ -757,8 +735,8 @@ export class ProcessOutput extends Error { stderr: ${stringify(this.stderr, chalk.red)}, signal: ${inspect(this.signal)}, exitCode: ${(this.exitCode === 0 ? chalk.green : chalk.red)(this.exitCode)}${ - exitCodeInfo(this.exitCode) - ? chalk.grey(' (' + exitCodeInfo(this.exitCode) + ')') + getExitCodeInfo(this.exitCode) + ? chalk.grey(' (' + getExitCodeInfo(this.exitCode) + ')') : '' }, duration: ${this.duration} diff --git a/src/error.ts b/src/error.ts new file mode 100644 index 0000000000..d8984a982e --- /dev/null +++ b/src/error.ts @@ -0,0 +1,226 @@ +// 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 EXIT_CODES = { + 2: 'Misuse of shell builtins', + 126: 'Invoked command cannot execute', + 127: 'Command not found', + 128: 'Invalid exit argument', + 129: 'Hangup', + 130: 'Interrupt', + 131: 'Quit and dump core', + 132: 'Illegal instruction', + 133: 'Trace/breakpoint trap', + 134: 'Process aborted', + 135: 'Bus error: "access to undefined portion of memory object"', + 136: 'Floating point exception: "erroneous arithmetic operation"', + 137: 'Kill (terminate immediately)', + 138: 'User-defined 1', + 139: 'Segmentation violation', + 140: 'User-defined 2', + 141: 'Write to pipe with no one reading', + 142: 'Signal raised by alarm', + 143: 'Termination (request to terminate)', + 145: 'Child process terminated, stopped (or continued*)', + 146: 'Continue if stopped', + 147: 'Stop executing temporarily', + 148: 'Terminal stop signal', + 149: 'Background process attempting to read from tty ("in")', + 150: 'Background process attempting to write to tty ("out")', + 151: 'Urgent data available on socket', + 152: 'CPU time limit exceeded', + 153: 'File size limit exceeded', + 154: 'Signal raised by timer counting virtual time: "virtual timer expired"', + 155: 'Profiling timer expired', + 157: 'Pollable event', + 159: 'Bad syscall', +} + +const ERRNO_CODES = { + 0: 'Success', + 1: 'Not super-user', + 2: 'No such file or directory', + 3: 'No such process', + 4: 'Interrupted system call', + 5: 'I/O error', + 6: 'No such device or address', + 7: 'Arg list too long', + 8: 'Exec format error', + 9: 'Bad file number', + 10: 'No children', + 11: 'No more processes', + 12: 'Not enough core', + 13: 'Permission denied', + 14: 'Bad address', + 15: 'Block device required', + 16: 'Mount device busy', + 17: 'File exists', + 18: 'Cross-device link', + 19: 'No such device', + 20: 'Not a directory', + 21: 'Is a directory', + 22: 'Invalid argument', + 23: 'Too many open files in system', + 24: 'Too many open files', + 25: 'Not a typewriter', + 26: 'Text file busy', + 27: 'File too large', + 28: 'No space left on device', + 29: 'Illegal seek', + 30: 'Read only file system', + 31: 'Too many links', + 32: 'Broken pipe', + 33: 'Math arg out of domain of func', + 34: 'Math result not representable', + 35: 'File locking deadlock error', + 36: 'File or path name too long', + 37: 'No record locks available', + 38: 'Function not implemented', + 39: 'Directory not empty', + 40: 'Too many symbolic links', + 42: 'No message of desired type', + 43: 'Identifier removed', + 44: 'Channel number out of range', + 45: 'Level 2 not synchronized', + 46: 'Level 3 halted', + 47: 'Level 3 reset', + 48: 'Link number out of range', + 49: 'Protocol driver not attached', + 50: 'No CSI structure available', + 51: 'Level 2 halted', + 52: 'Invalid exchange', + 53: 'Invalid request descriptor', + 54: 'Exchange full', + 55: 'No anode', + 56: 'Invalid request code', + 57: 'Invalid slot', + 59: 'Bad font file fmt', + 60: 'Device not a stream', + 61: 'No data (for no delay io)', + 62: 'Timer expired', + 63: 'Out of streams resources', + 64: 'Machine is not on the network', + 65: 'Package not installed', + 66: 'The object is remote', + 67: 'The link has been severed', + 68: 'Advertise error', + 69: 'Srmount error', + 70: 'Communication error on send', + 71: 'Protocol error', + 72: 'Multihop attempted', + 73: 'Cross mount point (not really error)', + 74: 'Trying to read unreadable message', + 75: 'Value too large for defined data type', + 76: 'Given log. name not unique', + 77: 'f.d. invalid for this operation', + 78: 'Remote address changed', + 79: 'Can access a needed shared lib', + 80: 'Accessing a corrupted shared lib', + 81: '.lib section in a.out corrupted', + 82: 'Attempting to link in too many libs', + 83: 'Attempting to exec a shared library', + 84: 'Illegal byte sequence', + 86: 'Streams pipe error', + 87: 'Too many users', + 88: 'Socket operation on non-socket', + 89: 'Destination address required', + 90: 'Message too long', + 91: 'Protocol wrong type for socket', + 92: 'Protocol not available', + 93: 'Unknown protocol', + 94: 'Socket type not supported', + 95: 'Not supported', + 96: 'Protocol family not supported', + 97: 'Address family not supported by protocol family', + 98: 'Address already in use', + 99: 'Address not available', + 100: 'Network interface is not configured', + 101: 'Network is unreachable', + 102: 'Connection reset by network', + 103: 'Connection aborted', + 104: 'Connection reset by peer', + 105: 'No buffer space available', + 106: 'Socket is already connected', + 107: 'Socket is not connected', + 108: "Can't send after socket shutdown", + 109: 'Too many references', + 110: 'Connection timed out', + 111: 'Connection refused', + 112: 'Host is down', + 113: 'Host is unreachable', + 114: 'Socket already connected', + 115: 'Connection already in progress', + 116: 'Stale file handle', + 122: 'Quota exceeded', + 123: 'No medium (in tape drive)', + 125: 'Operation canceled', + 130: 'Previous owner died', + 131: 'State not recoverable', +} + +export function getErrnoMessage(errno?: number): string { + return ( + ERRNO_CODES[-(errno as number) as keyof typeof ERRNO_CODES] || + 'Unknown error' + ) +} + +export function getExitCodeInfo(exitCode: number | null): string | undefined { + return EXIT_CODES[exitCode as keyof typeof EXIT_CODES] +} + +export const formatExitMessage = ( + code: number | null, + signal: NodeJS.Signals | null, + stderr: string, + from: string +) => { + let message = `exit code: ${code}` + if (code != 0 || signal != null) { + message = `${stderr || '\n'} at ${from}` + message += `\n exit code: ${code}${ + getExitCodeInfo(code) ? ' (' + getExitCodeInfo(code) + ')' : '' + }` + if (signal != null) { + message += `\n signal: ${signal}` + } + } + + return message +} + +export const formatErrorMessage = ( + err: NodeJS.ErrnoException, + from: string +) => { + return ( + `${err.message}\n` + + ` errno: ${err.errno} (${getErrnoMessage(err.errno)})\n` + + ` code: ${err.code}\n` + + ` at ${from}` + ) +} + +export function getCallerLocation(err = new Error('zx error')) { + return getCallerLocationFromString(err.stack) +} + +export function getCallerLocationFromString(stackString = 'unknown') { + return ( + stackString + .split(/^\s*(at\s)?/m) + .filter((s) => s?.includes(':'))[2] + ?.trim() || stackString + ) +} diff --git a/src/util.ts b/src/util.ts index cc26c88e67..aed7cb36c2 100644 --- a/src/util.ts +++ b/src/util.ts @@ -121,174 +121,6 @@ export function quotePowerShell(arg: string): string { return `'` + arg.replace(/'/g, "''") + `'` } -const EXIT_CODES = { - 2: 'Misuse of shell builtins', - 126: 'Invoked command cannot execute', - 127: 'Command not found', - 128: 'Invalid exit argument', - 129: 'Hangup', - 130: 'Interrupt', - 131: 'Quit and dump core', - 132: 'Illegal instruction', - 133: 'Trace/breakpoint trap', - 134: 'Process aborted', - 135: 'Bus error: "access to undefined portion of memory object"', - 136: 'Floating point exception: "erroneous arithmetic operation"', - 137: 'Kill (terminate immediately)', - 138: 'User-defined 1', - 139: 'Segmentation violation', - 140: 'User-defined 2', - 141: 'Write to pipe with no one reading', - 142: 'Signal raised by alarm', - 143: 'Termination (request to terminate)', - 145: 'Child process terminated, stopped (or continued*)', - 146: 'Continue if stopped', - 147: 'Stop executing temporarily', - 148: 'Terminal stop signal', - 149: 'Background process attempting to read from tty ("in")', - 150: 'Background process attempting to write to tty ("out")', - 151: 'Urgent data available on socket', - 152: 'CPU time limit exceeded', - 153: 'File size limit exceeded', - 154: 'Signal raised by timer counting virtual time: "virtual timer expired"', - 155: 'Profiling timer expired', - 157: 'Pollable event', - 159: 'Bad syscall', -} - -export function exitCodeInfo(exitCode: number | null): string | undefined { - return EXIT_CODES[exitCode as keyof typeof EXIT_CODES] -} - -const ERRNO_CODES = { - 0: 'Success', - 1: 'Not super-user', - 2: 'No such file or directory', - 3: 'No such process', - 4: 'Interrupted system call', - 5: 'I/O error', - 6: 'No such device or address', - 7: 'Arg list too long', - 8: 'Exec format error', - 9: 'Bad file number', - 10: 'No children', - 11: 'No more processes', - 12: 'Not enough core', - 13: 'Permission denied', - 14: 'Bad address', - 15: 'Block device required', - 16: 'Mount device busy', - 17: 'File exists', - 18: 'Cross-device link', - 19: 'No such device', - 20: 'Not a directory', - 21: 'Is a directory', - 22: 'Invalid argument', - 23: 'Too many open files in system', - 24: 'Too many open files', - 25: 'Not a typewriter', - 26: 'Text file busy', - 27: 'File too large', - 28: 'No space left on device', - 29: 'Illegal seek', - 30: 'Read only file system', - 31: 'Too many links', - 32: 'Broken pipe', - 33: 'Math arg out of domain of func', - 34: 'Math result not representable', - 35: 'File locking deadlock error', - 36: 'File or path name too long', - 37: 'No record locks available', - 38: 'Function not implemented', - 39: 'Directory not empty', - 40: 'Too many symbolic links', - 42: 'No message of desired type', - 43: 'Identifier removed', - 44: 'Channel number out of range', - 45: 'Level 2 not synchronized', - 46: 'Level 3 halted', - 47: 'Level 3 reset', - 48: 'Link number out of range', - 49: 'Protocol driver not attached', - 50: 'No CSI structure available', - 51: 'Level 2 halted', - 52: 'Invalid exchange', - 53: 'Invalid request descriptor', - 54: 'Exchange full', - 55: 'No anode', - 56: 'Invalid request code', - 57: 'Invalid slot', - 59: 'Bad font file fmt', - 60: 'Device not a stream', - 61: 'No data (for no delay io)', - 62: 'Timer expired', - 63: 'Out of streams resources', - 64: 'Machine is not on the network', - 65: 'Package not installed', - 66: 'The object is remote', - 67: 'The link has been severed', - 68: 'Advertise error', - 69: 'Srmount error', - 70: 'Communication error on send', - 71: 'Protocol error', - 72: 'Multihop attempted', - 73: 'Cross mount point (not really error)', - 74: 'Trying to read unreadable message', - 75: 'Value too large for defined data type', - 76: 'Given log. name not unique', - 77: 'f.d. invalid for this operation', - 78: 'Remote address changed', - 79: 'Can access a needed shared lib', - 80: 'Accessing a corrupted shared lib', - 81: '.lib section in a.out corrupted', - 82: 'Attempting to link in too many libs', - 83: 'Attempting to exec a shared library', - 84: 'Illegal byte sequence', - 86: 'Streams pipe error', - 87: 'Too many users', - 88: 'Socket operation on non-socket', - 89: 'Destination address required', - 90: 'Message too long', - 91: 'Protocol wrong type for socket', - 92: 'Protocol not available', - 93: 'Unknown protocol', - 94: 'Socket type not supported', - 95: 'Not supported', - 96: 'Protocol family not supported', - 97: 'Address family not supported by protocol family', - 98: 'Address already in use', - 99: 'Address not available', - 100: 'Network interface is not configured', - 101: 'Network is unreachable', - 102: 'Connection reset by network', - 103: 'Connection aborted', - 104: 'Connection reset by peer', - 105: 'No buffer space available', - 106: 'Socket is already connected', - 107: 'Socket is not connected', - 108: "Can't send after socket shutdown", - 109: 'Too many references', - 110: 'Connection timed out', - 111: 'Connection refused', - 112: 'Host is down', - 113: 'Host is unreachable', - 114: 'Socket already connected', - 115: 'Connection already in progress', - 116: 'Stale file handle', - 122: 'Quota exceeded', - 123: 'No medium (in tape drive)', - 125: 'Operation canceled', - 130: 'Previous owner died', - 131: 'State not recoverable', -} - -export function errnoMessage(errno?: number): string { - return ( - ERRNO_CODES[-(errno as number) as keyof typeof ERRNO_CODES] || - 'Unknown error' - ) -} - export type Duration = number | `${number}m` | `${number}s` | `${number}ms` export function parseDuration(d: Duration) { @@ -427,19 +259,6 @@ const RESERVED_WORDS = new Set([ 'in', ]) -export function getCallerLocation(err = new Error()) { - return getCallerLocationFromString(err.stack) -} - -export function getCallerLocationFromString(stackString = 'unknown') { - return ( - stackString - .split(/^\s*(at\s)?/m) - .filter((s) => s?.includes(':'))[2] - ?.trim() || stackString - ) -} - export const once = any>(fn: T) => { let called = false let result: ReturnType diff --git a/test/all.test.js b/test/all.test.js index bdfc794fa1..005e2d39a1 100644 --- a/test/all.test.js +++ b/test/all.test.js @@ -15,6 +15,7 @@ import './cli.test.js' import './core.test.js' import './deps.test.js' +import './error.test.ts' import './global.test.js' import './goods.test.js' import './index.test.js' diff --git a/test/core.test.js b/test/core.test.js index 34dbea860f..f690654db3 100644 --- a/test/core.test.js +++ b/test/core.test.js @@ -997,6 +997,27 @@ describe('core', () => { assert.throws(() => o.blob(), /Blob is not supported/) globalThis.Blob = Blob }) + + describe('static', () => { + test('getExitMessage()', () => { + assert.match( + ProcessOutput.getExitMessage(2, null, '', ''), + /Misuse of shell builtins/ + ) + }) + + test('getErrorMessage()', () => { + assert.match( + ProcessOutput.getErrorMessage({ errno: -2 }, ''), + /No such file or directory/ + ) + assert.match( + ProcessOutput.getErrorMessage({ errno: -1e9 }, ''), + /Unknown error/ + ) + assert.match(ProcessOutput.getErrorMessage({}, ''), /Unknown error/) + }) + }) }) describe('cd()', () => { diff --git a/test/error.test.ts b/test/error.test.ts new file mode 100644 index 0000000000..6e973f6bd1 --- /dev/null +++ b/test/error.test.ts @@ -0,0 +1,80 @@ +// 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. + +import assert from 'node:assert' +import { test, describe } from 'node:test' +import { + getErrnoMessage, + getExitCodeInfo, + getCallerLocationFromString, +} from '../src/error.ts' + +describe('error', () => { + test('getExitCodeInfo()', () => { + assert.equal(getExitCodeInfo(2), 'Misuse of shell builtins') + }) + + test('getErrnoMessage()', () => { + assert.equal(getErrnoMessage(-2), 'No such file or directory') + assert.equal(getErrnoMessage(1e9), 'Unknown error') + assert.equal(getErrnoMessage(undefined), 'Unknown error') + }) + + test('getCallerLocation: empty', () => { + assert.equal(getCallerLocationFromString(), 'unknown') + }) + + test('getCallerLocation: no-match', () => { + assert.equal(getCallerLocationFromString('stack\nstring'), 'stack\nstring') + }) + + test(`getCallerLocationFromString-v8`, () => { + const stack = ` + Error + at getCallerLocation (/Users/user/test.js:22:17) + at e (/Users/user/test.js:34:13) + at d (/Users/user/test.js:11:5) + at c (/Users/user/test.js:8:5) + at b (/Users/user/test.js:5:5) + at a (/Users/user/test.js:2:5) + at Object. (/Users/user/test.js:37:1) + at Module._compile (node:internal/modules/cjs/loader:1254:14) + at Module._extensions..js (node:internal/modules/cjs/loader:1308:10) + at Module.load (node:internal/modules/cjs/loader:1117:32) + at Module._load (node:internal/modules/cjs/loader:958:12) + ` + assert.match(getCallerLocationFromString(stack), /^.*:11:5.*$/) + }) + + test(`getCallerLocationFromString-JSC`, () => { + const stack = ` + getCallerLocation@/Users/user/test.js:22:17 + e@/Users/user/test.js:34:13 + d@/Users/user/test.js:11:5 + c@/Users/user/test.js:8:5 + b@/Users/user/test.js:5:5 + a@/Users/user/test.js:2:5 + module code@/Users/user/test.js:37:1 + evaluate@[native code] + moduleEvaluation@[native code] + moduleEvaluation@[native code] + @[native code] + asyncFunctionResume@[native code] + promiseReactionJobWithoutPromise@[native code] + promiseReactionJob@[native code] + d@/Users/user/test.js:11:5 + ` + assert.match(getCallerLocationFromString(stack), /^.*:11:5.*$/) + }) +}) diff --git a/test/util.test.js b/test/util.test.js index 893e8cdf9b..234ed6c5c6 100644 --- a/test/util.test.js +++ b/test/util.test.js @@ -16,8 +16,6 @@ import assert from 'node:assert' import fs from 'node:fs' import { test, describe } from 'node:test' import { - exitCodeInfo, - errnoMessage, formatCmd, isString, isStringLiteral, @@ -27,7 +25,6 @@ import { quotePowerShell, randomId, // normalizeMultilinePieces, - getCallerLocationFromString, tempdir, tempfile, preferLocalBin, @@ -36,16 +33,6 @@ import { } from '../build/util.js' describe('util', () => { - test('exitCodeInfo()', () => { - assert.equal(exitCodeInfo(2), 'Misuse of shell builtins') - }) - - test('errnoMessage()', () => { - assert.equal(errnoMessage(-2), 'No such file or directory') - assert.equal(errnoMessage(1e9), 'Unknown error') - assert.equal(errnoMessage(undefined), 'Unknown error') - }) - test('randomId()', () => { assert.ok(/^[a-z0-9]+$/.test(randomId())) assert.ok( @@ -120,90 +107,43 @@ describe('util', () => { // ' a ,b c d, e' // ) // }) -}) - -test('getCallerLocation: empty', () => { - assert.equal(getCallerLocationFromString(), 'unknown') -}) - -test('getCallerLocation: no-match', () => { - assert.equal(getCallerLocationFromString('stack\nstring'), 'stack\nstring') -}) - -test(`getCallerLocationFromString-v8`, () => { - const stack = ` - Error - at getCallerLocation (/Users/user/test.js:22:17) - at e (/Users/user/test.js:34:13) - at d (/Users/user/test.js:11:5) - at c (/Users/user/test.js:8:5) - at b (/Users/user/test.js:5:5) - at a (/Users/user/test.js:2:5) - at Object. (/Users/user/test.js:37:1) - at Module._compile (node:internal/modules/cjs/loader:1254:14) - at Module._extensions..js (node:internal/modules/cjs/loader:1308:10) - at Module.load (node:internal/modules/cjs/loader:1117:32) - at Module._load (node:internal/modules/cjs/loader:958:12) - ` - assert.match(getCallerLocationFromString(stack), /^.*:11:5.*$/) -}) - -test(`getCallerLocationFromString-JSC`, () => { - const stack = ` - getCallerLocation@/Users/user/test.js:22:17 - e@/Users/user/test.js:34:13 - d@/Users/user/test.js:11:5 - c@/Users/user/test.js:8:5 - b@/Users/user/test.js:5:5 - a@/Users/user/test.js:2:5 - module code@/Users/user/test.js:37:1 - evaluate@[native code] - moduleEvaluation@[native code] - moduleEvaluation@[native code] - @[native code] - asyncFunctionResume@[native code] - promiseReactionJobWithoutPromise@[native code] - promiseReactionJob@[native code] - d@/Users/user/test.js:11:5 - ` - assert.match(getCallerLocationFromString(stack), /^.*:11:5.*$/) -}) -test('tempdir() creates temporary folders', () => { - assert.match(tempdir(), /\/zx-/) - assert.match(tempdir('foo'), /\/foo$/) -}) + test('tempdir() creates temporary folders', () => { + assert.match(tempdir(), /\/zx-/) + assert.match(tempdir('foo'), /\/foo$/) + }) -test('tempfile() creates temporary files', () => { - assert.match(tempfile(), /\/zx-.+/) - assert.match(tempfile('foo.txt'), /\/zx-.+\/foo\.txt$/) + test('tempfile() creates temporary files', () => { + assert.match(tempfile(), /\/zx-.+/) + assert.match(tempfile('foo.txt'), /\/zx-.+\/foo\.txt$/) - const tf = tempfile('bar.txt', 'bar') - assert.match(tf, /\/zx-.+\/bar\.txt$/) - assert.equal(fs.readFileSync(tf, 'utf-8'), 'bar') -}) + const tf = tempfile('bar.txt', 'bar') + assert.match(tf, /\/zx-.+\/bar\.txt$/) + assert.equal(fs.readFileSync(tf, 'utf-8'), 'bar') + }) -test('preferLocalBin()', () => { - const env = { - PATH: '/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/local/sbin', - } - const _env = preferLocalBin(env, process.cwd()) - assert.equal( - _env.PATH, - `${process.cwd()}/node_modules/.bin:${process.cwd()}:${env.PATH}` - ) -}) + test('preferLocalBin()', () => { + const env = { + PATH: '/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/local/sbin', + } + const _env = preferLocalBin(env, process.cwd()) + assert.equal( + _env.PATH, + `${process.cwd()}/node_modules/.bin:${process.cwd()}:${env.PATH}` + ) + }) -test('camelToSnake()', () => { - assert.equal(camelToSnake('verbose'), 'VERBOSE') - assert.equal(camelToSnake('nothrow'), 'NOTHROW') - assert.equal(camelToSnake('preferLocal'), 'PREFER_LOCAL') - assert.equal(camelToSnake('someMoreBigStr'), 'SOME_MORE_BIG_STR') -}) + test('camelToSnake()', () => { + assert.equal(camelToSnake('verbose'), 'VERBOSE') + assert.equal(camelToSnake('nothrow'), 'NOTHROW') + assert.equal(camelToSnake('preferLocal'), 'PREFER_LOCAL') + assert.equal(camelToSnake('someMoreBigStr'), 'SOME_MORE_BIG_STR') + }) -test('snakeToCamel()', () => { - assert.equal(snakeToCamel('VERBOSE'), 'verbose') - assert.equal(snakeToCamel('NOTHROW'), 'nothrow') - assert.equal(snakeToCamel('PREFER_LOCAL'), 'preferLocal') - assert.equal(snakeToCamel('SOME_MORE_BIG_STR'), 'someMoreBigStr') + test('snakeToCamel()', () => { + assert.equal(snakeToCamel('VERBOSE'), 'verbose') + assert.equal(snakeToCamel('NOTHROW'), 'nothrow') + assert.equal(snakeToCamel('PREFER_LOCAL'), 'preferLocal') + assert.equal(snakeToCamel('SOME_MORE_BIG_STR'), 'someMoreBigStr') + }) }) diff --git a/tsconfig.json b/tsconfig.json index 9de8e07b0c..cfbc293de5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,10 @@ "outDir": "./build", "declaration": true, "emitDeclarationOnly": true, - "types": [] + "types": [], + "allowImportingTsExtensions": true, + "rewriteRelativeImportExtensions": true, + "verbatimModuleSyntax": true }, "include": ["./src/**/*"], "exclude": ["./src/globals.ts"]