From 4c40ae7417f862fec0781469fa6af31d0f2e3755 Mon Sep 17 00:00:00 2001 From: reduckted Date: Sat, 15 Dec 2018 20:36:39 +1000 Subject: [PATCH 1/4] Used inquirer to prompt for properties when creating a new rule. --- CONTRIBUTING.md | 8 +- build-tasks/create-rule.js | 176 +++++++++++++++++-------- build-tasks/templates/rule.template.js | 33 ++--- package-lock.json | 156 ++++++++++++++++++++++ package.json | 1 + 5 files changed, 295 insertions(+), 79 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 79116b66d..f1740aec1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,15 +12,13 @@ npm install npm test ``` -You can create new rule from template with `create-rule` script: +You can create a new rule from a template with the `create-rule` script: ```shell -npm run create-rule -- --rule-name=no-something-or-other +npm run create-rule ``` -> NOTE: `--` is required before script arguments. - -This script will create file for rule implementation (inside `src`) as well as folder with rule tests (inside `test`). +This will prompt you to enter the details of the new rule. Once you're done, it will create a file for the rule implementation (inside `src`) as well as folder with rule tests (inside `test`). More information about writing rule tests can be found in [TSLint documentation](https://palantir.github.io/tslint/develop/testing-rules/). diff --git a/build-tasks/create-rule.js b/build-tasks/create-rule.js index cb99c1ec2..f21e31105 100644 --- a/build-tasks/create-rule.js +++ b/build-tasks/create-rule.js @@ -1,62 +1,124 @@ const fs = require('fs'); -const { red } = require('chalk'); -const { readJSON, writeFile } = require('./common/files'); - -const ruleName = getRuleName(); -validateAguments(); - -const ruleFile = camelCase(ruleName) + 'Rule'; -const sourceFileName = 'src/' + ruleFile + '.ts'; -const testsFolder = 'tests/' + ruleName; -const testFile = testsFolder + '/test.ts.lint'; -const lintFile = testsFolder + '/tslint.json'; - -createImplementationFile(); -createTestFiles(); -addToConfig(); - -console.log('Rule created'); -console.log('Rule source: ' + sourceFileName); -console.log('Test file: ' + testFile); - -function getRuleName() { - const option = process.argv.find(str => str.startsWith('--rule-name')); - - if (!option) { - return; - } - - return option.split('=')[1]; -} - -function camelCase(input) { - return input.toLowerCase().replace(/-(.)/g, (match, group1) => group1.toUpperCase()); -} - -function validateAguments() { - const USAGE_EXAMPLE = '\nUsage example:\nnpm run create-rule -- --rule-name=no-something-or-other\n'; - - if (!ruleName) { - console.log(red('--rule-name parameter is required.' + USAGE_EXAMPLE)); - process.exit(1); +const inquirer = require('inquirer'); +const { execSync } = require('child_process'); +const { writeFile } = require('./common/files'); + +const questions = [ + { + name: 'name', + message: 'Name:', + type: 'input', + validate: value => { + if (!/^[a-z0-9]+(\-[a-z0-9]+)*$/.test(value)) { + return 'The name should consist of lowercase letters and numbers separated with "-" character.'; + } + + return true; + } + }, + { + name: 'description', + message: 'Description:', + type: 'input', + validate: value => { + if (!!value && !!value.trim()) { + return true; + } + return 'Please enter a description for the rule.'; + } + }, + { + name: 'type', + message: 'Rule type:', + type: 'list', + choices: ['functionality', 'maintainability', 'style', 'typescript'], + default: 'maintainability' + }, + { + name: 'issueClass', + message: 'Issue class:', + type: 'list', + choices: ['SDL', 'Non-SDL', 'Ignored'], + default: 'Non-SDL' + }, + { + name: 'issueType', + message: 'Issue type:', + type: 'list', + choices: ['Error', 'Warning'], + default: 'Warning' + }, + { + name: 'severity', + message: 'Severity:', + type: 'list', + choices: ['Critical', 'Important', 'Moderate', 'Low'], + default: 'Low' + }, + { + name: 'level', + message: 'Level:', + type: 'list', + choices: ['Mandatory', 'Opportunity for Excellence'], + default: 'Opportunity for Excellence' + }, + { + name: 'group', + message: 'Group:', + type: 'list', + choices: ['Clarity', 'Configurable', 'Correctness', 'Deprecated', 'Ignored', 'Security', 'Whitespace'], + default: 'Clarity' } - - if (!/^[a-z0-9]+(\-[a-z0-9]+)*$/.test(ruleName)) { - console.log(red('Rule name should consist of lowercase letters and numbers separated with "-" character.' + USAGE_EXAMPLE)); - process.exit(1); +]; + +inquirer.prompt(questions).then(answers => { + const sourceFileName = createImplementationFile(answers); + const testFileName = createTestFiles(answers.name); + + console.log(`Rule '${answers.name}' created.`); + console.log(`Source file: ${sourceFileName}`); + console.log(`Test file: ${testFileName}`); + + // If we're running in the VS Code terminal, try to open the + // new files. If we can't do it, then it's not a big deal. + if (process.env.VSCODE_CWD) { + try { + execSync(`code "${testFileName}"`); + execSync(`code "${sourceFileName}"`); + } catch (ex) { + // Couldn't open VS Code. + console.log(ex); + } } -} +}); -function createImplementationFile() { - const walkerName = ruleFile.charAt(0).toUpperCase() + ruleFile.substr(1) + 'Walker'; +function createImplementationFile(answers) { + const ruleFile = camelCase(answers.name) + 'Rule'; + const sourceFileName = 'src/' + ruleFile + '.ts'; + const walkerName = pascalCase(ruleFile) + 'Walker'; const ruleTemplate = require('./templates/rule.template'); - const ruleSource = ruleTemplate({ ruleName, walkerName }); + const ruleSource = ruleTemplate({ + ruleName: answers.name, + walkerName, + type: answers.type, + description: answers.description, + issueClass: answers.issueClass, + issueType: answers.issueType, + severity: answers.severity, + level: answers.level, + group: answers.group + }); writeFile(sourceFileName, ruleSource); + + return sourceFileName; } -function createTestFiles() { +function createTestFiles(ruleName) { + const testsFolder = 'tests/' + ruleName; + const testFile = testsFolder + '/test.ts.lint'; + const lintFile = testsFolder + '/tslint.json'; const testContent = '// Code that should be checked by rule'; const tslintContent = { rules: { @@ -64,16 +126,20 @@ function createTestFiles() { } }; - fs.mkdirSync(testsFolder); + if (!fs.existsSync(testsFolder)) { + fs.mkdirSync(testsFolder); + } writeFile(testFile, testContent); writeFile(lintFile, JSON.stringify(tslintContent, undefined, 4)); -} -function addToConfig() { - const currentRuleset = readJSON('tslint.json'); + return testFile; +} - currentRuleset.rules[ruleName] = true; +function camelCase(input) { + return input.toLowerCase().replace(/-(.)/g, (match, group1) => group1.toUpperCase()); +} - writeFile('tslint.json', JSON.stringify(currentRuleset, undefined, 4)); +function pascalCase(input) { + return input.charAt(0).toUpperCase() + input.substr(1); } diff --git a/build-tasks/templates/rule.template.js b/build-tasks/templates/rule.template.js index 500d7ab58..b90115ee7 100644 --- a/build-tasks/templates/rule.template.js +++ b/build-tasks/templates/rule.template.js @@ -1,42 +1,37 @@ -module.exports = ({ ruleName, walkerName }) => +module.exports = ({ ruleName, walkerName, type, description, issueClass, issueType, severity, level, group }) => `import * as ts from 'typescript'; import * as Lint from 'tslint'; -import {ExtendedMetadata} from './utils/ExtendedMetadata'; -// use (and contribute to) AstUtils for common AST functions // TODO: delete comment -import {AstUtils} from './utils/AstUtils'; -// use Utils instead of Underscore functions // TODO: delete comment -import {Utils} from './utils/Utils'; +import { ExtendedMetadata } from './utils/ExtendedMetadata'; +import { AstUtils } from './utils/AstUtils'; +import { Utils } from './utils/Utils'; const FAILURE_STRING: string = 'Some error message: '; // TODO: Define an error message export class Rule extends Lint.Rules.AbstractRule { - public static metadata: ExtendedMetadata = { ruleName: '${ruleName}', - type: 'maintainability', // one of: 'functionality' | 'maintainability' | 'style' | 'typescript' - description: '... add a meaningful one line description', + type: '${type}', + description: '${description}', options: null, // tslint:disable-line:no-null-keyword optionsDescription: '', - optionExamples: [], // Remove this property if the rule has no options + optionExamples: [], // TODO: Remove this property if the rule has no options typescriptOnly: false, - issueClass: 'Non-SDL', // one of: 'SDL' | 'Non-SDL' | 'Ignored' - issueType: 'Warning', // one of: 'Error' | 'Warning' - severity: 'Low', // one of: 'Critical' | 'Important' | 'Moderate' | 'Low' - level: 'Opportunity for Excellence', // one of 'Mandatory' | 'Opportunity for Excellence' - group: 'Clarity', // one of 'Ignored' | 'Security' | 'Correctness' | 'Clarity' | 'Whitespace' | 'Configurable' | 'Deprecated' - commonWeaknessEnumeration: '...' // if possible, please map your rule to a CWE (see cwe_descriptions.json and https://cwe.mitre.org) + issueClass: '${issueClass}', + issueType: '${issueType}', + severity: '${severity}', + level: '${level}', + group: '${group}', + commonWeaknessEnumeration: '...' // if possible, please map your rule to a CWE (see cwe_descriptions.json and https://cwe.mitre.org) }; public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - return this.applyWithWalker(new %WALKER_NAME%(sourceFile, this.getOptions())); + return this.applyWithWalker(new ${walkerName}(sourceFile, this.getOptions())); } } class ${walkerName} extends Lint.RuleWalker { - protected visitNode(node: ts.Node): void { - console.log(ts.SyntaxKind[node.kind] + ' ' + node.getText()); super.visitNode(node); } } diff --git a/package-lock.json b/package-lock.json index 1c708c871..7ecbae031 100644 --- a/package-lock.json +++ b/package-lock.json @@ -398,6 +398,12 @@ "supports-color": "^5.3.0" } }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, "check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", @@ -473,6 +479,12 @@ "string-width": "^1.0.1" } }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", @@ -1090,6 +1102,17 @@ } } }, + "external-editor": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", + "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, "extglob": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", @@ -2040,6 +2063,15 @@ } } }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, "ignore": { "version": "3.3.10", "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", @@ -2068,6 +2100,88 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, + "inquirer": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.1.tgz", + "integrity": "sha512-088kl3DRT2dLU5riVMKKr1DlImd6X7smDhpXUCkJDCKvTEJeRiXh0G132HG9u5a+6Ylw9plFRY7RuTnwohYSpg==", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.0", + "figures": "^2.0.0", + "lodash": "^4.17.10", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.1.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "strip-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.0.0.tgz", + "integrity": "sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow==", + "dev": true, + "requires": { + "ansi-regex": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz", + "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==", + "dev": true + } + } + } + } + }, "is-accessor-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", @@ -2968,6 +3082,12 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, "nan": { "version": "2.11.1", "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz", @@ -3171,6 +3291,12 @@ "mimic-fn": "^1.0.0" } }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -3473,6 +3599,15 @@ "glob": "^7.0.5" } }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, "run-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/run-node/-/run-node-1.0.0.tgz", @@ -3503,6 +3638,12 @@ "ret": "~0.1.10" } }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, "semver": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", @@ -3908,6 +4049,21 @@ "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", "dev": true }, + "through": { + "version": "2.3.8", + "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, "to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", diff --git a/package.json b/package.json index 2dada4af2..a75921444 100644 --- a/package.json +++ b/package.json @@ -85,6 +85,7 @@ "cpy-cli": "^2.0.0", "glob": "^7.1.3", "husky": "^1.1.3", + "inquirer": "^6.2.1", "lint-staged": "^8.0.4", "mocha": "5.2.0", "npm-run-all": "^4.1.5", From b9ff0107248aa29c76fa2471f1db11f86544037e Mon Sep 17 00:00:00 2001 From: reduckted Date: Tue, 18 Dec 2018 19:13:13 +1000 Subject: [PATCH 2/4] Added a question to control the `typescriptOnly` option. --- build-tasks/create-rule.js | 36 +++++++++++++++++++------- build-tasks/templates/rule.template.js | 4 +-- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/build-tasks/create-rule.js b/build-tasks/create-rule.js index f21e31105..71e93d62d 100644 --- a/build-tasks/create-rule.js +++ b/build-tasks/create-rule.js @@ -27,6 +27,12 @@ const questions = [ return 'Please enter a description for the rule.'; } }, + { + name: 'typescriptOnly', + message: 'TypeScript only:', + type: 'confirm', + default: false + }, { name: 'type', message: 'Rule type:', @@ -73,17 +79,17 @@ const questions = [ inquirer.prompt(questions).then(answers => { const sourceFileName = createImplementationFile(answers); - const testFileName = createTestFiles(answers.name); + const testFileNames = createTestFiles(answers); console.log(`Rule '${answers.name}' created.`); console.log(`Source file: ${sourceFileName}`); - console.log(`Test file: ${testFileName}`); + console.log(`Test files: ${testFileNames.join(', ')}`); // If we're running in the VS Code terminal, try to open the // new files. If we can't do it, then it's not a big deal. if (process.env.VSCODE_CWD) { try { - execSync(`code "${testFileName}"`); + testFileNames.forEach(fileName => execSync(`code "${fileName}"`)); execSync(`code "${sourceFileName}"`); } catch (ex) { // Couldn't open VS Code. @@ -103,6 +109,7 @@ function createImplementationFile(answers) { walkerName, type: answers.type, description: answers.description, + typescriptOnly: answers.typescriptOnly, issueClass: answers.issueClass, issueType: answers.issueType, severity: answers.severity, @@ -115,14 +122,15 @@ function createImplementationFile(answers) { return sourceFileName; } -function createTestFiles(ruleName) { - const testsFolder = 'tests/' + ruleName; - const testFile = testsFolder + '/test.ts.lint'; +function createTestFiles(answers) { + const testFiles = []; + const name = answers.name; + const testsFolder = 'tests/' + name; + const tsTestFile = testsFolder + '/test.ts.lint'; const lintFile = testsFolder + '/tslint.json'; - const testContent = '// Code that should be checked by rule'; const tslintContent = { rules: { - [ruleName]: true + [name]: true } }; @@ -130,10 +138,18 @@ function createTestFiles(ruleName) { fs.mkdirSync(testsFolder); } - writeFile(testFile, testContent); + writeFile(tsTestFile, '// TypeScript code that should be checked by the rule.'); + testFiles.push(tsTestFile); + + if (!answers.typescriptOnly) { + const jsTestFile = testsFolder + '/test.js.lint'; + writeFile(jsTestFile, '// JavaScript code that should be checked by the rule.'); + testFiles.push(jsTestFile); + } + writeFile(lintFile, JSON.stringify(tslintContent, undefined, 4)); - return testFile; + return testFiles; } function camelCase(input) { diff --git a/build-tasks/templates/rule.template.js b/build-tasks/templates/rule.template.js index b90115ee7..322a0ef4e 100644 --- a/build-tasks/templates/rule.template.js +++ b/build-tasks/templates/rule.template.js @@ -1,4 +1,4 @@ -module.exports = ({ ruleName, walkerName, type, description, issueClass, issueType, severity, level, group }) => +module.exports = ({ ruleName, walkerName, type, description, typescriptOnly, issueClass, issueType, severity, level, group }) => `import * as ts from 'typescript'; import * as Lint from 'tslint'; @@ -16,7 +16,7 @@ export class Rule extends Lint.Rules.AbstractRule { options: null, // tslint:disable-line:no-null-keyword optionsDescription: '', optionExamples: [], // TODO: Remove this property if the rule has no options - typescriptOnly: false, + typescriptOnly: ${typescriptOnly}, issueClass: '${issueClass}', issueType: '${issueType}', severity: '${severity}', From 202ad4358d6bd266fe5320755de2b82ed805df70 Mon Sep 17 00:00:00 2001 From: reduckted Date: Tue, 18 Dec 2018 19:16:29 +1000 Subject: [PATCH 3/4] Added a TODO to fill in the `options` and `optionsDescription`. --- build-tasks/templates/rule.template.js | 1 + 1 file changed, 1 insertion(+) diff --git a/build-tasks/templates/rule.template.js b/build-tasks/templates/rule.template.js index 322a0ef4e..d2e5f22e5 100644 --- a/build-tasks/templates/rule.template.js +++ b/build-tasks/templates/rule.template.js @@ -13,6 +13,7 @@ export class Rule extends Lint.Rules.AbstractRule { ruleName: '${ruleName}', type: '${type}', description: '${description}', + // TODO: Fill in the options and options description, or leave them as they are if there are no options. options: null, // tslint:disable-line:no-null-keyword optionsDescription: '', optionExamples: [], // TODO: Remove this property if the rule has no options From 953d27ca0efdfb01a69fd1c3becbeab6f2bb7d32 Mon Sep 17 00:00:00 2001 From: reduckted Date: Tue, 18 Dec 2018 19:56:29 +1000 Subject: [PATCH 4/4] Improved opening files in VS Code. --- build-tasks/create-rule.js | 41 ++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/build-tasks/create-rule.js b/build-tasks/create-rule.js index 71e93d62d..e7c2b33ee 100644 --- a/build-tasks/create-rule.js +++ b/build-tasks/create-rule.js @@ -85,17 +85,8 @@ inquirer.prompt(questions).then(answers => { console.log(`Source file: ${sourceFileName}`); console.log(`Test files: ${testFileNames.join(', ')}`); - // If we're running in the VS Code terminal, try to open the - // new files. If we can't do it, then it's not a big deal. - if (process.env.VSCODE_CWD) { - try { - testFileNames.forEach(fileName => execSync(`code "${fileName}"`)); - execSync(`code "${sourceFileName}"`); - } catch (ex) { - // Couldn't open VS Code. - console.log(ex); - } - } + // Attempt to open the files in the current editor. + tryOpenFiles([...testFileNames, sourceFileName]); }); function createImplementationFile(answers) { @@ -159,3 +150,31 @@ function camelCase(input) { function pascalCase(input) { return input.charAt(0).toUpperCase() + input.substr(1); } + +function tryOpenFiles(files) { + // Check if we're running in the VS Code terminal. If we + // are, then we can try to open the new files in VS Code. + // If we can't open the files, then it's not a big deal. + if (process.env.VSCODE_CWD) { + let exe; + + // We need to check if we're running in normal VS Code or the Insiders version. + // The `TERM_PROGRAM_VERSION` environment variable will contain the version number + // of VS Code. For VS Code Insiders, that version will have the suffix "-insider". + const version = process.env.TERM_PROGRAM_VERSION || ''; + if (version.endsWith('-insider')) { + exe = 'code-insiders'; + console.log('Opening the new files in VS Code Insiders...'); + } else { + exe = 'code'; + console.log('Opening the new files in VS Code...'); + } + + try { + files.forEach(fileName => execSync(`${exe} "${fileName}"`)); + } catch (ex) { + // Couldn't open VS Code. + console.log(ex); + } + } +}