From 03d0b84a5bf0d69d66155f93b2c1049afa260151 Mon Sep 17 00:00:00 2001 From: Teppei Sato Date: Thu, 28 Sep 2023 01:14:27 +0900 Subject: [PATCH] feat!: flat config (#1055) --- +mocha.js | 16 -- +module.js | 29 --- +node.js | 8 - +prettier.js | 5 - +typescript-with-type.js | 35 ---- +typescript.js | 71 ------- .eslintignore | 1 - .eslintrc.js | 6 - .gitignore | 1 + .prettierignore | 4 +- .prettierrc.closure.json | 3 +- .prettierrc.json | 4 - .vscode/settings.json | 9 + README.md | 168 +++++++--------- bin/cli.js | 5 + es2015.js | 5 - es2016.js | 5 - es2017.js | 5 - es2018.js | 11 -- es2019.js | 12 -- es2020.js | 13 -- es2021.js | 14 -- es2022.js | 15 -- es2023.js | 16 -- es5.js | 3 - eslint.config.js | 15 ++ examples/.eslintrc.json | 3 - examples/browser/.eslintrc.json | 3 - examples/browser/eslint.config.js | 17 ++ examples/browser/index.js | 3 - examples/browser/index.mjs | 7 + examples/es2015/.eslintrc.json | 3 - examples/es2015/index.js | 96 ---------- examples/es2017/.eslintrc.json | 3 - examples/es2017/index.js | 44 ----- examples/es2021/.eslintrc.json | 3 - examples/es2021/eslint.config.js | 11 ++ examples/es2021/index.js | 12 -- examples/es2021/index.mjs | 28 +++ examples/es2022/eslint.config.js | 11 ++ examples/es2022/index.mjs | 16 ++ examples/es2023/eslint.config.js | 11 ++ examples/es2023/index.mjs | 9 + examples/es5/.eslintrc.json | 3 - examples/es5/index.js | 78 -------- examples/lint-all.sh | 32 ++++ examples/mocha/.eslintrc.json | 3 - examples/mocha/eslint.config.js | 21 ++ examples/mocha/index.js | 4 + examples/mocha/test/index.js | 10 +- examples/node-es2017/.eslintrc.json | 3 - examples/node-es2017/index.js | 15 -- examples/node-module/.eslintrc.json | 3 - examples/node-module/cjs.cjs | 7 - examples/node-module/esm.mjs | 22 --- examples/node-module/index.js | 22 --- examples/node-v18/.eslintrc.json | 3 - examples/node-v20/.eslintrc.json | 3 - examples/node-v20/index.js | 3 - examples/node18/cjs.js | 4 + examples/node18/eslint.config.js | 11 ++ .../{node-v18/index.js => node18/esm.mjs} | 16 +- examples/node20/cjs.js | 4 + examples/node20/eslint.config.js | 11 ++ examples/node20/esm.mjs | 25 +++ examples/typescript-esm/cjs.cts | 38 ++++ examples/typescript-esm/eslint.config.js | 11 ++ examples/typescript-esm/esm.mts | 41 ++++ examples/typescript-esm/index.ts | 41 ++++ examples/typescript-esm/jsx.tsx | 44 +++++ .../mod2.ts => typescript-esm/mod-cjs.cts} | 2 +- examples/typescript-esm/mod-esm.mts | 5 + examples/typescript-esm/package.json | 3 + examples/typescript-esm/tsconfig.json | 16 ++ examples/typescript-module/.eslintrc.json | 4 - examples/typescript-module/cjs.cts | 14 -- examples/typescript-module/esm.mts | 21 -- examples/typescript-module/index.ts | 21 -- examples/typescript-module/jsx.tsx | 21 -- examples/typescript-module/tsconfig.json | 12 -- .../typescript-type-checked/eslint.config.js | 15 ++ examples/typescript-type-checked/index.ts | 5 + .../typescript-type-checked/tsconfig.json | 15 ++ examples/typescript-with-type/.eslintrc.json | 4 - examples/typescript-with-type/index.ts | 3 - examples/typescript-with-type/tsconfig.json | 62 ------ examples/typescript/.eslintrc.json | 4 - examples/typescript/cjs.ts | 45 +++++ examples/typescript/eslint.config.js | 3 + examples/typescript/eslint.config.mjs | 11 ++ examples/typescript/esm.mts | 17 -- examples/typescript/index.ts | 23 --- examples/typescript/mod.ts | 2 +- .../package.json | 2 +- examples/typescript/tsconfig.json | 15 ++ lib/es2015.js | 52 ----- lib/es2016.js | 13 -- lib/es2017.js | 28 --- lib/es2018.js | 15 -- lib/es2019.js | 15 -- lib/es2020.js | 14 -- lib/es2021-numeric-separators.js | 13 -- lib/es2021.js | 14 -- lib/es2022-class-fields.js | 7 - lib/es2022.js | 15 -- lib/es2023.js | 13 -- lib/module-globals.js | 13 -- lib/node.js | 41 ---- lib/ts-extensions.js | 4 - node-v18.js | 27 --- node-v20.js | 26 --- package-lock.json | 75 +++++++- package.json | 37 +++- src/build.ts | 88 +++++++++ src/cli.ts | 59 ++++++ lib/base.js => src/configs/base.ts | 181 +++++++++--------- +browser.js => src/configs/browser.ts | 14 +- src/configs/es2021.ts | 23 +++ src/configs/es2022.ts | 16 ++ src/configs/es2023.ts | 13 ++ lib/module-js.js => src/configs/js-esm.ts | 28 +-- src/configs/mocha.ts | 15 ++ .../configs/module-base.ts | 28 +-- src/configs/node-esm.ts | 15 ++ src/configs/node.ts | 52 +++++ src/configs/node18.ts | 6 + src/configs/node20.ts | 6 + src/configs/typescript-type-checked.ts | 32 ++++ src/configs/typescript.ts | 77 ++++++++ src/index.ts | 11 ++ src/merge.ts | 52 +++++ src/types/@eslint/js.d.ts | 1 + src/types/eslint-plugin-eslint-comments.d.ts | 1 + src/types/eslint-plugin-import.d.ts | 1 + src/types/eslint-plugin-jsdoc.d.ts | 1 + src/types/eslint-plugin-n.d.ts | 1 + src/types/eslint-plugin-unicorn.d.ts | 1 + src/utils.ts | 9 + templates/eslint.config-esm.mjs | 8 + templates/eslint.config.cjs | 3 + templates/eslint.config.mjs | 8 + test/configs.mjs | 167 ++++++++++++++++ test/fixtures/.prettier.eslintrc.json | 4 - .../.typescript-with-type.eslintrc.json | 4 - test/fixtures/.typescript.eslintrc.json | 4 - test/fixtures/es2015.no-const-assign.fail.js | 4 - ...15.padding-line-between-statements.pass.js | 4 - test/fixtures/es2015.quotes.pass.js | 1 - test/fixtures/es2017.no-return-await.fail.js | 3 - .../es2017.unicorn#prefer-includes.fail.js | 1 - .../es2018.prefer-object-spread.fail.js | 2 - ...icorn#prefer-string-trim-start-end.fail.js | 1 - ...lint-comments#no-duplicate-disable.fail.js | 3 + ...n.fail.js => es2021.getter-return.fail.js} | 3 +- ...s => es2021.jsdoc#check-tag-names.pass.js} | 0 test/fixtures/es2021.no-alert.pass.js | 2 + ...021.no-misleading-character-class.fail.js} | 0 ...ass.js => es2021.object-shorthand.pass.js} | 2 +- ...js => es2021.prefer-destructuring.fail.js} | 0 .../es2021.prefer-object-spread.fail.js | 2 + .../es2021.unicorn#no-hex-escape.fail.js | 1 + ...rn#prefer-string-starts-ends-with.fail.js} | 0 .../es2022.prefer-object-has-own.fail.js | 3 + .../es2023.prefer-object-has-own.fail.js | 1 + test/fixtures/es5.comma-style.fail.js | 6 - ....eslint-comments#no-unused-disable.fail.js | 3 - test/fixtures/es5.indent.pass.js | 7 - test/fixtures/es5.multiline-ternary.pass.js | 3 - test/fixtures/es5.no-alert.pass.js | 3 - test/fixtures/es5.no-empty.pass.js | 5 - .../es5.no-inner-declarations.fail.js | 3 - test/fixtures/es5.no-self-assign.fail.js | 1 - test/fixtures/es5.no-tabs.fail.js | 3 - .../fixtures/es5.object-curly-newline.fail.js | 8 - .../fixtures/es5.object-curly-newline.pass.js | 22 --- ...s5.padding-line-between-statements.pass.js | 6 - test/fixtures/es5.quote-props.pass.js | 7 - test/fixtures/es5.quotes.fail.js | 3 - test/fixtures/es5.quotes.pass.js | 4 - test/fixtures/es5.spaced-comment.fail.js | 3 - test/fixtures/es5.spaced-comment.pass.js | 3 - .../fixtures/es5.switch-colon-spacing.pass.js | 11 -- .../es5.unicorn#no-hex-escape.fail.js | 1 - test/fixtures/modules/default-export.ts | 1 + .../prettier.prettier#prettier%curly.fail.js | 4 - .../prettier.prettier#prettier%curly.pass.js | 21 -- ...tier.prettier#prettier%singleQuote.fail.js | 1 - test/fixtures/typescript-import-target.ts | 2 - ...int#no-unnecessary-type-assertion.fail.ts} | 0 .../typescript-type-checked.tsconfig.json | 15 ++ ...pt-eslint#no-duplicate-enum-values.fail.ts | 6 + ...pt.@typescript-eslint#no-namespace.fail.ts | 3 +- ...lint#no-unnecessary-type-assertion.pass.ts | 7 +- test/fixtures/typescript.import#first.fail.ts | 4 +- ...pt.import#no-useless-path-segments.fail.ts | 1 - .../typescript.jsdoc#check-tag-names.pass.ts | 9 - test/fixtures/typescript.no-dupe-keys.fail.js | 5 - test/fixtures/typescript.no-dupe-keys.pass.ts | 3 +- test/fixtures/typescript.node#shebang.fail.ts | 3 - test/index.js | 50 ----- test/lib/generateTest.js | 48 ----- tsconfig.json | 71 ++----- 202 files changed, 1604 insertions(+), 1718 deletions(-) delete mode 100644 +mocha.js delete mode 100644 +module.js delete mode 100644 +node.js delete mode 100644 +prettier.js delete mode 100644 +typescript-with-type.js delete mode 100644 +typescript.js delete mode 100644 .eslintignore delete mode 100644 .eslintrc.js delete mode 100644 .prettierrc.json create mode 100644 .vscode/settings.json create mode 100755 bin/cli.js delete mode 100644 es2015.js delete mode 100644 es2016.js delete mode 100644 es2017.js delete mode 100644 es2018.js delete mode 100644 es2019.js delete mode 100644 es2020.js delete mode 100644 es2021.js delete mode 100644 es2022.js delete mode 100644 es2023.js delete mode 100644 es5.js create mode 100644 eslint.config.js delete mode 100644 examples/.eslintrc.json delete mode 100644 examples/browser/.eslintrc.json create mode 100644 examples/browser/eslint.config.js delete mode 100644 examples/browser/index.js create mode 100644 examples/browser/index.mjs delete mode 100644 examples/es2015/.eslintrc.json delete mode 100644 examples/es2015/index.js delete mode 100644 examples/es2017/.eslintrc.json delete mode 100644 examples/es2017/index.js delete mode 100644 examples/es2021/.eslintrc.json create mode 100644 examples/es2021/eslint.config.js delete mode 100644 examples/es2021/index.js create mode 100644 examples/es2021/index.mjs create mode 100644 examples/es2022/eslint.config.js create mode 100644 examples/es2022/index.mjs create mode 100644 examples/es2023/eslint.config.js create mode 100644 examples/es2023/index.mjs delete mode 100644 examples/es5/.eslintrc.json delete mode 100644 examples/es5/index.js create mode 100755 examples/lint-all.sh delete mode 100644 examples/mocha/.eslintrc.json create mode 100644 examples/mocha/eslint.config.js create mode 100644 examples/mocha/index.js delete mode 100644 examples/node-es2017/.eslintrc.json delete mode 100644 examples/node-es2017/index.js delete mode 100644 examples/node-module/.eslintrc.json delete mode 100644 examples/node-module/cjs.cjs delete mode 100644 examples/node-module/esm.mjs delete mode 100644 examples/node-module/index.js delete mode 100644 examples/node-v18/.eslintrc.json delete mode 100644 examples/node-v20/.eslintrc.json delete mode 100644 examples/node-v20/index.js create mode 100644 examples/node18/cjs.js create mode 100644 examples/node18/eslint.config.js rename examples/{node-v18/index.js => node18/esm.mjs} (58%) create mode 100644 examples/node20/cjs.js create mode 100644 examples/node20/eslint.config.js create mode 100644 examples/node20/esm.mjs create mode 100644 examples/typescript-esm/cjs.cts create mode 100644 examples/typescript-esm/eslint.config.js create mode 100644 examples/typescript-esm/esm.mts create mode 100644 examples/typescript-esm/index.ts create mode 100644 examples/typescript-esm/jsx.tsx rename examples/{typescript/mod2.ts => typescript-esm/mod-cjs.cts} (81%) create mode 100644 examples/typescript-esm/mod-esm.mts create mode 100644 examples/typescript-esm/package.json create mode 100644 examples/typescript-esm/tsconfig.json delete mode 100644 examples/typescript-module/.eslintrc.json delete mode 100644 examples/typescript-module/cjs.cts delete mode 100644 examples/typescript-module/esm.mts delete mode 100644 examples/typescript-module/index.ts delete mode 100644 examples/typescript-module/jsx.tsx delete mode 100644 examples/typescript-module/tsconfig.json create mode 100644 examples/typescript-type-checked/eslint.config.js create mode 100644 examples/typescript-type-checked/index.ts create mode 100644 examples/typescript-type-checked/tsconfig.json delete mode 100644 examples/typescript-with-type/.eslintrc.json delete mode 100644 examples/typescript-with-type/index.ts delete mode 100644 examples/typescript-with-type/tsconfig.json delete mode 100644 examples/typescript/.eslintrc.json create mode 100644 examples/typescript/cjs.ts create mode 100644 examples/typescript/eslint.config.js create mode 100644 examples/typescript/eslint.config.mjs delete mode 100644 examples/typescript/esm.mts delete mode 100644 examples/typescript/index.ts rename examples/{typescript-module => typescript}/package.json (92%) create mode 100644 examples/typescript/tsconfig.json delete mode 100644 lib/es2015.js delete mode 100644 lib/es2016.js delete mode 100644 lib/es2017.js delete mode 100644 lib/es2018.js delete mode 100644 lib/es2019.js delete mode 100644 lib/es2020.js delete mode 100644 lib/es2021-numeric-separators.js delete mode 100644 lib/es2021.js delete mode 100644 lib/es2022-class-fields.js delete mode 100644 lib/es2022.js delete mode 100644 lib/es2023.js delete mode 100644 lib/module-globals.js delete mode 100644 lib/node.js delete mode 100644 lib/ts-extensions.js delete mode 100644 node-v18.js delete mode 100644 node-v20.js create mode 100644 src/build.ts create mode 100755 src/cli.ts rename lib/base.js => src/configs/base.ts (59%) rename +browser.js => src/configs/browser.ts (52%) create mode 100644 src/configs/es2021.ts create mode 100644 src/configs/es2022.ts create mode 100644 src/configs/es2023.ts rename lib/module-js.js => src/configs/js-esm.ts (55%) create mode 100644 src/configs/mocha.ts rename lib/module-base.js => src/configs/module-base.ts (61%) create mode 100644 src/configs/node-esm.ts create mode 100644 src/configs/node.ts create mode 100644 src/configs/node18.ts create mode 100644 src/configs/node20.ts create mode 100644 src/configs/typescript-type-checked.ts create mode 100644 src/configs/typescript.ts create mode 100644 src/index.ts create mode 100644 src/merge.ts create mode 100644 src/types/@eslint/js.d.ts create mode 100644 src/types/eslint-plugin-eslint-comments.d.ts create mode 100644 src/types/eslint-plugin-import.d.ts create mode 100644 src/types/eslint-plugin-jsdoc.d.ts create mode 100644 src/types/eslint-plugin-n.d.ts create mode 100644 src/types/eslint-plugin-unicorn.d.ts create mode 100644 src/utils.ts create mode 100644 templates/eslint.config-esm.mjs create mode 100644 templates/eslint.config.cjs create mode 100644 templates/eslint.config.mjs create mode 100644 test/configs.mjs delete mode 100644 test/fixtures/.prettier.eslintrc.json delete mode 100644 test/fixtures/.typescript-with-type.eslintrc.json delete mode 100644 test/fixtures/.typescript.eslintrc.json delete mode 100644 test/fixtures/es2015.no-const-assign.fail.js delete mode 100644 test/fixtures/es2015.padding-line-between-statements.pass.js delete mode 100644 test/fixtures/es2015.quotes.pass.js delete mode 100644 test/fixtures/es2017.no-return-await.fail.js delete mode 100644 test/fixtures/es2017.unicorn#prefer-includes.fail.js delete mode 100644 test/fixtures/es2018.prefer-object-spread.fail.js delete mode 100644 test/fixtures/es2019.unicorn#prefer-string-trim-start-end.fail.js create mode 100644 test/fixtures/es2021.eslint-comments#no-duplicate-disable.fail.js rename test/fixtures/{es5.getter-return.fail.js => es2021.getter-return.fail.js} (61%) rename test/fixtures/{es5.jsdoc#check-tag-names.pass.js => es2021.jsdoc#check-tag-names.pass.js} (100%) create mode 100644 test/fixtures/es2021.no-alert.pass.js rename test/fixtures/{es2015.no-misleading-character-class.fail.js => es2021.no-misleading-character-class.fail.js} (100%) rename test/fixtures/{es2015.object-shorthand.pass.js => es2021.object-shorthand.pass.js} (92%) rename test/fixtures/{es2015.prefer-destructuring.fail.js => es2021.prefer-destructuring.fail.js} (100%) create mode 100644 test/fixtures/es2021.prefer-object-spread.fail.js create mode 100644 test/fixtures/es2021.unicorn#no-hex-escape.fail.js rename test/fixtures/{es2015.unicorn#prefer-string-starts-ends-with.fail.js => es2021.unicorn#prefer-string-starts-ends-with.fail.js} (100%) create mode 100644 test/fixtures/es2022.prefer-object-has-own.fail.js create mode 100644 test/fixtures/es2023.prefer-object-has-own.fail.js delete mode 100644 test/fixtures/es5.comma-style.fail.js delete mode 100644 test/fixtures/es5.eslint-comments#no-unused-disable.fail.js delete mode 100644 test/fixtures/es5.indent.pass.js delete mode 100644 test/fixtures/es5.multiline-ternary.pass.js delete mode 100644 test/fixtures/es5.no-alert.pass.js delete mode 100644 test/fixtures/es5.no-empty.pass.js delete mode 100644 test/fixtures/es5.no-inner-declarations.fail.js delete mode 100644 test/fixtures/es5.no-self-assign.fail.js delete mode 100644 test/fixtures/es5.no-tabs.fail.js delete mode 100644 test/fixtures/es5.object-curly-newline.fail.js delete mode 100644 test/fixtures/es5.object-curly-newline.pass.js delete mode 100644 test/fixtures/es5.padding-line-between-statements.pass.js delete mode 100644 test/fixtures/es5.quote-props.pass.js delete mode 100644 test/fixtures/es5.quotes.fail.js delete mode 100644 test/fixtures/es5.quotes.pass.js delete mode 100644 test/fixtures/es5.spaced-comment.fail.js delete mode 100644 test/fixtures/es5.spaced-comment.pass.js delete mode 100644 test/fixtures/es5.switch-colon-spacing.pass.js delete mode 100644 test/fixtures/es5.unicorn#no-hex-escape.fail.js create mode 100644 test/fixtures/modules/default-export.ts delete mode 100644 test/fixtures/prettier.prettier#prettier%curly.fail.js delete mode 100644 test/fixtures/prettier.prettier#prettier%curly.pass.js delete mode 100644 test/fixtures/prettier.prettier#prettier%singleQuote.fail.js delete mode 100644 test/fixtures/typescript-import-target.ts rename test/fixtures/{typescript-with-type.@typescript-eslint#no-unnecessary-type-assertion.fail.ts => typescript-type-checked.@typescript-eslint#no-unnecessary-type-assertion.fail.ts} (100%) create mode 100644 test/fixtures/typescript-type-checked.tsconfig.json create mode 100644 test/fixtures/typescript.@typescript-eslint#no-duplicate-enum-values.fail.ts delete mode 100644 test/fixtures/typescript.import#no-useless-path-segments.fail.ts delete mode 100644 test/fixtures/typescript.jsdoc#check-tag-names.pass.ts delete mode 100644 test/fixtures/typescript.no-dupe-keys.fail.js delete mode 100644 test/fixtures/typescript.node#shebang.fail.ts delete mode 100644 test/index.js delete mode 100644 test/lib/generateTest.js diff --git a/+mocha.js b/+mocha.js deleted file mode 100644 index 4c162fa5..00000000 --- a/+mocha.js +++ /dev/null @@ -1,16 +0,0 @@ -"use strict"; - -module.exports = { - overrides: [ - { - files: ["**/test/**/*.js", "**/test/**/*.ts"], - env: { - mocha: true, - }, - rules: { - // allow `this.timeout(1000)` - "no-invalid-this": 0, - }, - }, - ], -}; diff --git a/+module.js b/+module.js deleted file mode 100644 index 99cdaf99..00000000 --- a/+module.js +++ /dev/null @@ -1,29 +0,0 @@ -"use strict"; - -/** - * @fileoverview Additional config for type:module in package.json - */ - -const TS_EXTENSIONS = require("./lib/ts-extensions"); - -module.exports = { - settings: { - "import/extensions": [".js", ".jsx", ".mjs"], - }, - overrides: [ - { - files: ["*.js", "*.jsx"], - extends: ["./lib/module-js.js"], - settings: { - "import/extensions": [".js", ".jsx", ".mjs"], - }, - }, - { - files: ["*.ts", "*.tsx"], - extends: ["./lib/module-globals.js"], - settings: { - "import/extensions": [".js", ".jsx", ".mjs", ...TS_EXTENSIONS], - }, - }, - ], -}; diff --git a/+node.js b/+node.js deleted file mode 100644 index 5c1f2f40..00000000 --- a/+node.js +++ /dev/null @@ -1,8 +0,0 @@ -"use strict"; - -module.exports = { - extends: ["./lib/node.js"], - rules: { - "n/no-unsupported-features/es-syntax": 0, - }, -}; diff --git a/+prettier.js b/+prettier.js deleted file mode 100644 index cb77db18..00000000 --- a/+prettier.js +++ /dev/null @@ -1,5 +0,0 @@ -"use strict"; - -module.exports = { - extends: ["prettier"], -}; diff --git a/+typescript-with-type.js b/+typescript-with-type.js deleted file mode 100644 index 472eb483..00000000 --- a/+typescript-with-type.js +++ /dev/null @@ -1,35 +0,0 @@ -"use strict"; - -/** @type {import("@typescript-eslint/utils").TSESLint.Linter.Config} */ -module.exports = { - extends: ["./+typescript.js"], - overrides: [ - { - files: ["*.ts", "*.tsx"], - parserOptions: { - project: "./tsconfig.json", - }, - rules: { - // Extend ESLint rules - "no-throw-literal": "off", - "@typescript-eslint/no-throw-literal": 2, - - "@typescript-eslint/await-thenable": 2, - // TS3.8+ - "@typescript-eslint/consistent-type-exports": 2, - "@typescript-eslint/no-floating-promises": 2, - "@typescript-eslint/no-for-in-array": 2, - "@typescript-eslint/no-misused-promises": 2, - "@typescript-eslint/no-mixed-enums": 2, - "@typescript-eslint/no-unnecessary-condition": 2, - "@typescript-eslint/no-unnecessary-type-assertion": 2, - "@typescript-eslint/restrict-plus-operands": 2, - "@typescript-eslint/restrict-template-expressions": 2, - "@typescript-eslint/switch-exhaustiveness-check": 2, - // override with jest/unbound-method for testing - // https://github.com/jest-community/eslint-plugin-jest/blob/main/docs/rules/unbound-method.md - "@typescript-eslint/unbound-method": 2, - }, - }, - ], -}; diff --git a/+typescript.js b/+typescript.js deleted file mode 100644 index 75e41fcf..00000000 --- a/+typescript.js +++ /dev/null @@ -1,71 +0,0 @@ -"use strict"; - -const TS_EXTENSIONS = require("./lib/ts-extensions"); - -/** @type {import("@typescript-eslint/utils").TSESLint.Linter.Config} */ -module.exports = { - overrides: [ - { - files: TS_EXTENSIONS.map((ext) => `*${ext}`), - extends: ["plugin:@typescript-eslint/recommended", "./lib/module-base.js"], - rules: { - // ES2019+ available in TypeScript - "n/no-unsupported-features/es-syntax": 0, - - // Allow special triple slashes comment: "/// " - "spaced-comment": [2, "always", { line: { markers: ["/"] }, block: { balanced: true } }], - - // Extend ESLint rules (enabled in lib/base.js) - // NOTE: skip extending stylistic rules that are overrided by prettier - "no-extra-semi": 0, - "@typescript-eslint/no-extra-semi": 2, - "no-invalid-this": 0, - "@typescript-eslint/no-invalid-this": 2, - "no-loop-func": 0, - "@typescript-eslint/no-loop-func": 2, - "no-unused-expressions": 0, - "@typescript-eslint/no-unused-expressions": [ - 2, - { allowShortCircuit: true, allowTernary: true, allowTaggedTemplates: true }, - ], - - // Override recommended rules - "@typescript-eslint/no-explicit-any": 0, - "@typescript-eslint/no-namespace": [2, { allowDeclarations: true }], - "@typescript-eslint/no-unused-vars": [2, { args: "none" }], - - // Stylistic rules - "@typescript-eslint/adjacent-overload-signatures": 2, - "@typescript-eslint/consistent-type-assertions": 2, - "@typescript-eslint/no-empty-interface": 2, - "@typescript-eslint/no-non-null-assertion": 2, - "@typescript-eslint/no-inferrable-types": 2, - - // Additional rules - "@typescript-eslint/consistent-type-imports": 2, - "@typescript-eslint/no-import-type-side-effects": 2, - // allow require for power-assert and verbatimModuleSyntax - // '@typescript-eslint/no-require-imports': 2, - "@typescript-eslint/prefer-literal-enum-member": 2, - "@typescript-eslint/prefer-ts-expect-error": 2, - }, - settings: { - jsdoc: { - mode: "typescript", - }, - "import/extensions": [".mjs", ...TS_EXTENSIONS], - "import/external-module-folders": ["node_modules", "node_modules/@types"], - "import/parsers": { - "@typescript-eslint/parser": TS_EXTENSIONS, - }, - "import/resolver": { - typescript: TS_EXTENSIONS, - }, - }, - }, - { - files: ["*.mts"], - extends: ["./lib/module-globals.js"], - }, - ], -}; diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 66e9ef6c..00000000 --- a/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -/test/fixtures diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 331ed5ba..00000000 --- a/.eslintrc.js +++ /dev/null @@ -1,6 +0,0 @@ -"use strict"; - -module.exports = { - root: true, - extends: ["./node-v18.js", "./+prettier.js", "./+mocha.js"], -}; diff --git a/.gitignore b/.gitignore index c8f50f7c..b5cb0234 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ +dist npm-debug.log diff --git a/.prettierignore b/.prettierignore index a7596cb4..67af5837 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,2 @@ -/test/fixtures -/examples +dist package-lock.json -tsconfig.json diff --git a/.prettierrc.closure.json b/.prettierrc.closure.json index b600537f..232dd9cf 100644 --- a/.prettierrc.closure.json +++ b/.prettierrc.closure.json @@ -3,6 +3,5 @@ "singleQuote": true, "quoteProps": "preserve", "bracketSpacing": false, - "arrowParens": "avoid", - "printWidth": 100 + "arrowParens": "avoid" } diff --git a/.prettierrc.json b/.prettierrc.json deleted file mode 100644 index 73e99cee..00000000 --- a/.prettierrc.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "$schema": "http://json.schemastore.org/prettierrc", - "printWidth": 100 -} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..120b6c45 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "eslint.experimental.useFlatConfig": true, + "eslint.workingDirectories": [ + { + "mode": "auto" + } + ], + "eslint.debug": true +} diff --git a/README.md b/README.md index 4e871021..7704b9ae 100644 --- a/README.md +++ b/README.md @@ -10,156 +10,122 @@ ESLint config set for me! ## Priority 1. Avoid "Possible Errors" -2. Keep "Best Practices" if common -3. Use "Modern Style (ES2015+)" if available -4. Format "Stylistic Issues" if fixable or use Prettier +2. Keep "Best Practices" if fixable +3. Use Prettier for stylistic formatting -This rules is based on `eslint:recommended`. -Only additional or orverwritten rules are specified. - -## Usage - -### Install +## Install ```console -$ npm i -D eslint-config-teppeis +$ npm i -D eslint eslint-config-teppeis ``` -`eslint` and `prettier` are installed as peer dependencies. - -### Configure - -Specify in your `.eslintrc.json`: +and run `npx eslint-config-teppeis --init` to generate initial config files. -```json -{ - "extends": "teppeis" -} -``` +## Usage -Default config equals to `teppeis/es2018`. +Load `eslint-config-teppeis` and export default `build()` in your `eslint.config.js`: -### Choose base ECMAScript version +```js +import { build, mocha } from "eslint-config-teppeis"; -```json -{ - "extends": "teppeis/es2015" -} +export default await build( + { base: "node18", typescript: true, esm: true }, + mocha, + { + ignores: ["dist", "test/fixtures"], + }, +); ``` -- `teppeis/es5` -- `teppeis/es2015` -- `teppeis/es2016` -- `teppeis/es2017` -- `teppeis/es2018` -- `teppeis/es2019` -- `teppeis/es2020` -- `teppeis/es2021` -- `teppeis/es2022` -- `teppeis/es2023`: equals to `teppeis` +### Options -### For Node.js +- `base` (enum, required): `es2021`, `es2022`, `es2023`, `node18` or `node20` +- `typescript` (boolean, default false): use TypeScript +- `project` (boolean|string|srting[], default false): the property of `parserOptions` to enable linting with type information +- `esm` (boolean, default false): treat `.js` and `.ts` as ESM for a project that configures `type:module` in `package.json` -They include [eslint-plugin-n](https://www.npmjs.com/package/eslint-plugin-n). +## Configs for customization -#### With specific version +### Pure ECMAScript -Chose config for specific Node version +Chose config for specific ECMAScript version -```json -{ - "extends": ["teppeis/node-v18"] -} +```js +import { es2021 } from "eslint-config-teppeis"; + +export default [es2021]; ``` -- `teppeis/node-v18` (v18.17+ Active LTS) -- `teppeis/node-v20` (v20.5+ Current) +- `es2021` +- `es2022` +- `es2023` -#### With Babel or other transpilers +### Node.js -Extends `teppeis/+node` after base config. +Chose config for specific Node version -```json -{ - "extends": ["teppeis/es2018", "teppeis/+node"] -} -``` +- `node18` (v18.17+ Active LTS) +- `node20` (v20.5+ Current) -In `teppeis/+node`, [n/no-unsupported-features/es-syntax](https://github.com/weiran-zsd/eslint-plugin-node/blob/master/docs/rules/no-unsupported-features/es-syntax.md) is disabled. -Available ES features depend on the base config. +```js +import { node18 } from "eslint-config-teppeis"; -[n/no-unsupported-features/es-builtins](https://github.com/weiran-zsd/eslint-plugin-node/blob/master/docs/rules/no-unsupported-features/es-builtins.md) and [n/no-unsupported-features/node-builtins](https://github.com/weiran-zsd/eslint-plugin-node/blob/master/docs/rules/no-unsupported-features/node-builtins.md) are enabled. It is assumed that polyfill is not used. +export default [node18]; +``` -## Customize +### TypeScript -### Use Prettier +Configs for TypeScript -This plugin includes `prettier` itself in `peerDependencies`. +- `typescript`: Enable rules that don't require type information +- `typescriptTypeChecked`: Require type information -Override dupulicated or conflicted rules with `teppeis/+prettier`. +```js +import { node18, typescript } from "eslint-config-teppeis"; -```json -{ - "extends": ["teppeis/es2018", "teppeis/+prettier"] -} +export default [node18, typescript]; ``` -### For TypeScript +### ES Modules -Configs for TypeScript (applied only for `*.ts` and `*.tsx`). +By default, only `*.mjs` and `*.mts` are treated as ES Modules in configs for Node.js. +If you use `type:module` in package.json, use `esm: true` like: -- `teppeis/+typescript`: Enable rules that don't require type information -- `teppeis/+typescript-with-type`: Require type information (slow) +```js +import { build } from "eslint-config-teppeis"; -```json -{ - "extends": ["teppeis/node-v18", "teppeis/+typescript", "teppeis/+prettier"] -} +export default build({ base: "node18", esm: true }); ``` -### For ES Modules +### Browsers -By default, only `*.mjs` and `*.mts` are treated as ES Modules in configs for Node.js. -If you use `type:module` in package.json, use `teppeis/+module` like: +This enables globals for browsers. -```json -{ - "extends": ["teppeis/node-v18", "teppeis/+module"] -} -``` +```js +import { es2023, browser } from "eslint-config-teppeis"; -or for TypeScript like: - -```json -{ - "extends": ["teppeis/node-v18", "teppeis/+typescript", "teppeis/+module"] -} +export default [es2023, browser]; ``` -### For browsers +### Mocha -This adds `browser` to `env`. +This enables globals for Mocha like `describe` or `it` only in `**/test/*.js`. -```json -{ - "extends": ["teppeis/es2018", "teppeis/+browser"] -} -``` +```js +import { es2023, mocha } from "eslint-config-teppeis"; -### Use Mocha for testing +export default [es2023, mocha]; +``` -This enables mocha globals like `describe` or `it` in `**/test/*.js`. +## Note: Prettier -```json -{ - "extends": ["teppeis/es2018", "teppeis/+mocha"] -} -``` +Just intall `prettier` and use it with `eslint-config-teppeis`. +These configs don't include rule settings that conflict with Pretteir. ## License Licensed under the MIT license. -Copyright (c) 2021, Teppei Sato +Copyright (c) 2023, Teppei Sato [npm-image]: https://badgen.net/npm/v/eslint-config-teppeis?icon=npm&label= [npm-url]: https://npmjs.org/package/eslint-config-teppeis diff --git a/bin/cli.js b/bin/cli.js new file mode 100755 index 00000000..816a2887 --- /dev/null +++ b/bin/cli.js @@ -0,0 +1,5 @@ +#!/usr/bin/env node + +import { run } from "../dist/cli.js"; + +run(); diff --git a/es2015.js b/es2015.js deleted file mode 100644 index 7959feb7..00000000 --- a/es2015.js +++ /dev/null @@ -1,5 +0,0 @@ -"use strict"; - -module.exports = { - extends: ["./lib/base.js", "./lib/es2015.js"], -}; diff --git a/es2016.js b/es2016.js deleted file mode 100644 index 1b70e275..00000000 --- a/es2016.js +++ /dev/null @@ -1,5 +0,0 @@ -"use strict"; - -module.exports = { - extends: ["./lib/base.js", "./lib/es2015.js", "./lib/es2016.js"], -}; diff --git a/es2017.js b/es2017.js deleted file mode 100644 index fd196479..00000000 --- a/es2017.js +++ /dev/null @@ -1,5 +0,0 @@ -"use strict"; - -module.exports = { - extends: ["./lib/base.js", "./lib/es2015.js", "./lib/es2016.js", "./lib/es2017.js"], -}; diff --git a/es2018.js b/es2018.js deleted file mode 100644 index e5bbf357..00000000 --- a/es2018.js +++ /dev/null @@ -1,11 +0,0 @@ -"use strict"; - -module.exports = { - extends: [ - "./lib/base.js", - "./lib/es2015.js", - "./lib/es2016.js", - "./lib/es2017.js", - "./lib/es2018.js", - ], -}; diff --git a/es2019.js b/es2019.js deleted file mode 100644 index a0ba4e34..00000000 --- a/es2019.js +++ /dev/null @@ -1,12 +0,0 @@ -"use strict"; - -module.exports = { - extends: [ - "./lib/base.js", - "./lib/es2015.js", - "./lib/es2016.js", - "./lib/es2017.js", - "./lib/es2018.js", - "./lib/es2019.js", - ], -}; diff --git a/es2020.js b/es2020.js deleted file mode 100644 index 6e3e737c..00000000 --- a/es2020.js +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; - -module.exports = { - extends: [ - "./lib/base.js", - "./lib/es2015.js", - "./lib/es2016.js", - "./lib/es2017.js", - "./lib/es2018.js", - "./lib/es2019.js", - "./lib/es2020.js", - ], -}; diff --git a/es2021.js b/es2021.js deleted file mode 100644 index b5c57b90..00000000 --- a/es2021.js +++ /dev/null @@ -1,14 +0,0 @@ -"use strict"; - -module.exports = { - extends: [ - "./lib/base.js", - "./lib/es2015.js", - "./lib/es2016.js", - "./lib/es2017.js", - "./lib/es2018.js", - "./lib/es2019.js", - "./lib/es2020.js", - "./lib/es2021.js", - ], -}; diff --git a/es2022.js b/es2022.js deleted file mode 100644 index 47bc2b64..00000000 --- a/es2022.js +++ /dev/null @@ -1,15 +0,0 @@ -"use strict"; - -module.exports = { - extends: [ - "./lib/base.js", - "./lib/es2015.js", - "./lib/es2016.js", - "./lib/es2017.js", - "./lib/es2018.js", - "./lib/es2019.js", - "./lib/es2020.js", - "./lib/es2021.js", - "./lib/es2022.js", - ], -}; diff --git a/es2023.js b/es2023.js deleted file mode 100644 index 68c9c9d0..00000000 --- a/es2023.js +++ /dev/null @@ -1,16 +0,0 @@ -"use strict"; - -module.exports = { - extends: [ - "./lib/base.js", - "./lib/es2015.js", - "./lib/es2016.js", - "./lib/es2017.js", - "./lib/es2018.js", - "./lib/es2019.js", - "./lib/es2020.js", - "./lib/es2021.js", - "./lib/es2022.js", - "./lib/es2023.js", - ], -}; diff --git a/es5.js b/es5.js deleted file mode 100644 index 42f9624a..00000000 --- a/es5.js +++ /dev/null @@ -1,3 +0,0 @@ -"use strict"; - -module.exports = require("./lib/base"); diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 00000000..d8f9b08a --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,15 @@ +import { build, mocha } from "./dist/index.js"; + +export default await build( + { base: "node18", typescript: true, esm: true }, + { + ignores: ["dist", "examples", "test/fixtures"], + }, + { + files: ["templates/*"], + rules: { + "n/no-missing-import": "off", + }, + }, + mocha, +); diff --git a/examples/.eslintrc.json b/examples/.eslintrc.json deleted file mode 100644 index c987ae81..00000000 --- a/examples/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "root": true -} diff --git a/examples/browser/.eslintrc.json b/examples/browser/.eslintrc.json deleted file mode 100644 index bc77a16f..00000000 --- a/examples/browser/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": ["../../es2015.js", "../../+browser.js"] -} diff --git a/examples/browser/eslint.config.js b/examples/browser/eslint.config.js new file mode 100644 index 00000000..9f70f560 --- /dev/null +++ b/examples/browser/eslint.config.js @@ -0,0 +1,17 @@ +import { browser, build } from "../../dist/index.js"; + +const configs = await build({ base: "node18" }); + +/** @type { import("eslint").Linter.FlatConfig[] } */ +export default [ + ...configs, + browser, + { + rules: { + "no-undef": 2, + }, + }, + { + ignores: ["eslint.config.{js,mjs}"], + }, +]; diff --git a/examples/browser/index.js b/examples/browser/index.js deleted file mode 100644 index d8b1f326..00000000 --- a/examples/browser/index.js +++ /dev/null @@ -1,3 +0,0 @@ -'use strict'; - -alert('foo'); diff --git a/examples/browser/index.mjs b/examples/browser/index.mjs new file mode 100644 index 00000000..055f7217 --- /dev/null +++ b/examples/browser/index.mjs @@ -0,0 +1,7 @@ +/* eslint-disable no-undef */ + +// globals.browser loaded +alert("foo"); + +// eslint-disable-next-line unicorn/prefer-dom-node-append +foo.appendChild(bar); diff --git a/examples/es2015/.eslintrc.json b/examples/es2015/.eslintrc.json deleted file mode 100644 index a6a10944..00000000 --- a/examples/es2015/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../../es2015.js" -} diff --git a/examples/es2015/index.js b/examples/es2015/index.js deleted file mode 100644 index 5d3eb79c..00000000 --- a/examples/es2015/index.js +++ /dev/null @@ -1,96 +0,0 @@ -/* eslint-disable no-unused-vars */ -'use strict'; - -class Foo { - constructor(foo) { - this.foo = foo; - } - - hello(name) { - return `Hello, ${name}`; - } - - invoke(param, cb) { - cb(param); - } - - call() { - this.invoke(param => param); - } - - namedCall() { - this.invoke(function foo(param) { - return param; - }); - } - - env() { - return Promise.resolve(1); - } - - confusingArrow() { - return n => (n * Math.random() > 0.5 ? 1 : 2); - } - - static [Symbol.hasInstance](obj) { - return obj.constructor === Foo; - } -} - -// object-shorthand: properties -const obj = { - 1: 'b', - foo() {}, - // allow properties - Foo: Foo, -}; - -// object-shorthand: methods -const foo = { - // prefer method shorthand to `function()` - foo(bar, baz) { - const piyo = bar + baz; - return piyo(); - }, - // allow arrow functions - bar: (bar, baz) => { - const piyo = bar + baz; - return piyo(); - }, - bar2: (bar, baz) => { - const piyo = bar + baz; - piyo(); - }, - baz: (bar, baz) => bar + baz, -}; - -// template-tab-spacing -function tag() {} -foo.foo(tag`hello`); -// no-unused-expressions: allowTaggedTemplates: true -tag`hello`; - -// https://eslint.org/docs/rules/generator-star-spacing -function* generator() {} -const anonymous = function*() { - yield* generator(); -}; -const method = { - *generator() {}, -}; - -// no-misleading-character-class -const a = /^[abc]$/; -const b = /^[👍]$/u; - -// unicorn/prefer-string-starts-ends-with -foo.startsWith('bar'); - -// prefer-spread: disable -const args = []; -foo.apply(undefined, args); - -// prefer-destructuring -const array = []; -const a0 = array[0]; -const {prop1} = foo; diff --git a/examples/es2017/.eslintrc.json b/examples/es2017/.eslintrc.json deleted file mode 100644 index ac56412c..00000000 --- a/examples/es2017/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../../es2017.js" -} diff --git a/examples/es2017/index.js b/examples/es2017/index.js deleted file mode 100644 index eae11d3d..00000000 --- a/examples/es2017/index.js +++ /dev/null @@ -1,44 +0,0 @@ -/* eslint-disable no-unused-vars, eslint-comments/disable-enable-pair */ -'use strict'; - -// ES2017 new globals: SharedArrayBuffer -new SharedArrayBuffer(1024); - -/* eslint-disable no-undef */ -// ES2017 new syntax: async/await -let foo = async a => { - await fetch(a); -}; - -async function wrap() { - for (const value of list) { - await doSomething(value); - } -} - -// comma-dangle -foo = { - bar: 'baz', - qux: 'quux', -}; -foo = {bar: 'baz', qux: 'quux'}; -foo = [1, 2]; -foo = [1, - 2]; -foo = [ - 1, - 2, -]; -console.log('baz', 'quux'); -console.log( - 'baz', - 'quux', -); -function paramsComma1(a, b) {} -function paramsComma2( - a, - b, -) {} - -// unicorn/prefer-includes -[].includes('foo'); diff --git a/examples/es2021/.eslintrc.json b/examples/es2021/.eslintrc.json deleted file mode 100644 index eb9b482c..00000000 --- a/examples/es2021/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../../es2021.js" -} diff --git a/examples/es2021/eslint.config.js b/examples/es2021/eslint.config.js new file mode 100644 index 00000000..7a0cff92 --- /dev/null +++ b/examples/es2021/eslint.config.js @@ -0,0 +1,11 @@ +import { build } from "../../dist/index.js"; + +const configs = await build({ base: "es2021" }); + +/** @type { import("eslint").Linter.FlatConfig[] } */ +export default [ + ...configs, + { + ignores: ["eslint.config.{js,mjs}"], + }, +]; diff --git a/examples/es2021/index.js b/examples/es2021/index.js deleted file mode 100644 index 49bb93f7..00000000 --- a/examples/es2021/index.js +++ /dev/null @@ -1,12 +0,0 @@ -/* eslint-disable no-unused-vars */ -'use strict'; - -// numeric separator (unicorn/numeric-separators-style) -const num1 = 100000000; // onlyIfContainsSeparator: true -const num2 = 1_000; // number.minimumDigits: 0 -const num3 = 10_000; - -// globals -new WeakRef(); -new FinalizationRegistry(); -new AggregateError(); diff --git a/examples/es2021/index.mjs b/examples/es2021/index.mjs new file mode 100644 index 00000000..ab11fa4c --- /dev/null +++ b/examples/es2021/index.mjs @@ -0,0 +1,28 @@ +/* eslint-disable no-unused-vars */ + +// ES2021: unicorn/numeric-separators-style +const num1 = 100000000; // onlyIfContainsSeparator: true +const num2 = 1_000; // number.minimumDigits: 0 +const num3 = 10_000; + +// ES2021: globals +new WeakRef(); +new FinalizationRegistry(); +new AggregateError(); + +// base +const x = 0; +// eslint-disable-next-line no-compare-neg-zero +if (x === -0) { + x.toString(); +} + +// eslint-plugin-jsdoc +/** + * @param {number} a + * @param {number} b + * @return {number} + */ +function bar(a, b) { + return a + b; +} diff --git a/examples/es2022/eslint.config.js b/examples/es2022/eslint.config.js new file mode 100644 index 00000000..7b9bb5a9 --- /dev/null +++ b/examples/es2022/eslint.config.js @@ -0,0 +1,11 @@ +import { build } from "../../dist/index.js"; + +const configs = await build({ base: "es2022" }); + +/** @type { import("eslint").Linter.FlatConfig[] } */ +export default [ + ...configs, + { + ignores: ["eslint.config.{js,mjs}"], + }, +]; diff --git a/examples/es2022/index.mjs b/examples/es2022/index.mjs new file mode 100644 index 00000000..ba0ff8de --- /dev/null +++ b/examples/es2022/index.mjs @@ -0,0 +1,16 @@ +/* eslint-disable no-unused-vars, no-undef */ + +// ES2022 new features +// eslint-disable-next-line prefer-object-has-own +Object.prototype.hasOwnProperty.call(obj, "a"); +// -> Object.hasOwn(obj, "a"); + +// eslint-disable-next-line unicorn/prefer-at +const last = array[array.length - 1]; +// -> const last = array.at(-1); + +// check base config enabled +// eslint-disable-next-line no-compare-neg-zero +if (x === -0) { + x.toString(); +} diff --git a/examples/es2023/eslint.config.js b/examples/es2023/eslint.config.js new file mode 100644 index 00000000..15a6d2e1 --- /dev/null +++ b/examples/es2023/eslint.config.js @@ -0,0 +1,11 @@ +import { build } from "../../dist/index.js"; + +const configs = await build({ base: "es2023" }); + +/** @type { import("eslint").Linter.FlatConfig[] } */ +export default [ + ...configs, + { + ignores: ["eslint.config.{js,mjs}"], + }, +]; diff --git a/examples/es2023/index.mjs b/examples/es2023/index.mjs new file mode 100644 index 00000000..e949dcaa --- /dev/null +++ b/examples/es2023/index.mjs @@ -0,0 +1,9 @@ +/* eslint-disable no-undef */ + +// ES2023 no new features + +// check base config enabled +// eslint-disable-next-line no-compare-neg-zero +if (x === -0) { + x.toString(); +} diff --git a/examples/es5/.eslintrc.json b/examples/es5/.eslintrc.json deleted file mode 100644 index 46397ae2..00000000 --- a/examples/es5/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../../es5.js" -} diff --git a/examples/es5/index.js b/examples/es5/index.js deleted file mode 100644 index 7d0e97d4..00000000 --- a/examples/es5/index.js +++ /dev/null @@ -1,78 +0,0 @@ -/* eslint-disable no-unused-vars, no-undef */ -'use strict'; - -/** - * Sets multiple properties on a node. - * @param {Element} element DOM node to set properties on. - * @param {Object} properties Hash of property:value pairs. - * @param {boolean|string|number|null|undefined} a - * @return {null} - */ -function setProperties(element, properties, a) { - Object.keys(properties).forEach(function(key) { - var val = properties[key]; - if (key === 'style') { - element.style.cssText = val; - } else if (key === 'class') { - element.className = val; - } else if (key === 'for') { - element.htmlFor = val; - } else { - element[key] = val; - } - }); - return null; -} - -setProperties(); - -/** - * @constructor - */ -function foo() {} - -/** - */ -function noparam(a, b) {} - -// comma-dangle -var foo2 = { - bar: 'baz', - qux: 'quux', -}; -foo2 = {bar: 'baz', qux: 'quux'}; -foo2 = [1, 2]; -foo2 = [1, - 2]; -foo2 = [ - 1, - 2, -]; -console.log('baz', 'quux'); -console.log( - 'baz', - 'quux' -); -function paramsComma1(a, b) {} -function paramsComma2( - a, - b -) {} - -// eslint-plugin-unicorn -var s = '\u001B'; - -// eslint-plugin-jsdoc -/** - * "jsdoc/check-types": [2, { unifyParentAndChildTypeChecks: true }] - * @param {Object>>} foo - * @return {bbbb} - */ -function quux(foo) { - return 1; -} - -/** - * @type {Array} - */ -var nonFunction; diff --git a/examples/lint-all.sh b/examples/lint-all.sh new file mode 100755 index 00000000..8062a4bd --- /dev/null +++ b/examples/lint-all.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +set -e + +examples_dir=$(cd $(dirname $0);pwd) +error=0 + +lint() { + local current_dir=$(pwd) + local target_dir="$examples_dir/$1" + echo "- $1" + cd "$target_dir" + set +e + npx eslint --max-warnings 0 . + if [ $? -ne 0 ]; then + error=1 + fi + set -e + cd $current_dir +} + +echo "Linting examples..." +lint "es2021" +lint "es2022" +lint "es2023" +lint "node18" +lint "node20" +lint "typescript" +lint "typescript-type-checked" +lint "mocha" +lint "browser" + +exit $error diff --git a/examples/mocha/.eslintrc.json b/examples/mocha/.eslintrc.json deleted file mode 100644 index 072c16b9..00000000 --- a/examples/mocha/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": ["../../node-v18.js", "../../+mocha.js"] -} diff --git a/examples/mocha/eslint.config.js b/examples/mocha/eslint.config.js new file mode 100644 index 00000000..eabf4492 --- /dev/null +++ b/examples/mocha/eslint.config.js @@ -0,0 +1,21 @@ +import globals from "globals"; +import { mocha } from "../../dist/index.js"; + +/** @type { import("eslint").Linter.FlatConfig[] } */ +export default [ + { + languageOptions: { + globals: { ...globals.node }, + }, + linterOptions: { + reportUnusedDisableDirectives: true, + }, + rules: { + "no-undef": 2, + }, + }, + mocha, + { + ignores: ["eslint.config.{js,mjs}"], + }, +]; diff --git a/examples/mocha/index.js b/examples/mocha/index.js new file mode 100644 index 00000000..9e95ac6b --- /dev/null +++ b/examples/mocha/index.js @@ -0,0 +1,4 @@ +// enabled globals.node +require(); +// disabled globals.mocha not in test dir +it(); // eslint-disable-line no-undef diff --git a/examples/mocha/test/index.js b/examples/mocha/test/index.js index e21fe915..e70d9c2a 100644 --- a/examples/mocha/test/index.js +++ b/examples/mocha/test/index.js @@ -1,12 +1,12 @@ -'use strict'; +"use strict"; -const assert = require('node:assert'); +const assert = require("node:assert/strict"); -describe('foo', () => { - it('bar', () => { +describe("foo", () => { + it("bar", () => { assert(true); }); - it('baz', function() { + it("baz", function () { // https://eslint.org/docs/rules/no-invalid-this this.timeout(5000); }); diff --git a/examples/node-es2017/.eslintrc.json b/examples/node-es2017/.eslintrc.json deleted file mode 100644 index 1abf7239..00000000 --- a/examples/node-es2017/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": ["../../es2017.js", "../../+node.js"] -} diff --git a/examples/node-es2017/index.js b/examples/node-es2017/index.js deleted file mode 100644 index cd61f0db..00000000 --- a/examples/node-es2017/index.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; - -require('eslint-plugin-import'); -require('./'); -// require('./missing'); - -function Foo() {} - -exports.obj = { - Foo: Foo, -}; - -console.log('foo'); - -exports.Foo = Foo; diff --git a/examples/node-module/.eslintrc.json b/examples/node-module/.eslintrc.json deleted file mode 100644 index fba8fe99..00000000 --- a/examples/node-module/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": ["../../node-v18.js", "../../+module.js"] -} diff --git a/examples/node-module/cjs.cjs b/examples/node-module/cjs.cjs deleted file mode 100644 index b4a55db2..00000000 --- a/examples/node-module/cjs.cjs +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -// module-globals is not loaded -require('node:assert'); -console.log(__filename); -exports.foo = 1; -module.exports = 1; diff --git a/examples/node-module/esm.mjs b/examples/node-module/esm.mjs deleted file mode 100644 index 33ccb04c..00000000 --- a/examples/node-module/esm.mjs +++ /dev/null @@ -1,22 +0,0 @@ -import assert from 'node:assert'; - -export const assertOk = assert.ok; - -// module-base -// eslint-disable-next-line import/no-self-import -import('./esm.mjs'); - -// module-globals -// eslint-disable-next-line unicorn/prefer-module -require('node:assert'); -// eslint-disable-next-line unicorn/prefer-module -console.log(__filename); -// eslint-disable-next-line unicorn/prefer-module -exports.foo = 1; -// eslint-disable-next-line unicorn/prefer-module -module.exports = 1; - -// module-js -// A extension is required in ESM import specifire -// eslint-disable-next-line import/extensions -import('./index'); diff --git a/examples/node-module/index.js b/examples/node-module/index.js deleted file mode 100644 index f4cf459c..00000000 --- a/examples/node-module/index.js +++ /dev/null @@ -1,22 +0,0 @@ -import assert from 'node:assert'; - -export const assertOk = assert.ok; - -// module-base -// eslint-disable-next-line import/no-self-import -import('./index.js'); - -// module-globals -// eslint-disable-next-line unicorn/prefer-module -require('node:assert'); -// eslint-disable-next-line unicorn/prefer-module -console.log(__filename); -// eslint-disable-next-line unicorn/prefer-module -exports.foo = 1; -// eslint-disable-next-line unicorn/prefer-module -module.exports = 1; - -// module-js -// A extension is required in ESM import specifire -// eslint-disable-next-line import/extensions -import('./esm'); diff --git a/examples/node-v18/.eslintrc.json b/examples/node-v18/.eslintrc.json deleted file mode 100644 index 2505a749..00000000 --- a/examples/node-v18/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../../node-v18.js" -} diff --git a/examples/node-v20/.eslintrc.json b/examples/node-v20/.eslintrc.json deleted file mode 100644 index 3040f071..00000000 --- a/examples/node-v20/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../../node-v20.js" -} diff --git a/examples/node-v20/index.js b/examples/node-v20/index.js deleted file mode 100644 index 1314a950..00000000 --- a/examples/node-v20/index.js +++ /dev/null @@ -1,3 +0,0 @@ -'use strict'; - -// no new features for linters diff --git a/examples/node18/cjs.js b/examples/node18/cjs.js new file mode 100644 index 00000000..303d75a0 --- /dev/null +++ b/examples/node18/cjs.js @@ -0,0 +1,4 @@ +"use strict"; + +const assert = require("node:assert/strict"); +assert.ok("OK!"); diff --git a/examples/node18/eslint.config.js b/examples/node18/eslint.config.js new file mode 100644 index 00000000..b2814cbe --- /dev/null +++ b/examples/node18/eslint.config.js @@ -0,0 +1,11 @@ +import { build } from "../../dist/index.js"; + +const configs = await build({ base: "node18" }); + +/** @type { import("eslint").Linter.FlatConfig[] } */ +export default [ + ...configs, + { + ignores: ["eslint.config.{js,mjs}"], + }, +]; diff --git a/examples/node-v18/index.js b/examples/node18/esm.mjs similarity index 58% rename from examples/node-v18/index.js rename to examples/node18/esm.mjs index 141c23c4..25f4636d 100644 --- a/examples/node-v18/index.js +++ b/examples/node18/esm.mjs @@ -1,13 +1,19 @@ /* eslint-disable no-unused-vars */ -'use strict'; +import fs from "node:fs"; -// ES2022 Class fields +// eslint-disable-next-line n/no-deprecated-api +fs.exists("./foo", () => {}); + +// eslint-disable-next-line unicorn/prefer-module +require("node:assert/strict"); + +// ES2022 new syntax: class fields class C { // Public instance and static fields (Node v12.0+) static foo; // Private class fields (Node v14.6+) - #x = 'x'; + #x = "x"; static check(obj) { // Ergonomic brand checks (Node v16.4+) return #x in obj; @@ -17,7 +23,3 @@ class C { C.foo = 2; } } - -// .at() method on the built-in indexables (Node v16.6+) -// https://node.green/#ES2022-features--at---method-on-the-built-in-indexables -[0, 1, 2].at(2); diff --git a/examples/node20/cjs.js b/examples/node20/cjs.js new file mode 100644 index 00000000..303d75a0 --- /dev/null +++ b/examples/node20/cjs.js @@ -0,0 +1,4 @@ +"use strict"; + +const assert = require("node:assert/strict"); +assert.ok("OK!"); diff --git a/examples/node20/eslint.config.js b/examples/node20/eslint.config.js new file mode 100644 index 00000000..9b459664 --- /dev/null +++ b/examples/node20/eslint.config.js @@ -0,0 +1,11 @@ +import { build } from "../../dist/index.js"; + +const configs = await build({ base: "node20" }); + +/** @type { import("eslint").Linter.FlatConfig[] } */ +export default [ + ...configs, + { + ignores: ["eslint.config.{js,mjs}"], + }, +]; diff --git a/examples/node20/esm.mjs b/examples/node20/esm.mjs new file mode 100644 index 00000000..25f4636d --- /dev/null +++ b/examples/node20/esm.mjs @@ -0,0 +1,25 @@ +/* eslint-disable no-unused-vars */ +import fs from "node:fs"; + +// eslint-disable-next-line n/no-deprecated-api +fs.exists("./foo", () => {}); + +// eslint-disable-next-line unicorn/prefer-module +require("node:assert/strict"); + +// ES2022 new syntax: class fields +class C { + // Public instance and static fields (Node v12.0+) + static foo; + + // Private class fields (Node v14.6+) + #x = "x"; + static check(obj) { + // Ergonomic brand checks (Node v16.4+) + return #x in obj; + } + static { + // Static initialization blocks (Node v16.11+) + C.foo = 2; + } +} diff --git a/examples/typescript-esm/cjs.cts b/examples/typescript-esm/cjs.cts new file mode 100644 index 00000000..1d6b7e09 --- /dev/null +++ b/examples/typescript-esm/cjs.cts @@ -0,0 +1,38 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/** + * @fileoverview A CommonJS TypeScript file in type:module and module:NodeNext + */ + +// (faux) import is available +import fs from "node:fs"; +import type { Foo } from "./mod-cjs.cjs"; +// @ts-expect-error TS2307 +import { Bar } from "./mod-cjs"; + +// @ts-expect-error TS1471 +import esm = require("./mod-esm.mjs"); +import cjs = require("./mod-cjs.cjs"); + +// node-esm is not loaded +require("node:assert"); + +console.log(__filename); + +// module-base is loaded +// eslint-disable-next-line import/no-self-import +import("./cjs.cjs"); + +// configs/base is loaded +const x = 0; +// eslint-disable-next-line no-compare-neg-zero +if (x === -0) { + x.toString(); +} + +// configs/node20 is loaded +// eslint-disable-next-line n/no-deprecated-api +fs.exists("./foo", () => {}); + +// configs/typescript is loaded +// eslint-disable-next-line @typescript-eslint/no-empty-interface +interface Empty {} diff --git a/examples/typescript-esm/eslint.config.js b/examples/typescript-esm/eslint.config.js new file mode 100644 index 00000000..53e0cb8d --- /dev/null +++ b/examples/typescript-esm/eslint.config.js @@ -0,0 +1,11 @@ +import { build } from "../../dist/index.js"; + +const configs = await build({ base: "node20", typescript: true, esm: true }); + +/** @type { import("eslint").Linter.FlatConfig[] } */ +export default [ + ...configs, + { + ignores: ["eslint.config.{js,mjs}"], + }, +]; diff --git a/examples/typescript-esm/esm.mts b/examples/typescript-esm/esm.mts new file mode 100644 index 00000000..de001c12 --- /dev/null +++ b/examples/typescript-esm/esm.mts @@ -0,0 +1,41 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/** + * @fileoverview An ESM TypeScript file in type:module and module:NodeNext + */ + +// ESM import is available +import fs from "node:fs"; +import type { Foo } from "./mod-esm.mjs"; +// requires an extension +// @ts-expect-error TS2835 +import { Bar } from "./mod-esm"; + +// @ts-expect-error TS1471 +import esm = require("./mod-esm.mjs"); +import cjs = require("./mod-cjs.cjs"); + +// node-esm is loaded +// eslint-disable-next-line unicorn/prefer-module +require("node:assert"); + +// eslint-disable-next-line unicorn/prefer-module +console.log(__filename); + +// module-base is loaded +// eslint-disable-next-line import/no-self-import +import("./esm.mjs"); + +// configs/base is loaded +const x = 0; +// eslint-disable-next-line no-compare-neg-zero +if (x === -0) { + x.toString(); +} + +// configs/node20 is loaded +// eslint-disable-next-line n/no-deprecated-api +fs.exists("./foo", () => {}); + +// configs/typescript is loaded +// eslint-disable-next-line @typescript-eslint/no-empty-interface +interface Empty {} diff --git a/examples/typescript-esm/index.ts b/examples/typescript-esm/index.ts new file mode 100644 index 00000000..eb75c7e3 --- /dev/null +++ b/examples/typescript-esm/index.ts @@ -0,0 +1,41 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/** + * @fileoverview An ESM TypeScript file in type:module and module:NodeNext + */ + +// ESM import is available +import fs from "node:fs"; +import type { Foo } from "./mod-esm.mjs"; +// requires an extension +// @ts-expect-error TS2835 +import { Bar } from "./mod-esm"; + +// @ts-expect-error TS1471 +import esm = require("./mod-esm.mjs"); +import cjs = require("./mod-cjs.cjs"); + +// node-esm is loaded +// eslint-disable-next-line unicorn/prefer-module +require("node:assert"); + +// eslint-disable-next-line unicorn/prefer-module +console.log(__filename); + +// module-base is loaded +// eslint-disable-next-line import/no-self-import +import("./index.js"); + +// configs/base is loaded +const x = 0; +// eslint-disable-next-line no-compare-neg-zero +if (x === -0) { + x.toString(); +} + +// configs/node20 is loaded +// eslint-disable-next-line n/no-deprecated-api +fs.exists("./foo", () => {}); + +// configs/typescript is loaded +// eslint-disable-next-line @typescript-eslint/no-empty-interface +interface Empty {} diff --git a/examples/typescript-esm/jsx.tsx b/examples/typescript-esm/jsx.tsx new file mode 100644 index 00000000..c5ae08a1 --- /dev/null +++ b/examples/typescript-esm/jsx.tsx @@ -0,0 +1,44 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/** + * @fileoverview A JSX TypeScript file in type:module and module:NodeNext + */ + +// ESM import is available +import fs from "node:fs"; +import type { Foo } from "./mod-esm.mjs"; +// requires an extension +// @ts-expect-error TS2835 +import { Bar } from "./mod-esm"; + +// @ts-expect-error TS1471 +import esm = require("./mod-esm.mjs"); +import cjs = require("./mod-cjs.cjs"); + +// node-esm is loaded +// eslint-disable-next-line unicorn/prefer-module +require("node:assert"); + +// eslint-disable-next-line unicorn/prefer-module +console.log(__filename); + +// module-base is loaded +// eslint-disable-next-line import/no-self-import +import("./jsx.js"); + +// configs/base is loaded +const x = 0; +// eslint-disable-next-line no-compare-neg-zero +if (x === -0) { + x.toString(); +} + +// configs/node20 is loaded +// eslint-disable-next-line n/no-deprecated-api +fs.exists("./foo", () => {}); + +// configs/typescript is loaded +// eslint-disable-next-line @typescript-eslint/no-empty-interface +interface Empty {} + +// JSX +const Button = ; diff --git a/examples/typescript/mod2.ts b/examples/typescript-esm/mod-cjs.cts similarity index 81% rename from examples/typescript/mod2.ts rename to examples/typescript-esm/mod-cjs.cts index 499f23cf..7b96a0c5 100644 --- a/examples/typescript/mod2.ts +++ b/examples/typescript-esm/mod-cjs.cts @@ -1,5 +1,5 @@ export interface Foo { - foo: string + foo: string; } export class Bar {} export class Baz {} diff --git a/examples/typescript-esm/mod-esm.mts b/examples/typescript-esm/mod-esm.mts new file mode 100644 index 00000000..7b96a0c5 --- /dev/null +++ b/examples/typescript-esm/mod-esm.mts @@ -0,0 +1,5 @@ +export interface Foo { + foo: string; +} +export class Bar {} +export class Baz {} diff --git a/examples/typescript-esm/package.json b/examples/typescript-esm/package.json new file mode 100644 index 00000000..3dbc1ca5 --- /dev/null +++ b/examples/typescript-esm/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/examples/typescript-esm/tsconfig.json b/examples/typescript-esm/tsconfig.json new file mode 100644 index 00000000..d3c2a9ef --- /dev/null +++ b/examples/typescript-esm/tsconfig.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Node 18", + "compilerOptions": { + "lib": ["es2023"], + "module": "NodeNext", + "moduleResolution": "NodeNext", + "target": "es2022", + "jsx": "preserve", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true + } +} diff --git a/examples/typescript-module/.eslintrc.json b/examples/typescript-module/.eslintrc.json deleted file mode 100644 index 405502f4..00000000 --- a/examples/typescript-module/.eslintrc.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "root": true, - "extends": ["../../node-v18.js", "../../+typescript.js", "../../+module.js"] -} diff --git a/examples/typescript-module/cjs.cts b/examples/typescript-module/cjs.cts deleted file mode 100644 index 5eb5e74c..00000000 --- a/examples/typescript-module/cjs.cts +++ /dev/null @@ -1,14 +0,0 @@ -import assert from 'node:assert'; - -export const assertOk = assert.ok; - -// module-base is loaded -// eslint-disable-next-line import/no-self-import -import('./cjs.cjs'); - -// module-globals is not loaded -require('node:assert'); - -console.log(__filename); -exports.foo = 1; -module.exports = 1; diff --git a/examples/typescript-module/esm.mts b/examples/typescript-module/esm.mts deleted file mode 100644 index 89b3f66a..00000000 --- a/examples/typescript-module/esm.mts +++ /dev/null @@ -1,21 +0,0 @@ -import assert from 'node:assert'; - -export const assertOk = assert.ok; - -// module-base -// eslint-disable-next-line import/no-self-import -import('./esm.mjs'); - -// module-globals: unavailable in Node ESM -// eslint-disable-next-line unicorn/prefer-module -require('node:assert'); -// eslint-disable-next-line unicorn/prefer-module -console.log(__filename); -// eslint-disable-next-line unicorn/prefer-module -exports.foo = 1; -// eslint-disable-next-line unicorn/prefer-module -module.exports = 1; - -// module-js is not loaded, but tsc throws an error -// @ts-expect-error ts(2835) require extension -import('./index'); diff --git a/examples/typescript-module/index.ts b/examples/typescript-module/index.ts deleted file mode 100644 index 8a7a6065..00000000 --- a/examples/typescript-module/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -import assert from 'node:assert'; - -export const assertOk = assert.ok; - -// module-base -// eslint-disable-next-line import/no-self-import -import('./index.js'); - -// module-globals: unavailable in Node ESM -// eslint-disable-next-line unicorn/prefer-module -require('node:assert'); -// eslint-disable-next-line unicorn/prefer-module -console.log(__filename); -// eslint-disable-next-line unicorn/prefer-module -exports.foo = 1; -// eslint-disable-next-line unicorn/prefer-module -module.exports = 1; - -// module-js is not loaded, but tsc throws an error -// @ts-expect-error ts(2835) require extension -import('./esm'); diff --git a/examples/typescript-module/jsx.tsx b/examples/typescript-module/jsx.tsx deleted file mode 100644 index 1af1d5da..00000000 --- a/examples/typescript-module/jsx.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import assert from 'node:assert'; - -export const assertOk = assert.ok; - -// module-base -// eslint-disable-next-line import/no-self-import -import('./jsx.jsx'); - -// module-globals: unavailable in Node ESM -// eslint-disable-next-line unicorn/prefer-module -require('node:assert'); -// eslint-disable-next-line unicorn/prefer-module -console.log(__filename); -// eslint-disable-next-line unicorn/prefer-module -exports.foo = 1; -// eslint-disable-next-line unicorn/prefer-module -module.exports = 1; - -// module-js is not loaded, but tsc throws an error -// @ts-expect-error ts(2835) require extension -import('./index'); diff --git a/examples/typescript-module/tsconfig.json b/examples/typescript-module/tsconfig.json deleted file mode 100644 index 6ce92796..00000000 --- a/examples/typescript-module/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "Node16", - "jsx": "preserve", - "noEmit": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true - } -} diff --git a/examples/typescript-type-checked/eslint.config.js b/examples/typescript-type-checked/eslint.config.js new file mode 100644 index 00000000..19c37f24 --- /dev/null +++ b/examples/typescript-type-checked/eslint.config.js @@ -0,0 +1,15 @@ +import { build } from "../../dist/index.js"; + +const configs = await build({ + base: "node20", + typescript: true, + project: true, +}); + +/** @type { import("eslint").Linter.FlatConfig[] } */ +export default [ + ...configs, + { + ignores: ["eslint.config.{js,mjs}"], + }, +]; diff --git a/examples/typescript-type-checked/index.ts b/examples/typescript-type-checked/index.ts new file mode 100644 index 00000000..500c1b46 --- /dev/null +++ b/examples/typescript-type-checked/index.ts @@ -0,0 +1,5 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ + +const arg1 = [1, 2]; +// eslint-disable-next-line @typescript-eslint/restrict-template-expressions +const msg1 = `arg1 = ${arg1}`; diff --git a/examples/typescript-type-checked/tsconfig.json b/examples/typescript-type-checked/tsconfig.json new file mode 100644 index 00000000..98bb0d2f --- /dev/null +++ b/examples/typescript-type-checked/tsconfig.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Node 18", + "compilerOptions": { + "lib": ["es2023"], + "module": "ESNext", + "moduleResolution": "Node10", + "target": "es2022", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true + } +} diff --git a/examples/typescript-with-type/.eslintrc.json b/examples/typescript-with-type/.eslintrc.json deleted file mode 100644 index f0cf724a..00000000 --- a/examples/typescript-with-type/.eslintrc.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "root": true, - "extends": ["../../es2022.js", "../../+typescript-with-type.js"] -} diff --git a/examples/typescript-with-type/index.ts b/examples/typescript-with-type/index.ts deleted file mode 100644 index 3f1dc0c9..00000000 --- a/examples/typescript-with-type/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -// no-unnecessary-type-assertion -const foo = 3 as number; -console.log(foo); diff --git a/examples/typescript-with-type/tsconfig.json b/examples/typescript-with-type/tsconfig.json deleted file mode 100644 index ec795e83..00000000 --- a/examples/typescript-with-type/tsconfig.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "compilerOptions": { - /* Basic Options */ - "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ - // "lib": [], /* Specify library files to be included in the compilation. */ - // "allowJs": true, /* Allow javascript files to be compiled. */ - // "checkJs": true, /* Report errors in .js files. */ - // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ - // "declaration": true, /* Generates corresponding '.d.ts' file. */ - // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - // "sourceMap": true, /* Generates corresponding '.map' file. */ - // "outFile": "./", /* Concatenate and emit output to single file. */ - // "outDir": "./", /* Redirect output structure to the directory. */ - // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - // "composite": true, /* Enable project compilation */ - // "incremental": true, /* Enable incremental compilation */ - // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ - // "removeComments": true, /* Do not emit comments to output. */ - // "noEmit": true, /* Do not emit outputs. */ - // "importHelpers": true, /* Import emit helpers from 'tslib'. */ - // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ - // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ - - /* Strict Type-Checking Options */ - "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* Enable strict null checks. */ - // "strictFunctionTypes": true, /* Enable strict checking of function types. */ - // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - - /* Additional Checks */ - // "noUnusedLocals": true, /* Report errors on unused locals. */ - // "noUnusedParameters": true, /* Report errors on unused parameters. */ - // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - - /* Module Resolution Options */ - // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ - // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ - // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - // "typeRoots": [], /* List of folders to include type definitions from. */ - // "types": [], /* Type declaration files to be included in compilation. */ - // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ - // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - - /* Source Map Options */ - // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - - /* Experimental Options */ - // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ - // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ - } -} diff --git a/examples/typescript/.eslintrc.json b/examples/typescript/.eslintrc.json deleted file mode 100644 index 4d9f5a22..00000000 --- a/examples/typescript/.eslintrc.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "root": true, - "extends": ["../../node-v18.js", "../../+typescript.js"] -} diff --git a/examples/typescript/cjs.ts b/examples/typescript/cjs.ts new file mode 100644 index 00000000..6825d7e7 --- /dev/null +++ b/examples/typescript/cjs.ts @@ -0,0 +1,45 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/** + * @fileoverview A TypeScript file in type:commonjs and module:commonjs + */ + +// (faux) import is available +import fs from "node:fs"; +import type { Foo } from "./mod"; +import { Bar, Baz } from "./mod"; + +// import/require() is available +import mod = require("./mod"); + +// global `require()` is available +require("node:assert"); + +// module-base is loaded +// eslint-disable-next-line import/no-self-import +import("./cjs"); + +// configs/base is loaded +const x = 0; +// eslint-disable-next-line no-compare-neg-zero +if (x === -0) { + x.toString(); +} + +// configs/node20 is loaded +// eslint-disable-next-line n/no-deprecated-api +fs.exists("./foo", () => {}); + +// configs/typescript is loaded +// eslint-disable-next-line @typescript-eslint/no-empty-interface +interface Empty {} + +/* eslint-disable jsdoc/check-param-names */ +/** + * @param {T} notFoo + * @return {T} + * @template T + */ +function id(foo: T): T { + return foo; +} +/* eslint-enable jsdoc/check-param-names */ diff --git a/examples/typescript/eslint.config.js b/examples/typescript/eslint.config.js new file mode 100644 index 00000000..7adac4ac --- /dev/null +++ b/examples/typescript/eslint.config.js @@ -0,0 +1,3 @@ +"use strict"; + +module.exports = import("./eslint.config.mjs").then((ns) => ns.default); diff --git a/examples/typescript/eslint.config.mjs b/examples/typescript/eslint.config.mjs new file mode 100644 index 00000000..0e46f0e4 --- /dev/null +++ b/examples/typescript/eslint.config.mjs @@ -0,0 +1,11 @@ +import { build } from "../../dist/index.js"; + +const configs = await build({ base: "node20", typescript: true }); + +/** @type { import("eslint").Linter.FlatConfig[] } */ +export default [ + ...configs, + { + ignores: ["eslint.config.{js,mjs}", "dist"], + }, +]; diff --git a/examples/typescript/esm.mts b/examples/typescript/esm.mts deleted file mode 100644 index d580028a..00000000 --- a/examples/typescript/esm.mts +++ /dev/null @@ -1,17 +0,0 @@ -import assert from 'node:assert'; - -export const assertOk = assert.ok; - -// module-base -// eslint-disable-next-line import/no-self-import -import('./esm.mjs'); - -// module-globals -// eslint-disable-next-line unicorn/prefer-module -require('node:assert'); -// eslint-disable-next-line unicorn/prefer-module -console.log(__filename); -// eslint-disable-next-line unicorn/prefer-module -exports.foo = 1; -// eslint-disable-next-line unicorn/prefer-module -module.exports = 1; diff --git a/examples/typescript/index.ts b/examples/typescript/index.ts deleted file mode 100644 index 595ad296..00000000 --- a/examples/typescript/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type {Foo} from './mod'; -import {Bar, Baz} from './mod'; -import Bar2 = require('./mod2'); - -type Three = 3; -// A recommended rule -// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -const three = 3; - -const foo: Foo = { - foo: '', -}; -new Bar(); -new Baz(); -new Bar2.Bar(); -console.log(three, foo); - -// module-base is loaded -// eslint-disable-next-line import/no-self-import -import('./index'); - -// module-globals is not loaded -require('node:assert'); diff --git a/examples/typescript/mod.ts b/examples/typescript/mod.ts index 499f23cf..7b96a0c5 100644 --- a/examples/typescript/mod.ts +++ b/examples/typescript/mod.ts @@ -1,5 +1,5 @@ export interface Foo { - foo: string + foo: string; } export class Bar {} export class Baz {} diff --git a/examples/typescript-module/package.json b/examples/typescript/package.json similarity index 92% rename from examples/typescript-module/package.json rename to examples/typescript/package.json index c9a44226..5bbefffb 100644 --- a/examples/typescript-module/package.json +++ b/examples/typescript/package.json @@ -1,3 +1,3 @@ { "type": "commonjs" -} \ No newline at end of file +} diff --git a/examples/typescript/tsconfig.json b/examples/typescript/tsconfig.json new file mode 100644 index 00000000..5f998a2b --- /dev/null +++ b/examples/typescript/tsconfig.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Node 18", + "compilerOptions": { + "lib": ["es2023"], + "module": "CommonJS", + "moduleResolution": "Node10", + "target": "es2022", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true + } +} diff --git a/lib/es2015.js b/lib/es2015.js deleted file mode 100644 index 0587cc3b..00000000 --- a/lib/es2015.js +++ /dev/null @@ -1,52 +0,0 @@ -"use strict"; - -module.exports = { - env: { - es2015: true, - }, - parserOptions: { - ecmaVersion: 2015, - }, - plugins: ["unicorn"], - rules: { - // ## ECMAScript 6 - // These rules are only relevant to ES6 environments. - "arrow-body-style": 2, - "arrow-parens": [2, "as-needed"], - "arrow-spacing": 2, - "generator-star-spacing": [2, { named: "after", anonymous: "neither", method: "neither" }], - "no-async-promise-executor": 2, - "no-confusing-arrow": [2, { onlyOneSimpleParam: true }], - // use "import/no-duplicates" instead - // "no-duplicate-imports": 2, - "no-import-assign": 2, - "no-misleading-character-class": 2, - "no-useless-computed-key": 2, - "no-useless-rename": 2, - "no-var": 2, - "object-shorthand": [2, "methods"], - "prefer-arrow-callback": [2, { allowNamedFunctions: true }], - "prefer-const": 2, - "prefer-destructuring": [2, { object: true, array: false }], - "prefer-numeric-literals": 2, - "prefer-rest-params": 2, - "prefer-template": 2, - "require-atomic-updates": 2, - "rest-spread-spacing": 2, - "template-curly-spacing": 2, - "template-tag-spacing": 2, - "yield-star-spacing": [2, "after"], - - // # eslint-plugin-unicorn - // https://github.com/sindresorhus/eslint-plugin-unicorn - "unicorn/custom-error-definition": 2, - "unicorn/no-for-loop": 2, - "unicorn/no-typeof-undefined": 2, - "unicorn/no-useless-promise-resolve-reject": 2, - "unicorn/prefer-default-parameters": 2, - "unicorn/prefer-includes": 2, - "unicorn/prefer-number-properties": [2, { checkInfinity: false }], - "unicorn/prefer-math-trunc": 2, - "unicorn/prefer-string-starts-ends-with": 2, - }, -}; diff --git a/lib/es2016.js b/lib/es2016.js deleted file mode 100644 index 14c2042a..00000000 --- a/lib/es2016.js +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; - -module.exports = { - env: { - es2016: true, - }, - parserOptions: { - ecmaVersion: 2016, - }, - rules: { - "prefer-exponentiation-operator": 2, - }, -}; diff --git a/lib/es2017.js b/lib/es2017.js deleted file mode 100644 index 78150692..00000000 --- a/lib/es2017.js +++ /dev/null @@ -1,28 +0,0 @@ -"use strict"; - -module.exports = { - env: { - es2017: true, - }, - parserOptions: { - ecmaVersion: 2017, - }, - rules: { - // ## Possible Errors - // this is a useful case of `await` - // 'no-await-in-loop': 0, - - // ## Override - // ES2017 allows trailing comma in "functions" - "comma-dangle": [ - 2, - { - arrays: "always-multiline", - objects: "always-multiline", - imports: "always-multiline", - exports: "always-multiline", - functions: "always-multiline", - }, - ], - }, -}; diff --git a/lib/es2018.js b/lib/es2018.js deleted file mode 100644 index 37430d59..00000000 --- a/lib/es2018.js +++ /dev/null @@ -1,15 +0,0 @@ -"use strict"; - -module.exports = { - env: { - es2018: true, - }, - parserOptions: { - ecmaVersion: 2018, - }, - rules: { - "prefer-object-spread": 2, - "unicorn/no-useless-fallback-in-spread": 2, - "unicorn/no-useless-spread": 2, - }, -}; diff --git a/lib/es2019.js b/lib/es2019.js deleted file mode 100644 index dd6e84a0..00000000 --- a/lib/es2019.js +++ /dev/null @@ -1,15 +0,0 @@ -"use strict"; - -module.exports = { - env: { - es2019: true, - }, - parserOptions: { - ecmaVersion: 2019, - }, - rules: { - // Fixable rules - "unicorn/prefer-string-trim-start-end": 2, - "unicorn/prefer-optional-catch-binding": 2, - }, -}; diff --git a/lib/es2020.js b/lib/es2020.js deleted file mode 100644 index 6e2b4ad6..00000000 --- a/lib/es2020.js +++ /dev/null @@ -1,14 +0,0 @@ -"use strict"; - -module.exports = { - env: { - es2020: true, - }, - parserOptions: { - ecmaVersion: 2020, - }, - rules: { - "no-unsafe-optional-chaining": 2, - "unicorn/prefer-logical-operator-over-ternary": 2, - }, -}; diff --git a/lib/es2021-numeric-separators.js b/lib/es2021-numeric-separators.js deleted file mode 100644 index 86d61169..00000000 --- a/lib/es2021-numeric-separators.js +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; - -module.exports = { - parserOptions: { - ecmaVersion: 2021, - }, - rules: { - "unicorn/numeric-separators-style": [ - "error", - { onlyIfContainsSeparator: true, number: { minimumDigits: 0 } }, - ], - }, -}; diff --git a/lib/es2021.js b/lib/es2021.js deleted file mode 100644 index 0c461023..00000000 --- a/lib/es2021.js +++ /dev/null @@ -1,14 +0,0 @@ -"use strict"; - -module.exports = { - extends: ["./es2021-numeric-separators.js"], - env: { - es2021: true, - }, - parserOptions: { - ecmaVersion: 2021, - }, - rules: { - "unicorn/prefer-string-replace-all": 2, - }, -}; diff --git a/lib/es2022-class-fields.js b/lib/es2022-class-fields.js deleted file mode 100644 index c21279ea..00000000 --- a/lib/es2022-class-fields.js +++ /dev/null @@ -1,7 +0,0 @@ -"use strict"; - -module.exports = { - parserOptions: { - ecmaVersion: 2022, - }, -}; diff --git a/lib/es2022.js b/lib/es2022.js deleted file mode 100644 index 9fe374e1..00000000 --- a/lib/es2022.js +++ /dev/null @@ -1,15 +0,0 @@ -"use strict"; - -module.exports = { - extends: ["./es2022-class-fields.js"], - env: { - es2022: true, - }, - parserOptions: { - ecmaVersion: 2022, - }, - rules: { - "prefer-object-has-own": 2, - "unicorn/prefer-at": 2, - }, -}; diff --git a/lib/es2023.js b/lib/es2023.js deleted file mode 100644 index 37a82c22..00000000 --- a/lib/es2023.js +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; - -module.exports = { - env: { - // `es2023` is not available - // https://eslint.org/docs/latest/use/configure/language-options#specifying-environments - es2022: true, - }, - parserOptions: { - ecmaVersion: 2023, - }, - rules: {}, -}; diff --git a/lib/module-globals.js b/lib/module-globals.js deleted file mode 100644 index 23095ce4..00000000 --- a/lib/module-globals.js +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; - -/** - * @fileoverview Disable globals unavailable in Node ESM - */ - -/** @type {import("@typescript-eslint/utils").TSESLint.Linter.Config} */ -const config = { - rules: { - "unicorn/prefer-module": 2, - }, -}; -module.exports = config; diff --git a/lib/node.js b/lib/node.js deleted file mode 100644 index f39416c7..00000000 --- a/lib/node.js +++ /dev/null @@ -1,41 +0,0 @@ -"use strict"; - -/** @type {import("@typescript-eslint/utils").TSESLint.Linter.Config} */ -module.exports = { - plugins: ["n"], - env: { - node: true, - }, - rules: { - // ## eslint-plugin-n - "n/handle-callback-err": 2, - "n/no-deprecated-api": 2, - "n/no-extraneous-import": 2, - "n/no-extraneous-require": 2, - // use "import/no-unresolved" instead - // "n/no-missing-import": 2, - "n/no-missing-require": 2, - "n/no-new-require": 2, - "n/no-unpublished-bin": 2, - "n/no-unpublished-import": 2, - "n/no-unpublished-require": 2, - "n/no-unsupported-features/es-builtins": 2, - "n/no-unsupported-features/es-syntax": 2, - "n/no-unsupported-features/node-builtins": 2, - "n/process-exit-as-throw": 2, - "n/shebang": 2, - - // ## eslint-plugin-unicorn - "unicorn/no-process-exit": 2, - "unicorn/prefer-node-protocol": 2, - }, - overrides: [ - { - files: ["*.mjs"], - extends: ["./module-js.js"], - settings: { - "import/extensions": [".mjs"], - }, - }, - ], -}; diff --git a/lib/ts-extensions.js b/lib/ts-extensions.js deleted file mode 100644 index d8b4cb66..00000000 --- a/lib/ts-extensions.js +++ /dev/null @@ -1,4 +0,0 @@ -"use strict"; - -const TS_EXTENSIONS = [".ts", ".tsx", ".mts", ".cts"]; -module.exports = TS_EXTENSIONS; diff --git a/node-v18.js b/node-v18.js deleted file mode 100644 index 0162affa..00000000 --- a/node-v18.js +++ /dev/null @@ -1,27 +0,0 @@ -"use strict"; - -// LTS 'Hydrogen' from v18.12.0 -// Maintenance Start from v18.1x.0 -const version = ">=18.15.0"; - -module.exports = { - extends: [ - "./lib/base.js", - "./lib/node.js", - "./lib/es2015.js", - "./lib/es2016.js", - "./lib/es2017.js", - "./lib/es2018.js", - "./lib/es2019.js", - "./lib/es2020.js", - "./lib/es2021.js", - "./lib/es2022.js", - "./lib/es2023.js", - ], - rules: { - // eslint-plugin-n has not been updated for Node v18 - "n/no-unsupported-features/es-builtins": [0, { version }], - "n/no-unsupported-features/es-syntax": [0, { version }], - "n/no-unsupported-features/node-builtins": [0, { version }], - }, -}; diff --git a/node-v20.js b/node-v20.js deleted file mode 100644 index d96d4d02..00000000 --- a/node-v20.js +++ /dev/null @@ -1,26 +0,0 @@ -"use strict"; - -// LTS 'Iodine' from v20.x.0 -const version = ">=20.5.0"; - -module.exports = { - extends: [ - "./lib/base.js", - "./lib/node.js", - "./lib/es2015.js", - "./lib/es2016.js", - "./lib/es2017.js", - "./lib/es2018.js", - "./lib/es2019.js", - "./lib/es2020.js", - "./lib/es2021.js", - "./lib/es2022.js", - "./lib/es2023.js", - ], - rules: { - // eslint-plugin-n has not been updated for Node v20 - "n/no-unsupported-features/es-builtins": [0, { version }], - "n/no-unsupported-features/es-syntax": [0, { version }], - "n/no-unsupported-features/node-builtins": [0, { version }], - }, -}; diff --git a/package-lock.json b/package-lock.json index fe1f1e79..90ef725e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,19 +9,27 @@ "version": "18.0.0", "license": "MIT", "dependencies": { + "@eslint/js": "^8.49.0", + "@types/eslint": "^8.44.2", "@typescript-eslint/eslint-plugin": "^6.7.3", "@typescript-eslint/parser": "^6.7.3", - "eslint-config-prettier": "^9.0.0", + "deepmerge": "^4.3.1", "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-import": "^2.28.1", "eslint-plugin-jsdoc": "^46.8.2", "eslint-plugin-n": "^16.1.0", - "eslint-plugin-unicorn": "^48.0.1" + "eslint-plugin-unicorn": "^48.0.1", + "globals": "^13.21.0" + }, + "bin": { + "eslint-config-teppeis": "bin/cli.js" }, "devDependencies": { "@types/node": "^18.18.0", + "@types/react": "^18.2.22", "eslint": "^8.50.0", + "eslint-config-prettier": "^9.0.0", "glob": "^10.3.10", "mocha": "^10.2.0", "npm-run-all": "^4.1.5", @@ -34,6 +42,11 @@ "peerDependencies": { "eslint": "^8.50.0", "prettier": "^3.0.3" + }, + "peerDependenciesMeta": { + "prettier": { + "optional": true + } } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -386,10 +399,24 @@ "node": ">=14" } }, + "node_modules/@types/eslint": { + "version": "8.44.2", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.2.tgz", + "integrity": "sha512-sdPRb9K6iL5XZOmBubg8yiFp5yS/JdUDQsq5e6h95km91MCYMuvp7mh1fjPEYUhvHepKpZOjnEaMBR4PxjWDzg==", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", + "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==" + }, "node_modules/@types/json-schema": { - "version": "7.0.13", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", - "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==" + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==" }, "node_modules/@types/json5": { "version": "0.0.29", @@ -407,6 +434,29 @@ "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==" }, + "node_modules/@types/prop-types": { + "version": "15.7.6", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.6.tgz", + "integrity": "sha512-RK/kBbYOQQHLYj9Z95eh7S6t7gq4Ojt/NT8HTk8bWVhA5DaF+5SMnxHKkP4gPNN3wAZkKP+VjAf0ebtYzf+fxg==", + "dev": true + }, + "node_modules/@types/react": { + "version": "18.2.22", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.22.tgz", + "integrity": "sha512-60fLTOLqzarLED2O3UQImc/lsNRgG0jE/a1mPW9KjMemY0LMITWEsbS4VvZ4p6rorEHd5YKxxmMKSDK505GHpA==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", + "dev": true + }, "node_modules/@types/semver": { "version": "7.5.3", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.3.tgz", @@ -1072,6 +1122,12 @@ "node": ">= 8" } }, + "node_modules/csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", + "dev": true + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -1105,6 +1161,14 @@ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/define-properties": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", @@ -1349,6 +1413,7 @@ "version": "9.0.0", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", + "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, diff --git a/package.json b/package.json index 64042876..fb1c5056 100644 --- a/package.json +++ b/package.json @@ -6,35 +6,48 @@ "engines": { "node": "^18.17.0 || >=20.5.0" }, - "main": "es2023.js", + "type": "module", + "main": "dist/index.js", "files": [ - "*.js", - "lib" + "bin", + "dist", + "templates" ], + "bin": { + "eslint-config-teppeis": "bin/cli.js" + }, "scripts": { + "build": "tsc", + "clean": "rm -rf dist", "lint": "run-p -l -c --aggregate-output lint:*", - "lint:eslint": "eslint .", + "lint:eslint": "eslint --max-warnings 0 .", "lint:prettier": "prettier --check .", "fix": "run-s fix:prettier fix:eslint", "fix:eslint": "npm run lint:eslint -- --fix", "fix:prettier": "npm run lint:prettier -- --write", - "test": "run-p -l -c --aggregate-output lint:* unit", - "unit": "mocha test" + "test": "npm-run-all -l -c --aggregate-output clean build -p lint:* test:*", + "test:examples": "./examples/lint-all.sh", + "test:unit": "mocha test" }, "dependencies": { + "@eslint/js": "^8.49.0", + "@types/eslint": "^8.44.2", "@typescript-eslint/eslint-plugin": "^6.7.3", "@typescript-eslint/parser": "^6.7.3", - "eslint-config-prettier": "^9.0.0", + "deepmerge": "^4.3.1", "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-import": "^2.28.1", "eslint-plugin-jsdoc": "^46.8.2", "eslint-plugin-n": "^16.1.0", - "eslint-plugin-unicorn": "^48.0.1" + "eslint-plugin-unicorn": "^48.0.1", + "globals": "^13.21.0" }, "devDependencies": { "@types/node": "^18.18.0", + "@types/react": "^18.2.22", "eslint": "^8.50.0", + "eslint-config-prettier": "^9.0.0", "glob": "^10.3.10", "mocha": "^10.2.0", "npm-run-all": "^4.1.5", @@ -45,6 +58,11 @@ "eslint": "^8.50.0", "prettier": "^3.0.3" }, + "peerDependenciesMeta": { + "prettier": { + "optional": true + } + }, "homepage": "https://github.com/teppeis/eslint-config-teppeis", "repository": "https://github.com/teppeis/eslint-config-teppeis", "bugs": { @@ -53,7 +71,8 @@ "keywords": [ "eslint", "eslint-config", - "prettier" + "prettier", + "typescript" ], "license": "MIT" } diff --git a/src/build.ts b/src/build.ts new file mode 100644 index 00000000..100e0b0c --- /dev/null +++ b/src/build.ts @@ -0,0 +1,88 @@ +import type { Linter } from "eslint"; +import { jsEsm } from "./configs/js-esm.js"; +import { merge } from "./merge.js"; +import { nonNull } from "./utils.js"; + +interface BuildOptions { + base: "es2021" | "es2022" | "es2023" | "node18" | "node20"; + esm?: boolean; + typescript?: boolean; + project?: boolean | string | string[]; +} + +export async function build( + options: BuildOptions, + ...additionalConfigs: Linter.FlatConfig[] +): Promise { + const { base, typescript, project, esm } = options; + + const baseConfigNames = ["es2021", "es2022", "es2023", "node18", "node20"]; + if (!baseConfigNames.includes(base)) { + throw new TypeError(`Unexpected base: ${base}`); + } + const baseConfig = (await import(`./configs/${base}.js`))[base]; + + let nodeEsmConfig = {}; + const nodeConfigNames = ["node18", "node20"]; + if (nodeConfigNames.includes(base)) { + const { nodeEsm } = await import("./configs/node-esm.js"); + nodeEsmConfig = nodeEsm; + } + + let tsConfig; + if (typescript) { + if (project) { + if ( + typeof project === "boolean" || + typeof project === "string" || + Array.isArray(project) + ) { + tsConfig = (await import("./configs/typescript-type-checked.js")) + .typescriptTypeChecked; + nonNull(nonNull(tsConfig.languageOptions).parserOptions).project = + project; + } else { + throw new TypeError(`project is unexpected: ${project}`); + } + } else { + tsConfig = (await import("./configs/typescript.js")).typescript; + } + } else if (project) { + throw new TypeError("Specify both `typescript` and `project`."); + } + + let cjsExtensions = "**/*.{js,jsx,cjs}"; + let esmExtensions = "**/*.mjs"; + let cjsTsExtensions = "**/*.{ts,tsx,cts}"; + let esmTsExtensions = "**/*.mts"; + if (esm) { + cjsExtensions = "**/*.cjs"; + esmExtensions = "**/*.{js,jsx,mjs}"; + cjsTsExtensions = "**/*.cts"; + esmTsExtensions = "**/*.{ts,tsx,mts}"; + } + const configArray = [ + { + ...baseConfig, + files: [cjsExtensions], + }, + { + ...merge(baseConfig, jsEsm, nodeEsmConfig), + files: [esmExtensions], + }, + ]; + if (tsConfig) { + configArray.push( + { + ...merge(baseConfig, tsConfig), + files: [cjsTsExtensions], + }, + { + ...merge(baseConfig, nodeEsmConfig, tsConfig), + files: [esmTsExtensions], + }, + ); + } + configArray.push(...additionalConfigs); + return configArray; +} diff --git a/src/cli.ts b/src/cli.ts new file mode 100755 index 00000000..389b03d5 --- /dev/null +++ b/src/cli.ts @@ -0,0 +1,59 @@ +import fs from "node:fs"; +import path from "node:path"; +import { parseArgs } from "node:util"; + +export function run() { + // parse args and only accept `--init` option + const { values } = parseArgs({ + options: { init: { type: "boolean" } }, + }); + if (!values.init) { + console.log("Usage: eslint-config-teppeis --init"); + // eslint-disable-next-line unicorn/no-process-exit + process.exit(1); + } + + const { type } = findUpPackageJson(); + const isTypeEsm = type === "module"; + const templateFile = isTypeEsm + ? "eslint.config-esm.mjs" + : "eslint.config.mjs"; + const templateUrl = resolveUrl(`../templates/${templateFile}`); + const cjsProxyUrl = resolveUrl(`../templates/eslint.config.cjs`); + + if (fs.existsSync("eslint.config.js")) { + throw new Error("eslint.config.js already exists."); + } + + if (fs.existsSync("eslint.config.mjs")) { + throw new Error("eslint.config.mjs already exists."); + } + + if (isTypeEsm) { + fs.copyFileSync(templateUrl, "eslint.config.js"); + console.log("create: eslint.config.js"); + } else { + fs.copyFileSync(cjsProxyUrl, "eslint.config.js"); + console.log("create: eslint.config.js (for CJS Proxy)"); + fs.copyFileSync(templateUrl, "eslint.config.mjs"); + console.log("create: eslint.config.mjs"); + } +} + +function findUpPackageJson(): any { + let dir = process.cwd(); + const { root } = path.parse(dir); + while (dir !== root) { + const pkgPath = path.join(dir, "package.json"); + if (fs.existsSync(pkgPath)) { + const pkg = fs.readFileSync(pkgPath, "utf8"); + return JSON.parse(pkg); + } + dir = path.dirname(dir); + } + throw new Error("package.json not found"); +} + +function resolveUrl(specifier: string): URL { + return new URL(specifier, import.meta.url); +} diff --git a/lib/base.js b/src/configs/base.ts similarity index 59% rename from lib/base.js rename to src/configs/base.ts index 898668c1..89aeb8c1 100644 --- a/lib/base.js +++ b/src/configs/base.ts @@ -1,30 +1,62 @@ -"use strict"; +import js from "@eslint/js"; +import type { Linter } from "eslint"; +import comments from "eslint-plugin-eslint-comments"; +import jsdoc from "eslint-plugin-jsdoc"; +import unicorn from "eslint-plugin-unicorn"; +import { merge } from "../merge.js"; -module.exports = { - extends: ["eslint:recommended", "plugin:eslint-comments/recommended"], - plugins: ["unicorn", "jsdoc"], +export const base: Linter.FlatConfig = merge(js.configs.recommended, { + languageOptions: { + sourceType: "script", + ecmaVersion: 2020, + }, + plugins: { + // Be careful when removing these plugins! + // They may be referenced by other config files. + unicorn, + jsdoc, + "eslint-comments": comments, + }, + linterOptions: { + // replace "eslint-comments/no-unused-disable" but warn only + reportUnusedDisableDirectives: true, + }, rules: { - // ## Possible Errors + ...comments.configs.recommended.rules, + + // ## Possible Problems + "array-callback-return": 2, + "no-async-promise-executor": 2, + // this is a useful case of `await` + // 'no-await-in-loop': 0, // overwrite recommended: allow `while (true)` "no-constant-condition": [2, { checkLoops: false }], // overwrite recommended: allow `try {foo();} catch (e) {}` "no-empty": [2, { allowEmptyCatch: true }], + // use "import/no-duplicates" instead + // "no-duplicate-imports": 2, + "no-import-assign": 2, + "no-misleading-character-class": 2, + "no-self-compare": 2, + "no-unmodified-loop-condition": 2, // overwrite recommended: disallow `if (! a < b) {}` "no-unsafe-negation": [2, { enforceForOrderingRelations: true }], + "no-unsafe-optional-chaining": 2, + // overwrite recommended: allow args + "no-unused-vars": [2, { args: "none" }], + "require-atomic-updates": 2, // overwrite recommended: disallow `array.indexOf(NaN)` "use-isnan": [2, { enforceForIndexOf: true }], - // ## Best Practices - // These are rules designed to prevent you from making mistakes. - // They either prescribe a better way of doing something or help you avoid footguns. - "array-callback-return": 2, + // ## Suggestions + "arrow-body-style": 2, // prefer `let` or `const` "block-scoped-var": 2, - curly: [2, "multi-line", "consistent"], "default-case": 2, // not for Closure "dot-notation": 2, eqeqeq: [2, "smart"], + "no-array-constructor": 2, "no-caller": 2, "no-eval": 2, // don't touch native prototype! @@ -36,126 +68,97 @@ module.exports = { "no-implied-eval": 2, "no-invalid-this": 2, "no-iterator": 2, + "no-label-var": 2, "no-labels": 2, "no-lone-blocks": 2, "no-loop-func": 2, - "no-multi-spaces": 2, "no-multi-str": 2, "no-new-func": 2, + "no-new-object": 2, "no-new-wrappers": 2, "no-octal-escape": 2, // want to warn only props... // "no-param-reassign": [2, {"props": true}], "no-proto": 2, "no-return-assign": 2, - "no-self-compare": 2, "no-sequences": 2, "no-throw-literal": 2, - "no-unmodified-loop-condition": 2, + "no-undef-init": 2, + // prevented by `no-global-assign` and `no-shadow-restricted-names` + // 'no-undefined': 2, + "no-unneeded-ternary": 2, "no-unused-expressions": [ 2, - { allowShortCircuit: true, allowTernary: true, allowTaggedTemplates: true }, + { + allowShortCircuit: true, + allowTernary: true, + allowTaggedTemplates: true, + }, ], "no-useless-call": 2, + "no-useless-computed-key": 2, "no-useless-concat": 2, + "no-useless-rename": 2, "no-useless-return": 2, + "no-var": 2, "no-void": 2, + "object-shorthand": [2, "methods"], + "operator-assignment": [2, "always"], + "prefer-arrow-callback": [2, { allowNamedFunctions: true }], + "prefer-const": 2, + "prefer-destructuring": [2, { object: true, array: false }], + "prefer-exponentiation-operator": 2, + "prefer-numeric-literals": 2, + "prefer-object-spread": 2, + "prefer-rest-params": 2, + "prefer-template": 2, radix: 2, // => es2017 // "require-await": 2, - yoda: [2, "never", { onlyEquality: true }], - - // ## Strict Mode - // These rules relate to using strict mode and strict mode directives. strict: [2, "global"], + yoda: [2, "never", { onlyEquality: true }], - // ## Variables - // These rules have to do with variable declarations. - "no-label-var": 2, - "no-undef-init": 2, - // prevented by `no-global-assign` and `no-shadow-restricted-names`. - // 'no-undefined': 2, - // overwrite recommended: allow args - "no-unused-vars": [2, { args: "none" }], - - // ## Stylistic Issues - // These rules are purely matters of style and are quite subjective. - "array-bracket-spacing": [2, "never"], - "block-spacing": [2, "always"], - "brace-style": [2, "1tbs", { allowSingleLine: true }], - "comma-dangle": [2, "always-multiline"], - "comma-spacing": [2, { after: true, before: false }], - "comma-style": [2, "last", { exceptions: { NewExpression: false } }], - "computed-property-spacing": [2, "never"], - "eol-last": 2, - "func-call-spacing": 2, - indent: [2, 2, { SwitchCase: 1 }], - "jsx-quotes": 2, - "key-spacing": 2, - "keyword-spacing": [2, { after: true, before: true }], - "linebreak-style": [2, "unix"], - "new-parens": 2, - "no-array-constructor": 2, - "no-multiple-empty-lines": [2, { max: 2, maxBOF: 0, maxEOF: 0 }], - "no-new-object": 2, - "no-tabs": 2, - "no-trailing-spaces": 2, - "no-unneeded-ternary": 2, - "no-whitespace-before-property": 2, - "nonblock-statement-body-position": 0, - "object-curly-spacing": [2, "never"], - "object-curly-newline": 2, - "operator-assignment": [2, "always"], - "operator-linebreak": [2, "after"], - "padded-blocks": [2, "never"], - "padding-line-between-statements": [ - 2, - { blankLine: "always", prev: "directive", next: "*" }, - { blankLine: "any", prev: "directive", next: "directive" }, - ], - "quote-props": [2, "as-needed", { unnecessary: false }], - quotes: [2, "single", { avoidEscape: true, allowTemplateLiterals: true }], - semi: 2, - "semi-spacing": [ - 2, - { - after: true, - before: false, - }, - ], - "semi-style": 2, - "space-before-blocks": 2, - "space-before-function-paren": [ - 2, - { anonymous: "never", named: "never", asyncArrow: "always" }, - ], - "space-in-parens": [2, "never"], - "space-infix-ops": 2, - "space-unary-ops": 2, - "spaced-comment": [2, "always", { block: { balanced: true } }], - "switch-colon-spacing": 2, + // ## Layout & Formatting "unicode-bom": 2, + // # prettier + // recommended rules but conflict with prettier + // https://github.com/prettier/eslint-config-prettier + "no-unexpected-multiline": 0, + "no-extra-semi": 0, + "no-mixed-spaces-and-tabs": 0, + // # eslint-plugin-eslint-comments // https://github.com/mysticatea/eslint-plugin-eslint-comments "eslint-comments/disable-enable-pair": [2, { allowWholeFile: true }], // overwrite recommended "eslint-comments/no-unlimited-disable": 0, - "eslint-comments/no-unused-disable": 2, // # eslint-plugin-unicorn // https://github.com/sindresorhus/eslint-plugin-unicorn - "unicorn/no-instanceof-array": 2, + "unicorn/custom-error-definition": 2, "unicorn/escape-case": 2, "unicorn/new-for-builtins": 2, + "unicorn/no-for-loop": 2, "unicorn/no-hex-escape": 2, - "unicorn/no-nested-ternary": 2, - "unicorn/prefer-negative-index": 2, - "unicorn/number-literal-case": 2, + "unicorn/no-instanceof-array": 2, + "unicorn/no-typeof-undefined": 2, + "unicorn/no-useless-fallback-in-spread": 2, + "unicorn/no-useless-promise-resolve-reject": 2, + "unicorn/no-useless-spread": 2, "unicorn/prefer-array-index-of": 2, "unicorn/prefer-date-now": 2, + "unicorn/prefer-default-parameters": 2, + "unicorn/prefer-includes": 2, + "unicorn/prefer-logical-operator-over-ternary": 2, + "unicorn/prefer-math-trunc": 2, + "unicorn/prefer-negative-index": 2, + "unicorn/prefer-number-properties": [2, { checkInfinity: false }], + "unicorn/prefer-optional-catch-binding": 2, "unicorn/prefer-regexp-test": 2, "unicorn/prefer-string-slice": 2, + "unicorn/prefer-string-starts-ends-with": 2, + "unicorn/prefer-string-trim-start-end": 2, "unicorn/prefer-type-error": 2, // # eslint-plugin-jsdoc @@ -190,4 +193,4 @@ module.exports = { }, }, }, -}; +}); diff --git a/+browser.js b/src/configs/browser.ts similarity index 52% rename from +browser.js rename to src/configs/browser.ts index 11ef4dcb..2c98490c 100644 --- a/+browser.js +++ b/src/configs/browser.ts @@ -1,9 +1,15 @@ -"use strict"; +import type { Linter } from "eslint"; +import unicorn from "eslint-plugin-unicorn"; +import globals from "globals"; -module.exports = { - env: { - browser: true, +export const browser: Linter.FlatConfig = { + files: ["**/*.{js,cjs,mjs,jsx,ts,tsx,cts,mts}"], + languageOptions: { + globals: { + ...globals.browser, + }, }, + plugins: { unicorn }, rules: { "unicorn/prefer-blob-reading-methods": 2, "unicorn/prefer-dom-node-append": 2, diff --git a/src/configs/es2021.ts b/src/configs/es2021.ts new file mode 100644 index 00000000..eac8fe84 --- /dev/null +++ b/src/configs/es2021.ts @@ -0,0 +1,23 @@ +import type { Linter } from "eslint"; +import globals from "globals"; +import { merge } from "../merge.js"; +import { base } from "./base.js"; + +export const es2021: Linter.FlatConfig = merge(base, { + languageOptions: { + parserOptions: { + // NOTE: ES2021 doesn't support top-level await. + ecmaVersion: 2021, + }, + globals: { + ...globals.es2021, + }, + }, + rules: { + "unicorn/numeric-separators-style": [ + "error", + { onlyIfContainsSeparator: true, number: { minimumDigits: 0 } }, + ], + "unicorn/prefer-string-replace-all": 2, + }, +}); diff --git a/src/configs/es2022.ts b/src/configs/es2022.ts new file mode 100644 index 00000000..b5f7c85a --- /dev/null +++ b/src/configs/es2022.ts @@ -0,0 +1,16 @@ +import type { Linter } from "eslint"; +import { merge } from "../merge.js"; +import { es2021 } from "./es2021.js"; + +export const es2022: Linter.FlatConfig = merge(es2021, { + languageOptions: { + parserOptions: { + ecmaVersion: 2022, + }, + // no new globals + }, + rules: { + "prefer-object-has-own": 2, + "unicorn/prefer-at": 2, + }, +}); diff --git a/src/configs/es2023.ts b/src/configs/es2023.ts new file mode 100644 index 00000000..e958c8da --- /dev/null +++ b/src/configs/es2023.ts @@ -0,0 +1,13 @@ +import type { Linter } from "eslint"; +import { merge } from "../merge.js"; +import { es2022 } from "./es2022.js"; + +export const es2023: Linter.FlatConfig = merge(es2022, { + languageOptions: { + parserOptions: { + ecmaVersion: 2023, + }, + // no new globals + }, + rules: {}, +}); diff --git a/lib/module-js.js b/src/configs/js-esm.ts similarity index 55% rename from lib/module-js.js rename to src/configs/js-esm.ts index d498baae..c8c1390f 100644 --- a/lib/module-js.js +++ b/src/configs/js-esm.ts @@ -1,25 +1,29 @@ -"use strict"; - /** * @fileoverview Config for Node ESM in JS (not TS, tsc checks them instead) */ +import type { Linter } from "eslint"; +import { merge } from "../merge.js"; +import { moduleBase } from "./module-base.js"; -/** @type {import("@typescript-eslint/utils").TSESLint.Linter.Config} */ -module.exports = { - extends: ["./module-base.js", "./module-globals.js"], +export const jsEsm: Linter.FlatConfig = merge(moduleBase, { rules: { - // ** Static analysis ** - "import/default": 2, - "import/named": 2, - "import/namespace": 2, - "import/no-unresolved": [2, { caseSensitiveStrict: true }], - // ** Helpful warnings ** "import/export": 2, "import/no-named-as-default": 2, // "import/no-named-as-default-member": 2, + // ** Static analysis ** + "import/default": 2, + "import/named": 2, + "import/namespace": 2, + // ** Style guide ** "import/extensions": [2, "always"], }, -}; + settings: { + "import/extensions": [".js", ".mjs", ".jsx"], + "import/parsers": { + espree: [".js", ".cjs", ".mjs", ".jsx"], + }, + }, +}); diff --git a/src/configs/mocha.ts b/src/configs/mocha.ts new file mode 100644 index 00000000..204cc106 --- /dev/null +++ b/src/configs/mocha.ts @@ -0,0 +1,15 @@ +import type { Linter } from "eslint"; +import globals from "globals"; + +export const mocha: Linter.FlatConfig = { + files: ["**/test/**/*.{js,cjs,mjs,jsx,ts,tsx,cts,mts}"], + languageOptions: { + globals: { + ...globals.mocha, + }, + }, + rules: { + // allow `this.timeout(1000)` + "no-invalid-this": 0, + }, +}; diff --git a/lib/module-base.js b/src/configs/module-base.ts similarity index 61% rename from lib/module-base.js rename to src/configs/module-base.ts index cd14d231..a223362c 100644 --- a/lib/module-base.js +++ b/src/configs/module-base.ts @@ -1,29 +1,25 @@ -"use strict"; - /** * @fileoverview Base config for TS and Node ESM JS */ +import type { Linter } from "eslint"; +import importPlugin from "eslint-plugin-import"; -/** @type {import("@typescript-eslint/utils").TSESLint.Linter.Config} */ -module.exports = { - parserOptions: { +export const moduleBase: Linter.FlatConfig = { + languageOptions: { sourceType: "module", }, - plugins: ["import"], + plugins: { import: importPlugin }, rules: { // for both TypeScript and non-TypeScript rules + // ** Helpful warnings ** + "import/no-mutable-exports": 2, + // ** Static analysis ** "import/no-absolute-path": 2, - // for monorepo - "import/no-relative-packages": 2, "import/no-self-import": 2, "import/no-useless-path-segments": 2, - // ** Helpful warnings ** - "import/no-deprecated": 2, - "import/no-mutable-exports": 2, - // ** Style guide ** "import/first": 2, "import/newline-after-import": 2, @@ -31,7 +27,13 @@ module.exports = { "import/order": [ 2, { - groups: [["builtin", "external"], "internal", "index", "parent", "sibling"], + groups: [ + ["builtin", "external"], + "internal", + "index", + "parent", + "sibling", + ], }, ], }, diff --git a/src/configs/node-esm.ts b/src/configs/node-esm.ts new file mode 100644 index 00000000..f42f285d --- /dev/null +++ b/src/configs/node-esm.ts @@ -0,0 +1,15 @@ +/** + * @fileoverview Disable globals unavailable in Node ESM + */ +import type { Linter } from "eslint"; +import unicorn from "eslint-plugin-unicorn"; + +export const nodeEsm: Linter.FlatConfig = { + languageOptions: { + sourceType: "module", + }, + plugins: { unicorn }, + rules: { + "unicorn/prefer-module": 2, + }, +}; diff --git a/src/configs/node.ts b/src/configs/node.ts new file mode 100644 index 00000000..c2fc4ba8 --- /dev/null +++ b/src/configs/node.ts @@ -0,0 +1,52 @@ +import type { Linter } from "eslint"; +import n from "eslint-plugin-n"; +import unicorn from "eslint-plugin-unicorn"; +import globals from "globals"; + +export const node: Linter.FlatConfig = { + languageOptions: { + sourceType: "commonjs", + globals: { + ...globals.node, + }, + }, + plugins: { n, unicorn }, + rules: { + // ## eslint-plugin-n + "n/handle-callback-err": 2, + "n/no-deprecated-api": 2, + "n/no-extraneous-import": 2, + "n/no-extraneous-require": 2, + "n/no-missing-import": 2, + "n/no-missing-require": 2, + "n/no-new-require": 2, + "n/no-unpublished-bin": 2, + "n/no-unpublished-import": 2, + "n/no-unpublished-require": 2, + // "n/no-unsupported-features/es-builtins": 2, + // "n/no-unsupported-features/es-syntax": 2, + // "n/no-unsupported-features/node-builtins": 2, + "n/process-exit-as-throw": 2, + "n/shebang": 2, + + // ## eslint-plugin-unicorn + "unicorn/no-process-exit": 2, + "unicorn/prefer-node-protocol": 2, + }, + settings: { + n: { + typescriptExtensionMap: [ + // Add for CJS (ex: `import "./foo"` -> `./foo.ts`). + // These are overridden and ignored in the TS to JS mapping. + [".ts", ""], + [".tsx", ""], + // Default mappings (for react non-preserve) + ["", ".js"], + [".ts", ".js"], + [".cts", ".cjs"], + [".mts", ".mjs"], + [".tsx", ".js"], + ], + }, + }, +}; diff --git a/src/configs/node18.ts b/src/configs/node18.ts new file mode 100644 index 00000000..e0579884 --- /dev/null +++ b/src/configs/node18.ts @@ -0,0 +1,6 @@ +import type { Linter } from "eslint"; +import { merge } from "../merge.js"; +import { es2023 } from "./es2023.js"; +import { node } from "./node.js"; + +export const node18: Linter.FlatConfig = merge(es2023, node); diff --git a/src/configs/node20.ts b/src/configs/node20.ts new file mode 100644 index 00000000..486988a4 --- /dev/null +++ b/src/configs/node20.ts @@ -0,0 +1,6 @@ +import type { Linter } from "eslint"; +import { merge } from "../merge.js"; +import { es2023 } from "./es2023.js"; +import { node } from "./node.js"; + +export const node20: Linter.FlatConfig = merge(es2023, node); diff --git a/src/configs/typescript-type-checked.ts b/src/configs/typescript-type-checked.ts new file mode 100644 index 00000000..d74d91fa --- /dev/null +++ b/src/configs/typescript-type-checked.ts @@ -0,0 +1,32 @@ +import type { Linter } from "eslint"; +import { merge } from "../merge.js"; +import { typescript } from "./typescript.js"; + +/** @type {import("eslint").Linter.FlatConfig} */ +export const typescriptTypeChecked: Linter.FlatConfig = merge(typescript, { + languageOptions: { + parserOptions: { + project: true, + }, + }, + rules: { + // Extend ESLint rules + "no-throw-literal": 0, + "@typescript-eslint/no-throw-literal": 2, + + "@typescript-eslint/await-thenable": 2, + "@typescript-eslint/consistent-type-exports": 2, + "@typescript-eslint/no-floating-promises": 2, + "@typescript-eslint/no-for-in-array": 2, + "@typescript-eslint/no-misused-promises": 2, + "@typescript-eslint/no-mixed-enums": 2, + "@typescript-eslint/no-unnecessary-condition": 2, + "@typescript-eslint/no-unnecessary-type-assertion": 2, + "@typescript-eslint/restrict-plus-operands": 2, + "@typescript-eslint/restrict-template-expressions": 2, + "@typescript-eslint/switch-exhaustiveness-check": 2, + // override with jest/unbound-method for testing + // https://github.com/jest-community/eslint-plugin-jest/blob/main/docs/rules/unbound-method.md + "@typescript-eslint/unbound-method": 2, + }, +}); diff --git a/src/configs/typescript.ts b/src/configs/typescript.ts new file mode 100644 index 00000000..6191b790 --- /dev/null +++ b/src/configs/typescript.ts @@ -0,0 +1,77 @@ +import tsEslintPlugin from "@typescript-eslint/eslint-plugin"; +import tsEslintParser from "@typescript-eslint/parser"; +import type { Linter } from "eslint"; +import { merge } from "../merge.js"; +import { nonNull } from "../utils.js"; +import { moduleBase } from "./module-base.js"; + +const tsEsEslintRecomRules = nonNull( + nonNull(tsEslintPlugin.configs["eslint-recommended"].overrides)[0].rules, +); + +export const typescript: Linter.FlatConfig = merge(moduleBase, { + files: ["**/*.{ts,tsx,mts,cts}"], + languageOptions: { + // @ts-expect-error TSESLint doesn't support flat config typings yet + parser: tsEslintParser, + parserOptions: { + sourceType: "module", + ecmaVersion: "latest", + }, + }, + plugins: { + // @ts-expect-error TSESLint doesn't support flat config typings yet + "@typescript-eslint": tsEslintPlugin, + }, + rules: { + ...tsEsEslintRecomRules, + ...tsEslintPlugin.configs.recommended.rules, + + // Allow special triple slashes comment: "/// " + "spaced-comment": [ + 2, + "always", + { line: { markers: ["/"] }, block: { balanced: true } }, + ], + + // Extend ESLint rules (enabled in base config) + // NOTE: skip extending stylistic rules that are overrided by prettier + "no-invalid-this": 0, + "@typescript-eslint/no-invalid-this": 2, + "no-loop-func": 0, + "@typescript-eslint/no-loop-func": 2, + "no-unused-expressions": 0, + "@typescript-eslint/no-unused-expressions": [ + 2, + { + allowShortCircuit: true, + allowTernary: true, + allowTaggedTemplates: true, + }, + ], + + // Override recommended rules + "@typescript-eslint/no-explicit-any": 0, + "@typescript-eslint/no-namespace": [2, { allowDeclarations: true }], + "@typescript-eslint/no-unused-vars": [2, { args: "none" }], + + // Stylistic rules + "@typescript-eslint/adjacent-overload-signatures": 2, + "@typescript-eslint/consistent-type-assertions": 2, + "@typescript-eslint/no-empty-interface": 2, + "@typescript-eslint/no-non-null-assertion": 2, + "@typescript-eslint/no-inferrable-types": 2, + + // Additional rules + "@typescript-eslint/consistent-type-imports": 2, + "@typescript-eslint/no-import-type-side-effects": 2, + // allow require for power-assert and verbatimModuleSyntax + // '@typescript-eslint/no-require-imports': 2, + "@typescript-eslint/prefer-literal-enum-member": 2, + "@typescript-eslint/prefer-ts-expect-error": 2, + }, + settings: { + // Don't enable rules that requires TypeScript parser for perf reasons. + "import/resolver": "typescript", + }, +}); diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 00000000..8f16a60d --- /dev/null +++ b/src/index.ts @@ -0,0 +1,11 @@ +export { build } from "./build.js"; +export { browser } from "./configs/browser.js"; +export { es2021 } from "./configs/es2021.js"; +export { es2022 } from "./configs/es2022.js"; +export { es2023 } from "./configs/es2023.js"; +export { mocha } from "./configs/mocha.js"; +export { node18 } from "./configs/node18.js"; +export { node20 } from "./configs/node20.js"; +export { typescriptTypeChecked } from "./configs/typescript-type-checked.js"; +export { typescript } from "./configs/typescript.js"; +export { merge } from "./merge.js"; diff --git a/src/merge.ts b/src/merge.ts new file mode 100644 index 00000000..83005918 --- /dev/null +++ b/src/merge.ts @@ -0,0 +1,52 @@ +import type { ArrayMergeOptions } from "deepmerge"; +import deepmerge from "deepmerge"; +import type { Linter } from "eslint"; + +type FlatConfig = Linter.FlatConfig; + +export function merge( + first: FlatConfig, + second: FlatConfig, + ...rest: FlatConfig[] +): FlatConfig { + const merged = { + ...first, + ...second, + settings: deepObjectMerge(first.settings ?? {}, second.settings ?? {}), + linterOptions: { + ...first?.linterOptions, + ...second?.linterOptions, + }, + languageOptions: { + ...first?.languageOptions, + ...second?.languageOptions, + globals: { + ...first?.languageOptions?.globals, + ...second?.languageOptions?.globals, + }, + parserOptions: deepObjectMerge( + first?.languageOptions?.parserOptions ?? {}, + second?.languageOptions?.parserOptions ?? {}, + ), + }, + plugins: { + ...first?.plugins, + ...second?.plugins, + }, + rules: deepObjectMerge(first.rules ?? {}, second.rules ?? {}), + }; + + if (rest.length > 0) { + return merge(merged, rest[0], ...rest.slice(1)); + } else { + return merged; + } +} + +const overwriteMerge = (dest: any[], src: any[], options?: ArrayMergeOptions) => + src; +function deepObjectMerge(first: Partial, second: Partial): T { + return deepmerge(first, second, { + arrayMerge: overwriteMerge, + }); +} diff --git a/src/types/@eslint/js.d.ts b/src/types/@eslint/js.d.ts new file mode 100644 index 00000000..d05344c1 --- /dev/null +++ b/src/types/@eslint/js.d.ts @@ -0,0 +1 @@ +declare module "@eslint/js"; diff --git a/src/types/eslint-plugin-eslint-comments.d.ts b/src/types/eslint-plugin-eslint-comments.d.ts new file mode 100644 index 00000000..05c6b7d8 --- /dev/null +++ b/src/types/eslint-plugin-eslint-comments.d.ts @@ -0,0 +1 @@ +declare module "eslint-plugin-eslint-comments"; diff --git a/src/types/eslint-plugin-import.d.ts b/src/types/eslint-plugin-import.d.ts new file mode 100644 index 00000000..a138aac7 --- /dev/null +++ b/src/types/eslint-plugin-import.d.ts @@ -0,0 +1 @@ +declare module "eslint-plugin-import"; diff --git a/src/types/eslint-plugin-jsdoc.d.ts b/src/types/eslint-plugin-jsdoc.d.ts new file mode 100644 index 00000000..f6bdf2f6 --- /dev/null +++ b/src/types/eslint-plugin-jsdoc.d.ts @@ -0,0 +1 @@ +declare module "eslint-plugin-jsdoc"; diff --git a/src/types/eslint-plugin-n.d.ts b/src/types/eslint-plugin-n.d.ts new file mode 100644 index 00000000..9aabf6dc --- /dev/null +++ b/src/types/eslint-plugin-n.d.ts @@ -0,0 +1 @@ +declare module "eslint-plugin-n"; diff --git a/src/types/eslint-plugin-unicorn.d.ts b/src/types/eslint-plugin-unicorn.d.ts new file mode 100644 index 00000000..f7f5ad15 --- /dev/null +++ b/src/types/eslint-plugin-unicorn.d.ts @@ -0,0 +1 @@ +declare module "eslint-plugin-unicorn"; diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 00000000..fc7cc316 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,9 @@ +/** + * Assert that the given value is not null or undefined. + */ +export function nonNull(x: T): NonNullable { + if (x == null) { + throw new Error("Unexpected null or undefined"); + } + return x; +} diff --git a/templates/eslint.config-esm.mjs b/templates/eslint.config-esm.mjs new file mode 100644 index 00000000..70934740 --- /dev/null +++ b/templates/eslint.config-esm.mjs @@ -0,0 +1,8 @@ +import { build } from "eslint-config-teppeis"; + +export default await build( + { base: "node18", typescript: true, esm: true }, + { + ignores: ["dist"], + }, +); diff --git a/templates/eslint.config.cjs b/templates/eslint.config.cjs new file mode 100644 index 00000000..7adac4ac --- /dev/null +++ b/templates/eslint.config.cjs @@ -0,0 +1,3 @@ +"use strict"; + +module.exports = import("./eslint.config.mjs").then((ns) => ns.default); diff --git a/templates/eslint.config.mjs b/templates/eslint.config.mjs new file mode 100644 index 00000000..edd75194 --- /dev/null +++ b/templates/eslint.config.mjs @@ -0,0 +1,8 @@ +import { build } from "eslint-config-teppeis"; + +export default await build( + { base: "node18", typescript: true }, + { + ignores: ["dist"], + }, +); diff --git a/test/configs.mjs b/test/configs.mjs new file mode 100644 index 00000000..6003851d --- /dev/null +++ b/test/configs.mjs @@ -0,0 +1,167 @@ +import { Linter } from "eslint"; +import prettierConfig from "eslint-config-prettier"; +import { globSync } from "glob"; +import assert from "node:assert/strict"; +import { readFile } from "node:fs/promises"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { es2021 } from "../dist/configs/es2021.js"; +import { es2022 } from "../dist/configs/es2022.js"; +import { es2023 } from "../dist/configs/es2023.js"; +import { typescriptTypeChecked } from "../dist/configs/typescript-type-checked.js"; +import { typescript } from "../dist/configs/typescript.js"; +import { merge } from "../dist/merge.js"; + +const __dirname = fileURLToPath(new URL(".", import.meta.url)); + +describe("es2021 config", () => { + testConfig(es2021, "es2021"); +}); + +describe("es2022 config", () => { + testConfig(es2022, "es2022"); +}); + +describe("es2023 config", () => { + testConfig(es2022, "es2022"); + + it("should not enable rules that are overridden by prettier", () => { + const es2023rules = new Set(Object.keys(es2023.rules)); + const commonRules = Object.keys(prettierConfig.rules).filter( + (rule) => es2023rules.has(rule) && isEnabledRule(es2023.rules[rule]), + ); + assert.deepEqual(commonRules, []); + }); +}); + +describe("typescript config", () => { + testConfig(typescript, "typescript"); + + it("should not enable rules that are overridden by prettier", () => { + const tsRules = new Set(Object.keys(typescript.rules)); + const commonRules = Object.keys(prettierConfig.rules).filter( + (rule) => tsRules.has(rule) && isEnabledRule(typescript.rules[rule]), + ); + assert.deepEqual(commonRules, []); + }); +}); + +describe("typescript-type-checked config", function () { + this.timeout(10000); + const config = merge(typescriptTypeChecked, { + languageOptions: { + parserOptions: { + project: `${__dirname}fixtures/typescript-type-checked.tsconfig.json`, + tsconfigRootDir: `${__dirname}fixtures`, + }, + }, + }); + testConfig(config, "typescript-type-checked"); + + it("should not enable rules that are overridden by prettier", () => { + const tsRules = new Set(Object.keys(typescriptTypeChecked.rules)); + const commonRules = Object.keys(prettierConfig.rules).filter( + (rule) => + tsRules.has(rule) && isEnabledRule(typescriptTypeChecked.rules[rule]), + ); + assert.deepEqual(commonRules, []); + }); +}); + +/** + * @param {*} ruleLevel 0/1/2/"off"/"warn"/"error" or [0, ] + * @return {boolean} + */ +function isEnabledRule(ruleLevel) { + assert(ruleLevel != null); + const level = Array.isArray(ruleLevel) ? ruleLevel[0] : ruleLevel; + if (level === 0 || level === "off") { + return false; + } else if ( + level === 1 || + level === 2 || + level === "warn" || + level === "error" + ) { + return true; + } else { + throw new TypeError(`Unexpected rule level: ${ruleLevel}`); + } +} + +/** + * @param {import("eslint").Linter.FlatConfig} config + * @param {string} configName + */ +function testConfig(config, configName) { + const fixtures = globSync( + `${__dirname}/fixtures/${configName}.*.@(js|ts)`, + ).sort(); + for (const fixture of fixtures) { + testFile(fixture, config); + } +} + +/** + * @param {string} filePath + * @param {import("eslint").Linter.FlatConfig} config + */ +async function testFile(filePath, config) { + const match = /([^.]*)\.(pass|fail)\.(?:js|ts)$/.exec(filePath); + if (!match) { + throw new Error(`Invalid filePath: ${filePath}`); + } + // Support rules from plugins + const ruleAndTestCase = match[1].split("%"); + const rule = ruleAndTestCase[0].replaceAll("#", "/"); + const testCase = ruleAndTestCase[1]; + const expected = match[2]; + + it(`${rule}${testCase ? ` (${testCase})` : ""}: ${expected}`, async () => { + const messages = await verify(filePath, config); + const fatals = messages.filter((msg) => !!msg.fatal); + if (fatals.length) { + fatals.forEach((fatal) => { + console.error( + `${filePath}:${fatal.line}:${fatal.column} ${fatal.message}`, + ); + }); + throw new Error("Fatal error"); + } + + const messagesForTheRule = messages.filter((msg) => msg.ruleId === rule); + if (expected === "pass" && messagesForTheRule.length > 0) { + assert.fail(formatMessages(messagesForTheRule).join("\n")); + } else if (expected === "fail" && messagesForTheRule.length === 0) { + if (messages.length > 0) { + assert.fail( + `Passed the rule unexpectedly and failed other rules:\n${formatMessages( + messages, + ).join("\n")}`, + ); + } else { + assert.fail("Passed the rule unexpectedly."); + } + } + }); +} + +/** + * @param {string} file + * @param {import("eslint").Linter.FlatConfig} config + */ +async function verify(file, config) { + const linter = new Linter({ configType: "flat" }); + const code = await readFile(file, "utf8"); + return linter.verify(code, [config], path.basename(file)); +} + +/** + * @param {import('eslint').Linter.LintMessage[]} messages + */ +function formatMessages(messages) { + return messages.map( + (msg) => + `${msg.line}:${msg.column} ${msg.message.slice(0, -1)} - ${msg.ruleId}`, + ); +} diff --git a/test/fixtures/.prettier.eslintrc.json b/test/fixtures/.prettier.eslintrc.json deleted file mode 100644 index 800f859c..00000000 --- a/test/fixtures/.prettier.eslintrc.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "root": true, - "extends": ["../../es2018.js", "../../+prettier.js"] -} diff --git a/test/fixtures/.typescript-with-type.eslintrc.json b/test/fixtures/.typescript-with-type.eslintrc.json deleted file mode 100644 index 1221e31b..00000000 --- a/test/fixtures/.typescript-with-type.eslintrc.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "root": true, - "extends": ["../../es2019.js", "../../+node.js", "../../+typescript-with-type.js"] -} diff --git a/test/fixtures/.typescript.eslintrc.json b/test/fixtures/.typescript.eslintrc.json deleted file mode 100644 index 6bffdf12..00000000 --- a/test/fixtures/.typescript.eslintrc.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "root": true, - "extends": ["../../es2019.js", "../../+node.js", "../../+typescript.js"] -} diff --git a/test/fixtures/es2015.no-const-assign.fail.js b/test/fixtures/es2015.no-const-assign.fail.js deleted file mode 100644 index 09a05db1..00000000 --- a/test/fixtures/es2015.no-const-assign.fail.js +++ /dev/null @@ -1,4 +0,0 @@ -'use strict'; - -const a = 1; -a = 2; diff --git a/test/fixtures/es2015.padding-line-between-statements.pass.js b/test/fixtures/es2015.padding-line-between-statements.pass.js deleted file mode 100644 index 28bf02eb..00000000 --- a/test/fixtures/es2015.padding-line-between-statements.pass.js +++ /dev/null @@ -1,4 +0,0 @@ -import foo from 'foo'; -const a = require('a'); - -const aaa = 1; diff --git a/test/fixtures/es2015.quotes.pass.js b/test/fixtures/es2015.quotes.pass.js deleted file mode 100644 index 415f4539..00000000 --- a/test/fixtures/es2015.quotes.pass.js +++ /dev/null @@ -1 +0,0 @@ -var backtick = `backtick`; diff --git a/test/fixtures/es2017.no-return-await.fail.js b/test/fixtures/es2017.no-return-await.fail.js deleted file mode 100644 index aff9b16a..00000000 --- a/test/fixtures/es2017.no-return-await.fail.js +++ /dev/null @@ -1,3 +0,0 @@ -'use strict'; - -async () => await doSomething(); diff --git a/test/fixtures/es2017.unicorn#prefer-includes.fail.js b/test/fixtures/es2017.unicorn#prefer-includes.fail.js deleted file mode 100644 index 8a658a2d..00000000 --- a/test/fixtures/es2017.unicorn#prefer-includes.fail.js +++ /dev/null @@ -1 +0,0 @@ -[].indexOf('foo') !== -1; diff --git a/test/fixtures/es2018.prefer-object-spread.fail.js b/test/fixtures/es2018.prefer-object-spread.fail.js deleted file mode 100644 index de852542..00000000 --- a/test/fixtures/es2018.prefer-object-spread.fail.js +++ /dev/null @@ -1,2 +0,0 @@ -const foo = {a: 1}; -const bar = Object.assign({}, foo) diff --git a/test/fixtures/es2019.unicorn#prefer-string-trim-start-end.fail.js b/test/fixtures/es2019.unicorn#prefer-string-trim-start-end.fail.js deleted file mode 100644 index 8b37b0b6..00000000 --- a/test/fixtures/es2019.unicorn#prefer-string-trim-start-end.fail.js +++ /dev/null @@ -1 +0,0 @@ -"foo".trimLeft(); diff --git a/test/fixtures/es2021.eslint-comments#no-duplicate-disable.fail.js b/test/fixtures/es2021.eslint-comments#no-duplicate-disable.fail.js new file mode 100644 index 00000000..403882b1 --- /dev/null +++ b/test/fixtures/es2021.eslint-comments#no-duplicate-disable.fail.js @@ -0,0 +1,3 @@ +/*eslint-disable no-undef */ + +var foo = bar(); //eslint-disable-line no-undef diff --git a/test/fixtures/es5.getter-return.fail.js b/test/fixtures/es2021.getter-return.fail.js similarity index 61% rename from test/fixtures/es5.getter-return.fail.js rename to test/fixtures/es2021.getter-return.fail.js index b549a5c5..3ac8c41d 100644 --- a/test/fixtures/es5.getter-return.fail.js +++ b/test/fixtures/es2021.getter-return.fail.js @@ -1,5 +1,6 @@ +// just recommended rule var p = { get name() { // no returns. - } + }, }; diff --git a/test/fixtures/es5.jsdoc#check-tag-names.pass.js b/test/fixtures/es2021.jsdoc#check-tag-names.pass.js similarity index 100% rename from test/fixtures/es5.jsdoc#check-tag-names.pass.js rename to test/fixtures/es2021.jsdoc#check-tag-names.pass.js diff --git a/test/fixtures/es2021.no-alert.pass.js b/test/fixtures/es2021.no-alert.pass.js new file mode 100644 index 00000000..d35b336a --- /dev/null +++ b/test/fixtures/es2021.no-alert.pass.js @@ -0,0 +1,2 @@ +// disabled rule +alert("no warning"); diff --git a/test/fixtures/es2015.no-misleading-character-class.fail.js b/test/fixtures/es2021.no-misleading-character-class.fail.js similarity index 100% rename from test/fixtures/es2015.no-misleading-character-class.fail.js rename to test/fixtures/es2021.no-misleading-character-class.fail.js diff --git a/test/fixtures/es2015.object-shorthand.pass.js b/test/fixtures/es2021.object-shorthand.pass.js similarity index 92% rename from test/fixtures/es2015.object-shorthand.pass.js rename to test/fixtures/es2021.object-shorthand.pass.js index dbc29e71..1dd4a0c5 100644 --- a/test/fixtures/es2015.object-shorthand.pass.js +++ b/test/fixtures/es2021.object-shorthand.pass.js @@ -1,5 +1,5 @@ const foo = { foo: (bar, baz) => { return bar + baz; - } + }, }; diff --git a/test/fixtures/es2015.prefer-destructuring.fail.js b/test/fixtures/es2021.prefer-destructuring.fail.js similarity index 100% rename from test/fixtures/es2015.prefer-destructuring.fail.js rename to test/fixtures/es2021.prefer-destructuring.fail.js diff --git a/test/fixtures/es2021.prefer-object-spread.fail.js b/test/fixtures/es2021.prefer-object-spread.fail.js new file mode 100644 index 00000000..cb128bd9 --- /dev/null +++ b/test/fixtures/es2021.prefer-object-spread.fail.js @@ -0,0 +1,2 @@ +const foo = { a: 1 }; +const bar = Object.assign({}, foo); diff --git a/test/fixtures/es2021.unicorn#no-hex-escape.fail.js b/test/fixtures/es2021.unicorn#no-hex-escape.fail.js new file mode 100644 index 00000000..9dce841a --- /dev/null +++ b/test/fixtures/es2021.unicorn#no-hex-escape.fail.js @@ -0,0 +1 @@ +var foo = "\x1B"; diff --git a/test/fixtures/es2015.unicorn#prefer-string-starts-ends-with.fail.js b/test/fixtures/es2021.unicorn#prefer-string-starts-ends-with.fail.js similarity index 100% rename from test/fixtures/es2015.unicorn#prefer-string-starts-ends-with.fail.js rename to test/fixtures/es2021.unicorn#prefer-string-starts-ends-with.fail.js diff --git a/test/fixtures/es2022.prefer-object-has-own.fail.js b/test/fixtures/es2022.prefer-object-has-own.fail.js new file mode 100644 index 00000000..75c5c691 --- /dev/null +++ b/test/fixtures/es2022.prefer-object-has-own.fail.js @@ -0,0 +1,3 @@ +// es2023 doesn't introduce new rules. so run same test as es2022 + +Object.prototype.hasOwnProperty.call(obj, "a"); diff --git a/test/fixtures/es2023.prefer-object-has-own.fail.js b/test/fixtures/es2023.prefer-object-has-own.fail.js new file mode 100644 index 00000000..79a1e5dc --- /dev/null +++ b/test/fixtures/es2023.prefer-object-has-own.fail.js @@ -0,0 +1 @@ +Object.prototype.hasOwnProperty.call(obj, "a"); diff --git a/test/fixtures/es5.comma-style.fail.js b/test/fixtures/es5.comma-style.fail.js deleted file mode 100644 index 1e0cc6e9..00000000 --- a/test/fixtures/es5.comma-style.fail.js +++ /dev/null @@ -1,6 +0,0 @@ -'use strict'; - -function Foo() {} -new Foo(a - ,b - ,c) diff --git a/test/fixtures/es5.eslint-comments#no-unused-disable.fail.js b/test/fixtures/es5.eslint-comments#no-unused-disable.fail.js deleted file mode 100644 index 67ef5f72..00000000 --- a/test/fixtures/es5.eslint-comments#no-unused-disable.fail.js +++ /dev/null @@ -1,3 +0,0 @@ -/* eslint-disable no-undef */ - -function foo() {} diff --git a/test/fixtures/es5.indent.pass.js b/test/fixtures/es5.indent.pass.js deleted file mode 100644 index 79225800..00000000 --- a/test/fixtures/es5.indent.pass.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -// http://eslint.org/docs/rules/indent - -foo - .bar - .baz(); diff --git a/test/fixtures/es5.multiline-ternary.pass.js b/test/fixtures/es5.multiline-ternary.pass.js deleted file mode 100644 index f23dcf19..00000000 --- a/test/fixtures/es5.multiline-ternary.pass.js +++ /dev/null @@ -1,3 +0,0 @@ -'use strict'; - -var a = Math.random() > 0.5 ? 1 : 2; diff --git a/test/fixtures/es5.no-alert.pass.js b/test/fixtures/es5.no-alert.pass.js deleted file mode 100644 index 050216b6..00000000 --- a/test/fixtures/es5.no-alert.pass.js +++ /dev/null @@ -1,3 +0,0 @@ -'use strict'; - -alert('no warning'); diff --git a/test/fixtures/es5.no-empty.pass.js b/test/fixtures/es5.no-empty.pass.js deleted file mode 100644 index 7628715b..00000000 --- a/test/fixtures/es5.no-empty.pass.js +++ /dev/null @@ -1,5 +0,0 @@ -'use strict'; - -try { - mayThrowError(); -} catch (e) {} diff --git a/test/fixtures/es5.no-inner-declarations.fail.js b/test/fixtures/es5.no-inner-declarations.fail.js deleted file mode 100644 index aac41be6..00000000 --- a/test/fixtures/es5.no-inner-declarations.fail.js +++ /dev/null @@ -1,3 +0,0 @@ -if (test) { - function doSomething() {} -} diff --git a/test/fixtures/es5.no-self-assign.fail.js b/test/fixtures/es5.no-self-assign.fail.js deleted file mode 100644 index 10f20a36..00000000 --- a/test/fixtures/es5.no-self-assign.fail.js +++ /dev/null @@ -1 +0,0 @@ -obj.a = obj.a; diff --git a/test/fixtures/es5.no-tabs.fail.js b/test/fixtures/es5.no-tabs.fail.js deleted file mode 100644 index 5d37fd5f..00000000 --- a/test/fixtures/es5.no-tabs.fail.js +++ /dev/null @@ -1,3 +0,0 @@ -'use strict'; - -var includesTab = 'test '; diff --git a/test/fixtures/es5.object-curly-newline.fail.js b/test/fixtures/es5.object-curly-newline.fail.js deleted file mode 100644 index 245c325d..00000000 --- a/test/fixtures/es5.object-curly-newline.fail.js +++ /dev/null @@ -1,8 +0,0 @@ -var a = {foo: 1 -}; -var b = { - foo: 1}; -var c = {foo: 1, bar: 2 -}; -var d = { - foo: 1, bar: 2}; diff --git a/test/fixtures/es5.object-curly-newline.pass.js b/test/fixtures/es5.object-curly-newline.pass.js deleted file mode 100644 index 0f6f5e84..00000000 --- a/test/fixtures/es5.object-curly-newline.pass.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict'; - -var a = {}; -var b = {foo: 1}; -var c = { - foo: 1 -}; -var d = { - foo: 1, bar: 2 -}; -var e = { - foo: 1, - bar: 2 -}; -var e2 = {foo: 1, - bar: 2}; -var f = {foo: function() {dosomething();}}; -var g = { - foo: function() { - dosomething(); - } -}; diff --git a/test/fixtures/es5.padding-line-between-statements.pass.js b/test/fixtures/es5.padding-line-between-statements.pass.js deleted file mode 100644 index e8c65400..00000000 --- a/test/fixtures/es5.padding-line-between-statements.pass.js +++ /dev/null @@ -1,6 +0,0 @@ -'use strict'; - -var a = require('a'); -var b = require('b'); - -var aaa = 1; diff --git a/test/fixtures/es5.quote-props.pass.js b/test/fixtures/es5.quote-props.pass.js deleted file mode 100644 index a34bbfc6..00000000 --- a/test/fixtures/es5.quote-props.pass.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -var x = { - 'foo': 1, // unnecessary: false - 'bar-baz': 2, // as-needed - hoge: 3 -}; diff --git a/test/fixtures/es5.quotes.fail.js b/test/fixtures/es5.quotes.fail.js deleted file mode 100644 index a575bede..00000000 --- a/test/fixtures/es5.quotes.fail.js +++ /dev/null @@ -1,3 +0,0 @@ -'use strict'; - -var single = "single"; diff --git a/test/fixtures/es5.quotes.pass.js b/test/fixtures/es5.quotes.pass.js deleted file mode 100644 index 96ebfc33..00000000 --- a/test/fixtures/es5.quotes.pass.js +++ /dev/null @@ -1,4 +0,0 @@ -'use strict'; - -var single = 'single'; -var unescaped = "a string containing 'single' quotes"; diff --git a/test/fixtures/es5.spaced-comment.fail.js b/test/fixtures/es5.spaced-comment.fail.js deleted file mode 100644 index 17b64f21..00000000 --- a/test/fixtures/es5.spaced-comment.fail.js +++ /dev/null @@ -1,3 +0,0 @@ -'use strict'; - -/* this is a comment*/ diff --git a/test/fixtures/es5.spaced-comment.pass.js b/test/fixtures/es5.spaced-comment.pass.js deleted file mode 100644 index 51bd4f9d..00000000 --- a/test/fixtures/es5.spaced-comment.pass.js +++ /dev/null @@ -1,3 +0,0 @@ -'use strict'; - -/* this is a comment */ diff --git a/test/fixtures/es5.switch-colon-spacing.pass.js b/test/fixtures/es5.switch-colon-spacing.pass.js deleted file mode 100644 index c454e849..00000000 --- a/test/fixtures/es5.switch-colon-spacing.pass.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict'; - -switch (a) { - case 0: foo(); break; - case 1: - bar(); - break; - default: - baz(); - break; -} diff --git a/test/fixtures/es5.unicorn#no-hex-escape.fail.js b/test/fixtures/es5.unicorn#no-hex-escape.fail.js deleted file mode 100644 index d4cbad52..00000000 --- a/test/fixtures/es5.unicorn#no-hex-escape.fail.js +++ /dev/null @@ -1 +0,0 @@ -var foo = '\x1B'; diff --git a/test/fixtures/modules/default-export.ts b/test/fixtures/modules/default-export.ts new file mode 100644 index 00000000..60c6c8d8 --- /dev/null +++ b/test/fixtures/modules/default-export.ts @@ -0,0 +1 @@ +export default "foo"; diff --git a/test/fixtures/prettier.prettier#prettier%curly.fail.js b/test/fixtures/prettier.prettier#prettier%curly.fail.js deleted file mode 100644 index 3f7bb0e0..00000000 --- a/test/fixtures/prettier.prettier#prettier%curly.fail.js +++ /dev/null @@ -1,4 +0,0 @@ -if (foo) - doSomething(); -else - doSomethingElse(); diff --git a/test/fixtures/prettier.prettier#prettier%curly.pass.js b/test/fixtures/prettier.prettier#prettier%curly.pass.js deleted file mode 100644 index cdb75eb3..00000000 --- a/test/fixtures/prettier.prettier#prettier%curly.pass.js +++ /dev/null @@ -1,21 +0,0 @@ -/* eslint-disable no-undef, strict */ - -if (foo) foo++; -else doSomething(); - -if (foo) foo++; -else if (bar) baz(); -else doSomething(); - -if (foo && barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr) - baz(); - -if (foo) { - foo++; -} - -while (foo && bar) baz(); - -while (true) { - doSomething(); -} diff --git a/test/fixtures/prettier.prettier#prettier%singleQuote.fail.js b/test/fixtures/prettier.prettier#prettier%singleQuote.fail.js deleted file mode 100644 index fec5e6fb..00000000 --- a/test/fixtures/prettier.prettier#prettier%singleQuote.fail.js +++ /dev/null @@ -1 +0,0 @@ -const str = "test"; diff --git a/test/fixtures/typescript-import-target.ts b/test/fixtures/typescript-import-target.ts deleted file mode 100644 index 3e688b86..00000000 --- a/test/fixtures/typescript-import-target.ts +++ /dev/null @@ -1,2 +0,0 @@ -// This is imported from other fixture file. -export default "foo"; diff --git a/test/fixtures/typescript-with-type.@typescript-eslint#no-unnecessary-type-assertion.fail.ts b/test/fixtures/typescript-type-checked.@typescript-eslint#no-unnecessary-type-assertion.fail.ts similarity index 100% rename from test/fixtures/typescript-with-type.@typescript-eslint#no-unnecessary-type-assertion.fail.ts rename to test/fixtures/typescript-type-checked.@typescript-eslint#no-unnecessary-type-assertion.fail.ts diff --git a/test/fixtures/typescript-type-checked.tsconfig.json b/test/fixtures/typescript-type-checked.tsconfig.json new file mode 100644 index 00000000..98bb0d2f --- /dev/null +++ b/test/fixtures/typescript-type-checked.tsconfig.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Node 18", + "compilerOptions": { + "lib": ["es2023"], + "module": "ESNext", + "moduleResolution": "Node10", + "target": "es2022", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true + } +} diff --git a/test/fixtures/typescript.@typescript-eslint#no-duplicate-enum-values.fail.ts b/test/fixtures/typescript.@typescript-eslint#no-duplicate-enum-values.fail.ts new file mode 100644 index 00000000..602a6c24 --- /dev/null +++ b/test/fixtures/typescript.@typescript-eslint#no-duplicate-enum-values.fail.ts @@ -0,0 +1,6 @@ +// Test that @typescript-eslint:recommended is loaded. + +enum E { + A = 0, + B = 0, +} diff --git a/test/fixtures/typescript.@typescript-eslint#no-namespace.fail.ts b/test/fixtures/typescript.@typescript-eslint#no-namespace.fail.ts index 2b59338e..6a3bb98a 100644 --- a/test/fixtures/typescript.@typescript-eslint#no-namespace.fail.ts +++ b/test/fixtures/typescript.@typescript-eslint#no-namespace.fail.ts @@ -1,2 +1,3 @@ -// This checks that a rule in @typescript-eslint/eslint-plugin is effective. +// Test that custom rule settings are loaded. + namespace foo {} diff --git a/test/fixtures/typescript.@typescript-eslint#no-unnecessary-type-assertion.pass.ts b/test/fixtures/typescript.@typescript-eslint#no-unnecessary-type-assertion.pass.ts index ae3d771c..b31a2aeb 100644 --- a/test/fixtures/typescript.@typescript-eslint#no-unnecessary-type-assertion.pass.ts +++ b/test/fixtures/typescript.@typescript-eslint#no-unnecessary-type-assertion.pass.ts @@ -1,3 +1,4 @@ -// This rule is disabled in `+typescript`, because it requires type information. -// It is enabled in `+typescript-with-type` config. -const foo = <3>3; +// This rule is disabled in `typescript` config, because it requires type information. +// It is enabled in `typescript-type-checked` config. +const foo = 3; +const bar = foo!; diff --git a/test/fixtures/typescript.import#first.fail.ts b/test/fixtures/typescript.import#first.fail.ts index 48c01b74..27afb378 100644 --- a/test/fixtures/typescript.import#first.fail.ts +++ b/test/fixtures/typescript.import#first.fail.ts @@ -1,4 +1,4 @@ -console.log("before import"); +// Test that module-base and eslint-plugin-import are loaded. +console.log("some statements before import"); import { foo } from "./modules/named-export-foo"; -console.log(foo); diff --git a/test/fixtures/typescript.import#no-useless-path-segments.fail.ts b/test/fixtures/typescript.import#no-useless-path-segments.fail.ts deleted file mode 100644 index 91d596a2..00000000 --- a/test/fixtures/typescript.import#no-useless-path-segments.fail.ts +++ /dev/null @@ -1 +0,0 @@ -import { foo } from "./../foo"; diff --git a/test/fixtures/typescript.jsdoc#check-tag-names.pass.ts b/test/fixtures/typescript.jsdoc#check-tag-names.pass.ts deleted file mode 100644 index b33f4599..00000000 --- a/test/fixtures/typescript.jsdoc#check-tag-names.pass.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** @type {number} */ -var id = 1; - -/** - * @param {T} t - * @constructor - * @template T - */ -var Container = function (t) {}; diff --git a/test/fixtures/typescript.no-dupe-keys.fail.js b/test/fixtures/typescript.no-dupe-keys.fail.js deleted file mode 100644 index 47c983f4..00000000 --- a/test/fixtures/typescript.no-dupe-keys.fail.js +++ /dev/null @@ -1,5 +0,0 @@ -// This ESLint rule should be enabled in JavaScript files. -const foo = { - bar: "baz", - bar: "qux", -}; diff --git a/test/fixtures/typescript.no-dupe-keys.pass.ts b/test/fixtures/typescript.no-dupe-keys.pass.ts index 3ddaeee3..7a1c9c66 100644 --- a/test/fixtures/typescript.no-dupe-keys.pass.ts +++ b/test/fixtures/typescript.no-dupe-keys.pass.ts @@ -1,4 +1,5 @@ -// This ESLint rule should be disabled in TypeScript files. +// Test that @typescript-eslint:eslint-recommended is loaded. + const foo = { bar: "baz", bar: "qux", diff --git a/test/fixtures/typescript.node#shebang.fail.ts b/test/fixtures/typescript.node#shebang.fail.ts deleted file mode 100644 index 7857ae88..00000000 --- a/test/fixtures/typescript.node#shebang.fail.ts +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env node - -// This test checks that eslint-plugin-n is effective. diff --git a/test/index.js b/test/index.js deleted file mode 100644 index cfa97308..00000000 --- a/test/index.js +++ /dev/null @@ -1,50 +0,0 @@ -"use strict"; - -const path = require("node:path"); -const glob = require("glob"); -const eslint = require("eslint"); - -const { ESLint } = eslint; -const generateTest = require("./lib/generateTest"); - -async function verify( - configName, - useModule = false, - configFile = `${__dirname}/../${configName}.js`, -) { - const paths = glob.sync(`${__dirname}/fixtures/${configName}.*.@(js|ts)`); - const options = { - overrideConfigFile: configFile, - ignore: false, - reportUnusedDisableDirectives: "error", - useEslintrc: false, - }; - if (useModule) { - options.parserOptions = { - sourceType: "module", - }; - } - const engine = new ESLint(options); - return engine.lintFiles(paths); -} - -function describeVerify(configName, useModule = false, configFile) { - if (configFile) { - configFile = path.join(__dirname, configFile); - } - describe(configName, async () => { - const results = await verify(configName, useModule, configFile); - results.forEach((result) => generateTest(result)); - }); -} - -describe("eslint-config-teppeis", () => { - describeVerify("es5"); - describeVerify("es2015", true); - describeVerify("es2016", true); - describeVerify("es2017", true); - describeVerify("es2019", true); - // describeVerify("+prettier", false, "fixtures/.prettier.eslintrc.json"); - describeVerify("typescript", true, "fixtures/.typescript.eslintrc.json"); - describeVerify("typescript-with-type", true, "fixtures/.typescript-with-type.eslintrc.json"); -}); diff --git a/test/lib/generateTest.js b/test/lib/generateTest.js deleted file mode 100644 index 6097adcc..00000000 --- a/test/lib/generateTest.js +++ /dev/null @@ -1,48 +0,0 @@ -"use strict"; - -const assert = require("node:assert"); -const path = require("node:path"); - -function formatMessages(messages) { - return messages.map( - (message) => - `${message.line}:${message.column} ${message.message.slice(0, -1)} - ${message.ruleId}`, - ); -} - -function generateTest(result) { - const filePath = path.basename(result.filePath); - const { messages } = result; - const fatals = messages.filter((_) => !!_.fatal); - if (fatals.length) { - fatals.forEach((fatal) => { - console.error(`${filePath}:${fatal.line}:${fatal.column} ${fatal.message}`); - }); - throw new Error("Fatal error"); - } - - const match = /([^.]*)\.(pass|fail)\.(?:js|ts)$/.exec(filePath); - if (!match) { - throw new Error(`Invalid filePath: ${filePath}`); - } - // Support rules from plugins - const ruleAndTestCase = match[1].split("%"); - const rule = ruleAndTestCase[0].replaceAll("#", "/"); - const testCase = ruleAndTestCase[1]; - const expected = match[2]; - - it(`${rule}${testCase ? ` (${testCase})` : ""}: ${expected}`, () => { - const messagesForTheRule = messages.filter((m) => m.ruleId === rule); - if (expected === "pass" && messagesForTheRule.length > 0) { - assert.fail(formatMessages(messagesForTheRule).join("\n")); - } else if (expected === "fail" && messagesForTheRule.length === 0) { - if (messages.length > 0) { - assert.fail(`${rule} passed, but: \n${formatMessages(messages).join("\n")}`); - } else { - assert.fail("No errors despite your expectation"); - } - } - }); -} - -module.exports = generateTest; diff --git a/tsconfig.json b/tsconfig.json index 48aaa95b..da4bb820 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,60 +1,17 @@ { + "$schema": "https://json.schemastore.org/tsconfig", "compilerOptions": { - /* Basic Options */ - "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ - // "lib": [], /* Specify library files to be included in the compilation. */ - // "allowJs": true, /* Allow javascript files to be compiled. */ - // "checkJs": true, /* Report errors in .js files. */ - "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ - // "declaration": true, /* Generates corresponding '.d.ts' file. */ - // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - // "sourceMap": true, /* Generates corresponding '.map' file. */ - // "outFile": "./", /* Concatenate and emit output to single file. */ - // "outDir": "./", /* Redirect output structure to the directory. */ - // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - // "composite": true, /* Enable project compilation */ - // "removeComments": true, /* Do not emit comments to output. */ - // "noEmit": true, /* Do not emit outputs. */ - // "importHelpers": true, /* Import emit helpers from 'tslib'. */ - // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ - // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ - - /* Strict Type-Checking Options */ - "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* Enable strict null checks. */ - // "strictFunctionTypes": true, /* Enable strict checking of function types. */ - // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - - /* Additional Checks */ - // "noUnusedLocals": true, /* Report errors on unused locals. */ - // "noUnusedParameters": true, /* Report errors on unused parameters. */ - // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - - /* Module Resolution Options */ - // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ - // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ - // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - // "typeRoots": [], /* List of folders to include type definitions from. */ - // "types": [], /* Type declaration files to be included in compilation. */ - // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ - // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - - /* Source Map Options */ - // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - - /* Experimental Options */ - // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ - // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ - } + "lib": ["es2023"], + "module": "node16", + "moduleResolution": "node16", + "target": "es2022", + "allowJs": true, + "outDir": "dist", + "declaration": true, + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["src"] }