From 9b18cf8da8cfceb2b8adc26974ef926d2fcff85f Mon Sep 17 00:00:00 2001 From: rajsite Date: Tue, 19 Jan 2021 10:50:59 -0600 Subject: [PATCH 01/10] Match peer and dev dependencies --- package-lock.json | 120 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 2 files changed, 121 insertions(+) diff --git a/package-lock.json b/package-lock.json index d24893f..d182c6f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -72,12 +72,132 @@ "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", "dev": true }, + "@types/json-schema": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", + "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", + "dev": true + }, "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "dev": true }, + "@typescript-eslint/eslint-plugin": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.14.0.tgz", + "integrity": "sha512-IJ5e2W7uFNfg4qh9eHkHRUCbgZ8VKtGwD07kannJvM5t/GU8P8+24NX8gi3Hf5jST5oWPY8kyV1s/WtfiZ4+Ww==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "4.14.0", + "@typescript-eslint/scope-manager": "4.14.0", + "debug": "^4.1.1", + "functional-red-black-tree": "^1.0.1", + "lodash": "^4.17.15", + "regexpp": "^3.0.0", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.14.0.tgz", + "integrity": "sha512-/J+LlRMdbPh4RdL4hfP1eCwHN5bAhFAGOTsvE6SxsrM/47XQiPSgF5MDgLyp/i9kbZV9Lx80DW0OpPkzL+uf8Q==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.14.0", + "@typescript-eslint/visitor-keys": "4.14.0" + } + }, + "@typescript-eslint/types": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.14.0.tgz", + "integrity": "sha512-VsQE4VvpldHrTFuVPY1ZnHn/Txw6cZGjL48e+iBxTi2ksa9DmebKjAeFmTVAYoSkTk7gjA7UqJ7pIsyifTsI4A==", + "dev": true + }, + "@typescript-eslint/visitor-keys": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.14.0.tgz", + "integrity": "sha512-MeHHzUyRI50DuiPgV9+LxcM52FCJFYjJiWHtXlbyC27b80mfOwKeiKI+MHOTEpcpfmoPFm/vvQS88bYIx6PZTA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.14.0", + "eslint-visitor-keys": "^2.0.0" + } + }, + "eslint-visitor-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", + "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", + "dev": true + } + } + }, + "@typescript-eslint/experimental-utils": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.14.0.tgz", + "integrity": "sha512-6i6eAoiPlXMKRbXzvoQD5Yn9L7k9ezzGRvzC/x1V3650rUk3c3AOjQyGYyF9BDxQQDK2ElmKOZRD0CbtdkMzQQ==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/scope-manager": "4.14.0", + "@typescript-eslint/types": "4.14.0", + "@typescript-eslint/typescript-estree": "4.14.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^2.0.0" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.14.0.tgz", + "integrity": "sha512-/J+LlRMdbPh4RdL4hfP1eCwHN5bAhFAGOTsvE6SxsrM/47XQiPSgF5MDgLyp/i9kbZV9Lx80DW0OpPkzL+uf8Q==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.14.0", + "@typescript-eslint/visitor-keys": "4.14.0" + } + }, + "@typescript-eslint/types": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.14.0.tgz", + "integrity": "sha512-VsQE4VvpldHrTFuVPY1ZnHn/Txw6cZGjL48e+iBxTi2ksa9DmebKjAeFmTVAYoSkTk7gjA7UqJ7pIsyifTsI4A==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.14.0.tgz", + "integrity": "sha512-wRjZ5qLao+bvS2F7pX4qi2oLcOONIB+ru8RGBieDptq/SudYwshveORwCVU4/yMAd4GK7Fsf8Uq1tjV838erag==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.14.0", + "@typescript-eslint/visitor-keys": "4.14.0", + "debug": "^4.1.1", + "globby": "^11.0.1", + "is-glob": "^4.0.1", + "lodash": "^4.17.15", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.14.0.tgz", + "integrity": "sha512-MeHHzUyRI50DuiPgV9+LxcM52FCJFYjJiWHtXlbyC27b80mfOwKeiKI+MHOTEpcpfmoPFm/vvQS88bYIx6PZTA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.14.0", + "eslint-visitor-keys": "^2.0.0" + } + }, + "eslint-visitor-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", + "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", + "dev": true + } + } + }, "@typescript-eslint/parser": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.9.1.tgz", diff --git a/package.json b/package.json index ca4e795..563548d 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "eslint-config-airbnb-base": "~14.2.0" }, "devDependencies": { + "@typescript-eslint/eslint-plugin": "^4.9.1", "eslint": "^7.6.0", "eslint-plugin-import": "^2.22.0" }, From 565de82ed70984309db392d29807cc7b873c1829 Mon Sep 17 00:00:00 2001 From: rajsite Date: Tue, 19 Jan 2021 19:00:00 -0600 Subject: [PATCH 02/10] TypeScript enable all rules --- .github/workflows/build.yml | 13 +- .github/workflows/release.yml | 18 --- .gitignore | 1 + index.js | 8 +- lib/typescript-base.js | 9 ++ ...ript-extensions-requiring-type-checking.js | 18 +++ lib/typescript-extensions.js | 115 ++++++++++++++++++ package-lock.json | 6 + package.json | 7 +- test/fixtures/typescript-smoke-test.ts | 13 ++ tools/print-typescript-properties.js | 38 ++++++ typescript-requiring-type-checking.js | 38 ++++++ typescript.js | 113 +++++++---------- 13 files changed, 304 insertions(+), 93 deletions(-) delete mode 100644 .github/workflows/release.yml create mode 100644 lib/typescript-base.js create mode 100644 lib/typescript-extensions-requiring-type-checking.js create mode 100644 lib/typescript-extensions.js create mode 100644 test/fixtures/typescript-smoke-test.ts create mode 100644 tools/print-typescript-properties.js create mode 100644 typescript-requiring-type-checking.js diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index da1cd55..f07394e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,8 +1,10 @@ on: - pull_request_target: + pull_request: push: branches: - '*' + tags: + - 'v*' jobs: build: @@ -12,4 +14,13 @@ jobs: - uses: actions/setup-node@v1 with: node-version: '12' + registry-url: 'https://registry.npmjs.org' + - run: npm ci + - run: npm run test-typescript + - run: npm run dev-print-typescript-props + + - if: startsWith(github.ref, 'refs/tags/v') + run: npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 0247ef7..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,18 +0,0 @@ -on: - push: - tags: - - 'v*' - -jobs: - release: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 - with: - node-version: '12' - registry-url: 'https://registry.npmjs.org' - - run: npm ci - - run: npm publish - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.gitignore b/.gitignore index 1faf560..263fea0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules .DS_Store *.tgz +.vscode diff --git a/index.js b/index.js index 92ca264..0155847 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,11 @@ module.exports = { - extends: 'airbnb-base', + extends: [ + /* + airbnb-base source: + https://github.com/airbnb/javascript/blob/master/packages/eslint-config-airbnb-base/index.js + */ + 'airbnb-base' + ], rules: { /* Omit arrow function parenthesis where they are not required to improve readability. diff --git a/lib/typescript-base.js b/lib/typescript-base.js new file mode 100644 index 0000000..c0a0b42 --- /dev/null +++ b/lib/typescript-base.js @@ -0,0 +1,9 @@ +module.exports = { + rules: { + /* + The following rules are already handled by the TypeScript compiler. + */ + 'import/named': 'off', + 'import/no-unresolved': 'off', + } +}; diff --git a/lib/typescript-extensions-requiring-type-checking.js b/lib/typescript-extensions-requiring-type-checking.js new file mode 100644 index 0000000..398e063 --- /dev/null +++ b/lib/typescript-extensions-requiring-type-checking.js @@ -0,0 +1,18 @@ +module.exports = { + rules: { + // 'dot-notation': 'off', + // '@typescript-eslint/dot-notation': 'error', + + // 'no-implied-eval': 'off', + // '@typescript-eslint/no-implied-eval': 'error', + + // 'no-throw-literal': 'off', + // '@typescript-eslint/no-throw-literal': 'error', + + // 'require-await': 'off', + // '@typescript-eslint/require-await': 'error', + + // 'no-return-await': 'off', + // '@typescript-eslint/return-await': 'error', + } +}; diff --git a/lib/typescript-extensions.js b/lib/typescript-extensions.js new file mode 100644 index 0000000..3569c92 --- /dev/null +++ b/lib/typescript-extensions.js @@ -0,0 +1,115 @@ +module.exports = { + rules: { + /* + The following are extension rules that replace core JavaScript rules to support + TypeScript. + * When upgrading, changes to these rules can be identified in the typescript-eslint + changelog under features and breaking changes: + https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin#extension-rules + * In addition, the `npm run dev-print-typescript-props` command can be used to list + the expected extension properties. + * The value of the extension properties should match the value chosen by the + JavaScript / Airbnb configuration. + */ + + 'brace-style': 'off', + '@typescript-eslint/brace-style': ['error', '1tbs', { allowSingleLine: true }], + + 'comma-dangle': 'off', + '@typescript-eslint/comma-dangle': ['error', 'only-multiline'], + + 'comma-spacing': 'off', + '@typescript-eslint/comma-spacing': ['error'], + + 'default-param-last': 'off', + // TODO: enable TS rule? + + 'func-call-spacing': 'off', + '@typescript-eslint/func-call-spacing': 'error', + + indent: 'off', + '@typescript-eslint/indent': ['error', 4], + + 'init-declarations': 'off', + // TODO: enable TS rule? + + 'keyword-spacing': 'off', + '@typescript-eslint/keyword-spacing': 'error', + + 'lines-between-class-members': 'off', + '@typescript-eslint/lines-between-class-members': 'error', + + 'no-array-constructor': 'off', + '@typescript-eslint/no-array-constructor': 'error', + + 'no-dupe-class-members': 'off', + '@typescript-eslint/no-dupe-class-members': 'error', + + 'no-duplicate-imports': 'off', + // TODO: enable TS rule? + + 'no-empty-function': 'off', + '@typescript-eslint/no-empty-function': ['error', { + allow: [ + 'arrowFunctions', + 'functions', + 'methods' + ] + }], + + 'no-extra-parens': 'off', + // TODO: enable TS rule? + + 'no-extra-semi': 'off', + '@typescript-eslint/no-extra-semi': 'error', + + 'no-invalid-this': 'off', + // TODO: enable TS rule? + + 'no-loop-func': 'off', + '@typescript-eslint/no-loop-func': 'error', + + 'no-loss-of-precision': 'off', + // TODO: enable TS rule? + + 'no-magic-numbers': 'off', + // TODO: enable TS rule? + + 'no-redeclare': 'off', + '@typescript-eslint/no-redeclare': 'error', + + 'no-shadow': 'off', + '@typescript-eslint/no-shadow': 'error', + + 'no-unused-expressions': 'off', + '@typescript-eslint/no-unused-expressions': 'error', + + 'no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': ['error', { vars: 'all', args: 'after-used', ignoreRestSiblings: true }], + + 'no-use-before-define': 'off', + '@typescript-eslint/no-use-before-define': ['error', { functions: false, classes: true, variables: true }], + + 'no-useless-constructor': 'off', + '@typescript-eslint/no-useless-constructor': 'error', + + 'object-curly-spacing': 'off', + // TODO: enable TS rule? + + quotes: 'off', + '@typescript-eslint/quotes': ['error', 'single', { avoidEscape: true }], + + semi: 'off', + '@typescript-eslint/semi': 'error', + + 'space-before-function-paren': 'off', + '@typescript-eslint/space-before-function-paren': ['error', { + anonymous: 'always', + named: 'never', + asyncArrow: 'always' + }], + + 'space-infix-ops': 'off', + '@typescript-eslint/space-infix-ops': 'error', + } +}; diff --git a/package-lock.json b/package-lock.json index d182c6f..d3bc3ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1697,6 +1697,12 @@ "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true }, + "typescript": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", + "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==", + "dev": true + }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", diff --git a/package.json b/package.json index 563548d..76fd057 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,9 @@ "main": "index.js", "scripts": { "prepare": "npm test", - "test": "eslint ." + "test": "eslint .", + "test-typescript": "eslint -c ./typescript.js ./test/fixtures/typescript-smoke-test.ts", + "dev-print-typescript-props": "node tools/print-typescript-properties" }, "repository": { "type": "git", @@ -36,7 +38,8 @@ "devDependencies": { "@typescript-eslint/eslint-plugin": "^4.9.1", "eslint": "^7.6.0", - "eslint-plugin-import": "^2.22.0" + "eslint-plugin-import": "^2.22.0", + "typescript": "^4.1.3" }, "peerDependencies": { "@typescript-eslint/eslint-plugin": "^4.9.1", diff --git a/test/fixtures/typescript-smoke-test.ts b/test/fixtures/typescript-smoke-test.ts new file mode 100644 index 0000000..027f543 --- /dev/null +++ b/test/fixtures/typescript-smoke-test.ts @@ -0,0 +1,13 @@ +// TypeScript Smoke Test + +export default class NI { + private _awesomeLevel = 1; + + public get awesome(): boolean { + return this._awesomeLevel > 0; + } + + public makeAwesomer(): void { + this._awesomeLevel += 1; + } +} diff --git a/tools/print-typescript-properties.js b/tools/print-typescript-properties.js new file mode 100644 index 0000000..ddd5c7f --- /dev/null +++ b/tools/print-typescript-properties.js @@ -0,0 +1,38 @@ +const plugin = require('@typescript-eslint/eslint-plugin'); + +const isTrue = val => val !== undefined && val !== false; +const recommended = key => isTrue(plugin.rules[key].meta.docs.recommended); +const requiresTypeChecking = key => isTrue(plugin.rules[key].meta.docs.requiresTypeChecking); +const extendsBaseRule = key => isTrue(plugin.rules[key].meta.docs.extendsBaseRule); +const print = keys => { + const results = {}; + keys.forEach(key => { results[`@typescript-eslint/${key}`] = 'error'; }); + global.console.log(JSON.stringify(results, null, 4)); +}; + +const typeScriptExtensions = Object.keys(plugin.rules) + .filter(key => extendsBaseRule(key)) + .filter(key => !requiresTypeChecking(key)); + +const typeScriptExtensionsRequiringTypeChecks = Object.keys(plugin.rules) + .filter(key => extendsBaseRule(key)) + .filter(key => requiresTypeChecking(key)); + +const typeScript = Object.keys(plugin.rules) + .filter(key => !extendsBaseRule(key)) + .filter(key => !recommended(key)) + .filter(key => !requiresTypeChecking(key)); + +const typeScriptRequiringTypeChecks = Object.keys(plugin.rules) + .filter(key => !extendsBaseRule(key)) + .filter(key => !recommended(key)) + .filter(key => requiresTypeChecking(key)); + +global.console.log('TypeScript Extensions:'); +print(typeScriptExtensions); +global.console.log('TypeScript Extensions Requiring Type Checks:'); +print(typeScriptExtensionsRequiringTypeChecks); +global.console.log('Remaining TypeScript Rules:'); +print(typeScript); +global.console.log('Remaining TypeScript Rules Requiring Type Checks:'); +print(typeScriptRequiringTypeChecks); diff --git a/typescript-requiring-type-checking.js b/typescript-requiring-type-checking.js new file mode 100644 index 0000000..7ee02c5 --- /dev/null +++ b/typescript-requiring-type-checking.js @@ -0,0 +1,38 @@ +module.exports = { + extends: [ + /* + @typescript-eslint/recommended source: + https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/src/configs/recommended.ts + + import/typescript source: + https://github.com/benmosher/eslint-plugin-import/blob/master/config/typescript.js + */ + './index', + 'plugin:@typescript-eslint/recommended', + 'plugin:import/typescript', + './lib/typescript-base', + './lib/typescript-extensions', + './lib/typescript-extensions-requiring-type-checking' + ], + parser: '@typescript-eslint/parser', + rules: { + // '@typescript-eslint/naming-convention': 'error', + // '@typescript-eslint/no-base-to-string': 'error', + // '@typescript-eslint/no-confusing-void-expression': 'error', + // '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'error', + // '@typescript-eslint/no-unnecessary-condition': 'error', + // '@typescript-eslint/no-unnecessary-qualifier': 'error', + // '@typescript-eslint/no-unnecessary-type-arguments': 'error', + // '@typescript-eslint/non-nullable-type-assertion-style': 'error', + // '@typescript-eslint/prefer-includes': 'error', + // '@typescript-eslint/prefer-nullish-coalescing': 'error', + // '@typescript-eslint/prefer-readonly': 'error', + // '@typescript-eslint/prefer-readonly-parameter-types': 'error', + // '@typescript-eslint/prefer-reduce-type-parameter': 'error', + // '@typescript-eslint/prefer-string-starts-ends-with': 'error', + // '@typescript-eslint/promise-function-async': 'error', + // '@typescript-eslint/require-array-sort-compare': 'error', + // '@typescript-eslint/strict-boolean-expressions': 'error', + // '@typescript-eslint/switch-exhaustiveness-check': 'error' + } +}; diff --git a/typescript.js b/typescript.js index 8f5fab6..fe9e25f 100644 --- a/typescript.js +++ b/typescript.js @@ -1,80 +1,51 @@ module.exports = { extends: [ + /* + @typescript-eslint/recommended source: + https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/src/configs/recommended.ts + + import/typescript source: + https://github.com/benmosher/eslint-plugin-import/blob/master/config/typescript.js + */ './index', 'plugin:@typescript-eslint/recommended', - 'plugin:import/typescript' + 'plugin:import/typescript', + './lib/typescript-base', + './lib/typescript-extensions' ], parser: '@typescript-eslint/parser', rules: { - /* - The following rules are already handled by the TypeScript compiler. - */ - 'import/named': 'off', - 'import/no-unresolved': 'off', - - /* - The following are extension rules that replace core JavaScript rules to support - TypeScript. When upgrading, changes to these rules can be identified in the - typescript-eslint changelog under features and breaking changes. - https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin#extension-rules - */ - 'brace-style': 'off', - '@typescript-eslint/brace-style': ['error', '1tbs', { allowSingleLine: true }], - 'comma-dangle': 'off', - '@typescript-eslint/comma-dangle': ['error', 'only-multiline'], - 'comma-spacing': 'off', - '@typescript-eslint/comma-spacing': ['error'], - 'dot-notation': 'off', - '@typescript-eslint/dot-notation': 'error', - 'func-call-spacing': 'off', - '@typescript-eslint/func-call-spacing': 'error', - indent: 'off', - '@typescript-eslint/indent': ['error', 4], - 'keyword-spacing': 'off', - '@typescript-eslint/keyword-spacing': 'error', - 'lines-between-class-members': 'off', - '@typescript-eslint/lines-between-class-members': 'error', - 'no-array-constructor': 'off', - '@typescript-eslint/no-array-constructor': 'error', - 'no-dupe-class-members': 'off', - '@typescript-eslint/no-dupe-class-members': 'error', - 'no-empty-function': 'off', - '@typescript-eslint/no-empty-function': ['error', { - allow: [ - 'arrowFunctions', - 'functions', - 'methods' - ] - }], - 'no-extra-semi': 'off', - '@typescript-eslint/no-extra-semi': 'error', - 'no-loop-func': 'off', - '@typescript-eslint/no-loop-func': 'error', - 'no-redeclare': 'off', - '@typescript-eslint/no-redeclare': 'error', - 'no-shadow': 'off', - '@typescript-eslint/no-shadow': 'error', - 'no-unused-expressions': 'off', - '@typescript-eslint/no-unused-expressions': 'error', - 'no-unused-vars': 'off', - '@typescript-eslint/no-unused-vars': ['error', { vars: 'all', args: 'after-used', ignoreRestSiblings: true }], - 'no-use-before-define': 'off', - '@typescript-eslint/no-use-before-define': ['error', { functions: false, classes: true, variables: true }], - 'no-useless-constructor': 'off', - '@typescript-eslint/no-useless-constructor': 'error', - quotes: 'off', - '@typescript-eslint/quotes': ['error', 'single', { avoidEscape: true }], - 'no-return-await': 'off', - '@typescript-eslint/return-await': 'error', - semi: 'off', - '@typescript-eslint/semi': 'error', - 'space-before-function-paren': 'off', - '@typescript-eslint/space-before-function-paren': ['error', { - anonymous: 'always', - named: 'never', - asyncArrow: 'always' - }], - 'space-infix-ops': 'off', - '@typescript-eslint/space-infix-ops': 'error', + '@typescript-eslint/array-type': 'error', + '@typescript-eslint/ban-tslint-comment': 'error', + '@typescript-eslint/class-literal-property-style': 'error', + '@typescript-eslint/consistent-indexed-object-style': 'error', + '@typescript-eslint/consistent-type-assertions': 'error', + '@typescript-eslint/consistent-type-definitions': 'error', + '@typescript-eslint/consistent-type-imports': 'error', + '@typescript-eslint/explicit-function-return-type': 'error', + '@typescript-eslint/explicit-member-accessibility': 'error', + '@typescript-eslint/member-delimiter-style': 'error', + '@typescript-eslint/member-ordering': 'error', + '@typescript-eslint/method-signature-style': 'error', + '@typescript-eslint/no-confusing-non-null-assertion': 'error', + '@typescript-eslint/no-dynamic-delete': 'error', + '@typescript-eslint/no-extraneous-class': 'error', + '@typescript-eslint/no-implicit-any-catch': 'error', + '@typescript-eslint/no-invalid-void-type': 'error', + '@typescript-eslint/no-parameter-properties': 'error', + '@typescript-eslint/no-require-imports': 'error', + '@typescript-eslint/no-type-alias': 'error', + '@typescript-eslint/no-unnecessary-type-constraint': 'error', + '@typescript-eslint/no-unused-vars-experimental': 'error', + '@typescript-eslint/prefer-enum-initializers': 'error', + '@typescript-eslint/prefer-for-of': 'error', + '@typescript-eslint/prefer-function-type': 'error', + '@typescript-eslint/prefer-literal-enum-member': 'error', + '@typescript-eslint/prefer-optional-chain': 'error', + '@typescript-eslint/prefer-ts-expect-error': 'error', + '@typescript-eslint/sort-type-union-intersection-members': 'error', + '@typescript-eslint/type-annotation-spacing': 'error', + '@typescript-eslint/typedef': 'error', + '@typescript-eslint/unified-signatures': 'error' } }; From 5cc5b60d910fa5799dbba4fffbde5bfe5b6622d3 Mon Sep 17 00:00:00 2001 From: rajsite Date: Tue, 19 Jan 2021 19:47:42 -0600 Subject: [PATCH 03/10] Synchronize TypeScript extensions to Airbnb and NI --- ...ript-extensions-requiring-type-checking.js | 26 +++--- lib/typescript-extensions.js | 81 +++++++++++++++---- 2 files changed, 83 insertions(+), 24 deletions(-) diff --git a/lib/typescript-extensions-requiring-type-checking.js b/lib/typescript-extensions-requiring-type-checking.js index 398e063..06b545d 100644 --- a/lib/typescript-extensions-requiring-type-checking.js +++ b/lib/typescript-extensions-requiring-type-checking.js @@ -1,18 +1,24 @@ module.exports = { rules: { - // 'dot-notation': 'off', - // '@typescript-eslint/dot-notation': 'error', + // Defined by Airbnb + 'dot-notation': 'off', + '@typescript-eslint/dot-notation': ['error', { allowKeywords: true }], - // 'no-implied-eval': 'off', - // '@typescript-eslint/no-implied-eval': 'error', + // Defined by Airbnb + 'no-implied-eval': 'off', + '@typescript-eslint/no-implied-eval': 'error', - // 'no-throw-literal': 'off', - // '@typescript-eslint/no-throw-literal': 'error', + // Defined by Airbnb + 'no-throw-literal': 'off', + '@typescript-eslint/no-throw-literal': 'error', - // 'require-await': 'off', - // '@typescript-eslint/require-await': 'error', + // Defined by Airbnb + 'require-await': 'off', + '@typescript-eslint/require-await': 'off', + // TODO: the reasoning here is dubious... I think we should enable the rule https://github.com/airbnb/javascript/issues/2013 - // 'no-return-await': 'off', - // '@typescript-eslint/return-await': 'error', + // Defined by Airbnb + 'no-return-await': 'off', + '@typescript-eslint/return-await': 'error', } }; diff --git a/lib/typescript-extensions.js b/lib/typescript-extensions.js index 3569c92..58cb337 100644 --- a/lib/typescript-extensions.js +++ b/lib/typescript-extensions.js @@ -12,42 +12,63 @@ module.exports = { JavaScript / Airbnb configuration. */ + // Defined by Airbnb 'brace-style': 'off', '@typescript-eslint/brace-style': ['error', '1tbs', { allowSingleLine: true }], + // Defined by NI 'comma-dangle': 'off', '@typescript-eslint/comma-dangle': ['error', 'only-multiline'], + // Defined by Airbnb 'comma-spacing': 'off', - '@typescript-eslint/comma-spacing': ['error'], + '@typescript-eslint/comma-spacing': ['error', { before: false, after: true }], + // Defined by Airbnb 'default-param-last': 'off', - // TODO: enable TS rule? + '@typescript-eslint/default-param-last': 'off', + // Defined by Airbnb 'func-call-spacing': 'off', - '@typescript-eslint/func-call-spacing': 'error', + '@typescript-eslint/func-call-spacing': ['error', 'never'], + // Defined by NI indent: 'off', '@typescript-eslint/indent': ['error', 4], + // Defined by Airbnb 'init-declarations': 'off', - // TODO: enable TS rule? + '@typescript-eslint/init-declarations': 'off', + // Defined by Airbnb 'keyword-spacing': 'off', - '@typescript-eslint/keyword-spacing': 'error', + '@typescript-eslint/keyword-spacing': ['error', { + before: true, + after: true, + overrides: { + return: { after: true }, + throw: { after: true }, + case: { after: true } + } + }], + // Defined by Airbnb 'lines-between-class-members': 'off', - '@typescript-eslint/lines-between-class-members': 'error', + '@typescript-eslint/lines-between-class-members': ['error', 'always', { exceptAfterSingleLine: false }], + // Defined by Airbnb 'no-array-constructor': 'off', '@typescript-eslint/no-array-constructor': 'error', + // Defined by Airbnb 'no-dupe-class-members': 'off', '@typescript-eslint/no-dupe-class-members': 'error', + // Defined by Airbnb 'no-duplicate-imports': 'off', - // TODO: enable TS rule? + '@typescript-eslint/no-duplicate-imports': 'off', + // Defined by Airbnb 'no-empty-function': 'off', '@typescript-eslint/no-empty-function': ['error', { allow: [ @@ -57,51 +78,82 @@ module.exports = { ] }], + // Defined by Airbnb 'no-extra-parens': 'off', - // TODO: enable TS rule? + '@typescript-eslint/no-extra-parens': ['off', 'all', { + conditionalAssign: true, + nestedBinaryExpressions: false, + returnAssign: false, + ignoreJSX: 'all', + enforceForArrowConditionals: false, + }], + // Defined by Airbnb 'no-extra-semi': 'off', '@typescript-eslint/no-extra-semi': 'error', + // Defined by Airbnb 'no-invalid-this': 'off', - // TODO: enable TS rule? + '@typescript-eslint/no-invalid-this': 'off', + // Defined by NI 'no-loop-func': 'off', '@typescript-eslint/no-loop-func': 'error', + // Defined by Airbnb 'no-loss-of-precision': 'off', - // TODO: enable TS rule? + '@typescript-eslint/no-loss-of-precision': 'off', + // Defined by Airbnb 'no-magic-numbers': 'off', - // TODO: enable TS rule? + '@typescript-eslint/no-magic-numbers': ['off', { + ignore: [], + ignoreArrayIndexes: true, + enforceConst: true, + detectObjects: false, + }], + // Defined by Airbnb 'no-redeclare': 'off', '@typescript-eslint/no-redeclare': 'error', + // Defined by Airbnb 'no-shadow': 'off', '@typescript-eslint/no-shadow': 'error', + // Defined by Airbnb 'no-unused-expressions': 'off', - '@typescript-eslint/no-unused-expressions': 'error', + '@typescript-eslint/no-unused-expressions': ['error', { + allowShortCircuit: false, + allowTernary: false, + allowTaggedTemplates: false, + }], + // Defined by Airbnb 'no-unused-vars': 'off', '@typescript-eslint/no-unused-vars': ['error', { vars: 'all', args: 'after-used', ignoreRestSiblings: true }], + // Defined by NI 'no-use-before-define': 'off', '@typescript-eslint/no-use-before-define': ['error', { functions: false, classes: true, variables: true }], + // Defined by Airbnb 'no-useless-constructor': 'off', '@typescript-eslint/no-useless-constructor': 'error', + // Defined by Airbnb 'object-curly-spacing': 'off', - // TODO: enable TS rule? + '@typescript-eslint/object-curly-spacing': ['error', 'always'], + // Defined by Airbnb quotes: 'off', '@typescript-eslint/quotes': ['error', 'single', { avoidEscape: true }], + // Defined by Airbnb semi: 'off', - '@typescript-eslint/semi': 'error', + '@typescript-eslint/semi': ['error', 'always'], + // Defined by NI 'space-before-function-paren': 'off', '@typescript-eslint/space-before-function-paren': ['error', { anonymous: 'always', @@ -109,6 +161,7 @@ module.exports = { asyncArrow: 'always' }], + // Defined by Airbnb 'space-infix-ops': 'off', '@typescript-eslint/space-infix-ops': 'error', } From dd66302a579804004da47b0bab6bf7d120b65b13 Mon Sep 17 00:00:00 2001 From: rajsite Date: Tue, 19 Jan 2021 23:04:42 -0600 Subject: [PATCH 04/10] TypeScript linting requiring type checking --- .github/workflows/build.yml | 1 + lib/typescript-base.js | 9 --- package.json | 5 +- test/typescript-type-checking/.eslintrc.js | 9 +++ .../index.ts} | 0 test/typescript-type-checking/tsconfig.json | 7 +++ test/typescript/.eslintrc.js | 4 ++ test/typescript/index.ts | 13 ++++ typescript-requiring-type-checking.js | 59 +++++++++---------- typescript.js | 25 +++++--- 10 files changed, 83 insertions(+), 49 deletions(-) delete mode 100644 lib/typescript-base.js create mode 100644 test/typescript-type-checking/.eslintrc.js rename test/{fixtures/typescript-smoke-test.ts => typescript-type-checking/index.ts} (100%) create mode 100644 test/typescript-type-checking/tsconfig.json create mode 100644 test/typescript/.eslintrc.js create mode 100644 test/typescript/index.ts diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f07394e..e5a03a8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,6 +18,7 @@ jobs: - run: npm ci - run: npm run test-typescript + - run: npm run test-typescript-typed - run: npm run dev-print-typescript-props - if: startsWith(github.ref, 'refs/tags/v') diff --git a/lib/typescript-base.js b/lib/typescript-base.js deleted file mode 100644 index c0a0b42..0000000 --- a/lib/typescript-base.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = { - rules: { - /* - The following rules are already handled by the TypeScript compiler. - */ - 'import/named': 'off', - 'import/no-unresolved': 'off', - } -}; diff --git a/package.json b/package.json index 76fd057..6300886 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,9 @@ "scripts": { "prepare": "npm test", "test": "eslint .", - "test-typescript": "eslint -c ./typescript.js ./test/fixtures/typescript-smoke-test.ts", - "dev-print-typescript-props": "node tools/print-typescript-properties" + "test-typescript": "eslint ./test/typescript/", + "test-typescript-typed": "eslint ./test/typescript-type-checking/", + "dev-print-typescript-props": "node ./tools/print-typescript-properties" }, "repository": { "type": "git", diff --git a/test/typescript-type-checking/.eslintrc.js b/test/typescript-type-checking/.eslintrc.js new file mode 100644 index 0000000..56ab480 --- /dev/null +++ b/test/typescript-type-checking/.eslintrc.js @@ -0,0 +1,9 @@ +module.exports = { + extends: '../../typescript-requiring-type-checking', + root: true, + ignorePatterns: ['*.js'], + parserOptions: { + project: ['./tsconfig.json'], + tsconfigRootDir: __dirname, + }, +}; diff --git a/test/fixtures/typescript-smoke-test.ts b/test/typescript-type-checking/index.ts similarity index 100% rename from test/fixtures/typescript-smoke-test.ts rename to test/typescript-type-checking/index.ts diff --git a/test/typescript-type-checking/tsconfig.json b/test/typescript-type-checking/tsconfig.json new file mode 100644 index 0000000..f50bdf2 --- /dev/null +++ b/test/typescript-type-checking/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": ["./index.ts"], + "compilerOptions": { + "strictNullChecks": true, + "target": "ES2020" + }, +} \ No newline at end of file diff --git a/test/typescript/.eslintrc.js b/test/typescript/.eslintrc.js new file mode 100644 index 0000000..8f38443 --- /dev/null +++ b/test/typescript/.eslintrc.js @@ -0,0 +1,4 @@ +module.exports = { + extends: '../../typescript', + root: true, +}; \ No newline at end of file diff --git a/test/typescript/index.ts b/test/typescript/index.ts new file mode 100644 index 0000000..027f543 --- /dev/null +++ b/test/typescript/index.ts @@ -0,0 +1,13 @@ +// TypeScript Smoke Test + +export default class NI { + private _awesomeLevel = 1; + + public get awesome(): boolean { + return this._awesomeLevel > 0; + } + + public makeAwesomer(): void { + this._awesomeLevel += 1; + } +} diff --git a/typescript-requiring-type-checking.js b/typescript-requiring-type-checking.js index 7ee02c5..69490e0 100644 --- a/typescript-requiring-type-checking.js +++ b/typescript-requiring-type-checking.js @@ -1,38 +1,37 @@ module.exports = { extends: [ - /* - @typescript-eslint/recommended source: - https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/src/configs/recommended.ts - - import/typescript source: - https://github.com/benmosher/eslint-plugin-import/blob/master/config/typescript.js - */ - './index', - 'plugin:@typescript-eslint/recommended', - 'plugin:import/typescript', - './lib/typescript-base', - './lib/typescript-extensions', + 'plugin:@typescript-eslint/recommended-requiring-type-checking', './lib/typescript-extensions-requiring-type-checking' ], parser: '@typescript-eslint/parser', rules: { - // '@typescript-eslint/naming-convention': 'error', - // '@typescript-eslint/no-base-to-string': 'error', - // '@typescript-eslint/no-confusing-void-expression': 'error', - // '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'error', - // '@typescript-eslint/no-unnecessary-condition': 'error', - // '@typescript-eslint/no-unnecessary-qualifier': 'error', - // '@typescript-eslint/no-unnecessary-type-arguments': 'error', - // '@typescript-eslint/non-nullable-type-assertion-style': 'error', - // '@typescript-eslint/prefer-includes': 'error', - // '@typescript-eslint/prefer-nullish-coalescing': 'error', - // '@typescript-eslint/prefer-readonly': 'error', - // '@typescript-eslint/prefer-readonly-parameter-types': 'error', - // '@typescript-eslint/prefer-reduce-type-parameter': 'error', - // '@typescript-eslint/prefer-string-starts-ends-with': 'error', - // '@typescript-eslint/promise-function-async': 'error', - // '@typescript-eslint/require-array-sort-compare': 'error', - // '@typescript-eslint/strict-boolean-expressions': 'error', - // '@typescript-eslint/switch-exhaustiveness-check': 'error' + /* + Overrides to TypeScript recommended rules + https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/src/configs/recommended-requiring-type-checking.ts + */ + + // None + + /* + TypeScript rules outside of the recommended configuration + */ + '@typescript-eslint/naming-convention': 'error', + '@typescript-eslint/no-base-to-string': 'error', + '@typescript-eslint/no-confusing-void-expression': 'error', + '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'error', + '@typescript-eslint/no-unnecessary-condition': 'error', + '@typescript-eslint/no-unnecessary-qualifier': 'error', + '@typescript-eslint/no-unnecessary-type-arguments': 'error', + '@typescript-eslint/non-nullable-type-assertion-style': 'error', + '@typescript-eslint/prefer-includes': 'error', + '@typescript-eslint/prefer-nullish-coalescing': 'error', + '@typescript-eslint/prefer-readonly': 'error', + '@typescript-eslint/prefer-readonly-parameter-types': 'error', + '@typescript-eslint/prefer-reduce-type-parameter': 'error', + '@typescript-eslint/prefer-string-starts-ends-with': 'error', + '@typescript-eslint/promise-function-async': 'error', + '@typescript-eslint/require-array-sort-compare': 'error', + '@typescript-eslint/strict-boolean-expressions': 'error', + '@typescript-eslint/switch-exhaustiveness-check': 'error' } }; diff --git a/typescript.js b/typescript.js index fe9e25f..3bf6128 100644 --- a/typescript.js +++ b/typescript.js @@ -1,20 +1,29 @@ module.exports = { extends: [ - /* - @typescript-eslint/recommended source: - https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/src/configs/recommended.ts - - import/typescript source: - https://github.com/benmosher/eslint-plugin-import/blob/master/config/typescript.js - */ './index', 'plugin:@typescript-eslint/recommended', 'plugin:import/typescript', - './lib/typescript-base', './lib/typescript-extensions' ], parser: '@typescript-eslint/parser', rules: { + /* + Overrides to import rules (already handled by the TypeScript compiler) + https://github.com/benmosher/eslint-plugin-import/blob/master/config/typescript.js + */ + 'import/named': 'off', + 'import/no-unresolved': 'off', + + /* + Overrides to TypeScript recommended rules + https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/src/configs/recommended.ts + */ + + // None + + /* + TypeScript rules outside of the recommended configuration + */ '@typescript-eslint/array-type': 'error', '@typescript-eslint/ban-tslint-comment': 'error', '@typescript-eslint/class-literal-property-style': 'error', From 66a88e7b052f871792ebad5946be3a54f145c8fb Mon Sep 17 00:00:00 2001 From: rajsite Date: Wed, 20 Jan 2021 12:24:22 -0600 Subject: [PATCH 05/10] Correct lockfile to @typescript-eslint 4.9.1 --- lib/typescript-extensions.js | 5 +- package-lock.json | 113 ++++---------------------- package.json | 4 +- typescript-requiring-type-checking.js | 3 +- typescript.js | 3 +- 5 files changed, 23 insertions(+), 105 deletions(-) diff --git a/lib/typescript-extensions.js b/lib/typescript-extensions.js index 58cb337..09613ca 100644 --- a/lib/typescript-extensions.js +++ b/lib/typescript-extensions.js @@ -141,9 +141,10 @@ module.exports = { 'no-useless-constructor': 'off', '@typescript-eslint/no-useless-constructor': 'error', + // Available in newer typescript-eslint version // Defined by Airbnb - 'object-curly-spacing': 'off', - '@typescript-eslint/object-curly-spacing': ['error', 'always'], + // 'object-curly-spacing': 'off', + // '@typescript-eslint/object-curly-spacing': ['error', 'always'], // Defined by Airbnb quotes: 'off', diff --git a/package-lock.json b/package-lock.json index d3bc3ee..a3d8297 100644 --- a/package-lock.json +++ b/package-lock.json @@ -73,9 +73,9 @@ "dev": true }, "@types/json-schema": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", - "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", + "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", "dev": true }, "@types/json5": { @@ -85,117 +85,32 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.14.0.tgz", - "integrity": "sha512-IJ5e2W7uFNfg4qh9eHkHRUCbgZ8VKtGwD07kannJvM5t/GU8P8+24NX8gi3Hf5jST5oWPY8kyV1s/WtfiZ4+Ww==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.9.1.tgz", + "integrity": "sha512-QRLDSvIPeI1pz5tVuurD+cStNR4sle4avtHhxA+2uyixWGFjKzJ+EaFVRW6dA/jOgjV5DTAjOxboQkRDE8cRlQ==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "4.14.0", - "@typescript-eslint/scope-manager": "4.14.0", + "@typescript-eslint/experimental-utils": "4.9.1", + "@typescript-eslint/scope-manager": "4.9.1", "debug": "^4.1.1", "functional-red-black-tree": "^1.0.1", - "lodash": "^4.17.15", "regexpp": "^3.0.0", "semver": "^7.3.2", "tsutils": "^3.17.1" - }, - "dependencies": { - "@typescript-eslint/scope-manager": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.14.0.tgz", - "integrity": "sha512-/J+LlRMdbPh4RdL4hfP1eCwHN5bAhFAGOTsvE6SxsrM/47XQiPSgF5MDgLyp/i9kbZV9Lx80DW0OpPkzL+uf8Q==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.14.0", - "@typescript-eslint/visitor-keys": "4.14.0" - } - }, - "@typescript-eslint/types": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.14.0.tgz", - "integrity": "sha512-VsQE4VvpldHrTFuVPY1ZnHn/Txw6cZGjL48e+iBxTi2ksa9DmebKjAeFmTVAYoSkTk7gjA7UqJ7pIsyifTsI4A==", - "dev": true - }, - "@typescript-eslint/visitor-keys": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.14.0.tgz", - "integrity": "sha512-MeHHzUyRI50DuiPgV9+LxcM52FCJFYjJiWHtXlbyC27b80mfOwKeiKI+MHOTEpcpfmoPFm/vvQS88bYIx6PZTA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.14.0", - "eslint-visitor-keys": "^2.0.0" - } - }, - "eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", - "dev": true - } } }, "@typescript-eslint/experimental-utils": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.14.0.tgz", - "integrity": "sha512-6i6eAoiPlXMKRbXzvoQD5Yn9L7k9ezzGRvzC/x1V3650rUk3c3AOjQyGYyF9BDxQQDK2ElmKOZRD0CbtdkMzQQ==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.9.1.tgz", + "integrity": "sha512-c3k/xJqk0exLFs+cWSJxIjqLYwdHCuLWhnpnikmPQD2+NGAx9KjLYlBDcSI81EArh9FDYSL6dslAUSwILeWOxg==", "dev": true, "requires": { "@types/json-schema": "^7.0.3", - "@typescript-eslint/scope-manager": "4.14.0", - "@typescript-eslint/types": "4.14.0", - "@typescript-eslint/typescript-estree": "4.14.0", + "@typescript-eslint/scope-manager": "4.9.1", + "@typescript-eslint/types": "4.9.1", + "@typescript-eslint/typescript-estree": "4.9.1", "eslint-scope": "^5.0.0", "eslint-utils": "^2.0.0" - }, - "dependencies": { - "@typescript-eslint/scope-manager": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.14.0.tgz", - "integrity": "sha512-/J+LlRMdbPh4RdL4hfP1eCwHN5bAhFAGOTsvE6SxsrM/47XQiPSgF5MDgLyp/i9kbZV9Lx80DW0OpPkzL+uf8Q==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.14.0", - "@typescript-eslint/visitor-keys": "4.14.0" - } - }, - "@typescript-eslint/types": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.14.0.tgz", - "integrity": "sha512-VsQE4VvpldHrTFuVPY1ZnHn/Txw6cZGjL48e+iBxTi2ksa9DmebKjAeFmTVAYoSkTk7gjA7UqJ7pIsyifTsI4A==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.14.0.tgz", - "integrity": "sha512-wRjZ5qLao+bvS2F7pX4qi2oLcOONIB+ru8RGBieDptq/SudYwshveORwCVU4/yMAd4GK7Fsf8Uq1tjV838erag==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.14.0", - "@typescript-eslint/visitor-keys": "4.14.0", - "debug": "^4.1.1", - "globby": "^11.0.1", - "is-glob": "^4.0.1", - "lodash": "^4.17.15", - "semver": "^7.3.2", - "tsutils": "^3.17.1" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.14.0.tgz", - "integrity": "sha512-MeHHzUyRI50DuiPgV9+LxcM52FCJFYjJiWHtXlbyC27b80mfOwKeiKI+MHOTEpcpfmoPFm/vvQS88bYIx6PZTA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.14.0", - "eslint-visitor-keys": "^2.0.0" - } - }, - "eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", - "dev": true - } } }, "@typescript-eslint/parser": { diff --git a/package.json b/package.json index 6300886..fe18538 100644 --- a/package.json +++ b/package.json @@ -29,8 +29,8 @@ "access": "public" }, "files": [ - ".eslintrc.js", - "typescript.js" + "/*.js", + "/lib/*" ], "dependencies": { "@typescript-eslint/parser": "^4.9.1", diff --git a/typescript-requiring-type-checking.js b/typescript-requiring-type-checking.js index 69490e0..aad26d7 100644 --- a/typescript-requiring-type-checking.js +++ b/typescript-requiring-type-checking.js @@ -22,7 +22,8 @@ module.exports = { '@typescript-eslint/no-unnecessary-condition': 'error', '@typescript-eslint/no-unnecessary-qualifier': 'error', '@typescript-eslint/no-unnecessary-type-arguments': 'error', - '@typescript-eslint/non-nullable-type-assertion-style': 'error', + // Available in newer typescript-eslint version + // '@typescript-eslint/non-nullable-type-assertion-style': 'error', '@typescript-eslint/prefer-includes': 'error', '@typescript-eslint/prefer-nullish-coalescing': 'error', '@typescript-eslint/prefer-readonly': 'error', diff --git a/typescript.js b/typescript.js index 3bf6128..c29922b 100644 --- a/typescript.js +++ b/typescript.js @@ -52,7 +52,8 @@ module.exports = { '@typescript-eslint/prefer-literal-enum-member': 'error', '@typescript-eslint/prefer-optional-chain': 'error', '@typescript-eslint/prefer-ts-expect-error': 'error', - '@typescript-eslint/sort-type-union-intersection-members': 'error', + // Available in newer typescript-eslint version + // '@typescript-eslint/sort-type-union-intersection-members': 'error', '@typescript-eslint/type-annotation-spacing': 'error', '@typescript-eslint/typedef': 'error', '@typescript-eslint/unified-signatures': 'error' From 33897ed15befbe527180de408710e352d2edd590 Mon Sep 17 00:00:00 2001 From: rajsite Date: Wed, 20 Jan 2021 12:35:46 -0600 Subject: [PATCH 06/10] Disable typescript requiring type checking rules --- ...ript-extensions-requiring-type-checking.js | 21 ++++++------ typescript-requiring-type-checking.js | 34 +++++++++---------- 2 files changed, 27 insertions(+), 28 deletions(-) diff --git a/lib/typescript-extensions-requiring-type-checking.js b/lib/typescript-extensions-requiring-type-checking.js index 06b545d..7d449aa 100644 --- a/lib/typescript-extensions-requiring-type-checking.js +++ b/lib/typescript-extensions-requiring-type-checking.js @@ -1,24 +1,23 @@ module.exports = { rules: { // Defined by Airbnb - 'dot-notation': 'off', - '@typescript-eslint/dot-notation': ['error', { allowKeywords: true }], + // 'dot-notation': 'off', + // '@typescript-eslint/dot-notation': ['error', { allowKeywords: true }], // Defined by Airbnb - 'no-implied-eval': 'off', - '@typescript-eslint/no-implied-eval': 'error', + // 'no-implied-eval': 'off', + // '@typescript-eslint/no-implied-eval': 'error', // Defined by Airbnb - 'no-throw-literal': 'off', - '@typescript-eslint/no-throw-literal': 'error', + // 'no-throw-literal': 'off', + // '@typescript-eslint/no-throw-literal': 'error', // Defined by Airbnb - 'require-await': 'off', - '@typescript-eslint/require-await': 'off', - // TODO: the reasoning here is dubious... I think we should enable the rule https://github.com/airbnb/javascript/issues/2013 + // 'require-await': 'off', + // '@typescript-eslint/require-await': 'off', // Defined by Airbnb - 'no-return-await': 'off', - '@typescript-eslint/return-await': 'error', + // 'no-return-await': 'off', + // '@typescript-eslint/return-await': 'error', } }; diff --git a/typescript-requiring-type-checking.js b/typescript-requiring-type-checking.js index aad26d7..da1b8c0 100644 --- a/typescript-requiring-type-checking.js +++ b/typescript-requiring-type-checking.js @@ -15,24 +15,24 @@ module.exports = { /* TypeScript rules outside of the recommended configuration */ - '@typescript-eslint/naming-convention': 'error', - '@typescript-eslint/no-base-to-string': 'error', - '@typescript-eslint/no-confusing-void-expression': 'error', - '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'error', - '@typescript-eslint/no-unnecessary-condition': 'error', - '@typescript-eslint/no-unnecessary-qualifier': 'error', - '@typescript-eslint/no-unnecessary-type-arguments': 'error', + // '@typescript-eslint/naming-convention': 'error', + // '@typescript-eslint/no-base-to-string': 'error', + // '@typescript-eslint/no-confusing-void-expression': 'error', + // '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'error', + // '@typescript-eslint/no-unnecessary-condition': 'error', + // '@typescript-eslint/no-unnecessary-qualifier': 'error', + // '@typescript-eslint/no-unnecessary-type-arguments': 'error', // Available in newer typescript-eslint version // '@typescript-eslint/non-nullable-type-assertion-style': 'error', - '@typescript-eslint/prefer-includes': 'error', - '@typescript-eslint/prefer-nullish-coalescing': 'error', - '@typescript-eslint/prefer-readonly': 'error', - '@typescript-eslint/prefer-readonly-parameter-types': 'error', - '@typescript-eslint/prefer-reduce-type-parameter': 'error', - '@typescript-eslint/prefer-string-starts-ends-with': 'error', - '@typescript-eslint/promise-function-async': 'error', - '@typescript-eslint/require-array-sort-compare': 'error', - '@typescript-eslint/strict-boolean-expressions': 'error', - '@typescript-eslint/switch-exhaustiveness-check': 'error' + // '@typescript-eslint/prefer-includes': 'error', + // '@typescript-eslint/prefer-nullish-coalescing': 'error', + // '@typescript-eslint/prefer-readonly': 'error', + // '@typescript-eslint/prefer-readonly-parameter-types': 'error', + // '@typescript-eslint/prefer-reduce-type-parameter': 'error', + // '@typescript-eslint/prefer-string-starts-ends-with': 'error', + // '@typescript-eslint/promise-function-async': 'error', + // '@typescript-eslint/require-array-sort-compare': 'error', + // '@typescript-eslint/strict-boolean-expressions': 'error', + // '@typescript-eslint/switch-exhaustiveness-check': 'error' } }; From fbddf5496f0bc65822ed066dd88f6428559add04 Mon Sep 17 00:00:00 2001 From: rajsite Date: Wed, 20 Jan 2021 13:47:06 -0600 Subject: [PATCH 07/10] Lock build Ubuntu version and save artifact --- .github/workflows/build.yml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e5a03a8..84b8e1d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,19 +8,31 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 + + # Install dependencies - uses: actions/setup-node@v1 with: node-version: '12' registry-url: 'https://registry.npmjs.org' - - run: npm ci + + # Build + - run: npm pack + - uses: actions/upload-artifact@v2 + with: + name: npm package + path: ./vireo-*.tgz + if-no-files-found: error + + # Test - run: npm run test-typescript - run: npm run test-typescript-typed - run: npm run dev-print-typescript-props + # Publish - if: startsWith(github.ref, 'refs/tags/v') run: npm publish env: From df0fb57e22676231ca5b479922ced2fb29fe38d1 Mon Sep 17 00:00:00 2001 From: rajsite Date: Wed, 20 Jan 2021 13:48:54 -0600 Subject: [PATCH 08/10] Update published artifact path --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 84b8e1d..4bdb732 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,7 +24,7 @@ jobs: - uses: actions/upload-artifact@v2 with: name: npm package - path: ./vireo-*.tgz + path: ./ni-eslint-config-*.tgz if-no-files-found: error # Test From edd86f4b89b20357a752b54028e28cd46e54e190 Mon Sep 17 00:00:00 2001 From: rajsite Date: Wed, 10 Mar 2021 22:17:00 -0600 Subject: [PATCH 09/10] Resolved TypeScript rules --- index.js | 8 +- lib/typescript-extensions.js | 9 ++- typescript.js | 147 +++++++++++++++++++++++++++++++---- 3 files changed, 146 insertions(+), 18 deletions(-) diff --git a/index.js b/index.js index 0155847..160c98d 100644 --- a/index.js +++ b/index.js @@ -63,11 +63,17 @@ module.exports = { */ 'linebreak-style': 'off', + /* + Requires empty lines between multiline class members but avoids the empty line + for single line members to reduce the amount of vertical space used in a class. + */ + 'lines-between-class-members': ['error', 'always', { exceptAfterSingleLine: true }], + /* Including one class per file is a best practice in general and also recommended by the Angular style guide. However, migrating older projects may not be trivial, and there may be exceptions for public/internal types that are only used as part of the interface - to the main type and no other types + to the main type and no other types. */ 'max-classes-per-file': ['error', 1], diff --git a/lib/typescript-extensions.js b/lib/typescript-extensions.js index 09613ca..ac170c7 100644 --- a/lib/typescript-extensions.js +++ b/lib/typescript-extensions.js @@ -52,9 +52,9 @@ module.exports = { } }], - // Defined by Airbnb + // Defined by NI 'lines-between-class-members': 'off', - '@typescript-eslint/lines-between-class-members': ['error', 'always', { exceptAfterSingleLine: false }], + '@typescript-eslint/lines-between-class-members': ['error', 'always', { exceptAfterSingleLine: true }], // Defined by Airbnb 'no-array-constructor': 'off', @@ -141,9 +141,10 @@ module.exports = { 'no-useless-constructor': 'off', '@typescript-eslint/no-useless-constructor': 'error', - // Available in newer typescript-eslint version + // Available in newer typescript-eslint version, enable on upgrade. + // See: https://github.com/ni/javascript-styleguide/pull/18#discussion_r569795937 // Defined by Airbnb - // 'object-curly-spacing': 'off', + 'object-curly-spacing': 'off', // '@typescript-eslint/object-curly-spacing': ['error', 'always'], // Defined by Airbnb diff --git a/typescript.js b/typescript.js index c29922b..cc48677 100644 --- a/typescript.js +++ b/typescript.js @@ -24,38 +24,159 @@ module.exports = { /* TypeScript rules outside of the recommended configuration */ + + /* + Prefer the array straight bracket syntax over generics in all cases. + */ '@typescript-eslint/array-type': 'error', + '@typescript-eslint/ban-tslint-comment': 'error', + '@typescript-eslint/class-literal-property-style': 'error', + '@typescript-eslint/consistent-indexed-object-style': 'error', + '@typescript-eslint/consistent-type-assertions': 'error', + '@typescript-eslint/consistent-type-definitions': 'error', - '@typescript-eslint/consistent-type-imports': 'error', + + /* + Type imports are useful for uncommon use cases such as modules with + side-efects and file-by-file transpiling. Usage will be determined + on a case-by-case basis. + */ + '@typescript-eslint/consistent-type-imports': 'off', + '@typescript-eslint/explicit-function-return-type': 'error', + + /* + Requiring an accessibility modifier helps when creating classes to ensure the + accessibility of a class member is intentionally decided and not relying on + the default of public accessibility. + */ '@typescript-eslint/explicit-member-accessibility': 'error', - '@typescript-eslint/member-delimiter-style': 'error', - '@typescript-eslint/member-ordering': 'error', - '@typescript-eslint/method-signature-style': 'error', + + /* + All interface members should be terminated with a semicolon including single line + definitions, consistent with classes. Object literal types should use commas + consistent with object literals. + */ + '@typescript-eslint/member-delimiter-style': ['error', { + overrides: { + interface: { + singleline: { + delimiter: 'semi', + requireLast: true + } + }, + typeLiteral: { + multiline: { + delimiter: 'comma', + requireLast: false + }, + singleline: { + delimiter: 'comma', + requireLast: false + } + } + } + }], + + /* + Group members by fields and methods and then order them by accessibility starting + with statics. Order members within these groups in a logical organization where + readability is the most important thing. Private fields that back public accessors + may be grouped with their accessor by disabling the rule with a comment. + */ + '@typescript-eslint/member-ordering': ['error', { + default: [ + 'signature', + 'public-static-field', + 'protected-static-field', + 'private-static-field', + 'static-field', + 'public-field', + 'protected-field', + 'private-field', + 'field', + 'public-constructor', + 'protected-constructor', + 'private-constructor', + 'constructor', + 'public-static-method', + 'protected-static-method', + 'private-static-method', + 'static-method', + 'public-method', + 'protected-method', + 'private-method', + 'method' + ] + }], + + /* + The stricter type checking that's possible by using property style when declaring + method signatures only has benefits in some cases of inheritance. Therefore we + prefer to align the style between interface and class definitions and to align + with other languages used by NI like C#. + */ + '@typescript-eslint/method-signature-style': 'off', + '@typescript-eslint/no-confusing-non-null-assertion': 'error', + '@typescript-eslint/no-dynamic-delete': 'error', - '@typescript-eslint/no-extraneous-class': 'error', - '@typescript-eslint/no-implicit-any-catch': 'error', + + '@typescript-eslint/no-extraneous-class': ['error', { allowWithDecorator: true, allowStaticOnly: true }], + + /* + TypeScript only supports catching `any` and `unknown` so this rule does not add + much value. With the rule no-throw-literal we enforce throwing `Error` objects, + which should be sufficient most of the time. For more type safety, consider + using type guards. + */ + '@typescript-eslint/no-implicit-any-catch': 'off', + '@typescript-eslint/no-invalid-void-type': 'error', - '@typescript-eslint/no-parameter-properties': 'error', + + /* + Parameter properties are a nice shorthand for defining properties ingested + in the constructor. + */ + '@typescript-eslint/no-parameter-properties': 'off', + '@typescript-eslint/no-require-imports': 'error', - '@typescript-eslint/no-type-alias': 'error', + + '@typescript-eslint/no-type-alias': 'off', + '@typescript-eslint/no-unnecessary-type-constraint': 'error', - '@typescript-eslint/no-unused-vars-experimental': 'error', - '@typescript-eslint/prefer-enum-initializers': 'error', + + '@typescript-eslint/no-unused-vars-experimental': 'off', + + /* + If an enum is crossing a code boundary (being serialized to JSON for example) then + you should initialize its values. But in most other cases code shouldn't care about + the values of enum items, so you can safely leave them implicit. + */ + '@typescript-eslint/prefer-enum-initializers': 'off', + '@typescript-eslint/prefer-for-of': 'error', + '@typescript-eslint/prefer-function-type': 'error', + '@typescript-eslint/prefer-literal-enum-member': 'error', + '@typescript-eslint/prefer-optional-chain': 'error', + '@typescript-eslint/prefer-ts-expect-error': 'error', - // Available in newer typescript-eslint version - // '@typescript-eslint/sort-type-union-intersection-members': 'error', + + // Available in newer typescript-eslint version, disable on upgrade. + // See: https://github.com/ni/javascript-styleguide/pull/18#discussion_r575487604 + // '@typescript-eslint/sort-type-union-intersection-members': 'off', + '@typescript-eslint/type-annotation-spacing': 'error', - '@typescript-eslint/typedef': 'error', + + '@typescript-eslint/typedef': 'off', + '@typescript-eslint/unified-signatures': 'error' } }; From 94d85c3c480cb3da173854d2e7c84b707528e157 Mon Sep 17 00:00:00 2001 From: rajsite Date: Thu, 11 Mar 2021 12:40:59 -0600 Subject: [PATCH 10/10] Resolved remaining TypeScript rules --- index.js | 3 +++ typescript.js | 12 +++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 160c98d..b8107a4 100644 --- a/index.js +++ b/index.js @@ -144,6 +144,9 @@ module.exports = { 'error', { selector: 'LabeledStatement', message: 'Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand.', + }, { + selector: "UnaryExpression[operator='delete']", + message: 'The `delete` operator is not allowed. If using an object keys as a map, use the ES `Map` data structure instead.' }, { selector: 'WithStatement', message: '`with` is disallowed in strict mode because it makes code impossible to predict and optimize.', diff --git a/typescript.js b/typescript.js index cc48677..784f6ef 100644 --- a/typescript.js +++ b/typescript.js @@ -34,11 +34,14 @@ module.exports = { '@typescript-eslint/class-literal-property-style': 'error', - '@typescript-eslint/consistent-indexed-object-style': 'error', + /* + Prefer the index signature syntax over the builtin `Record` type in all cases. + */ + '@typescript-eslint/consistent-indexed-object-style': ['error', 'index-signature'], '@typescript-eslint/consistent-type-assertions': 'error', - '@typescript-eslint/consistent-type-definitions': 'error', + '@typescript-eslint/consistent-type-definitions': ['error', 'interface'], /* Type imports are useful for uncommon use cases such as modules with @@ -124,7 +127,10 @@ module.exports = { '@typescript-eslint/no-confusing-non-null-assertion': 'error', - '@typescript-eslint/no-dynamic-delete': 'error', + /* + This rule is unnecessary because delete is banned via 'no-restricted-syntax' + */ + '@typescript-eslint/no-dynamic-delete': 'off', '@typescript-eslint/no-extraneous-class': ['error', { allowWithDecorator: true, allowStaticOnly: true }],