Skip to content

Commit bfa70a3

Browse files
committed
Add rudimentary implementation
1 parent e56d5ac commit bfa70a3

18 files changed

+573
-0
lines changed

.editorconfig

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
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

.github/CODE_OF_CONDUCT.md

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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 [email protected]. 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/

.github/CONTRIBUTING.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
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.

.github/ISSUE_TEMPLATE/bug.md

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
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).

.github/ISSUE_TEMPLATE/config.yml

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
blank_issues_enabled: true
2+
contact_links:
3+
- name: AVA on Spectrum
4+
url: https://spectrum.chat/ava
5+
about: Ask questions and discuss in our Spectrum community
6+
- name: Stack Overflow
7+
url: https://stackoverflow.com/questions/tagged/ava
8+
about: Tag your question on Stack Overflow

.github/ISSUE_TEMPLATE/feature.md

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
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

.github/workflows/ci.yml

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: Install and test @ava/typescript
2+
on:
3+
push:
4+
branches:
5+
- master
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: [^10.18.0, ^12.14.0, ^13.5.0]
17+
os: [ubuntu-latest, windows-latest]
18+
steps:
19+
- uses: actions/checkout@v1
20+
with:
21+
fetch-depth: 1
22+
- uses: actions/setup-node@v1
23+
with:
24+
node-version: ${{ matrix.node-version }}
25+
- run: npm install --no-audit
26+
- run: npm test
27+
- uses: codecov/codecov-action@v1
28+
with:
29+
token: ${{ secrets.CODECOV_TOKEN }}

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/.nyc_output
2+
/coverage
3+
/node_modules

.npmrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package-lock=false

README.md

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# @ava/typescript
2+
3+
Adds rudimentary [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+
}
29+
}
30+
}
31+
```
32+
33+
Both keys and values of the `rewritePaths` object must end with a `/`. Paths are relative to your project directory.
34+
35+
Output files are expected to have the `.js` extension.
36+
37+
## Add additional extensions
38+
39+
You can configure AVA to recognize additional file extensions. To add (partial†) JSX support:
40+
41+
**`package.json`:**
42+
43+
```json
44+
{
45+
"ava": {
46+
"typescript": {
47+
"extensions": [
48+
"ts",
49+
"tsx"
50+
],
51+
"rewritePaths": {
52+
"src/": "build/"
53+
}
54+
}
55+
}
56+
}
57+
```
58+
59+
See also AVA's [`extensions` option](https://github.com/avajs/ava/blob/master/docs/06-configuration.md#options).
60+
61+
† Note that the [*preserve* mode for JSX](https://www.typescriptlang.org/docs/handbook/jsx.html) is not (yet) supported.

index.js

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
'use strict';
2+
const path = require('path');
3+
4+
const escapeStringRegexp = require('escape-string-regexp');
5+
const isPlainObject = require('is-plain-object');
6+
7+
const pkg = require('./package.json');
8+
9+
function isValidExtensions(extensions) {
10+
return Array.isArray(extensions) &&
11+
extensions.length > 0 &&
12+
extensions.every(ext => typeof ext === 'string' && ext !== '') &&
13+
new Set(extensions).size === extensions.length;
14+
}
15+
16+
function isValidRewritePaths(rewritePaths) {
17+
if (!isPlainObject(rewritePaths)) {
18+
return false;
19+
}
20+
21+
return Object.entries(rewritePaths).every(([from, to]) => {
22+
return from.endsWith('/') && typeof to === 'string' && to.endsWith('/');
23+
});
24+
}
25+
26+
module.exports = ({negotiateProtocol}) => {
27+
const protocol = negotiateProtocol(['ava-3'], {version: pkg.version});
28+
if (protocol === null) {
29+
return;
30+
}
31+
32+
return {
33+
main({config}) {
34+
let valid = false;
35+
if (isPlainObject(config)) {
36+
const keys = Object.keys(config);
37+
if (keys.every(key => key === 'extensions' || key === 'rewritePaths')) {
38+
valid =
39+
(config.extensions === undefined || isValidExtensions(config.extensions)) &&
40+
isValidRewritePaths(config.rewritePaths);
41+
}
42+
}
43+
44+
if (!valid) {
45+
throw new Error(`Unexpected Typescript configuration for AVA. See https://github.com/avajs/typescript/blob/v${pkg.version}/README.md for allowed values.`);
46+
}
47+
48+
const {
49+
extensions = ['ts'],
50+
rewritePaths
51+
} = config;
52+
53+
return {
54+
async compile() {
55+
return {
56+
extensions: extensions.slice(),
57+
rewritePaths: Object.entries(rewritePaths).map(([from, to]) => [
58+
path.join(protocol.projectDir, from),
59+
path.join(protocol.projectDir, to)
60+
])
61+
};
62+
},
63+
64+
get extensions() {
65+
return extensions.slice();
66+
}
67+
};
68+
},
69+
70+
worker({extensionsToLoadAsModules, state: {extensions, rewritePaths}}) {
71+
const testFileExtension = new RegExp(`\\.(${extensions.map(ext => escapeStringRegexp(ext)).join('|')})$`);
72+
73+
return {
74+
canLoad(ref) {
75+
return testFileExtension.test(ref) && rewritePaths.some(([from]) => ref.startsWith(from));
76+
},
77+
78+
async load(ref, {requireFn}) {
79+
for (const extension of extensionsToLoadAsModules) {
80+
if (ref.endsWith(`.${extension}`)) {
81+
throw new Error('@ava/typescript cannot yet load ESM files');
82+
}
83+
}
84+
85+
const [from, to] = rewritePaths.find(([from]) => ref.startsWith(from));
86+
// TODO: Support JSX preserve mode — https://www.typescriptlang.org/docs/handbook/jsx.html
87+
const rewritten = `${to}${ref.slice(from.length)}`.replace(testFileExtension, '.js');
88+
return requireFn(rewritten);
89+
}
90+
};
91+
}
92+
};
93+
};

