Skip to content

Commit a3bf529

Browse files
Charles Lydingfilipesilva
Charles Lyding
authored andcommitted
feat(@angular/cli): add option to use lint to adjust generated files
1 parent 5cf395c commit a3bf529

File tree

11 files changed

+137
-29
lines changed

11 files changed

+137
-29
lines changed

packages/@angular/cli/blueprints/component/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ export default Blueprint.extend({
273273
this._writeStatusToUI(chalk.yellow,
274274
moduleStatus,
275275
path.relative(this.project.root, this.pathToModule));
276+
this.addModifiedFile(this.pathToModule);
276277
}));
277278
}
278279

packages/@angular/cli/blueprints/directive/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ export default Blueprint.extend({
167167
this._writeStatusToUI(chalk.yellow,
168168
'update',
169169
path.relative(this.project.root, this.pathToModule));
170+
this.addModifiedFile(this.pathToModule);
170171
}
171172

172173
return Promise.all(returns);

packages/@angular/cli/blueprints/guard/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ export default Blueprint.extend({
112112
this._writeStatusToUI(chalk.yellow,
113113
'update',
114114
path.relative(this.project.root, this.pathToModule));
115+
this.addModifiedFile(this.pathToModule);
115116
}
116117

117118
return Promise.all(returns);

packages/@angular/cli/blueprints/pipe/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ export default Blueprint.extend({
145145
this._writeStatusToUI(chalk.yellow,
146146
'update',
147147
path.relative(this.project.root, this.pathToModule));
148+
this.addModifiedFile(this.pathToModule);
148149
}
149150

150151
return Promise.all(returns);

packages/@angular/cli/blueprints/service/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ export default Blueprint.extend({
124124
this._writeStatusToUI(chalk.yellow,
125125
'update',
126126
path.relative(this.project.root, this.pathToModule));
127+
this.addModifiedFile(this.pathToModule);
127128
}
128129

129130
return Promise.all(returns);

packages/@angular/cli/commands/generate.ts

+31-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as fs from 'fs';
33
import * as os from 'os';
44
import * as path from 'path';
55
import { oneLine } from 'common-tags';
6+
import { CliConfig } from '../models/config';
67

78
const Command = require('../ember-cli/lib/models/command');
89
const Blueprint = require('../ember-cli/lib/models/blueprint');
@@ -32,6 +33,12 @@ export default Command.extend({
3233
aliases: ['d'],
3334
description: 'Run through without making any changes.'
3435
},
36+
{
37+
name: 'lint-fix',
38+
type: Boolean,
39+
aliases: ['lf'],
40+
description: 'Use lint to fix files after generation.'
41+
},
3542
{
3643
name: 'verbose',
3744
type: Boolean,
@@ -59,7 +66,7 @@ export default Command.extend({
5966

6067
const name = rawArgs[0];
6168
const blueprint = this.blueprints.find((bp: any) => bp.name === name
62-
|| (bp.aliases && bp.aliases.includes(name)));
69+
|| (bp.aliases && bp.aliases.includes(name)));
6370

6471
if (!blueprint) {
6572
SilentError.debugOrThrow('@angular/cli/commands/generate',
@@ -94,7 +101,7 @@ export default Command.extend({
94101
}
95102

96103
const blueprint = this.blueprints.find((bp: any) => bp.name === name
97-
|| (bp.aliases && bp.aliases.includes(name)));
104+
|| (bp.aliases && bp.aliases.includes(name)));
98105

99106
const blueprintOptions = {
100107
target: this.project.root,
@@ -110,6 +117,27 @@ export default Command.extend({
110117
...commandOptions
111118
};
112119

113-
return blueprint.install(blueprintOptions);
120+
return blueprint.install(blueprintOptions)
121+
.then(() => {
122+
const lintFix = commandOptions.lintFix !== undefined ?
123+
commandOptions.lintFix : CliConfig.getValue('defaults.lintFix');
124+
125+
if (lintFix && blueprint.modifiedFiles) {
126+
const LintTask = require('../tasks/lint').default;
127+
const lintTask = new LintTask({
128+
ui: this.ui,
129+
project: this.project
130+
});
131+
132+
return lintTask.run({
133+
fix: true,
134+
force: true,
135+
silent: true,
136+
configs: [{
137+
files: blueprint.modifiedFiles.filter((file: string) => /.ts$/.test(file))
138+
}]
139+
});
140+
}
141+
});
114142
}
115143
});

packages/@angular/cli/commands/lint.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import {oneLine} from 'common-tags';
1+
import { oneLine } from 'common-tags';
2+
import { CliConfig } from '../models/config';
23

34
const Command = require('../ember-cli/lib/models/command');
45

@@ -52,6 +53,9 @@ export default Command.extend({
5253
project: this.project
5354
});
5455

55-
return lintTask.run(commandOptions);
56+
return lintTask.run({
57+
...commandOptions,
58+
configs: CliConfig.fromProject().config.lint
59+
});
5660
}
5761
});

packages/@angular/cli/ember-cli/lib/models/blueprint.js

+8
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,13 @@ Blueprint.prototype._writeStatusToUI = function(chalkColor, keyword, message) {
364364
}
365365
};
366366

367+
Blueprint.prototype.addModifiedFile = function(file) {
368+
if (!this.modifiedFiles) {
369+
this.modifiedFiles = [];
370+
}
371+
this.modifiedFiles.push(file);
372+
}
373+
367374
/**
368375
@private
369376
@method _writeFile
@@ -372,6 +379,7 @@ Blueprint.prototype._writeStatusToUI = function(chalkColor, keyword, message) {
372379
*/
373380
Blueprint.prototype._writeFile = function(info) {
374381
if (!this.dryRun) {
382+
this.addModifiedFile(info.outputPath);
375383
return writeFile(info.outputPath, info.render());
376384
}
377385
};

packages/@angular/cli/lib/config/schema.json

+5
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,11 @@
299299
"description": "How often to check for file updates.",
300300
"type": "number"
301301
},
302+
"lintFix": {
303+
"description": "Use lint to fix files after generation",
304+
"type": "boolean",
305+
"default": false
306+
},
302307
"class": {
303308
"description": "Options for generating a class.",
304309
"type": "object",

packages/@angular/cli/tasks/lint.ts

+45-24
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,37 @@ import * as glob from 'glob';
44
import * as path from 'path';
55
import * as ts from 'typescript';
66
import { requireProjectModule } from '../utilities/require-project-module';
7-
import { CliConfig } from '../models/config';
8-
import { LintCommandOptions } from '../commands/lint';
97

108
const SilentError = require('silent-error');
119
const Task = require('../ember-cli/lib/models/task');
1210

13-
interface CliLintConfig {
11+
export interface CliLintConfig {
1412
files?: (string | string[]);
1513
project?: string;
1614
tslintConfig?: string;
1715
exclude?: (string | string[]);
1816
}
1917

18+
export class LintTaskOptions {
19+
fix: boolean;
20+
force: boolean;
21+
format? = 'prose';
22+
silent? = false;
23+
typeCheck? = false;
24+
configs: Array<CliLintConfig>;
25+
}
26+
2027
export default Task.extend({
21-
run: function (commandOptions: LintCommandOptions) {
28+
run: function (options: LintTaskOptions) {
29+
options = { ...new LintTaskOptions(), ...options };
2230
const ui = this.ui;
2331
const projectRoot = this.project.root;
24-
const lintConfigs: CliLintConfig[] = CliConfig.fromProject().config.lint || [];
32+
const lintConfigs = options.configs || [];
2533

2634
if (lintConfigs.length === 0) {
27-
ui.writeLine(chalk.yellow('No lint configuration(s) found.'));
35+
if (!options.silent) {
36+
ui.writeLine(chalk.yellow('No lint configuration(s) found.'));
37+
}
2838
return Promise.resolve(0);
2939
}
3040

@@ -37,15 +47,17 @@ export default Task.extend({
3747
let program: ts.Program;
3848
if (config.project) {
3949
program = Linter.createProgram(config.project);
40-
} else if (commandOptions.typeCheck) {
41-
ui.writeLine(chalk.yellow('A "project" must be specified to enable type checking.'));
50+
} else if (options.typeCheck) {
51+
if (!options.silent) {
52+
ui.writeLine(chalk.yellow('A "project" must be specified to enable type checking.'));
53+
}
4254
}
4355
const files = getFilesToLint(program, config, Linter);
4456
const lintOptions = {
45-
fix: commandOptions.fix,
46-
formatter: commandOptions.format
57+
fix: options.fix,
58+
formatter: options.format
4759
};
48-
const lintProgram = commandOptions.typeCheck ? program : undefined;
60+
const lintProgram = options.typeCheck ? program : undefined;
4961
const linter = new Linter(lintOptions, lintProgram);
5062

5163
let lastDirectory: string;
@@ -82,42 +94,51 @@ export default Task.extend({
8294
fixes: undefined
8395
});
8496

85-
const Formatter = tslint.findFormatter(commandOptions.format);
86-
const formatter = new Formatter();
87-
88-
const output = formatter.format(result.failures, result.fixes);
89-
if (output) {
90-
ui.writeLine(output);
97+
if (!options.silent) {
98+
const Formatter = tslint.findFormatter(options.format);
99+
if (!Formatter) {
100+
throw new SilentError(chalk.red(`Invalid lint format "${options.format}".`));
101+
}
102+
const formatter = new Formatter();
103+
104+
const output = formatter.format(result.failures, result.fixes);
105+
if (output) {
106+
ui.writeLine(output);
107+
}
91108
}
92109

93110
// print formatter output directly for non human-readable formats
94-
if (['prose', 'verbose', 'stylish'].indexOf(commandOptions.format) == -1) {
95-
return (result.failures.length == 0 || commandOptions.force)
111+
if (['prose', 'verbose', 'stylish'].indexOf(options.format) == -1) {
112+
return (result.failures.length == 0 || options.force)
96113
? Promise.resolve(0) : Promise.resolve(2);
97114
}
98115

99116
if (result.failures.length > 0) {
100-
ui.writeLine(chalk.red('Lint errors found in the listed files.'));
101-
return commandOptions.force ? Promise.resolve(0) : Promise.resolve(2);
117+
if (!options.silent) {
118+
ui.writeLine(chalk.red('Lint errors found in the listed files.'));
119+
}
120+
return options.force ? Promise.resolve(0) : Promise.resolve(2);
102121
}
103122

104-
ui.writeLine(chalk.green('All files pass linting.'));
123+
if (!options.silent) {
124+
ui.writeLine(chalk.green('All files pass linting.'));
125+
}
105126
return Promise.resolve(0);
106127
}
107128
});
108129

109130
function getFilesToLint(program: ts.Program, lintConfig: CliLintConfig, Linter: any): string[] {
110131
let files: string[] = [];
111132

112-
if (lintConfig.files !== null) {
133+
if (lintConfig.files) {
113134
files = Array.isArray(lintConfig.files) ? lintConfig.files : [lintConfig.files];
114135
} else if (program) {
115136
files = Linter.getFileNames(program);
116137
}
117138

118139
let globOptions = {};
119140

120-
if (lintConfig.exclude !== null) {
141+
if (lintConfig.exclude) {
121142
const excludePatterns = Array.isArray(lintConfig.exclude)
122143
? lintConfig.exclude
123144
: [lintConfig.exclude];

tests/e2e/tests/generate/lint-fix.ts

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { ng } from '../../utils/process';
2+
import { writeFile } from '../../utils/fs';
3+
import { expectToFail } from '../../utils/utils';
4+
5+
export default function () {
6+
const nestedConfigContent = `
7+
{
8+
"rules": {
9+
"quotemark": [
10+
true,
11+
"double",
12+
"avoid-escape"
13+
]
14+
}
15+
}`;
16+
17+
return Promise.resolve()
18+
// setup a double-quote tslint config
19+
.then(() => writeFile('src/app/tslint.json', nestedConfigContent))
20+
21+
// Generate a fixed new component but don't fix rest of app
22+
.then(() => ng('generate', 'component', 'test-component1', '--lint-fix'))
23+
.then(() => expectToFail(() => ng('lint')))
24+
25+
// Fix rest of app and generate new component
26+
.then(() => ng('lint', '--fix'))
27+
.then(() => ng('generate', 'component', 'test-component2', '--lint-fix'))
28+
.then(() => ng('lint'))
29+
30+
// Enable default option and generate all other module related blueprints
31+
.then(() => ng('set', 'defaults.lintFix', 'true'))
32+
.then(() => ng('generate', 'directive', 'test-directive'))
33+
.then(() => ng('generate', 'service', 'test-service', '--module', 'app.module.ts'))
34+
.then(() => ng('generate', 'pipe', 'test-pipe'))
35+
.then(() => ng('generate', 'guard', 'test-guard', '--module', 'app.module.ts'))
36+
.then(() => ng('lint'));
37+
}

0 commit comments

Comments
 (0)