Support .cts and .mts

This commit is contained in:
Mark Wubben 2022-12-10 11:38:40 +01:00
parent 34da7bf008
commit 4ec638c4d0
21 changed files with 132 additions and 58 deletions

View file

@ -47,7 +47,7 @@ You can enable compilation via the `compile` property. If `false`, AVA will assu
Output files are expected to have the `.js` extension.
AVA searches your entire project for `*.js`, `*.cjs`, `*.mjs` and `*.ts` files (or other extensions you've configured). It will ignore such files found in the `rewritePaths` targets (e.g. `build/`). If you use more specific paths, for instance `build/main/`, you may need to change AVA's `files` configuration to ignore other directories.
AVA searches your entire project for `*.js`, `*.cjs`, `*.mjs`, `*.ts`, `*.cts` and `*.mts` files (or other extensions you've configured). It will ignore such files found in the `rewritePaths` targets (e.g. `build/`). If you use more specific paths, for instance `build/main/`, you may need to change AVA's `files` configuration to ignore other directories.
## ES Modules

View file

@ -83,7 +83,7 @@ export default function typescriptProvider({negotiateProtocol}) {
validate(config, configProperties);
const {
extensions = ['ts'],
extensions = ['ts', 'cts', 'mts'],
rewritePaths: relativeRewritePaths,
compile,
} = config;
@ -118,7 +118,7 @@ export default function typescriptProvider({negotiateProtocol}) {
return rewritePaths.some(([from]) => filePath.startsWith(from));
},
resolveTestFile(testfile) {
resolveTestFile(testfile) { // Used under AVA 3.2 protocol by legacy watcher implementation.
if (!testFileExtension.test(testfile)) {
return testfile;
}
@ -129,8 +129,14 @@ export default function typescriptProvider({negotiateProtocol}) {
}
const [from, to] = rewrite;
// TODO: Support JSX preserve mode — https://www.typescriptlang.org/docs/handbook/jsx.html
return `${to}${testfile.slice(from.length)}`.replace(testFileExtension, '.js');
let newExtension = '.js';
if (testfile.endsWith('.cts')) {
newExtension = '.cjs';
} else if (testfile.endsWith('.mts')) {
newExtension = '.mjs';
}
return `${to}${testfile.slice(from.length)}`.replace(testFileExtension, newExtension);
},
updateGlobs({filePatterns, ignoredByWatcherPatterns}) {
@ -142,7 +148,11 @@ export default function typescriptProvider({negotiateProtocol}) {
],
ignoredByWatcherPatterns: [
...ignoredByWatcherPatterns,
...Object.values(relativeRewritePaths).map(to => `${to}**/*.js.map`),
...Object.values(relativeRewritePaths).flatMap(to => [
`${to}**/*.js.map`,
`${to}**/*.cjs.map`,
`${to}**/*.mjs.map`,
]),
],
};
},
@ -150,7 +160,7 @@ export default function typescriptProvider({negotiateProtocol}) {
},
worker({extensionsToLoadAsModules, state: {extensions, rewritePaths}}) {
const useImport = extensionsToLoadAsModules.includes('js');
const importJs = extensionsToLoadAsModules.includes('js');
const testFileExtension = new RegExp(`\\.(${extensions.map(ext => escapeStringRegexp(ext)).join('|')})$`);
return {
@ -160,8 +170,18 @@ export default function typescriptProvider({negotiateProtocol}) {
async load(ref, {requireFn}) {
const [from, to] = rewritePaths.find(([from]) => ref.startsWith(from));
// TODO: Support JSX preserve mode — https://www.typescriptlang.org/docs/handbook/jsx.html
const rewritten = `${to}${ref.slice(from.length)}`.replace(testFileExtension, '.js');
let rewritten = `${to}${ref.slice(from.length)}`;
let useImport = true;
if (ref.endsWith('.cts')) {
rewritten = rewritten.replace(/\.cts$/, '.cjs');
useImport = false;
} else if (ref.endsWith('.mts')) {
rewritten = rewritten.replace(/\.mts$/, '.mjs');
} else {
rewritten = rewritten.replace(testFileExtension, '.js');
useImport = importJs;
}
return useImport ? import(pathToFileURL(rewritten)) : requireFn(rewritten); // eslint-disable-line node/no-unsupported-features/es-syntax
},
};

View file

@ -52,7 +52,8 @@
},
"xo": {
"ignores": [
"test/broken-fixtures"
"test/broken-fixtures",
"test/fixtures/**/compiled/**"
]
}
}

View file

@ -1,33 +0,0 @@
import path from 'node:path';
import {fileURLToPath} from 'node:url';
import test from 'ava';
import execa from 'execa';
import createProviderMacro from './_with-provider.js';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const withProvider = createProviderMacro('ava-3.2', '3.2.0', path.join(__dirname, 'fixtures'));
const setup = async provider => ({
state: await provider.main({
config: {
rewritePaths: {
'esm/': 'esm/',
},
compile: false,
},
}).compile(),
});
test('worker(): import ESM', withProvider, async (t, provider) => {
const {state} = await setup(provider);
const {stdout, stderr} = await execa.node(
path.join(__dirname, 'fixtures/install-and-load'),
[JSON.stringify({extensionsToLoadAsModules: ['js'], state}), path.join(__dirname, 'fixtures/esm', 'index.ts')],
{cwd: path.join(__dirname, 'fixtures')},
);
if (stderr.length > 0) {
t.log(stderr);
}
t.snapshot(stdout);
});

View file

@ -1 +0,0 @@
console.log('logged in fixtures/esm/index.js');

View file

@ -1 +0,0 @@
console.log('logged in fixtures/esm/index.ts');

3
test/fixtures/load/compiled/index.cjs vendored Normal file
View file

@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
console.log('logged in fixtures/load/index.cts');

2
test/fixtures/load/compiled/index.js vendored Normal file
View file

@ -0,0 +1,2 @@
console.log('logged in fixtures/load/index.ts');
export {};

2
test/fixtures/load/compiled/index.mjs vendored Normal file
View file

@ -0,0 +1,2 @@
console.log('logged in fixtures/load/index.mts');
export {};

1
test/fixtures/load/index.cts vendored Normal file
View file

@ -0,0 +1 @@
console.log('logged in fixtures/load/index.cts');

1
test/fixtures/load/index.mts vendored Normal file
View file

@ -0,0 +1 @@
console.log('logged in fixtures/load/index.mts');

1
test/fixtures/load/index.ts vendored Normal file
View file

@ -0,0 +1 @@
console.log('logged in fixtures/load/index.ts');

View file

@ -1,5 +1,6 @@
{
"compilerOptions": {
"module": "Node16",
"outDir": "compiled"
},
"include": [

61
test/load.js Normal file
View file

@ -0,0 +1,61 @@
import path from 'node:path';
import {fileURLToPath} from 'node:url';
import test from 'ava';
import execa from 'execa';
import createProviderMacro from './_with-provider.js';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const withProvider = createProviderMacro('ava-3.2', '3.2.0', path.join(__dirname, 'fixtures'));
const setup = async provider => ({
state: await provider.main({
config: {
rewritePaths: {
'load/': 'load/compiled/',
},
compile: false,
},
}).compile(),
});
test('worker(): load .cts', withProvider, async (t, provider) => {
const {state} = await setup(provider);
const {stdout, stderr} = await execa.node(
path.join(__dirname, 'fixtures/install-and-load'),
[JSON.stringify({state}), path.join(__dirname, 'fixtures/load', 'index.cts')],
{cwd: path.join(__dirname, 'fixtures')},
);
if (stderr.length > 0) {
t.log(stderr);
}
t.snapshot(stdout);
});
test('worker(): load .mts', withProvider, async (t, provider) => {
const {state} = await setup(provider);
const {stdout, stderr} = await execa.node(
path.join(__dirname, 'fixtures/install-and-load'),
[JSON.stringify({state}), path.join(__dirname, 'fixtures/load', 'index.mts')],
{cwd: path.join(__dirname, 'fixtures')},
);
if (stderr.length > 0) {
t.log(stderr);
}
t.snapshot(stdout);
});
test('worker(): load .ts', withProvider, async (t, provider) => {
const {state} = await setup(provider);
const {stdout, stderr} = await execa.node(
path.join(__dirname, 'fixtures/install-and-load'),
[JSON.stringify({extensionsToLoadAsModules: ['js'], state}), path.join(__dirname, 'fixtures/load', 'index.ts')],
{cwd: path.join(__dirname, 'fixtures')},
);
if (stderr.length > 0) {
t.log(stderr);
}
t.snapshot(stdout);
});

View file

@ -53,8 +53,8 @@ test('main() config validation: rewrite paths must end in a /', withProvider, (t
validateConfig(t, provider, {rewritePaths: {'src/': 'build', compile: false}});
});
test('main() extensions: defaults to [\'ts\']', withProvider, (t, provider) => {
t.deepEqual(provider.main({config: {rewritePaths: {'src/': 'build/'}, compile: false}}).extensions, ['ts']);
test('main() extensions: defaults to [\'ts\', \'cts\', \'mts\']', withProvider, (t, provider) => {
t.deepEqual(provider.main({config: {rewritePaths: {'src/': 'build/'}, compile: false}}).extensions, ['ts', 'cts', 'mts']);
});
test('main() extensions: returns configured extensions', withProvider, (t, provider) => {
@ -76,6 +76,8 @@ test('main() ignoreChange()', withProvider, (t, provider) => {
test('main() resolveTestfile()', withProvider, (t, provider) => {
const main = provider.main({config: {rewritePaths: {'src/': 'build/'}, compile: false}});
t.is(main.resolveTestFile(path.join(__dirname, 'src/foo.ts')), path.join(__dirname, 'build/foo.js'));
t.is(main.resolveTestFile(path.join(__dirname, 'src/foo.cts')), path.join(__dirname, 'build/foo.cjs'));
t.is(main.resolveTestFile(path.join(__dirname, 'src/foo.mts')), path.join(__dirname, 'build/foo.mjs'));
t.is(main.resolveTestFile(path.join(__dirname, 'build/foo.js')), path.join(__dirname, 'build/foo.js'));
t.is(main.resolveTestFile(path.join(__dirname, 'foo/bar.ts')), path.join(__dirname, 'foo/bar.ts'));
});

View file

@ -1,11 +0,0 @@
# Snapshot report for `test/esm.js`
The actual snapshot is saved in `esm.js.snap`.
Generated by [AVA](https://avajs.dev).
## worker(): import ESM
> Snapshot 1
'logged in fixtures/esm/index.js'

Binary file not shown.

23
test/snapshots/load.js.md Normal file
View file

@ -0,0 +1,23 @@
# Snapshot report for `test/load.js`
The actual snapshot is saved in `load.js.snap`.
Generated by [AVA](https://avajs.dev).
## worker(): load .cts
> Snapshot 1
'logged in fixtures/load/index.cts'
## worker(): load .mts
> Snapshot 1
'logged in fixtures/load/index.mts'
## worker(): load .ts
> Snapshot 1
'logged in fixtures/load/index.ts'

BIN
test/snapshots/load.js.snap Normal file

Binary file not shown.

View file

@ -111,5 +111,7 @@ Generated by [AVA](https://avajs.dev).
ignoredByWatcherPatterns: [
'assets/**',
'build/**/*.js.map',
'build/**/*.cjs.map',
'build/**/*.mjs.map',
],
}