diff --git a/.changeset/cyan-needles-sniff.md b/.changeset/cyan-needles-sniff.md new file mode 100644 index 00000000..46b505dc --- /dev/null +++ b/.changeset/cyan-needles-sniff.md @@ -0,0 +1,5 @@ +--- +"@intlify/eslint-plugin-vue-i18n": major +--- + +Drop support for vue-i18n v8 syntax diff --git a/docs/.vitepress/components/eslint-code-block.vue b/docs/.vitepress/components/eslint-code-block.vue index d4bbda4d..8f733f5b 100644 --- a/docs/.vitepress/components/eslint-code-block.vue +++ b/docs/.vitepress/components/eslint-code-block.vue @@ -58,7 +58,7 @@ export default { }, messageSyntaxVersion: { type: String, - default: '^9' + default: '^11' } }, diff --git a/docs/started.md b/docs/started.md index 6627fb3e..38ac84fc 100644 --- a/docs/started.md +++ b/docs/started.md @@ -73,7 +73,7 @@ export default [ // Specify the version of `vue-i18n` you are using. // If not specified, the message will be parsed twice. - messageSyntaxVersion: '^9.0.0' + messageSyntaxVersion: '^11.0.0' } } } @@ -144,7 +144,7 @@ module.exports = { // Specify the version of `vue-i18n` you are using. // If not specified, the message will be parsed twice. - messageSyntaxVersion: '^9.0.0' + messageSyntaxVersion: '^11.0.0' } } } diff --git a/lib/rules/prefer-linked-key-with-paren.ts b/lib/rules/prefer-linked-key-with-paren.ts index 4ca8d7f9..1c628694 100644 --- a/lib/rules/prefer-linked-key-with-paren.ts +++ b/lib/rules/prefer-linked-key-with-paren.ts @@ -19,7 +19,6 @@ import { NodeTypes } from '../utils/message-compiler/utils' 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 { getFilename, getSourceCode } from '../utils/compat' @@ -42,7 +41,7 @@ function create(context: RuleContext): RuleListener { const sourceCode = getSourceCode(context) const messageSyntaxVersions = getMessageSyntaxVersions(context) - function verifyForNewSyntax( + function verifySyntax( message: string, reportNode: JSONAST.JSONStringLiteral | YAMLAST.YAMLScalar, getReportOffset: GetReportOffset @@ -83,49 +82,6 @@ function create(context: RuleContext): RuleListener { }) } - function verifyForV8( - message: string, - reportNode: JSONAST.JSONStringLiteral | YAMLAST.YAMLScalar, - getReportOffset: GetReportOffset - ) { - const { ast, errors } = parseForV8(message) - if (errors.length) { - return - } - traverseNode(ast, node => { - if (node.type !== NodeTypes.LinkedKey) { - return - } - if (message[node.loc!.start.offset - 1] === '(') { - return - } - let range: [number, number] | null = null - const start = getReportOffset(node.loc!.start.offset) - const end = getReportOffset(node.loc!.end.offset) - if (start != null && end != null) { - range = [start, end] - } - context.report({ - loc: range - ? { - start: sourceCode.getLocFromIndex(range[0]), - end: sourceCode.getLocFromIndex(range[1]) - } - : reportNode.loc, - message: 'The linked message key must be enclosed in parentheses.', - fix(fixer) { - if (!range) { - return null - } - return [ - fixer.insertTextBeforeRange(range, '('), - fixer.insertTextAfterRange(range, ')') - ] - } - }) - }) - } - function verifyMessage( message: string, reportNode: JSONAST.JSONStringLiteral | YAMLAST.YAMLScalar, @@ -134,18 +90,8 @@ function create(context: RuleContext): RuleListener { if (messageSyntaxVersions.reportIfMissingSetting()) { return } - const newSyntax = messageSyntaxVersions.v9 || messageSyntaxVersions.v10 - const v8Syntax = messageSyntaxVersions.v8 - if (newSyntax && v8Syntax) { - // This rule cannot support two versions in the same project. - return - } - if (newSyntax) { - verifyForNewSyntax(message, reportNode, getReportOffset) - } else if (v8Syntax) { - verifyForV8(message, reportNode, getReportOffset) - } + verifySyntax(message, reportNode, getReportOffset) } const createVisitorForJson = defineCreateVisitorForJson(verifyMessage) diff --git a/lib/rules/valid-message-syntax.ts b/lib/rules/valid-message-syntax.ts index e7b5f4a9..f982ffc5 100644 --- a/lib/rules/valid-message-syntax.ts +++ b/lib/rules/valid-message-syntax.ts @@ -15,7 +15,6 @@ import { } from '../utils/message-compiler/utils' import { parse } from '../utils/message-compiler/parser' import { parse as parseForV9 } from '../utils/message-compiler/parser-v9' -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' @@ -28,19 +27,15 @@ function create(context: RuleContext): RuleListener { const messageSyntaxVersions = getMessageSyntaxVersions(context) function* extractMessageErrors(message: string) { - // v10 and v9 generate nearly identical errors so only one of them will be returned. - const errorsForV10OrV9: CompileError[] = [] - if (messageSyntaxVersions.v10) { - errorsForV10OrV9.push(...parse(message).errors) + // v10/v11 and v9 generate nearly identical errors so only one of them will be returned. + const errors: CompileError[] = [] + if (messageSyntaxVersions.v10 || messageSyntaxVersions.v11) { + errors.push(...parse(message).errors) } - if (messageSyntaxVersions.v9 && !errorsForV10OrV9.length) { - errorsForV10OrV9.push(...parseForV9(message).errors) - } - yield* errorsForV10OrV9 - - if (messageSyntaxVersions.v8) { - yield* parseForV8(message).errors + if (messageSyntaxVersions.v9 && !errors.length) { + errors.push(...parseForV9(message).errors) } + yield* errors } function verifyMessage( message: string | number | undefined | null | boolean | bigint | RegExp, diff --git a/lib/utils/collect-linked-keys.ts b/lib/utils/collect-linked-keys.ts index 82fb75ab..b223df2c 100644 --- a/lib/utils/collect-linked-keys.ts +++ b/lib/utils/collect-linked-keys.ts @@ -7,7 +7,6 @@ import { traverseNode } from './message-compiler/traverser' import type { I18nLocaleMessageDictionary, RuleContext } from '../types' import { parse } from './message-compiler/parser' import { parse as parseForV9 } from './message-compiler/parser-v9' -import { parse as parseForV8 } from './message-compiler/parser-v8' import type { MessageSyntaxVersions } from './message-compiler/utils' import { NodeTypes } from './message-compiler/utils' import { getMessageSyntaxVersions } from './message-compiler/utils' @@ -28,15 +27,12 @@ function* extractUsedKeysFromLinks( if (typeof value === 'object') { yield* extractUsedKeysFromLinks(value, messageSyntaxVersions) } else if (typeof value === 'string') { - if (messageSyntaxVersions.v10) { + if (messageSyntaxVersions.v10 || messageSyntaxVersions.v11) { yield* extractUsedKeysFromAST(parse(value).ast) } if (messageSyntaxVersions.v9) { yield* extractUsedKeysFromAST(parseForV9(value).ast) } - if (messageSyntaxVersions.v8) { - yield* extractUsedKeysFromAST(parseForV8(value).ast) - } } } } diff --git a/lib/utils/message-compiler/parser-v8.ts b/lib/utils/message-compiler/parser-v8.ts deleted file mode 100644 index bce8627c..00000000 --- a/lib/utils/message-compiler/parser-v8.ts +++ /dev/null @@ -1,333 +0,0 @@ -/** - * A simplified version of the message parser that handles messages like vue-i18n v8. - * This parser probably has poor performance. - */ -import type { - CompileError, - MessageNode, - NamedNode, - PluralNode, - TextNode, - ResourceNode, - SourceLocation, - ListNode, - LinkedNode, - LinkedModifierNode, - LinkedKeyNode -} from '@intlify/message-compiler' -import { sortedLastIndex } from 'lodash' -import { NodeTypes } from './utils' -import type { ModuloNamedNode } from './parser-v9' - -export function parse(code: string): { - ast: ResourceNode - errors: CompileError[] -} { - const errors: CompileError[] = [] - const ast = parseAST(code, errors) - return { - ast, - errors - } -} - -class CodeContext { - public code: string - public buff: string - public offset: number - private lineStartIndices: number[] - private lines: string[] - constructor(code: string) { - this.code = code - this.buff = code - this.offset = 0 - this.lines = [] - this.lineStartIndices = [0] - const lineEndingPattern = /\r\n|[\r\n\u2028\u2029]/gu - let match - while ((match = lineEndingPattern.exec(this.code))) { - this.lines.push( - this.code.slice( - this.lineStartIndices[this.lineStartIndices.length - 1], - match.index - ) - ) - this.lineStartIndices.push(match.index + match[0].length) - } - this.lines.push( - this.code.slice(this.lineStartIndices[this.lineStartIndices.length - 1]) - ) - } - setOffset(offset: number) { - this.offset = offset - this.buff = this.code.slice(offset) - } - - getLocFromIndex(index: number) { - if (index === this.code.length) { - return { - line: this.lines.length, - column: this.lines[this.lines.length - 1].length + 1 - } - } - const lineNumber = sortedLastIndex(this.lineStartIndices, index) - return { - line: lineNumber, - column: index - this.lineStartIndices[lineNumber - 1] + 1 - } - } - - getNodeLoc(start: number, end: number) { - const startLoc = this.getLocFromIndex(start) - const endLoc = this.getLocFromIndex(end) - return { - start, - end, - loc: { - start: { ...startLoc, offset: start }, - end: { ...endLoc, offset: end } - } - } - } - setEndLoc( - node: { - end?: number - loc?: SourceLocation - }, - end: number - ) { - const endLoc = this.getLocFromIndex(end) - node.end = end - node.loc!.end = { ...endLoc, offset: end } - } - createCompileError(message: string, offset: number) { - const loc = this.getLocFromIndex(offset) - const error: CompileError = new SyntaxError(message) as never - error.code = 42 - error.location = { - start: { ...loc, offset }, - end: { ...loc, offset } - } - error.domain = 'parser' - return error - } -} - -function parseAST(code: string, errors: CompileError[]): ResourceNode { - const ctx = new CodeContext(code) - const regexp = /%?\{|@[\.:]|\s*\|\s*/u - let re - const node: ResourceNode = { - type: NodeTypes.Resource, - body: undefined as never, - ...ctx.getNodeLoc(0, code.length) - } - let messageNode: MessageNode = { - type: NodeTypes.Message, - items: [], - ...ctx.getNodeLoc(0, code.length) - } - let body: MessageNode | PluralNode = messageNode - while ((re = regexp.exec(ctx.buff))) { - const key = re[0] - const startOffset = ctx.offset + re.index - const endOffset = startOffset + key.length - if (ctx.offset < startOffset) { - const textNode: TextNode = { - type: NodeTypes.Text, - value: ctx.code.slice(ctx.offset, startOffset), - ...ctx.getNodeLoc(ctx.offset, startOffset) - } - messageNode.items.push(textNode) - } - if (key.trim() === '|') { - ctx.setEndLoc(messageNode, startOffset) - - if (body.type === NodeTypes.Message) { - const pluralNode: PluralNode = { - type: NodeTypes.Plural, - cases: [body], - start: body.start, - end: body.end, - loc: { - start: { ...body.loc!.start }, - end: { ...body.loc!.end } - } - } - body = pluralNode - } - messageNode = { - type: NodeTypes.Message, - items: [], - ...ctx.getNodeLoc(endOffset, endOffset) - } - body.cases.push(messageNode) - ctx.setOffset(endOffset) - continue - } - if (key === '{' || key === '%{') { - const endIndex = ctx.code.indexOf('}', endOffset) - let keyValue: string - if (endIndex > -1) { - keyValue = ctx.code.slice(endOffset, endIndex) - } else { - errors.push( - ctx.createCompileError('Unterminated closing brace', endOffset) - ) - keyValue = ctx.code.slice(endOffset) - } - - const placeholderEndOffset = endOffset + keyValue.length + 1 - - let node: NamedNode | ListNode | null = null - const trimmedKeyValue = keyValue.trim() - if (trimmedKeyValue) { - if (trimmedKeyValue !== keyValue) { - errors.push( - ctx.createCompileError( - 'Unexpected space before or after the placeholder key', - endOffset - ) - ) - } - if (/^-?\d+$/u.test(trimmedKeyValue)) { - const num = Number(trimmedKeyValue) - const listNode: ListNode = { - type: NodeTypes.List, - index: num, - ...ctx.getNodeLoc(endOffset - 1, placeholderEndOffset) - } - if (num < 0) { - errors.push( - ctx.createCompileError( - 'Unexpected minus placeholder index', - endOffset - ) - ) - } - node = listNode - } - if (!node) { - const namedNode: ModuloNamedNode = { - type: NodeTypes.Named, - key: trimmedKeyValue, - ...ctx.getNodeLoc(endOffset - 1, placeholderEndOffset) - } - if (key === '%{') { - namedNode.modulo = true - } - if (!/^[a-zA-Z][a-zA-Z0-9_$]*$/.test(namedNode.key)) { - errors.push( - ctx.createCompileError('Unexpected placeholder key', endOffset) - ) - } - node = namedNode - } - - messageNode.items.push(node) - } else { - errors.push( - ctx.createCompileError('Empty placeholder', placeholderEndOffset - 1) - ) - } - - ctx.setOffset(placeholderEndOffset) - continue - } - if (key[0] === '@') { - ctx.setOffset(endOffset) - - messageNode.items.push(parseLiked(ctx, errors)) - continue - } - } - if (ctx.buff) { - const textNode: TextNode = { - type: NodeTypes.Text, - value: ctx.buff, - ...ctx.getNodeLoc(ctx.offset, code.length) - } - messageNode.items.push(textNode) - } - ctx.setEndLoc(messageNode, code.length) - ctx.setEndLoc(body, code.length) - node.body = body - return node -} - -function parseLiked(ctx: CodeContext, errors: CompileError[]) { - const linked: LinkedNode = { - type: NodeTypes.Linked, - key: undefined as never, - ...ctx.getNodeLoc(ctx.offset - 2, ctx.offset) - } - const mark = ctx.code[ctx.offset - 1] - if (mark === '.') { - const modifierValue = /^[a-z]*/u.exec(ctx.buff)![0] - const modifierEndOffset = ctx.offset + modifierValue.length - const modifier: LinkedModifierNode = { - type: NodeTypes.LinkedModifier, - value: modifierValue, - ...ctx.getNodeLoc(ctx.offset - 1, modifierEndOffset) - } - // empty modifier... - if (!modifier.value) { - errors.push( - ctx.createCompileError( - 'Expected linked modifier value', - modifier.loc!.start.offset - ) - ) - } - ctx.setOffset(modifierEndOffset) - linked.modifier = modifier - if (ctx.code[ctx.offset] !== ':') { - // empty key... - errors.push( - ctx.createCompileError('Expected linked key value', ctx.offset) - ) - const key: LinkedKeyNode = { - type: NodeTypes.LinkedKey, - value: '', - ...ctx.getNodeLoc(ctx.offset, ctx.offset) - } - linked.key = key - ctx.setEndLoc(linked, ctx.offset) - return linked - } - ctx.setOffset(ctx.offset + 1) - } - let paren = false - if (ctx.buff[0] === '(') { - ctx.setOffset(ctx.offset + 1) - paren = true - } - // see https://github.com/kazupon/vue-i18n/blob/96a676cca51b592f3f8718b149ef26b3c8e70a64/src/index.js#L28 - const keyValue = /^[\w\-_|.]*/u.exec(ctx.buff)![0] - const keyEndOffset = ctx.offset + keyValue.length - const key: LinkedKeyNode = { - type: NodeTypes.LinkedKey, - value: keyValue, - ...ctx.getNodeLoc(ctx.offset, keyEndOffset) - } - // empty key... - if (!key.value) { - errors.push( - ctx.createCompileError('Expected linked key value', key.loc!.start.offset) - ) - } - linked.key = key - ctx.setOffset(keyEndOffset) - if (paren) { - if (ctx.buff[0] === ')') { - ctx.setOffset(ctx.offset + 1) - } else { - errors.push( - ctx.createCompileError('Unterminated closing paren', ctx.offset) - ) - } - } - - ctx.setEndLoc(linked, ctx.offset) - return linked -} diff --git a/lib/utils/message-compiler/utils.ts b/lib/utils/message-compiler/utils.ts index 24a7e564..99095c37 100644 --- a/lib/utils/message-compiler/utils.ts +++ b/lib/utils/message-compiler/utils.ts @@ -17,9 +17,9 @@ export const NodeTypes = { } as const export type MessageSyntaxVersions = { - v8: boolean v9: boolean v10: boolean + v11: boolean isNotSet: boolean reportIfMissingSetting: () => boolean } @@ -36,9 +36,9 @@ export function getMessageSyntaxVersions( if (!messageSyntaxVersion) { return { - v8: true, v9: true, v10: true, + v11: true, isNotSet: true, reportIfMissingSetting: () => { if (!puttedSettingsError.has(context)) { @@ -54,10 +54,20 @@ export function getMessageSyntaxVersions( } } const range = new Range(messageSyntaxVersion) + const v9 = intersects(range, '^9.0.0-0') + const v10 = intersects(range, '^10.0.0-0') + const v11 = intersects(range, '>=11.0.0-0') + if (!v9 && !v10 && !v11 && !puttedSettingsError.has(context)) { + context.report({ + loc: { line: 1, column: 0 }, + message: `Please specify 9 or higher for 'messageSyntaxVersion' at 'settings'.` + }) + puttedSettingsError.add(context) + } return { - v8: intersects(range, '^8.0.0 || <=8.0.0'), - v9: intersects(range, '^9.0.0-0'), - v10: intersects(range, '>=10.0.0-0'), + v9, + v10, + v11, isNotSet: false, reportIfMissingSetting: () => false } diff --git a/tests/lib/rules/prefer-linked-key-with-paren.ts b/tests/lib/rules/prefer-linked-key-with-paren.ts index c45c68f1..512bf7a6 100644 --- a/tests/lib/rules/prefer-linked-key-with-paren.ts +++ b/tests/lib/rules/prefer-linked-key-with-paren.ts @@ -27,16 +27,6 @@ tester.run('prefer-linked-key-with-paren', rule as never, { `, ...options.yaml() }, - { - code: ` - { - "foo": { - "bar": "baz" - } - } - `, - ...options.json('^8') - }, { code: ` foo: @@ -51,20 +41,6 @@ tester.run('prefer-linked-key-with-paren', rule as never, { `, ...options.json() }, - { - code: ` - foo: - bar: "@:(baz)" - `, - ...options.yaml('^8') - }, - { - code: ` - {"foo": { - "bar": "@:(baz)" } } - `, - ...options.json('^8') - }, { code: ` @@ -77,28 +53,12 @@ tester.run('prefer-linked-key-with-paren', rule as never, { `, ...options.vue() }, - - { - // This rule cannot support two versions in the same project. - code: ` - a: "@:link" - `, - ...options.yaml('^8 || ^9') - }, - { // message parse error code: ` a: "@.:link" `, ...options.yaml('^9') - }, - { - // message parse error - code: ` - a: "@.:link" - `, - ...options.yaml('^8') } ], @@ -139,42 +99,6 @@ tester.run('prefer-linked-key-with-paren', rule as never, { } ] }, - { - code: ` - foo: "@:baz" - `, - ...options.yaml('^8'), - output: ` - foo: "@:(baz)" - `, - errors: [ - { - message: 'The linked message key must be enclosed in parentheses.', - line: 2, - column: 15, - endLine: 2, - endColumn: 18 - } - ] - }, - { - code: ` - { "foo": "@:baz" } - `, - ...options.json('^8'), - output: ` - { "foo": "@:(baz)" } - `, - errors: [ - { - message: 'The linked message key must be enclosed in parentheses.', - line: 2, - column: 19, - endLine: 2, - endColumn: 22 - } - ] - }, { code: ` @@ -212,43 +136,6 @@ tester.run('prefer-linked-key-with-paren', rule as never, { } ] }, - { - code: ` - - { "foo": "@:baz" } - - - "foo": - - "@:baz" - - `, - ...options.vue('^8'), - output: ` - - { "foo": "@:(baz)" } - - - "foo": - - "@:(baz)" - - `, - errors: [ - { - message: 'The linked message key must be enclosed in parentheses.', - line: 3, - column: 19, - endLine: 3, - endColumn: 22 - }, - { - message: 'The linked message key must be enclosed in parentheses.', - line: 7, - column: 14, - endLine: 7, - endColumn: 17 - } - ] - }, { code: ` @@ -304,47 +191,31 @@ tester.run('prefer-linked-key-with-paren', rule as never, { 'The linked message key must be enclosed in brackets.' ] }, + { code: ` - a: message @:foo - b: 'message @:foo' - c: | - message @:foo - message @:foo - ? [{"message @:foo": "message @:foo"}] - : - ? "message @:foo" - : "foo" - `, - ...options.yaml('^8'), - output: ` - a: message @:(foo) - b: 'message @:(foo)' - c: | - message @:foo - message @:foo - ? [{"message @:foo": "message @:foo"}] - : - ? "message @:foo" - : "foo" + a: "@:(link)" + b: "@:{'link'}" `, + ...options.yaml(null), + output: null, errors: [ - 'The linked message key must be enclosed in parentheses.', - 'The linked message key must be enclosed in parentheses.', - 'The linked message key must be enclosed in parentheses.', - 'The linked message key must be enclosed in parentheses.' + `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` ] }, { code: ` - a: "@:(link)" - b: "@:{'link'}" + { + "foo": { + "bar": "baz" + } + } `, - ...options.yaml(null), + ...options.json('^8'), output: null, errors: [ - `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` + "Please specify 9 or higher for 'messageSyntaxVersion' at 'settings'." ] } ] diff --git a/tests/lib/rules/valid-message-syntax.ts b/tests/lib/rules/valid-message-syntax.ts index 4b1195ff..199ac702 100644 --- a/tests/lib/rules/valid-message-syntax.ts +++ b/tests/lib/rules/valid-message-syntax.ts @@ -21,16 +21,6 @@ const options = { } } }, - v8: { - languageOptions: { parser: jsonParser }, - filename: join(localesRoot, 'test.json'), - settings: { - 'vue-i18n': { - localeDir: `${localesRoot}/*.{json,yaml,yml}`, - messageSyntaxVersion: '^8.0.0' - } - } - }, v9: { languageOptions: { parser: jsonParser }, filename: join(localesRoot, 'test.json'), @@ -52,16 +42,6 @@ const options = { } } }, - v8: { - languageOptions: { parser: yamlParser }, - filename: join(localesRoot, 'test.yaml'), - settings: { - 'vue-i18n': { - localeDir: `${localesRoot}/*.{json,yaml,yml}`, - messageSyntaxVersion: '^8.0.0' - } - } - }, v9: { languageOptions: { parser: yamlParser }, filename: join(localesRoot, 'test.yaml'), @@ -118,18 +98,6 @@ tester.run('valid-message-syntax', rule as never, { `, ...options.yaml.default }, - { - code: ` - key: message {foo} - `, - ...options.yaml.v8 - }, - { - code: ` - key: message @:(v8) - `, - ...options.yaml.v8 - }, { code: ` key: message {foo} @@ -164,20 +132,10 @@ tester.run('valid-message-syntax', rule as never, { line: 3, column: 32 }, - { - message: 'Unexpected placeholder key', - line: 3, - column: 32 - }, { message: 'Not allowed nest placeholder', line: 4, column: 33 - }, - { - message: 'Unexpected placeholder key', - line: 4, - column: 33 } ] }, @@ -202,136 +160,6 @@ tester.run('valid-message-syntax', rule as never, { } ] }, - { - code: ` - { - "list-hello": "Hello! {{0}}", - "named-hello": "Hello! {{name}}" - } - `, - ...options.json.v8, - errors: [ - { - message: 'Unexpected placeholder key', - line: 3, - column: 32 - }, - { - message: 'Unexpected placeholder key', - line: 4, - column: 33 - } - ] - }, - // { - // // The syntax is now allowed. - // code: ` - // key: message @:(v8) - // `, - // ...options.yaml.default, - // errors: [ - // { - // 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 - // }, - // { - // message: 'Unexpected empty linked key', - // line: 2, - // column: 21 - // } - // ] - // }, - { - code: ` - key: message { v9 } - `, - ...options.yaml.default, - errors: [ - { - 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 - }, - { - message: 'Unexpected space before or after the placeholder key', - line: 2, - column: 21 - } - ] - }, - { - code: ` - key: message { v9 } - `, - ...options.yaml.v8, - errors: [ - { - message: 'Unexpected space before or after the placeholder key', - line: 2, - column: 21 - } - ] - }, - // { - // // The syntax is now allowed. - // code: ` - // key: message @:(v8) - // `, - // ...options.yaml.v9, - // errors: [ - // { - // message: 'Unexpected empty linked key', - // line: 2, - // column: 21 - // } - // ] - // }, - // { - // // The syntax is now allowed. - // code: ` - // key: message new line - // @:(v8) - // `, - // ...options.yaml.v9, - // errors: [ - // { - // message: 'Unexpected empty linked key', - // line: 3, - // column: 10 - // } - // ] - // }, - // { - // // The syntax is now allowed. - // code: ` - // key: "message new line - // @:(v8)" - // `, - // ...options.yaml.v9, - // errors: [ - // { - // message: 'Unexpected empty linked key', - // line: 3, - // column: 10 - // } - // ] - // }, - // { - // // The syntax is now allowed. - // code: ` - // key: 'message new line - // @:(v8)' - // `, - // ...options.yaml.v9, - // errors: [ - // { - // message: 'Unexpected empty linked key', - // line: 3, - // column: 10 - // } - // ] - // }, { code: ` @@ -344,6 +172,7 @@ tester.run('valid-message-syntax', rule as never, { a: "message {invalid" b: [ "message {valid}" ] + `, ...options.vue.v9, errors: [ @@ -371,6 +200,7 @@ tester.run('valid-message-syntax', rule as never, { a: "message {invalid" b: [ "message {valid}" ] + `, ...options.vue.default, errors: [ @@ -384,16 +214,6 @@ tester.run('valid-message-syntax', rule as never, { line: 4, column: 22 }, - { - message: 'Unterminated closing brace', - line: 4, - column: 22 - }, - { - message: 'Unterminated closing brace', - line: 9, - column: 22 - }, { message: 'Unterminated closing brace', line: 9, @@ -401,61 +221,6 @@ tester.run('valid-message-syntax', rule as never, { } ] }, - - { - code: ` - { - "foo": { - "a": "message {invalid", - "b": [ "message { v8invalid }" ], - "c": \`message - @.:invalid"\`, - "d": 42, - "e": /message/, - "f": ["message valid",,"message valid"] - } - } - `, - ...options.json.v8, - errors: [ - { - message: 'Unterminated closing brace', - line: 4, - column: 26 - }, - { - message: 'Unexpected space before or after the placeholder key', - line: 5, - column: 28 - }, - { - message: 'Expected linked modifier value', - line: 7, - column: 14 - }, - { - message: "Unexpected 'number' message", - line: 8, - column: 16, - endLine: 8, - endColumn: 18 - }, - { - message: "Unexpected 'RegExp' message", - line: 9, - column: 16, - endLine: 9, - endColumn: 25 - }, - { - message: "Unexpected 'null' message", - line: 10, - column: 16, - endLine: 10, - endColumn: 50 - } - ] - }, { code: ` foo: diff --git a/tests/lib/utils/collect-linked-keys.ts b/tests/lib/utils/collect-linked-keys.ts index 97099839..46b40398 100644 --- a/tests/lib/utils/collect-linked-keys.ts +++ b/tests/lib/utils/collect-linked-keys.ts @@ -39,18 +39,7 @@ describe('collectLinkedKeys', () => { const expected = ['message.homeAddress'] deepStrictEqual(collectLinkedKeys(object, createContext()), expected) }) - it('should be get the keys used in the linked message with brackets.', () => { - const object = { - message: { - dio: 'DIO', - linked: "There's a reason, you lost, @:(message.dio)." - } - } - - const expected = ['message.dio'] - deepStrictEqual(collectLinkedKeys(object, createContext()), expected) - }) - it('should be get the keys used in the linked message for v9.', () => { + it('should be get the keys used in the linked message.', () => { const object = { message: { dio: 'DIO', @@ -97,22 +86,15 @@ describe('collectLinkedKeys', () => { expected ) }) - it('v8', () => { - const expected = ['bar.a', 'bar.b', 'bar.c.a', 'foo.a', 'foo.b'] - deepStrictEqual( - collectLinkedKeys(object as never, createContext('^8.0.0')).sort(), - expected - ) - }) it('default', () => { - const expected = ['bar.a', 'bar.b', 'bar.c.a', 'bar.d', 'foo.a', 'foo.b'] + const expected = ['bar.a', 'bar.c.a', 'bar.d', 'foo.a', 'foo.b'] deepStrictEqual( collectLinkedKeys(object as never, createContext()).sort(), expected ) }) it('>=v8', () => { - const expected = ['bar.a', 'bar.b', 'bar.c.a', 'bar.d', 'foo.a', 'foo.b'] + const expected = ['bar.a', 'bar.c.a', 'bar.d', 'foo.a', 'foo.b'] deepStrictEqual( collectLinkedKeys(object as never, createContext('>=8.0.0')).sort(), expected diff --git a/tests/lib/utils/message-compiler/parser-v8-data.ts b/tests/lib/utils/message-compiler/parser-v8-data.ts deleted file mode 100644 index 3a27ecbb..00000000 --- a/tests/lib/utils/message-compiler/parser-v8-data.ts +++ /dev/null @@ -1,475 +0,0 @@ -export const errorsFixtures: { - code: string - expected: Record -}[] = [ - { - code: '@: empty key', - expected: { - ast: { - type: 'Resource', - body: { - type: 'Message', - items: [ - { - type: 'Linked', - key: { - type: 'LinkedKey', - value: '', - loc: [2, 2] - }, - loc: [0, 2] - }, - { - type: 'Text', - value: ' empty key', - loc: [2, 12] - } - ], - loc: [0, 12] - }, - loc: [0, 12] - }, - errors: [ - { - message: 'Expected linked key value', - location: [2, 2] - } - ] - } - }, - { - code: '@. empty mod', - expected: { - ast: { - type: 'Resource', - body: { - type: 'Message', - items: [ - { - type: 'Linked', - key: { - type: 'LinkedKey', - value: '', - loc: [2, 2] - }, - loc: [0, 2], - modifier: { - type: 'LinkedModifier', - value: '', - loc: [1, 2] - } - }, - { - type: 'Text', - value: ' empty mod', - loc: [2, 12] - } - ], - loc: [0, 12] - }, - loc: [0, 12] - }, - errors: [ - { - message: 'Expected linked modifier value', - location: [1, 1] - }, - { - message: 'Expected linked key value', - location: [2, 2] - } - ] - } - }, - { - code: '@.: empty key and mod', - expected: { - ast: { - type: 'Resource', - body: { - type: 'Message', - items: [ - { - type: 'Linked', - key: { - type: 'LinkedKey', - value: '', - loc: [3, 3] - }, - loc: [0, 3], - modifier: { - type: 'LinkedModifier', - value: '', - loc: [1, 2] - } - }, - { - type: 'Text', - value: ' empty key and mod', - loc: [3, 21] - } - ], - loc: [0, 21] - }, - loc: [0, 21] - }, - errors: [ - { - message: 'Expected linked modifier value', - location: [1, 1] - }, - { - message: 'Expected linked key value', - location: [3, 3] - } - ] - } - }, - { - code: '@.mod: empty key', - expected: { - ast: { - type: 'Resource', - body: { - type: 'Message', - items: [ - { - type: 'Linked', - key: { - type: 'LinkedKey', - value: '', - loc: [6, 6] - }, - loc: [0, 6], - modifier: { - type: 'LinkedModifier', - value: 'mod', - loc: [1, 5] - } - }, - { - type: 'Text', - value: ' empty key', - loc: [6, 16] - } - ], - loc: [0, 16] - }, - loc: [0, 16] - }, - errors: [ - { - message: 'Expected linked key value', - location: [6, 6] - } - ] - } - }, - { - code: '@.mod only mod', - expected: { - ast: { - type: 'Resource', - body: { - type: 'Message', - items: [ - { - type: 'Linked', - key: { - type: 'LinkedKey', - value: '', - loc: [5, 5] - }, - loc: [0, 5], - modifier: { - type: 'LinkedModifier', - value: 'mod', - loc: [1, 5] - } - }, - { - type: 'Text', - value: ' only mod', - loc: [5, 14] - } - ], - loc: [0, 14] - }, - loc: [0, 14] - }, - errors: [ - { - message: 'Expected linked key value', - location: [5, 5] - } - ] - } - }, - { - code: '@: empty key', - expected: { - ast: { - type: 'Resource', - body: { - type: 'Message', - items: [ - { - type: 'Linked', - key: { - type: 'LinkedKey', - value: '', - loc: [2, 2] - }, - loc: [0, 2] - }, - { - type: 'Text', - value: ' empty key', - loc: [2, 12] - } - ], - loc: [0, 12] - }, - loc: [0, 12] - }, - errors: [ - { - message: 'Expected linked key value', - location: [2, 2] - } - ] - } - }, - { - code: '@:() key with paren', - expected: { - ast: { - type: 'Resource', - body: { - type: 'Message', - items: [ - { - type: 'Linked', - key: { - type: 'LinkedKey', - value: '', - loc: [3, 3] - }, - loc: [0, 4] - }, - { - type: 'Text', - value: ' key with paren', - loc: [4, 19] - } - ], - loc: [0, 19] - }, - loc: [0, 19] - }, - errors: [ - { - message: 'Expected linked key value', - location: [3, 3] - } - ] - } - }, - { - code: '@:(foo) key with paren v8', - expected: { - ast: { - type: 'Resource', - body: { - type: 'Message', - items: [ - { - type: 'Linked', - key: { - type: 'LinkedKey', - value: 'foo', - loc: [3, 6] - }, - loc: [0, 7] - }, - { - type: 'Text', - value: ' key with paren v8', - loc: [7, 25] - } - ], - loc: [0, 25] - }, - loc: [0, 25] - }, - errors: [] - } - }, - { - code: 'unclose paren for v8 @:(foo.bar', - expected: { - ast: { - type: 'Resource', - body: { - type: 'Message', - items: [ - { - type: 'Text', - value: 'unclose paren for v8 ', - loc: [0, 21] - }, - { - type: 'Linked', - key: { - type: 'LinkedKey', - value: 'foo.bar', - loc: [24, 31] - }, - loc: [21, 31] - } - ], - loc: [0, 31] - }, - loc: [0, 31] - }, - errors: [ - { - message: 'Unterminated closing paren', - location: [31, 31] - } - ] - } - }, - { - code: 'unclose brace { foo', - expected: { - ast: { - type: 'Resource', - body: { - type: 'Message', - items: [ - { - type: 'Text', - value: 'unclose brace ', - loc: [0, 14] - }, - { - type: 'Named', - key: 'foo', - loc: [14, 20] - } - ], - loc: [0, 19] - }, - loc: [0, 19] - }, - errors: [ - { - message: 'Unterminated closing brace', - location: [15, 15] - }, - { - message: 'Unexpected space before or after the placeholder key', - location: [15, 15] - } - ] - } - }, - { - code: 'error placeholder {foo.bar}', - expected: { - ast: { - type: 'Resource', - body: { - type: 'Message', - items: [ - { - type: 'Text', - value: 'error placeholder ', - loc: [0, 18] - }, - { - type: 'Named', - key: 'foo.bar', - loc: [18, 27] - } - ], - loc: [0, 27] - }, - loc: [0, 27] - }, - errors: [ - { - message: 'Unexpected placeholder key', - location: [19, 19] - } - ] - } - }, - { - code: 'error placeholder { foo.bar }', - expected: { - ast: { - type: 'Resource', - body: { - type: 'Message', - items: [ - { - type: 'Text', - value: 'error placeholder ', - loc: [0, 18] - }, - { - type: 'Named', - key: 'foo.bar', - loc: [18, 29] - } - ], - loc: [0, 29] - }, - loc: [0, 29] - }, - errors: [ - { - message: 'Unexpected space before or after the placeholder key', - location: [19, 19] - }, - { - message: 'Unexpected placeholder key', - location: [19, 19] - } - ] - } - }, - { - code: 'spaced placeholder { foo }', - expected: { - ast: { - type: 'Resource', - body: { - type: 'Message', - items: [ - { - type: 'Text', - value: 'spaced placeholder ', - loc: [0, 19] - }, - { - type: 'Named', - key: 'foo', - loc: [19, 26] - } - ], - loc: [0, 26] - }, - loc: [0, 26] - }, - errors: [ - { - message: 'Unexpected space before or after the placeholder key', - location: [20, 20] - } - ] - } - } -] diff --git a/tests/lib/utils/message-compiler/parser-v8.ts b/tests/lib/utils/message-compiler/parser-v8.ts deleted file mode 100644 index 5505029e..00000000 --- a/tests/lib/utils/message-compiler/parser-v8.ts +++ /dev/null @@ -1,141 +0,0 @@ -/** - * @author Yosuke Ota - */ -import { deepStrictEqual } from 'assert' -import { parse } from '../../../../lib/utils/message-compiler/parser-v8' -import { parse as parseForV9 } from '../../../../lib/utils/message-compiler/parser-v9' -import { errorsFixtures } from './parser-v8-data' - -describe('parser-v8', () => { - describe('compare v9', () => { - const list = [ - 'message', - 'Hello World!', - 'Hello {target}!', - 'Hello %{target}!', - { - code: 'Hello { target }!', - errors: ['Unexpected space before or after the placeholder key'] - }, - 'Hello @:link', - 'Hello @.lower:link', - 'car | cars', - 'no apples | one apple | {count} apples', - 'no apples |\n one apple |\n {count} apples', - 'empty placeholder { }', - 'empty placeholder {}', - 'number placeholder {42}', - { - code: 'number placeholder { 42 }', - errors: ['Unexpected space before or after the placeholder key'] - }, - { - code: 'number placeholder { -42 }', - errors: [ - 'Unexpected space before or after the placeholder key', - 'Unexpected minus placeholder index' - ] - } - ] - for (const e of list) { - const code = typeof e === 'string' ? e : e.code - const errors = typeof e === 'string' ? null : e.errors - describe(JSON.stringify(code), () => { - it('should be equals', () => { - const v8 = normalize(parse(code)) - const v9 = normalize(parseForV9(code)) - if (errors) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - v8.errors = v8.errors.map((e: any) => e.message) - v9.errors = errors - } - deepStrictEqual(v8, v9) - }) - }) - } - }) - describe('errors', () => { - const list = errorsFixtures - for (const { code, expected } of list) { - describe(JSON.stringify(code), () => { - it('should be equals', () => { - const parsed = simply(parse(code)) - try { - deepStrictEqual(parsed, expected) - } catch (e) { - // require('fs').writeFileSync( - // __dirname + '/actual.json', - // JSON.stringify(parsed, null, 2) - // ) - throw e - } - }) - }) - } - }) -}) - -function normalize(obj: unknown) { - return JSON.parse( - JSON.stringify(obj, (key, value) => { - if (key === 'source' || key === 'domain') { - return undefined - } - if (key === 'end' && typeof value === 'number') { - return undefined - } - if (value instanceof Error) { - return { - // @ts-expect-error -- ignore - message: value.message, - ...value, - code: undefined - } - } - return value - }) - ) -} -function simply(obj: unknown) { - return JSON.parse( - JSON.stringify(obj, (key, value) => { - if (key === 'domain') { - return undefined - } - if (key === 'end' && typeof value === 'number') { - return undefined - } - if (key === 'start' && typeof value === 'number') { - return undefined - } - if (key === 'loc' || key === 'location') { - return [value.start.offset, value.end.offset] - } - if (key === 'type') { - return NodeTypes[value] - } - if (value instanceof Error) { - return { - // @ts-expect-error -- ignore - message: value.message, - ...value, - code: undefined - } - } - return value - }) - ) -} - -enum NodeTypes { - Resource = 0, - Plural = 1, - Message = 2, - Text = 3, - Named = 4, - List = 5, - Linked = 6, - LinkedKey = 7, - LinkedModifier = 8, - Literal = 9 -} diff --git a/tests/lib/utils/message-compiler/utils.ts b/tests/lib/utils/message-compiler/utils.ts index 071a4499..a398b932 100644 --- a/tests/lib/utils/message-compiler/utils.ts +++ b/tests/lib/utils/message-compiler/utils.ts @@ -18,40 +18,34 @@ describe('message-compiler utils', () => { return data } it('should be equal to the expected value', () => { - deepStrictEqual(get('^8.0.0'), { - v8: true, - v9: false, - v10: false, - isNotSet: false - }) deepStrictEqual(get('^9.0.0'), { - v8: false, v9: true, v10: false, + v11: false, isNotSet: false }) - deepStrictEqual(get('^7.0.0'), { - v8: true, + deepStrictEqual(get('^10.0.0'), { v9: false, - v10: false, + v10: true, + v11: false, isNotSet: false }) - deepStrictEqual(get('^10.0.0'), { - v8: false, + deepStrictEqual(get('^11.0.0'), { v9: false, - v10: true, + v10: false, + v11: true, isNotSet: false }) deepStrictEqual(get('>=5.0.0'), { - v8: true, v9: true, v10: true, + v11: true, isNotSet: false }) deepStrictEqual(get('^9.0.0-beta.8'), { - v8: false, v9: true, v10: false, + v11: false, isNotSet: false }) })