From fb3a4b6238921c74e31d100f609f3b5da1ec577e Mon Sep 17 00:00:00 2001 From: Stephen Cavaliere Date: Thu, 9 Feb 2017 17:11:40 -0500 Subject: [PATCH] feat(@angular/cli): add ability to exclude files and directories (#4437) Fixes #4350 --- .../cli/blueprints/ng2/files/package.json | 1 + packages/@angular/cli/lib/config/schema.json | 32 ++++++- packages/@angular/cli/tasks/lint.ts | 85 +++++++++++++------ tests/e2e/tests/lint/lint-with-exclude.ts | 20 +++++ 4 files changed, 111 insertions(+), 27 deletions(-) create mode 100644 tests/e2e/tests/lint/lint-with-exclude.ts diff --git a/packages/@angular/cli/blueprints/ng2/files/package.json b/packages/@angular/cli/blueprints/ng2/files/package.json index 777e531ca924..858eea45969c 100644 --- a/packages/@angular/cli/blueprints/ng2/files/package.json +++ b/packages/@angular/cli/blueprints/ng2/files/package.json @@ -7,6 +7,7 @@ "ng": "ng", "start": "ng serve", "test": "ng test", + "lint": "ng lint", "pree2e": "webdriver-manager update --standalone false --gecko false", "e2e": "protractor" }, diff --git a/packages/@angular/cli/lib/config/schema.json b/packages/@angular/cli/lib/config/schema.json index 3ebdc3a5a476..6d7819d12e3e 100644 --- a/packages/@angular/cli/lib/config/schema.json +++ b/packages/@angular/cli/lib/config/schema.json @@ -181,18 +181,46 @@ "type": "object", "properties": { "files": { - "type": "string" + "description": "File glob(s) to lint.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ], + "default": [] }, "project": { + "description": "Location of the tsconfig.json project file. Will also use as files to lint if 'files' property not present.", "type": "string" }, "tslintConfig": { + "description": "Location of the tslint.json configuration.", "type": "string", "default": "tslint.json" + }, + "exclude": { + "description": "File glob(s) to ignore.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ], + "default": [] } }, "required": [ - "files", "project" ], "additionalProperties": false diff --git a/packages/@angular/cli/tasks/lint.ts b/packages/@angular/cli/tasks/lint.ts index 6e4e3618acfa..df4fa00f53cc 100644 --- a/packages/@angular/cli/tasks/lint.ts +++ b/packages/@angular/cli/tasks/lint.ts @@ -1,36 +1,46 @@ const Task = require('../ember-cli/lib/models/task'); import * as chalk from 'chalk'; import * as path from 'path'; +import * as glob from 'glob'; +import * as ts from 'typescript'; import { requireDependency } from '../utilities/require-project-module'; import { CliConfig } from '../models/config'; import { LintCommandOptions } from '../commands/lint'; import { oneLine } from 'common-tags'; +interface CliLintConfig { + files?: (string | string[]); + project?: string; + tslintConfig?: string; + exclude?: (string | string[]); +} + export default Task.extend({ run: function (commandOptions: LintCommandOptions) { const ui = this.ui; const projectRoot = this.project.root; + const lintConfigs: CliLintConfig[] = CliConfig.fromProject().config.lint || []; - return new Promise(function (resolve, reject) { - const tslint = requireDependency(projectRoot, 'tslint'); - const Linter = tslint.Linter; - const Configuration = tslint.Configuration; + if (lintConfigs.length === 0) { + ui.writeLine(chalk.yellow(oneLine` + No lint config(s) found. + If this is not intended, run "ng update". + `)); - const lintConfigs = CliConfig.fromProject().config.lint || []; + return Promise.resolve(0); + } - if (lintConfigs.length === 0) { - ui.writeLine(chalk.yellow(oneLine` - No lint config(s) found. - If this is not intended, run "ng update". - `)); - return resolve(0); - } + const tslint = requireDependency(projectRoot, 'tslint'); + const Linter = tslint.Linter; + const Configuration = tslint.Configuration; - let errors = 0; + let errors = 0; + let results = ''; - lintConfigs.forEach((config) => { - const program = Linter.createProgram(config.project); - const files: string[] = Linter.getFileNames(program); + lintConfigs + .forEach((config) => { + const program: ts.Program = Linter.createProgram(config.project); + const files = getFilesToLint(program, config, Linter); const linter = new Linter({ fix: commandOptions.fix, @@ -45,17 +55,42 @@ export default Task.extend({ const result = linter.getResult(); errors += result.failureCount; - - ui.writeLine(result.output.trim().concat('\n')); + results = results.concat(result.output.trim().concat('\n')); }); - if (errors > 0) { - ui.writeLine(chalk.red('Lint errors found in the listed files.')); - return commandOptions.force ? resolve(0) : resolve(2); - } + if (errors > 0) { + ui.writeLine(results.trim()); + ui.writeLine(chalk.red('Lint errors found in the listed files.')); + return commandOptions.force ? Promise.resolve(0) : Promise.resolve(2); + } - ui.writeLine(chalk.green('All files pass linting.')); - return resolve(0); - }); + ui.writeLine(chalk.green('All files pass linting.')); + return Promise.resolve(0); } }); + +function getFilesToLint(program: ts.Program, lintConfig: CliLintConfig, Linter: any): string[] { + let files: string[] = []; + + if (lintConfig.files !== null) { + files = Array.isArray(lintConfig.files) ? lintConfig.files : [lintConfig.files]; + } else { + files = Linter.getFileNames(program); + } + + let globOptions = {}; + + if (lintConfig.exclude !== null) { + const excludePatterns = Array.isArray(lintConfig.exclude) + ? lintConfig.exclude + : [lintConfig.exclude]; + + globOptions = { ignore: excludePatterns, nodir: true }; + } + + files = files + .map((file: string) => glob.sync(file, globOptions)) + .reduce((a: string[], b: string[]) => a.concat(b), []); + + return files; +} diff --git a/tests/e2e/tests/lint/lint-with-exclude.ts b/tests/e2e/tests/lint/lint-with-exclude.ts new file mode 100644 index 000000000000..9420d9bc84b9 --- /dev/null +++ b/tests/e2e/tests/lint/lint-with-exclude.ts @@ -0,0 +1,20 @@ +import { ng } from '../../utils/process'; +import { writeFile } from '../../utils/fs'; +import { oneLine } from 'common-tags'; + +export default function () { + const fileName = 'src/app/foo.ts'; + + return Promise.resolve() + .then(() => ng('set', 'lint.0.exclude', '"**/foo.ts"')) + .then(() => writeFile(fileName, 'const foo = "";\n')) + .then(() => ng('lint')) + .then((output) => { + if (!output.match(/All files pass linting\./)) { + throw new Error(oneLine` + Expected to match "All files pass linting." + in ${output}. + `); + } + }); +}