Skip to content

Commit

Permalink
Improve integration with the TypeScript provider
Browse files Browse the repository at this point in the history
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 6c91153
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 19 deletions.
3 changes: 2 additions & 1 deletion eslint-plugin-helper.js
Expand Up @@ -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
})
};

Expand Down
3 changes: 2 additions & 1 deletion lib/cli.js
Expand Up @@ -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);
}
Expand Down Expand Up @@ -411,6 +411,7 @@ exports.run = async () => { // eslint-disable-line complexity
filter,
globs,
projectDir,
providers,
reporter
});
watcher.observeStdin(process.stdin);
Expand Down
9 changes: 8 additions & 1 deletion lib/globs.js
Expand Up @@ -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 = [
Expand Down Expand Up @@ -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.');
}
Expand Down Expand Up @@ -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};
}

Expand Down
6 changes: 4 additions & 2 deletions lib/provider-manager.js
Expand Up @@ -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) {
Expand Down
27 changes: 25 additions & 2 deletions lib/watcher.js
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion test/api.js
Expand Up @@ -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;
Expand Down
75 changes: 66 additions & 9 deletions test/globs.js
Expand Up @@ -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()
};
Expand Down Expand Up @@ -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()
};
Expand Down Expand Up @@ -133,7 +135,8 @@ test('isTest (pattern starts with directory)', t => {
const options = {
...globs.normalizeGlobs({
files: ['bar/**/*'],
extensions: ['js']
extensions: ['js'],
providers: []
}),
cwd: fixture()
};
Expand Down Expand Up @@ -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()
};

Expand Down Expand Up @@ -203,7 +232,8 @@ test('isIgnoredByWatcher with patterns', t => {
...globs.normalizeGlobs({
files: ['**/foo*'],
ignoredByWatcher: ['**/bar*'],
extensions: ['js']
extensions: ['js'],
providers: []
}),
cwd: fixture()
};
Expand All @@ -219,7 +249,8 @@ test('isIgnoredByWatcher (pattern starts with directory)', t => {
...globs.normalizeGlobs({
files: ['**/foo*'],
ignoredByWatcher: ['foo/**/*'],
extensions: ['js']
extensions: ['js'],
providers: []
}),
cwd: fixture()
};
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion test/helper/report.js
Expand Up @@ -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));
Expand Down
2 changes: 1 addition & 1 deletion test/watcher.js
Expand Up @@ -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);
Expand Down

0 comments on commit 6c91153

Please sign in to comment.