Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(lint): use tslint api for linting #4248

Merged
merged 1 commit into from
Jan 28, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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",
"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.