ava 3.0.0 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
- const babelManager = require('./lib/babel-manager');
3
2
  const normalizeExtensions = require('./lib/extensions');
4
3
  const {classify, hasExtension, isHelperish, matches, normalizeFileForMatching, normalizeGlobs, normalizePatterns} = require('./lib/globs');
5
4
  const loadConfig = require('./lib/load-config');
5
+ const providerManager = require('./lib/provider-manager');
6
6
 
7
7
  const configCache = new Map();
8
8
  const helperCache = new Map();
@@ -14,22 +14,37 @@ function load(projectDir, overrides) {
14
14
  }
15
15
 
16
16
  let conf;
17
- let babelProvider;
17
+ let providers;
18
18
  if (configCache.has(projectDir)) {
19
- ({conf, babelProvider} = configCache.get(projectDir));
19
+ ({conf, providers} = configCache.get(projectDir));
20
20
  } else {
21
21
  conf = loadConfig({resolveFrom: projectDir});
22
22
 
23
+ providers = [];
23
24
  if (Reflect.has(conf, 'babel')) {
24
- babelProvider = babelManager({projectDir}).main({config: conf.babel});
25
+ const {level, main} = providerManager.babel(projectDir);
26
+ providers.push({
27
+ level,
28
+ main: main({config: conf.babel}),
29
+ type: 'babel'
30
+ });
25
31
  }
26
32
 
27
- configCache.set(projectDir, {conf, babelProvider});
33
+ if (Reflect.has(conf, 'typescript')) {
34
+ const {level, main} = providerManager.typescript(projectDir);
35
+ providers.push({
36
+ level,
37
+ main: main({config: conf.typescript}),
38
+ type: 'typescript'
39
+ });
40
+ }
41
+
42
+ configCache.set(projectDir, {conf, providers});
28
43
  }
29
44
 
30
45
  const extensions = overrides && overrides.extensions ?
31
46
  normalizeExtensions(overrides.extensions) :
32
- normalizeExtensions(conf.extensions, babelProvider);
47
+ normalizeExtensions(conf.extensions, providers);
33
48
 
34
49
  let helperPatterns = [];
