├── .editorconfig ├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug.md │ ├── config.yml │ └── feature.md └── workflows │ └── ci.yml ├── .gitignore ├── .npmrc ├── LICENSE ├── README.md ├── index.js ├── package.json └── test ├── _with-provider.js ├── base.js ├── broken-fixtures ├── tsconfig.json └── typescript │ └── typescript.ts ├── compilation.js ├── fixtures ├── install-and-load.js ├── load │ ├── compiled │ │ ├── index.cjs │ │ ├── index.js │ │ └── index.mjs │ ├── index.cts │ ├── index.mts │ ├── index.ts │ └── tsconfig.json ├── tsconfig.json └── typescript │ ├── file.js │ ├── index.ts │ └── package.json ├── load.js ├── protocol-ava-6.js └── snapshots ├── compilation.js.md ├── compilation.js.snap ├── load.js.md ├── load.js.snap ├── protocol-ava-6.js.md └── protocol-ava-6.js.snap /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.yml] 11 | indent_style = space 12 | indent_size = 2 13 | 14 | [package.json] 15 | indent_style = space 16 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | Translations: [Español](https://github.com/avajs/ava-docs/blob/master/es_ES/code-of-conduct.md), [Français](https://github.com/avajs/ava-docs/blob/master/fr_FR/code-of-conduct.md), [Italiano](https://github.com/avajs/ava-docs/blob/master/it_IT/code-of-conduct.md), [日本語](https://github.com/avajs/ava-docs/blob/master/ja_JP/code-of-conduct.md), [Português](https://github.com/avajs/ava-docs/blob/master/pt_BR/code-of-conduct.md), [Русский](https://github.com/avajs/ava-docs/blob/master/ru_RU/code-of-conduct.md), [简体中文](https://github.com/avajs/ava-docs/blob/master/zh_CN/code-of-conduct.md) 4 | 5 | ## Our Pledge 6 | 7 | In the interest of fostering an open and welcoming environment, we as 8 | contributors and maintainers pledge to making participation in our project and 9 | our community a harassment-free experience for everyone, regardless of age, body 10 | size, disability, ethnicity, gender identity and expression, level of experience, 11 | nationality, personal appearance, race, religion, or sexual identity and 12 | orientation. 13 | 14 | ## Our Standards 15 | 16 | Examples of behavior that contributes to creating a positive environment 17 | include: 18 | 19 | * Using welcoming and inclusive language 20 | * Being respectful of differing viewpoints and experiences 21 | * Gracefully accepting constructive criticism 22 | * Focusing on what is best for the community 23 | * Showing empathy towards other community members 24 | 25 | Examples of unacceptable behavior by participants include: 26 | 27 | * The use of sexualized language or imagery and unwelcome sexual attention or 28 | advances 29 | * Trolling, insulting/derogatory comments, and personal or political attacks 30 | * Public or private harassment 31 | * Publishing others' private information, such as a physical or electronic 32 | address, without explicit permission 33 | * Other conduct which could reasonably be considered inappropriate in a 34 | professional setting 35 | 36 | ## Our Responsibilities 37 | 38 | Project maintainers are responsible for clarifying the standards of acceptable 39 | behavior and are expected to take appropriate and fair corrective action in 40 | response to any instances of unacceptable behavior. 41 | 42 | Project maintainers have the right and responsibility to remove, edit, or 43 | reject comments, commits, code, wiki edits, issues, and other contributions 44 | that are not aligned to this Code of Conduct, or to ban temporarily or 45 | permanently any contributor for other behaviors that they deem inappropriate, 46 | threatening, offensive, or harmful. 47 | 48 | ## Scope 49 | 50 | This Code of Conduct applies both within project spaces and in public spaces 51 | when an individual is representing the project or its community. Examples of 52 | representing a project or community include using an official project e-mail 53 | address, posting via an official social media account, or acting as an appointed 54 | representative at an online or offline event. Representation of a project may be 55 | further defined and clarified by project maintainers. 56 | 57 | ## Enforcement 58 | 59 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 60 | reported by contacting the project team at sindresorhus@gmail.com. All 61 | complaints will be reviewed and investigated and will result in a response that 62 | is deemed necessary and appropriate to the circumstances. The project team is 63 | obligated to maintain confidentiality with regard to the reporter of an incident. 64 | Further details of specific enforcement policies may be posted separately. 65 | 66 | Project maintainers who do not follow or enforce the Code of Conduct in good 67 | faith may face temporary or permanent repercussions as determined by other 68 | members of the project's leadership. 69 | 70 | ## Attribution 71 | 72 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 73 | available at [http://contributor-covenant.org/version/1/4][version] 74 | 75 | [homepage]: http://contributor-covenant.org 76 | [version]: http://contributor-covenant.org/version/1/4/ 77 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to AVA 2 | 3 | ✨ Thanks for contributing to AVA! ✨ 4 | 5 | Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms. 6 | 7 | This repository is a part of AVA. Start with reading AVA's [contributing guide](https://github.com/avajs/ava/blob/master/.github/CONTRIBUTING.md). Issue labels may be a little different in this repository but otherwise the same applies. 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: If something isn't working the way you expect it to 4 | labels: needs triage 5 | --- 6 | 7 | Please provide details about: 8 | 9 | * What you're trying to do 10 | * What happened 11 | * What you expected to happen 12 | 13 | Please share relevant sample code. Or better yet, provide a link to a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). 14 | 15 | We'll also need your AVA configuration (in `package.json` or `ava.config.*` configuration files) and how you're invoking AVA. Share the installed AVA version (get it by running `npx ava --version`) and `@ava/babel` version (from your `package.json` file). 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Suggestions for new or different behavior 4 | labels: question 5 | --- 6 | 7 | Please provide details about: 8 | 9 | * What you're trying to do 10 | * Why you can't use AVA's Babel support for this 11 | * And maybe how you think AVA could handle this 12 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Install and test @ava/typescript 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | paths-ignore: 8 | - '*.md' 9 | jobs: 10 | nodejs: 11 | name: Node.js 12 | runs-on: ${{ matrix.os }} 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | node-version: [^18.18, ^20.8, ^21, ^22] 17 | os: [ubuntu-latest, windows-latest] 18 | steps: 19 | - uses: actions/checkout@v4 20 | - uses: actions/setup-node@v4 21 | with: 22 | node-version: ${{ matrix.node-version }} 23 | - run: npm install --no-audit 24 | - run: npm test 25 | - uses: codecov/codecov-action@v4 26 | with: 27 | files: coverage/lcov.info 28 | name: ${{ matrix.os }}/${{ matrix.node-version }} 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /coverage 2 | /node_modules 3 | /test/fixtures/typescript/compiled 4 | /test/broken-fixtures/typescript/compiled 5 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Mark Wubben (https://novemberborn.net) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @ava/typescript 2 | 3 | Adds [TypeScript](https://www.typescriptlang.org/) support to [AVA](https://avajs.dev). 4 | 5 | This is designed to work for projects that precompile TypeScript. It allows AVA to load the compiled JavaScript, while configuring AVA to treat the TypeScript files as test files. 6 | 7 | In other words, say you have a test file at `src/test.ts`. You've configured TypeScript to output to `build/`. Using `@ava/typescript` you can run the test using `npx ava src/test.ts`. 8 | 9 | ## Enabling TypeScript support 10 | 11 | Add this package to your project: 12 | 13 | ```console 14 | npm install --save-dev @ava/typescript 15 | ``` 16 | 17 | Then, enable TypeScript support either in `package.json` or `ava.config.*`: 18 | 19 | **`package.json`:** 20 | 21 | ```json 22 | { 23 | "ava": { 24 | "typescript": { 25 | "rewritePaths": { 26 | "src/": "build/" 27 | }, 28 | "compile": false 29 | } 30 | } 31 | } 32 | ``` 33 | 34 | Both keys and values of the `rewritePaths` object must end with a `/`. Paths are relative to your project directory. 35 | 36 | You can enable compilation via the `compile` property. If `false`, AVA will assume you have already compiled your project. If set to `'tsc'`, AVA will run the TypeScript compiler before running your tests. This can be inefficient when using AVA in watch mode. 37 | 38 | Output files are expected to have the `.js` extension. 39 | 40 | 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. 41 | 42 | ## ES Modules 43 | 44 | If your `package.json` has configured `"type": "module"`, or you've configured AVA to treat the `js` extension as `module`, then `@ava/typescript` will import the output file as an ES module. Note that this is based on the *output file*, not the `ts` extension. 45 | 46 | ## Add additional extensions 47 | 48 | You can configure AVA to recognize additional file extensions. To add (partial†) JSX support: 49 | 50 | **`package.json`:** 51 | 52 | ```json 53 | { 54 | "ava": { 55 | "typescript": { 56 | "extensions": [ 57 | "ts", 58 | "tsx" 59 | ], 60 | "rewritePaths": { 61 | "src/": "build/" 62 | } 63 | } 64 | } 65 | } 66 | ``` 67 | 68 | If you use the [`allowJs` TypeScript option](https://www.typescriptlang.org/tsconfig/allowJs.html) you'll have to specify the `js`, `cjs` and `mjs` extensions for them to be rewritten. 69 | 70 | See also AVA's [`extensions` option](https://github.com/avajs/ava/blob/master/docs/06-configuration.md#options). 71 | 72 | † Note that the [*preserve* mode for JSX](https://www.typescriptlang.org/docs/handbook/jsx.html) is not (yet) supported. 73 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs'; 2 | import path from 'node:path'; 3 | import {pathToFileURL} from 'node:url'; 4 | import escapeStringRegexp from 'escape-string-regexp'; 5 | import {execa} from 'execa'; 6 | 7 | const package_ = JSON.parse(fs.readFileSync(new URL('package.json', import.meta.url))); 8 | const help = `See https://github.com/avajs/typescript/blob/v${package_.version}/README.md`; 9 | 10 | function isPlainObject(x) { 11 | return x !== null && typeof x === 'object' && Reflect.getPrototypeOf(x) === Object.prototype; 12 | } 13 | 14 | function validate(target, properties) { 15 | for (const key of Object.keys(properties)) { 16 | const {required, isValid} = properties[key]; 17 | const missing = !Reflect.has(target, key); 18 | 19 | if (missing) { 20 | if (required) { 21 | throw new Error(`Missing '${key}' property in TypeScript configuration for AVA. ${help}`); 22 | } 23 | 24 | continue; 25 | } 26 | 27 | if (!isValid(target[key])) { 28 | throw new Error(`Invalid '${key}' property in TypeScript configuration for AVA. ${help}`); 29 | } 30 | } 31 | 32 | for (const key of Object.keys(target)) { 33 | if (!Reflect.has(properties, key)) { 34 | throw new Error(`Unexpected '${key}' property in TypeScript configuration for AVA. ${help}`); 35 | } 36 | } 37 | } 38 | 39 | async function compileTypeScript(projectDirectory) { 40 | return execa('tsc', ['--incremental'], {preferLocal: true, cwd: projectDirectory}); 41 | } 42 | 43 | const configProperties = { 44 | compile: { 45 | required: true, 46 | isValid(compile) { 47 | return compile === false || compile === 'tsc'; 48 | }, 49 | }, 50 | rewritePaths: { 51 | required: true, 52 | isValid(rewritePaths) { 53 | if (!isPlainObject(rewritePaths)) { 54 | return false; 55 | } 56 | 57 | return Object.entries(rewritePaths).every(([from, to]) => from.endsWith('/') && typeof to === 'string' && to.endsWith('/')); 58 | }, 59 | }, 60 | extensions: { 61 | required: false, 62 | isValid(extensions) { 63 | return Array.isArray(extensions) 64 | && extensions.length > 0 65 | && extensions.every(extension => typeof extension === 'string' && extension !== '') 66 | && new Set(extensions).size === extensions.length; 67 | }, 68 | }, 69 | }; 70 | 71 | const changeInterpretations = Object.freeze(Object.assign(Object.create(null), { 72 | unspecified: 0, 73 | ignoreCompiled: 1, 74 | waitForOutOfBandCompilation: 2, 75 | })); 76 | 77 | export default function typescriptProvider({negotiateProtocol}) { 78 | const protocol = negotiateProtocol(['ava-6'], {version: package_.version}); 79 | if (protocol === null) { 80 | return; 81 | } 82 | 83 | return { 84 | main({config}) { 85 | if (!isPlainObject(config)) { 86 | throw new Error(`Unexpected Typescript configuration for AVA. ${help}`); 87 | } 88 | 89 | validate(config, configProperties); 90 | 91 | const { 92 | extensions = ['ts', 'cts', 'mts'], 93 | rewritePaths: relativeRewritePaths, 94 | compile, 95 | } = config; 96 | 97 | const rewritePaths = Object.entries(relativeRewritePaths).map(([from, to]) => [ 98 | path.join(protocol.projectDir, from), 99 | path.join(protocol.projectDir, to), 100 | ]); 101 | const testFileExtension = new RegExp(`\\.(${extensions.map(extension => escapeStringRegexp(extension)).join('|')})$`); 102 | 103 | const watchMode = { 104 | changeInterpretations, 105 | interpretChange(filePath) { 106 | if (config.compile === false) { 107 | for (const [from] of rewritePaths) { 108 | if (testFileExtension.test(filePath) && filePath.startsWith(from)) { 109 | return changeInterpretations.waitForOutOfBandCompilation; 110 | } 111 | } 112 | } 113 | 114 | if (config.compile === 'tsc') { 115 | for (const [, to] of rewritePaths) { 116 | if (filePath.startsWith(to)) { 117 | return changeInterpretations.ignoreCompiled; 118 | } 119 | } 120 | } 121 | 122 | return changeInterpretations.unspecified; 123 | }, 124 | 125 | resolvePossibleOutOfBandCompilationSources(filePath) { 126 | if (config.compile !== false) { 127 | return null; 128 | } 129 | 130 | // Only recognize .cjs, .mjs and .js files. 131 | if (!/\.(c|m)?js$/.test(filePath)) { 132 | return null; 133 | } 134 | 135 | for (const [from, to] of rewritePaths) { 136 | if (!filePath.startsWith(to)) { 137 | continue; 138 | } 139 | 140 | const rewritten = `${from}${filePath.slice(to.length)}`; 141 | const possibleExtensions = []; 142 | 143 | if (filePath.endsWith('.cjs')) { 144 | if (extensions.includes('cjs')) { 145 | possibleExtensions.push({replace: /\.cjs$/, extension: 'cjs'}); 146 | } 147 | 148 | if (extensions.includes('cts')) { 149 | possibleExtensions.push({replace: /\.cjs$/, extension: 'cts'}); 150 | } 151 | 152 | if (possibleExtensions.length === 0) { 153 | return null; 154 | } 155 | } 156 | 157 | if (filePath.endsWith('.mjs')) { 158 | if (extensions.includes('mjs')) { 159 | possibleExtensions.push({replace: /\.mjs$/, extension: 'mjs'}); 160 | } 161 | 162 | if (extensions.includes('mts')) { 163 | possibleExtensions.push({replace: /\.mjs$/, extension: 'mts'}); 164 | } 165 | 166 | if (possibleExtensions.length === 0) { 167 | return null; 168 | } 169 | } 170 | 171 | if (filePath.endsWith('.js')) { 172 | if (extensions.includes('js')) { 173 | possibleExtensions.push({replace: /\.js$/, extension: 'js'}); 174 | } 175 | 176 | if (extensions.includes('ts')) { 177 | possibleExtensions.push({replace: /\.js$/, extension: 'ts'}); 178 | } 179 | 180 | if (extensions.includes('tsx')) { 181 | possibleExtensions.push({replace: /\.js$/, extension: 'tsx'}); 182 | } 183 | 184 | if (possibleExtensions.length === 0) { 185 | return null; 186 | } 187 | } 188 | 189 | const possibleDeletedFiles = []; 190 | for (const {replace, extension} of possibleExtensions) { 191 | const possibleFilePath = rewritten.replace(replace, `.${extension}`); 192 | 193 | // Pick the first file path that exists. 194 | if (fs.existsSync(possibleFilePath)) { 195 | return [possibleFilePath]; 196 | } 197 | 198 | possibleDeletedFiles.push(possibleFilePath); 199 | } 200 | 201 | return possibleDeletedFiles; 202 | } 203 | 204 | return null; 205 | }, 206 | }; 207 | 208 | return { 209 | ...watchMode, 210 | 211 | async compile() { 212 | if (compile === 'tsc') { 213 | await compileTypeScript(protocol.projectDir); 214 | } 215 | 216 | return { 217 | extensions: [...extensions], 218 | rewritePaths: [...rewritePaths], 219 | }; 220 | }, 221 | 222 | get extensions() { 223 | return [...extensions]; 224 | }, 225 | 226 | updateGlobs({filePatterns, ignoredByWatcherPatterns}) { 227 | return { 228 | filePatterns: [ 229 | ...filePatterns, 230 | '!**/*.d.ts', 231 | ...Object.values(relativeRewritePaths).map(to => `!${to}**`), 232 | ], 233 | ignoredByWatcherPatterns: [ 234 | ...ignoredByWatcherPatterns, 235 | ...Object.values(relativeRewritePaths).flatMap(to => [ 236 | `${to}**/*.js.map`, 237 | `${to}**/*.cjs.map`, 238 | `${to}**/*.mjs.map`, 239 | ]), 240 | ], 241 | }; 242 | }, 243 | }; 244 | }, 245 | 246 | worker({extensionsToLoadAsModules, state: {extensions, rewritePaths}}) { 247 | const importJs = extensionsToLoadAsModules.includes('js'); 248 | const testFileExtension = new RegExp(`\\.(${extensions.map(extension => escapeStringRegexp(extension)).join('|')})$`); 249 | 250 | return { 251 | canLoad(reference) { 252 | return testFileExtension.test(reference) && rewritePaths.some(([from]) => reference.startsWith(from)); 253 | }, 254 | 255 | async load(reference, {requireFn}) { 256 | const [from, to] = rewritePaths.find(([from]) => reference.startsWith(from)); 257 | let rewritten = `${to}${reference.slice(from.length)}`; 258 | let useImport = true; 259 | if (reference.endsWith('.cts')) { 260 | rewritten = rewritten.replace(/\.cts$/, '.cjs'); 261 | useImport = false; 262 | } else if (reference.endsWith('.mts')) { 263 | rewritten = rewritten.replace(/\.mts$/, '.mjs'); 264 | } else { 265 | rewritten = rewritten.replace(testFileExtension, '.js'); 266 | useImport = importJs; 267 | } 268 | 269 | return useImport ? import(pathToFileURL(rewritten)) : requireFn(rewritten); 270 | }, 271 | }; 272 | }, 273 | }; 274 | } 275 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ava/typescript", 3 | "version": "5.0.0", 4 | "description": "TypeScript provider for AVA", 5 | "engines": { 6 | "node": "^18.18 || ^20.8 || ^21 || ^22" 7 | }, 8 | "files": [ 9 | "index.js" 10 | ], 11 | "exports": { 12 | ".": "./index.js" 13 | }, 14 | "type": "module", 15 | "author": "Mark Wubben (https://novemberborn.net)", 16 | "repository": "avajs/typescript", 17 | "license": "MIT", 18 | "keywords": [ 19 | "ava", 20 | "typescript" 21 | ], 22 | "scripts": { 23 | "test": "xo && c8 ava" 24 | }, 25 | "dependencies": { 26 | "escape-string-regexp": "^5.0.0", 27 | "execa": "^8.0.1" 28 | }, 29 | "devDependencies": { 30 | "ava": "^6.1.2", 31 | "c8": "^9.1.0", 32 | "del": "^7.1.0", 33 | "typescript": "^5.4.5", 34 | "xo": "^0.58.0" 35 | }, 36 | "c8": { 37 | "reporter": [ 38 | "html", 39 | "lcov", 40 | "text" 41 | ] 42 | }, 43 | "ava": { 44 | "files": [ 45 | "!test/broken-fixtures/**" 46 | ], 47 | "watcher": { 48 | "ignoreChanges": [ 49 | "test/fixtures/**", 50 | "test/broken-fixtures/**" 51 | ] 52 | }, 53 | "timeout": "60s" 54 | }, 55 | "xo": { 56 | "ignores": [ 57 | "test/broken-fixtures", 58 | "test/fixtures/**/compiled/**" 59 | ] 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /test/_with-provider.js: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs'; 2 | import path from 'node:path'; 3 | import {fileURLToPath} from 'node:url'; 4 | import makeProvider from '@ava/typescript'; 5 | 6 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); 7 | const package_ = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url))); 8 | 9 | const createProviderMacro = (identifier, avaVersion, projectDirectory = __dirname) => (t, run) => run(t, makeProvider({ 10 | negotiateProtocol(identifiers, {version}) { 11 | t.true(identifiers.includes(identifier)); 12 | t.is(version, package_.version); 13 | return { 14 | ava: {avaVersion}, 15 | identifier, 16 | normalizeGlobPatterns: patterns => patterns, 17 | async findFiles({patterns}) { 18 | return patterns.map(file => path.join(projectDirectory, file)); 19 | }, 20 | projectDir: projectDirectory, 21 | }; 22 | }, 23 | })); 24 | 25 | export default createProviderMacro; 26 | -------------------------------------------------------------------------------- /test/base.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import makeProvider from '@ava/typescript'; 3 | 4 | test('bails when negotiating protocol returns `null`', t => { 5 | const provider = makeProvider({negotiateProtocol: () => null}); 6 | t.is(provider, undefined); 7 | }); 8 | -------------------------------------------------------------------------------- /test/broken-fixtures/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "typescript/compiled" 4 | }, 5 | "include": [ 6 | "typescript" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /test/broken-fixtures/typescript/typescript.ts: -------------------------------------------------------------------------------- 1 | a 2 | -------------------------------------------------------------------------------- /test/compilation.js: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import {fileURLToPath} from 'node:url'; 3 | import test from 'ava'; 4 | import {deleteAsync} from 'del'; 5 | import {execaNode} from 'execa'; 6 | import createProviderMacro from './_with-provider.js'; 7 | 8 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); 9 | const withProvider = createProviderMacro('ava-6', '6.0.0', path.join(__dirname, 'fixtures')); 10 | const withAltProvider = createProviderMacro('ava-6', '6.0.0', path.join(__dirname, 'broken-fixtures')); 11 | 12 | test.before('deleting compiled files', async t => { 13 | t.log(await deleteAsync('test/fixtures/typescript/compiled')); 14 | t.log(await deleteAsync('test/broken-fixtures/typescript/compiled')); 15 | }); 16 | 17 | const compile = async provider => ({ 18 | state: await provider.main({ 19 | config: { 20 | rewritePaths: { 21 | 'ts/': 'typescript/', 22 | 'compiled/': 'typescript/compiled/', 23 | }, 24 | compile: 'tsc', 25 | }, 26 | }).compile(), 27 | }); 28 | 29 | test('worker(): load rewritten paths files', withProvider, async (t, provider) => { 30 | const {state} = await compile(provider); 31 | const {stdout, stderr} = await execaNode( 32 | path.join(__dirname, 'fixtures/install-and-load'), 33 | [JSON.stringify({state}), path.join(__dirname, 'fixtures/ts', 'file.ts')], 34 | {cwd: path.join(__dirname, 'fixtures')}, 35 | ); 36 | if (stderr.length > 0) { 37 | t.log(stderr); 38 | } 39 | 40 | t.snapshot(stdout); 41 | }); 42 | 43 | test('worker(): runs compiled files', withProvider, async (t, provider) => { 44 | const {state} = await compile(provider); 45 | const {stdout, stderr} = await execaNode( 46 | path.join(__dirname, 'fixtures/install-and-load'), 47 | [JSON.stringify({state}), path.join(__dirname, 'fixtures/compiled', 'index.ts')], 48 | {cwd: path.join(__dirname, 'fixtures')}, 49 | ); 50 | if (stderr.length > 0) { 51 | t.log(stderr); 52 | } 53 | 54 | t.snapshot(stdout); 55 | }); 56 | 57 | test('compile() error', withAltProvider, async (t, provider) => { 58 | const {message} = await t.throwsAsync(compile(provider)); 59 | 60 | t.snapshot(message); 61 | }); 62 | -------------------------------------------------------------------------------- /test/fixtures/install-and-load.js: -------------------------------------------------------------------------------- 1 | import {createRequire} from 'node:module'; 2 | import path from 'node:path'; 3 | import process from 'node:process'; 4 | import {fileURLToPath} from 'node:url'; 5 | import makeProvider from '@ava/typescript'; 6 | 7 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); 8 | 9 | const provider = makeProvider({ 10 | negotiateProtocol() { 11 | return {identifier: 'ava-6', ava: {version: '6.0.0'}, projectDir: __dirname}; 12 | }, 13 | }); 14 | 15 | const worker = provider.worker({ 16 | extensionsToLoadAsModules: [], 17 | state: {}, 18 | ...JSON.parse(process.argv[2]), 19 | }); 20 | 21 | const reference = path.resolve(process.argv[3]); 22 | 23 | if (worker.canLoad(reference)) { 24 | worker.load(reference, {requireFn: createRequire(import.meta.url)}); 25 | } 26 | -------------------------------------------------------------------------------- /test/fixtures/load/compiled/index.cjs: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | console.log('logged in fixtures/load/index.cts'); 4 | -------------------------------------------------------------------------------- /test/fixtures/load/compiled/index.js: -------------------------------------------------------------------------------- 1 | console.log('logged in fixtures/load/index.ts'); 2 | export {}; 3 | -------------------------------------------------------------------------------- /test/fixtures/load/compiled/index.mjs: -------------------------------------------------------------------------------- 1 | console.log('logged in fixtures/load/index.mts'); 2 | export {}; 3 | -------------------------------------------------------------------------------- /test/fixtures/load/index.cts: -------------------------------------------------------------------------------- 1 | console.log('logged in fixtures/load/index.cts'); 2 | -------------------------------------------------------------------------------- /test/fixtures/load/index.mts: -------------------------------------------------------------------------------- 1 | console.log('logged in fixtures/load/index.mts'); 2 | -------------------------------------------------------------------------------- /test/fixtures/load/index.ts: -------------------------------------------------------------------------------- 1 | console.log('logged in fixtures/load/index.ts'); 2 | -------------------------------------------------------------------------------- /test/fixtures/load/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strictNullChecks": true, 4 | "module": "Node16", 5 | "outDir": "compiled" 6 | }, 7 | "include": [ 8 | "." 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /test/fixtures/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strictNullChecks": true, 4 | "outDir": "typescript/compiled" 5 | }, 6 | "include": [ 7 | "typescript" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /test/fixtures/typescript/file.js: -------------------------------------------------------------------------------- 1 | console.log('logged in file.js'); 2 | -------------------------------------------------------------------------------- /test/fixtures/typescript/index.ts: -------------------------------------------------------------------------------- 1 | console.log('logged in fixtures/typescript/index.ts'); 2 | -------------------------------------------------------------------------------- /test/fixtures/typescript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "commonjs" 3 | } 4 | -------------------------------------------------------------------------------- /test/load.js: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import {fileURLToPath} from 'node:url'; 3 | import test from 'ava'; 4 | import {execaNode} from 'execa'; 5 | import createProviderMacro from './_with-provider.js'; 6 | 7 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); 8 | const withProvider = createProviderMacro('ava-6', '6.0.0', path.join(__dirname, 'fixtures')); 9 | 10 | const setup = async provider => ({ 11 | state: await provider.main({ 12 | config: { 13 | rewritePaths: { 14 | 'load/': 'load/compiled/', 15 | }, 16 | compile: false, 17 | }, 18 | }).compile(), 19 | }); 20 | 21 | test('worker(): load .cts', withProvider, async (t, provider) => { 22 | const {state} = await setup(provider); 23 | const {stdout, stderr} = await execaNode( 24 | path.join(__dirname, 'fixtures/install-and-load'), 25 | [JSON.stringify({state}), path.join(__dirname, 'fixtures/load', 'index.cts')], 26 | {cwd: path.join(__dirname, 'fixtures')}, 27 | ); 28 | if (stderr.length > 0) { 29 | t.log(stderr); 30 | } 31 | 32 | t.snapshot(stdout); 33 | }); 34 | 35 | test('worker(): load .mts', withProvider, async (t, provider) => { 36 | const {state} = await setup(provider); 37 | const {stdout, stderr} = await execaNode( 38 | path.join(__dirname, 'fixtures/install-and-load'), 39 | [JSON.stringify({state}), path.join(__dirname, 'fixtures/load', 'index.mts')], 40 | {cwd: path.join(__dirname, 'fixtures')}, 41 | ); 42 | if (stderr.length > 0) { 43 | t.log(stderr); 44 | } 45 | 46 | t.snapshot(stdout); 47 | }); 48 | 49 | test('worker(): load .ts', withProvider, async (t, provider) => { 50 | const {state} = await setup(provider); 51 | const {stdout, stderr} = await execaNode( 52 | path.join(__dirname, 'fixtures/install-and-load'), 53 | [JSON.stringify({extensionsToLoadAsModules: ['js'], state}), path.join(__dirname, 'fixtures/load', 'index.ts')], 54 | {cwd: path.join(__dirname, 'fixtures')}, 55 | ); 56 | if (stderr.length > 0) { 57 | t.log(stderr); 58 | } 59 | 60 | t.snapshot(stdout); 61 | }); 62 | -------------------------------------------------------------------------------- /test/protocol-ava-6.js: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs'; 2 | import path from 'node:path'; 3 | import {fileURLToPath} from 'node:url'; 4 | import test from 'ava'; 5 | import createProviderMacro from './_with-provider.js'; 6 | 7 | const projectDirectory = path.dirname(fileURLToPath(import.meta.url)); 8 | const package_ = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url))); 9 | const withProvider = createProviderMacro('ava-6', '5.3.0'); 10 | 11 | const validateConfig = (t, provider, config) => { 12 | const error = t.throws(() => provider.main({config})); 13 | error.message = error.message.replace(`v${package_.version}`, 'v${pkg.version}'); // eslint-disable-line no-template-curly-in-string 14 | t.snapshot(error); 15 | }; 16 | 17 | test('negotiates ava-6 protocol', withProvider, t => t.plan(2)); 18 | 19 | test('main() config validation: throw when config is not a plain object', withProvider, (t, provider) => { 20 | validateConfig(t, provider, false); 21 | validateConfig(t, provider, true); 22 | validateConfig(t, provider, null); 23 | validateConfig(t, provider, []); 24 | }); 25 | 26 | test('main() config validation: throw when config contains keys other than \'extensions\', \'rewritePaths\' or \'compile\'', withProvider, (t, provider) => { 27 | validateConfig(t, provider, {compile: false, foo: 1, rewritePaths: {'src/': 'build/'}}); 28 | }); 29 | 30 | test('main() config validation: throw when config.extensions contains empty strings', withProvider, (t, provider) => { 31 | validateConfig(t, provider, {extensions: ['']}); 32 | }); 33 | 34 | test('main() config validation: throw when config.extensions contains non-strings', withProvider, (t, provider) => { 35 | validateConfig(t, provider, {extensions: [1]}); 36 | }); 37 | 38 | test('main() config validation: throw when config.extensions contains duplicates', withProvider, (t, provider) => { 39 | validateConfig(t, provider, {extensions: ['ts', 'ts']}); 40 | }); 41 | 42 | test('main() config validation: config may not be an empty object', withProvider, (t, provider) => { 43 | validateConfig(t, provider, {}); 44 | }); 45 | 46 | test('main() config validation: throw when config.compile is invalid', withProvider, (t, provider) => { 47 | validateConfig(t, provider, {rewritePaths: {'src/': 'build/'}, compile: 1}); 48 | validateConfig(t, provider, {rewritePaths: {'src/': 'build/'}, compile: undefined}); 49 | }); 50 | 51 | test('main() config validation: rewrite paths must end in a /', withProvider, (t, provider) => { 52 | validateConfig(t, provider, {rewritePaths: {src: 'build/', compile: false}}); 53 | validateConfig(t, provider, {rewritePaths: {'src/': 'build', compile: false}}); 54 | }); 55 | 56 | test('main() extensions: defaults to [\'ts\', \'cts\', \'mts\']', withProvider, (t, provider) => { 57 | t.deepEqual(provider.main({config: {rewritePaths: {'src/': 'build/'}, compile: false}}).extensions, ['ts', 'cts', 'mts']); 58 | }); 59 | 60 | test('main() extensions: returns configured extensions', withProvider, (t, provider) => { 61 | const extensions = ['tsx']; 62 | t.deepEqual(provider.main({config: {extensions, rewritePaths: {'src/': 'build/'}, compile: false}}).extensions, extensions); 63 | }); 64 | 65 | test('main() extensions: always returns new arrays', withProvider, (t, provider) => { 66 | const main = provider.main({config: {rewritePaths: {'src/': 'build/'}, compile: false}}); 67 | t.not(main.extensions, main.extensions); 68 | }); 69 | 70 | test('main() updateGlobs()', withProvider, (t, provider) => { 71 | const main = provider.main({config: {rewritePaths: {'src/': 'build/'}, compile: false}}); 72 | t.snapshot(main.updateGlobs({ 73 | filePatterns: ['src/test.ts'], 74 | ignoredByWatcherPatterns: ['assets/**'], 75 | })); 76 | }); 77 | 78 | test('main() interpretChange() without compilation', withProvider, (t, provider) => { 79 | const main = provider.main({config: {rewritePaths: {'src/': 'build/'}, compile: false}}); 80 | t.is(main.interpretChange(path.join(projectDirectory, 'src/foo.ts')), main.changeInterpretations.waitForOutOfBandCompilation); 81 | t.is(main.interpretChange(path.join(projectDirectory, 'build/foo.js')), main.changeInterpretations.unspecified); 82 | t.is(main.interpretChange(path.join(projectDirectory, 'src/foo.txt')), main.changeInterpretations.unspecified); 83 | }); 84 | 85 | test('main() interpretChange() with compilation', withProvider, (t, provider) => { 86 | const main = provider.main({config: {rewritePaths: {'src/': 'build/'}, compile: 'tsc'}}); 87 | t.is(main.interpretChange(path.join(projectDirectory, 'src/foo.ts')), main.changeInterpretations.unspecified); 88 | t.is(main.interpretChange(path.join(projectDirectory, 'build/foo.js')), main.changeInterpretations.ignoreCompiled); 89 | t.is(main.interpretChange(path.join(projectDirectory, 'src/foo.txt')), main.changeInterpretations.unspecified); 90 | }); 91 | 92 | test('main() resolvePossibleOutOfBandCompilationSources() with compilation', withProvider, (t, provider) => { 93 | const main = provider.main({config: {rewritePaths: {'src/': 'build/'}, compile: 'tsc'}}); 94 | t.is(main.resolvePossibleOutOfBandCompilationSources(path.join(projectDirectory, 'build/foo.js')), null); 95 | }); 96 | 97 | test('main() resolvePossibleOutOfBandCompilationSources() unknown extension', withProvider, (t, provider) => { 98 | const main = provider.main({config: {rewritePaths: {'src/': 'build/'}, compile: false}}); 99 | t.is(main.resolvePossibleOutOfBandCompilationSources(path.join(projectDirectory, 'build/foo.bar')), null); 100 | }); 101 | 102 | test('main() resolvePossibleOutOfBandCompilationSources() not a build path', withProvider, (t, provider) => { 103 | const main = provider.main({config: {rewritePaths: {'src/': 'build/'}, compile: false}}); 104 | t.is(main.resolvePossibleOutOfBandCompilationSources(path.join(projectDirectory, 'lib/foo.js')), null); 105 | }); 106 | 107 | test('main() resolvePossibleOutOfBandCompilationSources() .cjs but .cts not configured', withProvider, (t, provider) => { 108 | const main = provider.main({config: {extensions: ['ts'], rewritePaths: {'src/': 'build/'}, compile: false}}); 109 | t.is(main.resolvePossibleOutOfBandCompilationSources(path.join(projectDirectory, 'build/foo.cjs')), null); 110 | }); 111 | 112 | test('main() resolvePossibleOutOfBandCompilationSources() .mjs but .mts not configured', withProvider, (t, provider) => { 113 | const main = provider.main({config: {extensions: ['ts'], rewritePaths: {'src/': 'build/'}, compile: false}}); 114 | t.is(main.resolvePossibleOutOfBandCompilationSources(path.join(projectDirectory, 'build/foo.mjs')), null); 115 | }); 116 | 117 | test('main() resolvePossibleOutOfBandCompilationSources() .js but .ts not configured', withProvider, (t, provider) => { 118 | const main = provider.main({config: {extensions: ['cts'], rewritePaths: {'src/': 'build/'}, compile: false}}); 119 | t.is(main.resolvePossibleOutOfBandCompilationSources(path.join(projectDirectory, 'build/foo.js')), null); 120 | }); 121 | 122 | test('main() resolvePossibleOutOfBandCompilationSources() .cjs and .cjs and .cts configured', withProvider, (t, provider) => { 123 | const main = provider.main({config: {extensions: ['cjs', 'cts'], rewritePaths: {'src/': 'build/'}, compile: false}}); 124 | t.deepEqual(main.resolvePossibleOutOfBandCompilationSources(path.join(projectDirectory, 'build/foo.cjs')), [path.join(projectDirectory, 'src/foo.cjs'), path.join(projectDirectory, 'src/foo.cts')]); 125 | }); 126 | 127 | test('main() resolvePossibleOutOfBandCompilationSources() .mjs and .mjs and .mts configured', withProvider, (t, provider) => { 128 | const main = provider.main({config: {extensions: ['mjs', 'mts'], rewritePaths: {'src/': 'build/'}, compile: false}}); 129 | t.deepEqual(main.resolvePossibleOutOfBandCompilationSources(path.join(projectDirectory, 'build/foo.mjs')), [path.join(projectDirectory, 'src/foo.mjs'), path.join(projectDirectory, 'src/foo.mts')]); 130 | }); 131 | 132 | test('main() resolvePossibleOutOfBandCompilationSources() .js and .js, .ts and .tsx configured', withProvider, (t, provider) => { 133 | const main = provider.main({config: {extensions: ['js', 'ts', 'tsx'], rewritePaths: {'src/': 'build/'}, compile: false}}); 134 | t.deepEqual(main.resolvePossibleOutOfBandCompilationSources(path.join(projectDirectory, 'build/foo.js')), [path.join(projectDirectory, 'src/foo.js'), path.join(projectDirectory, 'src/foo.ts'), path.join(projectDirectory, 'src/foo.tsx')]); 135 | }); 136 | 137 | test('main() resolvePossibleOutOfBandCompilationSources() returns the first possible path that exists', withProvider, (t, provider) => { 138 | const main = provider.main({config: {extensions: ['js', 'ts', 'tsx'], rewritePaths: {'fixtures/load/': 'fixtures/load/compiled/'}, compile: false}}); 139 | t.deepEqual(main.resolvePossibleOutOfBandCompilationSources(path.join(projectDirectory, 'fixtures/load/compiled/index.js')), [path.join(projectDirectory, 'fixtures/load/index.ts')]); 140 | }); 141 | -------------------------------------------------------------------------------- /test/snapshots/compilation.js.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `test/compilation.js` 2 | 3 | The actual snapshot is saved in `compilation.js.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## worker(): load rewritten paths files 8 | 9 | > Snapshot 1 10 | 11 | 'logged in file.js' 12 | 13 | ## worker(): runs compiled files 14 | 15 | > Snapshot 1 16 | 17 | 'logged in fixtures/typescript/index.ts' 18 | 19 | ## compile() error 20 | 21 | > Snapshot 1 22 | 23 | `Command failed with exit code 2: tsc --incremental␊ 24 | typescript/typescript.ts(1,1): error TS2304: Cannot find name 'a'.` 25 | -------------------------------------------------------------------------------- /test/snapshots/compilation.js.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avajs/typescript/851ecc3d31062c62f23e5f075c1445a9cca644ce/test/snapshots/compilation.js.snap -------------------------------------------------------------------------------- /test/snapshots/load.js.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `test/load.js` 2 | 3 | The actual snapshot is saved in `load.js.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## worker(): load .cts 8 | 9 | > Snapshot 1 10 | 11 | 'logged in fixtures/load/index.cts' 12 | 13 | ## worker(): load .mts 14 | 15 | > Snapshot 1 16 | 17 | 'logged in fixtures/load/index.mts' 18 | 19 | ## worker(): load .ts 20 | 21 | > Snapshot 1 22 | 23 | 'logged in fixtures/load/index.ts' 24 | -------------------------------------------------------------------------------- /test/snapshots/load.js.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avajs/typescript/851ecc3d31062c62f23e5f075c1445a9cca644ce/test/snapshots/load.js.snap -------------------------------------------------------------------------------- /test/snapshots/protocol-ava-6.js.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `test/protocol-ava-6.js` 2 | 3 | The actual snapshot is saved in `protocol-ava-6.js.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## main() config validation: throw when config is not a plain object 8 | 9 | > Snapshot 1 10 | 11 | Error { 12 | message: 'Unexpected Typescript configuration for AVA. See https://github.com/avajs/typescript/blob/v${pkg.version}/README.md', 13 | } 14 | 15 | > Snapshot 2 16 | 17 | Error { 18 | message: 'Unexpected Typescript configuration for AVA. See https://github.com/avajs/typescript/blob/v${pkg.version}/README.md', 19 | } 20 | 21 | > Snapshot 3 22 | 23 | Error { 24 | message: 'Unexpected Typescript configuration for AVA. See https://github.com/avajs/typescript/blob/v${pkg.version}/README.md', 25 | } 26 | 27 | > Snapshot 4 28 | 29 | Error { 30 | message: 'Unexpected Typescript configuration for AVA. See https://github.com/avajs/typescript/blob/v${pkg.version}/README.md', 31 | } 32 | 33 | ## main() config validation: throw when config contains keys other than 'extensions', 'rewritePaths' or 'compile' 34 | 35 | > Snapshot 1 36 | 37 | Error { 38 | message: 'Unexpected \'foo\' property in TypeScript configuration for AVA. See https://github.com/avajs/typescript/blob/v${pkg.version}/README.md', 39 | } 40 | 41 | ## main() config validation: throw when config.extensions contains empty strings 42 | 43 | > Snapshot 1 44 | 45 | Error { 46 | message: 'Missing \'compile\' property in TypeScript configuration for AVA. See https://github.com/avajs/typescript/blob/v${pkg.version}/README.md', 47 | } 48 | 49 | ## main() config validation: throw when config.extensions contains non-strings 50 | 51 | > Snapshot 1 52 | 53 | Error { 54 | message: 'Missing \'compile\' property in TypeScript configuration for AVA. See https://github.com/avajs/typescript/blob/v${pkg.version}/README.md', 55 | } 56 | 57 | ## main() config validation: throw when config.extensions contains duplicates 58 | 59 | > Snapshot 1 60 | 61 | Error { 62 | message: 'Missing \'compile\' property in TypeScript configuration for AVA. See https://github.com/avajs/typescript/blob/v${pkg.version}/README.md', 63 | } 64 | 65 | ## main() config validation: config may not be an empty object 66 | 67 | > Snapshot 1 68 | 69 | Error { 70 | message: 'Missing \'compile\' property in TypeScript configuration for AVA. See https://github.com/avajs/typescript/blob/v${pkg.version}/README.md', 71 | } 72 | 73 | ## main() config validation: throw when config.compile is invalid 74 | 75 | > Snapshot 1 76 | 77 | Error { 78 | message: 'Invalid \'compile\' property in TypeScript configuration for AVA. See https://github.com/avajs/typescript/blob/v${pkg.version}/README.md', 79 | } 80 | 81 | > Snapshot 2 82 | 83 | Error { 84 | message: 'Invalid \'compile\' property in TypeScript configuration for AVA. See https://github.com/avajs/typescript/blob/v${pkg.version}/README.md', 85 | } 86 | 87 | ## main() config validation: rewrite paths must end in a / 88 | 89 | > Snapshot 1 90 | 91 | Error { 92 | message: 'Missing \'compile\' property in TypeScript configuration for AVA. See https://github.com/avajs/typescript/blob/v${pkg.version}/README.md', 93 | } 94 | 95 | > Snapshot 2 96 | 97 | Error { 98 | message: 'Missing \'compile\' property in TypeScript configuration for AVA. See https://github.com/avajs/typescript/blob/v${pkg.version}/README.md', 99 | } 100 | 101 | ## main() updateGlobs() 102 | 103 | > Snapshot 1 104 | 105 | { 106 | filePatterns: [ 107 | 'src/test.ts', 108 | '!**/*.d.ts', 109 | '!build/**', 110 | ], 111 | ignoredByWatcherPatterns: [ 112 | 'assets/**', 113 | 'build/**/*.js.map', 114 | 'build/**/*.cjs.map', 115 | 'build/**/*.mjs.map', 116 | ], 117 | } 118 | -------------------------------------------------------------------------------- /test/snapshots/protocol-ava-6.js.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avajs/typescript/851ecc3d31062c62f23e5f075c1445a9cca644ce/test/snapshots/protocol-ava-6.js.snap --------------------------------------------------------------------------------