fix(linter): fix exclude pattern for tslint and eslint schematics (#3113)

This commit is contained in:
Rares Matei 2020-06-05 21:04:53 +01:00 committed by GitHub
parent 1f90080d67
commit 5a59f090fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 274 additions and 35 deletions

View File

@ -286,7 +286,7 @@ If you are using `Protractor` for E2E testing:
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": "apps/<app name>-e2e/tsconfig.e2e.json",
"exclude": ["**/node_modules/**", "!apps/<app name>-e2e/**"]
"exclude": ["**/node_modules/**", "!apps/<app name>-e2e/**/*"]
}
}
}

View File

@ -3,7 +3,7 @@ module.exports = {
transform: {
'^.+\\.(ts|js|html)$': 'ts-jest',
},
resolver: '@nrwl/jest/plugins/resolver',
resolver: '../../scripts/patched-jest-resolver.js',
moduleFileExtensions: ['ts', 'js', 'html'],
coverageReporters: ['html'],
maxWorkers: 2,

View File

@ -24,10 +24,10 @@ describe('app', () => {
expect(
workspaceJson.projects['my-app'].architect.lint.options.exclude
).toEqual(['**/node_modules/**', '!apps/my-app/**']);
).toEqual(['**/node_modules/**', '!apps/my-app/**/*']);
expect(
workspaceJson.projects['my-app-e2e'].architect.lint.options.exclude
).toEqual(['**/node_modules/**', '!apps/my-app-e2e/**']);
).toEqual(['**/node_modules/**', '!apps/my-app-e2e/**/*']);
});
it('should remove the e2e target on the application', async () => {
@ -175,11 +175,11 @@ describe('app', () => {
expect(
workspaceJson.projects['my-dir-my-app'].architect.lint.options.exclude
).toEqual(['**/node_modules/**', '!apps/my-dir/my-app/**']);
).toEqual(['**/node_modules/**', '!apps/my-dir/my-app/**/*']);
expect(
workspaceJson.projects['my-dir-my-app-e2e'].architect.lint.options
.exclude
).toEqual(['**/node_modules/**', '!apps/my-dir/my-app-e2e/**']);
).toEqual(['**/node_modules/**', '!apps/my-dir/my-app-e2e/**/*']);
});
it('should update nx.json', async () => {
@ -456,7 +456,7 @@ describe('app', () => {
builder: '@angular-devkit/build-angular:tslint',
options: {
tsConfig: 'apps/my-app-e2e/tsconfig.e2e.json',
exclude: ['**/node_modules/**', '!apps/my-app-e2e/**'],
exclude: ['**/node_modules/**', '!apps/my-app-e2e/**/*'],
},
},
},

View File

@ -510,7 +510,7 @@ function updateProject(options: NormalizedSchema): Rule {
join(normalize(options.appProjectRoot), 'e2e/tsconfig.json')
);
fixedProject.architect.lint.options.exclude.push(
'!' + join(normalize(options.appProjectRoot), '**')
'!' + join(normalize(options.appProjectRoot), '**/*')
);
if (options.e2eTestRunner === 'none') {
@ -618,7 +618,7 @@ function updateE2eProject(options: NormalizedSchema): Rule {
tsConfig: `${options.e2eProjectRoot}/tsconfig.e2e.json`,
exclude: [
'**/node_modules/**',
'!' + join(normalize(options.e2eProjectRoot), '**'),
'!' + join(normalize(options.e2eProjectRoot), '**/*'),
],
},
},

View File

@ -137,7 +137,7 @@ export function updateProject(options: NormalizedSchema): Rule {
path !== join(normalize(options.projectRoot), 'tsconfig.spec.json')
);
fixedProject.architect.lint.options.exclude.push(
'!' + join(normalize(options.projectRoot), '**')
'!' + join(normalize(options.projectRoot), '**/*')
);
json.projects[options.name] = fixedProject;

View File

@ -98,7 +98,7 @@ describe('lib', () => {
]);
expect(
workspaceJson.projects['my-lib'].architect.lint.options.exclude
).toEqual(['**/node_modules/**', '!libs/my-lib/**']);
).toEqual(['**/node_modules/**', '!libs/my-lib/**/*']);
});
it('should remove "build" target from workspace.json when a library is not publishable', async () => {
@ -452,7 +452,7 @@ describe('lib', () => {
]);
expect(
workspaceJson.projects['my-dir-my-lib'].architect.lint.options.exclude
).toEqual(['**/node_modules/**', '!libs/my-dir/my-lib/**']);
).toEqual(['**/node_modules/**', '!libs/my-dir/my-lib/**/*']);
});
it('should update tsconfig.json', async () => {
@ -969,7 +969,7 @@ describe('lib', () => {
]);
expect(
workspaceJson.projects['my-lib'].architect.lint.options.exclude
).toEqual(['**/node_modules/**', '!libs/my-lib/**']);
).toEqual(['**/node_modules/**', '!libs/my-lib/**/*']);
});
it('should generate module spec when addModuleSpec is specified', async () => {

View File

@ -51,7 +51,7 @@ describe('schematic:cypress-project', () => {
builder: '@angular-devkit/build-angular:tslint',
options: {
tsConfig: ['apps/my-app-e2e/tsconfig.e2e.json'],
exclude: ['**/node_modules/**', '!apps/my-app-e2e/**'],
exclude: ['**/node_modules/**', '!apps/my-app-e2e/**/*'],
},
});
expect(project.architect.e2e).toEqual({
@ -83,7 +83,7 @@ describe('schematic:cypress-project', () => {
options: {
linter: 'eslint',
tsConfig: ['apps/my-app-e2e/tsconfig.e2e.json'],
exclude: ['**/node_modules/**', '!apps/my-app-e2e/**'],
exclude: ['**/node_modules/**', '!apps/my-app-e2e/**/*'],
},
});
});
@ -160,7 +160,7 @@ describe('schematic:cypress-project', () => {
builder: '@angular-devkit/build-angular:tslint',
options: {
tsConfig: ['apps/my-dir/my-app-e2e/tsconfig.e2e.json'],
exclude: ['**/node_modules/**', '!apps/my-dir/my-app-e2e/**'],
exclude: ['**/node_modules/**', '!apps/my-dir/my-app-e2e/**/*'],
},
});

View File

@ -21,7 +21,7 @@ describe('lib', () => {
expect(workspaceJson.projects['my-lib'].architect.lint).toEqual({
builder: '@angular-devkit/build-angular:tslint',
options: {
exclude: ['**/node_modules/**', '!libs/my-lib/**'],
exclude: ['**/node_modules/**', '!libs/my-lib/**/*'],
tsConfig: [
'libs/my-lib/tsconfig.lib.json',
'libs/my-lib/tsconfig.spec.json',
@ -268,7 +268,7 @@ describe('lib', () => {
expect(workspaceJson.projects['my-dir-my-lib'].architect.lint).toEqual({
builder: '@angular-devkit/build-angular:tslint',
options: {
exclude: ['**/node_modules/**', '!libs/my-dir/my-lib/**'],
exclude: ['**/node_modules/**', '!libs/my-dir/my-lib/**/*'],
tsConfig: [
'libs/my-dir/my-lib/tsconfig.lib.json',
'libs/my-dir/my-lib/tsconfig.spec.json',

View File

@ -59,7 +59,7 @@ describe('app', () => {
'apps/my-node-app/tsconfig.app.json',
'apps/my-node-app/tsconfig.spec.json',
],
exclude: ['**/node_modules/**', '!apps/my-node-app/**'],
exclude: ['**/node_modules/**', '!apps/my-node-app/**/*'],
},
});
expect(workspaceJson.projects['my-node-app-e2e']).toBeUndefined();
@ -127,7 +127,7 @@ describe('app', () => {
'apps/my-dir/my-node-app/tsconfig.app.json',
'apps/my-dir/my-node-app/tsconfig.spec.json',
],
exclude: ['**/node_modules/**', '!apps/my-dir/my-node-app/**'],
exclude: ['**/node_modules/**', '!apps/my-dir/my-node-app/**/*'],
},
});

View File

@ -21,7 +21,7 @@ describe('lib', () => {
expect(workspaceJson.projects['my-lib'].architect.lint).toEqual({
builder: '@angular-devkit/build-angular:tslint',
options: {
exclude: ['**/node_modules/**', '!libs/my-lib/**'],
exclude: ['**/node_modules/**', '!libs/my-lib/**/*'],
tsConfig: [
'libs/my-lib/tsconfig.lib.json',
'libs/my-lib/tsconfig.spec.json',
@ -159,7 +159,7 @@ describe('lib', () => {
expect(workspaceJson.projects['my-dir-my-lib'].architect.lint).toEqual({
builder: '@angular-devkit/build-angular:tslint',
options: {
exclude: ['**/node_modules/**', '!libs/my-dir/my-lib/**'],
exclude: ['**/node_modules/**', '!libs/my-dir/my-lib/**/*'],
tsConfig: [
'libs/my-dir/my-lib/tsconfig.lib.json',
'libs/my-dir/my-lib/tsconfig.spec.json',

View File

@ -44,7 +44,7 @@ describe('NxPlugin plugin', () => {
expect(project.architect.lint).toEqual({
builder: '@angular-devkit/build-angular:tslint',
options: {
exclude: ['**/node_modules/**', '!libs/my-plugin/**'],
exclude: ['**/node_modules/**', '!libs/my-plugin/**/*'],
tsConfig: [
'libs/my-plugin/tsconfig.lib.json',
'libs/my-plugin/tsconfig.spec.json',

View File

@ -294,7 +294,7 @@ describe('app', () => {
expect(workspaceJson.projects['my-app'].architect.lint).toEqual({
builder: '@angular-devkit/build-angular:tslint',
options: {
exclude: ['**/node_modules/**', '!apps/my-app/**'],
exclude: ['**/node_modules/**', '!apps/my-app/**/*'],
tsConfig: [
'apps/my-app/tsconfig.app.json',
'apps/my-app/tsconfig.spec.json',

View File

@ -20,7 +20,7 @@ describe('lib', () => {
expect(workspaceJson.projects['my-lib'].architect.lint).toEqual({
builder: '@angular-devkit/build-angular:tslint',
options: {
exclude: ['**/node_modules/**', '!libs/my-lib/**'],
exclude: ['**/node_modules/**', '!libs/my-lib/**/*'],
tsConfig: [
'libs/my-lib/tsconfig.lib.json',
'libs/my-lib/tsconfig.spec.json',
@ -184,7 +184,7 @@ describe('lib', () => {
expect(workspaceJson.projects['my-dir-my-lib'].architect.lint).toEqual({
builder: '@angular-devkit/build-angular:tslint',
options: {
exclude: ['**/node_modules/**', '!libs/my-dir/my-lib/**'],
exclude: ['**/node_modules/**', '!libs/my-dir/my-lib/**/*'],
tsConfig: [
'libs/my-dir/my-lib/tsconfig.lib.json',
'libs/my-dir/my-lib/tsconfig.spec.json',

View File

@ -282,7 +282,7 @@ describe('app', () => {
expect(workspaceJson.projects['my-app'].architect.lint).toEqual({
builder: '@angular-devkit/build-angular:tslint',
options: {
exclude: ['**/node_modules/**', '!apps/my-app/**'],
exclude: ['**/node_modules/**', '!apps/my-app/**/*'],
tsConfig: [
'apps/my-app/tsconfig.app.json',
'apps/my-app/tsconfig.spec.json',

View File

@ -79,6 +79,11 @@
"version": "9.4.0-beta.1",
"description": "Remove config builder option when using eslint to enable automatic detection",
"factory": "./src/migrations/update-9-4-0/update-eslint-config"
},
"update-linters-exclude": {
"version": "9.4.0-beta.1",
"description": "Fix exclude patterns in tslint and eslint to ensure it does not break linting when a files option is defined",
"factory": "./src/migrations/update-9-4-0/update-linters-exclude"
}
},
"packageJsonUpdates": {

View File

@ -40,7 +40,7 @@ describe('Update 8.2.0', () => {
builder: '@angular-devkit/build-angular:tslint',
options: {
tsConfig: ['my-app/tsconfig.json'],
exclude: ['**/node_modules/**', '!my-app/**'],
exclude: ['**/node_modules/**', '!my-app/**/*'],
},
});
});

View File

@ -9,7 +9,7 @@ const addExcludes = updateWorkspace((workspace) => {
if (target.builder !== '@angular-devkit/build-angular:tslint') {
return;
}
const exceptRootGlob = '!' + join(normalize(project.root), '**');
const exceptRootGlob = '!' + join(normalize(project.root), '**/*');
if (!target.options.exclude) {
target.options.exclude = [];

View File

@ -0,0 +1,154 @@
import { Tree } from '@angular-devkit/schematics';
import { readWorkspace } from '@nrwl/workspace';
import { createEmptyWorkspace } from '@nrwl/workspace/testing';
import { callRule, runMigration } from '../../utils/testing';
import { updateWorkspace } from '@nrwl/workspace/src/utils/workspace';
describe('Update eslint and tslint exclude pattern for 9.4.0', () => {
let tree: Tree;
beforeEach(async () => {
tree = Tree.empty();
tree = createEmptyWorkspace(tree);
tree = await callRule(
updateWorkspace((workspace) => {
workspace.projects.add({
name: 'proj1',
root: 'apps/proj1',
architect: {
lint: {
builder: 'some-other-linter',
options: {
exclude: ['**/node_modules/**', '!apps/proj1/**'],
},
},
},
});
workspace.projects.add({
name: 'proj2',
root: 'apps/proj2',
architect: {
lint: {
builder: '@nrwl/linter:lint',
options: {
linter: 'eslint',
exclude: ['!apps/proj2/**', '**/node_modules/**'],
},
},
},
});
workspace.projects.add({
name: 'proj3',
root: 'apps/proj3',
architect: {
lint: {
builder: '@angular-devkit/build-angular:tslint',
options: {
linter: 'eslint',
exclude: ['!apps/proj3/**', '**/node_modules/**'],
},
},
},
});
workspace.projects.add({
name: 'proj4',
root: 'apps/proj4',
architect: {
lint: {
builder: '@angular-devkit/build-angular:tslint',
options: {
linter: 'tslint',
exclude: ['!apps/proj4/*', '**/node_modules/**'],
},
},
},
});
workspace.projects.add({
name: 'proj5',
root: 'apps/proj5',
architect: {
lint: {
builder: '@nrwl/linter:lint',
options: {
linter: 'tslint',
exclude: ['!apps/proj5/*', '**/node_modules/**'],
},
},
},
});
workspace.projects.add({
name: 'proj6',
root: 'libs/proj6',
architect: {
lint: {
builder: '@angular-devkit/build-angular:tslint',
options: {
linter: 'eslint',
exclude: [
'**/node_modules/**',
'libs/proj6/**',
'!libs/proj6/**',
],
},
},
},
});
workspace.projects.add({
name: 'proj7',
root: 'libs/proj7',
architect: {
lint: {
builder: '@nrwl/linter:lint',
options: {
linter: 'eslint',
exclude: [
'**/node_modules/**',
'libs/proj7/**',
'!libs/proj7/**',
],
},
},
},
});
}),
tree
);
});
it('should fix the exclude option when using tslint', async () => {
const result = await runMigration('update-linters-exclude', {}, tree);
const json = readWorkspace(result);
expect(json.projects.proj1.architect.lint.options.exclude).toEqual([
'**/node_modules/**',
'!apps/proj1/**',
]);
expect(json.projects.proj2.architect.lint.options.exclude).toEqual([
'!apps/proj2/**/*',
'**/node_modules/**',
]);
expect(json.projects.proj3.architect.lint.options.exclude).toEqual([
'!apps/proj3/**/*',
'**/node_modules/**',
]);
expect(json.projects.proj4.architect.lint.options.exclude).toEqual([
'!apps/proj4/*',
'**/node_modules/**',
]);
expect(json.projects.proj5.architect.lint.options.exclude).toEqual([
'!apps/proj5/*',
'**/node_modules/**',
]);
expect(json.projects.proj6.architect.lint.options.exclude).toEqual([
'**/node_modules/**',
'libs/proj6/**',
'!libs/proj6/**/*',
]);
expect(json.projects.proj7.architect.lint.options.exclude).toEqual([
'**/node_modules/**',
'libs/proj7/**',
'!libs/proj7/**/*',
]);
});
});

View File

@ -0,0 +1,29 @@
import { chain, SchematicContext, Tree } from '@angular-devkit/schematics';
import { formatFiles, updateWorkspaceInTree } from '@nrwl/workspace';
function updateExcludePattern(host: Tree, context: SchematicContext) {
return updateWorkspaceInTree((json) => {
Object.keys(json.projects).forEach((name) => {
const p = json.projects[name];
const faultyPattern = `!${p.root}/**`;
const builders = [
'@nrwl/linter:lint',
'@angular-devkit/build-angular:tslint',
];
if (
builders.includes(p.architect?.lint.builder) &&
p.architect?.lint.options?.exclude.includes(faultyPattern)
) {
const index: number = p.architect?.lint.options?.exclude.indexOf(
faultyPattern
);
p.architect.lint.options.exclude[index] = faultyPattern + '/*';
}
});
return json;
});
}
export default function () {
return chain([updateExcludePattern, formatFiles()]);
}

View File

@ -22,7 +22,7 @@ describe('lib', () => {
expect(workspaceJson.projects['my-lib'].architect.lint).toEqual({
builder: '@angular-devkit/build-angular:tslint',
options: {
exclude: ['**/node_modules/**', '!libs/my-lib/**'],
exclude: ['**/node_modules/**', '!libs/my-lib/**/*'],
tsConfig: [
'libs/my-lib/tsconfig.lib.json',
'libs/my-lib/tsconfig.spec.json',
@ -172,7 +172,7 @@ describe('lib', () => {
expect(workspaceJson.projects['my-dir-my-lib'].architect.lint).toEqual({
builder: '@angular-devkit/build-angular:tslint',
options: {
exclude: ['**/node_modules/**', '!libs/my-dir/my-lib/**'],
exclude: ['**/node_modules/**', '!libs/my-dir/my-lib/**/*'],
tsConfig: [
'libs/my-dir/my-lib/tsconfig.lib.json',
'libs/my-dir/my-lib/tsconfig.spec.json',

View File

@ -94,7 +94,7 @@ describe('updateWorkspace Rule', () => {
'apps/my-source/tsconfig.app.json',
'apps/my-source/tsconfig.spec.json',
],
exclude: ['**/node_modules/**', '!apps/my-source/**'],
exclude: ['**/node_modules/**', '!apps/my-source/**/*'],
},
},
test: {
@ -129,7 +129,7 @@ describe('updateWorkspace Rule', () => {
builder: '@angular-devkit/build-angular:tslint',
options: {
tsConfig: ['apps/my-source-e2e/tsconfig.e2e.json'],
exclude: ['**/node_modules/**', '!apps/my-source-e2e/**'],
exclude: ['**/node_modules/**', '!apps/my-source-e2e/**/*'],
},
},
},

View File

@ -30,7 +30,7 @@ export function generateProjectLint(
builder: '@angular-devkit/build-angular:tslint',
options: {
tsConfig: [tsConfigPath],
exclude: ['**/node_modules/**', '!' + projectRoot + '/**'],
exclude: ['**/node_modules/**', '!' + projectRoot + '/**/*'],
},
};
} else if (linter === Linter.EsLint) {
@ -42,7 +42,7 @@ export function generateProjectLint(
// nested configurations.
linter: 'eslint',
tsConfig: [tsConfigPath],
exclude: ['**/node_modules/**', '!' + projectRoot + '/**'],
exclude: ['**/node_modules/**', '!' + projectRoot + '/**/*'],
},
};
} else {

View File

@ -0,0 +1,51 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const path_1 = require('path');
const ts = require('typescript');
const defaultResolver_1 = require('jest-resolve/build/defaultResolver');
function getCompilerSetup(rootDir) {
const tsConfigPath =
ts.findConfigFile(rootDir, ts.sys.fileExists, 'tsconfig.spec.json') ||
ts.findConfigFile(rootDir, ts.sys.fileExists, 'tsconfig.test.json') ||
ts.findConfigFile(rootDir, ts.sys.fileExists, 'tsconfig.jest.json');
if (!tsConfigPath) {
console.error(
`Cannot locate a tsconfig.spec.json. Please create one at ${rootDir}/tsconfig.spec.json`
);
}
const readResult = ts.readConfigFile(tsConfigPath, ts.sys.readFile);
const config = ts.parseJsonConfigFileContent(
readResult.config,
ts.sys,
path_1.dirname(tsConfigPath)
);
const compilerOptions = config.options;
const host = ts.createCompilerHost(compilerOptions, true);
return { compilerOptions, host };
}
let compilerSetup;
module.exports = function (path, options) {
const ext = path_1.extname(path);
if (
ext === '.css' ||
ext === '.scss' ||
ext === '.sass' ||
ext === '.less' ||
ext === '.styl'
) {
return require.resolve('identity-obj-proxy');
}
// Try to use the defaultResolver
try {
if (path.indexOf('@nrwl/workspace') > -1) {
throw 'Reference to local Nx package found. Use local version instead.';
}
return defaultResolver_1.default(path, options);
} catch (e) {
// Fallback to using typescript
compilerSetup = compilerSetup || getCompilerSetup(options.rootDir);
const { compilerOptions, host } = compilerSetup;
return ts.resolveModuleName(path, options.basedir, compilerOptions, host)
.resolvedModule.resolvedFileName;
}
};