diff --git a/e2e-test/flat-config/__snapshots__/index.test.ts.snap b/e2e-test/flat-config/__snapshots__/index.test.ts.snap new file mode 100644 index 00000000..fed83f40 --- /dev/null +++ b/e2e-test/flat-config/__snapshots__/index.test.ts.snap @@ -0,0 +1,3 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`fix problems with flat config 1`] = `"const a = 1;"`; diff --git a/e2e-test/flat-config/index.test.ts b/e2e-test/flat-config/index.test.ts new file mode 100644 index 00000000..c6450568 --- /dev/null +++ b/e2e-test/flat-config/index.test.ts @@ -0,0 +1,60 @@ +import { afterEach, test, expect } from 'vitest'; +import { createIFF } from '../../src/test-util/fixtures.js'; +import dedent from 'dedent'; +import { ChildProcessWithoutNullStreams, spawn } from 'child_process'; +import { createStreamWatcher } from '../../src/test-util/stream-watcher.js'; +import { readFile } from 'node:fs/promises'; +import { dirname, join } from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +const ETX = String.fromCharCode(0x03); // ^C +const LF = String.fromCharCode(0x0a); // \n + +const iff = await createIFF({ + 'src/index.js': 'let a = 1;', + 'eslint.config.js': dedent` + export default [ + { rules: { 'prefer-const': 'error' } }, + ]; + `, + 'package.json': '{ "type": "module" }', +}); + +function waitForClose(child: ChildProcessWithoutNullStreams) { + return new Promise((resolve) => { + child.on('close', resolve); + }); +} + +let child: ChildProcessWithoutNullStreams; +afterEach(async () => { + child.kill(); + await waitForClose(child); + await iff.reset(); +}); + +test('fix problems with flat config', async () => { + child = spawn( + 'node', + [ + join(__dirname, '../../bin/eslint-interactive.js'), + 'src', + // merge stderr to stdout + '2>&1', + ], + { shell: true, stdio: 'pipe', cwd: iff.rootDir, env: { ...process.env, ESLINT_USE_FLAT_CONFIG: 'true' } }, + ); + const streamWatcher = createStreamWatcher(child.stdout, { debug: false }); + + await streamWatcher.match(/Which rules would you like to apply action\?/); + child.stdin.write(' '); // Select `prefer-const` rule + child.stdin.write(LF); // Confirm the choice + await streamWatcher.match(/Which action do you want to do\?/); + child.stdin.write('1'); // Focus on `Run `eslint --fix`` + child.stdin.write(LF); // Confirm the choice + await streamWatcher.match(/Fixing done\./); + expect(await readFile(iff.paths['src/index.js'], 'utf-8')).toMatchSnapshot(); + child.stdin.write(ETX); // Exit +}); diff --git a/e2e-test/flat-config/package.json b/e2e-test/flat-config/package.json new file mode 100644 index 00000000..1101ade6 --- /dev/null +++ b/e2e-test/flat-config/package.json @@ -0,0 +1,6 @@ +{ + "type": "module", + "dependencies": { + "eslint-interactive": "../../" + } +} diff --git a/e2e-test/global-installation/index.test.ts b/e2e-test/global-installation/index.test.ts index 0a9092f3..3ece619f 100644 --- a/e2e-test/global-installation/index.test.ts +++ b/e2e-test/global-installation/index.test.ts @@ -36,7 +36,7 @@ test('can print error with eslint-formatter-codeframe', async () => { ], { shell: true, stdio: 'pipe', cwd: __dirname, env: { ...process.env, ESLINT_USE_FLAT_CONFIG: 'false' } }, ); - const streamWatcher = createStreamWatcher(child.stdout, { debug: true }); + const streamWatcher = createStreamWatcher(child.stdout, { debug: false }); await streamWatcher.match(/Which rules would you like to apply action\?/); child.stdin.write(' '); // Select `semi` rule diff --git a/fixtures/eslint.config.mjs b/fixtures/eslint.config.mjs new file mode 100644 index 00000000..85151cbf --- /dev/null +++ b/fixtures/eslint.config.mjs @@ -0,0 +1,40 @@ +// @ts-check + +import { FlatCompat } from '@eslint/eslintrc'; + +const compat = new FlatCompat(); + +export default [ + ...compat.plugins('import'), + ...compat.env({ node: true, es2020: true }), + { + files: ['**/*.js', '**/*.mjs', '**/*.jsx'], + languageOptions: { + sourceType: 'module', + ecmaVersion: 2020, + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + }, + rules: { + 'semi': 2, + 'import/order': [2, { alphabetize: { order: 'asc' } }], + 'prefer-const': 2, + 'no-unused-vars': [ + 2, + { + ignoreRestSiblings: true, + argsIgnorePattern: '^_', + caughtErrors: 'all', + }, + ], + 'ban-exponentiation-operator': 'off', // Disable in flat config + 'no-useless-escape': 2, + 'no-unsafe-negation': 2, + 'arrow-body-style': [2, 'always'], + + }, + }, +] diff --git a/package.json b/package.json index 70caa62a..9bf6335b 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "@types/node": "^20.8.10", "@types/yargs": "^17.0.29", "dedent": "^1.5.1", - "eslint": "^8.53.0", + "eslint": "^8.57.0", "fs-extra": "^11.1.1", "import-meta-resolve": "^4.0.0", "npm-run-all": "^4.1.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6328f9bb..cbb0e158 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -55,7 +55,7 @@ importers: devDependencies: '@mizdra/eslint-config-mizdra': specifier: 2.1.0-alpha.0 - version: 2.1.0-alpha.0(eslint@8.53.0)(prettier@3.0.3)(typescript@5.2.2) + version: 2.1.0-alpha.0(eslint@8.57.0)(prettier@3.0.3)(typescript@5.2.2) '@mizdra/inline-fixture-files': specifier: ^1.1.0 version: 1.1.0 @@ -90,8 +90,8 @@ importers: specifier: ^1.5.1 version: 1.5.1 eslint: - specifier: ^8.53.0 - version: 8.53.0 + specifier: ^8.57.0 + version: 8.57.0 fs-extra: specifier: ^11.1.1 version: 11.1.1 @@ -378,14 +378,14 @@ packages: dev: true optional: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.53.0): + /@eslint-community/eslint-utils@4.4.0(eslint@8.57.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: - eslint: 8.53.0 - eslint-visitor-keys: 3.4.1 + eslint: 8.57.0 + eslint-visitor-keys: 3.4.3 dev: true /@eslint-community/regexpp@4.10.0: @@ -393,8 +393,8 @@ packages: engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} dev: true - /@eslint/eslintrc@2.1.3: - resolution: {integrity: sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==} + /@eslint/eslintrc@2.1.4: + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 @@ -410,8 +410,8 @@ packages: - supports-color dev: true - /@eslint/js@8.53.0: - resolution: {integrity: sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==} + /@eslint/js@8.57.0: + resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true @@ -420,11 +420,11 @@ packages: engines: {node: '>=14'} dev: true - /@humanwhocodes/config-array@0.11.13: - resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==} + /@humanwhocodes/config-array@0.11.14: + resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} dependencies: - '@humanwhocodes/object-schema': 2.0.1 + '@humanwhocodes/object-schema': 2.0.2 debug: 4.3.4 minimatch: 3.1.2 transitivePeerDependencies: @@ -436,8 +436,8 @@ packages: engines: {node: '>=12.22'} dev: true - /@humanwhocodes/object-schema@2.0.1: - resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==} + /@humanwhocodes/object-schema@2.0.2: + resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==} dev: true /@jest/schemas@29.6.3: @@ -451,7 +451,7 @@ packages: resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} dev: true - /@mizdra/eslint-config-mizdra@2.1.0-alpha.0(eslint@8.53.0)(prettier@3.0.3)(typescript@5.2.2): + /@mizdra/eslint-config-mizdra@2.1.0-alpha.0(eslint@8.57.0)(prettier@3.0.3)(typescript@5.2.2): resolution: {integrity: sha512-3BQ/CBBfe128WRbsrDDLbNmx3nCuKLt9UL1xqf16v2nZwlYLjOpQ8eS3XPADiUOkPlcjbKu0DCrZ1ubG9TL1MA==} engines: {node: '>=16.0.0'} peerDependencies: @@ -464,15 +464,15 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/eslint-plugin': 5.59.2(@typescript-eslint/parser@5.59.2)(eslint@8.53.0)(typescript@5.2.2) - '@typescript-eslint/parser': 5.59.2(eslint@8.53.0)(typescript@5.2.2) - eslint: 8.53.0 - eslint-config-prettier: 9.0.0(eslint@8.53.0) - eslint-plugin-import: /eslint-plugin-i@2.29.0(@typescript-eslint/parser@5.59.2)(eslint@8.53.0) - eslint-plugin-n: 15.7.0(eslint@8.53.0) - eslint-plugin-react: 7.32.2(eslint@8.53.0) - eslint-plugin-react-hooks: 4.6.0(eslint@8.53.0) - eslint-plugin-unicorn: 49.0.0(eslint@8.53.0) + '@typescript-eslint/eslint-plugin': 5.59.2(@typescript-eslint/parser@5.59.2)(eslint@8.57.0)(typescript@5.2.2) + '@typescript-eslint/parser': 5.59.2(eslint@8.57.0)(typescript@5.2.2) + eslint: 8.57.0 + eslint-config-prettier: 9.0.0(eslint@8.57.0) + eslint-plugin-import: /eslint-plugin-i@2.29.0(@typescript-eslint/parser@5.59.2)(eslint@8.57.0) + eslint-plugin-n: 15.7.0(eslint@8.57.0) + eslint-plugin-react: 7.32.2(eslint@8.57.0) + eslint-plugin-react-hooks: 4.6.0(eslint@8.57.0) + eslint-plugin-unicorn: 49.0.0(eslint@8.57.0) prettier: 3.0.3 typescript: 5.2.2 transitivePeerDependencies: @@ -690,7 +690,7 @@ packages: '@types/yargs-parser': 21.0.2 dev: true - /@typescript-eslint/eslint-plugin@5.59.2(@typescript-eslint/parser@5.59.2)(eslint@8.53.0)(typescript@5.2.2): + /@typescript-eslint/eslint-plugin@5.59.2(@typescript-eslint/parser@5.59.2)(eslint@8.57.0)(typescript@5.2.2): resolution: {integrity: sha512-yVrXupeHjRxLDcPKL10sGQ/QlVrA8J5IYOEWVqk0lJaSZP7X5DfnP7Ns3cc74/blmbipQ1htFNVGsHX6wsYm0A==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -702,12 +702,12 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 5.59.2(eslint@8.53.0)(typescript@5.2.2) + '@typescript-eslint/parser': 5.59.2(eslint@8.57.0)(typescript@5.2.2) '@typescript-eslint/scope-manager': 5.59.2 - '@typescript-eslint/type-utils': 5.59.2(eslint@8.53.0)(typescript@5.2.2) - '@typescript-eslint/utils': 5.59.2(eslint@8.53.0)(typescript@5.2.2) + '@typescript-eslint/type-utils': 5.59.2(eslint@8.57.0)(typescript@5.2.2) + '@typescript-eslint/utils': 5.59.2(eslint@8.57.0)(typescript@5.2.2) debug: 4.3.4 - eslint: 8.53.0 + eslint: 8.57.0 grapheme-splitter: 1.0.4 ignore: 5.2.4 natural-compare-lite: 1.4.0 @@ -718,7 +718,7 @@ packages: - supports-color dev: true - /@typescript-eslint/parser@5.59.2(eslint@8.53.0)(typescript@5.2.2): + /@typescript-eslint/parser@5.59.2(eslint@8.57.0)(typescript@5.2.2): resolution: {integrity: sha512-uq0sKyw6ao1iFOZZGk9F8Nro/8+gfB5ezl1cA06SrqbgJAt0SRoFhb9pXaHvkrxUpZaoLxt8KlovHNk8Gp6/HQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -732,7 +732,7 @@ packages: '@typescript-eslint/types': 5.59.2 '@typescript-eslint/typescript-estree': 5.59.2(typescript@5.2.2) debug: 4.3.4 - eslint: 8.53.0 + eslint: 8.57.0 typescript: 5.2.2 transitivePeerDependencies: - supports-color @@ -746,7 +746,7 @@ packages: '@typescript-eslint/visitor-keys': 5.59.2 dev: true - /@typescript-eslint/type-utils@5.59.2(eslint@8.53.0)(typescript@5.2.2): + /@typescript-eslint/type-utils@5.59.2(eslint@8.57.0)(typescript@5.2.2): resolution: {integrity: sha512-b1LS2phBOsEy/T381bxkkywfQXkV1dWda/z0PhnIy3bC5+rQWQDS7fk9CSpcXBccPY27Z6vBEuaPBCKCgYezyQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -757,9 +757,9 @@ packages: optional: true dependencies: '@typescript-eslint/typescript-estree': 5.59.2(typescript@5.2.2) - '@typescript-eslint/utils': 5.59.2(eslint@8.53.0)(typescript@5.2.2) + '@typescript-eslint/utils': 5.59.2(eslint@8.57.0)(typescript@5.2.2) debug: 4.3.4 - eslint: 8.53.0 + eslint: 8.57.0 tsutils: 3.21.0(typescript@5.2.2) typescript: 5.2.2 transitivePeerDependencies: @@ -792,19 +792,19 @@ packages: - supports-color dev: true - /@typescript-eslint/utils@5.59.2(eslint@8.53.0)(typescript@5.2.2): + /@typescript-eslint/utils@5.59.2(eslint@8.57.0)(typescript@5.2.2): resolution: {integrity: sha512-kSuF6/77TZzyGPhGO4uVp+f0SBoYxCDf+lW3GKhtKru/L8k/Hd7NFQxyWUeY7Z/KGB2C6Fe3yf2vVi4V9TsCSQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@types/json-schema': 7.0.14 '@types/semver': 7.3.13 '@typescript-eslint/scope-manager': 5.59.2 '@typescript-eslint/types': 5.59.2 '@typescript-eslint/typescript-estree': 5.59.2(typescript@5.2.2) - eslint: 8.53.0 + eslint: 8.57.0 eslint-scope: 5.1.1 semver: 7.5.4 transitivePeerDependencies: @@ -1475,13 +1475,13 @@ packages: engines: {node: '>=10'} dev: true - /eslint-config-prettier@9.0.0(eslint@8.53.0): + /eslint-config-prettier@9.0.0(eslint@8.57.0): resolution: {integrity: sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==} hasBin: true peerDependencies: eslint: '>=7.0.0' dependencies: - eslint: 8.53.0 + eslint: 8.57.0 dev: true /eslint-formatter-codeframe@7.32.1: @@ -1502,7 +1502,7 @@ packages: - supports-color dev: true - /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.59.2)(eslint-import-resolver-node@0.3.9)(eslint@8.53.0): + /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.59.2)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} engines: {node: '>=4'} peerDependencies: @@ -1523,26 +1523,26 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.59.2(eslint@8.53.0)(typescript@5.2.2) + '@typescript-eslint/parser': 5.59.2(eslint@8.57.0)(typescript@5.2.2) debug: 3.2.7 - eslint: 8.53.0 + eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color dev: true - /eslint-plugin-es@4.1.0(eslint@8.53.0): + /eslint-plugin-es@4.1.0(eslint@8.57.0): resolution: {integrity: sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==} engines: {node: '>=8.10.0'} peerDependencies: eslint: '>=4.19.1' dependencies: - eslint: 8.53.0 + eslint: 8.57.0 eslint-utils: 2.1.0 regexpp: 3.2.0 dev: true - /eslint-plugin-i@2.29.0(@typescript-eslint/parser@5.59.2)(eslint@8.53.0): + /eslint-plugin-i@2.29.0(@typescript-eslint/parser@5.59.2)(eslint@8.57.0): resolution: {integrity: sha512-slGeTS3GQzx9267wLJnNYNO8X9EHGsc75AKIAFvnvMYEcTJKotPKL1Ru5PIGVHIVet+2DsugePWp8Oxpx8G22w==} engines: {node: '>=12'} peerDependencies: @@ -1550,9 +1550,9 @@ packages: dependencies: debug: 3.2.7 doctrine: 2.1.0 - eslint: 8.53.0 + eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.59.2)(eslint-import-resolver-node@0.3.9)(eslint@8.53.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.59.2)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) get-tsconfig: 4.7.2 is-glob: 4.0.3 minimatch: 3.1.2 @@ -1565,16 +1565,16 @@ packages: - supports-color dev: true - /eslint-plugin-n@15.7.0(eslint@8.53.0): + /eslint-plugin-n@15.7.0(eslint@8.57.0): resolution: {integrity: sha512-jDex9s7D/Qial8AGVIHq4W7NswpUD5DPDL2RH8Lzd9EloWUuvUkHfv4FRLMipH5q2UtyurorBkPeNi1wVWNh3Q==} engines: {node: '>=12.22.0'} peerDependencies: eslint: '>=7.0.0' dependencies: builtins: 5.0.1 - eslint: 8.53.0 - eslint-plugin-es: 4.1.0(eslint@8.53.0) - eslint-utils: 3.0.0(eslint@8.53.0) + eslint: 8.57.0 + eslint-plugin-es: 4.1.0(eslint@8.57.0) + eslint-utils: 3.0.0(eslint@8.57.0) ignore: 5.2.4 is-core-module: 2.13.1 minimatch: 3.1.2 @@ -1582,16 +1582,16 @@ packages: semver: 7.5.4 dev: true - /eslint-plugin-react-hooks@4.6.0(eslint@8.53.0): + /eslint-plugin-react-hooks@4.6.0(eslint@8.57.0): resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} engines: {node: '>=10'} peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 dependencies: - eslint: 8.53.0 + eslint: 8.57.0 dev: true - /eslint-plugin-react@7.32.2(eslint@8.53.0): + /eslint-plugin-react@7.32.2(eslint@8.57.0): resolution: {integrity: sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==} engines: {node: '>=4'} peerDependencies: @@ -1601,7 +1601,7 @@ packages: array.prototype.flatmap: 1.3.1 array.prototype.tosorted: 1.1.1 doctrine: 2.1.0 - eslint: 8.53.0 + eslint: 8.57.0 estraverse: 5.3.0 jsx-ast-utils: 3.3.3 minimatch: 3.1.2 @@ -1615,17 +1615,17 @@ packages: string.prototype.matchall: 4.0.8 dev: true - /eslint-plugin-unicorn@49.0.0(eslint@8.53.0): + /eslint-plugin-unicorn@49.0.0(eslint@8.57.0): resolution: {integrity: sha512-0fHEa/8Pih5cmzFW5L7xMEfUTvI9WKeQtjmKpTUmY+BiFCDxkxrTdnURJOHKykhtwIeyYsxnecbGvDCml++z4Q==} engines: {node: '>=16'} peerDependencies: eslint: '>=8.52.0' dependencies: '@babel/helper-validator-identifier': 7.22.20 - '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) ci-info: 3.9.0 clean-regexp: 1.0.0 - eslint: 8.53.0 + eslint: 8.57.0 esquery: 1.5.0 indent-string: 4.0.0 is-builtin-module: 3.2.1 @@ -1661,13 +1661,13 @@ packages: eslint-visitor-keys: 1.3.0 dev: true - /eslint-utils@3.0.0(eslint@8.53.0): + /eslint-utils@3.0.0(eslint@8.57.0): resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} peerDependencies: eslint: '>=5' dependencies: - eslint: 8.53.0 + eslint: 8.57.0 eslint-visitor-keys: 2.1.0 dev: true @@ -1681,26 +1681,21 @@ packages: engines: {node: '>=10'} dev: true - /eslint-visitor-keys@3.4.1: - resolution: {integrity: sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true - /eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint@8.53.0: - resolution: {integrity: sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==} + /eslint@8.57.0: + resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@eslint-community/regexpp': 4.10.0 - '@eslint/eslintrc': 2.1.3 - '@eslint/js': 8.53.0 - '@humanwhocodes/config-array': 0.11.13 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.57.0 + '@humanwhocodes/config-array': 0.11.14 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 '@ungap/structured-clone': 1.2.0 diff --git a/src/__snapshots__/core.test.ts.snap b/src/__snapshots__/core.test.ts.snap index fa6b66df..bf9da771 100644 --- a/src/__snapshots__/core.test.ts.snap +++ b/src/__snapshots__/core.test.ts.snap @@ -69,6 +69,63 @@ exports[`Core > disablePerLine 1`] = ` let a = 1;" `; +exports[`Core > flat config 1`] = ` +[ + { + "errorCount": 1, + "filePath": "/src/.index.js", + "messages": [ + { + "ruleId": "prefer-const", + "severity": 2, + }, + ], + "warningCount": 0, + }, + { + "errorCount": 1, + "filePath": "/src/index.js", + "messages": [ + { + "ruleId": "prefer-const", + "severity": 2, + }, + ], + "warningCount": 0, + }, + { + "errorCount": 1, + "filePath": "/src/index.jsx", + "messages": [ + { + "ruleId": "prefer-const", + "severity": 2, + }, + ], + "warningCount": 0, + }, + { + "errorCount": 1, + "filePath": "/src/index.mjs", + "messages": [ + { + "ruleId": "prefer-const", + "severity": 2, + }, + ], + "warningCount": 0, + }, +] +`; + +exports[`Core > flat config 2`] = `"const a = 1;"`; + +exports[`Core > flat config 3`] = `"const a = 1;"`; + +exports[`Core > flat config 4`] = `"const a = 1;"`; + +exports[`Core > flat config 5`] = `"const a = 1;"`; + exports[`Core > lint > returns lint results 1`] = ` [ { diff --git a/src/cli/run.ts b/src/cli/run.ts index d4751379..cce6bd3e 100644 --- a/src/cli/run.ts +++ b/src/cli/run.ts @@ -31,10 +31,7 @@ export async function run(options: Options) { } const parsedCLIOptions = parseArgv(options.argv); const usingFlatConfig = await shouldUseFlatConfig(); - if (usingFlatConfig) { - throw new Error('Flat Config is not yet supported.'); // TODO: support flat config - } - const config = translateCLIOptions(parsedCLIOptions, 'eslintrc'); // TODO: support flat config + const config = translateCLIOptions(parsedCLIOptions, usingFlatConfig ? 'flat' : 'eslintrc'); // Directly executing the Core API will hog the main thread and halt the spinner. // So we wrap it with comlink and run it on the Worker. diff --git a/src/config.ts b/src/config.ts index 2ebd04ba..7dea6406 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,4 +1,4 @@ -import type { LegacyESLint } from 'eslint/use-at-your-own-risk'; +import type { LegacyESLint, FlatESLint } from 'eslint/use-at-your-own-risk'; import { cliOptionsDefaults, ParsedCLIOptions } from './cli/parse-argv.js'; import { DeepPartial } from './util/type-check.js'; type LegacyESLintOptions = { type: 'eslintrc' } & Pick< @@ -14,8 +14,12 @@ type LegacyESLintOptions = { type: 'eslintrc' } & Pick< | 'cwd' | 'resolvePluginsRelativeTo' >; +type FlatESLintOptions = { type: 'flat' } & Pick< + FlatESLint.Options, + 'overrideConfigFile' | 'cache' | 'cacheLocation' | 'overrideConfig' | 'cwd' +>; -export type ESLintOptions = LegacyESLintOptions; // TODO: support flat config +export type ESLintOptions = LegacyESLintOptions | FlatESLintOptions; /** The config of eslint-interactive */ export type Config = { @@ -47,7 +51,17 @@ export function translateCLIOptions(options: ParsedCLIOptions, eslintOptionsType }, }; } else if (eslintOptionsType === 'flat') { - throw new Error('Flat config is not supported yet'); + return { + patterns: options.patterns, + formatterName: options.formatterName, + quiet: options.quiet, + eslintOptions: { + type: 'flat', + overrideConfigFile: options.overrideConfigFile, + cache: options.cache, + cacheLocation: options.cacheLocation, + }, + }; } else { throw new Error(`Unexpected configType: ${String(eslintOptionsType)}`); } @@ -81,12 +95,9 @@ export type NormalizedConfig = { export function normalizeConfig(config: Config): NormalizedConfig { const cwd = config.cwd ?? configDefaults.cwd; - return { - patterns: config.patterns, - formatterName: config.formatterName ?? configDefaults.formatterName, - quiet: config.quiet ?? configDefaults.quiet, - cwd, - eslintOptions: { + let eslintOptions: NormalizedConfig['eslintOptions']; + if (config.eslintOptions.type === 'eslintrc') { + eslintOptions = { type: 'eslintrc', useEslintrc: config.eslintOptions.useEslintrc ?? configDefaults.eslintOptions.useEslintrc, overrideConfigFile: config.eslintOptions.overrideConfigFile ?? configDefaults.eslintOptions.overrideConfigFile, @@ -99,6 +110,22 @@ export function normalizeConfig(config: Config): NormalizedConfig { cwd, resolvePluginsRelativeTo: config.eslintOptions.resolvePluginsRelativeTo ?? configDefaults.eslintOptions.resolvePluginsRelativeTo, - }, + }; + } else { + eslintOptions = { + type: 'flat', + overrideConfigFile: config.eslintOptions.overrideConfigFile ?? configDefaults.eslintOptions.overrideConfigFile, + cache: config.eslintOptions.cache ?? configDefaults.eslintOptions.cache, + cacheLocation: config.eslintOptions.cacheLocation ?? configDefaults.eslintOptions.cacheLocation, + overrideConfig: config.eslintOptions.overrideConfig ?? configDefaults.eslintOptions.overrideConfig, + cwd, + }; + } + return { + patterns: config.patterns, + formatterName: config.formatterName ?? configDefaults.formatterName, + quiet: config.quiet ?? configDefaults.quiet, + cwd, + eslintOptions, }; } diff --git a/src/core.test.ts b/src/core.test.ts index e49f1263..314965af 100644 --- a/src/core.test.ts +++ b/src/core.test.ts @@ -272,4 +272,44 @@ describe('Core', () => { await undo(); expect(await readFile(iff.paths['src/no-unused-vars.js'], 'utf-8')).toEqual(original); }); + test('flat config', async () => { + const iff = await createIFF({ + 'src/index.js': 'let a = 1;', + 'src/index.mjs': 'let a = 1;', + 'src/index.jsx': 'let a = 1;', + 'src/.index.js': 'let a = 1;', + 'eslint.config.js': dedent` + export default [ + { + files: ['**/*.js', '**/*.mjs', '**/*.jsx'], + languageOptions: { + sourceType: 'module', + ecmaVersion: 2020, + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + }, + rules: { 'prefer-const': 'error' }, + }, + ]; + `, + 'package.json': '{ "type": "module" }', + }); + const core = new Core({ + patterns: ['src'], + cwd: iff.rootDir, + eslintOptions: { + type: 'flat', + }, + }); + const results = await core.lint(); + expect(normalizeResults(results, iff.rootDir)).toMatchSnapshot(); + await core.applyAutoFixes(results, ['prefer-const']); + expect(await readFile(iff.paths['src/index.js'], 'utf-8')).toMatchSnapshot(); + expect(await readFile(iff.paths['src/index.mjs'], 'utf-8')).toMatchSnapshot(); + expect(await readFile(iff.paths['src/index.jsx'], 'utf-8')).toMatchSnapshot(); + expect(await readFile(iff.paths['src/.index.js'], 'utf-8')).toMatchSnapshot(); + }); }); diff --git a/src/core.ts b/src/core.ts index 452e4cd2..d3288951 100644 --- a/src/core.ts +++ b/src/core.ts @@ -20,7 +20,7 @@ import { import { format } from './formatter/index.js'; import { filterResultsByRuleId } from './util/eslint.js'; -const { LegacyESLint } = eslintPkg; +const { LegacyESLint, FlatESLint } = eslintPkg; /** * Generate results to undo. @@ -46,12 +46,13 @@ export class Core { constructor(config: Config) { this.config = normalizeConfig(config); - const { type, ...eslintOptions } = this.config.eslintOptions; - if (type === 'eslintrc') { - this.eslint = new LegacyESLint(eslintOptions); + const eslintOptions = this.config.eslintOptions; + if (eslintOptions.type === 'eslintrc') { + const { type, ...rest } = eslintOptions; + this.eslint = new LegacyESLint(rest); } else { - // TODO: support flat config - throw new Error(`Flat Config is not yet supported.`); + const { type, ...rest } = eslintOptions; + this.eslint = new FlatESLint(rest); } } @@ -190,13 +191,18 @@ export class Core { ): Promise { // NOTE: Extract only necessary results and files for performance const filteredResultsOfLint = filterResultsByRuleId(resultsOfLint, ruleIds); - const linter = new Linter({ configType: 'eslintrc' }); + const linter = new Linter({ configType: this.config.eslintOptions.type }); // eslint-disable-next-line prefer-const for (let { filePath, source } of filteredResultsOfLint) { if (!source) throw new Error('Source code is required to apply fixes.'); - // eslint-disable-next-line no-await-in-loop - const config: Linter.Config = await this.eslint.calculateConfigForFile(filePath); + const config: Linter.Config | Linter.FlatConfig[] = + this.config.eslintOptions.type === 'eslintrc' + ? // eslint-disable-next-line no-await-in-loop + await this.eslint.calculateConfigForFile(filePath) + : // NOTE: For some reason, if files is not specified, it will not match .jsx + // eslint-disable-next-line no-await-in-loop + [{ ...(await this.eslint.calculateConfigForFile(filePath)), files: ['**/*.*', '**/*'] }]; const fixedResult = verifyAndFix(linter, source, config, filePath, ruleIds, fixCreator); diff --git a/src/eslint/linter.ts b/src/eslint/linter.ts index f4c65e9c..8677ead0 100644 --- a/src/eslint/linter.ts +++ b/src/eslint/linter.ts @@ -27,7 +27,7 @@ type FixedResult = { export function verifyAndFix( linter: Linter, text: string, - config: Linter.Config, + config: Linter.Config | Linter.FlatConfig[], filePath: string, ruleIds: string[], fixCreator: (context: FixContext) => Rule.Fix[],