diff --git a/workspaces/adventure-pack/jest.config.js b/workspaces/adventure-pack/jest.config.js deleted file mode 100644 index 56068c3d..00000000 --- a/workspaces/adventure-pack/jest.config.js +++ /dev/null @@ -1,6 +0,0 @@ -const config = { - preset: "ts-jest", - testEnvironment: "node", -}; - -export default config; diff --git a/workspaces/adventure-pack/jest.config.ts b/workspaces/adventure-pack/jest.config.ts new file mode 100644 index 00000000..5507e3b3 --- /dev/null +++ b/workspaces/adventure-pack/jest.config.ts @@ -0,0 +1,44 @@ +import type { Config } from "jest"; + +const config: Config = { + preset: "ts-jest", + testEnvironment: "node", +}; + +export default config; + +// Hack to make this config runnable as a script, since the combination of +// Jest, ESM, and a TypeScript config has been painful to get working otherwise. +import("node:process").then(async ({ default: process }) => { + if (import.meta.filename !== process.argv[1]) { + return; + } + + try { + const { spawnWithSafeStdio } = await import( + "@code-chronicles/util/spawnWithSafeStdio" + ); + await spawnWithSafeStdio( + "jest", + ["--color", "-c", JSON.stringify(config), ...process.argv.slice(2)], + { + stdio: "inherit", + env: { + ...process.env, + NODE_OPTIONS: [ + "--experimental-vm-modules", + process.env.NODE_OPTIONS?.trim(), + ] + .filter(Boolean) + .join(" "), + }, + }, + ); + } catch (err) { + console.error( + (err as Record | null | undefined)?.message ?? err, + ); + // eslint-disable-next-line require-atomic-updates -- Updating `process.exitCode` on error is logical. + process.exitCode = 1; + } +}); diff --git a/workspaces/adventure-pack/package.json b/workspaces/adventure-pack/package.json index 717fea67..dfc48044 100644 --- a/workspaces/adventure-pack/package.json +++ b/workspaces/adventure-pack/package.json @@ -47,10 +47,10 @@ "goodies:python3:test": "bash goodies/python3/test.sh", "goodies:typescript:format": "prettier --color --write goodies/typescript", "goodies:typescript:install": "yarn", - "goodies:typescript:test": "jest --color \"/goodies/typescript/\"", + "goodies:typescript:test": "tsx ./jest.config.ts \"/goodies/typescript/\"", "build-app": "tsx src/scripts/build/main.ts", "build-chrome-extension": "tsx src/scripts/build/buildChromeExtension.ts", - "package-goodies:test": "cross-env NODE_OPTIONS=\"--experimental-vm-modules\" jest --color --testPathIgnorePatterns=\"/goodies/\"", + "package-goodies:test": "tsx ./jest.config.ts --testPathIgnorePatterns=\"/goodies/\"", "format": "yarn goodies:java:format && yarn goodies:kotlin:format && yarn goodies:python3:format && yarn goodies:typescript:format && prettier --color --write .", "lint": "eslint --color --max-warnings=0 .", "postinstall": "yarn goodies:java:install && yarn goodies:kotlin:install && yarn goodies:python3:install", diff --git a/workspaces/util/jest.config.mts b/workspaces/util/jest.config.mts new file mode 100644 index 00000000..5507e3b3 --- /dev/null +++ b/workspaces/util/jest.config.mts @@ -0,0 +1,44 @@ +import type { Config } from "jest"; + +const config: Config = { + preset: "ts-jest", + testEnvironment: "node", +}; + +export default config; + +// Hack to make this config runnable as a script, since the combination of +// Jest, ESM, and a TypeScript config has been painful to get working otherwise. +import("node:process").then(async ({ default: process }) => { + if (import.meta.filename !== process.argv[1]) { + return; + } + + try { + const { spawnWithSafeStdio } = await import( + "@code-chronicles/util/spawnWithSafeStdio" + ); + await spawnWithSafeStdio( + "jest", + ["--color", "-c", JSON.stringify(config), ...process.argv.slice(2)], + { + stdio: "inherit", + env: { + ...process.env, + NODE_OPTIONS: [ + "--experimental-vm-modules", + process.env.NODE_OPTIONS?.trim(), + ] + .filter(Boolean) + .join(" "), + }, + }, + ); + } catch (err) { + console.error( + (err as Record | null | undefined)?.message ?? err, + ); + // eslint-disable-next-line require-atomic-updates -- Updating `process.exitCode` on error is logical. + process.exitCode = 1; + } +}); diff --git a/workspaces/util/jest.config.ts b/workspaces/util/jest.config.ts deleted file mode 100644 index c5e46bb4..00000000 --- a/workspaces/util/jest.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { Config } from "jest"; - -const config: Config = { - preset: "ts-jest", - testEnvironment: "node", -}; - -export default config; diff --git a/workspaces/util/package.json b/workspaces/util/package.json index 21c5f981..015c6686 100644 --- a/workspaces/util/package.json +++ b/workspaces/util/package.json @@ -19,7 +19,7 @@ "scripts": { "format": "prettier --color --write .", "lint": "eslint --color --max-warnings=0 .", - "test": "jest --color .", + "test": "tsx ./jest.config.mts", "typecheck": "tsc --pretty --project ." }, "dependencies": { @@ -35,7 +35,7 @@ "jest": "29.7.0", "prettier": "3.3.3", "ts-jest": "29.2.5", - "ts-node": "10.9.2", + "tsx": "4.19.1", "type-fest": "4.26.1", "typescript": "5.6.2" } diff --git a/workspaces/util/src/spawnWithSafeStdio.ts b/workspaces/util/src/spawnWithSafeStdio.ts index ee4352b2..804f102e 100644 --- a/workspaces/util/src/spawnWithSafeStdio.ts +++ b/workspaces/util/src/spawnWithSafeStdio.ts @@ -1,27 +1,29 @@ import process from "node:process"; import { spawn, type SpawnOptions } from "node:child_process"; +// TODO: audit callsites since the behavior is changing + export function spawnWithSafeStdio( command: string, args: readonly string[], - options?: Omit, + options?: SpawnOptions, ): Promise { return new Promise((resolve, reject) => { const childProcess = spawn(command, args, { - ...options, + // When using "inherit" mode, it sometimes broke the interaction with + // the actions/github-script@v7 GitHub Action, so we default to piping. + stdio: ["ignore", "pipe", "pipe"], // Without a shell specified, many comands seem to fail to spawn on // Windows. I verified that it's not a PATH issue. It seems like it's // probably https://github.com/nodejs/node-v0.x-archive/issues/5841 ...(process.platform === "win32" && { shell: options?.shell ?? "bash" }), - // When using "inherit" mode, it sometimes broke the interaction with - // the actions/github-script@v7 GitHub Action... - stdio: ["ignore", "pipe", "pipe"], + ...options, }); - childProcess.stdout.pipe(process.stdout); - childProcess.stderr.pipe(process.stderr); + childProcess.stdout?.pipe(process.stdout); + childProcess.stderr?.pipe(process.stderr); childProcess.on("error", reject); childProcess.on("exit", (exitCode) => { diff --git a/yarn.lock b/yarn.lock index 9b5fa03b..c22b073f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -689,22 +689,13 @@ __metadata: nullthrows: "patch:nullthrows@npm%3A1.1.1#~/.yarn/patches/nullthrows-npm-1.1.1-3d1f817134.patch" prettier: "npm:3.3.3" ts-jest: "npm:29.2.5" - ts-node: "npm:10.9.2" + tsx: "npm:4.19.1" type-fest: "npm:4.26.1" typescript: "npm:5.6.2" zod: "npm:3.23.8" languageName: unknown linkType: soft -"@cspotcode/source-map-support@npm:^0.8.0": - version: 0.8.1 - resolution: "@cspotcode/source-map-support@npm:0.8.1" - dependencies: - "@jridgewell/trace-mapping": "npm:0.3.9" - checksum: 10c0/05c5368c13b662ee4c122c7bfbe5dc0b613416672a829f3e78bc49a357a197e0218d6e74e7c66cfcd04e15a179acab080bd3c69658c9fbefd0e1ccd950a07fc6 - languageName: node - linkType: hard - "@discordjs/builders@npm:^1.8.2": version: 1.9.0 resolution: "@discordjs/builders@npm:1.9.0" @@ -1326,7 +1317,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/resolve-uri@npm:^3.0.3, @jridgewell/resolve-uri@npm:^3.1.0": +"@jridgewell/resolve-uri@npm:^3.1.0": version: 3.1.2 resolution: "@jridgewell/resolve-uri@npm:3.1.2" checksum: 10c0/d502e6fb516b35032331406d4e962c21fe77cdf1cbdb49c6142bcbd9e30507094b18972778a6e27cbad756209cfe34b1a27729e6fa08a2eb92b33943f680cf1e @@ -1357,16 +1348,6 @@ __metadata: languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:0.3.9": - version: 0.3.9 - resolution: "@jridgewell/trace-mapping@npm:0.3.9" - dependencies: - "@jridgewell/resolve-uri": "npm:^3.0.3" - "@jridgewell/sourcemap-codec": "npm:^1.4.10" - checksum: 10c0/fa425b606d7c7ee5bfa6a31a7b050dd5814b4082f318e0e4190f991902181b4330f43f4805db1dd4f2433fd0ed9cc7a7b9c2683f1deeab1df1b0a98b1e24055b - languageName: node - linkType: hard - "@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.20, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25": version: 0.3.25 resolution: "@jridgewell/trace-mapping@npm:0.3.25" @@ -1533,34 +1514,6 @@ __metadata: languageName: node linkType: hard -"@tsconfig/node10@npm:^1.0.7": - version: 1.0.11 - resolution: "@tsconfig/node10@npm:1.0.11" - checksum: 10c0/28a0710e5d039e0de484bdf85fee883bfd3f6a8980601f4d44066b0a6bcd821d31c4e231d1117731c4e24268bd4cf2a788a6787c12fc7f8d11014c07d582783c - languageName: node - linkType: hard - -"@tsconfig/node12@npm:^1.0.7": - version: 1.0.11 - resolution: "@tsconfig/node12@npm:1.0.11" - checksum: 10c0/dddca2b553e2bee1308a056705103fc8304e42bb2d2cbd797b84403a223b25c78f2c683ec3e24a095e82cd435387c877239bffcb15a590ba817cd3f6b9a99fd9 - languageName: node - linkType: hard - -"@tsconfig/node14@npm:^1.0.0": - version: 1.0.3 - resolution: "@tsconfig/node14@npm:1.0.3" - checksum: 10c0/67c1316d065fdaa32525bc9449ff82c197c4c19092b9663b23213c8cbbf8d88b6ed6a17898e0cbc2711950fbfaf40388938c1c748a2ee89f7234fc9e7fe2bf44 - languageName: node - linkType: hard - -"@tsconfig/node16@npm:^1.0.2": - version: 1.0.4 - resolution: "@tsconfig/node16@npm:1.0.4" - checksum: 10c0/05f8f2734e266fb1839eb1d57290df1664fe2aa3b0fdd685a9035806daa635f7519bf6d5d9b33f6e69dd545b8c46bd6e2b5c79acb2b1f146e885f7f11a42a5bb - languageName: node - linkType: hard - "@types/babel__core@npm:^7.1.14": version: 7.20.5 resolution: "@types/babel__core@npm:7.20.5" @@ -2194,16 +2147,7 @@ __metadata: languageName: node linkType: hard -"acorn-walk@npm:^8.1.1": - version: 8.3.4 - resolution: "acorn-walk@npm:8.3.4" - dependencies: - acorn: "npm:^8.11.0" - checksum: 10c0/76537ac5fb2c37a64560feaf3342023dadc086c46da57da363e64c6148dc21b57d49ace26f949e225063acb6fb441eabffd89f7a3066de5ad37ab3e328927c62 - languageName: node - linkType: hard - -"acorn@npm:^8.11.0, acorn@npm:^8.12.0, acorn@npm:^8.4.1, acorn@npm:^8.7.1, acorn@npm:^8.8.2": +"acorn@npm:^8.12.0, acorn@npm:^8.7.1, acorn@npm:^8.8.2": version: 8.12.1 resolution: "acorn@npm:8.12.1" bin: @@ -2326,13 +2270,6 @@ __metadata: languageName: node linkType: hard -"arg@npm:^4.1.0": - version: 4.1.3 - resolution: "arg@npm:4.1.3" - checksum: 10c0/070ff801a9d236a6caa647507bdcc7034530604844d64408149a26b9e87c2f97650055c0f049abd1efc024b334635c01f29e0b632b371ac3f26130f4cf65997a - languageName: node - linkType: hard - "argparse@npm:^1.0.7": version: 1.0.10 resolution: "argparse@npm:1.0.10" @@ -2957,13 +2894,6 @@ __metadata: languageName: node linkType: hard -"create-require@npm:^1.1.0": - version: 1.1.1 - resolution: "create-require@npm:1.1.1" - checksum: 10c0/157cbc59b2430ae9a90034a5f3a1b398b6738bf510f713edc4d4e45e169bc514d3d99dd34d8d01ca7ae7830b5b8b537e46ae8f3c8f932371b0875c0151d7ec91 - languageName: node - linkType: hard - "cross-env@npm:7.0.3": version: 7.0.3 resolution: "cross-env@npm:7.0.3" @@ -3135,13 +3065,6 @@ __metadata: languageName: node linkType: hard -"diff@npm:^4.0.1": - version: 4.0.2 - resolution: "diff@npm:4.0.2" - checksum: 10c0/81b91f9d39c4eaca068eb0c1eb0e4afbdc5bb2941d197f513dd596b820b956fef43485876226d65d497bebc15666aa2aa82c679e84f65d5f2bfbf14ee46e32c1 - languageName: node - linkType: hard - "discord-api-types@npm:0.37.83": version: 0.37.83 resolution: "discord-api-types@npm:0.37.83" @@ -5740,7 +5663,7 @@ __metadata: languageName: node linkType: hard -"make-error@npm:^1.1.1, make-error@npm:^1.3.6": +"make-error@npm:^1.3.6": version: 1.3.6 resolution: "make-error@npm:1.3.6" checksum: 10c0/171e458d86854c6b3fc46610cfacf0b45149ba043782558c6875d9f42f222124384ad0b468c92e996d815a8a2003817a710c0a160e49c1c394626f76fa45396f @@ -7720,44 +7643,6 @@ __metadata: languageName: node linkType: hard -"ts-node@npm:10.9.2": - version: 10.9.2 - resolution: "ts-node@npm:10.9.2" - dependencies: - "@cspotcode/source-map-support": "npm:^0.8.0" - "@tsconfig/node10": "npm:^1.0.7" - "@tsconfig/node12": "npm:^1.0.7" - "@tsconfig/node14": "npm:^1.0.0" - "@tsconfig/node16": "npm:^1.0.2" - acorn: "npm:^8.4.1" - acorn-walk: "npm:^8.1.1" - arg: "npm:^4.1.0" - create-require: "npm:^1.1.0" - diff: "npm:^4.0.1" - make-error: "npm:^1.1.1" - v8-compile-cache-lib: "npm:^3.0.1" - yn: "npm:3.1.1" - peerDependencies: - "@swc/core": ">=1.2.50" - "@swc/wasm": ">=1.2.50" - "@types/node": "*" - typescript: ">=2.7" - peerDependenciesMeta: - "@swc/core": - optional: true - "@swc/wasm": - optional: true - bin: - ts-node: dist/bin.js - ts-node-cwd: dist/bin-cwd.js - ts-node-esm: dist/bin-esm.js - ts-node-script: dist/bin-script.js - ts-node-transpile-only: dist/bin-transpile.js - ts-script: dist/bin-script-deprecated.js - checksum: 10c0/5f29938489f96982a25ba650b64218e83a3357d76f7bede80195c65ab44ad279c8357264639b7abdd5d7e75fc269a83daa0e9c62fd8637a3def67254ecc9ddc2 - languageName: node - linkType: hard - "tsconfig-paths@npm:^3.15.0": version: 3.15.0 resolution: "tsconfig-paths@npm:3.15.0" @@ -8040,13 +7925,6 @@ __metadata: languageName: node linkType: hard -"v8-compile-cache-lib@npm:^3.0.1": - version: 3.0.1 - resolution: "v8-compile-cache-lib@npm:3.0.1" - checksum: 10c0/bdc36fb8095d3b41df197f5fb6f11e3a26adf4059df3213e3baa93810d8f0cc76f9a74aaefc18b73e91fe7e19154ed6f134eda6fded2e0f1c8d2272ed2d2d391 - languageName: node - linkType: hard - "v8-to-istanbul@npm:^9.0.1": version: 9.3.0 resolution: "v8-to-istanbul@npm:9.3.0" @@ -8369,13 +8247,6 @@ __metadata: languageName: node linkType: hard -"yn@npm:3.1.1": - version: 3.1.1 - resolution: "yn@npm:3.1.1" - checksum: 10c0/0732468dd7622ed8a274f640f191f3eaf1f39d5349a1b72836df484998d7d9807fbea094e2f5486d6b0cd2414aad5775972df0e68f8604db89a239f0f4bf7443 - languageName: node - linkType: hard - "yocto-queue@npm:^0.1.0": version: 0.1.0 resolution: "yocto-queue@npm:0.1.0"