Skip to content

Commit

Permalink
Lint edit actions (Polymer#924)
Browse files Browse the repository at this point in the history
* Abstract away inquirer boilerplate.

There's a couple things that `init` does special to deal with inquirer. Abstract that away into our own prompt function .

* Add support for edit actions to the linter.

* Simplify our `prompt` wrapper a bit.

* Update CHANGELOG.

* Always apply fixes when called with --fix

* Update analyzer and linter.

* Add an integration test of lint edit actions.

* Changes for clarity and improved documentation.

* Appveyor no longer running with mingw shell.

So we need to test for that in our windows test.

* Fix lint test.

* Update yarn.lock
  • Loading branch information
rictic committed Nov 16, 2017
1 parent 524a206 commit 9eefa86
Show file tree
Hide file tree
Showing 14 changed files with 557 additions and 257 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Expand Up @@ -2,6 +2,9 @@

## Unreleased
- Added `--fix` option to `polymer lint`. When passed, some warnings with simple mechanical solutions will be fixed.
- Also supports warnings which can be addressed with less-safe changes via
an interactive prompt and the `--edits` flag. See `polymer lint --help` for
more info.
- `build` Added a CLI argument for setting the `basePath` option: `--base-path`.
<!-- Add new, unreleased items here. -->

Expand Down
37 changes: 22 additions & 15 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Expand Up @@ -64,9 +64,9 @@
"merge-stream": "^1.0.1",
"mz": "^2.6.0",
"plylog": "^0.4.0",
"polymer-analyzer": "^2.3.0",
"polymer-analyzer": "^2.6.0",
"polymer-build": "^2.1.0",
"polymer-linter": "^2.1.0",
"polymer-linter": "^2.2.0",
"polymer-project-config": "^3.4.0",
"polyserve": "^0.23.0",
"request": "^2.72.0",
Expand Down
2 changes: 1 addition & 1 deletion src/commands/command.ts
Expand Up @@ -31,7 +31,7 @@ export interface Command {
/**
* Documentation to append onto the output of `polymer help commandName`.
*/
extraUsageGroups?(config: ProjectConfig): UsageGroup[];
extraUsageGroups?(config: ProjectConfig): Promise<UsageGroup[]>;
}

/**
Expand Down
6 changes: 3 additions & 3 deletions src/commands/help.ts
Expand Up @@ -84,9 +84,9 @@ export class HelpCommand implements Command {
]);
}

generateCommandUsage(command: Command, config: ProjectConfig) {
async generateCommandUsage(command: Command, config: ProjectConfig) {
const extraUsageGroups =
command.extraUsageGroups ? command.extraUsageGroups(config) : [];
command.extraUsageGroups ? await command.extraUsageGroups(config) : [];
const usageGroups: commandLineUsage.UsageGroup[] = [
{
header: `polymer ${command.name}`,
Expand Down Expand Up @@ -120,6 +120,6 @@ export class HelpCommand implements Command {
}

logger.debug(`printing help for command '${commandName}'...`);
console.log(this.generateCommandUsage(command, config));
console.log(await this.generateCommandUsage(command, config));
}
}
42 changes: 31 additions & 11 deletions src/commands/lint.ts
Expand Up @@ -17,19 +17,18 @@
// unused code. Any imports that are only used as types will be removed from the
// output JS and so not result in a require() statement.

import * as chalkTypeOnly from 'chalk';
import {ArgDescriptor} from 'command-line-args';
import {UsageGroup} from 'command-line-usage';
import * as lintLibTypeOnly from 'polymer-linter';
import {ProjectConfig} from 'polymer-project-config';
import * as lintImplementationTypeOnly from '../lint/lint';

import {Command} from './command';

export interface Options {
rules?: string[];
input?: string[];
fix?: boolean;
edits?: string[];
prompt: boolean;
}

export class LintCommand implements Command {
Expand Down Expand Up @@ -61,6 +60,27 @@ export class LintCommand implements Command {
type: Boolean,
description: `Automatically fix as many issues as possible by ` +
`updating your source on disk.`
},
{
name: 'edits',
type: String,
alias: 'e',
multiple: true,
description: `The lint edits to apply. Used with --fix. ` +
`Edits are less-safe fixes. When running in an interactive prompt ` +
`we will ask whether to apply an edit, but you can automatically ` +
`apply all edits of a type using this flag, like ` +
`--edit=content-with-select`
},
{
name: 'prompt',
type: (value: string) => {
return value.toLowerCase().trim() !== 'false';
},
defaultValue: !!process.stdin.isTTY,
description:
`Whether to allow interactive prompts. Use --prompt=false when` +
` running as part of an automated script without a human at stdin.`
}
];

Expand All @@ -77,20 +97,20 @@ export class LintCommand implements Command {
this._loadPlugins(config);

// Defer dependency loading until this specific command is run.
const lintImplementation: typeof lintImplementationTypeOnly =
require('../lint/lint');
const lintImplementation = await import('../lint/lint');

return lintImplementation.lint(options, config);
}

extraUsageGroups(config: ProjectConfig): UsageGroup[] {
const lintLib: typeof lintLibTypeOnly = require('polymer-linter');
const chalk: typeof chalkTypeOnly = require('chalk');
async extraUsageGroups(config: ProjectConfig): Promise<UsageGroup[]> {
const lintLib = await import('polymer-linter');
const chalk = await import('chalk');
this._loadPlugins(config);
const collectionsDocs = [];
for (const collection of lintLib.registry.allRuleCollections) {
collectionsDocs.push(` ${
chalk.bold(collection.code)
}: ${this._indent(collection.description)}`);
collectionsDocs.push(
` ${chalk.bold(collection.code)}: ` +
`${this._indent(collection.description)}`);
}
const rulesDocs = [];
for (const rule of lintLib.registry.allRules) {
Expand Down
39 changes: 3 additions & 36 deletions src/init/init.ts
Expand Up @@ -13,17 +13,16 @@
*/

import * as chalk from 'chalk';
import {execSync} from 'child_process';
import * as fs from 'fs';
import * as logging from 'plylog';

import findup = require('findup-sync');
import * as YeomanEnvironment from 'yeoman-environment';
import {prompt, Question as InquirerQuestion} from 'inquirer';
import {createApplicationGenerator} from '../init/application/application';
import {createElementGenerator} from '../init/element/element';
import {createGithubGenerator} from '../init/github';
import Generator = require('yeoman-generator');
import {prompt} from '../util';

const logger = logging.getLogger('init');

Expand Down Expand Up @@ -71,27 +70,6 @@ const localGenerators: {[name: string]: GeneratorInfo} = {
},
};

