Skip to content

Commit

Permalink
refactor(lint): use tslint api for linting (#4248)
Browse files Browse the repository at this point in the history
Closes #867, #3993

BREAKING CHANGE: 

In order to use the updated `ng lint` command, the following section will have to be added to the project's `angular-cli.json` at the root level of the json object.

  ```json
  "lint": [
    {
      "files": "src/**/*.ts",
      "project": "src/tsconfig.json"
    },
    {
      "files": "e2e/**/*.ts",
      "project": "e2e/tsconfig.json"
    }
  ],
  ```

Alternatively, you can run `ng update`.
  • Loading branch information
delasteve authored and filipesilva committed Jan 28, 2017
1 parent 1bbd12d commit 0664beb
Show file tree
Hide file tree
Showing 14 changed files with 224 additions and 32 deletions.
12 changes: 9 additions & 3 deletions docs/documentation/lint.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
<!-- Links in /docs/documentation should NOT have `.md` at the end, because they end up in our wiki at release. -->

# ng lint

## Overview
`ng lint` will lint your app code.
`ng lint` will lint you app code using tslint.

## Options

`--fix` will attempt to fix lint errors

This will use the `lint` npm script that in generated projects uses `tslint`.
`--force` will always return error code 0 even with lint errors

You can modify the these scripts in `package.json` to run whatever tool you prefer.
`--format` (`-t`) the output formatter to use
10 changes: 10 additions & 0 deletions packages/angular-cli/blueprints/ng2/files/angular-cli.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@
"config": "./protractor.conf.js"
}
},
"lint": [
{
"files": "<%= sourceDir %>/**/*.ts",
"project": "<%= sourceDir %>/tsconfig.json"
},
{
"files": "e2e/**/*.ts",
"project": "e2e/tsconfig.json"
}
],
"test": {
"karma": {
"config": "./karma.conf.js"
Expand Down
1 change: 0 additions & 1 deletion packages/angular-cli/blueprints/ng2/files/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
"scripts": {
"ng": "ng",
"start": "ng serve",
"lint": "tslint \"<%= sourceDir %>/**/*.ts\" --project src/tsconfig.json --type-check && tslint \"e2e/**/*.ts\" --project e2e/tsconfig.json --type-check",

This comment has been minimized.

Copy link
@kaznovac

kaznovac Jan 30, 2017

please leave "lint": "ng lint" to be consistent with the rest of the script (see start, test)
it also helps if the ng-cli is only locally installed

This comment has been minimized.

Copy link
@delasteve

delasteve Feb 5, 2017

Author Contributor

Added in #4437

"test": "ng test",
"pree2e": "webdriver-manager update --standalone false --gecko false",
"e2e": "protractor"
Expand Down
17 changes: 15 additions & 2 deletions packages/angular-cli/commands/lint.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,29 @@
const Command = require('../ember-cli/lib/models/command');

export interface LintCommandOptions {
fix?: boolean;
format?: string;
force?: boolean;
}

export default Command.extend({
name: 'lint',
aliases: ['l'],
description: 'Lints code in existing project',
works: 'insideProject',
run: function () {
availableOptions: [
{ name: 'fix', type: Boolean, default: false },
{ name: 'force', type: Boolean, default: false },
{ name: 'format', alias: 't', type: String, default: 'prose' }
],
run: function (commandOptions: LintCommandOptions) {
const LintTask = require('../tasks/lint').default;

const lintTask = new LintTask({
ui: this.ui,
project: this.project
});

return lintTask.run();
return lintTask.run(commandOptions);
}
});
28 changes: 27 additions & 1 deletion packages/angular-cli/lib/config/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,9 @@
}
},
"additionalProperties": true,
"required": ["input"]
"required": [
"input"
]
}
]
},
Expand Down Expand Up @@ -173,6 +175,30 @@
},
"additionalProperties": false
},
"lint": {
"description": "Properties to be passed to TSLint.",
"type": "array",
"items": {
"type": "object",
"properties": {
"files": {
"type": "string"
},
"project": {
"type": "string"
},
"tslintConfig": {
"type": "string",
"default": "tslint.json"
}
},
"required": [
"files",
"project"
],
"additionalProperties": false
}
},
"test": {
"type": "object",
"properties": {
Expand Down
63 changes: 51 additions & 12 deletions packages/angular-cli/tasks/lint.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,61 @@
const Task = require('../ember-cli/lib/models/task');
import * as chalk from 'chalk';
import {exec} from 'child_process';
import * as path from 'path';
import { requireDependency } from '../utilities/require-project-module';
import { CliConfig } from '../models/config';
import { LintCommandOptions } from '../commands/lint';
import { oneLine } from 'common-tags';

export default Task.extend({
run: function () {
run: function (commandOptions: LintCommandOptions) {
const ui = this.ui;
const projectRoot = this.project.root;

return new Promise(function(resolve, reject) {
exec('npm run lint', (err, stdout) => {
ui.writeLine(stdout);
if (err) {
ui.writeLine(chalk.red('Lint errors found in the listed files.'));
reject();
} else {
ui.writeLine(chalk.green('All files pass linting.'));
resolve();
}
return new Promise(function (resolve, reject) {
const tslint = requireDependency(projectRoot, 'tslint');
const Linter = tslint.Linter;
const Configuration = tslint.Configuration;

const lintConfigs = CliConfig.fromProject().config.lint || [];

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);
}

let errors = 0;

lintConfigs.forEach((config) => {
const program = Linter.createProgram(config.project);
const files: string[] = Linter.getFileNames(program);

const linter = new Linter({
fix: commandOptions.fix,
formatter: commandOptions.format
}, program);

files.forEach((file) => {
const fileContents = program.getSourceFile(file).getFullText();
const configLoad = Configuration.findConfiguration(config.tslintConfig, file);
linter.lint(file, fileContents, configLoad.results);
});

const result = linter.getResult();
errors += result.failureCount;

ui.writeLine(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);
}

ui.writeLine(chalk.green('All files pass linting.'));
return resolve(0);
});
}
});
8 changes: 1 addition & 7 deletions packages/angular-cli/tasks/test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
const Task = require('../ember-cli/lib/models/task');
import { TestOptions } from '../commands/test';
import * as path from 'path';

// require dependencies within the target project
function requireDependency(root: string, moduleName: string) {
const packageJson = require(path.join(root, 'node_modules', moduleName, 'package.json'));
const main = path.normalize(packageJson.main);
return require(path.join(root, 'node_modules', moduleName, main));
}
import { requireDependency } from '../utilities/require-project-module';

export default Task.extend({
run: function (options: TestOptions) {
Expand Down
8 changes: 8 additions & 0 deletions packages/angular-cli/utilities/require-project-module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import * as path from 'path';

// require dependencies within the target project
export function requireDependency(root: string, moduleName: string) {
const packageJson = require(path.join(root, 'node_modules', moduleName, 'package.json'));
const main = path.normalize(packageJson.main);
return require(path.join(root, 'node_modules', moduleName, main));
}
28 changes: 28 additions & 0 deletions tests/e2e/tests/lint/lint-no-config-section.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { ng } from '../../utils/process';
import { oneLine } from 'common-tags';

export default function () {
return Promise.resolve()
.then(() => ng('set', 'lint', '[]'))
.then(() => ng('lint'))
.then((output) => {
if (!output.match(/No lint config\(s\) found\./)) {
throw new Error(oneLine`
Expected to match "No lint configs found."
in ${output}.
`);
}

return output;
})
.then((output) => {
if (!output.match(/If this is not intended, run "ng update"\./)) {
throw new Error(oneLine`
Expected to match "If this is not intended, run "ng update"."
in ${output}.
`);
}

return output;
});
}
16 changes: 16 additions & 0 deletions tests/e2e/tests/lint/lint-with-fix.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ng } from '../../utils/process';
import { readFile, writeFile } from '../../utils/fs';

export default function () {
const fileName = 'src/app/foo.ts';

return Promise.resolve()
.then(() => writeFile(fileName, 'const foo = "";\n'))
.then(() => ng('lint', '--fix', '--force'))
.then(() => readFile(fileName))
.then(content => {
if (!content.match(/const foo = '';/)) {
throw new Error(`Expected to match "const foo = '';" in ${content}.`);
}
});
}
26 changes: 26 additions & 0 deletions tests/e2e/tests/lint/lint-with-force.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
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(() => writeFile(fileName, 'const foo = "";\n'))
.then(() => ng('lint', '--force'))
.then((output) => {
if (!output.match(/" should be '/)) {
throw new Error(`Expected to match "" should be '" in ${output}.`);
}

return output;
})
.then((output) => {
if (!output.match(/Lint errors found in the listed files\./)) {
throw new Error(oneLine`
Expected to match "Lint errors found in the listed files."
in ${output}.
`);
}
});
}
19 changes: 19 additions & 0 deletions tests/e2e/tests/lint/lint-with-format.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
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(() => writeFile(fileName, 'const foo = "";\n'))
.then(() => ng('lint', '--format=stylish', '--force'))
.then((output) => {
if (!output.match(/1:13 quotemark " should be '/)) {
throw new Error(oneLine`
Expected to match "1:13 quotemark " should be '"
in ${output}.
`);
}
});
}
14 changes: 14 additions & 0 deletions tests/e2e/tests/lint/lint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ng } from '../../utils/process';
import { oneLine } from 'common-tags';

export default function () {
return ng('lint')
.then((output) => {
if (!output.match(/All files pass linting\./)) {
throw new Error(oneLine`
Expected to match "All files pass linting."
in ${output}.
`);
}
});
}
6 changes: 0 additions & 6 deletions tests/e2e/tests/test/lint.ts

This file was deleted.

0 comments on commit 0664beb

Please sign in to comment.