diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7a0f26735..1972ad4b9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,15 +3,14 @@ on: push: branches: [develop] pull_request: - branches: [master, develop, next] jobs: test: name: Test runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest] - timeout-minutes: 15 + os: [ubuntu-latest, windows-latest] + timeout-minutes: 5 steps: - name: Checkout diff --git a/package.json b/package.json index 7521db615..b094d1cd4 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "kolorist": "^1.8.0", "lint-staged": "^14.0.1", "magic-string": "^0.30.3", - "manten": "^1.1.0", + "manten": "^1.2.0", "node-pty": "^1.0.0", "outdent": "^0.8.0", "pkgroll": "^1.11.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d2bd97f6b..615be531d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -82,8 +82,8 @@ devDependencies: specifier: ^0.30.3 version: 0.30.3 manten: - specifier: ^1.1.0 - version: 1.1.0 + specifier: ^1.2.0 + version: 1.2.0 node-pty: specifier: ^1.0.0 version: 1.0.0 @@ -401,10 +401,10 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/schemas': 29.6.3 - '@types/istanbul-lib-coverage': 2.0.4 - '@types/istanbul-reports': 3.0.1 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 '@types/node': 20.6.0 - '@types/yargs': 17.0.24 + '@types/yargs': 17.0.31 chalk: 4.1.2 dev: true @@ -625,20 +625,20 @@ packages: resolution: {integrity: sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==} dev: true - /@types/istanbul-lib-coverage@2.0.4: - resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} + /@types/istanbul-lib-coverage@2.0.6: + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} dev: true - /@types/istanbul-lib-report@3.0.0: - resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==} + /@types/istanbul-lib-report@3.0.3: + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} dependencies: - '@types/istanbul-lib-coverage': 2.0.4 + '@types/istanbul-lib-coverage': 2.0.6 dev: true - /@types/istanbul-reports@3.0.1: - resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==} + /@types/istanbul-reports@3.0.4: + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} dependencies: - '@types/istanbul-lib-report': 3.0.0 + '@types/istanbul-lib-report': 3.0.3 dev: true /@types/json-schema@7.0.12: @@ -693,22 +693,22 @@ packages: source-map: 0.6.1 dev: true - /@types/stack-utils@2.0.1: - resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} + /@types/stack-utils@2.0.3: + resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} dev: true /@types/unist@2.0.8: resolution: {integrity: sha512-d0XxK3YTObnWVp6rZuev3c49+j4Lo8g4L1ZRm9z5L0xpoZycUPshHgczK5gsUMaZOstjVYYi09p5gYvUtfChYw==} dev: true - /@types/yargs-parser@21.0.0: - resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} + /@types/yargs-parser@21.0.3: + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} dev: true - /@types/yargs@17.0.24: - resolution: {integrity: sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==} + /@types/yargs@17.0.31: + resolution: {integrity: sha512-bocYSx4DI8TmdlvxqGpVNXOgCNR1Jj0gNPhhAY+iz1rgKDAaYrAYdFYnhDV1IFuiuVc9HkOwyDcFxaTElF3/wg==} dependencies: - '@types/yargs-parser': 21.0.0 + '@types/yargs-parser': 21.0.3 dev: true /@typescript-eslint/eslint-plugin@6.7.0(@typescript-eslint/parser@6.7.0)(eslint@8.49.0)(typescript@5.2.2): @@ -2684,7 +2684,7 @@ packages: dependencies: '@babel/code-frame': 7.22.13 '@jest/types': 29.6.3 - '@types/stack-utils': 2.0.1 + '@types/stack-utils': 2.0.3 chalk: 4.1.2 graceful-fs: 4.2.11 micromatch: 4.0.5 @@ -2931,8 +2931,8 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true - /manten@1.1.0: - resolution: {integrity: sha512-DyEpskY9RzqXWO3IZqH8SKxsYHdKyN+k+cLtLoTTg82/DGKbKZuHs6nBV17xchnBVFxibAWGhNSXo7h1EHO0jw==} + /manten@1.2.0: + resolution: {integrity: sha512-H6+meeudHQqh8k4N5IKB40TP27V1rjcYfmRdkJ5ffCaMYxVKW0M1p9+LVAH0uGoxcMcHKJ4GNUPhOit4Ok/ykA==} dependencies: expect: 29.7.0 dev: true diff --git a/tests/fixtures/catch-signals.js b/tests/fixtures/catch-signals.js deleted file mode 100644 index e04df1006..000000000 --- a/tests/fixtures/catch-signals.js +++ /dev/null @@ -1,18 +0,0 @@ -const signals = [ - 'SIGINT', - 'SIGTERM', -]; - -for (const name of signals) { - process.on(name, () => { - console.log(name); - - setTimeout(() => { - console.log(name, 'HANDLER COMPLETED'); - process.exit(200); - }, 100); - }); -} - -setTimeout(() => {}, 1e5); -console.log('READY'); diff --git a/tests/fixtures/import-file.js b/tests/fixtures/import-file.js deleted file mode 100644 index ab57f282e..000000000 --- a/tests/fixtures/import-file.js +++ /dev/null @@ -1,2 +0,0 @@ -// Must be .js file so it can toggle between commonjs and module -import(process.argv[2]).then(m => console.log(JSON.stringify(m))); diff --git a/tests/fixtures/import-file.ts b/tests/fixtures/import-file.ts deleted file mode 100644 index ab57f282e..000000000 --- a/tests/fixtures/import-file.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Must be .js file so it can toggle between commonjs and module -import(process.argv[2]).then(m => console.log(JSON.stringify(m))); diff --git a/tests/fixtures/keep-alive.js b/tests/fixtures/keep-alive.js deleted file mode 100644 index 3948c11df..000000000 --- a/tests/fixtures/keep-alive.js +++ /dev/null @@ -1,2 +0,0 @@ -setTimeout(() => {}, 1e5); -console.log('READY'); diff --git a/tests/fixtures/lib/cjs-ext-cjs/index.cjs b/tests/fixtures/lib/cjs-ext-cjs/index.cjs deleted file mode 100644 index 80f48bdde..000000000 --- a/tests/fixtures/lib/cjs-ext-cjs/index.cjs +++ /dev/null @@ -1,56 +0,0 @@ -async function test(description, testFunction) { - try { - const result = await testFunction(); - if (!result) { throw result; } - console.log(`✔ ${description}`); - } catch (error) { - console.log(`✖ ${description}: ${error.toString().split('\n').shift()}`); - } -} - -console.log('loaded cjs-ext-cjs/index.cjs'); - -test( - 'has CJS context', - () => typeof require !== 'undefined' || typeof module !== 'undefined', -); - -// esbuild uses import.meta as a signal for ESM -// test( -// 'import.meta.url', -// () => Boolean(import.meta.url), -// ); - -test( - 'name in error', - () => { - let nameInError; - try { - nameInError(); - } catch (error) { - return error.message.includes('nameInError'); - } - }, -); - -test( - 'sourcemaps', - () => new Error().stack.includes(':38:'), -); - -test( - 'resolves optional node prefix', - () => Boolean(require('node:fs')), -); - -test( - 'resolves required node prefix', - () => Boolean(require('node:test')), -); - -test( - 'has dynamic import', - () => import('fs').then(Boolean), -); - -module.exports = 1234; diff --git a/tests/fixtures/lib/cjs-ext-js/index.js b/tests/fixtures/lib/cjs-ext-js/index.js deleted file mode 100644 index c2b54b9c3..000000000 --- a/tests/fixtures/lib/cjs-ext-js/index.js +++ /dev/null @@ -1,59 +0,0 @@ -async function test(description, testFunction) { - try { - const result = await testFunction(); - if (!result) { throw result; } - console.log(`✔ ${description}`); - } catch (error) { - console.log(`✖ ${description}: ${error.toString().split('\n').shift()}`); - } -} - -console.log('loaded cjs-ext-js/index.js'); - -test( - 'has CJS context', - () => typeof require !== 'undefined' || typeof module !== 'undefined', -); - -// esbuild uses import.meta as a signal for ESM -// test( -// 'import.meta.url', -// () => Boolean(import.meta.url), -// ); - -test( - 'name in error', - () => { - let nameInError; - try { - nameInError(); - } catch (error) { - return error.message.includes('nameInError'); - } - }, -); - -test( - 'sourcemaps', - () => new Error().stack.includes(':38:'), -); - -test( - 'resolves optional node prefix', - () => Boolean(require('node:fs')), -); - -test( - 'resolves required node prefix', - () => Boolean(require('node:test')), -); - -test( - 'has dynamic import', - () => import('fs').then(Boolean), -); - -test( - 'module exports', - () => module.exports = 1234, -); diff --git a/tests/fixtures/lib/esm-ext-js/index.js b/tests/fixtures/lib/esm-ext-js/index.js deleted file mode 100644 index 45659781a..000000000 --- a/tests/fixtures/lib/esm-ext-js/index.js +++ /dev/null @@ -1,55 +0,0 @@ -async function test(description, testFunction) { - try { - const result = await testFunction(); - if (!result) { throw result; } - console.log(`✔ ${description}`); - } catch (error) { - console.log(`✖ ${description}: ${error.toString().split('\n').shift()}`); - } -} - -console.log('loaded esm-ext-js/index.js'); - -test( - 'has CJS context', - () => typeof require !== 'undefined' || typeof module !== 'undefined', -); - -test( - 'import.meta.url', - () => Boolean(import.meta.url), -); - -test( - 'name in error', - () => { - let nameInError; - try { - nameInError(); - } catch (error) { - return error.message.includes('nameInError'); - } - }, -); - -test( - 'sourcemaps', - () => new Error().stack.includes(':37:'), -); - -test( - 'has dynamic import', - () => import('fs').then(Boolean), -); - -test( - 'resolves optional node prefix', - () => import('node:fs').then(Boolean), -); - -test( - 'resolves required node prefix', - () => import('node:test').then(Boolean), -); - -export default 1234; diff --git a/tests/fixtures/lib/esm-ext-mjs/index.mjs b/tests/fixtures/lib/esm-ext-mjs/index.mjs deleted file mode 100644 index d1c0e5aec..000000000 --- a/tests/fixtures/lib/esm-ext-mjs/index.mjs +++ /dev/null @@ -1,55 +0,0 @@ -async function test(description, testFunction) { - try { - const result = await testFunction(); - if (!result) { throw result; } - console.log(`✔ ${description}`); - } catch (error) { - console.log(`✖ ${description}: ${error.toString().split('\n').shift()}`); - } -} - -console.log('loaded esm-ext-mjs/index.mjs'); - -test( - 'has CJS context', - () => typeof require !== 'undefined' || typeof module !== 'undefined', -); - -test( - 'import.meta.url', - () => Boolean(import.meta.url), -); - -test( - 'name in error', - () => { - let nameInError; - try { - nameInError(); - } catch (error) { - return error.message.includes('nameInError'); - } - }, -); - -test( - 'sourcemaps', - () => new Error().stack.includes(':37:'), -); - -test( - 'has dynamic import', - () => import('fs').then(Boolean), -); - -test( - 'resolves optional node prefix', - () => import('node:fs').then(Boolean), -); - -test( - 'resolves required node prefix', - () => import('node:test').then(Boolean), -); - -export default 1234; diff --git a/tests/fixtures/lib/ts-ext-cts/index.cts b/tests/fixtures/lib/ts-ext-cts/index.cts deleted file mode 100644 index 05d74947a..000000000 --- a/tests/fixtures/lib/ts-ext-cts/index.cts +++ /dev/null @@ -1,56 +0,0 @@ -async function test(description: string, testFunction: () => any | Promise) { - try { - const result = await testFunction(); - if (!result) { throw result; } - console.log(`✔ ${description}`); - } catch (error) { - console.log(`✖ ${description}: ${(error as any).toString().split('\n').shift()}`); - } -} - -console.log('loaded ts-ext-cts/index.cts'); - -test( - 'has CJS context', - () => typeof require !== 'undefined' || typeof module !== 'undefined', -); - -test( - 'import.meta.url', - () => Boolean(import.meta.url), -); - -test( - 'name in error', - () => { - let nameInError; - try { - // @ts-expect-error - this is a test - nameInError(); - } catch (error) { - return (error as any).message.includes('nameInError'); - } - }, -); - -test( - 'sourcemaps', - () => new Error().stack!.includes(':38:'), -); - -test( - 'resolves optional node prefix', - () => Boolean(require('node:fs')), -); - -test( - 'resolves required node prefix', - () => Boolean(require('node:test')), -); - -test( - 'has dynamic import', - () => import('fs').then(Boolean), -); - -export default 1234; diff --git a/tests/fixtures/lib/ts-ext-jsx/index.jsx b/tests/fixtures/lib/ts-ext-jsx/index.jsx deleted file mode 100644 index fd462a717..000000000 --- a/tests/fixtures/lib/ts-ext-jsx/index.jsx +++ /dev/null @@ -1,59 +0,0 @@ -async function test(description, testFunction) { - try { - const result = await testFunction(); - if (!result) { throw result; } - console.log(`✔ ${description}`); - } catch (error) { - console.log(`✖ ${description}: ${error.toString().split('\n').shift()}`); - } -} - -console.log('loaded ts-ext-jsx/index.jsx'); - -test( - 'has CJS context', - () => typeof require !== 'undefined' || typeof module !== 'undefined', -); - -test( - 'import.meta.url', - () => Boolean(import.meta.url), -); - -test( - 'name in error', - () => { - let nameInError; - try { - nameInError(); - } catch (error) { - return error.message.includes('nameInError'); - } - }, -); - -test( - 'sourcemaps', - () => new Error().stack.includes(':37:'), -); - -test( - 'has dynamic import', - () => import('fs').then(Boolean), -); - -test( - 'resolves optional node prefix', - () => import('node:fs').then(Boolean), -); - -test( - 'resolves required node prefix', - () => import('node:test').then(Boolean), -); - -const React = { - createElement: (...args) => Array.from(args), -}; - -export default (
hello world
); diff --git a/tests/fixtures/lib/ts-ext-mts/index.mts b/tests/fixtures/lib/ts-ext-mts/index.mts deleted file mode 100644 index 9df079336..000000000 --- a/tests/fixtures/lib/ts-ext-mts/index.mts +++ /dev/null @@ -1,56 +0,0 @@ -async function test(description: string, testFunction: () => any | Promise) { - try { - const result = await testFunction(); - if (!result) { throw result; } - console.log(`✔ ${description}`); - } catch (error) { - console.log(`✖ ${description}: ${(error as any).toString().split('\n').shift()}`); - } -} - -console.log('loaded ts-ext-mts/index.mts'); - -test( - 'has CJS context', - () => typeof require !== 'undefined' || typeof module !== 'undefined', -); - -test( - 'import.meta.url', - () => Boolean(import.meta.url), -); - -test( - 'name in error', - () => { - let nameInError; - try { - // @ts-expect-error - this is a test - nameInError(); - } catch (error) { - return (error as any).message.includes('nameInError'); - } - }, -); - -test( - 'sourcemaps', - () => new Error().stack!.includes(':38:'), -); - -test( - 'has dynamic import', - () => import('fs').then(Boolean), -); - -test( - 'resolves optional node prefix', - () => import('node:fs').then(Boolean), -); - -test( - 'resolves required node prefix', - () => import('node:test').then(Boolean), -); - -export default 1234; diff --git a/tests/fixtures/lib/ts-ext-ts/index.ts b/tests/fixtures/lib/ts-ext-ts/index.ts deleted file mode 100644 index e36c69b8b..000000000 --- a/tests/fixtures/lib/ts-ext-ts/index.ts +++ /dev/null @@ -1,56 +0,0 @@ -async function test(description: string, testFunction: () => any | Promise) { - try { - const result = await testFunction(); - if (!result) { throw result; } - console.log(`✔ ${description}`); - } catch (error) { - console.log(`✖ ${description}: ${(error as any).toString().split('\n').shift()}`); - } -} - -console.log('loaded ts-ext-ts/index.ts'); - -test( - 'has CJS context', - () => typeof require !== 'undefined' || typeof module !== 'undefined', -); - -test( - 'import.meta.url', - () => Boolean(import.meta.url), -); - -test( - 'name in error', - () => { - let nameInError; - try { - // @ts-expect-error - this is a test - nameInError(); - } catch (error) { - return (error as any).message.includes('nameInError'); - } - }, -); - -test( - 'sourcemaps', - () => new Error().stack!.includes(':38:'), -); - -test( - 'has dynamic import', - () => import('fs').then(Boolean), -); - -test( - 'resolves optional node prefix', - () => import('node:fs').then(Boolean), -); - -test( - 'resolves required node prefix', - () => import('node:test').then(Boolean), -); - -export default 1234; diff --git a/tests/fixtures/lib/ts-ext-ts/index.tsx.ts b/tests/fixtures/lib/ts-ext-ts/index.tsx.ts deleted file mode 100644 index 7e012596b..000000000 --- a/tests/fixtures/lib/ts-ext-ts/index.tsx.ts +++ /dev/null @@ -1,56 +0,0 @@ -async function test(description: string, testFunction: () => any | Promise) { - try { - const result = await testFunction(); - if (!result) { throw result; } - console.log(`✔ ${description}`); - } catch (error) { - console.log(`✖ ${description}: ${(error as any).toString().split('\n').shift()}`); - } -} - -console.log('loaded ts-ext-ts/index.tsx.ts'); - -test( - 'has CJS context', - () => typeof require !== 'undefined' || typeof module !== 'undefined', -); - -test( - 'import.meta.url', - () => Boolean(import.meta.url), -); - -test( - 'name in error', - () => { - let nameInError; - try { - // @ts-expect-error - this is a test - nameInError(); - } catch (error) { - return (error as any).message.includes('nameInError'); - } - }, -); - -test( - 'sourcemaps', - () => new Error().stack!.includes(':38:'), -); - -test( - 'has dynamic import', - () => import('fs').then(Boolean), -); - -test( - 'resolves optional node prefix', - () => import('node:fs').then(Boolean), -); - -test( - 'resolves required node prefix', - () => import('node:test').then(Boolean), -); - -export default 1234; diff --git a/tests/fixtures/lib/ts-ext-tsx/index.tsx b/tests/fixtures/lib/ts-ext-tsx/index.tsx deleted file mode 100644 index f1ce11bfa..000000000 --- a/tests/fixtures/lib/ts-ext-tsx/index.tsx +++ /dev/null @@ -1,60 +0,0 @@ -async function test(description: string, testFunction: () => any | Promise) { - try { - const result = await testFunction(); - if (!result) { throw result; } - console.log(`✔ ${description}`); - } catch (error) { - console.log(`✖ ${description}: ${(error as any).toString().split('\n').shift()}`); - } -} - -console.log('loaded ts-ext-tsx/index.tsx'); - -test( - 'has CJS context', - () => typeof require !== 'undefined' || typeof module !== 'undefined', -); - -test( - 'import.meta.url', - () => Boolean(import.meta.url), -); - -test( - 'name in error', - () => { - let nameInError; - try { - // @ts-expect-error - this is a test - nameInError(); - } catch (error) { - return (error as any).message.includes('nameInError'); - } - }, -); - -test( - 'sourcemaps', - () => new Error().stack!.includes(':38:'), -); - -test( - 'has dynamic import', - () => import('fs').then(Boolean), -); - -test( - 'resolves optional node prefix', - () => import('node:fs').then(Boolean), -); - -test( - 'resolves required node prefix', - () => import('node:test').then(Boolean), -); - -const React = { - createElement: (...args: any[]) => Array.from(args), -}; - -export default (
hello world
); diff --git a/tests/fixtures/lib/wasm/index.js b/tests/fixtures/lib/wasm/index.js deleted file mode 100644 index 2e6d373be..000000000 --- a/tests/fixtures/lib/wasm/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import { myValue } from './test.wasm'; - -console.log(myValue.valueOf()); diff --git a/tests/fixtures/log-argv.ts b/tests/fixtures/log-argv.ts deleted file mode 100644 index a8fd079cd..000000000 --- a/tests/fixtures/log-argv.ts +++ /dev/null @@ -1 +0,0 @@ -console.log(JSON.stringify(process.argv) as string); // Unnecessary TS syntax to test diff --git a/tests/fixtures/node_modules/package-exports/index.js b/tests/fixtures/node_modules/package-exports/index.js deleted file mode 100644 index b7a20332d..000000000 --- a/tests/fixtures/node_modules/package-exports/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export default 'default export'; -export const namedExport = 'named export'; diff --git a/tests/fixtures/node_modules/package-exports/package.json b/tests/fixtures/node_modules/package-exports/package.json deleted file mode 100644 index 02f110a9a..000000000 --- a/tests/fixtures/node_modules/package-exports/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "package-exports", - "exports": { - "./index.js": "./index.js" - } -} diff --git a/tests/fixtures/node_modules/package-module/index.js b/tests/fixtures/node_modules/package-module/index.js deleted file mode 100644 index b7a20332d..000000000 --- a/tests/fixtures/node_modules/package-module/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export default 'default export'; -export const namedExport = 'named export'; diff --git a/tests/fixtures/node_modules/package-module/package.json b/tests/fixtures/node_modules/package-module/package.json deleted file mode 100644 index 611b46a8b..000000000 --- a/tests/fixtures/node_modules/package-module/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "package-module", - "type": "module", - "main": "index.js" -} diff --git a/tests/fixtures/node_modules/package-module/ts.ts b/tests/fixtures/node_modules/package-module/ts.ts deleted file mode 100644 index 091f60949..000000000 --- a/tests/fixtures/node_modules/package-module/ts.ts +++ /dev/null @@ -1,2 +0,0 @@ -export default 'ts default export'; -export const namedExport: string = 'ts named export'; diff --git a/tests/fixtures/node_modules/package-typescript-export/index.ts b/tests/fixtures/node_modules/package-typescript-export/index.ts deleted file mode 100644 index 091f60949..000000000 --- a/tests/fixtures/node_modules/package-typescript-export/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export default 'ts default export'; -export const namedExport: string = 'ts named export'; diff --git a/tests/fixtures/node_modules/package-typescript-export/package.json b/tests/fixtures/node_modules/package-typescript-export/package.json deleted file mode 100644 index 507f6aae5..000000000 --- a/tests/fixtures/node_modules/package-typescript-export/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "package-typescript-export", - "type": "module", - "exports": { - "import": "./index.ts" - } -} diff --git a/tests/fixtures/require-file.cjs b/tests/fixtures/require-file.cjs deleted file mode 100644 index 77bb3f859..000000000 --- a/tests/fixtures/require-file.cjs +++ /dev/null @@ -1 +0,0 @@ -console.log(JSON.stringify(require(process.argv[2]))); diff --git a/tests/fixtures/require-file.cts b/tests/fixtures/require-file.cts deleted file mode 100644 index 77bb3f859..000000000 --- a/tests/fixtures/require-file.cts +++ /dev/null @@ -1 +0,0 @@ -console.log(JSON.stringify(require(process.argv[2]))); diff --git a/tests/fixtures/test-runner-file.ts b/tests/fixtures/test-runner-file.ts deleted file mode 100644 index 6e7fc8aa8..000000000 --- a/tests/fixtures/test-runner-file.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { test } from 'node:test'; -import assert from 'assert'; - -test('passing test', () => { - assert.strictEqual(1, 1); -}); diff --git a/tests/fixtures/lib/wasm/test.wasm b/tests/fixtures/test.wasm similarity index 100% rename from tests/fixtures/lib/wasm/test.wasm rename to tests/fixtures/test.wasm diff --git a/tests/fixtures/tsconfig/dependency-resolve-current-directory.ts b/tests/fixtures/tsconfig/dependency-resolve-current-directory.ts deleted file mode 100644 index 5295f76ed..000000000 --- a/tests/fixtures/tsconfig/dependency-resolve-current-directory.ts +++ /dev/null @@ -1 +0,0 @@ -import 'resolve-current-directory'; diff --git a/tests/fixtures/tsconfig/dependency-should-not-resolve-baseUrl.ts b/tests/fixtures/tsconfig/dependency-should-not-resolve-baseUrl.ts deleted file mode 100644 index 84f5a6724..000000000 --- a/tests/fixtures/tsconfig/dependency-should-not-resolve-baseUrl.ts +++ /dev/null @@ -1 +0,0 @@ -import 'should-not-resolve-baseUrl'; diff --git a/tests/fixtures/tsconfig/dependency-should-not-resolve-paths.ts b/tests/fixtures/tsconfig/dependency-should-not-resolve-paths.ts deleted file mode 100644 index c8fd4ce7a..000000000 --- a/tests/fixtures/tsconfig/dependency-should-not-resolve-paths.ts +++ /dev/null @@ -1 +0,0 @@ -import 'should-not-resolve-paths'; diff --git a/tests/fixtures/tsconfig/node_modules/resolve-current-directory/lib/file.cjs b/tests/fixtures/tsconfig/node_modules/resolve-current-directory/lib/file.cjs deleted file mode 100644 index 9ed959542..000000000 --- a/tests/fixtures/tsconfig/node_modules/resolve-current-directory/lib/file.cjs +++ /dev/null @@ -1 +0,0 @@ -require('.'); diff --git a/tests/fixtures/tsconfig/node_modules/resolve-current-directory/lib/file.mjs b/tests/fixtures/tsconfig/node_modules/resolve-current-directory/lib/file.mjs deleted file mode 100644 index 8f7bcd336..000000000 --- a/tests/fixtures/tsconfig/node_modules/resolve-current-directory/lib/file.mjs +++ /dev/null @@ -1 +0,0 @@ -import '.'; diff --git a/tests/fixtures/tsconfig/node_modules/resolve-current-directory/lib/index.js b/tests/fixtures/tsconfig/node_modules/resolve-current-directory/lib/index.js deleted file mode 100644 index 7f2bfb16a..000000000 --- a/tests/fixtures/tsconfig/node_modules/resolve-current-directory/lib/index.js +++ /dev/null @@ -1 +0,0 @@ -console.log('resolved'); diff --git a/tests/fixtures/tsconfig/node_modules/resolve-current-directory/package.json b/tests/fixtures/tsconfig/node_modules/resolve-current-directory/package.json deleted file mode 100644 index aa3034061..000000000 --- a/tests/fixtures/tsconfig/node_modules/resolve-current-directory/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "exports": { - "module": "./lib/file.mjs", - "default": "./lib/file.cjs" - } -} diff --git a/tests/fixtures/tsconfig/node_modules/should-not-resolve-baseUrl/index.cjs b/tests/fixtures/tsconfig/node_modules/should-not-resolve-baseUrl/index.cjs deleted file mode 100644 index 4e1262c5a..000000000 --- a/tests/fixtures/tsconfig/node_modules/should-not-resolve-baseUrl/index.cjs +++ /dev/null @@ -1 +0,0 @@ -console.log(require('resolve-target')); diff --git a/tests/fixtures/tsconfig/node_modules/should-not-resolve-baseUrl/index.mjs b/tests/fixtures/tsconfig/node_modules/should-not-resolve-baseUrl/index.mjs deleted file mode 100644 index 4d49952c5..000000000 --- a/tests/fixtures/tsconfig/node_modules/should-not-resolve-baseUrl/index.mjs +++ /dev/null @@ -1,3 +0,0 @@ -import value from 'resolve-target'; - -console.log(value); diff --git a/tests/fixtures/tsconfig/node_modules/should-not-resolve-baseUrl/node_modules/resolve-target/index.js b/tests/fixtures/tsconfig/node_modules/should-not-resolve-baseUrl/node_modules/resolve-target/index.js deleted file mode 100644 index 08c72abce..000000000 --- a/tests/fixtures/tsconfig/node_modules/should-not-resolve-baseUrl/node_modules/resolve-target/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = 'resolved'; \ No newline at end of file diff --git a/tests/fixtures/tsconfig/node_modules/should-not-resolve-baseUrl/node_modules/resolve-target/package.json b/tests/fixtures/tsconfig/node_modules/should-not-resolve-baseUrl/node_modules/resolve-target/package.json deleted file mode 100644 index 0967ef424..000000000 --- a/tests/fixtures/tsconfig/node_modules/should-not-resolve-baseUrl/node_modules/resolve-target/package.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/tests/fixtures/tsconfig/node_modules/should-not-resolve-baseUrl/package.json b/tests/fixtures/tsconfig/node_modules/should-not-resolve-baseUrl/package.json deleted file mode 100644 index ba5e30e4c..000000000 --- a/tests/fixtures/tsconfig/node_modules/should-not-resolve-baseUrl/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "exports": { - "module": "./index.mjs", - "default": "./index.cjs" - } -} diff --git a/tests/fixtures/tsconfig/node_modules/should-not-resolve-paths/index.cjs b/tests/fixtures/tsconfig/node_modules/should-not-resolve-paths/index.cjs deleted file mode 100644 index 0a617e463..000000000 --- a/tests/fixtures/tsconfig/node_modules/should-not-resolve-paths/index.cjs +++ /dev/null @@ -1 +0,0 @@ -console.log(require('p/nested-resolve-target')); diff --git a/tests/fixtures/tsconfig/node_modules/should-not-resolve-paths/index.mjs b/tests/fixtures/tsconfig/node_modules/should-not-resolve-paths/index.mjs deleted file mode 100644 index 1b9fc008c..000000000 --- a/tests/fixtures/tsconfig/node_modules/should-not-resolve-paths/index.mjs +++ /dev/null @@ -1,3 +0,0 @@ -import value from 'p/nested-resolve-target'; - -console.log(value); diff --git a/tests/fixtures/tsconfig/node_modules/should-not-resolve-paths/node_modules/p/nested-resolve-target.js b/tests/fixtures/tsconfig/node_modules/should-not-resolve-paths/node_modules/p/nested-resolve-target.js deleted file mode 100644 index 08c72abce..000000000 --- a/tests/fixtures/tsconfig/node_modules/should-not-resolve-paths/node_modules/p/nested-resolve-target.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = 'resolved'; \ No newline at end of file diff --git a/tests/fixtures/tsconfig/node_modules/should-not-resolve-paths/node_modules/p/package.json b/tests/fixtures/tsconfig/node_modules/should-not-resolve-paths/node_modules/p/package.json deleted file mode 100644 index 0967ef424..000000000 --- a/tests/fixtures/tsconfig/node_modules/should-not-resolve-paths/node_modules/p/package.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/tests/fixtures/tsconfig/node_modules/should-not-resolve-paths/package.json b/tests/fixtures/tsconfig/node_modules/should-not-resolve-paths/package.json deleted file mode 100644 index ba5e30e4c..000000000 --- a/tests/fixtures/tsconfig/node_modules/should-not-resolve-paths/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "exports": { - "module": "./index.mjs", - "default": "./index.cjs" - } -} diff --git a/tests/fixtures/tsconfig/src/base-url.ts b/tests/fixtures/tsconfig/src/base-url.ts deleted file mode 100644 index 4d49952c5..000000000 --- a/tests/fixtures/tsconfig/src/base-url.ts +++ /dev/null @@ -1,3 +0,0 @@ -import value from 'resolve-target'; - -console.log(value); diff --git a/tests/fixtures/tsconfig/src/index.ts b/tests/fixtures/tsconfig/src/index.ts deleted file mode 100644 index f9729c6b0..000000000 --- a/tests/fixtures/tsconfig/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -console.log('Should not run'); diff --git a/tests/fixtures/tsconfig/src/paths-exact-match.ts b/tests/fixtures/tsconfig/src/paths-exact-match.ts deleted file mode 100644 index 1b4b714fc..000000000 --- a/tests/fixtures/tsconfig/src/paths-exact-match.ts +++ /dev/null @@ -1,3 +0,0 @@ -import value from 'paths-exact-match'; - -console.log(value); diff --git a/tests/fixtures/tsconfig/src/paths-prefix-match.ts b/tests/fixtures/tsconfig/src/paths-prefix-match.ts deleted file mode 100644 index 1b9fc008c..000000000 --- a/tests/fixtures/tsconfig/src/paths-prefix-match.ts +++ /dev/null @@ -1,3 +0,0 @@ -import value from 'p/nested-resolve-target'; - -console.log(value); diff --git a/tests/fixtures/tsconfig/src/paths-suffix-match.ts b/tests/fixtures/tsconfig/src/paths-suffix-match.ts deleted file mode 100644 index 73cb17386..000000000 --- a/tests/fixtures/tsconfig/src/paths-suffix-match.ts +++ /dev/null @@ -1,3 +0,0 @@ -import value from 'nested-resolve-target/s'; - -console.log(value); diff --git a/tests/fixtures/tsconfig/src/resolve-target.ts b/tests/fixtures/tsconfig/src/resolve-target.ts deleted file mode 100644 index 3e8855613..000000000 --- a/tests/fixtures/tsconfig/src/resolve-target.ts +++ /dev/null @@ -1 +0,0 @@ -export default 'resolve-target'; diff --git a/tests/fixtures/tsconfig/src/tsx.tsx b/tests/fixtures/tsconfig/src/tsx.tsx deleted file mode 100644 index 9b979274c..000000000 --- a/tests/fixtures/tsconfig/src/tsx.tsx +++ /dev/null @@ -1,12 +0,0 @@ -export default [ - ( -
- hello world -
- ), - ( - <> - goodbye world - - ), -]; diff --git a/tests/fixtures/tsconfig/src/utils/nested-resolve-target.ts b/tests/fixtures/tsconfig/src/utils/nested-resolve-target.ts deleted file mode 100644 index 65a22c6cb..000000000 --- a/tests/fixtures/tsconfig/src/utils/nested-resolve-target.ts +++ /dev/null @@ -1 +0,0 @@ -export default 'nested-resolve-target'; diff --git a/tests/fixtures/tsconfig/tsconfig-custom/tsconfig.custom-name.json b/tests/fixtures/tsconfig/tsconfig-custom/tsconfig.custom-name.json deleted file mode 100644 index f8e649961..000000000 --- a/tests/fixtures/tsconfig/tsconfig-custom/tsconfig.custom-name.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "jsxFactory": "console.error" - }, - "include": [ - "../src" - ] -} diff --git a/tests/fixtures/tsconfig/tsconfig.json b/tests/fixtures/tsconfig/tsconfig.json deleted file mode 100644 index 40c2faa92..000000000 --- a/tests/fixtures/tsconfig/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "compilerOptions": { - "jsx": "react", - "jsxFactory": "console.log", - "jsxFragmentFactory": "null", - "baseUrl": "./src", - "paths": { - "paths-exact-match": ["resolve-target"], - "p/*": ["utils/*"], - "*/s": ["utils/*"] - }, - } -} diff --git a/tests/index.ts b/tests/index.ts index c5e014756..7e7d0cb16 100644 --- a/tests/index.ts +++ b/tests/index.ts @@ -1,63 +1,22 @@ import { describe } from 'manten'; -import { createFixture } from 'fs-fixture'; import { createNode } from './utils/tsx'; import { nodeVersions } from './utils/node-versions'; -const packageTypes = [ - 'commonjs', - 'module', -] as const; - (async () => { - for (const packageType of packageTypes) { - await describe(`Package type: ${packageType}`, async ({ describe, onFinish }) => { - const fixture = await createFixture('./tests/fixtures/'); - - onFinish(async () => await fixture.rm()); + await describe('tsx', async ({ runTestSuite, describe }) => { + await runTestSuite(import('./specs/cli')); + await runTestSuite(import('./specs/watch')); + await runTestSuite(import('./specs/repl')); - await fixture.writeJson('package.json', { - type: packageType, - }); + for (const nodeVersion of nodeVersions) { + const node = await createNode(nodeVersion); - await describe('tsx', ({ runTestSuite }) => { - runTestSuite( - import('./specs/cli'), - fixture.path, - ); + await describe(`Node ${node.version}`, ({ runTestSuite }) => { runTestSuite( - import('./specs/watch'), - fixture.path, + import('./specs/smoke'), + node, ); }); - - for (const nodeVersion of nodeVersions) { - const node = await createNode(nodeVersion, fixture.path); - - node.packageType = packageType; - - await describe(`Node ${node.version}`, ({ runTestSuite }) => { - runTestSuite( - import('./specs/repl'), - node, - ); - runTestSuite( - import('./specs/javascript'), - node, - ); - runTestSuite( - import('./specs/typescript'), - node, - ); - runTestSuite( - import('./specs/json'), - node, - ); - runTestSuite( - import('./specs/wasm'), - node, - ); - }); - } - }); - } + } + }); })(); diff --git a/tests/specs/cli.ts b/tests/specs/cli.ts index 65c32d8e6..f5f5b6e1a 100644 --- a/tests/specs/cli.ts +++ b/tests/specs/cli.ts @@ -1,69 +1,91 @@ import path from 'path'; import { testSuite, expect } from 'manten'; +import { createFixture } from 'fs-fixture'; import packageJson from '../../package.json'; import { tsx, tsxPath } from '../utils/tsx'; import { ptyShell, isWindows } from '../utils/pty-shell'; +import { expectMatchInOrder } from '../utils/expect-match-in-order'; -export default testSuite(({ describe }, fixturePath: string) => { +export default testSuite(({ describe }) => { describe('CLI', ({ describe, test }) => { - describe('version', ({ test }) => { - test('shows version', async () => { - const tsxProcess = await tsx({ - args: ['--version'], - }); - - expect(tsxProcess.exitCode).toBe(0); - expect(tsxProcess.stdout).toBe(`tsx v${packageJson.version}\nnode ${process.version}`); - expect(tsxProcess.stderr).toBe(''); + describe('argv', async ({ describe, onFinish }) => { + const fixture = await createFixture({ + // Unnecessary TS to test syntax + 'log-argv.ts': 'console.log(JSON.stringify(process.argv) as string)', }); + onFinish(async () => await fixture.rm()); - test('doesn\'t show version with file', async () => { - const tsxProcess = await tsx({ - args: [ - path.join(fixturePath, 'log-argv.ts'), - '--version', - ], + describe('version', ({ test }) => { + test('shows version', async () => { + const tsxProcess = await tsx({ + args: ['--version'], + }); + + expect(tsxProcess.exitCode).toBe(0); + expect(tsxProcess.stdout).toBe(`tsx v${packageJson.version}\nnode ${process.version}`); + expect(tsxProcess.stderr).toBe(''); }); - expect(tsxProcess.exitCode).toBe(0); - expect(tsxProcess.stdout).toMatch('"--version"'); - expect(tsxProcess.stdout).not.toMatch(packageJson.version); - expect(tsxProcess.stderr).toBe(''); - }); - }); + test('doesn\'t show version with file', async () => { + const tsxProcess = await tsx({ + args: [ + path.join(fixture.path, 'log-argv.ts'), + '--version', + ], + }); - describe('help', ({ test }) => { - test('shows help', async () => { - const tsxProcess = await tsx({ - args: ['--help'], + expect(tsxProcess.exitCode).toBe(0); + expect(tsxProcess.stdout).toMatch('"--version"'); + expect(tsxProcess.stdout).not.toMatch(packageJson.version); + expect(tsxProcess.stderr).toBe(''); }); - - expect(tsxProcess.exitCode).toBe(0); - expect(tsxProcess.stdout).toMatch('Node.js runtime enhanced with esbuild for loading TypeScript & ESM'); - expect(tsxProcess.stdout).toMatch('Usage: node [options] [ script.js ] [arguments]'); - expect(tsxProcess.stderr).toBe(''); }); - test('doesn\'t show help with file', async () => { - const tsxProcess = await tsx({ - args: [ - path.join(fixturePath, 'log-argv.ts'), - '--help', - ], + describe('help', ({ test }) => { + test('shows help', async () => { + const tsxProcess = await tsx({ + args: ['--help'], + }); + + expect(tsxProcess.exitCode).toBe(0); + expect(tsxProcess.stdout).toMatch('Node.js runtime enhanced with esbuild for loading TypeScript & ESM'); + expect(tsxProcess.stdout).toMatch('Usage: node [options] [ script.js ] [arguments]'); + expect(tsxProcess.stderr).toBe(''); }); - expect(tsxProcess.exitCode).toBe(0); - expect(tsxProcess.stdout).toMatch('"--help"'); - expect(tsxProcess.stdout).not.toMatch('tsx'); - expect(tsxProcess.stderr).toBe(''); + test('doesn\'t show help with file', async () => { + const tsxProcess = await tsx({ + args: [ + path.join(fixture.path, 'log-argv.ts'), + '--help', + ], + }); + + expect(tsxProcess.exitCode).toBe(0); + expect(tsxProcess.stdout).toMatch('"--help"'); + expect(tsxProcess.stdout).not.toMatch('tsx'); + expect(tsxProcess.stderr).toBe(''); + }); }); }); - test('Node.js test runner', async () => { + test('Node.js test runner', async ({ onTestFinish }) => { + const fixture = await createFixture({ + 'test.ts': ` + import { test } from 'node:test'; + import assert from 'assert'; + + test('passing test', () => { + assert.strictEqual(1, 1); + }); + `, + }); + onTestFinish(async () => await fixture.rm()); + const tsxProcess = await tsx({ args: [ '--test', - path.join(fixturePath, 'test-runner-file.ts'), + path.join(fixture.path, 'test.ts'), ], }); @@ -71,71 +93,110 @@ export default testSuite(({ describe }, fixturePath: string) => { expect(tsxProcess.exitCode).toBe(0); }, 10_000); - describe('Relays kill signal', ({ test }) => { - const signals = ['SIGINT', 'SIGTERM']; - - for (const signal of signals) { - test(signal, async () => { - const tsxProcess = tsx({ - args: [ - path.join(fixturePath, 'catch-signals.js'), - ], + describe('Signals', async ({ describe, onFinish }) => { + const fixture = await createFixture({ + 'catch-signals.js': ` + const signals = [ + 'SIGINT', + 'SIGTERM', + ]; + + for (const name of signals) { + process.on(name, () => { + console.log(name); + + setTimeout(() => { + console.log(name, 'HANDLER COMPLETED'); + process.exit(200); + }, 100); }); + } + + setTimeout(() => {}, 1e5); + console.log('READY'); + `, + 'keep-alive.js': ` + setTimeout(() => {}, 1e5); + console.log('READY'); + `, + }); + onFinish(async () => await fixture.rm()); - tsxProcess.stdout!.once('data', () => { - tsxProcess.kill(signal, { - forceKillAfterTimeout: false, + describe('Relays kill signal', ({ test }) => { + const signals = ['SIGINT', 'SIGTERM']; + + for (const signal of signals) { + test(signal, async ({ onTestFail }) => { + const tsxProcess = tsx({ + args: [ + path.join(fixture.path, 'catch-signals.js'), + ], }); - }); - const tsxProcessResolved = await tsxProcess; - - if (process.platform === 'win32') { - /** - * Windows doesn't support sending signals to processes. - * https://nodejs.org/api/process.html#signal-events - * - * Sending SIGINT, SIGTERM, and SIGKILL will cause the unconditional termination - * of the target process, and afterwards, subprocess will report that the process - * was terminated by signal. - */ - expect(tsxProcessResolved.stdout).toBe('READY'); - } else { - expect(tsxProcessResolved.exitCode).toBe(200); - expect(tsxProcessResolved.stdout).toBe(`READY\n${signal}\n${signal} HANDLER COMPLETED`); - } + tsxProcess.stdout!.once('data', () => { + tsxProcess.kill(signal, { + forceKillAfterTimeout: false, + }); + }); + + const tsxProcessResolved = await tsxProcess; + + onTestFail(() => { + console.log(tsxProcessResolved); + }); + + if (process.platform === 'win32') { + /** + * Windows doesn't support sending signals to processes. + * https://nodejs.org/api/process.html#signal-events + * + * Sending SIGINT, SIGTERM, and SIGKILL will cause the unconditional termination + * of the target process, and afterwards, subprocess will report that the process + * was terminated by signal. + */ + expect(tsxProcessResolved.stdout).toBe('READY'); + } else { + expect(tsxProcessResolved.exitCode).toBe(200); + expectMatchInOrder(tsxProcessResolved.stdout, [ + 'READY\n', + `${signal}\n`, + `${signal} HANDLER COMPLETED`, + ]); + } + }, 10_000); + } + }); + + describe('Ctrl + C', ({ test }) => { + test('Exit code', async () => { + const output = await ptyShell( + [ + `${process.execPath} ${tsxPath} ${path.join(fixture.path, 'keep-alive.js')}\r`, + stdout => stdout.includes('READY') && '\u0003', + `echo EXIT_CODE: ${isWindows ? '$LastExitCode' : '$?'}\r`, + ], + ); + expect(output).toMatch(/EXIT_CODE:\s+130/); }, 10_000); - } - }); - describe('Ctrl + C', ({ test }) => { - test('Exit code', async () => { - const output = await ptyShell( - [ - `${process.execPath} ${tsxPath} ./tests/fixtures/keep-alive.js\r`, - stdout => stdout.includes('READY') && '\u0003', - `echo EXIT_CODE: ${isWindows ? '$LastExitCode' : '$?'}\r`, - ], - ); - expect(output).toMatch(/EXIT_CODE:\s+130/); - }, 10_000); - - test('Catchable', async () => { - const output = await ptyShell( - [ - `${process.execPath} ${tsxPath} ./tests/fixtures/catch-signals.js\r`, - stdout => stdout.includes('READY') && '\u0003', - `echo EXIT_CODE: ${isWindows ? '$LastExitCode' : '$?'}\r`, - ], - ); - - expect(output).toMatch( - process.platform === 'win32' - ? 'READY\r\nSIGINT\r\nSIGINT HANDLER COMPLETED\r\n' - : 'READY\r\n^CSIGINT\r\nSIGINT HANDLER COMPLETED\r\n', - ); - expect(output).toMatch(/EXIT_CODE:\s+200/); - }, 10_000); + test('Catchable', async () => { + const output = await ptyShell( + [ + `${process.execPath} ${tsxPath} ${path.join(fixture.path, 'catch-signals.js')}\r`, + stdout => stdout.includes('READY') && '\u0003', + `echo EXIT_CODE: ${isWindows ? '$LastExitCode' : '$?'}\r`, + ], + ); + + expectMatchInOrder(output, [ + 'READY\r\n', + process.platform === 'win32' ? '' : '^C', + 'SIGINT\r\n', + 'SIGINT HANDLER COMPLETED\r\n', + /EXIT_CODE:\s+200/, + ]); + }, 10_000); + }); }); }); }); diff --git a/tests/specs/javascript/cjs.ts b/tests/specs/javascript/cjs.ts deleted file mode 100644 index 136d4c449..000000000 --- a/tests/specs/javascript/cjs.ts +++ /dev/null @@ -1,212 +0,0 @@ -import { testSuite, expect } from 'manten'; -import semver from 'semver'; -import type { ExecaReturnValue } from 'execa'; -import type { NodeApis } from '../../utils/tsx'; -import nodeSupports from '../../utils/node-supports'; - -export default testSuite(async ({ describe }, node: NodeApis) => { - describe('Load CJS', ({ describe }) => { - describe('.cjs extension', ({ describe }) => { - function assertResults({ stdout, stderr }: ExecaReturnValue) { - expect(stdout).toMatch('loaded cjs-ext-cjs/index.cjs'); - expect(stdout).toMatch('✔ has CJS context'); - expect(stdout).toMatch('✔ name in error'); - expect(stdout).toMatch('✔ sourcemaps'); - expect(stdout).toMatch('✔ has dynamic import'); - expect(stdout).toMatch('✔ resolves optional node prefix'); - expect(stdout).toMatch( - semver.satisfies(node.version, nodeSupports.testRunner) - ? '✔ resolves required node prefix' - : '✖ resolves required node prefix: Error', - ); - - expect(stderr).not.toMatch(/loader/i); - } - - describe('full path', ({ test }) => { - const importPath = './lib/cjs-ext-cjs/index.cjs'; - - test('Load', async () => { - const nodeProcess = await node.load(importPath); - assertResults(nodeProcess); - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath); - assertResults(nodeProcess); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - }); - - test('TypeScript Import', async () => { - const nodeProcess = await node.import(importPath, { typescript: true }); - assertResults(nodeProcess); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath); - assertResults(nodeProcess); - expect(nodeProcess.stdout).toMatch('1234'); - }); - }); - - describe('extensionless - should not work', ({ test }) => { - const importPath = './lib/cjs-ext-cjs/index'; - - test('Load', async () => { - const nodeProcess = await node.load(importPath); - expect(nodeProcess.stderr).toMatch('Cannot find module'); - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath); - expect(nodeProcess.stderr).toMatch('Cannot find module'); - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath); - expect(nodeProcess.stderr).toMatch('Cannot find module'); - }); - }); - - describe('directory', ({ test }) => { - const importPath = './lib/cjs-ext-cjs'; - - test('Load', async () => { - const nodeProcess = await node.load(importPath); - expect(nodeProcess.stderr).toMatch('Cannot find module'); - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath); - expect(nodeProcess.stderr).toMatch('Cannot find module'); - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath); - expect(nodeProcess.stderr).toMatch('Cannot find module'); - }); - }); - }); - - describe('.js extension', ({ describe }) => { - function assertCjsResults({ stdout, stderr }: ExecaReturnValue) { - expect(stdout).toMatch('loaded cjs-ext-js/index.js'); - expect(stdout).toMatch('✔ has CJS context'); - expect(stdout).toMatch('✔ name in error'); - expect(stdout).toMatch('✔ sourcemaps'); - expect(stdout).toMatch('✔ has dynamic import'); - expect(stdout).toMatch('✔ resolves optional node prefix'); - expect(stdout).toMatch( - semver.satisfies(node.version, nodeSupports.testRunner) - ? '✔ resolves required node prefix' - : '✖ resolves required node prefix: Error', - ); - - expect(stderr).not.toMatch(/loader/i); - } - - function assertEsmResults({ stdout, stderr }: ExecaReturnValue) { - expect(stdout).toMatch('loaded cjs-ext-js/index.js'); - expect(stdout).toMatch('✖ has CJS context'); - expect(stdout).toMatch('✔ name in error'); - expect(stdout).toMatch('✔ sourcemaps'); - expect(stdout).toMatch('✔ has dynamic import'); - expect(stderr).toBe(''); - } - - describe('full path', ({ test }) => { - const importPath = './lib/cjs-ext-js/index.js'; - - test('Load', async () => { - const nodeProcess = await node.load(importPath); - - if (node.isCJS) { - assertCjsResults(nodeProcess); - } else { - assertEsmResults(nodeProcess); - } - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath); - - if (node.isCJS) { - assertCjsResults(nodeProcess); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - } else { - assertEsmResults(nodeProcess); - } - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath); - assertCjsResults(nodeProcess); - expect(nodeProcess.stdout).toMatch('1234'); - }); - }); - - describe('extensionless', ({ test }) => { - const importPath = './lib/cjs-ext-js/index'; - - test('Load', async () => { - const nodeProcess = await node.load(importPath); - - if (node.isCJS) { - assertCjsResults(nodeProcess); - } else { - assertEsmResults(nodeProcess); - } - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath); - - if (node.isCJS) { - assertCjsResults(nodeProcess); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - } else { - assertEsmResults(nodeProcess); - } - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath); - assertCjsResults(nodeProcess); - expect(nodeProcess.stdout).toMatch('1234'); - }); - }); - - describe('directory', ({ test }) => { - const importPath = './lib/cjs-ext-js'; - - test('Load', async () => { - const nodeProcess = await node.load(importPath); - - if (node.isCJS) { - assertCjsResults(nodeProcess); - } else { - assertEsmResults(nodeProcess); - } - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath); - - if (node.isCJS) { - assertCjsResults(nodeProcess); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - } else { - assertEsmResults(nodeProcess); - } - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath); - assertCjsResults(nodeProcess); - expect(nodeProcess.stdout).toMatch('1234'); - }); - }); - }); - }); -}); diff --git a/tests/specs/javascript/dependencies.ts b/tests/specs/javascript/dependencies.ts deleted file mode 100644 index 1fc0e2d7e..000000000 --- a/tests/specs/javascript/dependencies.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { testSuite, expect } from 'manten'; -import type { NodeApis } from '../../utils/tsx'; - -export default testSuite(async ({ describe }, node: NodeApis) => { - describe('Dependencies', ({ describe }) => { - describe('module dependency', ({ test }) => { - const output = '{"default":"default export","namedExport":"named export"}'; - - test('Import', async () => { - const nodeProcess = await node.import('package-module'); - expect(nodeProcess.stdout).toBe(output); - expect(nodeProcess.stderr).toBe(''); - }); - }); - }); -}); diff --git a/tests/specs/javascript/esm.ts b/tests/specs/javascript/esm.ts deleted file mode 100644 index 4fad2dee2..000000000 --- a/tests/specs/javascript/esm.ts +++ /dev/null @@ -1,193 +0,0 @@ -import { testSuite, expect } from 'manten'; -import semver from 'semver'; -import type { ExecaReturnValue } from 'execa'; -import type { NodeApis } from '../../utils/tsx'; -import nodeSupports from '../../utils/node-supports'; - -export default testSuite(async ({ describe }, node: NodeApis) => { - describe('Load ESM', ({ describe }) => { - describe('.mjs extension', ({ describe }) => { - function assertResults( - { stdout, stderr }: ExecaReturnValue, - cjsContext = false, - ) { - expect(stdout).toMatch('loaded esm-ext-mjs/index.mjs'); - expect(stdout).toMatch( - cjsContext - ? '✔ has CJS context' - : '✖ has CJS context', - ); - expect(stdout).toMatch('✔ name in error'); - expect(stdout).toMatch('✔ sourcemaps'); - expect(stdout).toMatch('✔ has dynamic import'); - expect(stdout).toMatch('✔ resolves optional node prefix'); - expect(stdout).toMatch( - semver.satisfies(node.version, nodeSupports.testRunner) - ? '✔ resolves required node prefix' - : '✖ resolves required node prefix: Error', - ); - expect(stderr).not.toMatch(/loader/i); - } - - describe('full path', ({ test }) => { - const importPath = './lib/esm-ext-mjs/index.mjs'; - - test('Load', async () => { - const nodeProcess = await node.load(importPath); - assertResults(nodeProcess); - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath); - assertResults(nodeProcess); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - }); - - test('TypeScript Import', async () => { - const nodeProcess = await node.import(importPath, { typescript: true }); - assertResults(nodeProcess); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath); - - // By "require()"ing an ESM file, it forces it to be compiled in a CJS context - assertResults(nodeProcess, true); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - }); - }); - - describe('extensionless - should not work', ({ test }) => { - const importPath = './lib/esm-ext-mjs/index'; - - test('Load', async () => { - const nodeProcess = await node.load(importPath); - expect(nodeProcess.stderr).toMatch('Cannot find module'); - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath); - expect(nodeProcess.stderr).toMatch('Cannot find module'); - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath); - expect(nodeProcess.stderr).toMatch('Cannot find module'); - }); - }); - - describe('directory - should not work', ({ test }) => { - const importPath = './lib/esm-ext-mjs'; - - test('Load', async () => { - const nodeProcess = await node.load(importPath); - expect(nodeProcess.stderr).toMatch('Cannot find module'); - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath); - expect(nodeProcess.stderr).toMatch('Cannot find module'); - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath); - expect(nodeProcess.stderr).toMatch('Cannot find module'); - }); - }); - }); - - describe('.js extension', ({ describe }) => { - function assertResults( - { stdout, stderr }: ExecaReturnValue, - cjsContext = false, - ) { - expect(stdout).toMatch('loaded esm-ext-js/index.js'); - expect(stdout).toMatch( - cjsContext - ? '✔ has CJS context' - : '✖ has CJS context', - ); - expect(stdout).toMatch('✔ name in error'); - expect(stdout).toMatch('✔ sourcemaps'); - expect(stdout).toMatch('✔ has dynamic import'); - expect(stdout).toMatch('✔ resolves optional node prefix'); - expect(stdout).toMatch( - semver.satisfies(node.version, nodeSupports.testRunner) - ? '✔ resolves required node prefix' - : '✖ resolves required node prefix: Error', - ); - expect(stderr).not.toMatch(/loader/i); - } - - describe('full path', ({ test }) => { - const importPath = './lib/esm-ext-js/index.js'; - - test('Load', async () => { - const nodeProcess = await node.load(importPath); - assertResults(nodeProcess, node.isCJS); - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath); - assertResults(nodeProcess, node.isCJS); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath); - - // By "require()"ing an ESM file, it forces it to be compiled in a CJS context - assertResults(nodeProcess, true); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - }); - }); - - describe('extensionless', ({ test }) => { - const importPath = './lib/esm-ext-js/index'; - - test('Load', async () => { - const nodeProcess = await node.load(importPath); - assertResults(nodeProcess, node.isCJS); - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath); - assertResults(nodeProcess, node.isCJS); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath); - - // By "require()"ing an ESM file, it forces it to be compiled in a CJS context - assertResults(nodeProcess, true); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - }); - }); - - describe('directory', ({ test }) => { - const importPath = './lib/esm-ext-js'; - - test('Load', async () => { - const nodeProcess = await node.load(importPath); - assertResults(nodeProcess, node.isCJS); - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath); - assertResults(nodeProcess, node.isCJS); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath); - - // By "require()"ing an ESM file, it forces it to be compiled in a CJS context - assertResults(nodeProcess, true); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - }); - }); - }); - }); -}); diff --git a/tests/specs/javascript/index.ts b/tests/specs/javascript/index.ts deleted file mode 100644 index 36a852168..000000000 --- a/tests/specs/javascript/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { testSuite } from 'manten'; -import type { NodeApis } from '../../utils/tsx'; - -export default testSuite(async ({ describe }, node: NodeApis) => { - describe('JavaScript', ({ runTestSuite }) => { - runTestSuite(import('./cjs'), node); - runTestSuite(import('./esm'), node); - runTestSuite(import('./dependencies'), node); - }); -}); diff --git a/tests/specs/json.ts b/tests/specs/json.ts deleted file mode 100644 index ccb2fe3f8..000000000 --- a/tests/specs/json.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { testSuite, expect } from 'manten'; -import { createFixture } from 'fs-fixture'; -import type { NodeApis } from '../utils/tsx'; - -export default testSuite(async ({ describe }, node: NodeApis) => { - describe('Load JSON', async ({ describe, onFinish }) => { - const fixture = await createFixture({ - 'package.json': JSON.stringify({ - type: node.packageType, - }), - 'index.json': JSON.stringify({ - loaded: 'json', - }), - }); - - onFinish(async () => await fixture.rm()); - - describe('full path', async ({ test }) => { - test('Load', async () => { - const nodeProcess = await node.loadFile(fixture.path, './index.json'); - expect(nodeProcess.stdout).toBe(''); - expect(nodeProcess.exitCode).toBe(0); - }); - - test('Import', async () => { - const nodeProcess = await node.importFile(fixture.path, './index.json'); - expect(nodeProcess.stdout).toMatch('default: { loaded: \'json\' }'); - expect(nodeProcess.stderr).toBe(''); - }); - - test('Require', async () => { - const nodeProcess = await node.requireFile(fixture.path, './index.json'); - expect(nodeProcess.stdout).toBe('{ loaded: \'json\' }'); - expect(nodeProcess.stderr).toBe(''); - }); - }); - - describe('extensionless', ({ test }) => { - test('Load', async () => { - const nodeProcess = await node.loadFile(fixture.path, './index'); - expect(nodeProcess.stdout).toBe(''); - expect(nodeProcess.exitCode).toBe(0); - }); - - test('Import', async () => { - const nodeProcess = await node.importFile(fixture.path, './index'); - expect(nodeProcess.stdout).toMatch('default: { loaded: \'json\' }'); - expect(nodeProcess.stderr).toBe(''); - }); - - test('Require', async () => { - const nodeProcess = await node.requireFile(fixture.path, './index'); - expect(nodeProcess.stdout).toBe('{ loaded: \'json\' }'); - expect(nodeProcess.stderr).toBe(''); - }); - }); - - describe('directory', ({ test }) => { - test('Load', async () => { - const nodeProcess = await node.loadFile(fixture.path, '.'); - expect(nodeProcess.stdout).toBe(''); - expect(nodeProcess.exitCode).toBe(0); - }); - - test('Import', async () => { - const nodeProcess = await node.importFile(fixture.path, '.'); - expect(nodeProcess.stdout).toMatch('default: { loaded: \'json\' }'); - expect(nodeProcess.stderr).toBe(''); - }); - - test('Require', async () => { - const nodeProcess = await node.requireFile(fixture.path, '.'); - expect(nodeProcess.stdout).toBe('{ loaded: \'json\' }'); - expect(nodeProcess.stderr).toBe(''); - }); - }); - }); -}); diff --git a/tests/specs/repl.ts b/tests/specs/repl.ts index 838e661e8..9f13cd188 100644 --- a/tests/specs/repl.ts +++ b/tests/specs/repl.ts @@ -1,10 +1,10 @@ import { testSuite } from 'manten'; -import { type NodeApis } from '../utils/tsx'; +import { tsx } from '../utils/tsx'; -export default testSuite(async ({ describe }, node: NodeApis) => { - describe('repl', ({ test }) => { +export default testSuite(async ({ describe }) => { + describe('REPL', ({ test }) => { test('handles ts', async () => { - const tsxProcess = node.tsx({ + const tsxProcess = tsx({ args: ['--interactive'], }); @@ -32,7 +32,7 @@ export default testSuite(async ({ describe }, node: NodeApis) => { }, 40_000); test('doesn\'t error on require', async () => { - const tsxProcess = node.tsx({ + const tsxProcess = tsx({ args: ['--interactive'], }); @@ -58,7 +58,7 @@ export default testSuite(async ({ describe }, node: NodeApis) => { }, 40_000); test('supports incomplete expression in segments', async () => { - const tsxProcess = node.tsx({ + const tsxProcess = tsx({ args: ['--interactive'], }); @@ -87,7 +87,7 @@ export default testSuite(async ({ describe }, node: NodeApis) => { }, 40_000); test('errors on import statement', async () => { - const tsxProcess = node.tsx({ + const tsxProcess = tsx({ args: ['--interactive'], }); diff --git a/tests/specs/smoke.ts b/tests/specs/smoke.ts new file mode 100644 index 000000000..48c6d170c --- /dev/null +++ b/tests/specs/smoke.ts @@ -0,0 +1,646 @@ +import path from 'path'; +import { pathToFileURL } from 'url'; +import { testSuite, expect } from 'manten'; +import { createFixture } from 'fs-fixture'; +import type { NodeApis } from '../utils/tsx'; + +const cjsContextCheck = 'typeof module !== \'undefined\''; +const tsCheck = '1 as number'; + +const declareReact = ` +const React = { + createElement: (...args) => Array.from(args), +}; +`; +const jsxCheck = '<>
JSX
'; + +const preserveName = ` +assert( + (function functionName() {}).name === 'functionName', + 'Name should be preserved' +); +`; + +const wasmPath = path.resolve('tests/fixtures/test.wasm'); +const wasmPathUrl = pathToFileURL(wasmPath).toString(); + +const syntaxLowering = ` +// es2016 - Exponentiation operator +10 ** 4; + +// es2017 - Async functions +(async () => {}); + +// es2018 - Spread properties +({...Object}); + +// es2018 - Rest properties +const {...x} = Object; + +// es2019 - Optional catch binding +try {} catch {} + +// es2020 - Optional chaining +Object?.keys; + +// es2020 - Nullish coalescing +Object ?? true + +// es2020 - import.meta +// import.meta + +// es2021 - Logical assignment operators +// let a = false; a ??= true; a ||= true; a &&= true; + +// es2022 - Class instance fields +(class { x }); + +// es2022 - Static class fields +(class { static x }); + +// es2022 - Private instance methods +(class { #x() {} }); + +// es2022 - Private instance fields +(class { #x }); + +// es2022 - Private static methods +(class { static #x() {} }); + +// es2022 - Private static fields +(class { static #x }); + +// es2022 - Class static blocks +(class { static {} }); + +export const named = 2; +export default 1; +`; + +const sourcemap = { + test: 'const { stack } = new Error(); assert(stack.includes(\':SOURCEMAP_LINE\'), \'Expected SOURCEMAP_LINE in stack:\' + stack)', + tag: ( + strings: TemplateStringsArray, + ...values: string[] + ) => { + const finalString = String.raw({ raw: strings }, ...values); + const lineNumber = finalString.split('\n').findIndex(line => line.includes('SOURCEMAP_LINE')) + 1; + return finalString.replaceAll('SOURCEMAP_LINE', lineNumber.toString()); + }, +}; + +const files = { + 'js/index.js': ` + import assert from 'assert'; + ${syntaxLowering} + ${preserveName} + export const cjsContext = ${cjsContextCheck}; + `, + + 'json/index.json': JSON.stringify({ loaded: 'json' }), + + 'cjs/index.cjs': sourcemap.tag` + const assert = require('node:assert'); + assert(${cjsContextCheck}, 'Should have CJS context'); + ${preserveName} + ${sourcemap.test} + exports.named = 'named'; + `, + + 'mjs/index.mjs': ` + export const mjsHasCjsContext = ${cjsContextCheck}; + `, + + 'ts/index.ts': sourcemap.tag` + import assert from 'assert'; + import type {Type} from 'resolved-by-tsc' + + interface Foo {} + + type Foo = number + + declare module 'foo' {} + + enum BasicEnum { + Left, + Right, + } + + enum NamedEnum { + SomeEnum = 'some-value', + } + + export const a = BasicEnum.Left; + + export const b = NamedEnum.SomeEnum; + + export default function foo(): string { + return 'foo' + } + + // For "ts as tsx" test + const bar = (value: T) => fn(); + + ${preserveName} + ${sourcemap.test} + export const cjsContext = ${cjsContextCheck}; + ${tsCheck}; + `, + + // TODO: test resolution priority for files 'index.tsx` & 'index.tsx.ts` via 'index.tsx' + + 'jsx/index.jsx': sourcemap.tag` + import assert from 'assert'; + export const cjsContext = ${cjsContextCheck}; + ${declareReact} + export const jsx = ${jsxCheck}; + ${preserveName} + ${sourcemap.test} + `, + + 'tsx/index.tsx': sourcemap.tag` + import assert from 'assert'; + export const cjsContext = ${cjsContextCheck}; + ${tsCheck}; + ${declareReact} + export const jsx = ${jsxCheck}; + ${preserveName} + ${sourcemap.test} + `, + + 'mts/index.mts': sourcemap.tag` + import assert from 'assert'; + export const mjsHasCjsContext = ${cjsContextCheck}; + ${tsCheck}; + ${preserveName} + ${sourcemap.test} + `, + + 'cts/index.cts': sourcemap.tag` + const assert = require('assert'); + assert(${cjsContextCheck}, 'Should have CJS context'); + ${tsCheck}; + ${preserveName} + ${sourcemap.test} + `, + + 'expect-errors.js': ` + export const expectErrors = async (...assertions) => { + let errors = await Promise.all( + assertions.map(async ([fn, expectedError]) => { + let thrown; + try { + await fn(); + } catch (error) { + thrown = error; + } + + if (!thrown) { + return new Error('No error thrown'); + } else if (!thrown.message.includes(expectedError)) { + return new Error(\`Message \${JSON.stringify(expectedError)} not found in \${JSON.stringify(thrown.message)}\`); + } + }), + ); + + errors = errors.filter(Boolean); + + if (errors.length > 0) { + console.error(errors); + process.exitCode = 1; + } + }; + `, + + 'file.txt': 'hello', + + node_modules: { + 'pkg-commonjs': { + 'package.json': JSON.stringify({ + type: 'commonjs', + }), + 'index.js': syntaxLowering, + }, + 'pkg-module': { + 'package.json': JSON.stringify({ + type: 'module', + exports: './index.js', + }), + 'index.js': syntaxLowering, + }, + }, + + tsconfig: { + 'file.ts': '', + + 'jsx.jsx': ` + // tsconfig not applied to jsx because allowJs is not set + import { expectErrors } from '../expect-errors'; + expectErrors( + [() => ${jsxCheck}, 'React is not defined'], + + // These should throw unless allowJs is set + // [() => import('prefix/file'), "Cannot find package 'prefix'"], + // [() => import('paths-exact-match'), "Cannot find package 'paths-exact-match'"], + // [() => import('file'), "Cannot find package 'file'"], + ); + `, + + 'node_modules/tsconfig-should-not-apply': { + 'package.json': JSON.stringify({ + exports: { + import: './index.mjs', + default: './index.cjs', + }, + }), + 'index.mjs': ` + import { expectErrors } from '../../../expect-errors'; + expectErrors( + [() => import('prefix/file'), "Cannot find package 'prefix'"], + [() => import('paths-exact-match'), "Cannot find package 'paths-exact-match'"], + [() => import('file'), "Cannot find package 'file'"], + ); + `, + 'index.cjs': ` + const { expectErrors } = require('../../../expect-errors'); + expectErrors( + [() => require('prefix/file'), "Cannot find module"], + [() => require('paths-exact-match'), "Cannot find module"], + [() => require('file'), "Cannot find module"], + ); + `, + }, + + 'index.tsx': ` + ${jsxCheck}; + + import './jsx'; + + // Resolves relative to baseUrl + import 'file'; + + // Resolves paths - exact match + import 'paths-exact-match'; + + // Resolves paths - prefix match + import 'prefix/file'; + + // Resolves paths - suffix match + import 'file/suffix'; + + // tsconfig should not apply to dependency + import "tsconfig-should-not-apply"; + `, + + 'tsconfig.json': JSON.stringify({ + compilerOptions: { + jsxFactory: 'Array', + jsxFragmentFactory: 'null', + baseUrl: '.', + paths: { + 'paths-exact-match': ['file'], + 'prefix/*': ['*'], + '*/suffix': ['*'], + }, + }, + }), + + 'tsconfig-allowJs.json': JSON.stringify({ + extends: './tsconfig.json', + compilerOptions: { + allowJs: true, + }, + }), + }, +}; + +const packageTypes = [ + 'module', + 'commonjs', +] as const; + +export default testSuite(async ({ describe }, { tsx }: NodeApis) => { + describe('Smoke', ({ describe }) => { + for (const packageType of packageTypes) { + const isCommonJs = packageType === 'commonjs'; + + describe(packageType, ({ test, describe }) => { + test('from .js', async ({ onTestFinish, onTestFail }) => { + const fixture = await createFixture({ + ...files, + 'package.json': JSON.stringify({ type: packageType }), + 'import-from-js.js': ` + import assert from 'assert'; + import { expectErrors } from './expect-errors'; + + // node: prefix + import 'node:fs'; + + import * as pkgCommonjs from 'pkg-commonjs'; + import * as pkgModule from 'pkg-module'; + + // .js + import * as js from './js/index.js'; + // import * as js from './js/index.js?query=123'; Support query + import './js/index'; + import './js/'; + + // No double .default.default in Dynamic Import + import('./js/index.js').then(m => { + if (typeof m.default === 'object') { + assert( + !('default' in m.default), + 'Should not have double .default.default in Dynamic Import', + ); + } + }); + + // .json + import * as json from './json/index.json'; + import './json/index'; + import './json/'; + + // .cjs + import * as cjs from './cjs/index.cjs'; + expectErrors( + [() => import('./cjs/index'), 'Cannot find module'], + [() => import('./cjs/'), 'Cannot find module'], + ${ + isCommonJs + ? ` + [() => require('./cjs/index'), 'Cannot find module'], + [() => require('./cjs/'), 'Cannot find module'], + ` + : '' + } + ); + + // .mjs + import * as mjs from './mjs/index.mjs'; + expectErrors( + [() => import('./mjs/index'), 'Cannot find module'], + [() => import('./mjs/'), 'Cannot find module'], + ${ + isCommonJs + ? ` + [() => require('./mjs/index'), 'Cannot find module'], + [() => require('./mjs/'), 'Cannot find module'], + ` + : '' + } + ); + + // Is TS loadable here? + // Import jsx? + + // Unsupported files + expectErrors( + [() => import('./file.txt'), 'Unknown file extension'], + [() => import(${JSON.stringify(wasmPathUrl)}), 'Unknown file extension'], + ${ + isCommonJs + ? ` + [() => require('./file.txt'), 'hello is not defined'], + [() => require(${JSON.stringify(wasmPath)}), 'Invalid or unexpected token'], + ` + : '' + } + ); + + console.log(JSON.stringify({ + js, + json, + cjs, + mjs, + pkgCommonjs, + pkgModule, + })); + + // Could .js import TS files? + `, + }); + onTestFinish(async () => await fixture.rm()); + + const p = await tsx(['import-from-js.js'], fixture.path); + onTestFail((error) => { + console.error(error); + console.log(p); + }); + expect(p.failed).toBe(false); + expect(p.stdout).toMatch(`"js":{"cjsContext":${isCommonJs},"default":1,"named":2}`); + expect(p.stdout).toMatch('"json":{"default":{"loaded":"json"},"loaded":"json"}'); + expect(p.stdout).toMatch('"cjs":{"default":{"named":"named"},"named":"named"}'); + expect(p.stdout).toMatch('"pkgModule":{"default":1,"named":2}'); + if (isCommonJs) { + expect(p.stdout).toMatch('"pkgCommonjs":{"default":1,"named":2}'); + } else { + expect(p.stdout).toMatch('"pkgCommonjs":{"default":{"default":1,"named":2}}'); + } + + // By "require()"ing an ESM file, it forces it to be compiled in a CJS context + expect(p.stdout).toMatch(`"mjs":{"mjsHasCjsContext":${isCommonJs}}`); + + expect(p.stderr).toBe(''); + }); + + describe('from .ts', async ({ test, onFinish }) => { + const fixture = await createFixture({ + ...files, + 'package.json': JSON.stringify({ type: packageType }), + + 'import-from-ts.ts': ` + import assert from 'assert'; + import { expectErrors } from './expect-errors'; + + // node: prefix + import 'node:fs'; + + // Dependencies + import * as pkgCommonjs from 'pkg-commonjs'; + import * as pkgModule from 'pkg-module'; + + // TODO: Test resolving TS files in dependencies (e.g. implicit extensions & export maps) + + // .js + import * as js from './js/index.js'; + // import * as js from './js/index.js?query=123'; TODO: Support query + import './js/index'; + import './js/'; + + // No double .default.default in Dynamic Import + import('./js/index.js').then(m => { + if (typeof m.default === 'object') { + assert( + !('default' in m.default), + 'Should not have double .default.default in Dynamic Import', + ); + } + }); + + // .json + import * as json from './json/index.json'; + import './json/index'; + import './json/'; + + // .cjs + import * as cjs from './cjs/index.cjs'; + expectErrors( + [() => import('./cjs/index'), 'Cannot find module'], + [() => import('./cjs/'), 'Cannot find module'], + ${ + isCommonJs + ? ` + [() => require('./cjs/index'), 'Cannot find module'], + [() => require('./cjs/'), 'Cannot find module'], + ` + : '' + } + ); + + // .mjs + import * as mjs from './mjs/index.mjs'; + expectErrors( + [() => import('./mjs/index'), 'Cannot find module'], + [() => import('./mjs/'), 'Cannot find module'], + ${ + isCommonJs + ? ` + [() => require('./mjs/index'), 'Cannot find module'], + [() => require('./mjs/'), 'Cannot find module'], + ` + : '' + } + ); + + // .ts + import './ts/index.ts'; + import './ts/index.js'; + // import './ts/index.jsx'; + import './ts/index'; + import './ts/'; + + // .jsx + import * as jsx from './jsx/index.jsx'; + // import './jsx/index.js'; + import './jsx/index'; + import './jsx/'; + + // .tsx + import './tsx/index.tsx'; + // import './tsx/index.js'; + import './tsx/index.jsx'; + import './tsx/index'; + import './tsx/'; + + // .cts + import './cts/index.cjs'; + expectErrors( + // TODO: + // [() => import('./cts/index.cts'), 'Cannot find module'], + [() => import('./cts/index'), 'Cannot find module'], + [() => import('./cts/'), 'Cannot find module'], + ${ + isCommonJs + ? ` + [() => require('./cts/index'), 'Cannot find module'], + [() => require('./cts/'), 'Cannot find module'], + ` + : '' + } + ); + // Loading via Node arg should not work via .cjs but with .cts + + // .mts + import './mts/index.mjs'; + expectErrors( + // TODO: + // [() => import('./mts/index.mts'), 'Cannot find module'], + [() => import('./mts/index'), 'Cannot find module'], + [() => import('./mts/'), 'Cannot find module'], + ${ + isCommonJs + ? ` + [() => require('./mts/index'), 'Cannot find module'], + [() => require('./mts/'), 'Cannot find module'], + ` + : '' + } + ); + // Loading via Node arg should not work via .mjs but with .mts + + // Unsupported files + expectErrors( + [() => import('./file.txt'), 'Unknown file extension'], + [() => import(${JSON.stringify(wasmPathUrl)}), 'Unknown file extension'], + ${ + isCommonJs + ? ` + [() => require('./file.txt'), 'hello is not defined'], + [() => require(${JSON.stringify(wasmPath)}), 'Invalid or unexpected token'], + ` + : '' + } + ); + + console.log(JSON.stringify({ + js, + json, + jsx, + cjs, + mjs, + pkgCommonjs, + pkgModule, + })); + `, + }); + onFinish(async () => await fixture.rm()); + + test('import all', async ({ onTestFail }) => { + const p = await tsx(['import-from-ts.ts'], fixture.path); + onTestFail((error) => { + console.error(error); + console.log(p); + }); + expect(p.failed).toBe(false); + expect(p.stdout).toMatch(`"js":{"cjsContext":${isCommonJs},"default":1,"named":2}`); + expect(p.stdout).toMatch('"json":{"default":{"loaded":"json"},"loaded":"json"}'); + expect(p.stdout).toMatch('"cjs":{"default":{"named":"named"},"named":"named"}'); + expect(p.stdout).toMatch(`"jsx":{"cjsContext":${isCommonJs},"jsx":[null,null,["div",null,"JSX"]]}`); + expect(p.stdout).toMatch('"pkgModule":{"default":1,"named":2}'); + if (isCommonJs) { + expect(p.stdout).toMatch('"pkgCommonjs":{"default":1,"named":2}'); + } else { + expect(p.stdout).toMatch('"pkgCommonjs":{"default":{"default":1,"named":2}}'); + } + + // By "require()"ing an ESM file, it forces it to be compiled in a CJS context + expect(p.stdout).toMatch(`"mjs":{"mjsHasCjsContext":${isCommonJs}}`); + expect(p.stderr).toBe(''); + }); + + test('tsconfig', async ({ onTestFail }) => { + const pTsconfig = await tsx(['index.tsx'], path.join(fixture.path, 'tsconfig')); + onTestFail((error) => { + console.error(error); + console.log(pTsconfig); + }); + expect(pTsconfig.failed).toBe(false); + expect(pTsconfig.stderr).toBe(''); + expect(pTsconfig.stdout).toBe(''); + }); + + test('custom tsconfig', async ({ onTestFail }) => { + const pTsconfigAllowJs = await tsx(['--tsconfig', 'tsconfig-allowJs.json', 'jsx.jsx'], path.join(fixture.path, 'tsconfig')); + onTestFail((error) => { + console.error(error); + console.log(pTsconfigAllowJs); + }); + expect(pTsconfigAllowJs.failed).toBe(true); + expect(pTsconfigAllowJs.stderr).toMatch('Error: No error thrown'); + expect(pTsconfigAllowJs.stdout).toBe(''); + }); + }); + }); + } + }); +}); diff --git a/tests/specs/typescript/cts.ts b/tests/specs/typescript/cts.ts deleted file mode 100644 index e68dc3ad2..000000000 --- a/tests/specs/typescript/cts.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { testSuite, expect } from 'manten'; -import semver from 'semver'; -import type { ExecaReturnValue } from 'execa'; -import type { NodeApis } from '../../utils/tsx'; -import nodeSupports from '../../utils/node-supports'; - -export default testSuite(async ({ describe }, node: NodeApis) => { - describe('.cts extension', ({ describe }) => { - function assertResults({ stdout, stderr }: ExecaReturnValue) { - expect(stdout).toMatch('loaded ts-ext-cts/index.cts'); - expect(stdout).toMatch('✔ has CJS context'); - expect(stdout).toMatch('✔ name in error'); - expect(stdout).toMatch('✔ sourcemaps'); - expect(stdout).toMatch('✔ has dynamic import'); - expect(stdout).toMatch('✔ resolves optional node prefix'); - expect(stdout).toMatch( - semver.satisfies(node.version, nodeSupports.testRunner) - ? '✔ resolves required node prefix' - : '✖ resolves required node prefix: Error', - ); - expect(stderr).not.toMatch(/loader/i); - } - - describe('full path', ({ test }) => { - const importPath = './lib/ts-ext-cts/index.cts'; - - test('Load', async () => { - const nodeProcess = await node.load(importPath); - assertResults(nodeProcess); - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath); - assertResults(nodeProcess); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath); - assertResults(nodeProcess); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - }); - }); - - describe('full path via .cjs', ({ test }) => { - const importPath = './lib/ts-ext-cts/index.cjs'; - - test('Load - should not work', async () => { - const nodeProcess = await node.load(importPath); - expect(nodeProcess.stderr).toMatch('Cannot find module'); - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath, { typescript: true }); - assertResults(nodeProcess); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath, { typescript: true }); - assertResults(nodeProcess); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - }); - }); - - describe('extensionless - should not work', ({ test }) => { - const importPath = './lib/ts-ext-cts/index'; - - test('Load', async () => { - const nodeProcess = await node.load(importPath); - expect(nodeProcess.stderr).toMatch('Cannot find module'); - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath); - expect(nodeProcess.stderr).toMatch('Cannot find module'); - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath); - expect(nodeProcess.stderr).toMatch('Cannot find module'); - }); - }); - - describe('directory - should not work', ({ test }) => { - const importPath = './lib/ts-ext-cts'; - - test('Load', async () => { - const nodeProcess = await node.load(importPath); - expect(nodeProcess.stderr).toMatch('Cannot find module'); - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath); - expect(nodeProcess.stderr).toMatch('Cannot find module'); - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath); - expect(nodeProcess.stderr).toMatch('Cannot find module'); - }); - }); - }); -}); diff --git a/tests/specs/typescript/dependencies.ts b/tests/specs/typescript/dependencies.ts deleted file mode 100644 index 987eea347..000000000 --- a/tests/specs/typescript/dependencies.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { testSuite, expect } from 'manten'; -import type { NodeApis } from '../../utils/tsx'; - -export default testSuite(async ({ describe }, node: NodeApis) => { - describe('Dependencies', ({ describe }) => { - describe('TypeScript dependency', ({ test }) => { - const output = '{"default":"ts default export","namedExport":"ts named export"}'; - - test('Import', async () => { - const nodeProcess = await node.import('package-module/ts.ts'); - expect(nodeProcess.stdout).toBe(output); - expect(nodeProcess.stderr).toBe(''); - }); - - test('Import extensionless', async () => { - const nodeProcess = await node.import('package-module/ts'); - expect(nodeProcess.stdout).toBe(output); - expect(nodeProcess.stderr).toBe(''); - }); - - test('Import', async () => { - const nodeProcess = await node.import('package-typescript-export'); - expect(nodeProcess.stdout).toBe(output); - expect(nodeProcess.stderr).toBe(''); - }); - }); - - describe('Export map', ({ test }) => { - const output = '{"default":"default export","namedExport":"named export"}'; - - test('Import', async () => { - const nodeProcess = await node.import('package-exports/index.js', { - typescript: true, - }); - expect(nodeProcess.stdout).toBe(output); - }); - }); - }); -}); diff --git a/tests/specs/typescript/index.ts b/tests/specs/typescript/index.ts deleted file mode 100644 index 2b947c276..000000000 --- a/tests/specs/typescript/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { testSuite } from 'manten'; -import type { NodeApis } from '../../utils/tsx'; - -export default testSuite(async ({ describe }, node: NodeApis) => { - describe('TypeScript', async ({ runTestSuite }) => { - runTestSuite(import('./ts'), node); - runTestSuite(import('./tsx'), node); - runTestSuite(import('./jsx'), node); - runTestSuite(import('./mts'), node); - runTestSuite(import('./cts'), node); - runTestSuite(import('./tsconfig'), node); - runTestSuite(import('./dependencies'), node); - }); -}); diff --git a/tests/specs/typescript/jsx.ts b/tests/specs/typescript/jsx.ts deleted file mode 100644 index 58215b009..000000000 --- a/tests/specs/typescript/jsx.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { testSuite, expect } from 'manten'; -import semver from 'semver'; -import type { ExecaReturnValue } from 'execa'; -import type { NodeApis } from '../../utils/tsx'; -import nodeSupports from '../../utils/node-supports'; - -export default testSuite(async ({ describe }, node: NodeApis) => { - describe('.jsx extension', ({ describe }) => { - function assertResults( - { stdout, stderr }: ExecaReturnValue, - cjsContext = false, - ) { - expect(stdout).toMatch('loaded ts-ext-jsx/index.jsx'); - expect(stdout).toMatch( - cjsContext - ? '✔ has CJS context' - : '✖ has CJS context', - ); - expect(stdout).toMatch('✔ name in error'); - expect(stdout).toMatch('✔ sourcemaps'); - expect(stdout).toMatch('✔ has dynamic import'); - expect(stdout).toMatch('✔ resolves optional node prefix'); - expect(stdout).toMatch( - semver.satisfies(node.version, nodeSupports.testRunner) - ? '✔ resolves required node prefix' - : '✖ resolves required node prefix: Error', - ); - expect(stderr).not.toMatch(/loader/i); - } - - describe('full path', ({ test }) => { - const importPath = './lib/ts-ext-jsx/index.jsx'; - - test('Load', async () => { - const nodeProcess = await node.load(importPath); - assertResults(nodeProcess, node.isCJS); - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath); - assertResults(nodeProcess, node.isCJS); - expect(nodeProcess.stdout).toMatch('{"default":["div",null,"hello world"]}'); - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath); - - // By "require()"ing an ESM file, it forces it to be compiled in a CJS context - assertResults(nodeProcess, true); - expect(nodeProcess.stdout).toMatch('{"default":["div",null,"hello world"]}'); - }); - }); - - describe('extensionless', ({ test }) => { - const importPath = './lib/ts-ext-jsx/index'; - - test('Load', async () => { - const nodeProcess = await node.load(importPath); - assertResults(nodeProcess, node.isCJS); - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath); - assertResults(nodeProcess, node.isCJS); - expect(nodeProcess.stdout).toMatch('{"default":["div",null,"hello world"]}'); - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath); - - // By "require()"ing an ESM file, it forces it to be compiled in a CJS context - assertResults(nodeProcess, true); - expect(nodeProcess.stdout).toMatch('{"default":["div",null,"hello world"]}'); - }); - }); - - describe('directory', ({ test }) => { - const importPath = './lib/ts-ext-jsx'; - - test('Load', async () => { - const nodeProcess = await node.load(importPath); - assertResults(nodeProcess, node.isCJS); - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath); - assertResults(nodeProcess, node.isCJS); - expect(nodeProcess.stdout).toMatch('{"default":["div",null,"hello world"]}'); - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath); - - // By "require()"ing an ESM file, it forces it to be compiled in a CJS context - assertResults(nodeProcess, true); - expect(nodeProcess.stdout).toMatch('{"default":["div",null,"hello world"]}'); - }); - }); - }); -}); diff --git a/tests/specs/typescript/mts.ts b/tests/specs/typescript/mts.ts deleted file mode 100644 index 6d1ad432e..000000000 --- a/tests/specs/typescript/mts.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { testSuite, expect } from 'manten'; -import semver from 'semver'; -import type { ExecaReturnValue } from 'execa'; -import type { NodeApis } from '../../utils/tsx'; -import nodeSupports from '../../utils/node-supports'; - -export default testSuite(async ({ describe }, node: NodeApis) => { - describe('.mts extension', ({ describe }) => { - function assertResults( - { stdout, stderr }: ExecaReturnValue, - cjsContext = false, - ) { - expect(stdout).toMatch('loaded ts-ext-mts/index.mts'); - expect(stdout).toMatch( - cjsContext - ? '✔ has CJS context' - : '✖ has CJS context', - ); - expect(stdout).toMatch('✔ name in error'); - expect(stdout).toMatch('✔ sourcemaps'); - expect(stdout).toMatch('✔ has dynamic import'); - expect(stdout).toMatch('✔ resolves optional node prefix'); - expect(stdout).toMatch( - semver.satisfies(node.version, nodeSupports.testRunner) - ? '✔ resolves required node prefix' - : '✖ resolves required node prefix: Error', - ); - expect(stderr).not.toMatch(/loader/i); - } - - describe('full path', ({ test }) => { - const importPath = './lib/ts-ext-mts/index.mts'; - - test('Load', async () => { - const nodeProcess = await node.load(importPath); - assertResults(nodeProcess); - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath); - assertResults(nodeProcess); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath); - - // By "require()"ing an ESM file, it forces it to be compiled in a CJS context - assertResults(nodeProcess, true); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - }); - }); - - describe('full path via .mjs', ({ test }) => { - const importPath = './lib/ts-ext-mts/index.mjs'; - - test('Load - should not work', async () => { - const nodeProcess = await node.load(importPath); - expect(nodeProcess.stderr).toMatch('Cannot find module'); - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath, { typescript: true }); - assertResults(nodeProcess); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath, { typescript: true }); - - // By "require()"ing an ESM file, it forces it to be compiled in a CJS context - assertResults(nodeProcess, true); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - }); - }); - - describe('extensionless - should not work', ({ test }) => { - const importPath = './lib/ts-ext-mts/index'; - - test('Load', async () => { - const nodeProcess = await node.load(importPath); - expect(nodeProcess.stderr).toMatch('Cannot find module'); - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath); - expect(nodeProcess.stderr).toMatch('Cannot find module'); - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath); - expect(nodeProcess.stderr).toMatch('Cannot find module'); - }); - }); - - describe('directory - should not work', ({ test }) => { - const importPath = './lib/ts-ext-mts'; - - test('Load', async () => { - const nodeProcess = await node.load(importPath); - expect(nodeProcess.stderr).toMatch('Cannot find module'); - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath); - expect(nodeProcess.stderr).toMatch('Cannot find module'); - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath); - expect(nodeProcess.stderr).toMatch('Cannot find module'); - }); - }); - }); -}); diff --git a/tests/specs/typescript/ts.ts b/tests/specs/typescript/ts.ts deleted file mode 100644 index d9cbd37a5..000000000 --- a/tests/specs/typescript/ts.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { testSuite, expect } from 'manten'; -import semver from 'semver'; -import type { ExecaReturnValue } from 'execa'; -import type { NodeApis } from '../../utils/tsx'; -import nodeSupports from '../../utils/node-supports'; - -export default testSuite(async ({ describe }, node: NodeApis) => { - describe('.ts extension', ({ describe }) => { - function assertResults( - { stdout, stderr }: ExecaReturnValue, - cjsContext = false, - loadedMessage = 'loaded ts-ext-ts/index.ts', - ) { - expect(stdout).toMatch(loadedMessage); - expect(stdout).toMatch( - cjsContext - ? '✔ has CJS context' - : '✖ has CJS context', - ); - expect(stdout).toMatch('✔ name in error'); - expect(stdout).toMatch('✔ sourcemaps'); - expect(stdout).toMatch('✔ has dynamic import'); - expect(stdout).toMatch('✔ resolves optional node prefix'); - expect(stdout).toMatch( - semver.satisfies(node.version, nodeSupports.testRunner) - ? '✔ resolves required node prefix' - : '✖ resolves required node prefix: Error', - ); - expect(stderr).not.toMatch(/loader/i); - } - - describe('full path', ({ test }) => { - const importPath = './lib/ts-ext-ts/index.ts'; - - test('Load', async () => { - const nodeProcess = await node.load(importPath); - assertResults(nodeProcess, node.isCJS); - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath); - assertResults(nodeProcess, node.isCJS); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath); - - // By "require()"ing an ESM file, it forces it to be compiled in a CJS context - assertResults(nodeProcess, true); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - }); - - test('Require flag', async () => { - const nodeProcess = await node.requireFlag(importPath); - assertResults(nodeProcess, true); - }); - }); - - describe('full path via .js', ({ test }) => { - const importPath = './lib/ts-ext-ts/index.js'; - - test('Load', async () => { - const nodeProcess = await node.load(importPath); - expect(nodeProcess.stderr).toMatch('Cannot find module'); - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath, { typescript: true }); - assertResults(nodeProcess, node.isCJS); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath, { typescript: true }); - - // By "require()"ing an ESM file, it forces it to be compiled in a CJS context - assertResults(nodeProcess, true); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - }); - }); - - describe('extensionless', ({ test }) => { - const importPath = './lib/ts-ext-ts/index'; - - test('Load', async () => { - const nodeProcess = await node.load(importPath); - assertResults(nodeProcess, node.isCJS); - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath); - assertResults(nodeProcess, node.isCJS); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath); - - // By "require()"ing an ESM file, it forces it to be compiled in a CJS context - assertResults(nodeProcess, true); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - }); - }); - - describe('extensionless with subextension', ({ test }) => { - const importPath = './lib/ts-ext-ts/index.tsx'; - - test('Load', async () => { - const nodeProcess = await node.load(importPath); - assertResults(nodeProcess, node.isCJS, 'loaded ts-ext-ts/index.tsx.ts'); - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath); - assertResults(nodeProcess, node.isCJS, 'loaded ts-ext-ts/index.tsx.ts'); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath); - - // By "require()"ing an ESM file, it forces it to be compiled in a CJS context - assertResults(nodeProcess, true, 'loaded ts-ext-ts/index.tsx.ts'); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - }); - }); - - describe('directory', ({ test }) => { - const importPath = './lib/ts-ext-ts'; - - test('Load', async () => { - const nodeProcess = await node.load(importPath); - assertResults(nodeProcess, node.isCJS); - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath); - assertResults(nodeProcess, node.isCJS); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath); - - // By "require()"ing an ESM file, it forces it to be compiled in a CJS context - assertResults(nodeProcess, true); - expect(nodeProcess.stdout).toMatch('{"default":1234}'); - }); - }); - }); -}); diff --git a/tests/specs/typescript/tsconfig.ts b/tests/specs/typescript/tsconfig.ts deleted file mode 100644 index 9b9d70835..000000000 --- a/tests/specs/typescript/tsconfig.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { testSuite, expect } from 'manten'; -import type { NodeApis } from '../../utils/tsx'; - -export default testSuite(async ({ describe }, node: NodeApis) => { - describe('tsconfig', ({ test, describe }) => { - test('jsxFactory & jsxFragmentFactory', async () => { - const nodeProcess = await node.load('./src/tsx.tsx', { - cwd: './tsconfig', - }); - expect(nodeProcess.stdout).toBe('div null hello world\nnull null goodbye world'); - }); - - test('Custom tsconfig.json path', async () => { - const nodeProcess = await node.load('./src/tsx.tsx', { - cwd: './tsconfig', - args: ['--tsconfig', './tsconfig-custom/tsconfig.custom-name.json'], - }); - expect(nodeProcess.stdout).toBe(''); - expect(nodeProcess.stderr).toBe('div null hello world\nnull null goodbye world'); - }); - - describe('paths', ({ test, describe }) => { - test('resolves baseUrl', async () => { - const nodeProcess = await node.load('./src/base-url.ts', { - cwd: './tsconfig', - }); - expect(nodeProcess.stdout).toBe('resolve-target'); - }); - - test('resolves paths exact match', async () => { - const nodeProcess = await node.load('./src/paths-exact-match.ts', { - cwd: './tsconfig', - }); - expect(nodeProcess.stdout).toBe('resolve-target'); - }); - - test('resolves paths prefix', async () => { - const nodeProcess = await node.load('./src/paths-prefix-match.ts', { - cwd: './tsconfig', - }); - expect(nodeProcess.stdout).toBe('nested-resolve-target'); - }); - - test('resolves paths suffix', async () => { - const nodeProcess = await node.load('./src/paths-suffix-match.ts', { - cwd: './tsconfig', - }); - expect(nodeProcess.stdout).toBe('nested-resolve-target'); - }); - - describe('dependency', ({ test }) => { - test('resolve current directory', async () => { - const nodeProcess = await node.load('./dependency-resolve-current-directory', { - cwd: './tsconfig', - }); - expect(nodeProcess.stdout).toBe('resolved'); - }); - - test('should not resolve baseUrl', async () => { - const nodeProcess = await node.load('./dependency-should-not-resolve-baseUrl', { - cwd: './tsconfig', - }); - expect(nodeProcess.stdout).toBe('resolved'); - }); - - test('should not resolve paths', async () => { - const nodeProcess = await node.load('./dependency-should-not-resolve-paths', { - cwd: './tsconfig', - }); - expect(nodeProcess.stdout).toBe('resolved'); - }); - }); - }); - }); -}); diff --git a/tests/specs/typescript/tsx.ts b/tests/specs/typescript/tsx.ts deleted file mode 100644 index 2f7a847c7..000000000 --- a/tests/specs/typescript/tsx.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { testSuite, expect } from 'manten'; -import semver from 'semver'; -import type { ExecaReturnValue } from 'execa'; -import type { NodeApis } from '../../utils/tsx'; -import nodeSupports from '../../utils/node-supports'; - -export default testSuite(async ({ describe }, node: NodeApis) => { - describe('.tsx extension', ({ describe }) => { - function assertResults( - { stdout, stderr }: ExecaReturnValue, - cjsContext = false, - ) { - expect(stdout).toMatch('loaded ts-ext-tsx/index.tsx'); - expect(stdout).toMatch( - cjsContext - ? '✔ has CJS context' - : '✖ has CJS context', - ); - expect(stdout).toMatch('✔ name in error'); - expect(stdout).toMatch('✔ sourcemaps'); - expect(stdout).toMatch('✔ has dynamic import'); - expect(stdout).toMatch('✔ resolves optional node prefix'); - expect(stdout).toMatch( - semver.satisfies(node.version, nodeSupports.testRunner) - ? '✔ resolves required node prefix' - : '✖ resolves required node prefix: Error', - ); - expect(stderr).not.toMatch(/loader/i); - } - - describe('full path', ({ test }) => { - const importPath = './lib/ts-ext-tsx/index.tsx'; - - test('Load', async () => { - const nodeProcess = await node.load(importPath); - assertResults(nodeProcess, node.isCJS); - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath); - assertResults(nodeProcess, node.isCJS); - expect(nodeProcess.stdout).toMatch('{"default":["div",null,"hello world"]}'); - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath); - - // By "require()"ing an ESM file, it forces it to be compiled in a CJS context - assertResults(nodeProcess, true); - expect(nodeProcess.stdout).toMatch('{"default":["div",null,"hello world"]}'); - }); - }); - - describe('extensionless', ({ test }) => { - const importPath = './lib/ts-ext-tsx/index'; - - test('Load', async () => { - const nodeProcess = await node.load(importPath); - assertResults(nodeProcess, node.isCJS); - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath); - assertResults(nodeProcess, node.isCJS); - expect(nodeProcess.stdout).toMatch('{"default":["div",null,"hello world"]}'); - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath); - - // By "require()"ing an ESM file, it forces it to be compiled in a CJS context - assertResults(nodeProcess, true); - expect(nodeProcess.stdout).toMatch('{"default":["div",null,"hello world"]}'); - }); - }); - - describe('directory', ({ test }) => { - const importPath = './lib/ts-ext-tsx'; - - test('Load', async () => { - const nodeProcess = await node.load(importPath); - assertResults(nodeProcess, node.isCJS); - }); - - test('Import', async () => { - const nodeProcess = await node.import(importPath); - assertResults(nodeProcess, node.isCJS); - expect(nodeProcess.stdout).toMatch('{"default":["div",null,"hello world"]}'); - }); - - test('Require', async () => { - const nodeProcess = await node.require(importPath); - - // By "require()"ing an ESM file, it forces it to be compiled in a CJS context - assertResults(nodeProcess, true); - expect(nodeProcess.stdout).toMatch('{"default":["div",null,"hello world"]}'); - }); - }); - }); -}); diff --git a/tests/specs/wasm.ts b/tests/specs/wasm.ts deleted file mode 100644 index 823e07211..000000000 --- a/tests/specs/wasm.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { testSuite, expect } from 'manten'; -import type { NodeApis } from '../utils/tsx'; - -export default testSuite(async ({ describe }, node: NodeApis) => { - describe('Load WASM', ({ test }) => { - const importPath = './lib/wasm/index.js'; - - test('Unsupported extension error', async () => { - const nodeProcess = await node.load(importPath); - - expect(nodeProcess.exitCode).toBe(1); - - if (node.isCJS) { - expect(nodeProcess.stderr).toMatch('Invalid or unexpected token'); - } else { - expect(nodeProcess.stderr).toMatch('ERR_UNKNOWN_FILE_EXTENSION'); - } - }); - - test('Loads with experimental flag', async () => { - const nodeProcess = await node.load(importPath, { - args: ['--experimental-wasm-modules'], - }); - - if (node.isCJS) { - expect(nodeProcess.exitCode).toBe(1); - expect(nodeProcess.stderr).toMatch('Invalid or unexpected token'); - } else { - expect(nodeProcess.exitCode).toBe(0); - expect(nodeProcess.stdout).toBe('1234'); - } - }); - }); -}); diff --git a/tests/specs/watch.ts b/tests/specs/watch.ts index 3ce64dffa..e66afcb1d 100644 --- a/tests/specs/watch.ts +++ b/tests/specs/watch.ts @@ -10,25 +10,48 @@ type MaybePromise = T | Promise; const interact = async ( stdout: Readable, actions: ((data: string) => MaybePromise)[], + timeout: number, ) => { + const startTime = Date.now(); + const logs: [time: number, string][] = []; + let currentAction = actions.shift(); - const buffers: Buffer[] = []; + setTimeout(timeout).then( + () => { + if (currentAction) { + console.error(`Timeout ${timeout}ms exceeded:`); + console.log(logs); + } + }, + () => {}, + ); + while (currentAction) { for await (const [chunk] of on(stdout, 'data')) { - buffers.push(chunk); - if (await currentAction(chunk.toString())) { + const chunkString = chunk.toString(); + logs.push([ + Date.now() - startTime, + chunkString, + ]); + + const gotoNextAction = await currentAction(chunkString); + if (gotoNextAction) { currentAction = actions.shift(); break; } } } - - return Buffer.concat(buffers).toString(); }; -export default testSuite(async ({ describe }, fixturePath: string) => { - describe('watch', ({ test, describe }) => { +export default testSuite(async ({ describe }) => { + describe('watch', async ({ test, describe, onFinish }) => { + const fixture = await createFixture({ + // Unnecessary TS to test syntax + 'log-argv.ts': 'console.log(JSON.stringify(process.argv) as string)', + }); + onFinish(async () => await fixture.rm()); + test('require file path', async () => { const tsxProcess = await tsx({ args: ['watch'], @@ -39,7 +62,7 @@ export default testSuite(async ({ describe }, fixturePath: string) => { test('watch files for changes', async ({ onTestFinish }) => { let initialValue = Date.now(); - const fixture = await createFixture({ + const fixtureWatch = await createFixture({ 'package.json': JSON.stringify({ type: 'module', }), @@ -49,13 +72,14 @@ export default testSuite(async ({ describe }, fixturePath: string) => { `, 'value.js': `export const value = ${initialValue};`, }); - onTestFinish(async () => await fixture.rm()); + onTestFinish(async () => await fixtureWatch.rm()); const tsxProcess = tsx({ args: [ 'watch', - path.join(fixture.path, 'index.js'), + 'index.js', ], + cwd: fixtureWatch.path, }); await interact( @@ -64,12 +88,13 @@ export default testSuite(async ({ describe }, fixturePath: string) => { async (data) => { if (data.includes(`${initialValue}\n`)) { initialValue = Date.now(); - await fixture.writeFile('value.js', `export const value = ${initialValue};`); + await fixtureWatch.writeFile('value.js', `export const value = ${initialValue};`); return true; } }, data => data.includes(`${initialValue}\n`), ], + 5000, ); tsxProcess.kill(); @@ -81,8 +106,9 @@ export default testSuite(async ({ describe }, fixturePath: string) => { const tsxProcess = tsx({ args: [ 'watch', - path.join(fixturePath, 'log-argv.ts'), + 'log-argv.ts', ], + cwd: fixture.path, }); await interact( @@ -96,6 +122,7 @@ export default testSuite(async ({ describe }, fixturePath: string) => { }, data => data.includes('log-argv.ts'), ], + 5000, ); tsxProcess.kill(); @@ -109,14 +136,16 @@ export default testSuite(async ({ describe }, fixturePath: string) => { const tsxProcess = tsx({ args: [ 'watch', - path.join(fixturePath, 'log-argv.ts'), + 'log-argv.ts', '--some-flag', ], + cwd: fixture.path, }); await interact( tsxProcess.stdout!, [data => data.startsWith('["')], + 5000, ); tsxProcess.kill(); @@ -125,8 +154,8 @@ export default testSuite(async ({ describe }, fixturePath: string) => { expect(all).toMatch('"--some-flag"'); }, 10_000); - test('wait for exit', async ({ onTestFinish }) => { - const fixture = await createFixture({ + test('wait for exit', async ({ onTestFinish, onTestFail }) => { + const fixtureExit = await createFixture({ 'index.js': ` console.log('start'); const sleepSync = (delay) => { @@ -140,13 +169,26 @@ export default testSuite(async ({ describe }, fixturePath: string) => { `, }); - onTestFinish(async () => await fixture.rm()); - const tsxProcess = tsx({ args: [ 'watch', - path.join(fixture.path, 'index.js'), + 'index.js', ], + cwd: fixtureExit.path, + }); + + onTestFail(async () => { + if (tsxProcess.exitCode === null) { + console.log('Force killing hanging process\n\n'); + tsxProcess.kill('SIGKILL'); + console.log({ + tsxProcess: await tsxProcess, + }); + } + }); + + onTestFinish(async () => { + await fixtureExit.rm(); }); await interact( @@ -160,6 +202,7 @@ export default testSuite(async ({ describe }, fixturePath: string) => { }, data => data.includes('end\n'), ], + 5000, ); tsxProcess.kill(); @@ -179,35 +222,41 @@ export default testSuite(async ({ describe }, fixturePath: string) => { expect(tsxProcess.stderr).toBe(''); }); - test('passes down --help to file', async () => { + test('passes down --help to file', async ({ onTestFail }) => { const tsxProcess = tsx({ args: [ 'watch', - path.join(fixturePath, 'log-argv.ts'), + 'log-argv.ts', '--help', ], + cwd: fixture.path, }); await interact( tsxProcess.stdout!, [data => data.startsWith('["')], + 5000, ); tsxProcess.kill(); const { all } = await tsxProcess; + onTestFail(() => { + console.log(all); + }); + expect(all).toMatch('"--help"'); - }, 5000); + }, 10_000); }); describe('ignore', ({ test }) => { - test('file path & glob', async ({ onTestFinish }) => { + test('file path & glob', async ({ onTestFinish, onTestFail }) => { const entryFile = 'index.js'; const fileA = 'file-a.js'; const fileB = 'directory/file-b.js'; const depA = 'node_modules/a/index.js'; - const fixture = await createFixture({ + const fixtureGlob = await createFixture({ [fileA]: 'export default "logA"', [fileB]: 'export default "logB"', [depA]: 'export default "logC"', @@ -219,50 +268,63 @@ export default testSuite(async ({ describe }, fixturePath: string) => { `.trim(), }); - onTestFinish(async () => await fixture.rm()); + onTestFinish(async () => await fixtureGlob.rm()); const tsxProcess = tsx({ - cwd: fixture.path, + cwd: fixtureGlob.path, args: [ 'watch', '--clear-screen=false', `--ignore=${fileA}`, - `--ignore=${path.join(fixture.path, 'directory/*')}`, + `--ignore=${path.join(fixtureGlob.path, 'directory/*')}`, entryFile, ], }); - const negativeSignal = '"fail"'; + + onTestFail(async () => { + // If timed out, force kill process + if (tsxProcess.exitCode === null) { + console.log('Force killing hanging process\n\n'); + tsxProcess.kill(); + console.log({ + tsxProcess: await tsxProcess, + }); + } + }); + + const negativeSignal = 'fail'; await interact( tsxProcess.stdout!, [ async (data) => { - if (data.includes('fail')) { + if (data.includes(negativeSignal)) { throw new Error('should not log ignored file'); } if (data === 'logA logB logC\n') { // These changes should not trigger a re-run await Promise.all([ - fixture.writeFile(fileA, `export default ${negativeSignal}`), - fixture.writeFile(fileB, `export default ${negativeSignal}`), - fixture.writeFile(depA, `export default ${negativeSignal}`), + fixtureGlob.writeFile(fileA, `export default "${negativeSignal}"`), + fixtureGlob.writeFile(fileB, `export default "${negativeSignal}"`), + fixtureGlob.writeFile(depA, `export default "${negativeSignal}"`), ]); - await setTimeout(1500); - await fixture.writeFile(entryFile, 'console.log("TERMINATE")'); + await setTimeout(1000); + fixtureGlob.writeFile(entryFile, 'console.log("TERMINATE")'); return true; } }, data => data === 'TERMINATE\n', ], + 9000, ); tsxProcess.kill(); - const { all, stderr } = await tsxProcess; - expect(all).not.toMatch('fail'); - expect(stderr).toBe(''); + const p = await tsxProcess; + expect(p.all).not.toMatch('fail'); + expect(p.stderr).toBe(''); }, 10_000); }); }); diff --git a/tests/utils/expect-match-in-order.ts b/tests/utils/expect-match-in-order.ts new file mode 100644 index 000000000..0cf95a403 --- /dev/null +++ b/tests/utils/expect-match-in-order.ts @@ -0,0 +1,37 @@ +type Searchable = string | RegExp; + +const stringify = ( + value: { toString(): string }, +) => JSON.stringify( + value.toString(), // Might be RegExp which requires .toString() +); + +const expectedError = ( + expected: Searchable, + before?: Searchable, +) => new Error(`Expected ${stringify(expected)} ${before ? `to be after ${stringify(before)}` : ''}`); + +export const expectMatchInOrder = ( + subject: string, + expectedOrder: Searchable[], +) => { + let remaining = subject; + for (let i = 0; i < expectedOrder.length; i += 1) { + const previousElement = i > 0 ? expectedOrder[i - 1] : undefined; + const currentElement = expectedOrder[i]; + + if (typeof currentElement === 'string') { + const index = remaining.indexOf(currentElement); + if (index === -1) { + throw expectedError(currentElement, previousElement); + } + remaining = remaining.slice(index + currentElement.length); + } else { + const match = remaining.match(currentElement); + if (!match) { + throw expectedError(currentElement, previousElement); + } + remaining = remaining.slice(match.index! + match[0].length); + } + } +}; diff --git a/tests/utils/pty-shell/index.ts b/tests/utils/pty-shell/index.ts index 527523719..eeef888f2 100644 --- a/tests/utils/pty-shell/index.ts +++ b/tests/utils/pty-shell/index.ts @@ -22,7 +22,7 @@ const getStdin = ( export const ptyShell = ( stdins: StdInArray, -) => new Promise((resolve, reject) => { +) => new Promise((resolve, reject) => { const childProcess = execaNode( fileURLToPath(new URL('node-pty.mjs', import.meta.url)), [shell], diff --git a/tests/utils/tsx.ts b/tests/utils/tsx.ts index 00152509f..ea82587f0 100644 --- a/tests/utils/tsx.ts +++ b/tests/utils/tsx.ts @@ -1,5 +1,4 @@ import path from 'path'; -import fs from 'fs/promises'; import { fileURLToPath } from 'url'; import { execaNode } from 'execa'; import getNode from 'get-node'; @@ -21,6 +20,7 @@ export const tsx = ( { env: { ESBK_DISABLE_CACHE: '1', + DEBUG: '1', }, nodePath: options.nodePath, nodeOptions: [], @@ -30,10 +30,9 @@ export const tsx = ( }, ); -export async function createNode( +export const createNode = async ( nodeVersion: string, - fixturePath: string, -) { +) => { console.log('Getting node', nodeVersion); const startTime = Date.now(); const node = await getNode(nodeVersion, { @@ -43,125 +42,25 @@ export async function createNode( return { version: node.version, - packageType: '', - get isCJS() { - return this.packageType === 'commonjs'; - }, - tsx( - options: Options, - ) { - return tsx({ - ...options, - nodePath: node.path, - }); - }, - load( - filePath: string, - options?: { - cwd?: string; - args?: string[]; - }, - ) { - return this.tsx( - { - args: [ - ...(options?.args ?? []), - filePath, - ], - cwd: path.join(fixturePath, options?.cwd ?? ''), + tsx: ( + args: string[], + cwd?: string, + ) => execaNode( + tsxPath, + args, + { + cwd, + env: { + ESBK_DISABLE_CACHE: '1', + DEBUG: '1', }, - ); - }, - import( - filePath: string, - options?: { - typescript?: boolean; - }, - ) { - return this.tsx({ - args: [ - `./import-file${options?.typescript ? '.ts' : '.js'}`, - filePath, - ], - cwd: fixturePath, - }); - }, - require( - filePath: string, - options?: { - typescript?: boolean; - }, - ) { - return this.tsx({ - args: [ - `./require-file${options?.typescript ? '.cts' : '.cjs'}`, - filePath, - ], - cwd: fixturePath, - }); - }, - requireFlag( - filePath: string, - ) { - return this.tsx({ - args: [ - '--eval', - 'null', - '--require', - filePath, - ], - cwd: fixturePath, - }); - }, - - loadFile( - cwd: string, - filePath: string, - options?: { - args?: string[]; + nodePath: node.path, + nodeOptions: [], + reject: false, + all: true, }, - ) { - return this.tsx( - { - args: [ - ...(options?.args ?? []), - filePath, - ], - cwd, - }, - ); - }, - - async importFile( - cwd: string, - importFrom: string, - fileExtension = '.mjs', - ) { - const fileName = `_${Math.random().toString(36).slice(2)}${fileExtension}`; - const filePath = path.resolve(cwd, fileName); - await fs.writeFile(filePath, `import * as _ from '${importFrom}';console.log(_)`); - try { - return await this.loadFile(cwd, filePath); - } finally { - await fs.rm(filePath); - } - }, - - async requireFile( - cwd: string, - requireFrom: string, - fileExtension = '.cjs', - ) { - const fileName = `_${Math.random().toString(36).slice(2)}${fileExtension}`; - const filePath = path.resolve(cwd, fileName); - await fs.writeFile(filePath, `const _ = require('${requireFrom}');console.log(_)`); - try { - return await this.loadFile(cwd, filePath); - } finally { - await fs.rm(filePath); - } - }, + ), }; -} +}; export type NodeApis = Awaited>;