/**
* Check if the current shell environment is MinGW. MinGW can't handle some
* yeoman features, so we can use this check to downgrade gracefully.
*/
function checkIsMinGW(): boolean {
const isWindows = /^win/.test(process.platform);
if (!isWindows) {
return false;
}

// uname might not exist if using cmd or powershell,
// which would throw an exception
try {
const uname = execSync('uname -s').toString();
return !!/^mingw/i.test(uname);
} catch (error) {
logger.debug(
'`uname -s` failed to execute correctly', {err: error.message});
return false;
}
}

/**
* Get a description for the given generator. If this is an external generator,
Expand Down Expand Up @@ -199,7 +177,7 @@ async function createYeomanEnvironment() {
* the list of available generators by filtering relevent ones out from
* the environment list.
*/
function createSelectPrompt(env: YeomanEnvironment): InquirerQuestion {
function createSelectPrompt(env: YeomanEnvironment) {
const generators = env.getGeneratorsMeta();
const allGeneratorNames = Object.keys(generators).filter((k) => {
return k.startsWith('polymer-init') && k !== 'polymer-init:app';
Expand All @@ -209,16 +187,7 @@ function createSelectPrompt(env: YeomanEnvironment): InquirerQuestion {
return getGeneratorDescription(generator, generatorName);
});

// Some windows emulators (mingw) don't handle arrows correctly
// https://github.com/SBoudrias/Inquirer.js/issues/266
// Fall back to rawlist and use number input
// Credit to
// https://gist.github.com/geddski/c42feb364f3c671d22b6390d82b8af8f
const isMinGW = checkIsMinGW();

return {
type: isMinGW ? 'rawlist' : 'list',
name: 'generatorName',
message: 'Which starter template would you like to use?',
choices: choices,
};
Expand Down Expand Up @@ -265,9 +234,7 @@ export async function promptGeneratorSelection(options?: {[name: string]: any}):
Promise<void> {
options = options || {};
const env = await(options['env'] || createYeomanEnvironment());
// TODO(justinfagnani): the typings for inquirer appear wrong
const answers = await(prompt([createSelectPrompt(env)]) as any);
const generatorName = answers['generatorName'];
const generatorName = await prompt(createSelectPrompt(env));
await runGenerator(
generatorName, {templateName: getDisplayName(generatorName), env: env});
}

0 comments on commit 9eefa86

Please sign in to comment.