Skip to content
Permalink
Browse files

Improve integration with the TypeScript provider

This allows the watcher to cater for the TypeScript build process.

It also allows the provider to impact file selection.
  • Loading branch information
novemberborn committed Feb 2, 2020
1 parent 7ccb208 commit 6c9115308c4caa4dd0f3966a521be9293d64e799
Showing with 110 additions and 19 deletions.
  1. +2 βˆ’1 eslint-plugin-helper.js
  2. +2 βˆ’1 lib/cli.js
  3. +8 βˆ’1 lib/globs.js
  4. +4 βˆ’2 lib/provider-manager.js
  5. +25 βˆ’2 lib/watcher.js
  6. +1 βˆ’1 test/api.js
  7. +66 βˆ’9 test/globs.js
  8. +1 βˆ’1 test/helper/report.js
  9. +1 βˆ’1 test/watcher.js
@@ -59,7 +59,8 @@ function load(projectDir, overrides) {
cwd: projectDir,
...normalizeGlobs({
extensions,
files: overrides && overrides.files ? overrides.files : conf.files
files: overrides && overrides.files ? overrides.files : conf.files,
providers
})
};

@@ -322,7 +322,7 @@ exports.run = async () => { // eslint-disable-line complexity

let globs;
try {
globs = normalizeGlobs({files: conf.files, ignoredByWatcher: conf.ignoredByWatcher, extensions});
globs = normalizeGlobs({files: conf.files, ignoredByWatcher: conf.ignoredByWatcher, extensions, providers});
} catch (error) {
exit(error.message);
}
@@ -411,6 +411,7 @@ exports.run = async () => { // eslint-disable-line complexity
filter,
globs,
projectDir,
providers,
reporter
});
watcher.observeStdin(process.stdin);
@@ -4,6 +4,7 @@ const globby = require('globby');
const ignoreByDefault = require('ignore-by-default');
const picomatch = require('picomatch');
const slash = require('slash');
const {levels: providerLevels} = require('./provider-manager');

