diff --git a/.changeset/wicked-carpets-sing.md b/.changeset/wicked-carpets-sing.md new file mode 100644 index 00000000..cea1b1f2 --- /dev/null +++ b/.changeset/wicked-carpets-sing.md @@ -0,0 +1,5 @@ +--- +"@intlify/eslint-plugin-vue-i18n": minor +--- + +fix: `no-unused-keys` rule not working when using flat config diff --git a/files/empty.json b/files/empty.json deleted file mode 100644 index 0967ef42..00000000 --- a/files/empty.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/lib/utils/collect-keys.ts b/lib/utils/collect-keys.ts index c34b619f..8f5346b2 100644 --- a/lib/utils/collect-keys.ts +++ b/lib/utils/collect-keys.ts @@ -2,9 +2,7 @@ * @fileoverview Collect localization keys * @author kazuya kawaguchi (a.k.a. kazupon) */ -import type { Linter } from 'eslint' -import { parseForESLint, AST as VAST } from 'vue-eslint-parser' -import { readFileSync } from 'fs' +import { AST as VAST } from 'vue-eslint-parser' import { resolve, extname } from 'path' import { listFilesToProcess } from './glob-utils' import { ResourceLoader } from './resource-loader' @@ -12,13 +10,11 @@ import { CacheLoader } from './cache-loader' import { defineCacheFunction } from './cache-function' import debugBuilder from 'debug' import type { RuleContext, VisitorKeys } from '../types' -// @ts-expect-error -- ignore -import { Legacy } from '@eslint/eslintrc' import { getCwd } from './get-cwd' import { isStaticLiteral, getStaticLiteralValue } from './index' -import importFresh from 'import-fresh' +import type { Parser } from './parser-config-resolver' +import { buildParserFromConfig } from './parser-config-resolver' const debug = debugBuilder('eslint-plugin-vue-i18n:collect-keys') -const { CascadingConfigArrayFactory } = Legacy /** * @@ -74,56 +70,20 @@ function getKeyFromI18nComponent(node: VAST.VAttribute) { } } -function getParser(parser: string | undefined): { - parseForESLint?: typeof parseForESLint - parse: (code: string, options: unknown) => VAST.ESLintProgram -} { - if (parser) { - try { - return require(parser) - } catch (_e) { - // ignore - } - } - return { - parseForESLint, - parse(code: string, options: unknown) { - return parseForESLint(code, options).ast - } - } -} - /** * Collect the used keys from source code text. * @param {string} text * @param {string} filename * @returns {string[]} */ -function collectKeysFromText( - text: string, - filename: string, - getConfigForFile: (filePath: string) => Linter.Config -) { +function collectKeysFromText(filename: string, parser: Parser) { const effectiveFilename = filename || '' debug(`collectKeysFromFile ${effectiveFilename}`) - const config = getConfigForFile(effectiveFilename) - const parser = getParser(config.parser) - - const parserOptions = Object.assign({}, config.parserOptions, { - loc: true, - range: true, - raw: true, - tokens: true, - comment: true, - eslintVisitorKeys: true, - eslintScopeManager: true, - filePath: effectiveFilename - }) try { - const parseResult = - typeof parser.parseForESLint === 'function' - ? parser.parseForESLint(text, parserOptions) - : { ast: parser.parse(text, parserOptions) } + const parseResult = parser(filename) + if (!parseResult) { + return [] + } return collectKeysFromAST(parseResult.ast, parseResult.visitorKeys) } catch (_e) { return [] @@ -137,20 +97,7 @@ function collectKeysFromText( function collectKeyResourcesFromFiles(fileNames: string[], cwd: string) { debug('collectKeysFromFiles', fileNames) - const configArrayFactory = new CascadingConfigArrayFactory({ - additionalPluginPool: new Map([ - ['@intlify/vue-i18n', importFresh('../index')] - ]), - cwd, - async getEslintRecommendedConfig() { - return await import('../../files/empty.json') - }, - async getEslintAllConfig() { - return await import('../../files/empty.json') - }, - eslintRecommendedPath: require.resolve('../../files/empty.json'), - eslintAllPath: require.resolve('../../files/empty.json') - }) + const parser = buildParserFromConfig(cwd) const results = [] @@ -160,21 +107,12 @@ function collectKeyResourcesFromFiles(fileNames: string[], cwd: string) { results.push( new ResourceLoader(resolve(filename), () => { - const text = readFileSync(resolve(filename), 'utf8') - return collectKeysFromText(text, filename, getConfigForFile) + return collectKeysFromText(filename, parser) }) ) } return results - - function getConfigForFile(filePath: string) { - const absolutePath = resolve(cwd, filePath) - return configArrayFactory - .getConfigArrayForFile(absolutePath) - .extractConfig(absolutePath) - .toCompatibleObjectAsConfigFileContent() - } } /** diff --git a/lib/utils/parser-config-resolver/build-parser-using-flat-config.ts b/lib/utils/parser-config-resolver/build-parser-using-flat-config.ts new file mode 100644 index 00000000..0f3fd9ec --- /dev/null +++ b/lib/utils/parser-config-resolver/build-parser-using-flat-config.ts @@ -0,0 +1,14 @@ +// @ts-expect-error -- ignore +import { createSyncFn } from 'synckit' +import type { ParseResult, Parser } from '.' + +const getSync = createSyncFn(require.resolve('./worker')) + +/** + * Build synchronously parser using the flat config + */ +export function buildParserUsingFlatConfig(cwd: string): Parser { + return (filePath: string) => { + return getSync(cwd, filePath) as ParseResult + } +} diff --git a/lib/utils/parser-config-resolver/build-parser-using-legacy-config.ts b/lib/utils/parser-config-resolver/build-parser-using-legacy-config.ts new file mode 100644 index 00000000..76bf95da --- /dev/null +++ b/lib/utils/parser-config-resolver/build-parser-using-legacy-config.ts @@ -0,0 +1,48 @@ +import type { Parser } from '.' +// @ts-expect-error -- ignore +import { Legacy } from '@eslint/eslintrc' +import path from 'path' +import { parseByParser } from './parse-by-parser' +const { CascadingConfigArrayFactory } = Legacy + +/** + * Build parser using legacy config + */ +export function buildParserUsingLegacyConfig(cwd: string): Parser { + const configArrayFactory = new CascadingConfigArrayFactory({ + additionalPluginPool: new Map([ + ['@intlify/vue-i18n', require('../../index')] + ]), + cwd, + getEslintRecommendedConfig() { + return {} + }, + getEslintAllConfig() { + return {} + } + }) + + function getConfigForFile(filePath: string) { + const absolutePath = path.resolve(cwd, filePath) + return configArrayFactory + .getConfigArrayForFile(absolutePath) + .extractConfig(absolutePath) + .toCompatibleObjectAsConfigFileContent() + } + + return (filePath: string) => { + const config = getConfigForFile(filePath) + + const parserOptions = Object.assign({}, config.parserOptions, { + loc: true, + range: true, + raw: true, + tokens: true, + comment: true, + eslintVisitorKeys: true, + eslintScopeManager: true, + filePath + }) + return parseByParser(filePath, config.parser, parserOptions) + } +} diff --git a/lib/utils/parser-config-resolver/index.ts b/lib/utils/parser-config-resolver/index.ts new file mode 100644 index 00000000..6c0a09bc --- /dev/null +++ b/lib/utils/parser-config-resolver/index.ts @@ -0,0 +1,24 @@ +import { shouldUseFlatConfig } from './should-use-flat-config' +import type { AST as VAST } from 'vue-eslint-parser' +import { buildParserUsingLegacyConfig } from './build-parser-using-legacy-config' +import { buildParserUsingFlatConfig } from './build-parser-using-flat-config' + +export type ParseResult = Pick< + VAST.ESLintExtendedProgram, + 'ast' | 'visitorKeys' +> | null +export type Parser = (filePath: string) => ParseResult + +const parsers: Record = {} + +export function buildParserFromConfig(cwd: string): Parser { + const parser = parsers[cwd] + if (parser) { + return parser + } + if (shouldUseFlatConfig(cwd)) { + return (parsers[cwd] = buildParserUsingFlatConfig(cwd)) + } + + return (parsers[cwd] = buildParserUsingLegacyConfig(cwd)) +} diff --git a/lib/utils/parser-config-resolver/parse-by-parser.ts b/lib/utils/parser-config-resolver/parse-by-parser.ts new file mode 100644 index 00000000..7c225289 --- /dev/null +++ b/lib/utils/parser-config-resolver/parse-by-parser.ts @@ -0,0 +1,45 @@ +import type { Linter } from 'eslint' +import { readFileSync } from 'fs' +import path from 'path' +import { parseForESLint } from 'vue-eslint-parser' +import type { ParseResult } from '.' + +export function parseByParser( + filePath: string, + parserDefine: Linter.ParserModule | string | undefined, + parserOptions: unknown +): ParseResult { + const parser = getParser(parserDefine, filePath) + try { + const text = readFileSync(path.resolve(filePath), 'utf8') + const parseResult = + 'parseForESLint' in parser && typeof parser.parseForESLint === 'function' + ? parser.parseForESLint(text, parserOptions) + : // eslint-disable-next-line @typescript-eslint/no-explicit-any + { ast: (parser as any).parse(text, parserOptions) } + return parseResult as ParseResult + } catch (_e) { + return null + } +} + +function getParser( + parser: Linter.ParserModule | string | undefined, + filePath: string +): Linter.ParserModule { + if (parser) { + if (typeof parser === 'string') { + try { + return require(parser) + } catch (_e) { + // ignore + } + } else { + return parser + } + } + if (filePath.endsWith('.vue')) { + return { parseForESLint } as Linter.ParserModule + } + return require('espree') +} diff --git a/lib/utils/parser-config-resolver/should-use-flat-config.ts b/lib/utils/parser-config-resolver/should-use-flat-config.ts new file mode 100644 index 00000000..d266e8ca --- /dev/null +++ b/lib/utils/parser-config-resolver/should-use-flat-config.ts @@ -0,0 +1,66 @@ +/** copied from https://github.com/eslint/eslint/blob/v8.56.0/lib/eslint/flat-eslint.js#L1119 */ + +import path from 'path' +import fs from 'fs' + +const FLAT_CONFIG_FILENAMES = [ + 'eslint.config.js', + 'eslint.config.mjs', + 'eslint.config.cjs' +] +/** + * Returns whether flat config should be used. + * @returns {Promise} Whether flat config should be used. + */ +export function shouldUseFlatConfig(cwd: string): boolean { + // eslint-disable-next-line no-process-env -- ignore + switch (process.env.ESLINT_USE_FLAT_CONFIG) { + case 'true': + return true + case 'false': + return false + default: + // If neither explicitly enabled nor disabled, then use the presence + // of a flat config file to determine enablement. + return Boolean(findFlatConfigFile(cwd)) + } +} + +/** + * Searches from the current working directory up until finding the + * given flat config filename. + * @param {string} cwd The current working directory to search from. + * @returns {string|undefined} The filename if found or `undefined` if not. + */ +export function findFlatConfigFile(cwd: string) { + return findUp(FLAT_CONFIG_FILENAMES, { cwd }) +} + +/** We used https://github.com/sindresorhus/find-up/blob/b733bb70d3aa21b22fa011be8089110d467c317f/index.js#L94 as a reference */ +function findUp(names: string[], options: { cwd: string }) { + let directory = path.resolve(options.cwd) + const { root } = path.parse(directory) + const stopAt = path.resolve(directory, root) + // eslint-disable-next-line no-constant-condition -- ignore + while (true) { + for (const name of names) { + const target = path.resolve(directory, name) + const stat = fs.existsSync(target) + ? fs.statSync(target, { + throwIfNoEntry: false + }) + : null + if (stat?.isFile()) { + return target + } + } + + if (directory === stopAt) { + break + } + + directory = path.dirname(directory) + } + + return null +} diff --git a/lib/utils/parser-config-resolver/worker.ts b/lib/utils/parser-config-resolver/worker.ts new file mode 100644 index 00000000..97a19925 --- /dev/null +++ b/lib/utils/parser-config-resolver/worker.ts @@ -0,0 +1,42 @@ +// @ts-expect-error -- ignore +import { runAsWorker } from 'synckit' +import { getESLint } from 'eslint-compat-utils/eslint' +import type { Linter } from 'eslint' +import type { ParseResult } from '.' +import { parseByParser } from './parse-by-parser' +const ESLint = getESLint() + +runAsWorker(async (cwd: string, filePath: string): Promise => { + const eslint = new ESLint({ cwd }) + const config: Linter.FlatConfig = await eslint.calculateConfigForFile( + filePath + ) + const languageOptions = config.languageOptions || {} + const parserOptions = Object.assign( + { + sourceType: languageOptions.sourceType || 'module', + ecmaVersion: languageOptions.ecmaVersion || 'latest' + }, + languageOptions.parserOptions, + { + loc: true, + range: true, + raw: true, + tokens: true, + comment: true, + eslintVisitorKeys: true, + eslintScopeManager: true, + filePath + } + ) + + const result = parseByParser(filePath, languageOptions.parser, parserOptions) + if (!result) { + return null + } + + return { + ast: result.ast, + visitorKeys: result?.visitorKeys + } +}) diff --git a/package.json b/package.json index ef5c50aa..b4c71f82 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "files": [ "dist" ], - "main": "dist/lib/index.js", + "main": "dist/index.js", "scripts": { "build": "tsc --project ./tsconfig.build.json", "clean": "git clean -fx .nyc_output coverage dist docs/.vitepress/dist", @@ -75,6 +75,7 @@ "lodash": "^4.17.21", "parse5": "^7.1.2", "semver": "^7.5.4", + "synckit": "^0.9.0", "vue-eslint-parser": "^9.3.1", "yaml-eslint-parser": "^1.2.2" }, @@ -100,7 +101,7 @@ "eslint-config-prettier": "^9.0.0", "eslint-plugin-markdown": "^3.0.0", "eslint-plugin-prettier": "^4.2.1", - "eslint-plugin-vue": "^9.15.1", + "eslint-plugin-vue": "^9.24.1", "eslint4b": "^7.32.0", "espree": "^9.6.1", "esquery": "^1.5.0", @@ -114,6 +115,7 @@ "opener": "^1.5.2", "path-scurry": "^1.10.1", "prettier": "^2.8.8", + "ts-node": "^10.9.2", "typescript": "^5.1.6", "vitepress": "^1.0.2", "vue-eslint-editor": "^1.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index df1c16c3..64aabc33 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -53,6 +53,9 @@ dependencies: semver: specifier: ^7.5.4 version: 7.5.4 + synckit: + specifier: ^0.9.0 + version: 0.9.0 vue-eslint-parser: specifier: ^9.3.1 version: 9.3.1(eslint@8.57.0) @@ -125,8 +128,8 @@ devDependencies: specifier: ^4.2.1 version: 4.2.1(eslint-config-prettier@9.0.0)(eslint@8.57.0)(prettier@2.8.8) eslint-plugin-vue: - specifier: ^9.15.1 - version: 9.15.1(eslint@8.57.0) + specifier: ^9.24.1 + version: 9.24.1(eslint@8.57.0) eslint4b: specifier: ^7.32.0 version: 7.32.0 @@ -166,6 +169,9 @@ devDependencies: prettier: specifier: ^2.8.8 version: 2.8.8 + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@types/node@20.4.5)(typescript@5.1.6) typescript: specifier: ^5.1.6 version: 5.1.6 @@ -804,6 +810,13 @@ packages: prettier: 2.8.8 dev: true + /@cspotcode/source-map-support@0.8.1: + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + dev: true + /@docsearch/css@3.6.0: resolution: {integrity: sha512-+sbxb71sWre+PwDK7X2T8+bhS6clcVMLwBPznX45Qu6opJcgRjAp7gYSDzVFp187J+feSj5dNBN1mJoi6ckkUQ==} dev: true @@ -1074,7 +1087,7 @@ packages: ajv: 6.12.6 debug: 4.3.4(supports-color@8.1.1) espree: 9.6.1 - globals: 13.20.0 + globals: 13.24.0 ignore: 5.2.4 import-fresh: 3.3.0 js-yaml: 4.1.0 @@ -1108,7 +1121,7 @@ packages: resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} dependencies: - '@humanwhocodes/object-schema': 2.0.2 + '@humanwhocodes/object-schema': 2.0.3 debug: 4.3.4(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: @@ -1118,8 +1131,8 @@ packages: resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} - /@humanwhocodes/object-schema@2.0.2: - resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==} + /@humanwhocodes/object-schema@2.0.3: + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} /@intlify/core-base@9.3.0-beta.27: resolution: {integrity: sha512-hWI8dZh9rRLxDt1IqPJQnXgMW5KZrNX2Z4uJCN348gsPVvsN8eB/J71TcNJs+C1mfIjQPwtmzUWPNhTewi8QGg==} @@ -1221,6 +1234,13 @@ packages: '@jridgewell/sourcemap-codec': 1.4.14 dev: true + /@jridgewell/trace-mapping@0.3.9: + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.0 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + /@manypkg/find-root@1.1.0: resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} dependencies: @@ -1266,6 +1286,11 @@ packages: dev: false optional: true + /@pkgr/core@0.1.1: + resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + dev: false + /@rollup/rollup-android-arm-eabi@4.13.2: resolution: {integrity: sha512-3XFIDKWMFZrMnao1mJhnOT1h2g0169Os848NhhmGweEcfJ4rCi+3yMCOLG4zA61rbJdkcrM/DjVZm9Hg5p5w7g==} cpu: [arm] @@ -1396,6 +1421,22 @@ packages: shiki: 1.2.3 dev: true + /@tsconfig/node10@1.0.11: + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + dev: true + + /@tsconfig/node12@1.0.11: + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + dev: true + + /@tsconfig/node14@1.0.3: + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + dev: true + + /@tsconfig/node16@1.0.4: + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + dev: true + /@types/debug@4.1.8: resolution: {integrity: sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==} dependencies: @@ -1850,6 +1891,11 @@ packages: dependencies: acorn: 8.10.0 + /acorn-walk@8.3.2: + resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} + engines: {node: '>=0.4.0'} + dev: true + /acorn@7.4.1: resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} engines: {node: '>=0.4.0'} @@ -1955,6 +2001,10 @@ packages: resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==} dev: true + /arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + dev: true + /argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} dependencies: @@ -2261,6 +2311,10 @@ packages: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} dev: true + /create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + dev: true + /cross-spawn@5.1.0: resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} dependencies: @@ -2372,6 +2426,11 @@ packages: engines: {node: '>=8'} dev: true + /diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + dev: true + /diff@5.0.0: resolution: {integrity: sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==} engines: {node: '>=0.3.1'} @@ -2606,19 +2665,20 @@ packages: prettier-linter-helpers: 1.0.0 dev: true - /eslint-plugin-vue@9.15.1(eslint@8.57.0): - resolution: {integrity: sha512-CJE/oZOslvmAR9hf8SClTdQ9JLweghT6JCBQNrT2Iel1uVw0W0OLJxzvPd6CxmABKCvLrtyDnqGV37O7KQv6+A==} + /eslint-plugin-vue@9.24.1(eslint@8.57.0): + resolution: {integrity: sha512-wk3SuwmS1pZdcuJlokGYEi/buDOwD6KltvhIZyOnpJ/378dcQ4zchu9PAMbbLAaydCz1iYc5AozszcOOgZIIOg==} engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: - eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 + eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) eslint: 8.57.0 + globals: 13.24.0 natural-compare: 1.4.0 nth-check: 2.1.1 - postcss-selector-parser: 6.0.13 - semver: 7.5.4 - vue-eslint-parser: 9.3.1(eslint@8.57.0) + postcss-selector-parser: 6.0.16 + semver: 7.6.0 + vue-eslint-parser: 9.4.2(eslint@8.57.0) xml-name-validator: 4.0.0 transitivePeerDependencies: - supports-color @@ -2723,7 +2783,7 @@ packages: file-entry-cache: 6.0.1 find-up: 5.0.0 glob-parent: 6.0.2 - globals: 13.20.0 + globals: 13.24.0 graphemer: 1.4.0 ignore: 5.2.4 imurmurhash: 0.1.4 @@ -2858,7 +2918,7 @@ packages: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} dependencies: - flat-cache: 3.0.4 + flat-cache: 3.2.0 /fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} @@ -2898,11 +2958,12 @@ packages: pkg-dir: 4.2.0 dev: true - /flat-cache@3.0.4: - resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} + /flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} engines: {node: ^10.12.0 || >=12.0.0} dependencies: - flatted: 3.2.7 + flatted: 3.3.1 + keyv: 4.5.4 rimraf: 3.0.2 /flat@5.0.2: @@ -2910,8 +2971,8 @@ packages: hasBin: true dev: true - /flatted@3.2.7: - resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} + /flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} /focus-trap@7.5.4: resolution: {integrity: sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==} @@ -3099,6 +3160,12 @@ packages: dependencies: type-fest: 0.20.2 + /globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + /globals@15.0.0: resolution: {integrity: sha512-m/C/yR4mjO6pXDTm9/R/SpYTAIyaUB4EOzcaaMEl7mds7Mshct9GfejiJNQGjHHbdMPey13Kpu4TMbYi9ex1pw==} engines: {node: '>=18'} @@ -3593,6 +3660,9 @@ packages: hasBin: true dev: true + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + /json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} dev: true @@ -3633,6 +3703,11 @@ packages: graceful-fs: 4.2.11 dev: true + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + dependencies: + json-buffer: 3.0.1 + /kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} @@ -3790,6 +3865,10 @@ packages: semver: 7.5.4 dev: true + /make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + dev: true + /map-obj@1.0.1: resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} engines: {node: '>=0.10.0'} @@ -4310,8 +4389,8 @@ packages: find-up: 4.1.0 dev: true - /postcss-selector-parser@6.0.13: - resolution: {integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==} + /postcss-selector-parser@6.0.16: + resolution: {integrity: sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==} engines: {node: '>=4'} dependencies: cssesc: 3.0.0 @@ -4590,6 +4669,14 @@ packages: dependencies: lru-cache: 6.0.0 + /semver@7.6.0: + resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + /serialize-javascript@6.0.0: resolution: {integrity: sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==} dependencies: @@ -4877,6 +4964,14 @@ packages: engines: {node: '>= 0.4'} dev: true + /synckit@0.9.0: + resolution: {integrity: sha512-7RnqIMq572L8PeEzKeBINYEJDDxpcH8JEgLwUqBd3TkofhFRbkq4QLR0u+36avGAhCRbk2nnmjcW9SE531hPDg==} + engines: {node: ^14.18.0 || >=16.0.0} + dependencies: + '@pkgr/core': 0.1.1 + tslib: 2.6.2 + dev: false + /tabbable@6.2.0: resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} dev: true @@ -4935,6 +5030,41 @@ packages: typescript: 5.1.6 dev: true + /ts-node@10.9.2(@types/node@20.4.5)(typescript@5.1.6): + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 20.4.5 + acorn: 8.10.0 + acorn-walk: 8.3.2 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.1.6 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + dev: true + + /tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + dev: false + /tty-table@4.2.1: resolution: {integrity: sha512-xz0uKo+KakCQ+Dxj1D/tKn2FSyreSYWzdkL/BYhgN6oMW808g8QRMuh1atAV9fjTPbWBjfbkKQpI/5rEcnAc7g==} engines: {node: '>=8.0.0'} @@ -5069,6 +5199,10 @@ packages: hasBin: true dev: true + /v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + dev: true + /validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} dependencies: @@ -5203,6 +5337,25 @@ packages: semver: 7.5.4 transitivePeerDependencies: - supports-color + dev: false + + /vue-eslint-parser@9.4.2(eslint@8.57.0): + resolution: {integrity: sha512-Ry9oiGmCAK91HrKMtCrKFWmSFWvYkpGglCeFAIqDdr9zdXmMMpJOmUJS7WWsW7fX81h6mwHmUZCQQ1E0PkSwYQ==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '>=6.0.0' + dependencies: + debug: 4.3.4(supports-color@8.1.1) + eslint: 8.57.0 + eslint-scope: 7.2.1 + eslint-visitor-keys: 3.4.1 + espree: 9.6.1 + esquery: 1.5.0 + lodash: 4.17.21 + semver: 7.6.0 + transitivePeerDependencies: + - supports-color + dev: true /vue-github-button@3.1.0: resolution: {integrity: sha512-S69NFalucetUW4AjMkLlHY/fgTWu6LEGEtehMewXRNLnjODhDvJj4lwt0u0BSPXLTDB+PLk1tWlwx3pw+ItLgQ==} @@ -5456,6 +5609,11 @@ packages: yargs-parser: 21.1.1 dev: true + /yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + dev: true + /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} diff --git a/tests/fixtures/no-unused-keys/invalid/typescript-with-flat-config/eslint.config.cjs b/tests/fixtures/no-unused-keys/invalid/typescript-with-flat-config/eslint.config.cjs new file mode 100644 index 00000000..06f6c5ff --- /dev/null +++ b/tests/fixtures/no-unused-keys/invalid/typescript-with-flat-config/eslint.config.cjs @@ -0,0 +1,39 @@ +const base = require("../../../../../lib/configs/flat/base"); +module.exports = [ + ...base, + { + files: ['**/*.vue', '*.vue'], + languageOptions: { + parser: require('vue-eslint-parser'), + parserOptions: { + parser: "@typescript-eslint/parser", + }, + }, + }, + { + files: ['**/*.ts', '*.ts'], + languageOptions: { + parser: require('@typescript-eslint/parser'), + }, + }, + { + rules: { + "@intlify/vue-i18n/no-unused-keys": [ + "error", + { + src: "./src", + extensions: [".tsx", ".ts", ".vue"], + enableFix: true, + }, + ], + }, + settings: { + "vue-i18n": { + localeDir: { + pattern: `./locales/*.{json,yaml,yml}`, + localeKey: "file", + }, + }, + }, + }, +]; diff --git a/tests/fixtures/no-unused-keys/invalid/typescript-with-flat-config/locales/en.json b/tests/fixtures/no-unused-keys/invalid/typescript-with-flat-config/locales/en.json new file mode 100644 index 00000000..da032bfa --- /dev/null +++ b/tests/fixtures/no-unused-keys/invalid/typescript-with-flat-config/locales/en.json @@ -0,0 +1,13 @@ +{ + "hello": "hello world", + "messages": { + "hello": "hi DIO!", + "link": "@:message.hello", + "nested": { + "hello": "hi jojo!" + } + }, + "hello_dio": "hello underscore DIO!", + "hello {name}": "hello {name}!", + "hello-dio": "hello hyphen DIO!" +} diff --git a/tests/fixtures/no-unused-keys/invalid/typescript-with-flat-config/locales/ja.yaml b/tests/fixtures/no-unused-keys/invalid/typescript-with-flat-config/locales/ja.yaml new file mode 100644 index 00000000..422fec42 --- /dev/null +++ b/tests/fixtures/no-unused-keys/invalid/typescript-with-flat-config/locales/ja.yaml @@ -0,0 +1,9 @@ +hello: "ハローワールド" +messages: + hello: "こんにちは、DIO!" + link: "@:message.hello" + nested: + hello: "こんにちは、ジョジョ!" +hello_dio: "こんにちは、アンダースコア DIO!" +"hello {name}": "こんにちは、{name}!" +hello-dio: "こんにちは、ハイフン DIO!" diff --git a/tests/fixtures/no-unused-keys/invalid/typescript-with-flat-config/src/App.vue b/tests/fixtures/no-unused-keys/invalid/typescript-with-flat-config/src/App.vue new file mode 100644 index 00000000..39f19675 --- /dev/null +++ b/tests/fixtures/no-unused-keys/invalid/typescript-with-flat-config/src/App.vue @@ -0,0 +1,14 @@ + + + diff --git a/tests/fixtures/no-unused-keys/invalid/typescript-with-flat-config/src/main.ts b/tests/fixtures/no-unused-keys/invalid/typescript-with-flat-config/src/main.ts new file mode 100644 index 00000000..9beae6b4 --- /dev/null +++ b/tests/fixtures/no-unused-keys/invalid/typescript-with-flat-config/src/main.ts @@ -0,0 +1,2 @@ +const $t = (a:any) => {} +$t('hello') diff --git a/tests/lib/rules/no-unused-keys.ts b/tests/lib/rules/no-unused-keys.ts index 1f671420..5e3c0a1a 100644 --- a/tests/lib/rules/no-unused-keys.ts +++ b/tests/lib/rules/no-unused-keys.ts @@ -5,8 +5,6 @@ import { RuleTester } from '../eslint-compat' import { join } from 'node:path' import rule from '../../../lib/rules/no-unused-keys' import { getTestCasesFromFixtures } from '../test-utils' -import { satisfies } from 'semver' -import { version } from 'eslint/package.json' import * as vueParser from 'vue-eslint-parser' import * as tsParser from '@typescript-eslint/parser' @@ -126,65 +124,63 @@ new RuleTester({ ` }, - // eslint-disable-next-line @typescript-eslint/no-var-requires - ...(satisfies(version, '>=6') - ? [ - ...getTestCasesFromFixtures({ - cwd: join(cwdRoot, './valid/vue-cli-format'), - localeDir: `./locales/*.{json,yaml,yml}`, - options: [ - { - src: '.' - } - ] - }), - ...getTestCasesFromFixtures({ - cwd: join(cwdRoot, './valid/constructor-option-format'), - localeDir: { - pattern: `./locales/*.{json,yaml,yml}`, - localeKey: 'key' - }, - options: [ - { - src: '.' - } - ] - }), - ...getTestCasesFromFixtures({ - cwd: join(cwdRoot, './valid/multiple-locales'), - localeDir: [ - `./locales1/*.{json,yaml,yml}`, - { - pattern: `./locales2/*.{json,yaml,yml}`, - localeKey: 'file' - }, - { - pattern: `./locales3/*.{json,yaml,yml}`, - localeKey: 'key' - } - ], - options: [ - { - src: '.' - } - ] - }), - ...getTestCasesFromFixtures({ - cwd: join(cwdRoot, './valid/path-locales'), - localeDir: { - pattern: `./locales/**/*.{json,yaml,yml}`, - localeKey: 'path', - localePattern: - /^.*\/(?[A-Za-z0-9-_]+)\/.*\.(json5?|ya?ml)$/ - }, - options: [ - { - src: '.' - } - ] - }) - ] - : []), + ...getTestCasesFromFixtures({ + eslint: '>=6', + cwd: join(cwdRoot, './valid/vue-cli-format'), + localeDir: `./locales/*.{json,yaml,yml}`, + options: [ + { + src: '.' + } + ] + }), + ...getTestCasesFromFixtures({ + eslint: '>=6', + cwd: join(cwdRoot, './valid/constructor-option-format'), + localeDir: { + pattern: `./locales/*.{json,yaml,yml}`, + localeKey: 'key' + }, + options: [ + { + src: '.' + } + ] + }), + ...getTestCasesFromFixtures({ + eslint: '>=6', + cwd: join(cwdRoot, './valid/multiple-locales'), + localeDir: [ + `./locales1/*.{json,yaml,yml}`, + { + pattern: `./locales2/*.{json,yaml,yml}`, + localeKey: 'file' + }, + { + pattern: `./locales3/*.{json,yaml,yml}`, + localeKey: 'key' + } + ], + options: [ + { + src: '.' + } + ] + }), + ...getTestCasesFromFixtures({ + eslint: '>=6', + cwd: join(cwdRoot, './valid/path-locales'), + localeDir: { + pattern: `./locales/**/*.{json,yaml,yml}`, + localeKey: 'path', + localePattern: /^.*\/(?[A-Za-z0-9-_]+)\/.*\.(json5?|ya?ml)$/ + }, + options: [ + { + src: '.' + } + ] + }), { filename: 'test.vue', code: ` @@ -2132,17 +2128,15 @@ ${' '.repeat(6)} } } ), - // eslint-disable-next-line @typescript-eslint/no-var-requires - ...(satisfies(version, '>=6') - ? [ - ...getTestCasesFromFixtures( - { - cwd: join(cwdRoot, './invalid/vue-cli-format'), - localeDir: `./locales/*.{json,yaml,yml}`, - options: [{ src: '.', enableFix: true }] - }, - (() => { - const fixAllEn = `{ + ...getTestCasesFromFixtures( + { + eslint: '>=6', + cwd: join(cwdRoot, './invalid/vue-cli-format'), + localeDir: `./locales/*.{json,yaml,yml}`, + options: [{ src: '.', enableFix: true }] + }, + (() => { + const fixAllEn = `{ "hello": "hello world", "messages": { "hello": "hi DIO!", @@ -2153,26 +2147,26 @@ ${' '.repeat(6)} "hello {name}": "hello {name}!" } ` - const fixAllJa = `hello: "ハローワールド" + const fixAllJa = `hello: "ハローワールド" messages: hello: "こんにちは、DIO!" nested: {} hello_dio: "こんにちは、アンダースコア DIO!" "hello {name}": "こんにちは、{name}!" ` - return { - 'src/App.vue': true, - 'src/main.js': true, - 'locales/en.json': { - output: fixAllEn, - errors: [ - { - message: "unused 'messages.link' key", - line: 5, - suggestions: [ - { - desc: "Remove the 'messages.link' key.", - output: `{ + return { + 'src/App.vue': true, + 'src/main.js': true, + 'locales/en.json': { + output: fixAllEn, + errors: [ + { + message: "unused 'messages.link' key", + line: 5, + suggestions: [ + { + desc: "Remove the 'messages.link' key.", + output: `{ "hello": "hello world", "messages": { "hello": "hi DIO!", @@ -2185,20 +2179,20 @@ hello_dio: "こんにちは、アンダースコア DIO!" "hello-dio": "hello hyphen DIO!" } ` - }, - { - desc: 'Remove all unused keys.', - output: fixAllEn - } - ] - }, - { - message: "unused 'messages.nested.hello' key", - line: 7, - suggestions: [ - { - desc: "Remove the 'messages.nested.hello' key.", - output: `{ + }, + { + desc: 'Remove all unused keys.', + output: fixAllEn + } + ] + }, + { + message: "unused 'messages.nested.hello' key", + line: 7, + suggestions: [ + { + desc: "Remove the 'messages.nested.hello' key.", + output: `{ "hello": "hello world", "messages": { "hello": "hi DIO!", @@ -2211,20 +2205,20 @@ hello_dio: "こんにちは、アンダースコア DIO!" "hello-dio": "hello hyphen DIO!" } ` - }, - { - desc: 'Remove all unused keys.', - output: fixAllEn - } - ] - }, - { - message: "unused 'hello-dio' key", - line: 12, - suggestions: [ - { - desc: "Remove the 'hello-dio' key.", - output: `{ + }, + { + desc: 'Remove all unused keys.', + output: fixAllEn + } + ] + }, + { + message: "unused 'hello-dio' key", + line: 12, + suggestions: [ + { + desc: "Remove the 'hello-dio' key.", + output: `{ "hello": "hello world", "messages": { "hello": "hi DIO!", @@ -2237,25 +2231,25 @@ hello_dio: "こんにちは、アンダースコア DIO!" "hello {name}": "hello {name}!" } ` - }, - { - desc: 'Remove all unused keys.', - output: fixAllEn - } - ] - } - ] - }, - 'locales/ja.yaml': { - output: fixAllJa, - errors: [ - { - message: "unused 'messages.link' key", - line: 4, - suggestions: [ - { - desc: "Remove the 'messages.link' key.", - output: `hello: "ハローワールド" + }, + { + desc: 'Remove all unused keys.', + output: fixAllEn + } + ] + } + ] + }, + 'locales/ja.yaml': { + output: fixAllJa, + errors: [ + { + message: "unused 'messages.link' key", + line: 4, + suggestions: [ + { + desc: "Remove the 'messages.link' key.", + output: `hello: "ハローワールド" messages: hello: "こんにちは、DIO!" nested: @@ -2264,20 +2258,20 @@ hello_dio: "こんにちは、アンダースコア DIO!" "hello {name}": "こんにちは、{name}!" hello-dio: "こんにちは、ハイフン DIO!" ` - }, - { - desc: 'Remove all unused keys.', - output: fixAllJa - } - ] - }, - { - message: "unused 'messages.nested.hello' key", - line: 6, - suggestions: [ - { - desc: "Remove the 'messages.nested.hello' key.", - output: `hello: "ハローワールド" + }, + { + desc: 'Remove all unused keys.', + output: fixAllJa + } + ] + }, + { + message: "unused 'messages.nested.hello' key", + line: 6, + suggestions: [ + { + desc: "Remove the 'messages.nested.hello' key.", + output: `hello: "ハローワールド" messages: hello: "こんにちは、DIO!" link: "@:message.hello" @@ -2286,20 +2280,20 @@ hello_dio: "こんにちは、アンダースコア DIO!" "hello {name}": "こんにちは、{name}!" hello-dio: "こんにちは、ハイフン DIO!" ` - }, - { - desc: 'Remove all unused keys.', - output: fixAllJa - } - ] - }, - { - message: "unused 'hello-dio' key", - line: 9, - suggestions: [ - { - desc: "Remove the 'hello-dio' key.", - output: `hello: "ハローワールド" + }, + { + desc: 'Remove all unused keys.', + output: fixAllJa + } + ] + }, + { + message: "unused 'hello-dio' key", + line: 9, + suggestions: [ + { + desc: "Remove the 'hello-dio' key.", + output: `hello: "ハローワールド" messages: hello: "こんにちは、DIO!" link: "@:message.hello" @@ -2308,29 +2302,30 @@ messages: hello_dio: "こんにちは、アンダースコア DIO!" "hello {name}": "こんにちは、{name}!" ` - }, - { - desc: 'Remove all unused keys.', - output: fixAllJa - } - ] - } - ] - } + }, + { + desc: 'Remove all unused keys.', + output: fixAllJa + } + ] } - })() - ), - ...getTestCasesFromFixtures( - { - cwd: join(cwdRoot, './invalid/constructor-option-format'), - localeDir: { - pattern: `./locales/*.{json,yaml,yml}`, - localeKey: 'key' - }, - options: [{ src: '.', enableFix: true }] - }, - (() => { - const fixAll = `{ + ] + } + } + })() + ), + ...getTestCasesFromFixtures( + { + eslint: '>=6', + cwd: join(cwdRoot, './invalid/constructor-option-format'), + localeDir: { + pattern: `./locales/*.{json,yaml,yml}`, + localeKey: 'key' + }, + options: [{ src: '.', enableFix: true }] + }, + (() => { + const fixAll = `{ "en": { "hello": "hello world", "messages": { @@ -2353,19 +2348,19 @@ hello_dio: "こんにちは、アンダースコア DIO!" } } ` - return { - 'src/App.vue': true, - 'src/main.js': true, - 'locales/index.json': { - output: fixAll, - errors: [ - { - message: "unused 'en.messages.link' key", - line: 6, - suggestions: [ - { - desc: "Remove the 'en.messages.link' key.", - output: `{ + return { + 'src/App.vue': true, + 'src/main.js': true, + 'locales/index.json': { + output: fixAll, + errors: [ + { + message: "unused 'en.messages.link' key", + line: 6, + suggestions: [ + { + desc: "Remove the 'en.messages.link' key.", + output: `{ "en": { "hello": "hello world", "messages": { @@ -2393,20 +2388,20 @@ hello_dio: "こんにちは、アンダースコア DIO!" } } ` - }, - { - desc: 'Remove all unused keys.', - output: fixAll - } - ] - }, - { - message: "unused 'en.messages.nested.hello' key", - line: 8, - suggestions: [ - { - desc: "Remove the 'en.messages.nested.hello' key.", - output: `{ + }, + { + desc: 'Remove all unused keys.', + output: fixAll + } + ] + }, + { + message: "unused 'en.messages.nested.hello' key", + line: 8, + suggestions: [ + { + desc: "Remove the 'en.messages.nested.hello' key.", + output: `{ "en": { "hello": "hello world", "messages": { @@ -2434,20 +2429,20 @@ hello_dio: "こんにちは、アンダースコア DIO!" } } ` - }, - { - desc: 'Remove all unused keys.', - output: fixAll - } - ] - }, - { - message: "unused 'en.hello-dio' key", - line: 13, - suggestions: [ - { - desc: "Remove the 'en.hello-dio' key.", - output: `{ + }, + { + desc: 'Remove all unused keys.', + output: fixAll + } + ] + }, + { + message: "unused 'en.hello-dio' key", + line: 13, + suggestions: [ + { + desc: "Remove the 'en.hello-dio' key.", + output: `{ "en": { "hello": "hello world", "messages": { @@ -2475,20 +2470,20 @@ hello_dio: "こんにちは、アンダースコア DIO!" } } ` - }, - { - desc: 'Remove all unused keys.', - output: fixAll - } - ] - }, - { - message: "unused 'ja.messages.link' key", - line: 19, - suggestions: [ - { - desc: "Remove the 'ja.messages.link' key.", - output: `{ + }, + { + desc: 'Remove all unused keys.', + output: fixAll + } + ] + }, + { + message: "unused 'ja.messages.link' key", + line: 19, + suggestions: [ + { + desc: "Remove the 'ja.messages.link' key.", + output: `{ "en": { "hello": "hello world", "messages": { @@ -2516,20 +2511,20 @@ hello_dio: "こんにちは、アンダースコア DIO!" } } ` - }, - { - desc: 'Remove all unused keys.', - output: fixAll - } - ] - }, - { - message: "unused 'ja.messages.nested.hello' key", - line: 21, - suggestions: [ - { - desc: "Remove the 'ja.messages.nested.hello' key.", - output: `{ + }, + { + desc: 'Remove all unused keys.', + output: fixAll + } + ] + }, + { + message: "unused 'ja.messages.nested.hello' key", + line: 21, + suggestions: [ + { + desc: "Remove the 'ja.messages.nested.hello' key.", + output: `{ "en": { "hello": "hello world", "messages": { @@ -2557,20 +2552,20 @@ hello_dio: "こんにちは、アンダースコア DIO!" } } ` - }, - { - desc: 'Remove all unused keys.', - output: fixAll - } - ] - }, - { - message: "unused 'ja.hello-dio' key", - line: 26, - suggestions: [ - { - desc: "Remove the 'ja.hello-dio' key.", - output: `{ + }, + { + desc: 'Remove all unused keys.', + output: fixAll + } + ] + }, + { + message: "unused 'ja.hello-dio' key", + line: 26, + suggestions: [ + { + desc: "Remove the 'ja.hello-dio' key.", + output: `{ "en": { "hello": "hello world", "messages": { @@ -2598,39 +2593,40 @@ hello_dio: "こんにちは、アンダースコア DIO!" } } ` - }, - { - desc: 'Remove all unused keys.', - output: fixAll - } - ] - } - ] - } + }, + { + desc: 'Remove all unused keys.', + output: fixAll + } + ] } - })() - ), - ...getTestCasesFromFixtures( - { - cwd: join(cwdRoot, './invalid/multiple-locales'), - localeDir: [ - `./locales1/*.{json,yaml,yml}`, - { - pattern: `./locales2/*.{json,yaml,yml}`, - localeKey: 'file' - }, - { - pattern: `./locales3/*.{json,yaml,yml}`, - localeKey: 'key' - } - ], - options: [{ src: '.', enableFix: true }] - }, - { - 'src/App.vue': true, - 'src/main.js': true, - 'locales1/en.json': { - output: `{ + ] + } + } + })() + ), + ...getTestCasesFromFixtures( + { + eslint: '>=6', + cwd: join(cwdRoot, './invalid/multiple-locales'), + localeDir: [ + `./locales1/*.{json,yaml,yml}`, + { + pattern: `./locales2/*.{json,yaml,yml}`, + localeKey: 'file' + }, + { + pattern: `./locales3/*.{json,yaml,yml}`, + localeKey: 'key' + } + ], + options: [{ src: '.', enableFix: true }] + }, + { + 'src/App.vue': true, + 'src/main.js': true, + 'locales1/en.json': { + output: `{ "hello": "hello world", "messages": { "hello": "hi DIO!", @@ -2639,14 +2635,14 @@ hello_dio: "こんにちは、アンダースコア DIO!" } } `, - errors: [ - { - line: 5, - message: "unused 'messages.link' key", - suggestions: [ - { - desc: "Remove the 'messages.link' key.", - output: `{ + errors: [ + { + line: 5, + message: "unused 'messages.link' key", + suggestions: [ + { + desc: "Remove the 'messages.link' key.", + output: `{ "hello": "hello world", "messages": { "hello": "hi DIO!", @@ -2656,10 +2652,10 @@ hello_dio: "こんにちは、アンダースコア DIO!" } } ` - }, - { - desc: 'Remove all unused keys.', - output: `{ + }, + { + desc: 'Remove all unused keys.', + output: `{ "hello": "hello world", "messages": { "hello": "hi DIO!", @@ -2668,16 +2664,16 @@ hello_dio: "こんにちは、アンダースコア DIO!" } } ` - } - ] - }, - { - line: 7, - message: "unused 'messages.nested.hello' key", - suggestions: [ - { - desc: "Remove the 'messages.nested.hello' key.", - output: `{ + } + ] + }, + { + line: 7, + message: "unused 'messages.nested.hello' key", + suggestions: [ + { + desc: "Remove the 'messages.nested.hello' key.", + output: `{ "hello": "hello world", "messages": { "hello": "hi DIO!", @@ -2687,10 +2683,10 @@ hello_dio: "こんにちは、アンダースコア DIO!" } } ` - }, - { - desc: 'Remove all unused keys.', - output: `{ + }, + { + desc: 'Remove all unused keys.', + output: `{ "hello": "hello world", "messages": { "hello": "hi DIO!", @@ -2699,36 +2695,36 @@ hello_dio: "こんにちは、アンダースコア DIO!" } } ` - } - ] - } - ] - }, - 'locales2/en.json': { - output: `{ + } + ] + } + ] + }, + 'locales2/en.json': { + output: `{ "hello_dio": "hello underscore DIO!", "hello {name}": "hello {name}!" } `, - errors: [ - { - line: 4, - message: "unused 'hello-dio' key", - suggestions: [ - { - desc: "Remove the 'hello-dio' key.", - output: `{ + errors: [ + { + line: 4, + message: "unused 'hello-dio' key", + suggestions: [ + { + desc: "Remove the 'hello-dio' key.", + output: `{ "hello_dio": "hello underscore DIO!", "hello {name}": "hello {name}!" } ` - } - ] - } - ] - }, - 'locales3/index.json': { - output: `{ + } + ] + } + ] + }, + 'locales3/index.json': { + output: `{ "ja": { "hello": "ハローワールド", "messages": { @@ -2741,14 +2737,14 @@ hello_dio: "こんにちは、アンダースコア DIO!" } } `, - errors: [ - { - line: 6, - message: "unused 'ja.messages.link' key", - suggestions: [ - { - desc: "Remove the 'ja.messages.link' key.", - output: `{ + errors: [ + { + line: 6, + message: "unused 'ja.messages.link' key", + suggestions: [ + { + desc: "Remove the 'ja.messages.link' key.", + output: `{ "ja": { "hello": "ハローワールド", "messages": { @@ -2763,10 +2759,10 @@ hello_dio: "こんにちは、アンダースコア DIO!" } } ` - }, - { - desc: 'Remove all unused keys.', - output: `{ + }, + { + desc: 'Remove all unused keys.', + output: `{ "ja": { "hello": "ハローワールド", "messages": { @@ -2779,16 +2775,16 @@ hello_dio: "こんにちは、アンダースコア DIO!" } } ` - } - ] - }, - { - line: 8, - message: "unused 'ja.messages.nested.hello' key", - suggestions: [ - { - desc: "Remove the 'ja.messages.nested.hello' key.", - output: `{ + } + ] + }, + { + line: 8, + message: "unused 'ja.messages.nested.hello' key", + suggestions: [ + { + desc: "Remove the 'ja.messages.nested.hello' key.", + output: `{ "ja": { "hello": "ハローワールド", "messages": { @@ -2803,10 +2799,10 @@ hello_dio: "こんにちは、アンダースコア DIO!" } } ` - }, - { - desc: 'Remove all unused keys.', - output: `{ + }, + { + desc: 'Remove all unused keys.', + output: `{ "ja": { "hello": "ハローワールド", "messages": { @@ -2819,16 +2815,16 @@ hello_dio: "こんにちは、アンダースコア DIO!" } } ` - } - ] - }, - { - line: 13, - message: "unused 'ja.hello-dio' key", - suggestions: [ - { - desc: "Remove the 'ja.hello-dio' key.", - output: `{ + } + ] + }, + { + line: 13, + message: "unused 'ja.hello-dio' key", + suggestions: [ + { + desc: "Remove the 'ja.hello-dio' key.", + output: `{ "ja": { "hello": "ハローワールド", "messages": { @@ -2843,10 +2839,10 @@ hello_dio: "こんにちは、アンダースコア DIO!" } } ` - }, - { - desc: 'Remove all unused keys.', - output: `{ + }, + { + desc: 'Remove all unused keys.', + output: `{ "ja": { "hello": "ハローワールド", "messages": { @@ -2859,35 +2855,69 @@ hello_dio: "こんにちは、アンダースコア DIO!" } } ` - } - ] - } - ] - } + } + ] } - ), - ...getTestCasesFromFixtures( + ] + } + } + ), + ...getTestCasesFromFixtures( + { + eslint: '>=6', + cwd: join(cwdRoot, './invalid/typescript'), + localeDir: { + pattern: `./locales/*.{json,yaml,yml}`, + localeKey: 'file' + }, + options: [ + { + src: './src', + extensions: ['.tsx', '.ts', '.vue'], + enableFix: true + } + ], + languageOptions: { + parser: tsParser + } + }, + { + 'src/App.vue': true, + 'locales/en.json': { + output: `{ + "hello": "hello world", + "messages": { + "hello": "hi DIO!", + "nested": { + } + }, + "hello_dio": "hello underscore DIO!", + "hello {name}": "hello {name}!" +} +`, + errors: [ { - cwd: join(cwdRoot, './invalid/typescript'), - localeDir: { - pattern: `./locales/*.{json,yaml,yml}`, - localeKey: 'file' - }, - options: [ + message: "unused 'messages.link' key", + suggestions: [ { - src: './src', - extensions: ['.tsx', '.ts', '.vue'], - enableFix: true - } - ], - languageOptions: { - parser: tsParser - } - }, - { - 'src/App.vue': true, - 'locales/en.json': { - output: `{ + desc: "Remove the 'messages.link' key.", + output: `{ + "hello": "hello world", + "messages": { + "hello": "hi DIO!", + "nested": { + "hello": "hi jojo!" + } + }, + "hello_dio": "hello underscore DIO!", + "hello {name}": "hello {name}!", + "hello-dio": "hello hyphen DIO!" +} +` + }, + { + desc: 'Remove all unused keys.', + output: `{ "hello": "hello world", "messages": { "hello": "hi DIO!", @@ -2897,30 +2927,410 @@ hello_dio: "こんにちは、アンダースコア DIO!" "hello_dio": "hello underscore DIO!", "hello {name}": "hello {name}!" } -`, - errors: [ - "unused 'messages.link' key", - "unused 'messages.nested.hello' key", - "unused 'hello-dio' key" - ] - }, - 'locales/ja.yaml': { - output: `hello: "ハローワールド" -messages: - hello: "こんにちは、DIO!" +` + } + ] + }, + { + message: "unused 'messages.nested.hello' key", + suggestions: [ + { + desc: "Remove the 'messages.nested.hello' key.", + output: `{ + "hello": "hello world", + "messages": { + "hello": "hi DIO!", + "link": "@:message.hello", + "nested": { + } + }, + "hello_dio": "hello underscore DIO!", + "hello {name}": "hello {name}!", + "hello-dio": "hello hyphen DIO!" +} +` + }, + { + desc: 'Remove all unused keys.', + output: `{ + "hello": "hello world", + "messages": { + "hello": "hi DIO!", + "nested": { + } + }, + "hello_dio": "hello underscore DIO!", + "hello {name}": "hello {name}!" +} +` + } + ] + }, + { + message: "unused 'hello-dio' key", + suggestions: [ + { + desc: "Remove the 'hello-dio' key.", + output: `{ + "hello": "hello world", + "messages": { + "hello": "hi DIO!", + "link": "@:message.hello", + "nested": { + "hello": "hi jojo!" + } + }, + "hello_dio": "hello underscore DIO!", + "hello {name}": "hello {name}!" +} +` + }, + { + desc: 'Remove all unused keys.', + output: `{ + "hello": "hello world", + "messages": { + "hello": "hi DIO!", + "nested": { + } + }, + "hello_dio": "hello underscore DIO!", + "hello {name}": "hello {name}!" +} +` + } + ] + } + ] + }, + 'locales/ja.yaml': { + output: `hello: "ハローワールド" +messages: + hello: "こんにちは、DIO!" nested: {} hello_dio: "こんにちは、アンダースコア DIO!" "hello {name}": "こんにちは、{name}!" `, - errors: [ - "unused 'messages.link' key", - "unused 'messages.nested.hello' key", - "unused 'hello-dio' key" - ] - } + errors: [ + { + message: "unused 'messages.link' key", + suggestions: [ + { + desc: "Remove the 'messages.link' key.", + output: `hello: "ハローワールド" +messages: + hello: "こんにちは、DIO!" + nested: + hello: "こんにちは、ジョジョ!" +hello_dio: "こんにちは、アンダースコア DIO!" +"hello {name}": "こんにちは、{name}!" +hello-dio: "こんにちは、ハイフン DIO!" +` + }, + { + desc: 'Remove all unused keys.', + output: `hello: "ハローワールド" +messages: + hello: "こんにちは、DIO!" + nested: {} +hello_dio: "こんにちは、アンダースコア DIO!" +"hello {name}": "こんにちは、{name}!" +` + } + ] + }, + { + message: "unused 'messages.nested.hello' key", + suggestions: [ + { + desc: "Remove the 'messages.nested.hello' key.", + output: `hello: "ハローワールド" +messages: + hello: "こんにちは、DIO!" + link: "@:message.hello" + nested: {} +hello_dio: "こんにちは、アンダースコア DIO!" +"hello {name}": "こんにちは、{name}!" +hello-dio: "こんにちは、ハイフン DIO!" +` + }, + { + desc: 'Remove all unused keys.', + output: `hello: "ハローワールド" +messages: + hello: "こんにちは、DIO!" + nested: {} +hello_dio: "こんにちは、アンダースコア DIO!" +"hello {name}": "こんにちは、{name}!" +` + } + ] + }, + { + message: "unused 'hello-dio' key", + suggestions: [ + { + desc: "Remove the 'hello-dio' key.", + output: `hello: "ハローワールド" +messages: + hello: "こんにちは、DIO!" + link: "@:message.hello" + nested: + hello: "こんにちは、ジョジョ!" +hello_dio: "こんにちは、アンダースコア DIO!" +"hello {name}": "こんにちは、{name}!" +` + }, + { + desc: 'Remove all unused keys.', + output: `hello: "ハローワールド" +messages: + hello: "こんにちは、DIO!" + nested: {} +hello_dio: "こんにちは、アンダースコア DIO!" +"hello {name}": "こんにちは、{name}!" +` + } + ] } - ) - ] - : []) + ] + } + } + ), + ...getTestCasesFromFixtures( + { + eslint: '>=8', + cwd: join(cwdRoot, './invalid/typescript-with-flat-config'), + localeDir: { + pattern: `./locales/*.{json,yaml,yml}`, + localeKey: 'file' + }, + options: [ + { + src: './src', + extensions: ['.tsx', '.ts', '.vue'], + enableFix: true + } + ], + languageOptions: { + parser: tsParser + } + }, + { + 'src/App.vue': true, + 'locales/en.json': { + output: `{ + "hello": "hello world", + "messages": { + "hello": "hi DIO!", + "nested": { + } + }, + "hello_dio": "hello underscore DIO!", + "hello {name}": "hello {name}!" +} +`, + errors: [ + { + message: "unused 'messages.link' key", + suggestions: [ + { + desc: "Remove the 'messages.link' key.", + output: `{ + "hello": "hello world", + "messages": { + "hello": "hi DIO!", + "nested": { + "hello": "hi jojo!" + } + }, + "hello_dio": "hello underscore DIO!", + "hello {name}": "hello {name}!", + "hello-dio": "hello hyphen DIO!" +} +` + }, + { + desc: 'Remove all unused keys.', + output: `{ + "hello": "hello world", + "messages": { + "hello": "hi DIO!", + "nested": { + } + }, + "hello_dio": "hello underscore DIO!", + "hello {name}": "hello {name}!" +} +` + } + ] + }, + { + message: "unused 'messages.nested.hello' key", + suggestions: [ + { + desc: "Remove the 'messages.nested.hello' key.", + output: `{ + "hello": "hello world", + "messages": { + "hello": "hi DIO!", + "link": "@:message.hello", + "nested": { + } + }, + "hello_dio": "hello underscore DIO!", + "hello {name}": "hello {name}!", + "hello-dio": "hello hyphen DIO!" +} +` + }, + { + desc: 'Remove all unused keys.', + output: `{ + "hello": "hello world", + "messages": { + "hello": "hi DIO!", + "nested": { + } + }, + "hello_dio": "hello underscore DIO!", + "hello {name}": "hello {name}!" +} +` + } + ] + }, + { + message: "unused 'hello-dio' key", + suggestions: [ + { + desc: "Remove the 'hello-dio' key.", + output: `{ + "hello": "hello world", + "messages": { + "hello": "hi DIO!", + "link": "@:message.hello", + "nested": { + "hello": "hi jojo!" + } + }, + "hello_dio": "hello underscore DIO!", + "hello {name}": "hello {name}!" +} +` + }, + { + desc: 'Remove all unused keys.', + output: `{ + "hello": "hello world", + "messages": { + "hello": "hi DIO!", + "nested": { + } + }, + "hello_dio": "hello underscore DIO!", + "hello {name}": "hello {name}!" +} +` + } + ] + } + ] + }, + 'locales/ja.yaml': { + output: `hello: "ハローワールド" +messages: + hello: "こんにちは、DIO!" + nested: {} +hello_dio: "こんにちは、アンダースコア DIO!" +"hello {name}": "こんにちは、{name}!" +`, + errors: [ + { + message: "unused 'messages.link' key", + suggestions: [ + { + desc: "Remove the 'messages.link' key.", + output: `hello: "ハローワールド" +messages: + hello: "こんにちは、DIO!" + nested: + hello: "こんにちは、ジョジョ!" +hello_dio: "こんにちは、アンダースコア DIO!" +"hello {name}": "こんにちは、{name}!" +hello-dio: "こんにちは、ハイフン DIO!" +` + }, + { + desc: 'Remove all unused keys.', + output: `hello: "ハローワールド" +messages: + hello: "こんにちは、DIO!" + nested: {} +hello_dio: "こんにちは、アンダースコア DIO!" +"hello {name}": "こんにちは、{name}!" +` + } + ] + }, + { + message: "unused 'messages.nested.hello' key", + suggestions: [ + { + desc: "Remove the 'messages.nested.hello' key.", + output: `hello: "ハローワールド" +messages: + hello: "こんにちは、DIO!" + link: "@:message.hello" + nested: {} +hello_dio: "こんにちは、アンダースコア DIO!" +"hello {name}": "こんにちは、{name}!" +hello-dio: "こんにちは、ハイフン DIO!" +` + }, + { + desc: 'Remove all unused keys.', + output: `hello: "ハローワールド" +messages: + hello: "こんにちは、DIO!" + nested: {} +hello_dio: "こんにちは、アンダースコア DIO!" +"hello {name}": "こんにちは、{name}!" +` + } + ] + }, + { + message: "unused 'hello-dio' key", + suggestions: [ + { + desc: "Remove the 'hello-dio' key.", + output: `hello: "ハローワールド" +messages: + hello: "こんにちは、DIO!" + link: "@:message.hello" + nested: + hello: "こんにちは、ジョジョ!" +hello_dio: "こんにちは、アンダースコア DIO!" +"hello {name}": "こんにちは、{name}!" +` + }, + { + desc: 'Remove all unused keys.', + output: `hello: "ハローワールド" +messages: + hello: "こんにちは、DIO!" + nested: {} +hello_dio: "こんにちは、アンダースコア DIO!" +"hello {name}": "こんにちは、{name}!" +` + } + ] + } + ] + } + } + ) ] }) diff --git a/tests/lib/test-utils.ts b/tests/lib/test-utils.ts index 3251e078..1e890c16 100644 --- a/tests/lib/test-utils.ts +++ b/tests/lib/test-utils.ts @@ -5,23 +5,29 @@ import type { RuleTester } from 'eslint' import * as vueParser from 'vue-eslint-parser' import * as jsonParser from 'jsonc-eslint-parser' import * as yamlParser from 'yaml-eslint-parser' +import { satisfies } from 'semver' +import { version } from 'eslint/package.json' type LanguageOptions = { parser: object } export function getTestCasesFromFixtures(testOptions: { + eslint?: string cwd: string options?: unknown[] localeDir?: SettingsVueI18nLocaleDir languageOptions?: LanguageOptions + only?: boolean }): IterableIterator export function getTestCasesFromFixtures( testOptions: { + eslint?: string cwd: string options?: unknown[] localeDir?: SettingsVueI18nLocaleDir languageOptions?: LanguageOptions + only?: boolean }, outputs: { [file: string]: @@ -31,10 +37,12 @@ export function getTestCasesFromFixtures( ): IterableIterator export function* getTestCasesFromFixtures( testOptions: { + eslint?: string cwd: string options?: unknown[] localeDir?: SettingsVueI18nLocaleDir languageOptions?: LanguageOptions + only?: boolean }, outputs?: { [file: string]: @@ -45,6 +53,9 @@ export function* getTestCasesFromFixtures( if (!testOptions) { return } + if (testOptions.eslint && !satisfies(version, testOptions.eslint)) { + return + } for (const { filename, relative, parser } of extractTargetFiles( testOptions.cwd )) { @@ -62,7 +73,8 @@ export function* getTestCasesFromFixtures( localeDir: testOptions.localeDir, cwd: testOptions.cwd } - } + }, + ...(testOptions.only ? { only: true } : {}) } if (outputs) { const output = outputs[relative]