diff --git a/test/node.js/.gitignore b/test/node.js/.gitignore deleted file mode 100644 index edad843264..0000000000 --- a/test/node.js/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -# Paths copied from Node.js repository -upstream/ - -# Paths for test runner -summary/ -summary.md diff --git a/test/node.js/.prettierignore b/test/node.js/.prettierignore deleted file mode 100644 index 42b5527ca1..0000000000 --- a/test/node.js/.prettierignore +++ /dev/null @@ -1 +0,0 @@ -upstream/ diff --git a/test/node.js/bunfig.toml b/test/node.js/bunfig.toml deleted file mode 100644 index e630e9b8b5..0000000000 --- a/test/node.js/bunfig.toml +++ /dev/null @@ -1,2 +0,0 @@ -[test] -preload = ["./common/preload.js"] diff --git a/test/node.js/common/assert.js b/test/node.js/common/assert.js deleted file mode 100644 index e38fe9c7c6..0000000000 --- a/test/node.js/common/assert.js +++ /dev/null @@ -1,273 +0,0 @@ -import { expect } from "bun:test"; - -function deepEqual(actual, expected, message) { - if (isIgnored(expected, message)) { - return; - } - try { - expect(actual).toEqual(expected); - } catch (cause) { - throwError(cause, message); - } -} - -function deepStrictEqual(actual, expected, message) { - if (isIgnored(expected, message)) { - return; - } - try { - expect(actual).toStrictEqual(expected); - } catch (cause) { - throwError(cause, message); - } -} - -function doesNotMatch(string, regexp, message) { - if (isIgnored(regexp, message)) { - return; - } - try { - expect(string).not.toMatch(regexp); - } catch (cause) { - throwError(cause, message); - } -} - -function doesNotReject(asyncFn, error, message) { - if (isIgnored(error, message)) { - return; - } - try { - expect(asyncFn).rejects.toThrow(error); - } catch (cause) { - throwError(cause, message); - } -} - -function doesNotThrow(fn, error, message) { - if (isIgnored(error, message)) { - return; - } - todo("doesNotThrow"); -} - -function equal(actual, expected, message) { - if (isIgnored(expected, message)) { - return; - } - try { - expect(actual).toBe(expected); - } catch (cause) { - throwError(cause, message); - } -} - -function fail(actual, expected, message, operator, stackStartFn) { - if (isIgnored(expected, message)) { - return; - } - todo("fail"); -} - -function ifError(value) { - if (isIgnored(value)) { - return; - } - todo("ifError"); -} - -function match(string, regexp, message) { - if (isIgnored(regexp, message)) { - return; - } - try { - expect(string).toMatch(regexp); - } catch (cause) { - throwError(cause, message); - } -} - -function notDeepEqual(actual, expected, message) { - if (isIgnored(expected, message)) { - return; - } - todo("notDeepEqual"); -} - -function notDeepStrictEqual(actual, expected, message) { - if (isIgnored(expected, message)) { - return; - } - todo("notDeepStrictEqual"); -} - -function notEqual(actual, expected, message) { - if (isIgnored(expected, message)) { - return; - } - try { - expect(actual).not.toBe(expected); - } catch (cause) { - throwError(cause, message); - } -} - -function notStrictEqual(actual, expected, message) { - if (isIgnored(expected, message)) { - return; - } - try { - expect(actual).not.toStrictEqual(expected); - } catch (cause) { - throwError(cause, message); - } -} - -function ok(value, message) { - if (isIgnored(message)) { - return; - } - equal(!!value, true, message); -} - -function rejects(asyncFn, error, message) { - if (isIgnored(error, message)) { - return; - } - todo("rejects"); -} - -function strictEqual(actual, expected, message) { - if (isIgnored(expected, message)) { - return; - } - try { - expect(actual).toBe(expected); - } catch (cause) { - throwError(cause, message); - } -} - -function throws(fn, error, message) { - try { - let result; - try { - result = fn(); - } catch (cause) { - const matcher = toErrorMatcher(error); - expect(cause).toEqual(matcher); - return; - } - expect(result).toBe("Expected function to throw an error, instead it returned"); - } catch (cause) { - throwError(cause, message); - } -} - -function toErrorMatcher(expected) { - let message; - if (typeof expected === "string") { - message = expected; - } else if (expected instanceof RegExp) { - message = expected.source; - } else if (typeof expected === "object") { - message = expected.message; - } - - for (const [expected, actual] of similarErrors) { - if (message && expected.test(message)) { - message = actual; - break; - } - } - - if (!message) { - return expect.anything(); - } - - if (typeof expected === "object") { - return expect.objectContaining({ - ...expected, - message: expect.stringMatching(message), - }); - } - - return expect.stringMatching(message); -} - -const similarErrors = [ - [/Invalid typed array length/i, /length too large/i], - [/Unknown encoding/i, /Invalid encoding/i], - [ - /The ".*" argument must be of type string or an instance of Buffer or ArrayBuffer/i, - /Invalid input, must be a string, Buffer, or ArrayBuffer/i, - ], - [/The ".*" argument must be an instance of Buffer or Uint8Array./i, /Expected Buffer/i], - [/The ".*" argument must be an instance of Array./i, /Argument must be an array/i], - [/The value of ".*" is out of range./i, /Offset is out of bounds/i], - [/Attempt to access memory outside buffer bounds/i, /Out of bounds access/i], -]; - -const ignoredExpectations = [ - // Reason: Bun has a nicer format for `Buffer.inspect()`. - /^ { - if (calls !== n) { - throw new Error(`function should be called exactly ${n} times:\n ${callSite}`); - } - }); - - return mustCallFn; -} - -function mustNotCall() { - const callSite = getCallSite(mustNotCall); - - return function mustNotCall(...args) { - const argsInfo = args.length > 0 ? `\ncalled with arguments: ${args.map(arg => inspect(arg)).join(", ")}` : ""; - assert.fail(`${msg || "function should not have been called"} at ${callSite}` + argsInfo); - }; -} - -function printSkipMessage(message) { - console.warn(message); -} - -function skip(message) { - printSkipMessage(message); - process.exit(0); -} - -function expectsError(validator, exact) { - return mustCall((...args) => { - if (args.length !== 1) { - // Do not use `assert.strictEqual()` to prevent `inspect` from - // always being called. - assert.fail(`Expected one argument, got ${inspect(args)}`); - } - const error = args.pop(); - // The error message should be non-enumerable - assert.strictEqual(Object.prototype.propertyIsEnumerable.call(error, "message"), false); - - assert.throws(() => { - throw error; - }, validator); - return true; - }, exact); -} - -function expectWarning(name, code, message) { - // Do nothing -} - -function invalidArgTypeHelper(input) { - return ` Received: ${inspect(input)}`; -} - -function getCallSite(fn) { - const originalStackFormatter = Error.prepareStackTrace; - Error.prepareStackTrace = (_, stack) => `${stack[0].getFileName()}:${stack[0].getLineNumber()}`; - const error = new Error(); - Error.captureStackTrace(error, fn); - error.stack; // With the V8 Error API, the stack is not formatted until it is accessed - Error.prepareStackTrace = originalStackFormatter; - return error.stack; -} - -export { - hasIntl, - hasCrypto, - hasOpenSSL3, - hasOpenSSL31, - hasQuic, - // ... - isWindows, - isSunOS, - isFreeBSD, - isOpenBSD, - isLinux, - isOSX, - isAsan, - isPi, - // ... - isDumbTerminal, - // ... - mustCall, - mustNotCall, - printSkipMessage, - skip, - expectsError, - expectWarning, - // ... - inspect, - invalidArgTypeHelper, -}; diff --git a/test/node.js/common/preload.js b/test/node.js/common/preload.js deleted file mode 100644 index 8f3b714f19..0000000000 --- a/test/node.js/common/preload.js +++ /dev/null @@ -1,10 +0,0 @@ -const { mock } = require("bun:test"); -const assert = require("./assert"); - -mock.module("assert", () => { - return assert; -}); - -mock.module("internal/test/binding", () => { - return {}; -}); diff --git a/test/node.js/metadata.mjs b/test/node.js/metadata.mjs deleted file mode 100644 index 16a4fcf7de..0000000000 --- a/test/node.js/metadata.mjs +++ /dev/null @@ -1,32 +0,0 @@ -import { spawnSync } from "node:child_process"; - -const isBun = !!process.isBun; -const os = process.platform === "win32" ? "windows" : process.platform; -const arch = process.arch === "arm64" ? "aarch64" : process.arch; -const version = isBun ? Bun.version : process.versions.node; -const revision = isBun ? Bun.revision : undefined; -const baseline = (() => { - if (!isBun || arch !== "x64") { - return undefined; - } - const { stdout } = spawnSync(process.execPath, ["--print", "Bun.unsafe.segfault()"], { - encoding: "utf8", - timeout: 5_000, - }); - if (stdout.includes("baseline")) { - return true; - } - return undefined; -})(); -const name = baseline ? `bun-${os}-${arch}-baseline` : `${isBun ? "bun" : "node"}-${os}-${arch}`; - -console.log( - JSON.stringify({ - name, - os, - arch, - version, - revision, - baseline, - }), -); diff --git a/test/node.js/package.json b/test/node.js/package.json deleted file mode 100644 index 5136aaa87d..0000000000 --- a/test/node.js/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "private": true, - "scripts": { - "test": "node runner.mjs --exec-path $(which bun-debug || which bun)" - } -} diff --git a/test/node.js/runner.mjs b/test/node.js/runner.mjs deleted file mode 100644 index 5507638616..0000000000 --- a/test/node.js/runner.mjs +++ /dev/null @@ -1,437 +0,0 @@ -import { parseArgs } from "node:util"; -import { spawnSync } from "node:child_process"; -import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, writeFileSync, appendFileSync, realpathSync } from "node:fs"; -import { tmpdir } from "node:os"; -import { basename, join } from "node:path"; -import readline from "node:readline/promises"; - -const testPath = new URL("./", import.meta.url); -const nodePath = new URL("upstream/", testPath); -const nodeTestPath = new URL("test/", nodePath); -const metadataScriptPath = new URL("metadata.mjs", testPath); -const testJsonPath = new URL("tests.json", testPath); -const summariesPath = new URL("summary/", testPath); -const summaryMdPath = new URL("summary.md", testPath); -const cwd = new URL("../../", testPath); - -async function main() { - const { values, positionals } = parseArgs({ - allowPositionals: true, - options: { - help: { - type: "boolean", - short: "h", - }, - baseline: { - type: "boolean", - }, - interactive: { - type: "boolean", - short: "i", - }, - "exec-path": { - type: "string", - }, - pull: { - type: "boolean", - }, - summary: { - type: "boolean", - }, - }, - }); - - if (values.help) { - printHelp(); - return; - } - - if (values.summary) { - printSummary(); - return; - } - - if (values.pull) { - pullTests(true); - return; - } - - pullTests(); - const summary = await runTests(values, positionals); - const regressedTests = appendSummary(summary); - printSummary(summary, regressedTests); - - process.exit(regressedTests?.length ? 1 : 0); -} - -function printHelp() { - console.log(`Usage: ${process.argv0} ${basename(import.meta.filename)} [options]`); - console.log(); - console.log("Options:"); - console.log(" -h, --help Show this help message"); - console.log(" -e, --exec-path Path to the bun executable to run"); - console.log(" -i, --interactive Pause and wait for input after a failing test"); - console.log(" -s, --summary Print a summary of the tests (does not run tests)"); -} - -function pullTests(force) { - if (!force && existsSync(nodeTestPath)) { - return; - } - - console.log("Pulling tests..."); - const { status, error, stderr } = spawnSync( - "git", - ["submodule", "update", "--init", "--recursive", "--progress", "--depth=1", "--checkout", "upstream"], - { - cwd: testPath, - stdio: "inherit", - }, - ); - - if (error || status !== 0) { - throw error || new Error(stderr); - } - - for (const { filename, status } of getTests(nodeTestPath)) { - if (status === "TODO") { - continue; - } - - const src = new URL(filename, nodeTestPath); - const dst = new URL(filename, testPath); - - try { - writeFileSync(dst, readFileSync(src)); - } catch (error) { - if (error.code === "ENOENT") { - mkdirSync(new URL(".", dst), { recursive: true }); - writeFileSync(dst, readFileSync(src)); - } else { - throw error; - } - } - } -} - -async function runTests(options, filters) { - const { interactive } = options; - const bunPath = process.isBun ? process.execPath : "bun"; - const execPath = options["exec-path"] || bunPath; - - let reader; - if (interactive) { - reader = readline.createInterface({ - input: process.stdin, - output: process.stdout, - }); - } - - const results = []; - const tests = getTests(testPath); - for (const { label, filename, status: filter } of tests) { - if (filters?.length && !filters.some(filter => label?.includes(filter))) { - continue; - } - - if (filter !== "OK") { - results.push({ label, filename, status: filter }); - continue; - } - - const { pathname: filePath } = new URL(filename, testPath); - const tmp = tmpdirSync(); - const timestamp = Date.now(); - const { - status: exitCode, - signal: signalCode, - error: spawnError, - } = spawnSync(execPath, ["test", filePath], { - cwd: testPath, - stdio: "inherit", - env: { - PATH: process.env.PATH, - HOME: tmp, - TMPDIR: tmp, - TZ: "Etc/UTC", - FORCE_COLOR: "1", - BUN_DEBUG_QUIET_LOGS: "1", - BUN_GARBAGE_COLLECTOR_LEVEL: "1", - BUN_RUNTIME_TRANSPILER_CACHE_PATH: "0", - GITHUB_ACTIONS: "false", // disable for now - }, - timeout: 30_000, - }); - - const duration = Math.ceil(Date.now() - timestamp); - const status = exitCode === 0 ? "PASS" : "FAIL"; - let error; - if (signalCode) { - error = signalCode; - } else if (spawnError) { - const { message } = spawnError; - if (message.includes("timed out") || message.includes("timeout")) { - error = "TIMEOUT"; - } else { - error = message; - } - } else if (exitCode !== 0) { - error = `code ${exitCode}`; - } - results.push({ label, filename, status, error, timestamp, duration }); - - if (reader && status === "FAIL") { - const answer = await reader.question("Continue? [Y/n] "); - if (answer.toUpperCase() !== "Y") { - break; - } - } - } - - reader?.close(); - return { - v: 1, - metadata: getMetadata(execPath), - tests: results, - }; -} - -function getTests(filePath) { - const tests = []; - const testData = JSON.parse(readFileSync(testJsonPath, "utf8")); - - for (const filename of readdirSync(filePath, { recursive: true })) { - if (!isJavaScript(filename) || !isTest(filename)) { - continue; - } - - let match; - for (const { label, pattern, skip: skipList = [], todo: todoList = [] } of testData) { - if (!filename.startsWith(pattern)) { - continue; - } - - if (skipList.some(({ file }) => filename.endsWith(file))) { - tests.push({ label, filename, status: "SKIP" }); - } else if (todoList.some(({ file }) => filename.endsWith(file))) { - tests.push({ label, filename, status: "TODO" }); - } else { - tests.push({ label, filename, status: "OK" }); - } - - match = true; - break; - } - - if (!match) { - tests.push({ filename, status: "TODO" }); - } - } - - return tests; -} - -function appendSummary(summary) { - const { metadata, tests, ...extra } = summary; - const { name } = metadata; - - const summaryPath = new URL(`${name}.json`, summariesPath); - const summaryData = { - metadata, - tests: tests.map(({ label, filename, status, error }) => ({ label, filename, status, error })), - ...extra, - }; - - const regressedTests = []; - if (existsSync(summaryPath)) { - const previousData = JSON.parse(readFileSync(summaryPath, "utf8")); - const { v } = previousData; - if (v === 1) { - const { tests: previousTests } = previousData; - for (const { label, filename, status, error } of tests) { - if (status !== "FAIL") { - continue; - } - const previousTest = previousTests.find(({ filename: file }) => file === filename); - if (previousTest) { - const { status: previousStatus } = previousTest; - if (previousStatus !== "FAIL") { - regressedTests.push({ label, filename, error }); - } - } - } - } - } - - if (regressedTests.length) { - return regressedTests; - } - - const summaryText = JSON.stringify(summaryData, null, 2); - try { - writeFileSync(summaryPath, summaryText); - } catch (error) { - if (error.code === "ENOENT") { - mkdirSync(summariesPath, { recursive: true }); - writeFileSync(summaryPath, summaryText); - } else { - throw error; - } - } -} - -function printSummary(summaryData, regressedTests) { - let metadataInfo = {}; - let testInfo = {}; - let labelInfo = {}; - let errorInfo = {}; - - const summaryList = []; - if (summaryData) { - summaryList.push(summaryData); - } else { - for (const filename of readdirSync(summariesPath)) { - if (!filename.endsWith(".json")) { - continue; - } - - const summaryPath = new URL(filename, summariesPath); - const summaryData = JSON.parse(readFileSync(summaryPath, "utf8")); - summaryList.push(summaryData); - } - } - - for (const summaryData of summaryList) { - const { v, metadata, tests } = summaryData; - if (v !== 1) { - continue; - } - - const { name, version, revision } = metadata; - if (revision) { - metadataInfo[name] = - `${version}-[\`${revision.slice(0, 7)}\`](https://github.com/oven-sh/bun/commit/${revision})`; - } else { - metadataInfo[name] = `${version}`; - } - - for (const test of tests) { - const { label, filename, status, error } = test; - if (label) { - labelInfo[label] ||= { pass: 0, fail: 0, skip: 0, todo: 0, total: 0 }; - labelInfo[label][status.toLowerCase()] += 1; - labelInfo[label].total += 1; - } - testInfo[name] ||= { pass: 0, fail: 0, skip: 0, todo: 0, total: 0 }; - testInfo[name][status.toLowerCase()] += 1; - testInfo[name].total += 1; - if (status === "FAIL") { - errorInfo[filename] ||= {}; - errorInfo[filename][name] = error; - } - } - } - - let summaryMd = `## Node.js tests -`; - - if (!summaryData) { - summaryMd += ` -| Platform | Conformance | Passed | Failed | Skipped | Total | -| - | - | - | - | - | - | -`; - - for (const [name, { pass, fail, skip, total }] of Object.entries(testInfo)) { - testInfo[name].coverage = (((pass + fail + skip) / total) * 100).toFixed(2); - testInfo[name].conformance = ((pass / total) * 100).toFixed(2); - } - - for (const [name, { conformance, pass, fail, skip, total }] of Object.entries(testInfo)) { - summaryMd += `| \`${name}\` ${metadataInfo[name]} | ${conformance} % | ${pass} | ${fail} | ${skip} | ${total} |\n`; - } - } - - summaryMd += ` -| API | Conformance | Passed | Failed | Skipped | Total | -| - | - | - | - | - | - | -`; - - for (const [label, { pass, fail, skip, total }] of Object.entries(labelInfo)) { - labelInfo[label].coverage = (((pass + fail + skip) / total) * 100).toFixed(2); - labelInfo[label].conformance = ((pass / total) * 100).toFixed(2); - } - - for (const [label, { conformance, pass, fail, skip, total }] of Object.entries(labelInfo)) { - summaryMd += `| \`${label}\` | ${conformance} % | ${pass} | ${fail} | ${skip} | ${total} |\n`; - } - - if (!summaryData) { - writeFileSync(summaryMdPath, summaryMd); - } - - const githubSummaryPath = process.env.GITHUB_STEP_SUMMARY; - if (githubSummaryPath) { - appendFileSync(githubSummaryPath, summaryMd); - } - - console.log("=".repeat(process.stdout.columns)); - console.log("Summary by platform:"); - console.table(testInfo); - console.log("Summary by label:"); - console.table(labelInfo); - if (regressedTests?.length) { - const isTty = process.stdout.isTTY; - if (isTty) { - process.stdout.write("\x1b[31m"); - } - const { name } = summaryData.metadata; - console.log(`Regressions found in ${regressedTests.length} tests for ${name}:`); - console.table(regressedTests); - if (isTty) { - process.stdout.write("\x1b[0m"); - } - } -} - -function isJavaScript(filename) { - return /\.(m|c)?js$/.test(filename); -} - -function isTest(filename) { - return /^test-/.test(basename(filename)); -} - -function getMetadata(execPath) { - const { pathname: filePath } = metadataScriptPath; - const { status: exitCode, stdout } = spawnSync(execPath, [filePath], { - cwd, - stdio: ["ignore", "pipe", "ignore"], - env: { - PATH: process.env.PATH, - BUN_DEBUG_QUIET_LOGS: "1", - }, - timeout: 5_000, - }); - - if (exitCode === 0) { - try { - return JSON.parse(stdout); - } catch { - // Ignore - } - } - - return { - os: process.platform, - arch: process.arch, - }; -} - -function tmpdirSync(pattern = "bun.test.") { - return mkdtempSync(join(realpathSync(tmpdir()), pattern)); -} - -main().catch(error => { - console.error(error); - process.exit(1); -}); diff --git a/test/node.js/tests.json b/test/node.js/tests.json deleted file mode 100644 index 8ef5ee4f3e..0000000000 --- a/test/node.js/tests.json +++ /dev/null @@ -1,166 +0,0 @@ -[ - { - "label": "node:buffer", - "pattern": "parallel/test-buffer", - "skip": [ - { - "file": "backing-arraybuffer.js", - "reason": "Internal binding checks if the buffer is on the heap" - } - ], - "todo": [ - { - "file": "constants.js", - "reason": "Hangs" - }, - { - "file": "tostring-rangeerror.js", - "reason": "Hangs" - } - ] - }, - { - "label": "node:path", - "pattern": "parallel/test-path" - }, - { - "label": "node:child_process", - "pattern": "parallel/test-child-process" - }, - { - "label": "node:async_hooks", - "pattern": "parallel/test-async-hooks" - }, - { - "label": "node:crypto", - "pattern": "parallel/test-crypto" - }, - { - "label": "node:dgram", - "pattern": "parallel/test-dgram" - }, - { - "label": "node:diagnostics_channel", - "pattern": "parallel/test-diagnostics-channel" - }, - { - "label": "node:fs", - "pattern": "parallel/test-fs" - }, - { - "label": "node:dns", - "pattern": "parallel/test-dns" - }, - { - "label": "node:domain", - "pattern": "parallel/test-domain" - }, - { - "label": "node:events", - "pattern": "parallel/test-event-emitter" - }, - { - "label": "node:http", - "pattern": "parallel/test-http" - }, - { - "label": "node:http2", - "pattern": "parallel/test-http2" - }, - { - "label": "node:https", - "pattern": "parallel/test-https" - }, - { - "label": "node:net", - "pattern": "parallel/test-net" - }, - { - "label": "node:os", - "pattern": "parallel/test-os" - }, - { - "label": "process", - "pattern": "parallel/test-process" - }, - { - "label": "node:stream", - "pattern": "parallel/test-stream" - }, - { - "label": "node:stream", - "pattern": "parallel/test-readable" - }, - { - "label": "node:timers", - "pattern": "parallel/test-timers" - }, - { - "label": "node:timers", - "pattern": "parallel/test-next-tick" - }, - { - "label": "node:tls", - "pattern": "parallel/test-tls" - }, - { - "label": "node:tty", - "pattern": "parallel/test-tty" - }, - { - "label": "node:url", - "pattern": "parallel/test-url" - }, - { - "label": "node:util", - "pattern": "parallel/test-util" - }, - { - "label": "node:trace_events", - "pattern": "parallel/test-trace-events" - }, - { - "label": "node:vm", - "pattern": "parallel/test-vm" - }, - { - "label": "node:zlib", - "pattern": "parallel/test-zlib" - }, - { - "label": "node:worker_threads", - "pattern": "parallel/test-worker" - }, - { - "label": "node:readline", - "pattern": "parallel/test-readline" - }, - { - "label": "web:crypto", - "pattern": "parallel/test-webcrypto" - }, - { - "label": "web:streams", - "pattern": "parallel/test-webstream" - }, - { - "label": "web:streams", - "pattern": "parallel/test-whatwg-webstreams" - }, - { - "label": "web:encoding", - "pattern": "parallel/test-whatwg-encoding" - }, - { - "label": "web:url", - "pattern": "parallel/test-whatwg-url" - }, - { - "label": "web:websocket", - "pattern": "parallel/test-websocket" - }, - { - "label": "web:performance", - "pattern": "parallel/test-performance" - } -] diff --git a/test/node.js/tsconfig.json b/test/node.js/tsconfig.json deleted file mode 100644 index b2ad667c9f..0000000000 --- a/test/node.js/tsconfig.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "include": [".", "../../packages/bun-types/index.d.ts"], - "compilerOptions": { - "lib": ["ESNext"], - "module": "ESNext", - "target": "ESNext", - "moduleResolution": "bundler", - "moduleDetection": "force", - "allowImportingTsExtensions": true, - "experimentalDecorators": true, - "noEmit": true, - "composite": true, - "strict": true, - "downlevelIteration": true, - "skipLibCheck": true, - "jsx": "preserve", - "allowSyntheticDefaultImports": true, - "forceConsistentCasingInFileNames": true, - "allowJs": true, - "resolveJsonModule": true, - "noImplicitThis": false, - "paths": { - "assert": ["./common/assert.js"] - } - }, - "exclude": [] -}