35
50
  if (overrides && overrides.helpers !== undefined) {
@@ -44,7 +59,8 @@ function load(projectDir, overrides) {
44
59
  cwd: projectDir,
45
60
  ...normalizeGlobs({
46
61
  extensions,
47
- files: overrides && overrides.files ? overrides.files : conf.files
62
+ files: overrides && overrides.files ? overrides.files : conf.files,
63
+ providers
48
64
  })
49
65
  };
50
66
 
package/index.d.ts CHANGED
@@ -1,5 +1,3 @@
1
- /// <reference types="node"/>
2
-
3
1
  export interface Subscribable {
4
2
  subscribe(observer: {
5
3
  error(err: any): void,
@@ -247,16 +245,11 @@ export interface SnapshotAssertion {
247
245
  }
248
246
 
249
247
  export interface ThrowsAssertion {
250
- /**
251
- * Assert that the function throws [an error](https://www.npmjs.com/package/is-error). If so, returns the error value.
252
- */
253
- <ThrownError extends Error>(fn: () => any, expectations?: null, message?: string): ThrownError;
254
-
255
248
  /**
256
249
  * Assert that the function throws [an error](https://www.npmjs.com/package/is-error). If so, returns the error value.
257
250
  * The error must satisfy all expectations.
258
251
  */
259
- <ThrownError extends Error>(fn: () => any, expectations: ThrowsExpectation, message?: string): ThrownError;
252
+ <ThrownError extends Error>(fn: () => any, expectations?: ThrowsExpectation | null, message?: string): ThrownError;
260
253
 
261
254
  /** Skip this assertion. */
262
255
  skip(fn: () => any, expectations?: any, message?: string): void;
package/lib/api.js CHANGED
@@ -185,8 +185,11 @@ class Api extends Emittery {
185
185
  }
186
186
  });
187
187
 
188
- const {babelProvider} = this.options;
189
- const babelState = babelProvider === undefined ? null : await babelProvider.compile({cacheDir, files: testFiles});
188
+ const {providers = []} = this.options;
189
+ const providerStates = (await Promise.all(providers.map(async ({type, main}) => {
190
+ const state = await main.compile({cacheDir, files: testFiles});
191
+ return state === null ? null : {type, state};
192
+ }))).filter(state => state !== null);
190
193
 
191
194
  // Resolve the correct concurrency value.
192
195
  let concurrency = Math.min(os.cpus().length, isCi ? 2 : Infinity);
@@ -208,7 +211,7 @@ class Api extends Emittery {
208
211
 
209
212
  const options = {
210
213
  ...apiOptions,
211
- babelState,
214
+ providerStates,
212
215
  recordNewSnapshots: !isCi,
213
216
  // If we're looking for matches, run every single test process in exclusive-only mode
214
217
  runOnlyExclusive: apiOptions.match.length > 0 || runtimeOptions.runOnlyExclusive === true
package/lib/assert.js CHANGED
@@ -73,7 +73,7 @@ function getErrorWithLongStackTrace() {
73
73
  }
74
74
 
75
75
  function validateExpectations(assertion, expectations, numArgs) { // eslint-disable-line complexity
76
- if (numArgs === 1 || expectations === null) {
76
+ if (numArgs === 1 || expectations === null || expectations === undefined) {
77
77
  expectations = {};
78
78
  } else if (
79
79
  typeof expectations === 'function' ||
@@ -85,7 +85,7 @@ function validateExpectations(assertion, expectations, numArgs) { // eslint-disa
85
85
  ) {
86
86
  throw new AssertionError({
87
87
  assertion,
88
- message: `The second argument to \`t.${assertion}()\` must be an expectation object or \`null\``,
88
+ message: `The second argument to \`t.${assertion}()\` must be an expectation object, \`null\` or \`undefined\``,
89
89
  values: [formatWithLabel('Called with:', expectations)]
90
90
  });
91
91
  } else {
@@ -7,8 +7,8 @@ const debug = require('debug')('ava');
7
7
  let ignoreStackLines = [];
8
8
 
9
9
  const avaInternals = /\/ava\/(?:lib\/|lib\/worker\/)?[\w-]+\.js:\d+:\d+\)?$/;
10
- const avaDependencies = /\/node_modules\/(?:@ava\/babel|append-transform|empower-core|nyc|require-precompiled|(?:ava\/node_modules\/)?(?:babel-runtime|core-js))\//;
11
- const stackFrameLine = /^.+( \(.+:\d+:\d+\)|:\d+:\d+)$/; // eslint-disable-line prefer-named-capture-group
10
+ const avaDependencies = /\/node_modules\/(?:@ava\/babel|@ava\/require-precompiled|append-transform|empower-core|nyc|require-precompiled|(?:ava\/node_modules\/)?(?:babel-runtime|core-js))\//;
11
+ const stackFrameLine = /^.+( \(.+:\d+:\d+\)|:\d+:\d+)$/;
12
12
 
13
13
  if (!debug.enabled) {
14
14
  ignoreStackLines = StackUtils.nodeInternals();
package/lib/cli.js CHANGED
@@ -256,11 +256,11 @@ exports.run = async () => { // eslint-disable-line complexity
256
256
  const MiniReporter = require('./reporters/mini');
257
257
  const TapReporter = require('./reporters/tap');
258
258
  const Watcher = require('./watcher');
259
- const babelManager = require('./babel-manager');
260
259
  const normalizeExtensions = require('./extensions');
261
260
  const {normalizeGlobs, normalizePatterns} = require('./globs');
262
261
  const normalizeNodeArguments = require('./node-arguments');
263
262
  const validateEnvironmentVariables = require('./environment-variables');
263
+ const providerManager = require('./provider-manager');
264
264
 
265
265
  let pkg;
266
266
  try {
@@ -279,10 +279,28 @@ exports.run = async () => { // eslint-disable-line complexity
279
279
  js: defaultModuleType
280
280
  };
281
281
 
282
- let babelProvider;
282
+ const providers = [];
283
283
  if (Reflect.has(conf, 'babel')) {
284
284
  try {
285
- babelProvider = babelManager({projectDir}).main({config: conf.babel});
285
+ const {level, main} = providerManager.babel(projectDir);
286
+ providers.push({
287
+ level,
288
+ main: main({config: conf.babel}),
289
+ type: 'babel'
290
+ });
291
+ } catch (error) {
292
+ exit(error.message);
293
+ }
294
+ }
295
+
296
+ if (Reflect.has(conf, 'typescript')) {
297
+ try {
298
+ const {level, main} = providerManager.typescript(projectDir);
299
+ providers.push({
300
+ level,
301
+ main: main({config: conf.typescript}),
302
+ type: 'typescript'
303
+ });
286
304
  } catch (error) {
287
305
  exit(error.message);
288
306
  }
@@ -297,14 +315,14 @@ exports.run = async () => { // eslint-disable-line complexity
297
315
 
298
316
  let extensions;
299
317
  try {
300
- extensions = normalizeExtensions(conf.extensions, babelProvider);
318
+ extensions = normalizeExtensions(conf.extensions, providers);
301
319
  } catch (error) {
302
320
  exit(error.message);
303
321
  }
304
322
 
305
323
  let globs;
306
324
  try {
307
- globs = normalizeGlobs({files: conf.files, ignoredByWatcher: conf.ignoredByWatcher, extensions});
325
+ globs = normalizeGlobs({files: conf.files, ignoredByWatcher: conf.ignoredByWatcher, extensions, providers});
308
326
  } catch (error) {
309
327
  exit(error.message);
310
328
  }
@@ -328,22 +346,22 @@ exports.run = async () => { // eslint-disable-line complexity
328
346
  const filter = normalizePatterns(input.map(fileOrPattern => path.relative(projectDir, path.resolve(process.cwd(), fileOrPattern))));
329
347
 
330
348
  const api = new Api({
331
- babelProvider,
332
349
  cacheEnabled: combined.cache !== false,
333
350
  chalkOptions,
334
351
  concurrency: combined.concurrency || 0,
335
352
  debug,
353
+ environmentVariables,
336
354
  experiments,
337
355
  extensions,
338
356
  failFast: combined.failFast,
339
357
  failWithoutAssertions: combined.failWithoutAssertions !== false,
340
358
  globs,
341
- moduleTypes,
342
- environmentVariables,
343
359
  match,
360
+ moduleTypes,
344
361
  nodeArguments,
345
362
  parallelRuns,
346
363
  projectDir,
364
+ providers,
347
365
  ranFromCli: true,
348
366
  require: arrify(combined.require),
349
367
  serial: combined.serial,
@@ -393,6 +411,7 @@ exports.run = async () => { // eslint-disable-line complexity
393
411
  filter,
394
412
  globs,
395
413
  projectDir,
414
+ providers,
396
415
  reporter
397
416
  });
398
417
  watcher.observeStdin(process.stdin);
package/lib/extensions.js CHANGED
@@ -1,4 +1,4 @@
1
- module.exports = (configuredExtensions, babelProvider) => {
1
+ module.exports = (configuredExtensions, providers = []) => {
2
2
  // Combine all extensions possible for testing. Remove duplicate extensions.
3
3
  const duplicates = new Set();
4
4
  const seen = new Set();
@@ -16,15 +16,15 @@ module.exports = (configuredExtensions, babelProvider) => {
16
16
  combine(configuredExtensions);
17
17
  }
18
18
 
19
- if (babelProvider !== undefined) {
20
- combine(babelProvider.extensions);
19
+ for (const {main} of providers) {
20
+ combine(main.extensions);
21
21
  }
22
22
 
23
23
  if (duplicates.size > 0) {
24
24
  throw new Error(`Unexpected duplicate extensions in options: '${[...duplicates].join('\', \'')}'.`);
25
25
  }
26
26
 
27
- // Unless the default was used by `babelProvider`, as long as the extensions aren't explicitly set, set the default.
27
+ // Unless the default was used by providers, as long as the extensions aren't explicitly set, set the default.
28
28
  if (configuredExtensions === undefined) {
29
29
  if (!seen.has('cjs')) {
30
30
  seen.add('cjs');
package/lib/globs.js CHANGED
@@ -4,6 +4,7 @@ const globby = require('globby');
4
4
  const ignoreByDefault = require('ignore-by-default');
5
5
  const picomatch = require('picomatch');
6
6
  const slash = require('slash');
7
+ const {levels: providerLevels} = require('./provider-manager');
7
8
 
8
9
  const defaultIgnorePatterns = [...ignoreByDefault.directories(), '**/node_modules'];
9
10
  const defaultPicomatchIgnorePatterns = [
@@ -43,7 +44,7 @@ function normalizePatterns(patterns) {
43
44
 
44
45
  exports.normalizePatterns = normalizePatterns;
45
46
 
46
- function normalizeGlobs({extensions, files: filePatterns, ignoredByWatcher: ignoredByWatcherPatterns}) {
47
+ function normalizeGlobs({extensions, files: filePatterns, ignoredByWatcher: ignoredByWatcherPatterns, providers}) {
47
48
  if (filePatterns !== undefined && (!Array.isArray(filePatterns) || filePatterns.length === 0)) {
48
49
  throw new Error('The \'files\' configuration must be an array containing glob patterns.');
49
50
  }
@@ -83,6 +84,12 @@ function normalizeGlobs({extensions, files: filePatterns, ignoredByWatcher: igno
83
84
  ignoredByWatcherPatterns = [...defaultIgnoredByWatcherPatterns];
84
85
  }
85
86
 
87
+ for (const {level, main} of providers) {
88
+ if (level >= providerLevels.pathRewrites) {
89
+ ({filePatterns, ignoredByWatcherPatterns} = main.updateGlobs({filePatterns, ignoredByWatcherPatterns}));
90
+ }
91
+ }
92
+
86
93
  return {extensions, filePatterns, ignoredByWatcherPatterns};
87
94
  }
88
95
 
@@ -7,42 +7,17 @@ const pkgConf = require('pkg-conf');
7
7
 
8
8
  const NO_SUCH_FILE = Symbol('no ava.config.js file');
9
9
  const MISSING_DEFAULT_EXPORT = Symbol('missing default export');
10
- const EXPERIMENTS = new Set(['tryAssertion']);
11
-
12
- class LegacyCommonJsAccessError extends Error {
13
- constructor(thing, fileForErrorMessage) {
14
- super(`${thing} is not available in ${fileForErrorMessage}. Use a .cjs file instead`);
15
- this.name = 'LegacyCommonJsAccessError';
16
- }
17
- }
10
+ const EXPERIMENTS = new Set();
18
11
 
19
12
  // *Very* rudimentary support for loading ava.config.js files containing an `export default` statement.
20
- const evaluateJsConfig = (configFile, fileForErrorMessage) => {
13
+ const evaluateJsConfig = configFile => {
21
14
  const contents = fs.readFileSync(configFile, 'utf8');
22
15
  const script = new vm.Script(`'use strict';(()=>{let __export__;\n${contents.replace(/export default/g, '__export__ =')};return __export__;})()`, {
23
16
  filename: configFile,
24
17
  lineOffset: -1
25
18
  });
26
19
  return {
27
- default: script.runInNewContext({
28
- console,
29
- process,
30
- get __dirname() {
31
- throw new LegacyCommonJsAccessError('__dirname', fileForErrorMessage);
32
- },
33
- get __filename() {
34
- throw new LegacyCommonJsAccessError('__filename', fileForErrorMessage);
35
- },
36
- get module() {
37
- throw new LegacyCommonJsAccessError('module', fileForErrorMessage);
38
- },
39
- get exports() {
40
- throw new LegacyCommonJsAccessError('exports', fileForErrorMessage);
41
- },
42
- get require() {
43
- throw new LegacyCommonJsAccessError('require()', fileForErrorMessage);
44
- }
45
- })
20
+ default: script.runInThisContext()
46
21
  };
47
22
  };
48
23
 
@@ -55,17 +30,13 @@ const loadJsConfig = ({projectDir, configFile = path.join(projectDir, 'ava.confi
55
30
 
56
31
  let config;
57
32
  try {
58
- ({default: config = MISSING_DEFAULT_EXPORT} = evaluateJsConfig(configFile, fileForErrorMessage));
33
+ ({default: config = MISSING_DEFAULT_EXPORT} = evaluateJsConfig(configFile));
59
34
  } catch (error) {
60
35
  if (error.code === 'ENOENT') {
61
36
  return null;
62
37
  }
63
38
 
64
- if (error.name === 'LegacyCommonJsAccessError') {
65
- throw error;
66
- } else {
67
- throw Object.assign(new Error(`Error loading ${fileForErrorMessage}`), {parent: error});
68
- }
39
+ throw Object.assign(new Error(`Error loading ${fileForErrorMessage}: ${error.message}`), {parent: error});
69
40
  }
70
41
 
71
42
  if (config === MISSING_DEFAULT_EXPORT) {
@@ -0,0 +1,53 @@
1
+ const pkg = require('../package.json');
2
+ const globs = require('./globs');
3
+
4
+ const levels = {
5
+ ava3: 1,
6
+ pathRewrites: 2
7
+ };
8
+
9
+ exports.levels = levels;
10
+
11
+ const levelsByProtocol = {
12
+ 'ava-3': levels.ava3,
13
+ 'ava-3.2': levels.pathRewrites
14
+ };
15
+
16
+ function load(providerModule, projectDir) {
17
+ const ava = {version: pkg.version};
18
+ const makeProvider = require(providerModule);
19
+
20
+ let fatal;
21
+ let level;
22
+ const provider = makeProvider({
23
+ negotiateProtocol(identifiers, {version}) {
24
+ const [identifier] = identifiers.filter(identifier => Reflect.has(levelsByProtocol, identifier));
25
+
26
+ if (identifier === undefined) {
27
+ fatal = new Error(`This version of AVA (${ava.version}) is not compatible with ${providerModule}@${version}`);
28
+ return null;
29
+ }
30
+
31
+ level = levelsByProtocol[identifier];
32
+
33
+ return {
34
+ ava,
35
+ async findFiles({extensions, patterns}) {
36
+ return globs.findFiles({cwd: projectDir, extensions, filePatterns: patterns});
37
+ },
38
+ identifier,
39
+ normalizeGlobPatterns: globs.normalizePatterns,
40
+ projectDir
41
+ };
42
+ }
43
+ });
44
+
45
+ if (fatal) {
46
+ throw fatal;
47
+ }
48
+
49
+ return {...provider, level};
50
+ }
51
+
52
+ exports.babel = projectDir => load('@ava/babel', projectDir);
53
+ exports.typescript = projectDir => load('@ava/typescript', projectDir);
package/lib/test.js CHANGED
@@ -69,22 +69,20 @@ class ExecutionContext extends assert.Assertions {
69
69
  };
70
70
 
71
71
  this.try = async (...attemptArgs) => {
72
- if (test.experiments.tryAssertion !== true) {
73
- throw new Error('t.try() is currently an experiment. Opt in by setting `nonSemVerExperiments.tryAssertion` to `true`.');
74
- }
75
-
76
72
  const {args, buildTitle, implementations, receivedImplementationArray} = parseTestArgs(attemptArgs);
77
73
 
78
74
  if (implementations.length === 0) {
79
75
  throw new TypeError('Expected an implementation.');
80
76
  }
81
77
 
82
- const attemptPromises = implementations.map(implementation => {
78
+ const attemptPromises = implementations.map((implementation, index) => {
83
79
  let {title, isSet, isValid, isEmpty} = buildTitle(implementation);
84
80
 
85
81
  if (!isSet || isEmpty) {
86
- title = `${test.title} (attempt ${test.attemptCount + 1})`;
87
- } else if (!isValid) {
82
+ title = `${test.title} attempt ${test.attemptCount + 1 + index}`;
83
+ } else if (isValid) {
84
+ title = `${test.title} ─ ${title}`;
85
+ } else {
88
86
  throw new TypeError('`t.try()` titles must be strings'); // Throw synchronously!
89
87
  }
90
88
 
@@ -594,7 +592,7 @@ class Test {
594
592
  if (this.metadata.callback) {
595
593
  if (returnedObservable || returnedPromise) {
596
594
  const asyncType = returnedObservable ? 'observables' : 'promises';
597
- this.saveFirstError(new Error(`Do not return ${asyncType} from tests declared via \`test.cb(...)\`, if you want to return a promise simply declare the test via \`test(...)\``));
595
+ this.saveFirstError(new Error(`Do not return ${asyncType} from tests declared via \`test.cb(...)\`. Use \`test.cb(...)\` for legacy callback APIs. When using promises, observables or async functions, use \`test(...)\`.`));
598
596
  return this.finishPromised();
599
597
  }
600
598
 
package/lib/watcher.js CHANGED
@@ -6,6 +6,7 @@ const diff = require('lodash/difference');
6
6
  const flatten = require('lodash/flatten');
7
7
  const chalk = require('./chalk').get();
8
8
  const {applyTestFileFilter, classify, getChokidarIgnorePatterns} = require('./globs');
9
+ const {levels: providerLevels} = require('./provider-manager');
9
10
 
10
11
  function rethrowAsync(err) {
11
12
  // Don't swallow exceptions. Note that any
@@ -77,13 +78,14 @@ class TestDependency {
77
78
  }
78
79
 
79
80
  class Watcher {
80
- constructor({api, filter = [], globs, projectDir, reporter}) {
81
+ constructor({api, filter = [], globs, projectDir, providers, reporter}) {
81
82
  this.debouncer = new Debouncer(this);
82
83
 
83
84
  this.clearLogOnNextRun = true;
84
85
  this.runVector = 0;
85
86
  this.previousFiles = [];
86
87
  this.globs = {cwd: projectDir, ...globs};
88
+ this.providers = providers.filter(({level}) => level >= providerLevels.pathRewrites);
87
89
  this.run = (specificFiles = [], updateSnapshots = false) => {
88
90
  const clearLogOnNextRun = this.clearLogOnNextRun && this.runVector > 0;
89
91
  if (this.runVector > 0) {
@@ -190,6 +192,15 @@ class Watcher {
190
192
  }
191
193
 
192
194
  updateTestDependencies(file, dependencies) {
195
+ // Ensure the rewritten test file path is included in the dependencies,
196
+ // since changes to non-rewritten paths are ignored.
197
+ for (const {main} of this.providers) {
198
+ const rewritten = main.resolveTestFile(file);
199
+ if (!dependencies.includes(rewritten)) {
200
+ dependencies = [rewritten, ...dependencies];
201
+ }
202
+ }
203
+
193
204
  if (dependencies.length === 0) {
194
205
  this.testDependencies = this.testDependencies.filter(dep => dep.file !== file);
195
206
  return;
@@ -358,7 +369,7 @@ class Watcher {
358
369
  const {dirtyStates} = this;
359
370
  this.dirtyStates = {};
360
371
 
361
- const dirtyPaths = Object.keys(dirtyStates).filter(path => {
372
+ let dirtyPaths = Object.keys(dirtyStates).filter(path => {
362
373
  if (this.touchedFiles.has(path)) {
363
374
  debug('Ignoring known touched file %s', path);
364
375
  this.touchedFiles.delete(path);
@@ -367,6 +378,18 @@ class Watcher {
367
378
 
368
379
  return true;
369
380
  });
381
+
382
+ for (const {main} of this.providers) {
383
+ dirtyPaths = dirtyPaths.filter(path => {
384
+ if (main.ignoreChange(path)) {
385
+ debug('Ignoring changed file %s', path);
386
+ return false;
387
+ }
388
+
389
+ return true;
390
+ });
391
+ }
392
+
370
393
  const dirtyHelpersAndSources = [];
371
394
  const dirtyTests = [];
372
395
  for (const filePath of dirtyPaths) {
@@ -1,10 +1,20 @@
1
1
  'use strict';
2
+ const {pathToFileURL} = require('url');
2
3
  const currentlyUnhandled = require('currently-unhandled')();
3
4
 
4
5
  require('./ensure-forked'); // eslint-disable-line import/no-unassigned-import
5
6
 
6
7
  const ipc = require('./ipc');
7
8
 
9
+ const supportsESM = async () => {
10
+ try {
11
+ await import('data:text/javascript,');
12
+ return true;
13
+ } catch {}
14
+
15
+ return false;
16
+ };
17
+
8
18
  ipc.send({type: 'ready-for-options'});
9
19
  ipc.options.then(async options => {
10
20
  require('./options').set(options);
@@ -15,8 +25,8 @@ ipc.options.then(async options => {
15
25
  global.console = Object.assign(global.console, new console.Console({stdout, stderr, colorMode: true}));
16
26
  }
17
27
 
18
- const babelManager = require('../babel-manager');
19
28
  const nowAndTimers = require('../now-and-timers');
29
+ const providerManager = require('../provider-manager');
20
30
  const Runner = require('../runner');
21
31
  const serializeError = require('../serialize-error');
22
32
  const dependencyTracking = require('./dependency-tracker');
@@ -102,7 +112,7 @@ ipc.options.then(async options => {
102
112
  return runner;
103
113
  };
104
114
 
105
- // Store value in case to prevent required modules from modifying it.
115
+ // Store value to prevent required modules from modifying it.
106
116
  const testPath = options.file;
107
117
 
108
118
  // Install basic source map support.
@@ -118,25 +128,46 @@ ipc.options.then(async options => {
118
128
 
119
129
  // Install before processing options.require, so if helpers are added to the
120
130
  // require configuration the *compiled* helper will be loaded.
121
- let babelProvider;
122
- if (options.babelState !== null) {
123
- const {projectDir} = options;
124
- babelProvider = babelManager({projectDir}).worker({extensionsToLoadAsModules, state: options.babelState});
125
- runner.powerAssert = babelProvider.powerAssert;
126
- }
131
+ const {projectDir, providerStates = []} = options;
132
+ const providers = providerStates.map(({type, state}) => {
133
+ if (type === 'babel') {
134
+ const provider = providerManager.babel(projectDir).worker({extensionsToLoadAsModules, state});
135
+ runner.powerAssert = provider.powerAssert;
136
+ return provider;
137
+ }
138
+
139
+ if (type === 'typescript') {
140
+ return providerManager.typescript(projectDir).worker({extensionsToLoadAsModules, state});
141
+ }
142
+
143
+ return null;
144
+ }).filter(provider => provider !== null);
127
145
 
128
146
  let requireFn = require;
147
+ let isESMSupported;
129
148
  const load = async ref => {
130
149
  for (const extension of extensionsToLoadAsModules) {
131
150
  if (ref.endsWith(`.${extension}`)) {
132
- ipc.send({type: 'internal-error', err: serializeError('Internal runner error', false, new Error('AVA cannot yet load ESM files.'))});
151
+ if (typeof isESMSupported !== 'boolean') {
152
+ // Lazily determine support since this prints an experimental warning.
153
+ // eslint-disable-next-line no-await-in-loop
154
+ isESMSupported = await supportsESM();
155
+ }
156
+
157
+ if (isESMSupported) {
158
+ return import(pathToFileURL(ref));
159
+ }
160
+
161
+ ipc.send({type: 'internal-error', err: serializeError('Internal runner error', false, new Error('ECMAScript Modules are not supported in this Node.js version.'))});
133
162
  exit(1);
134
163
  return;
135
164
  }
136
165
  }
137
166
 
138
- if (babelProvider !== undefined && babelProvider.canLoad(ref)) {
139
- return babelProvider.load(ref, {requireFn});
167
+ for (const provider of providers) {
168
+ if (provider.canLoad(ref)) {
169
+ return provider.load(ref, {requireFn});
170
+ }
140
171
  }
141
172
 
142
173
  return requireFn(ref);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ava",
3
- "version": "3.0.0",
3
+ "version": "3.4.0",
4
4
  "description": "Testing can be a drag. AVA helps you get it done.",
5
5
  "license": "MIT",
6
6
  "repository": "avajs/ava",
@@ -95,8 +95,8 @@
95
95
  "p-map": "^3.0.0",
96
96
  "picomatch": "^2.2.1",
97
97
  "pkg-conf": "^3.1.0",
98
- "plur": "^3.1.1",
99
- "pretty-ms": "^5.1.0",
98
+ "plur": "^4.0.0",
99
+ "pretty-ms": "^6.0.0",
100
100
  "read-pkg": "^5.2.0",
101
101
  "resolve-cwd": "^3.0.0",
102
102
  "slash": "^3.0.0",
@@ -106,33 +106,32 @@
106
106
  "supertap": "^1.0.0",
107
107
  "temp-dir": "^2.0.0",
108
108
  "trim-off-newlines": "^1.0.1",
109
- "update-notifier": "^4.0.0",
109
+ "update-notifier": "^4.1.0",
110
110
  "write-file-atomic": "^3.0.1",
111
111
  "yargs": "^15.1.0"
112
112
  },
113
113
  "devDependencies": {
114
- "@ava/babel": "^1.0.0",
115
- "@types/node": "^10.17.13",
114
+ "@ava/babel": "^1.0.1",
115
+ "@sinonjs/fake-timers": "^6.0.0",
116
116
  "ansi-escapes": "^4.3.0",
117
117
  "delay": "^4.3.0",
118
118
  "esm": "^3.2.25",
119
119
  "execa": "^4.0.0",
120
120
  "get-stream": "^5.1.0",
121
- "lolex": "^5.1.2",
122
121
  "p-event": "^4.1.0",
123
122
  "proxyquire": "^2.1.3",
124
123
  "react": "^16.12.0",
125
124
  "react-test-renderer": "^16.12.0",
126
125
  "replace-string": "^3.0.0",
127
- "sinon": "^8.1.0",
126
+ "sinon": "^8.1.1",
128
127
  "source-map-fixtures": "^2.1.0",
129
128
  "tap": "^14.10.6",
130
129
  "temp-write": "^4.0.0",
131
- "tempy": "^0.3.0",
130
+ "tempy": "^0.4.0",
132
131
  "touch": "^3.1.0",
133
132
  "ts-node": "^8.6.2",
134
133
  "typescript": "^3.7.5",
135
- "xo": "^0.25.3",
134
+ "xo": "^0.26.1",
136
135
  "zen-observable": "^0.8.15"
137
136
  },
138
137
  "xo": {
@@ -1,33 +0,0 @@
1
- const pkg = require('../package.json');
2
- const globs = require('./globs');
3
-
4
- module.exports = ({projectDir}) => {
5
- const ava = {version: pkg.version};
6
- const makeProvider = require('@ava/babel');
7
-
8
- let fatal;
9
- const provider = makeProvider({
10
- negotiateProtocol(identifiers, {version}) {
11
- if (!identifiers.includes('ava-3')) {
12
- fatal = new Error(`This version of AVA (${ava.version}) is not compatible with@ava/babel@${version}`);
13
- return null;
14
- }
15
-
16
- return {
17
- ava,
18
- async findFiles({extensions, patterns}) {
19
- return globs.findFiles({cwd: projectDir, extensions, filePatterns: patterns});
20
- },
21
- identifier: 'ava-3',
22
- normalizeGlobPatterns: globs.normalizePatterns,
23
- projectDir
24
- };
25
- }
26
- });
27
-
28
- if (fatal) {
29
- throw fatal;
30
- }
31
-
32
- return provider;
33
- };