package.json

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"name": "@ava/typescript",
3+
"version": "0.0.0",
4+
"description": "TypeScript provider for AVA",
5+
"engines": {
6+
"node": ">=10.18.0 <11 || >=12.14.0 <13 || >=13.5.0"
7+
},
8+
"files": [
9+
"index.js"
10+
],
11+
"author": "Mark Wubben (https://novemberborn.net)",
12+
"repository": "avajs/typescript",
13+
"license": "MIT",
14+
"keywords": [
15+
"ava",
16+
"typescript"
17+
],
18+
"scripts": {
19+
"test": "xo && nyc ava"
20+
},
21+
"dependencies": {
22+
"escape-string-regexp": "^2.0.0",
23+
"is-plain-object": "^3.0.0"
24+
},
25+
"devDependencies": {
26+
"ava": "^3.0.0",
27+
"execa": "^4.0.0",
28+
"nyc": "^15.0.0",
29+
"xo": "^0.25.3"
30+
},
31+
"nyc": {
32+
"reporter": [
33+
"html",
34+
"lcov",
35+
"text"
36+
]
37+
}
38+
}

test/base.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const test = require('ava');
2+
const makeProvider = require('..');
3+
4+
test('bails when negotiating protocol returns `null`', t => {
5+
const provider = makeProvider({negotiateProtocol: () => null});
6+
t.is(provider, undefined);
7+
});

test/fixtures/file.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
console.log('logged in file.js');

test/fixtures/install-and-load.js

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
const path = require('path');
2+
const makeProvider = require('../..');
3+
4+
const provider = makeProvider({
5+
negotiateProtocol() {
6+
return {identifier: process.argv[2], ava: {version: '3.0.0'}, projectDir: path.resolve(__dirname, '..')};
7+
}
8+
});
9+
10+
const worker = provider.worker({
11+
extensionsToLoadAsModules: [],
12+
state: JSON.parse(process.argv[3])
13+
});
14+
15+
const ref = path.resolve(process.argv[4]);
16+
17+
if (worker.canLoad(ref)) {
18+
worker.load(ref, {requireFn: require});
19+
}

0 commit comments

Comments
 (0)