const defaultIgnorePatterns = [...ignoreByDefault.directories(), '**/node_modules'];
const defaultPicomatchIgnorePatterns = [
@@ -43,7 +44,7 @@ function normalizePatterns(patterns) {

exports.normalizePatterns = normalizePatterns;

function normalizeGlobs({extensions, files: filePatterns, ignoredByWatcher: ignoredByWatcherPatterns}) {
function normalizeGlobs({extensions, files: filePatterns, ignoredByWatcher: ignoredByWatcherPatterns, providers}) {
if (filePatterns !== undefined && (!Array.isArray(filePatterns) || filePatterns.length === 0)) {
throw new Error('The \'files\' configuration must be an array containing glob patterns.');
}
@@ -83,6 +84,12 @@ function normalizeGlobs({extensions, files: filePatterns, ignoredByWatcher: igno
ignoredByWatcherPatterns = [...defaultIgnoredByWatcherPatterns];
}

for (const {level, main} of providers) {
if (level >= providerLevels.pathRewrites) {
({filePatterns, ignoredByWatcherPatterns} = main.updateGlobs({filePatterns, ignoredByWatcherPatterns}));
}
}

return {extensions, filePatterns, ignoredByWatcherPatterns};
}

@@ -2,13 +2,15 @@ const pkg = require('../package.json');
const globs = require('./globs');

const levels = {
ava3: 1
ava3: 1,
pathRewrites: 2
};

exports.levels = levels;

const levelsByProtocol = {
'ava-3': levels.ava3
'ava-3': levels.ava3,
'ava-3.2': levels.pathRewrites
};

function load(providerModule, projectDir) {
@@ -6,6 +6,7 @@ const diff = require('lodash/difference');
const flatten = require('lodash/flatten');
const chalk = require('./chalk').get();
const {applyTestFileFilter, classify, getChokidarIgnorePatterns} = require('./globs');
const {levels: providerLevels} = require('./provider-manager');

function rethrowAsync(err) {
// Don't swallow exceptions. Note that any
@@ -77,13 +78,14 @@ class TestDependency {
}

class Watcher {
constructor({api, filter = [], globs, projectDir, reporter}) {
constructor({api, filter = [], globs, projectDir, providers, reporter}) {
this.debouncer = new Debouncer(this);

this.clearLogOnNextRun = true;
this.runVector = 0;
this.previousFiles = [];
this.globs = {cwd: projectDir, ...globs};
this.providers = providers.filter(({level}) => level >= providerLevels.pathRewrites);
this.run = (specificFiles = [], updateSnapshots = false) => {
const clearLogOnNextRun = this.clearLogOnNextRun && this.runVector > 0;
if (this.runVector > 0) {
@@ -190,6 +192,15 @@ class Watcher {
}

updateTestDependencies(file, dependencies) {
// Ensure the rewritten test file path is included in the dependencies,
// since changes to non-rewritten paths are ignored.
for (const {main} of this.providers) {
const rewritten = main.resolveTestFile(file);
if (!dependencies.includes(rewritten)) {
dependencies = [rewritten, ...dependencies];
}
}

if (dependencies.length === 0) {
this.testDependencies = this.testDependencies.filter(dep => dep.file !== file);
return;
@@ -358,7 +369,7 @@ class Watcher {
const {dirtyStates} = this;
this.dirtyStates = {};

const dirtyPaths = Object.keys(dirtyStates).filter(path => {
let dirtyPaths = Object.keys(dirtyStates).filter(path => {
if (this.touchedFiles.has(path)) {
debug('Ignoring known touched file %s', path);
this.touchedFiles.delete(path);
@@ -367,6 +378,18 @@ class Watcher {

return true;
});

for (const {main} of this.providers) {
dirtyPaths = dirtyPaths.filter(path => {
if (main.ignoreChange(path)) {
debug('Ignoring changed file %s', path);
return false;
}

return true;
});
}

const dirtyHelpersAndSources = [];
const dirtyTests = [];
for (const filePath of dirtyPaths) {
@@ -24,7 +24,7 @@ function apiCreator(options = {}) {
options.concurrency = 2;
options.extensions = options.extensions || ['js'];
options.experiments = {};
options.globs = normalizeGlobs({files: options.files, ignoredByWatcher: options.ignoredByWatcher, extensions: options.extensions});
options.globs = normalizeGlobs({files: options.files, ignoredByWatcher: options.ignoredByWatcher, extensions: options.extensions, providers: []});
const instance = new Api(options);

return instance;
@@ -17,15 +17,16 @@ function fixture(...args) {
}

test('ignores relativeness in patterns', t => {
const {filePatterns} = globs.normalizeGlobs({files: ['./foo.js', '!./bar'], extensions: ['js']});
const {filePatterns} = globs.normalizeGlobs({files: ['./foo.js', '!./bar'], extensions: ['js'], providers: []});
t.deepEqual(filePatterns, ['foo.js', '!bar']);
t.end();
});

test('isTest with defaults', t => {
const options = {
...globs.normalizeGlobs({
extensions: ['js']
extensions: ['js'],
providers: []
}),
cwd: fixture()
};
@@ -99,7 +100,8 @@ test('isTest with patterns', t => {
const options = {
...globs.normalizeGlobs({
files: ['**/foo*.js', '**/foo*/**/*.js', '!**/fixtures', '!**/helpers'],
extensions: ['js']
extensions: ['js'],
providers: []
}),
cwd: fixture()
};
@@ -133,7 +135,8 @@ test('isTest (pattern starts with directory)', t => {
const options = {
...globs.normalizeGlobs({
files: ['bar/**/*'],
extensions: ['js']
extensions: ['js'],
providers: []
}),
cwd: fixture()
};
@@ -163,9 +166,35 @@ test('isTest (pattern starts with directory)', t => {
t.end();
});

test('isTest after provider modifications', t => {
const options = {
...globs.normalizeGlobs({
extensions: ['js'],
providers: [{
level: 2,
main: {
updateGlobs({filePatterns, ignoredByWatcherPatterns}) {
t.true(filePatterns.length > 0);
t.true(ignoredByWatcherPatterns.length > 0);
return {
filePatterns: ['foo.js'],
ignoredByWatcherPatterns
};
}
}
}]
}),
cwd: fixture()
};

t.true(globs.classify(fixture('foo.js'), options).isTest);
t.false(globs.classify(fixture('bar.js'), options).isTest);
t.end();
});

test('isIgnoredByWatcher with defaults', t => {
const options = {
...globs.normalizeGlobs({extensions: ['js']}),
...globs.normalizeGlobs({extensions: ['js'], providers: []}),
cwd: fixture()
};

@@ -203,7 +232,8 @@ test('isIgnoredByWatcher with patterns', t => {
...globs.normalizeGlobs({
files: ['**/foo*'],
ignoredByWatcher: ['**/bar*'],
extensions: ['js']
extensions: ['js'],
providers: []
}),
cwd: fixture()
};
@@ -219,7 +249,8 @@ test('isIgnoredByWatcher (pattern starts with directory)', t => {
...globs.normalizeGlobs({
files: ['**/foo*'],
ignoredByWatcher: ['foo/**/*'],
extensions: ['js']
extensions: ['js'],
providers: []
}),
cwd: fixture()
};
@@ -230,6 +261,32 @@ test('isIgnoredByWatcher (pattern starts with directory)', t => {
t.end();
});

test('isIgnoredByWatcher after provider modifications', t => {
const options = {
...globs.normalizeGlobs({
extensions: ['js'],
providers: [{
level: 2,
main: {
updateGlobs({filePatterns, ignoredByWatcherPatterns}) {
t.true(filePatterns.length > 0);
t.true(ignoredByWatcherPatterns.length > 0);
return {
filePatterns,
ignoredByWatcherPatterns: ['foo.js']
};
}
}
}]
}),
cwd: fixture()
};

t.true(globs.classify(fixture('foo.js'), options).isIgnoredByWatcher);
t.false(globs.classify(fixture('bar.js'), options).isIgnoredByWatcher);
t.end();
});

test('findFiles finds non-ignored files (just .js)', async t => {
const fixtureDir = fixture('default-patterns');
process.chdir(fixtureDir);
@@ -251,7 +308,7 @@ test('findFiles finds non-ignored files (just .js)', async t => {

const actual = await globs.findFiles({
cwd: fixtureDir,
...globs.normalizeGlobs({files: ['!**/fixtures/*.*', '!**/helpers/*.*'], extensions: ['js']})
...globs.normalizeGlobs({files: ['!**/fixtures/*.*', '!**/helpers/*.*'], extensions: ['js'], providers: []})
});
actual.sort();
t.deepEqual(actual, expected);
@@ -270,7 +327,7 @@ test('findFiles finds non-ignored files (.js, .jsx)', async t => {

const actual = await globs.findFiles({
cwd: fixtureDir,
...globs.normalizeGlobs({files: ['!**/fixtures/*', '!**/helpers/*'], extensions: ['js', 'jsx']})
...globs.normalizeGlobs({files: ['!**/fixtures/*', '!**/helpers/*'], extensions: ['js', 'jsx'], providers: []})
});
actual.sort();
t.deepEqual(actual, expected);
@@ -102,7 +102,7 @@ const run = (type, reporter, match = []) => {
pattern = '*.ts';
}

options.globs = normalizeGlobs({extensions: options.extensions});
options.globs = normalizeGlobs({extensions: options.extensions, providers: []});

const api = createApi(options);
api.on('run', plan => reporter.startRun(plan));
@@ -145,7 +145,7 @@ group('chokidar', (beforeEach, test, group) => {
Subject = proxyWatcher();
});

const start = ignoredByWatcher => new Subject({reporter, api, filter: [], globs: normalizeGlobs({files, ignoredByWatcher, extensions: ['js']}), projectDir: process.cwd()});
const start = ignoredByWatcher => new Subject({reporter, api, filter: [], globs: normalizeGlobs({files, ignoredByWatcher, extensions: ['js'], providers: []}), projectDir: process.cwd(), providers: []});

const emitChokidar = (event, path) => {
chokidarEmitter.emit('all', event, path);

0 comments on commit 6c91153

Please sign in to comment.
You can’t perform that action at this time.