From a488f15f35190ea4803ba9716e6ffc207277d7d2 Mon Sep 17 00:00:00 2001 From: Yosuke Ota Date: Wed, 17 Jan 2024 12:00:20 +0900 Subject: [PATCH] feat: add support for eslint v9 (#463) * feat: add support for eslint v9 * Create shiny-colts-search.md * fix * format --- .changeset/shiny-colts-search.md | 5 ++ .eslintrc.cjs | 37 ++++++++++++- .github/workflows/test.yml | 4 +- docs/.vitepress/components/resource-group.vue | 2 +- lib/rules/key-format-style.ts | 14 +++-- lib/rules/no-deprecated-i18n-component.ts | 4 +- lib/rules/no-duplicate-keys-in-locale.ts | 24 ++++---- lib/rules/no-dynamic-keys.ts | 5 +- lib/rules/no-html-messages.ts | 8 ++- lib/rules/no-missing-keys-in-other-locales.ts | 13 +++-- lib/rules/no-raw-text.ts | 41 +++++++------- lib/rules/no-unknown-locale.ts | 13 +++-- lib/rules/no-unused-keys.ts | 20 ++++--- lib/rules/prefer-linked-key-with-paren.ts | 14 +++-- lib/rules/prefer-sfc-lang-attr.ts | 11 ++-- lib/rules/sfc-locale-attr.ts | 4 +- lib/rules/valid-message-syntax.ts | 14 +++-- lib/types/eslint.ts | 14 ++--- lib/types/vue-parser-services.ts | 4 +- lib/utils/compat.ts | 9 +++ lib/utils/get-cwd.ts | 5 +- lib/utils/index.ts | 35 +++++++----- package.json | 4 +- pnpm-lock.yaml | 16 ++++++ scripts/new-rule.ts | 2 +- tests/lib/eslint-compat.ts | 9 +++ tests/lib/rules/key-format-style.ts | 27 +++++---- .../lib/rules/no-deprecated-i18n-component.ts | 6 +- .../rules/no-deprecated-i18n-place-attr.ts | 6 +- .../rules/no-deprecated-i18n-places-prop.ts | 6 +- .../lib/rules/no-duplicate-keys-in-locale.ts | 10 +++- tests/lib/rules/no-dynamic-keys.ts | 6 +- tests/lib/rules/no-html-messages.ts | 10 +++- tests/lib/rules/no-i18n-t-path-prop.ts | 6 +- .../rules/no-missing-keys-in-other-locales.ts | 33 +++++------ tests/lib/rules/no-missing-keys.ts | 22 ++++++-- tests/lib/rules/no-raw-text.ts | 15 +++-- tests/lib/rules/no-unknown-locale.ts | 36 ++++++------ tests/lib/rules/no-unused-keys.ts | 15 +++-- tests/lib/rules/no-v-html.ts | 10 +++- .../lib/rules/prefer-linked-key-with-paren.ts | 23 ++++---- tests/lib/rules/prefer-sfc-lang-attr.ts | 10 +++- tests/lib/rules/sfc-locale-attr.ts | 9 ++- tests/lib/rules/valid-message-syntax.ts | 55 +++++++++---------- tests/lib/test-utils.ts | 42 +++++++------- tests/lib/utils/index.ts | 6 +- tests/lib/utils/rule.ts | 4 +- tsconfig.json | 4 +- 48 files changed, 424 insertions(+), 268 deletions(-) create mode 100644 .changeset/shiny-colts-search.md create mode 100644 lib/utils/compat.ts create mode 100644 tests/lib/eslint-compat.ts diff --git a/.changeset/shiny-colts-search.md b/.changeset/shiny-colts-search.md new file mode 100644 index 00000000..4e5b0b71 --- /dev/null +++ b/.changeset/shiny-colts-search.md @@ -0,0 +1,5 @@ +--- +"@intlify/eslint-plugin-vue-i18n": minor +--- + +feat: add support for eslint v9 diff --git a/.eslintrc.cjs b/.eslintrc.cjs index f85f92c8..c6539e65 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -20,7 +20,42 @@ module.exports = { rules: { 'object-shorthand': 'error', 'no-debugger': 'error', - 'vue/multi-word-component-names': 'off' + 'vue/multi-word-component-names': 'off', + + 'prefer-template': 'error', + 'no-restricted-properties': [ + 'error', + { + object: 'context', + property: 'getSourceCode', + message: 'Use lib/utils/compat.ts' + }, + { + object: 'context', + property: 'getFilename', + message: 'Use lib/utils/compat.ts' + }, + { + object: 'context', + property: 'getPhysicalFilename', + message: 'Use lib/utils/compat.ts' + }, + { + object: 'context', + property: 'getCwd', + message: 'Use lib/utils/compat.ts' + }, + { + object: 'context', + property: 'getScope', + message: 'Use lib/utils/compat.ts' + }, + { + object: 'context', + property: 'parserServices', + message: 'Use lib/utils/compat.ts' + } + ] }, overrides: [ { diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7f43d6e2..dab7c2c8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -56,8 +56,8 @@ jobs: strategy: matrix: os: [ubuntu-latest] - eslint: [5, 6, 7, 8] - node: [16] + eslint: [5, 6, 7, 8, ^9.0.0-0] + node: [20] steps: - name: Checkout uses: actions/checkout@v4 diff --git a/docs/.vitepress/components/resource-group.vue b/docs/.vitepress/components/resource-group.vue index 203fcd70..5e9d884d 100644 --- a/docs/.vitepress/components/resource-group.vue +++ b/docs/.vitepress/components/resource-group.vue @@ -14,7 +14,7 @@ export default { return { $resourceGroup: { async set(fileName, code) { - Vue.set(data.fileContents, '/path/' + fileName, code) + Vue.set(data.fileContents, `/path/${fileName}`, code) const timeSeq = ++waitSeq await Vue.nextTick() diff --git a/lib/rules/key-format-style.ts b/lib/rules/key-format-style.ts index 736dbdf9..1842a590 100644 --- a/lib/rules/key-format-style.ts +++ b/lib/rules/key-format-style.ts @@ -10,6 +10,7 @@ import type { RuleContext, RuleListener } from '../types' import { getCasingChecker } from '../utils/casing' import type { LocaleMessage } from '../utils/locale-messages' import { createRule } from '../utils/rule' +import { getFilename, getSourceCode } from '../utils/compat' const debug = debugBuilder('eslint-plugin-vue-i18n:key-format-style') const allowedCaseOptions = [ @@ -21,7 +22,8 @@ const allowedCaseOptions = [ type CaseOption = (typeof allowedCaseOptions)[number] function create(context: RuleContext): RuleListener { - const filename = context.getFilename() + const filename = getFilename(context) + const sourceCode = getSourceCode(context) const expectCasing: CaseOption = context.options[0] ?? 'camelCase' const checker = getCasingChecker(expectCasing) const allowArray: boolean = context.options[1]?.allowArray @@ -115,7 +117,6 @@ function create(context: RuleContext): RuleListener { if (cachedLoc) { return cachedLoc } - const sourceCode = context.getSourceCode() return (cachedLoc = { start: sourceCode.getLocFromIndex(offset + start), end: sourceCode.getLocFromIndex(offset + end) @@ -264,7 +265,10 @@ function create(context: RuleContext): RuleListener { return createVisitorForYaml(targetLocaleMessage) } ) - } else if (context.parserServices.isJSON || context.parserServices.isYAML) { + } else if ( + sourceCode.parserServices.isJSON || + sourceCode.parserServices.isYAML + ) { const localeMessages = getLocaleMessages(context) const targetLocaleMessage = localeMessages.findExistLocaleMessage(filename) if (!targetLocaleMessage) { @@ -272,9 +276,9 @@ function create(context: RuleContext): RuleListener { return {} } - if (context.parserServices.isJSON) { + if (sourceCode.parserServices.isJSON) { return createVisitorForJson(targetLocaleMessage) - } else if (context.parserServices.isYAML) { + } else if (sourceCode.parserServices.isYAML) { return createVisitorForYaml(targetLocaleMessage) } return {} diff --git a/lib/rules/no-deprecated-i18n-component.ts b/lib/rules/no-deprecated-i18n-component.ts index 6d3676e1..6743c9a1 100644 --- a/lib/rules/no-deprecated-i18n-component.ts +++ b/lib/rules/no-deprecated-i18n-component.ts @@ -5,6 +5,7 @@ import { defineTemplateBodyVisitor } from '../utils/index' import type { RuleContext, RuleListener } from '../types' import type { AST as VAST } from 'vue-eslint-parser' import { createRule } from '../utils/rule' +import { getSourceCode } from '../utils/compat' function create(context: RuleContext): RuleListener { return defineTemplateBodyVisitor(context, { @@ -12,7 +13,8 @@ function create(context: RuleContext): RuleListener { if (node.name !== 'i18n') { return } - const tokenStore = context.parserServices.getTemplateBodyTokenStore() + const sourceCode = getSourceCode(context) + const tokenStore = sourceCode.parserServices.getTemplateBodyTokenStore() const tagNameToken = tokenStore.getFirstToken(node.startTag) context.report({ node: tagNameToken, diff --git a/lib/rules/no-duplicate-keys-in-locale.ts b/lib/rules/no-duplicate-keys-in-locale.ts index e2d5831d..33311c1a 100644 --- a/lib/rules/no-duplicate-keys-in-locale.ts +++ b/lib/rules/no-duplicate-keys-in-locale.ts @@ -16,6 +16,7 @@ import type { import { joinPath } from '../utils/key-path' import { getCwd } from '../utils/get-cwd' import { createRule } from '../utils/rule' +import { getFilename, getSourceCode } from '../utils/compat' const debug = debugBuilder('eslint-plugin-vue-i18n:no-duplicate-keys-in-locale') interface DictData { @@ -34,13 +35,14 @@ interface PathStack { function getMessageFilepath(fullPath: string, context: RuleContext) { const cwd = getCwd(context) if (fullPath.startsWith(cwd)) { - return fullPath.replace(cwd + '/', './') + return fullPath.replace(`${cwd}/`, './') } return fullPath } function create(context: RuleContext): RuleListener { - const filename = context.getFilename() + const filename = getFilename(context) + const sourceCode = getSourceCode(context) const options = (context.options && context.options[0]) || {} const ignoreI18nBlock = Boolean(options.ignoreI18nBlock) @@ -129,7 +131,7 @@ function create(context: RuleContext): RuleListener { } if (typeof value.value !== 'object') { reportFiles.push( - '"' + getMessageFilepath(value.source.fullpath, context) + '"' + `"${getMessageFilepath(value.source.fullpath, context)}"` ) } else { nextOtherDictionaries.push({ @@ -145,7 +147,7 @@ function create(context: RuleContext): RuleListener { message: `duplicate key '${keyPathStr}' in '${pathStack.locale}'. ${ reportFiles.length === 0 ? last - : reportFiles.join(', ') + ', and ' + last + : `${reportFiles.join(', ')}, and ${last}` } has the same key`, loc: reportNode.loc }) @@ -316,7 +318,7 @@ function create(context: RuleContext): RuleListener { lm => lm !== targetLocaleMessage ) return createVisitorForJson( - ctx.getSourceCode(), + getSourceCode(ctx), targetLocaleMessage, otherLocaleMessages ) @@ -335,13 +337,16 @@ function create(context: RuleContext): RuleListener { lm => lm !== targetLocaleMessage ) return createVisitorForYaml( - ctx.getSourceCode(), + getSourceCode(ctx), targetLocaleMessage, otherLocaleMessages ) } ) - } else if (context.parserServices.isJSON || context.parserServices.isYAML) { + } else if ( + sourceCode.parserServices.isJSON || + sourceCode.parserServices.isYAML + ) { const localeMessages = getLocaleMessages(context) const targetLocaleMessage = localeMessages.findExistLocaleMessage(filename) if (!targetLocaleMessage) { @@ -349,17 +354,16 @@ function create(context: RuleContext): RuleListener { return {} } - const sourceCode = context.getSourceCode() const otherLocaleMessages: LocaleMessage[] = localeMessages.localeMessages.filter(lm => lm !== targetLocaleMessage) - if (context.parserServices.isJSON) { + if (sourceCode.parserServices.isJSON) { return createVisitorForJson( sourceCode, targetLocaleMessage, otherLocaleMessages ) - } else if (context.parserServices.isYAML) { + } else if (sourceCode.parserServices.isYAML) { return createVisitorForYaml( sourceCode, targetLocaleMessage, diff --git a/lib/rules/no-dynamic-keys.ts b/lib/rules/no-dynamic-keys.ts index 7639adb4..a5206777 100644 --- a/lib/rules/no-dynamic-keys.ts +++ b/lib/rules/no-dynamic-keys.ts @@ -5,12 +5,13 @@ import { defineTemplateBodyVisitor, isStaticLiteral } from '../utils/index' import type { RuleContext, RuleListener } from '../types' import type { AST as VAST } from 'vue-eslint-parser' import { createRule } from '../utils/rule' +import { getSourceCode } from '../utils/compat' function getNodeName(context: RuleContext, node: VAST.Node): string { if (node.type === 'Identifier') { return node.name } - const sourceCode = context.getSourceCode() + const sourceCode = getSourceCode(context) if ( sourceCode.ast.range[0] <= node.range[0] && node.range[1] <= sourceCode.ast.range[1] @@ -20,7 +21,7 @@ function getNodeName(context: RuleContext, node: VAST.Node): string { .map(t => t.value) .join('') } - const tokenStore = context.parserServices.getTemplateBodyTokenStore() + const tokenStore = sourceCode.parserServices.getTemplateBodyTokenStore() return tokenStore .getTokens(node) .map(t => t.value) diff --git a/lib/rules/no-html-messages.ts b/lib/rules/no-html-messages.ts index 2cecb9e2..c1d28607 100644 --- a/lib/rules/no-html-messages.ts +++ b/lib/rules/no-html-messages.ts @@ -10,6 +10,7 @@ import type { AST as YAMLAST } from 'yaml-eslint-parser' import type { RuleContext, RuleListener } from '../types' import { createRule } from '../utils/rule' import type { DefaultTreeAdapterMap } from 'parse5' +import { getFilename, getSourceCode } from '../utils/compat' const debug = debugBuilder('eslint-plugin-vue-i18n:no-html-messages') @@ -25,7 +26,8 @@ function findHTMLNode(node: DocumentFragment): Element | undefined { } function create(context: RuleContext): RuleListener { - const filename = context.getFilename() + const filename = getFilename(context) + const sourceCode = getSourceCode(context) /** * @param {JSONLiteral} node @@ -97,14 +99,14 @@ function create(context: RuleContext): RuleListener { } } ) - } else if (context.parserServices.isJSON) { + } else if (sourceCode.parserServices.isJSON) { if (!getLocaleMessages(context).findExistLocaleMessage(filename)) { return {} } return { JSONLiteral: verifyJSONLiteral } - } else if (context.parserServices.isYAML) { + } else if (sourceCode.parserServices.isYAML) { if (!getLocaleMessages(context).findExistLocaleMessage(filename)) { return {} } diff --git a/lib/rules/no-missing-keys-in-other-locales.ts b/lib/rules/no-missing-keys-in-other-locales.ts index b876421d..c715988c 100644 --- a/lib/rules/no-missing-keys-in-other-locales.ts +++ b/lib/rules/no-missing-keys-in-other-locales.ts @@ -14,12 +14,14 @@ import type { import type { LocaleMessage, LocaleMessages } from '../utils/locale-messages' import { joinPath } from '../utils/key-path' import { createRule } from '../utils/rule' +import { getFilename, getSourceCode } from '../utils/compat' const debug = debugBuilder( 'eslint-plugin-vue-i18n:no-missing-keys-in-other-locales' ) function create(context: RuleContext): RuleListener { - const filename = context.getFilename() + const filename = getFilename(context) + const sourceCode = getSourceCode(context) const ignoreLocales: string[] = context.options[0]?.ignoreLocales || [] function reportMissing( @@ -307,7 +309,10 @@ function create(context: RuleContext): RuleListener { return createVisitorForYaml(targetLocaleMessage, localeMessages) } ) - } else if (context.parserServices.isJSON || context.parserServices.isYAML) { + } else if ( + sourceCode.parserServices.isJSON || + sourceCode.parserServices.isYAML + ) { const localeMessages = getLocaleMessages(context) const targetLocaleMessage = localeMessages.findExistLocaleMessage(filename) if (!targetLocaleMessage) { @@ -315,9 +320,9 @@ function create(context: RuleContext): RuleListener { return {} } - if (context.parserServices.isJSON) { + if (sourceCode.parserServices.isJSON) { return createVisitorForJson(targetLocaleMessage, localeMessages) - } else if (context.parserServices.isYAML) { + } else if (sourceCode.parserServices.isYAML) { return createVisitorForYaml(targetLocaleMessage, localeMessages) } return {} diff --git a/lib/rules/no-raw-text.ts b/lib/rules/no-raw-text.ts index a6d3050e..d4a7ef0b 100644 --- a/lib/rules/no-raw-text.ts +++ b/lib/rules/no-raw-text.ts @@ -29,6 +29,7 @@ import type { import { isKebabCase, pascalCase } from '../utils/casing' import { createRule } from '../utils/rule' import { toRegExp } from '../utils/regexp' +import { getSourceCode } from '../utils/compat' type LiteralValue = VAST.ESLintLiteral['value'] type TemplateOptionValueNode = StaticLiteral @@ -94,10 +95,11 @@ function calculateLoc( if (!base) { return node.loc } + const sourceCode = getSourceCode(context) const range = calculateRange(node, base) return { - start: context.getSourceCode().getLocFromIndex(range[0]), - end: context.getSourceCode().getLocFromIndex(range[1]) + start: sourceCode.getLocFromIndex(range[0]), + end: sourceCode.getLocFromIndex(range[1]) } } @@ -297,7 +299,7 @@ function checkVAttribute( const literalRange = calculateRange(literal, baseNode) const replaceRange = [literalRange[0] + 1, literalRange[1] - 1] as Range const keyRange = calculateRange(attribute.key, baseNode) - const sourceCode = context.getSourceCode() + const sourceCode = getSourceCode(context) const attrQuote = sourceCode.text[literalRange[0]] const quotes: Quotes = new Set(attrQuote as never) if (baseNode) { @@ -372,7 +374,7 @@ function checkText( } } const replaceRange = calculateRange(textNode, baseNode) - const sourceCode = context.getSourceCode() + const sourceCode = getSourceCode(context) const quotes: Quotes = new Set() if (baseNode) { const baseQuote = sourceCode.text[baseNode.range[0]] @@ -439,8 +441,9 @@ function getComponentTemplateValueNode( if (isStaticLiteral(templateNode.value)) { return templateNode.value } else if (templateNode.value.type === 'Identifier') { + const sourceCode = getSourceCode(context) const templateVariable = findVariable( - context.getScope().variables, + sourceCode.getScope(node).variables, templateNode.value.name ) if (templateVariable) { @@ -470,7 +473,8 @@ function withoutEscape( if (!baseNode) { return false } - const sourceText = context.getSourceCode().getText(baseNode).slice(1, -1) + const sourceCode = getSourceCode(context) + const sourceText = sourceCode.getText(baseNode).slice(1, -1) const templateText = `${getStaticLiteralValue(baseNode)}` return sourceText === templateText } @@ -489,7 +493,8 @@ function getFixableI18nBlocks( context: RuleContext, newKey: string ): I18nBlockInfo[] | null { - const df = context.parserServices.getDocumentFragment?.() + const sourceCode = getSourceCode(context) + const df = sourceCode.parserServices.getDocumentFragment?.() if (!df) { return null } @@ -550,7 +555,7 @@ function getFixableI18nBlocks( objects, offsets: { getLoc: (index: number) => { - return context.getSourceCode().getLocFromIndex(getIndex(index)) + return sourceCode.getLocFromIndex(getIndex(index)) }, getIndex } @@ -570,9 +575,10 @@ function* generateFixAddI18nBlock( resource: string, replaceFixes: Fix[] ): IterableIterator { + const sourceCode = getSourceCode(context) const text = JSON.stringify(resource) - const df = context.parserServices.getDocumentFragment!()! - const tokenStore = context.parserServices.getTemplateBodyTokenStore() + const df = sourceCode.parserServices.getDocumentFragment!()! + const tokenStore = sourceCode.parserServices.getTemplateBodyTokenStore() if (!i18nBlocks.length) { let baseToken: VAST.VElement | VAST.Token = df.children.find(isVElement)! @@ -603,21 +609,18 @@ function* generateFixAddI18nBlock( for (const objectNode of objects) { const first = objectNode.properties[0] - let indent = + let indent = `${ /^\s*/.exec( - context.getSourceCode().lines[ - offsets.getLoc(objectNode.range[0]).line - 1 - ] - )![0] + ' ' + sourceCode.lines[offsets.getLoc(objectNode.range[0]).line - 1] + )![0] + } ` let next = '' if (first) { if (objectNode.loc.start.line === first.loc.start.line) { - next = ',\n' + indent + next = `,\n${indent}` } else { indent = /^\s*/.exec( - context.getSourceCode().lines[ - offsets.getLoc(first.range[0]).line - 1 - ] + sourceCode.lines[offsets.getLoc(first.range[0]).line - 1] )![0] next = ',' } diff --git a/lib/rules/no-unknown-locale.ts b/lib/rules/no-unknown-locale.ts index 51169456..d606dec2 100644 --- a/lib/rules/no-unknown-locale.ts +++ b/lib/rules/no-unknown-locale.ts @@ -12,10 +12,12 @@ import { getAttribute } from '../utils/index' import type { LocaleMessage } from '../utils/locale-messages' +import { getFilename, getSourceCode } from '../utils/compat' const debug = debugBuilder('eslint-plugin-vue-i18n:no-unknown-locale') function create(context: RuleContext): RuleListener { - const filename = context.getFilename() + const filename = getFilename(context) + const sourceCode = getSourceCode(context) const locales: string[] = context.options[0]?.locales || [] const disableRFC5646 = context.options[0]?.disableRFC5646 || false @@ -218,7 +220,10 @@ function create(context: RuleContext): RuleListener { ) } ) - } else if (context.parserServices.isJSON || context.parserServices.isYAML) { + } else if ( + sourceCode.parserServices.isJSON || + sourceCode.parserServices.isYAML + ) { const localeMessages = getLocaleMessages(context) const targetLocaleMessage = localeMessages.findExistLocaleMessage(filename) if (!targetLocaleMessage) { @@ -226,9 +231,9 @@ function create(context: RuleContext): RuleListener { return {} } - if (context.parserServices.isJSON) { + if (sourceCode.parserServices.isJSON) { return createVisitorForJson(targetLocaleMessage, null) - } else if (context.parserServices.isYAML) { + } else if (sourceCode.parserServices.isYAML) { return createVisitorForYaml(targetLocaleMessage, null) } return {} diff --git a/lib/rules/no-unused-keys.ts b/lib/rules/no-unused-keys.ts index da519d5a..d6133916 100644 --- a/lib/rules/no-unused-keys.ts +++ b/lib/rules/no-unused-keys.ts @@ -24,6 +24,7 @@ import { joinPath, parsePath } from '../utils/key-path' import { getCwd } from '../utils/get-cwd' import { createRule } from '../utils/rule' import { toRegExp } from '../utils/regexp' +import { getFilename, getSourceCode } from '../utils/compat' const debug = debugBuilder('eslint-plugin-vue-i18n:no-unused-keys') type UsedKeys = { @@ -76,7 +77,7 @@ function getUsedKeysMap( } function create(context: RuleContext): RuleListener { - const filename = context.getFilename() + const filename = getFilename(context) const options = (context.options && context.options[0]) || {} const enableFix = options.enableFix const ignores = ((options.ignores || []) as string[]).map(toRegExp) @@ -491,6 +492,7 @@ function create(context: RuleContext): RuleListener { } } + const sourceCode = getSourceCode(context) if (extname(filename) === '.vue') { const createCustomBlockRule = ( createVisitor: ( @@ -501,8 +503,8 @@ function create(context: RuleContext): RuleListener { return ctx => { const localeMessages = getLocaleMessages(context) const usedLocaleMessageKeys = collectKeysFromAST( - context.getSourceCode().ast as VAST.ESLintProgram, - context.getSourceCode().visitorKeys + sourceCode.ast as VAST.ESLintProgram, + sourceCode.visitorKeys ) const targetLocaleMessage = localeMessages.findBlockLocaleMessage( ctx.parserServices.customBlock @@ -517,7 +519,7 @@ function create(context: RuleContext): RuleListener { context ) - return createVisitor(ctx.getSourceCode(), usedKeys) + return createVisitor(getSourceCode(ctx), usedKeys) } } return defineCustomBlocksVisitor( @@ -525,7 +527,10 @@ function create(context: RuleContext): RuleListener { createCustomBlockRule(createVisitorForJson), createCustomBlockRule(createVisitorForYaml) ) - } else if (context.parserServices.isJSON || context.parserServices.isYAML) { + } else if ( + sourceCode.parserServices.isJSON || + sourceCode.parserServices.isYAML + ) { const localeMessages = getLocaleMessages(context) const targetLocaleMessage = localeMessages.findExistLocaleMessage(filename) if (!targetLocaleMessage) { @@ -540,7 +545,6 @@ function create(context: RuleContext): RuleListener { extensions, context ) - const sourceCode = context.getSourceCode() const usedKeys = getUsedKeysMap( targetLocaleMessage, @@ -548,9 +552,9 @@ function create(context: RuleContext): RuleListener { usedLocaleMessageKeys, context ) - if (context.parserServices.isJSON) { + if (sourceCode.parserServices.isJSON) { return createVisitorForJson(sourceCode, usedKeys) - } else if (context.parserServices.isYAML) { + } else if (sourceCode.parserServices.isYAML) { return createVisitorForYaml(sourceCode, usedKeys) } return {} diff --git a/lib/rules/prefer-linked-key-with-paren.ts b/lib/rules/prefer-linked-key-with-paren.ts index ac550b23..6120e449 100644 --- a/lib/rules/prefer-linked-key-with-paren.ts +++ b/lib/rules/prefer-linked-key-with-paren.ts @@ -16,6 +16,7 @@ import { parse } from '../utils/message-compiler/parser' import { parse as parseForV8 } from '../utils/message-compiler/parser-v8' import { traverseNode } from '../utils/message-compiler/traverser' import { createRule } from '../utils/rule' +import { getFilename, getSourceCode } from '../utils/compat' const debug = debugBuilder( 'eslint-plugin-vue-i18n:prefer-linked-key-with-paren' ) @@ -33,8 +34,8 @@ function getSingleQuote(node: JSONAST.JSONStringLiteral | YAMLAST.YAMLScalar) { type GetReportOffset = (offset: number) => number | null function create(context: RuleContext): RuleListener { - const filename = context.getFilename() - const sourceCode = context.getSourceCode() + const filename = getFilename(context) + const sourceCode = getSourceCode(context) const messageSyntaxVersions = getMessageSyntaxVersions(context) function verifyForV9( @@ -221,7 +222,10 @@ function create(context: RuleContext): RuleListener { createVisitorForJson, createVisitorForYaml ) - } else if (context.parserServices.isJSON || context.parserServices.isYAML) { + } else if ( + sourceCode.parserServices.isJSON || + sourceCode.parserServices.isYAML + ) { const localeMessages = getLocaleMessages(context) const targetLocaleMessage = localeMessages.findExistLocaleMessage(filename) if (!targetLocaleMessage) { @@ -229,9 +233,9 @@ function create(context: RuleContext): RuleListener { return {} } - if (context.parserServices.isJSON) { + if (sourceCode.parserServices.isJSON) { return createVisitorForJson() - } else if (context.parserServices.isYAML) { + } else if (sourceCode.parserServices.isYAML) { return createVisitorForYaml() } return {} diff --git a/lib/rules/prefer-sfc-lang-attr.ts b/lib/rules/prefer-sfc-lang-attr.ts index f9d80354..f982d816 100644 --- a/lib/rules/prefer-sfc-lang-attr.ts +++ b/lib/rules/prefer-sfc-lang-attr.ts @@ -1,9 +1,11 @@ import { getAttribute, isI18nBlock } from '../utils/index' import type { RuleContext, RuleListener } from '../types' import { createRule } from '../utils/rule' +import { getSourceCode } from '../utils/compat' function create(context: RuleContext): RuleListener { - const df = context.parserServices.getDocumentFragment?.() + const sourceCode = getSourceCode(context) + const df = sourceCode.parserServices.getDocumentFragment?.() if (!df) { return {} } @@ -29,13 +31,14 @@ function create(context: RuleContext): RuleListener { return fixer.replaceTextRange(langAttrs.range, 'lang="json"') } const tokenStore = - context.parserServices.getTemplateBodyTokenStore() + sourceCode.parserServices.getTemplateBodyTokenStore() const closeToken = tokenStore.getLastToken(i18n.startTag) const beforeToken = tokenStore.getTokenBefore(closeToken) return fixer.insertTextBeforeRange( closeToken.range, - (beforeToken.range[1] < closeToken.range[0] ? '' : ' ') + - 'lang="json" ' + `${ + beforeToken.range[1] < closeToken.range[0] ? '' : ' ' + }lang="json" ` ) } }) diff --git a/lib/rules/sfc-locale-attr.ts b/lib/rules/sfc-locale-attr.ts index 7e796a0a..ff284d4e 100644 --- a/lib/rules/sfc-locale-attr.ts +++ b/lib/rules/sfc-locale-attr.ts @@ -1,4 +1,5 @@ import type { RuleContext, RuleListener } from '../types' +import { getSourceCode } from '../utils/compat' import { isI18nBlock, getAttribute } from '../utils/index' import { createRule } from '../utils/rule' @@ -23,7 +24,8 @@ export = createRule({ } }, create(context: RuleContext): RuleListener { - const df = context.parserServices.getDocumentFragment?.() + const sourceCode = getSourceCode(context) + const df = sourceCode.parserServices.getDocumentFragment?.() if (!df) { return {} } diff --git a/lib/rules/valid-message-syntax.ts b/lib/rules/valid-message-syntax.ts index fc8b043a..32eef98b 100644 --- a/lib/rules/valid-message-syntax.ts +++ b/lib/rules/valid-message-syntax.ts @@ -17,11 +17,12 @@ import { parse } from '../utils/message-compiler/parser' import { parse as parseForV8 } from '../utils/message-compiler/parser-v8' import type { CompileError } from '@intlify/message-compiler' import { createRule } from '../utils/rule' +import { getFilename, getSourceCode } from '../utils/compat' const debug = debugBuilder('eslint-plugin-vue-i18n:valid-message-syntax') function create(context: RuleContext): RuleListener { - const filename = context.getFilename() - const sourceCode = context.getSourceCode() + const filename = getFilename(context) + const sourceCode = getSourceCode(context) const allowNotString = Boolean(context.options[0]?.allowNotString) const messageSyntaxVersions = getMessageSyntaxVersions(context) @@ -177,7 +178,10 @@ function create(context: RuleContext): RuleListener { createVisitorForJson, createVisitorForYaml ) - } else if (context.parserServices.isJSON || context.parserServices.isYAML) { + } else if ( + sourceCode.parserServices.isJSON || + sourceCode.parserServices.isYAML + ) { const localeMessages = getLocaleMessages(context) const targetLocaleMessage = localeMessages.findExistLocaleMessage(filename) if (!targetLocaleMessage) { @@ -185,9 +189,9 @@ function create(context: RuleContext): RuleListener { return {} } - if (context.parserServices.isJSON) { + if (sourceCode.parserServices.isJSON) { return createVisitorForJson() - } else if (context.parserServices.isYAML) { + } else if (sourceCode.parserServices.isYAML) { return createVisitorForYaml() } return {} diff --git a/lib/types/eslint.ts b/lib/types/eslint.ts index b7ebdb8b..ffd47f1e 100644 --- a/lib/types/eslint.ts +++ b/lib/types/eslint.ts @@ -39,13 +39,7 @@ export interface RuleContext { } } parserPath: string - parserServices: { - isYAML?: true - isJSON?: true - } & VueParserServices - getFilename(): string - getSourceCode(): SourceCode - getScope(): Scope + report(descriptor: ReportDescriptor): void getCwd?: () => string } @@ -124,6 +118,12 @@ export interface SourceCode extends TokenStore { hasBOM: boolean scopeManager: ScopeManager visitorKeys: VisitorKeys + parserServices: { + isYAML?: true + isJSON?: true + } & VueParserServices + + getScope(node: MaybeNode): Scope getText(node?: MaybeNode, beforeCount?: number, afterCount?: number): string getLines(): string[] diff --git a/lib/types/vue-parser-services.ts b/lib/types/vue-parser-services.ts index d9dd0ab0..097cd962 100644 --- a/lib/types/vue-parser-services.ts +++ b/lib/types/vue-parser-services.ts @@ -1,5 +1,5 @@ import type { Rule } from 'eslint' -import type { RuleContext } from './eslint' +import type { RuleContext, SourceCode } from './eslint' import type { AST as VAST } from 'vue-eslint-parser' import type { TokenStore } from './types' import type { VElement } from 'vue-eslint-parser/ast' @@ -53,6 +53,6 @@ export interface VueParserServices { export type CustomBlockVisitorFactory = ( context: RuleContext & { - parserServices: RuleContext['parserServices'] & { customBlock: VElement } + parserServices: SourceCode['parserServices'] & { customBlock: VElement } } ) => RuleListener | null diff --git a/lib/utils/compat.ts b/lib/utils/compat.ts new file mode 100644 index 00000000..2635b776 --- /dev/null +++ b/lib/utils/compat.ts @@ -0,0 +1,9 @@ +import * as compat from 'eslint-compat-utils' +import type { RuleContext, SourceCode } from '../types' + +export function getFilename(context: RuleContext): string { + return compat.getFilename(context as never) +} +export function getSourceCode(context: RuleContext): SourceCode { + return compat.getSourceCode(context as never) as never +} diff --git a/lib/utils/get-cwd.ts b/lib/utils/get-cwd.ts index 7ee84670..5c2c6bbb 100644 --- a/lib/utils/get-cwd.ts +++ b/lib/utils/get-cwd.ts @@ -1,7 +1,6 @@ import type { RuleContext } from '../types' +import { getCwd as getCwdCompat } from 'eslint-compat-utils' export function getCwd(context: RuleContext): string { - return ( - context.settings?.['vue-i18n']?.cwd ?? context.getCwd?.() ?? process.cwd() - ) + return context.settings?.['vue-i18n']?.cwd ?? getCwdCompat(context as never) } diff --git a/lib/utils/index.ts b/lib/utils/index.ts index 1f17d1f4..c7d90503 100644 --- a/lib/utils/index.ts +++ b/lib/utils/index.ts @@ -25,6 +25,7 @@ import type { import * as jsoncESLintParser from 'jsonc-eslint-parser' import * as yamlESLintParser from 'yaml-eslint-parser' import { getCwd } from './get-cwd' +import { getFilename, getSourceCode } from './compat' interface LocaleFiles { files: string[] @@ -42,8 +43,9 @@ export function defineTemplateBodyVisitor( templateBodyVisitor: TemplateListener, scriptVisitor?: RuleListener ): RuleListener { - if (context.parserServices.defineTemplateBodyVisitor == null) { - const filename = context.getFilename() + const sourceCode = getSourceCode(context) + if (sourceCode.parserServices.defineTemplateBodyVisitor == null) { + const filename = getFilename(context) if (extname(filename) === '.vue') { context.report({ loc: UNEXPECTED_ERROR_LOCATION, @@ -53,7 +55,7 @@ export function defineTemplateBodyVisitor( } return {} } - return context.parserServices.defineTemplateBodyVisitor( + return sourceCode.parserServices.defineTemplateBodyVisitor( templateBodyVisitor, scriptVisitor ) @@ -156,13 +158,14 @@ export function getLocaleMessages( context: RuleContext, options?: { ignoreMissingSettingsError?: boolean } ): LocaleMessages { + const sourceCode = getSourceCode(context) const { settings } = context /** @type {SettingsVueI18nLocaleDir | null} */ const localeDir = (settings['vue-i18n'] && settings['vue-i18n'].localeDir) || null const documentFragment = - context.parserServices.getDocumentFragment && - context.parserServices.getDocumentFragment() + sourceCode.parserServices.getDocumentFragment && + sourceCode.parserServices.getDocumentFragment() /** @type {VElement[]} */ const i18nBlocks = (documentFragment && @@ -265,12 +268,12 @@ function getLocaleMessagesFromI18nBlocks( context: RuleContext, i18nBlocks: VAST.VElement[] ) { - const sourceCode = context.getSourceCode() + const sourceCode = getSourceCode(context) let localeMessages = i18nBlockLocaleMessages.get(sourceCode.ast) if (localeMessages) { return localeMessages } - const filename = context.getFilename() + const filename = getFilename(context) localeMessages = i18nBlocks .map(block => { const attrs = getStaticAttributes(block) @@ -327,10 +330,11 @@ export function defineCustomBlocksVisitor( jsonRule: CustomBlockVisitorFactory, yamlRule: CustomBlockVisitorFactory ): RuleListener { - if (!context.parserServices.defineCustomBlocksVisitor) { + const sourceCode = getSourceCode(context) + if (!sourceCode.parserServices.defineCustomBlocksVisitor) { return {} } - const jsonVisitor = context.parserServices.defineCustomBlocksVisitor( + const jsonVisitor = sourceCode.parserServices.defineCustomBlocksVisitor( context, jsoncESLintParser, { @@ -343,7 +347,7 @@ export function defineCustomBlocksVisitor( create: jsonRule } ) - const yamlVisitor = context.parserServices.defineCustomBlocksVisitor( + const yamlVisitor = sourceCode.parserServices.defineCustomBlocksVisitor( context, yamlESLintParser, { @@ -383,7 +387,7 @@ export function getVueObjectType( const parent = node.parent if (parent.type === 'ExportDefaultDeclaration') { // export default {} in .vue || .jsx - const ext = extname(context.getFilename()).toLowerCase() + const ext = extname(getFilename(context)).toLowerCase() if ( (ext === '.vue' || ext === '.jsx' || !ext) && skipTSAsExpression(parent.declaration) === node @@ -439,7 +443,7 @@ export function getVueObjectType( (pp.key.type === 'Identifier' ? pp.key.name : pp.key.type === 'Literal' - ? pp.key.value + '' + ? `${pp.key.value}` : '') === 'components' ) { return 'components-option' @@ -463,9 +467,10 @@ export function getVueObjectType( export function getScriptSetupElement( context: RuleContext ): VAST.VElement | null { + const sourceCode = getSourceCode(context) const df = - context.parserServices.getDocumentFragment && - context.parserServices.getDocumentFragment() + sourceCode.parserServices.getDocumentFragment && + sourceCode.parserServices.getDocumentFragment() if (!df) { return null } @@ -662,7 +667,7 @@ function getComponentComments(context: RuleContext) { if (tokens) { return tokens } - const sourceCode = context.getSourceCode() + const sourceCode = getSourceCode(context) tokens = sourceCode .getAllComments() .filter(comment => /@vue\/component/g.test(comment.value)) diff --git a/package.json b/package.json index 6c6ff672..d11f2a4e 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "@intlify/core-base": "beta", "@intlify/message-compiler": "beta", "debug": "^4.3.4", + "eslint-compat-utils": "^0.4.1", "glob": "^10.3.3", "ignore": "^5.2.4", "import-fresh": "^3.3.0", @@ -98,6 +99,7 @@ "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-vue": "^9.15.1", "eslint4b": "^7.32.0", + "espree": "^9.6.1", "esquery": "^1.5.0", "json-schema": "^0.4.0", "lerna-changelog": "^2.2.0", @@ -115,7 +117,7 @@ "vue-github-button": "^3.1.0" }, "peerDependencies": { - "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0-0" }, "changelog": { "labels": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 56e68dd2..90e53d2a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ dependencies: debug: specifier: ^4.3.4 version: 4.3.4(supports-color@8.1.1) + eslint-compat-utils: + specifier: ^0.4.1 + version: 0.4.1(eslint@8.45.0) glob: specifier: ^10.3.3 version: 10.3.3 @@ -124,6 +127,9 @@ devDependencies: eslint4b: specifier: ^7.32.0 version: 7.32.0 + espree: + specifier: ^9.6.1 + version: 9.6.1 esquery: specifier: ^1.5.0 version: 1.5.0 @@ -2492,6 +2498,16 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + /eslint-compat-utils@0.4.1(eslint@8.45.0): + resolution: {integrity: sha512-5N7ZaJG5pZxUeNNJfUchurLVrunD1xJvyg5kYOIVF8kg1f3ajTikmAu/5fZ9w100omNPOoMjngRszh/Q/uFGMg==} + engines: {node: '>=12'} + peerDependencies: + eslint: '>=6.0.0' + dependencies: + eslint: 8.45.0 + semver: 7.5.4 + dev: false + /eslint-config-prettier@9.0.0(eslint@8.45.0): resolution: {integrity: sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==} hasBin: true diff --git a/scripts/new-rule.ts b/scripts/new-rule.ts index 94b9426d..836caff8 100644 --- a/scripts/new-rule.ts +++ b/scripts/new-rule.ts @@ -45,7 +45,7 @@ export = createRule({ ) writeFileSync( testFile, - `import { RuleTester } from 'eslint' + `import { RuleTester } from '../eslint-compat' import rule from '../../../lib/rules/${ruleId}' const vueParser = require.resolve('vue-eslint-parser') diff --git a/tests/lib/eslint-compat.ts b/tests/lib/eslint-compat.ts new file mode 100644 index 00000000..51c4ade5 --- /dev/null +++ b/tests/lib/eslint-compat.ts @@ -0,0 +1,9 @@ +import { getLegacyESLint, getESLint } from 'eslint-compat-utils/eslint' +import { getLinter } from 'eslint-compat-utils/linter' +import { getRuleTester, getRuleIdPrefix } from 'eslint-compat-utils/rule-tester' + +export const LegacyESLint = getLegacyESLint() +export const ESLint = getESLint() +export const RuleTester = getRuleTester() +export const TEST_RULE_ID_PREFIX = getRuleIdPrefix() +export const Linter = getLinter() diff --git a/tests/lib/rules/key-format-style.ts b/tests/lib/rules/key-format-style.ts index 0ddcca1f..71ac624b 100644 --- a/tests/lib/rules/key-format-style.ts +++ b/tests/lib/rules/key-format-style.ts @@ -3,35 +3,35 @@ */ import { join } from 'node:path' -import { RuleTester } from 'eslint' +import { RuleTester } from '../eslint-compat' import rule from '../../../lib/rules/key-format-style' import type { SettingsVueI18nLocaleDirObject } from '../../../lib/types' +import * as vueParser from 'vue-eslint-parser' +import * as jsonParser from 'jsonc-eslint-parser' +import * as yamlParser from 'yaml-eslint-parser' -const vueParser = require.resolve('vue-eslint-parser') -const jsonParser = require.resolve('jsonc-eslint-parser') -const yamlParser = require.resolve('yaml-eslint-parser') const fileLocalesRoot = join(__dirname, '../../fixtures/key-format-style/file') const keyLocalesRoot = join(__dirname, '../../fixtures/key-format-style/key') const options = { json: { file: { - parser: jsonParser, + languageOptions: { parser: jsonParser }, filename: join(fileLocalesRoot, 'test.json'), settings: { 'vue-i18n': { - localeDir: fileLocalesRoot + '/*.{json,yaml,yml}' + localeDir: `${fileLocalesRoot}/*.{json,yaml,yml}` } } }, key: { - parser: jsonParser, + languageOptions: { parser: jsonParser }, filename: join(keyLocalesRoot, 'test.json'), settings: { 'vue-i18n': { localeDir: { - pattern: keyLocalesRoot + '/*.{json,yaml,yml}', + pattern: `${keyLocalesRoot}/*.{json,yaml,yml}`, localeKey: 'key' } as SettingsVueI18nLocaleDirObject } @@ -40,21 +40,21 @@ const options = { }, yaml: { file: { - parser: yamlParser, + languageOptions: { parser: yamlParser }, filename: join(fileLocalesRoot, 'test.yaml'), settings: { 'vue-i18n': { - localeDir: fileLocalesRoot + '/*.{json,yaml,yml}' + localeDir: `${fileLocalesRoot}/*.{json,yaml,yml}` } } }, key: { - parser: yamlParser, + languageOptions: { parser: yamlParser }, filename: join(keyLocalesRoot, 'test.yaml'), settings: { 'vue-i18n': { localeDir: { - pattern: keyLocalesRoot + '/*.{json,yaml,yml}', + pattern: `${keyLocalesRoot}/*.{json,yaml,yml}`, localeKey: 'key' } as SettingsVueI18nLocaleDirObject } @@ -64,8 +64,7 @@ const options = { } const tester = new RuleTester({ - parser: vueParser, - parserOptions: { ecmaVersion: 2015 } + languageOptions: { parser: vueParser, ecmaVersion: 2015 } }) tester.run('key-format-style', rule as never, { diff --git a/tests/lib/rules/no-deprecated-i18n-component.ts b/tests/lib/rules/no-deprecated-i18n-component.ts index b4fe88ab..15c455dc 100644 --- a/tests/lib/rules/no-deprecated-i18n-component.ts +++ b/tests/lib/rules/no-deprecated-i18n-component.ts @@ -1,12 +1,12 @@ /** * @author Yosuke Ota */ -import { RuleTester } from 'eslint' +import { RuleTester } from '../eslint-compat' import rule from '../../../lib/rules/no-deprecated-i18n-component' +import * as vueParser from 'vue-eslint-parser' const tester = new RuleTester({ - parser: require.resolve('vue-eslint-parser'), - parserOptions: { ecmaVersion: 2015 } + languageOptions: { parser: vueParser, ecmaVersion: 2015 } }) tester.run('no-deprecated-i18n-component', rule as never, { diff --git a/tests/lib/rules/no-deprecated-i18n-place-attr.ts b/tests/lib/rules/no-deprecated-i18n-place-attr.ts index 10dd01f1..ef006215 100644 --- a/tests/lib/rules/no-deprecated-i18n-place-attr.ts +++ b/tests/lib/rules/no-deprecated-i18n-place-attr.ts @@ -1,12 +1,12 @@ /** * @author Yosuke Ota */ -import { RuleTester } from 'eslint' +import { RuleTester } from '../eslint-compat' import rule from '../../../lib/rules/no-deprecated-i18n-place-attr' +import * as vueParser from 'vue-eslint-parser' const tester = new RuleTester({ - parser: require.resolve('vue-eslint-parser'), - parserOptions: { ecmaVersion: 2015 } + languageOptions: { parser: vueParser, ecmaVersion: 2015 } }) tester.run('no-deprecated-i18n-place-attr', rule as never, { diff --git a/tests/lib/rules/no-deprecated-i18n-places-prop.ts b/tests/lib/rules/no-deprecated-i18n-places-prop.ts index 586bccb6..c71bdb74 100644 --- a/tests/lib/rules/no-deprecated-i18n-places-prop.ts +++ b/tests/lib/rules/no-deprecated-i18n-places-prop.ts @@ -1,12 +1,12 @@ /** * @author Yosuke Ota */ -import { RuleTester } from 'eslint' +import { RuleTester } from '../eslint-compat' import rule from '../../../lib/rules/no-deprecated-i18n-places-prop' +import * as vueParser from 'vue-eslint-parser' const tester = new RuleTester({ - parser: require.resolve('vue-eslint-parser'), - parserOptions: { ecmaVersion: 2015 } + languageOptions: { parser: vueParser, ecmaVersion: 2015 } }) tester.run('no-deprecated-i18n-places-prop', rule as never, { diff --git a/tests/lib/rules/no-duplicate-keys-in-locale.ts b/tests/lib/rules/no-duplicate-keys-in-locale.ts index 251be6d9..961a3770 100644 --- a/tests/lib/rules/no-duplicate-keys-in-locale.ts +++ b/tests/lib/rules/no-duplicate-keys-in-locale.ts @@ -1,17 +1,21 @@ /** * @author Yosuke Ota */ -import { RuleTester } from 'eslint' +import { RuleTester } from '../eslint-compat' import { join } from 'node:path' import rule from '../../../lib/rules/no-duplicate-keys-in-locale' import { getTestCasesFromFixtures } from '../test-utils' +import * as vueParser from 'vue-eslint-parser' const cwdRoot = join(__dirname, '../../fixtures/no-duplicate-keys-in-locale') new RuleTester({ - parser: require.resolve('vue-eslint-parser'), - parserOptions: { ecmaVersion: 2015, sourceType: 'module' } + languageOptions: { + parser: vueParser, + ecmaVersion: 2015, + sourceType: 'module' + } }).run('no-duplicate-keys-in-locale', rule as never, { valid: [ { diff --git a/tests/lib/rules/no-dynamic-keys.ts b/tests/lib/rules/no-dynamic-keys.ts index 12cbc7dc..9ab1570e 100644 --- a/tests/lib/rules/no-dynamic-keys.ts +++ b/tests/lib/rules/no-dynamic-keys.ts @@ -1,12 +1,12 @@ /** * @author kazuya kawaguchi (a.k.a. kazupon) */ -import { RuleTester } from 'eslint' +import { RuleTester } from '../eslint-compat' import rule from '../../../lib/rules/no-dynamic-keys' +import * as vueParser from 'vue-eslint-parser' const tester = new RuleTester({ - parser: require.resolve('vue-eslint-parser'), - parserOptions: { ecmaVersion: 2015 } + languageOptions: { parser: vueParser, ecmaVersion: 2015 } }) tester.run('no-dynamic-keys', rule as never, { diff --git a/tests/lib/rules/no-html-messages.ts b/tests/lib/rules/no-html-messages.ts index b37f0b1a..86fab084 100644 --- a/tests/lib/rules/no-html-messages.ts +++ b/tests/lib/rules/no-html-messages.ts @@ -1,17 +1,21 @@ /** * @author kazuya kawaguchi (a.k.a. kazupon) */ -import { RuleTester } from 'eslint' +import { RuleTester } from '../eslint-compat' import { join } from 'node:path' import { readFileSync } from 'fs' import rule from '../../../lib/rules/no-html-messages' import { getTestCasesFromFixtures } from '../test-utils' +import * as vueParser from 'vue-eslint-parser' const cwdRoot = join(__dirname, '../../fixtures/no-html-messages') new RuleTester({ - parser: require.resolve('vue-eslint-parser'), - parserOptions: { ecmaVersion: 2015, sourceType: 'module' } + languageOptions: { + parser: vueParser, + ecmaVersion: 2015, + sourceType: 'module' + } }).run('no-html-messages', rule as never, { valid: [ { diff --git a/tests/lib/rules/no-i18n-t-path-prop.ts b/tests/lib/rules/no-i18n-t-path-prop.ts index 8c85579f..ec4bac95 100644 --- a/tests/lib/rules/no-i18n-t-path-prop.ts +++ b/tests/lib/rules/no-i18n-t-path-prop.ts @@ -1,12 +1,12 @@ /** * @author Yosuke Ota */ -import { RuleTester } from 'eslint' +import { RuleTester } from '../eslint-compat' import rule from '../../../lib/rules/no-i18n-t-path-prop' +import * as vueParser from 'vue-eslint-parser' const tester = new RuleTester({ - parser: require.resolve('vue-eslint-parser'), - parserOptions: { ecmaVersion: 2015 } + languageOptions: { parser: vueParser, ecmaVersion: 2015 } }) tester.run('no-i18n-t-path-prop', rule as never, { diff --git a/tests/lib/rules/no-missing-keys-in-other-locales.ts b/tests/lib/rules/no-missing-keys-in-other-locales.ts index 317db237..bc537f48 100644 --- a/tests/lib/rules/no-missing-keys-in-other-locales.ts +++ b/tests/lib/rules/no-missing-keys-in-other-locales.ts @@ -2,12 +2,11 @@ * @author Yosuke Ota */ import { join } from 'node:path' -import { RuleTester } from 'eslint' +import { RuleTester } from '../eslint-compat' import rule from '../../../lib/rules/no-missing-keys-in-other-locales' - -const vueParser = require.resolve('vue-eslint-parser') -const jsonParser = require.resolve('jsonc-eslint-parser') -const yamlParser = require.resolve('yaml-eslint-parser') +import * as vueParser from 'vue-eslint-parser' +import * as jsonParser from 'jsonc-eslint-parser' +import * as yamlParser from 'yaml-eslint-parser' const FIXTURES_ROOT = join( __dirname, @@ -35,16 +34,19 @@ const YAML_FILENAME_LOCALE_KEY_TYPE_KEY = join( const SETTINGS = { FILE: { 'vue-i18n': { - localeDir: - join(FIXTURES_ROOT, 'vue-cli-format/locales') + '/*.{json,yaml,yml}' + localeDir: `${join( + FIXTURES_ROOT, + 'vue-cli-format/locales' + )}/*.{json,yaml,yml}` } }, KEY: { 'vue-i18n': { localeDir: { - pattern: - join(FIXTURES_ROOT, 'constructor-option-format/locales') + - '/*.{json,yaml,yml}', + pattern: `${join( + FIXTURES_ROOT, + 'constructor-option-format/locales' + )}/*.{json,yaml,yml}`, localeKey: 'key' } } @@ -54,29 +56,28 @@ const SETTINGS = { const OPTIONS = { JSON_LOCALE_KEY_TYPE_FILE: { filename: JSON_FILENAME_LOCALE_KEY_TYPE_FILE, - parser: jsonParser, + languageOptions: { parser: jsonParser }, settings: SETTINGS.FILE }, JSON_LOCALE_KEY_TYPE_KEY: { filename: JSON_FILENAME_LOCALE_KEY_TYPE_KEY, - parser: jsonParser, + languageOptions: { parser: jsonParser }, settings: SETTINGS.KEY }, YAML_LOCALE_KEY_TYPE_FILE: { filename: YAML_FILENAME_LOCALE_KEY_TYPE_FILE, - parser: yamlParser, + languageOptions: { parser: yamlParser }, settings: SETTINGS.FILE }, YAML_LOCALE_KEY_TYPE_KEY: { filename: YAML_FILENAME_LOCALE_KEY_TYPE_KEY, - parser: yamlParser, + languageOptions: { parser: yamlParser }, settings: SETTINGS.KEY } } const tester = new RuleTester({ - parser: vueParser, - parserOptions: { ecmaVersion: 2015 } + languageOptions: { parser: vueParser, ecmaVersion: 2015 } }) tester.run('no-missing-keys-in-other-locales', rule as never, { diff --git a/tests/lib/rules/no-missing-keys.ts b/tests/lib/rules/no-missing-keys.ts index 163c9dc8..24e8e57d 100644 --- a/tests/lib/rules/no-missing-keys.ts +++ b/tests/lib/rules/no-missing-keys.ts @@ -2,8 +2,12 @@ * @author kazuya kawaguchi (a.k.a. kazupon) */ import { join } from 'node:path' -import { RuleTester } from 'eslint' +import { RuleTester } from '../eslint-compat' +import type { RuleTester as RawRuleTester } from 'eslint' import rule from '../../../lib/rules/no-missing-keys' +import * as vueParser from 'vue-eslint-parser' +// @ts-expect-error -- missing type +import * as espree from 'espree' const localeDirs = [ './tests/fixtures/no-missing-keys/vue-cli-format/locales/*.{json,yaml,yml}', @@ -28,7 +32,7 @@ const localeDirs = [ ] function buildTestsForLocales< - T extends RuleTester.ValidTestCase | RuleTester.InvalidTestCase + T extends RawRuleTester.ValidTestCase | RawRuleTester.InvalidTestCase >(testcases: T[], otherTestcases: T[]): T[] { const result: T[] = [] for (const testcase of testcases) { @@ -45,8 +49,11 @@ function buildTestsForLocales< } const tester = new RuleTester({ - parser: require.resolve('vue-eslint-parser'), - parserOptions: { ecmaVersion: 2015, sourceType: 'module' } + languageOptions: { + parser: vueParser, + ecmaVersion: 2015, + sourceType: 'module' + } }) tester.run('no-missing-keys', rule as never, { @@ -232,7 +239,7 @@ tester.run('no-missing-keys', rule as never, { ] ), - invalid: buildTestsForLocales( + invalid: buildTestsForLocales( [ { // basic @@ -298,7 +305,10 @@ tester.run('no-missing-keys', rule as never, { ] }, { - parser: require.resolve('espree'), + // @ts-expect-error -- Type error for eslint v9 + languageOptions: { + parser: espree + }, code: `$t('messages.missing')`, errors: [ `'messages.missing' does not exist in localization message resources` diff --git a/tests/lib/rules/no-raw-text.ts b/tests/lib/rules/no-raw-text.ts index 8b1640c9..0bb20c90 100644 --- a/tests/lib/rules/no-raw-text.ts +++ b/tests/lib/rules/no-raw-text.ts @@ -1,16 +1,19 @@ /** * @author kazuya kawaguchi (a.k.a. kazupon) */ -import { RuleTester } from 'eslint' +import { RuleTester } from '../eslint-compat' import rule from '../../../lib/rules/no-raw-text' +import * as vueParser from 'vue-eslint-parser' const tester = new RuleTester({ - parser: require.resolve('vue-eslint-parser'), - parserOptions: { - ecmaVersion: 2015, + languageOptions: { + parser: vueParser, + ecmaVersion: 2020, sourceType: 'module', - ecmaFeatures: { - jsx: true + parserOptions: { + ecmaFeatures: { + jsx: true + } } } }) diff --git a/tests/lib/rules/no-unknown-locale.ts b/tests/lib/rules/no-unknown-locale.ts index 2b6d5aaa..42fe2ca9 100644 --- a/tests/lib/rules/no-unknown-locale.ts +++ b/tests/lib/rules/no-unknown-locale.ts @@ -1,42 +1,42 @@ import { join } from 'node:path' -import { RuleTester } from 'eslint' +import { RuleTester } from '../eslint-compat' import rule from '../../../lib/rules/no-unknown-locale' import type { SettingsVueI18nLocaleDirObject } from '../../../lib/types' +import * as vueParser from 'vue-eslint-parser' +import * as jsonParser from 'jsonc-eslint-parser' +import * as yamlParser from 'yaml-eslint-parser' -const vueParser = require.resolve('vue-eslint-parser') -const jsonParser = require.resolve('jsonc-eslint-parser') -const yamlParser = require.resolve('yaml-eslint-parser') const fileLocalesRoot = join(__dirname, '../../fixtures/no-unknown-locale/file') const keyLocalesRoot = join(__dirname, '../../fixtures/no-unknown-locale/key') const options = { json: { fileTest: { - parser: jsonParser, + languageOptions: { parser: jsonParser }, filename: join(fileLocalesRoot, 'test.json'), settings: { 'vue-i18n': { - localeDir: fileLocalesRoot + '/*.{json,yaml,yml}' + localeDir: `${fileLocalesRoot}/*.{json,yaml,yml}` } } }, fileEn: { - parser: jsonParser, + languageOptions: { parser: jsonParser }, filename: join(fileLocalesRoot, 'en.json'), settings: { 'vue-i18n': { - localeDir: fileLocalesRoot + '/*.{json,yaml,yml}' + localeDir: `${fileLocalesRoot}/*.{json,yaml,yml}` } } }, key: { - parser: jsonParser, + languageOptions: { parser: jsonParser }, filename: join(keyLocalesRoot, 'test.json'), settings: { 'vue-i18n': { localeDir: { - pattern: keyLocalesRoot + '/*.{json,yaml,yml}', + pattern: `${keyLocalesRoot}/*.{json,yaml,yml}`, localeKey: 'key' } as SettingsVueI18nLocaleDirObject } @@ -45,30 +45,30 @@ const options = { }, yaml: { fileTest: { - parser: yamlParser, + languageOptions: { parser: yamlParser }, filename: join(fileLocalesRoot, 'test.yaml'), settings: { 'vue-i18n': { - localeDir: fileLocalesRoot + '/*.{json,yaml,yml}' + localeDir: `${fileLocalesRoot}/*.{json,yaml,yml}` } } }, fileEn: { - parser: yamlParser, + languageOptions: { parser: yamlParser }, filename: join(fileLocalesRoot, 'en.yaml'), settings: { 'vue-i18n': { - localeDir: fileLocalesRoot + '/*.{json,yaml,yml}' + localeDir: `${fileLocalesRoot}/*.{json,yaml,yml}` } } }, key: { - parser: yamlParser, + languageOptions: { parser: yamlParser }, filename: join(keyLocalesRoot, 'test.yaml'), settings: { 'vue-i18n': { localeDir: { - pattern: keyLocalesRoot + '/*.{json,yaml,yml}', + pattern: `${keyLocalesRoot}/*.{json,yaml,yml}`, localeKey: 'key' } as SettingsVueI18nLocaleDirObject } @@ -78,8 +78,8 @@ const options = { } const tester = new RuleTester({ - parser: vueParser, - parserOptions: { + languageOptions: { + parser: vueParser, ecmaVersion: 2020, sourceType: 'module' } diff --git a/tests/lib/rules/no-unused-keys.ts b/tests/lib/rules/no-unused-keys.ts index d0eb8693..93dedb28 100644 --- a/tests/lib/rules/no-unused-keys.ts +++ b/tests/lib/rules/no-unused-keys.ts @@ -1,17 +1,22 @@ /** * @author kazuya kawaguchi (a.k.a. kazupon) */ -import { RuleTester } from 'eslint' +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' const cwdRoot = join(__dirname, '../../fixtures/no-unused-keys') new RuleTester({ - parser: require.resolve('vue-eslint-parser'), - parserOptions: { ecmaVersion: 2015, sourceType: 'module' } + languageOptions: { + parser: vueParser, + ecmaVersion: 2020, + sourceType: 'module' + } }).run('no-unused-keys', rule as never, { valid: [ { @@ -1992,8 +1997,8 @@ hello_dio: "こんにちは、アンダースコア DIO!" enableFix: true } ], - parserOptions: { - parser: '@typescript-eslint/parser' + languageOptions: { + parser: tsParser } }, { diff --git a/tests/lib/rules/no-v-html.ts b/tests/lib/rules/no-v-html.ts index aa0f9d81..cb062d86 100644 --- a/tests/lib/rules/no-v-html.ts +++ b/tests/lib/rules/no-v-html.ts @@ -1,12 +1,16 @@ /** * @author kazuya kawaguchi (a.k.a. kazupon) */ -import { RuleTester } from 'eslint' +import { RuleTester } from '../eslint-compat' import rule from '../../../lib/rules/no-v-html' +import * as vueParser from 'vue-eslint-parser' const tester = new RuleTester({ - parser: require.resolve('vue-eslint-parser'), - parserOptions: { ecmaVersion: 2015 } + languageOptions: { + parser: vueParser, + ecmaVersion: 2020, + sourceType: 'module' + } }) tester.run('no-v-html', rule as never, { diff --git a/tests/lib/rules/prefer-linked-key-with-paren.ts b/tests/lib/rules/prefer-linked-key-with-paren.ts index 0a253bec..c26a146a 100644 --- a/tests/lib/rules/prefer-linked-key-with-paren.ts +++ b/tests/lib/rules/prefer-linked-key-with-paren.ts @@ -2,12 +2,12 @@ * @author Yosuke Ota */ import { join } from 'node:path' -import { RuleTester } from 'eslint' +import { RuleTester, TEST_RULE_ID_PREFIX } from '../eslint-compat' import rule from '../../../lib/rules/prefer-linked-key-with-paren' +import * as vueParser from 'vue-eslint-parser' +import * as jsonParser from 'jsonc-eslint-parser' +import * as yamlParser from 'yaml-eslint-parser' -const vueParser = require.resolve('vue-eslint-parser') -const jsonParser = require.resolve('jsonc-eslint-parser') -const yamlParser = require.resolve('yaml-eslint-parser') const FIXTURES_ROOT = join( __dirname, '../../fixtures/prefer-linked-key-with-paren' @@ -16,12 +16,12 @@ const FIXTURES_ROOT = join( const options = { json(messageSyntaxVersion: string | null = '^9.0.0') { return { - parser: jsonParser, + languageOptions: { parser: jsonParser }, filename: join(FIXTURES_ROOT, 'test.json'), settings: { 'vue-i18n': { localeDir: { - pattern: FIXTURES_ROOT + '/*.{json,yaml,yml}' + pattern: `${FIXTURES_ROOT}/*.{json,yaml,yml}` }, messageSyntaxVersion } @@ -30,12 +30,12 @@ const options = { }, yaml(messageSyntaxVersion: string | null = '^9.0.0') { return { - parser: yamlParser, + languageOptions: { parser: yamlParser }, filename: join(FIXTURES_ROOT, 'test.yaml'), settings: { 'vue-i18n': { localeDir: { - pattern: FIXTURES_ROOT + '/*.{json,yaml,yml}' + pattern: `${FIXTURES_ROOT}/*.{json,yaml,yml}` }, messageSyntaxVersion } @@ -44,7 +44,7 @@ const options = { }, vue(messageSyntaxVersion: string | null = '^9.0.0') { return { - parser: vueParser, + languageOptions: { parser: vueParser }, filename: join(FIXTURES_ROOT, 'test.vue'), settings: { 'vue-i18n': { @@ -56,8 +56,7 @@ const options = { } const tester = new RuleTester({ - parser: vueParser, - parserOptions: { ecmaVersion: 2015 } + languageOptions: { parser: vueParser, ecmaVersion: 2015 } }) tester.run('prefer-linked-key-with-paren', rule as never, { @@ -386,7 +385,7 @@ tester.run('prefer-linked-key-with-paren', rule as never, { ...options.yaml(null), output: null, errors: [ - "If you want to use 'prefer-linked-key-with-paren' rule, you need to set 'messageSyntaxVersion' at 'settings'. See the 'eslint-plugin-vue-i18n' documentation" + `If you want to use '${TEST_RULE_ID_PREFIX}prefer-linked-key-with-paren' rule, you need to set 'messageSyntaxVersion' at 'settings'. See the 'eslint-plugin-vue-i18n' documentation` ] } ] diff --git a/tests/lib/rules/prefer-sfc-lang-attr.ts b/tests/lib/rules/prefer-sfc-lang-attr.ts index 93e431b6..ff2023ad 100644 --- a/tests/lib/rules/prefer-sfc-lang-attr.ts +++ b/tests/lib/rules/prefer-sfc-lang-attr.ts @@ -1,9 +1,13 @@ -import { RuleTester } from 'eslint' +import { RuleTester } from '../eslint-compat' import rule from '../../../lib/rules/prefer-sfc-lang-attr' +import * as vueParser from 'vue-eslint-parser' new RuleTester({ - parser: require.resolve('vue-eslint-parser'), - parserOptions: { ecmaVersion: 2020, sourceType: 'module' } + languageOptions: { + parser: vueParser, + ecmaVersion: 2020, + sourceType: 'module' + } }).run('prefer-sfc-lang-attr', rule as never, { valid: [ { diff --git a/tests/lib/rules/sfc-locale-attr.ts b/tests/lib/rules/sfc-locale-attr.ts index b69c8834..496f8ea7 100644 --- a/tests/lib/rules/sfc-locale-attr.ts +++ b/tests/lib/rules/sfc-locale-attr.ts @@ -1,11 +1,10 @@ -import { RuleTester } from 'eslint' +import { RuleTester } from '../eslint-compat' import rule from '../../../lib/rules/sfc-locale-attr' - -const vueParser = require.resolve('vue-eslint-parser') +import * as vueParser from 'vue-eslint-parser' const tester = new RuleTester({ - parser: vueParser, - parserOptions: { + languageOptions: { + parser: vueParser, ecmaVersion: 2020, sourceType: 'module' } diff --git a/tests/lib/rules/valid-message-syntax.ts b/tests/lib/rules/valid-message-syntax.ts index bb4e59f2..40c7da59 100644 --- a/tests/lib/rules/valid-message-syntax.ts +++ b/tests/lib/rules/valid-message-syntax.ts @@ -2,41 +2,41 @@ * @author Yosuke Ota */ import { join } from 'node:path' -import { RuleTester } from 'eslint' +import { RuleTester, TEST_RULE_ID_PREFIX } from '../eslint-compat' import rule from '../../../lib/rules/valid-message-syntax' +import * as vueParser from 'vue-eslint-parser' +import * as jsonParser from 'jsonc-eslint-parser' +import * as yamlParser from 'yaml-eslint-parser' -const vueParser = require.resolve('vue-eslint-parser') -const jsonParser = require.resolve('jsonc-eslint-parser') -const yamlParser = require.resolve('yaml-eslint-parser') const localesRoot = join(__dirname, '../../fixtures/valid-message-syntax') const options = { json: { default: { - parser: jsonParser, + languageOptions: { parser: jsonParser }, filename: join(localesRoot, 'test.json'), settings: { 'vue-i18n': { - localeDir: localesRoot + '/*.{json,yaml,yml}' + localeDir: `${localesRoot}/*.{json,yaml,yml}` } } }, v8: { - parser: jsonParser, + languageOptions: { parser: jsonParser }, filename: join(localesRoot, 'test.json'), settings: { 'vue-i18n': { - localeDir: localesRoot + '/*.{json,yaml,yml}', + localeDir: `${localesRoot}/*.{json,yaml,yml}`, messageSyntaxVersion: '^8.0.0' } } }, v9: { - parser: jsonParser, + languageOptions: { parser: jsonParser }, filename: join(localesRoot, 'test.json'), settings: { 'vue-i18n': { - localeDir: localesRoot + '/*.{json,yaml,yml}', + localeDir: `${localesRoot}/*.{json,yaml,yml}`, messageSyntaxVersion: '^9.0.0' } } @@ -44,30 +44,30 @@ const options = { }, yaml: { default: { - parser: yamlParser, + languageOptions: { parser: yamlParser }, filename: join(localesRoot, 'test.yaml'), settings: { 'vue-i18n': { - localeDir: localesRoot + '/*.{json,yaml,yml}' + localeDir: `${localesRoot}/*.{json,yaml,yml}` } } }, v8: { - parser: yamlParser, + languageOptions: { parser: yamlParser }, filename: join(localesRoot, 'test.yaml'), settings: { 'vue-i18n': { - localeDir: localesRoot + '/*.{json,yaml,yml}', + localeDir: `${localesRoot}/*.{json,yaml,yml}`, messageSyntaxVersion: '^8.0.0' } } }, v9: { - parser: yamlParser, + languageOptions: { parser: yamlParser }, filename: join(localesRoot, 'test.yaml'), settings: { 'vue-i18n': { - localeDir: localesRoot + '/*.{json,yaml,yml}', + localeDir: `${localesRoot}/*.{json,yaml,yml}`, messageSyntaxVersion: '^9.0.0' } } @@ -75,20 +75,20 @@ const options = { }, vue: { default: { - parser: vueParser, + languageOptions: { parser: vueParser }, filename: join(localesRoot, 'test.vue'), settings: { 'vue-i18n': { - localeDir: localesRoot + '/*.{json,yaml,yml}' + localeDir: `${localesRoot}/*.{json,yaml,yml}` } } }, v9: { - parser: vueParser, + languageOptions: { parser: vueParser }, filename: join(localesRoot, 'test.vue'), settings: { 'vue-i18n': { - localeDir: localesRoot + '/*.{json,yaml,yml}', + localeDir: `${localesRoot}/*.{json,yaml,yml}`, messageSyntaxVersion: '^9.0.0' } } @@ -97,8 +97,7 @@ const options = { } const tester = new RuleTester({ - parser: vueParser, - parserOptions: { ecmaVersion: 2015 } + languageOptions: { parser: vueParser, ecmaVersion: 2015 } }) tester.run('valid-message-syntax', rule as never, { @@ -156,8 +155,7 @@ tester.run('valid-message-syntax', rule as never, { ...options.json.default, errors: [ { - message: - "If you want to use 'valid-message-syntax' rule, you need to set 'messageSyntaxVersion' at 'settings'. See the 'eslint-plugin-vue-i18n' documentation", + message: `If you want to use '${TEST_RULE_ID_PREFIX}valid-message-syntax' rule, you need to set 'messageSyntaxVersion' at 'settings'. See the 'eslint-plugin-vue-i18n' documentation`, line: 1, column: 1 }, @@ -232,8 +230,7 @@ tester.run('valid-message-syntax', rule as never, { ...options.yaml.default, errors: [ { - message: - "If you want to use 'valid-message-syntax' rule, you need to set 'messageSyntaxVersion' at 'settings'. See the 'eslint-plugin-vue-i18n' documentation", + message: `If you want to use '${TEST_RULE_ID_PREFIX}valid-message-syntax' rule, you need to set 'messageSyntaxVersion' at 'settings'. See the 'eslint-plugin-vue-i18n' documentation`, line: 1, column: 1 }, @@ -251,8 +248,7 @@ tester.run('valid-message-syntax', rule as never, { ...options.yaml.default, errors: [ { - message: - "If you want to use 'valid-message-syntax' rule, you need to set 'messageSyntaxVersion' at 'settings'. See the 'eslint-plugin-vue-i18n' documentation", + message: `If you want to use '${TEST_RULE_ID_PREFIX}valid-message-syntax' rule, you need to set 'messageSyntaxVersion' at 'settings'. See the 'eslint-plugin-vue-i18n' documentation`, line: 1, column: 1 }, @@ -374,8 +370,7 @@ tester.run('valid-message-syntax', rule as never, { ...options.vue.default, errors: [ { - message: - "If you want to use 'valid-message-syntax' rule, you need to set 'messageSyntaxVersion' at 'settings'. See the 'eslint-plugin-vue-i18n' documentation", + message: `If you want to use '${TEST_RULE_ID_PREFIX}valid-message-syntax' rule, you need to set 'messageSyntaxVersion' at 'settings'. See the 'eslint-plugin-vue-i18n' documentation`, line: 1, column: 1 }, diff --git a/tests/lib/test-utils.ts b/tests/lib/test-utils.ts index 41b6fb57..3251e078 100644 --- a/tests/lib/test-utils.ts +++ b/tests/lib/test-utils.ts @@ -2,19 +2,26 @@ import { readFileSync, readdirSync, statSync } from 'fs' import { join, extname } from 'path' import type { SettingsVueI18nLocaleDir } from '../../lib/types' 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' + +type LanguageOptions = { + parser: object +} export function getTestCasesFromFixtures(testOptions: { cwd: string options?: unknown[] localeDir?: SettingsVueI18nLocaleDir - parserOptions?: RuleTester.ValidTestCase['parserOptions'] + languageOptions?: LanguageOptions }): IterableIterator export function getTestCasesFromFixtures( testOptions: { cwd: string options?: unknown[] localeDir?: SettingsVueI18nLocaleDir - parserOptions?: RuleTester.ValidTestCase['parserOptions'] + languageOptions?: LanguageOptions }, outputs: { [file: string]: @@ -27,6 +34,7 @@ export function* getTestCasesFromFixtures( cwd: string options?: unknown[] localeDir?: SettingsVueI18nLocaleDir + languageOptions?: LanguageOptions }, outputs?: { [file: string]: @@ -44,8 +52,11 @@ export function* getTestCasesFromFixtures( code: readFileSync(filename, 'utf8'), filename, options: testOptions.options || [], - parser, - parserOptions: {}, + // @ts-expect-error -- Type error for eslint v9 + languageOptions: { + ...testOptions.languageOptions, + ...(parser ? { parser } : {}) + }, settings: { 'vue-i18n': { localeDir: testOptions.localeDir, @@ -69,18 +80,18 @@ export function* getTestCasesFromFixtures( } } -const PARSERS = { +const PARSERS: Record = { '.js': undefined, - '.vue': require.resolve('vue-eslint-parser'), - '.json': require.resolve('jsonc-eslint-parser'), - '.json5': require.resolve('jsonc-eslint-parser'), - '.yaml': require.resolve('yaml-eslint-parser'), - '.yml': require.resolve('yaml-eslint-parser') + '.vue': vueParser, + '.json': jsonParser, + '.json5': jsonParser, + '.yaml': yamlParser, + '.yml': yamlParser } function* extractTargetFiles(dir: string): IterableIterator<{ filename: string relative: string - parser: string | undefined + parser: object | undefined }> { for (const relative of readdirSync(dir)) { if (relative === 'node_modules' || relative === '.eslintrc.js') { @@ -88,14 +99,7 @@ function* extractTargetFiles(dir: string): IterableIterator<{ } const filename = join(dir, relative) const ext = extname(relative) - if ( - ext === '.js' || - ext === '.vue' || - ext === '.json' || - ext === '.json5' || - ext === '.yaml' || - ext === '.yml' - ) { + if (PARSERS[ext]) { yield { filename, relative, parser: PARSERS[ext] } continue } diff --git a/tests/lib/utils/index.ts b/tests/lib/utils/index.ts index 7cbdba68..74bfcc6c 100644 --- a/tests/lib/utils/index.ts +++ b/tests/lib/utils/index.ts @@ -27,16 +27,18 @@ describe('getLocaleMessages', () => { }) const localeDir = 'tests/fixtures/utils/get-locale-messages/**/*.json' + const parserServices = {} const dummyContext: RuleContext = { getFilename() { return 'input.vue' }, getSourceCode() { return { - ast: {} + ast: {}, + parserServices } }, - parserServices: {}, + parserServices, settings: { 'vue-i18n': { localeDir diff --git a/tests/lib/utils/rule.ts b/tests/lib/utils/rule.ts index 4802b98e..711c38e0 100644 --- a/tests/lib/utils/rule.ts +++ b/tests/lib/utils/rule.ts @@ -5,10 +5,10 @@ describe('valid rule meta', () => { for (const ruleId of Object.keys(rules)) { const rule = rules[ruleId as keyof typeof rules] - it('should be valid rule url for ' + ruleId + '.', () => { + it(`should be valid rule url for ${ruleId}.`, () => { strictEqual( rule.meta.docs.url, - 'https://eslint-plugin-vue-i18n.intlify.dev/rules/' + ruleId + '.html' + `https://eslint-plugin-vue-i18n.intlify.dev/rules/${ruleId}.html` ) }) } diff --git a/tsconfig.json b/tsconfig.json index 4162df1d..9e95d3bd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,7 @@ /* Basic Options */ // "incremental": true, /* Enable incremental compilation */ "target": "es2015" /* 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'. */, + "module": "node16" /* 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. */ @@ -36,7 +36,7 @@ // "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). */, + "moduleResolution": "node16" /* 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. */