Skip to content

Commit

Permalink
feat(@angular/cli): add option to use lint to adjust generated files
Browse files Browse the repository at this point in the history
  • Loading branch information
Charles Lyding authored and filipesilva committed May 9, 2017
1 parent 5cf395c commit a3bf529
Show file tree
Hide file tree
Showing 11 changed files with 137 additions and 29 deletions.
1 change: 1 addition & 0 deletions packages/@angular/cli/blueprints/component/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ export default Blueprint.extend({
this._writeStatusToUI(chalk.yellow,
moduleStatus,
path.relative(this.project.root, this.pathToModule));
this.addModifiedFile(this.pathToModule);
}));
}

Expand Down
1 change: 1 addition & 0 deletions packages/@angular/cli/blueprints/directive/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ export default Blueprint.extend({
this._writeStatusToUI(chalk.yellow,
'update',
path.relative(this.project.root, this.pathToModule));
this.addModifiedFile(this.pathToModule);
}

return Promise.all(returns);
Expand Down
1 change: 1 addition & 0 deletions packages/@angular/cli/blueprints/guard/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ export default Blueprint.extend({
this._writeStatusToUI(chalk.yellow,
'update',
path.relative(this.project.root, this.pathToModule));
this.addModifiedFile(this.pathToModule);
}

return Promise.all(returns);
Expand Down
1 change: 1 addition & 0 deletions packages/@angular/cli/blueprints/pipe/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ export default Blueprint.extend({
this._writeStatusToUI(chalk.yellow,
'update',
path.relative(this.project.root, this.pathToModule));
this.addModifiedFile(this.pathToModule);
}

return Promise.all(returns);
Expand Down
1 change: 1 addition & 0 deletions packages/@angular/cli/blueprints/service/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ export default Blueprint.extend({
this._writeStatusToUI(chalk.yellow,
'update',
path.relative(this.project.root, this.pathToModule));
this.addModifiedFile(this.pathToModule);
}

return Promise.all(returns);
Expand Down
34 changes: 31 additions & 3 deletions packages/@angular/cli/commands/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { oneLine } from 'common-tags';
import { CliConfig } from '../models/config';

const Command = require('../ember-cli/lib/models/command');
const Blueprint = require('../ember-cli/lib/models/blueprint');
Expand Down Expand Up @@ -32,6 +33,12 @@ export default Command.extend({
aliases: ['d'],
description: 'Run through without making any changes.'
},
{
name: 'lint-fix',
type: Boolean,
aliases: ['lf'],
description: 'Use lint to fix files after generation.'
},
{
name: 'verbose',
type: Boolean,
Expand Down Expand Up @@ -59,7 +66,7 @@ export default Command.extend({

const name = rawArgs[0];
const blueprint = this.blueprints.find((bp: any) => bp.name === name
|| (bp.aliases && bp.aliases.includes(name)));
|| (bp.aliases && bp.aliases.includes(name)));

if (!blueprint) {
SilentError.debugOrThrow('@angular/cli/commands/generate',
Expand Down Expand Up @@ -94,7 +101,7 @@ export default Command.extend({
}

const blueprint = this.blueprints.find((bp: any) => bp.name === name
|| (bp.aliases && bp.aliases.includes(name)));
|| (bp.aliases && bp.aliases.includes(name)));

const blueprintOptions = {
target: this.project.root,
Expand All @@ -110,6 +117,27 @@ export default Command.extend({
...commandOptions
};

return blueprint.install(blueprintOptions);
return blueprint.install(blueprintOptions)
.then(() => {
const lintFix = commandOptions.lintFix !== undefined ?
commandOptions.lintFix : CliConfig.getValue('defaults.lintFix');

if (lintFix && blueprint.modifiedFiles) {
const LintTask = require('../tasks/lint').default;
const lintTask = new LintTask({
ui: this.ui,
project: this.project
});

return lintTask.run({
fix: true,
force: true,
silent: true,
configs: [{
files: blueprint.modifiedFiles.filter((file: string) => /.ts$/.test(file))
}]
});
}
});
}
});
8 changes: 6 additions & 2 deletions packages/@angular/cli/commands/lint.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {oneLine} from 'common-tags';
import { oneLine } from 'common-tags';
import { CliConfig } from '../models/config';

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

Expand Down Expand Up @@ -52,6 +53,9 @@ export default Command.extend({
project: this.project
});

return lintTask.run(commandOptions);
return lintTask.run({
...commandOptions,
configs: CliConfig.fromProject().config.lint
});
}
});
8 changes: 8 additions & 0 deletions packages/@angular/cli/ember-cli/lib/models/blueprint.js
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,13 @@ Blueprint.prototype._writeStatusToUI = function(chalkColor, keyword, message) {
}
};

Blueprint.prototype.addModifiedFile = function(file) {
if (!this.modifiedFiles) {
this.modifiedFiles = [];
}
this.modifiedFiles.push(file);
}

/**
@private
@method _writeFile
Expand All @@ -372,6 +379,7 @@ Blueprint.prototype._writeStatusToUI = function(chalkColor, keyword, message) {
*/
Blueprint.prototype._writeFile = function(info) {
if (!this.dryRun) {
this.addModifiedFile(info.outputPath);
return writeFile(info.outputPath, info.render());
}
};
Expand Down
5 changes: 5 additions & 0 deletions packages/@angular/cli/lib/config/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,11 @@
"description": "How often to check for file updates.",
"type": "number"
},
"lintFix": {
"description": "Use lint to fix files after generation",
"type": "boolean",
"default": false
},
"class": {
"description": "Options for generating a class.",
"type": "object",
Expand Down
69 changes: 45 additions & 24 deletions packages/@angular/cli/tasks/lint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,37 @@ import * as glob from 'glob';
import * as path from 'path';
import * as ts from 'typescript';
import { requireProjectModule } from '../utilities/require-project-module';
import { CliConfig } from '../models/config';
import { LintCommandOptions } from '../commands/lint';

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

interface CliLintConfig {
export interface CliLintConfig {
files?: (string | string[]);
project?: string;
tslintConfig?: string;
exclude?: (string | string[]);
}

export class LintTaskOptions {
fix: boolean;
force: boolean;
format? = 'prose';
silent? = false;
typeCheck? = false;
configs: Array<CliLintConfig>;
}

export default Task.extend({
run: function (commandOptions: LintCommandOptions) {
run: function (options: LintTaskOptions) {
options = { ...new LintTaskOptions(), ...options };
const ui = this.ui;
const projectRoot = this.project.root;
const lintConfigs: CliLintConfig[] = CliConfig.fromProject().config.lint || [];
const lintConfigs = options.configs || [];

if (lintConfigs.length === 0) {
ui.writeLine(chalk.yellow('No lint configuration(s) found.'));
if (!options.silent) {
ui.writeLine(chalk.yellow('No lint configuration(s) found.'));
}
return Promise.resolve(0);
}

Expand All @@ -37,15 +47,17 @@ export default Task.extend({
let program: ts.Program;
if (config.project) {
program = Linter.createProgram(config.project);
} else if (commandOptions.typeCheck) {
ui.writeLine(chalk.yellow('A "project" must be specified to enable type checking.'));
} else if (options.typeCheck) {
if (!options.silent) {
ui.writeLine(chalk.yellow('A "project" must be specified to enable type checking.'));
}
}
const files = getFilesToLint(program, config, Linter);
const lintOptions = {
fix: commandOptions.fix,
formatter: commandOptions.format
fix: options.fix,
formatter: options.format
};
const lintProgram = commandOptions.typeCheck ? program : undefined;
const lintProgram = options.typeCheck ? program : undefined;
const linter = new Linter(lintOptions, lintProgram);

let lastDirectory: string;
Expand Down Expand Up @@ -82,42 +94,51 @@ export default Task.extend({
fixes: undefined
});

const Formatter = tslint.findFormatter(commandOptions.format);
const formatter = new Formatter();

const output = formatter.format(result.failures, result.fixes);
if (output) {
ui.writeLine(output);
if (!options.silent) {
const Formatter = tslint.findFormatter(options.format);
if (!Formatter) {
throw new SilentError(chalk.red(`Invalid lint format "${options.format}".`));
}
const formatter = new Formatter();

const output = formatter.format(result.failures, result.fixes);
if (output) {
ui.writeLine(output);
}
}

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

if (result.failures.length > 0) {
ui.writeLine(chalk.red('Lint errors found in the listed files.'));
return commandOptions.force ? Promise.resolve(0) : Promise.resolve(2);
if (!options.silent) {
ui.writeLine(chalk.red('Lint errors found in the listed files.'));
}
return options.force ? Promise.resolve(0) : Promise.resolve(2);
}

ui.writeLine(chalk.green('All files pass linting.'));
if (!options.silent) {
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) {
if (lintConfig.files) {
files = Array.isArray(lintConfig.files) ? lintConfig.files : [lintConfig.files];
} else if (program) {
files = Linter.getFileNames(program);
}

let globOptions = {};

if (lintConfig.exclude !== null) {
if (lintConfig.exclude) {
const excludePatterns = Array.isArray(lintConfig.exclude)
? lintConfig.exclude
: [lintConfig.exclude];
Expand Down
37 changes: 37 additions & 0 deletions tests/e2e/tests/generate/lint-fix.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { ng } from '../../utils/process';
import { writeFile } from '../../utils/fs';
import { expectToFail } from '../../utils/utils';

export default function () {
const nestedConfigContent = `
{
"rules": {
"quotemark": [
true,
"double",
"avoid-escape"
]
}
}`;

return Promise.resolve()
// setup a double-quote tslint config
.then(() => writeFile('src/app/tslint.json', nestedConfigContent))

// Generate a fixed new component but don't fix rest of app
.then(() => ng('generate', 'component', 'test-component1', '--lint-fix'))
.then(() => expectToFail(() => ng('lint')))

// Fix rest of app and generate new component
.then(() => ng('lint', '--fix'))
.then(() => ng('generate', 'component', 'test-component2', '--lint-fix'))
.then(() => ng('lint'))

// Enable default option and generate all other module related blueprints
.then(() => ng('set', 'defaults.lintFix', 'true'))
.then(() => ng('generate', 'directive', 'test-directive'))
.then(() => ng('generate', 'service', 'test-service', '--module', 'app.module.ts'))
.then(() => ng('generate', 'pipe', 'test-pipe'))
.then(() => ng('generate', 'guard', 'test-guard', '--module', 'app.module.ts'))
.then(() => ng('lint'));
}

0 comments on commit a3bf529

Please sign in to comment.