fix(providence-analytics): allowlist takes precedence over -mode

This commit is contained in:
Thijs Louisse 2020-08-12 13:34:39 +02:00
parent e9996fa22b
commit 623b10a3d7
6 changed files with 82 additions and 32 deletions

View file

@ -0,0 +1,15 @@
---
'providence-analytics': minor
---
Custom '--allowlist' takes precedence over '--allowlist-mode'
#### Features
- Custom '--allowlist' takes precedence over '--allowlist-mode' when conflicting.
For instance, when running CLI with '--allowlist-mode git --allowlist ./dist'
(and .gitignore contained '/dist'), './dist' will still be analyzed.
#### Patches
- Align naming conventions between CLI and InputDataService.gatherFilesFromDir

View file

@ -117,12 +117,12 @@ async function cli({ cwd } = {}) {
gatherFilesConfig: { gatherFilesConfig: {
extensions: commander.extensions, extensions: commander.extensions,
allowlistMode: commander.allowlistMode, allowlistMode: commander.allowlistMode,
filter: commander.allowlist, allowlist: commander.allowlist,
}, },
gatherFilesConfigReference: { gatherFilesConfigReference: {
extensions: commander.extensions, extensions: commander.extensions,
allowlistMode: commander.allowlistModeReference, allowlistMode: commander.allowlistModeReference,
filter: commander.allowlistReference, allowlist: commander.allowlistReference,
}, },
debugEnabled: commander.debug, debugEnabled: commander.debug,
queryMethod, queryMethod,

View file

@ -22,11 +22,11 @@ async function launchProvidenceWithExtendDocs({
{ {
gatherFilesConfig: { gatherFilesConfig: {
extensions: extensions || ['.js'], extensions: extensions || ['.js'],
filter: allowlist || ['!coverage', '!test'], allowlist: allowlist || ['!coverage', '!test'],
}, },
gatherFilesConfigReference: { gatherFilesConfigReference: {
extensions: extensions || ['.js'], extensions: extensions || ['.js'],
filter: allowlistReference || ['!coverage', '!test'], allowlist: allowlistReference || ['!coverage', '!test'],
}, },
queryMethod: 'ast', queryMethod: 'ast',
report: false, report: false,

View file

@ -281,7 +281,7 @@ class InputDataService {
static get defaultGatherFilesConfig() { static get defaultGatherFilesConfig() {
return { return {
extensions: ['.js'], extensions: ['.js'],
filter: ['!node_modules/**', '!bower_components/**', '!**/*.conf.js', '!**/*.config.js'], allowlist: ['!node_modules/**', '!bower_components/**', '!**/*.conf.js', '!**/*.config.js'],
depth: Infinity, depth: Infinity,
}; };
} }
@ -312,8 +312,11 @@ class InputDataService {
...this.defaultGatherFilesConfig, ...this.defaultGatherFilesConfig,
...customConfig, ...customConfig,
}; };
if (!customConfig.omitDefaultFilter) { if (!customConfig.omitDefaultAllowlist) {
cfg.filter = [...this.defaultGatherFilesConfig.filter, ...(customConfig.filter || [])]; cfg.allowlist = [
...this.defaultGatherFilesConfig.allowlist,
...(customConfig.allowlist || []),
];
} }
const allowlistModes = ['npm', 'git', 'all']; const allowlistModes = ['npm', 'git', 'all'];
if (customConfig.allowlistMode && !allowlistModes.includes(customConfig.allowlistMode)) { if (customConfig.allowlistMode && !allowlistModes.includes(customConfig.allowlistMode)) {
@ -338,12 +341,12 @@ class InputDataService {
const removeFilter = gitIgnorePaths; const removeFilter = gitIgnorePaths;
const keepFilter = npmPackagePaths; const keepFilter = npmPackagePaths;
cfg.filter.forEach(filterEntry => { cfg.allowlist.forEach(allowEntry => {
const { negated, pattern } = isNegatedGlob(filterEntry); const { negated, pattern } = isNegatedGlob(allowEntry);
if (negated) { if (negated) {
removeFilter.push(pattern); removeFilter.push(pattern);
} else { } else {
keepFilter.push(filterEntry); keepFilter.push(allowEntry);
} }
}); });
@ -351,19 +354,34 @@ class InputDataService {
globPattern += `.{${cfg.extensions.map(e => e.slice(1)).join(',')},}`; globPattern += `.{${cfg.extensions.map(e => e.slice(1)).join(',')},}`;
const globRes = multiGlobSync(globPattern); const globRes = multiGlobSync(globPattern);
const filteredGlobRes = globRes.filter(filePath => { let filteredGlobRes;
if (removeFilter.length || keepFilter.length) {
filteredGlobRes = globRes.filter(filePath => {
const localFilePath = filePath.replace(`${startPath}/`, ''); const localFilePath = filePath.replace(`${startPath}/`, '');
if (removeFilter.length) { let shouldRemove = removeFilter.length && anymatch(removeFilter, localFilePath);
const remove = anymatch(removeFilter, localFilePath); let shouldKeep = keepFilter.length && anymatch(keepFilter, localFilePath);
if (remove) {
return false; if (shouldRemove && shouldKeep) {
// Contradicting configs: the one defined by end user takes highest precedence
// If the match came from allowListMode, it loses.
if (allowlistMode === 'git' && anymatch(gitIgnorePaths, localFilePath)) {
// shouldRemove was caused by .gitignore, shouldKeep by custom allowlist
shouldRemove = false;
} else if (allowlistMode === 'npm' && anymatch(npmPackagePaths, localFilePath)) {
// shouldKeep was caused by npm "files", shouldRemove by custom allowlist
shouldKeep = false;
} }
} }
if (removeFilter.length && shouldRemove) {
return false;
}
if (!keepFilter.length) { if (!keepFilter.length) {
return true; return true;
} }
return anymatch(keepFilter, localFilePath); return shouldKeep;
}); });
}
if (!filteredGlobRes || !filteredGlobRes.length) { if (!filteredGlobRes || !filteredGlobRes.length) {
LogService.warn(`No files found for path '${startPath}'`); LogService.warn(`No files found for path '${startPath}'`);
} }

View file

@ -229,7 +229,7 @@ describe('Providence CLI', () => {
it('"-a --allowlist"', async () => { it('"-a --allowlist"', async () => {
await runCli(`${analyzeCmd} -a /mocked/path/example-project`, rootDir); await runCli(`${analyzeCmd} -a /mocked/path/example-project`, rootDir);
expect(pathsArrayFromCsStub.args[0][0]).to.equal('/mocked/path/example-project'); expect(pathsArrayFromCsStub.args[0][0]).to.equal('/mocked/path/example-project');
expect(providenceStub.args[0][1].gatherFilesConfig.filter).to.eql([ expect(providenceStub.args[0][1].gatherFilesConfig.allowlist).to.eql([
'/mocked/path/example-project', '/mocked/path/example-project',
]); ]);
@ -238,7 +238,7 @@ describe('Providence CLI', () => {
await runCli(`${analyzeCmd} --allowlist /mocked/path/example-project`, rootDir); await runCli(`${analyzeCmd} --allowlist /mocked/path/example-project`, rootDir);
expect(pathsArrayFromCsStub.args[0][0]).to.equal('/mocked/path/example-project'); expect(pathsArrayFromCsStub.args[0][0]).to.equal('/mocked/path/example-project');
expect(providenceStub.args[0][1].gatherFilesConfig.filter).to.eql([ expect(providenceStub.args[0][1].gatherFilesConfig.allowlist).to.eql([
'/mocked/path/example-project', '/mocked/path/example-project',
]); ]);
}); });
@ -246,7 +246,7 @@ describe('Providence CLI', () => {
it('"--allowlist-reference"', async () => { it('"--allowlist-reference"', async () => {
await runCli(`${analyzeCmd} --allowlist-reference /mocked/path/example-project`, rootDir); await runCli(`${analyzeCmd} --allowlist-reference /mocked/path/example-project`, rootDir);
expect(pathsArrayFromCsStub.args[0][0]).to.equal('/mocked/path/example-project'); expect(pathsArrayFromCsStub.args[0][0]).to.equal('/mocked/path/example-project');
expect(providenceStub.args[0][1].gatherFilesConfigReference.filter).to.eql([ expect(providenceStub.args[0][1].gatherFilesConfigReference.allowlist).to.eql([
'/mocked/path/example-project', '/mocked/path/example-project',
]); ]);
}); });

View file

@ -121,7 +121,7 @@ describe('InputDataService', () => {
it('allows passing excluded folders', async () => { it('allows passing excluded folders', async () => {
const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', {
extensions: ['.html', '.js'], extensions: ['.html', '.js'],
filter: ['!nested/**'], allowlist: ['!nested/**'],
}); });
expect(globOutput).to.eql([ expect(globOutput).to.eql([
'/fictional/project/index.html', '/fictional/project/index.html',
@ -135,7 +135,7 @@ describe('InputDataService', () => {
it('allows passing excluded files', async () => { it('allows passing excluded files', async () => {
const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', {
extensions: ['.html', '.js'], extensions: ['.html', '.js'],
filter: ['!index.js', '!**/*/index.js'], allowlist: ['!index.js', '!**/*/index.js'],
}); });
expect(globOutput).to.eql([ expect(globOutput).to.eql([
'/fictional/project/index.html', '/fictional/project/index.html',
@ -149,7 +149,7 @@ describe('InputDataService', () => {
it('allows passing exclude globs', async () => { it('allows passing exclude globs', async () => {
const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', {
extensions: ['.html', '.js'], extensions: ['.html', '.js'],
filter: ['!**/*.test.{html,js}'], allowlist: ['!**/*.test.{html,js}'],
}); });
expect(globOutput).to.eql([ expect(globOutput).to.eql([
'/fictional/project/index.html', '/fictional/project/index.html',
@ -183,7 +183,7 @@ describe('InputDataService', () => {
'./added/file.js': '', './added/file.js': '',
}); });
const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', {
filter: ['*', 'added/**/*'], allowlist: ['*', 'added/**/*'],
}); });
expect(globOutput).to.eql([ expect(globOutput).to.eql([
'/fictional/project/added/file.js', '/fictional/project/added/file.js',
@ -311,7 +311,24 @@ build/
]); ]);
}); });
describe('Default filter', () => { it('custom "allowlist" will take precedence over "allowlistMode"', async () => {
mockProject({
'./dist/bundle.js': '', // generated by build step
'./src/a.js': '',
'./src/b.js': '',
'.gitignore': '/dist', // Because we have a .gitignore, allowlistMode will be git
'./package.json': JSON.stringify({
files: ['dist'], // This will not be considered by default, unless explicitly configured in allowlist
}),
});
const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', {
allowlist: ['dist/**'],
allowlistMode: 'git', // for clarity, (would also be autodetected if not provided)
});
expect(globOutput).to.eql(['/fictional/project/dist/bundle.js']);
});
describe('Default allowlist', () => {
it('merges default config filter with configured filter', async () => { it('merges default config filter with configured filter', async () => {
mockProject({ mockProject({
'./node_modules/root-lvl.js': '', './node_modules/root-lvl.js': '',
@ -320,7 +337,7 @@ build/
'./omit.js': '', './omit.js': '',
}); });
const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', {
filter: ['added*'], allowlist: ['added*'],
}); });
expect(globOutput).to.eql(['/fictional/project/added.js']); expect(globOutput).to.eql(['/fictional/project/added.js']);
}); });
@ -335,8 +352,8 @@ build/
'./omit.js': '', './omit.js': '',
}); });
const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', { const globOutput = InputDataService.gatherFilesFromDir('/fictional/project', {
filter: ['!omit*'], allowlist: ['!omit*'],
omitDefaultFilter: true, omitDefaultAllowlist: true,
}); });
expect(globOutput).to.eql([ expect(globOutput).to.eql([
'/fictional/project/abc.config.js', '/fictional/project/abc.config.js',