From 330955ed91b58a87ce8c0a36cc9125c3b1015541 Mon Sep 17 00:00:00 2001 From: PBK-B Date: Sat, 4 Jan 2025 19:18:12 +0800 Subject: [PATCH] feat: add vue-template-lexer #617 --- README.md | 13 +++---- src/index.js | 1 + src/lexers/html-lexer.js | 22 ------------ src/lexers/vue-template-lexer.js | 49 ++++++++++++++++++++++++++ src/parser.js | 4 ++- test/lexers/html-lexer.test.js | 22 ------------ test/lexers/vue-template-lexer.test.js | 31 ++++++++++++++++ 7 files changed, 89 insertions(+), 53 deletions(-) create mode 100644 src/lexers/vue-template-lexer.js create mode 100644 test/lexers/vue-template-lexer.test.js diff --git a/README.md b/README.md index 35a0d424..bcddd883 100644 --- a/README.md +++ b/README.md @@ -376,19 +376,16 @@ Typescript is supported via Javascript and Jsx lexers. If you are using Javascri ```js { - // Vue needs to parse its syntax through both JavascriptLexer and HTMLLexer + // Vue needs to parse its syntax through both VueTemplateLexer + // This lexer supports HTMLLexer and JavascriptLexer options vue: [ { - lexer: 'JavascriptLexer', + lexer: 'VueTemplateLexer', + vueBindAttr: true, // enable vue template syntax bind attribute functions: ['t', '$t'], // Array of functions to match namespaceFunctions: ['useTranslation', 'withTranslation'], }, - { - lexer: 'HTMLLexer', - functions: ['t', '$t'], // Array of functions to match - vueBindAttr: true, // enable vue template syntax bind attribute - }, - ], + ] } ``` diff --git a/src/index.js b/src/index.js index 1e3c5bcd..ca2b23e7 100644 --- a/src/index.js +++ b/src/index.js @@ -9,3 +9,4 @@ export { default as HandlebarsLexer } from './lexers/handlebars-lexer.js' export { default as HTMLLexer } from './lexers/html-lexer.js' export { default as JavascriptLexer } from './lexers/javascript-lexer.js' export { default as JsxLexer } from './lexers/jsx-lexer.js' +export { default as VueTemplateLexer } from './lexers/vue-template-lexer.js' diff --git a/src/lexers/html-lexer.js b/src/lexers/html-lexer.js index 9308b4e3..2401f3b4 100644 --- a/src/lexers/html-lexer.js +++ b/src/lexers/html-lexer.js @@ -1,5 +1,4 @@ import BaseLexer from './base-lexer.js' -import JavascriptLexer from './javascript-lexer.js' import * as cheerio from 'cheerio' export default class HTMLLexer extends BaseLexer { @@ -8,7 +7,6 @@ export default class HTMLLexer extends BaseLexer { this.attr = options.attr || 'data-i18n' this.optionAttr = options.optionAttr || 'data-i18n-options' - this.vueBindAttr = options.vueBindAttr || false } extract(content) { @@ -41,26 +39,6 @@ export default class HTMLLexer extends BaseLexer { } }) - // support vue bind attribute - if (this.vueBindAttr) { - let keys = [] - const jsLexer = new JavascriptLexer({ functions: this.functions }) - // traverse all tags to filter bind attribute - $('*').each((index, node) => { - const attributes = node.attributes.filter( - (item) => item.name.startsWith(':') || item.name.startsWith('v-bind:') - ) - if (attributes.length > 0) { - // there are calculation attributes. - attributes.forEach((content) => { - const items = jsLexer.extract(content.value) - keys.push(...items) - }) - } - }) - this.keys.push(...keys) - } - return this.keys } } diff --git a/src/lexers/vue-template-lexer.js b/src/lexers/vue-template-lexer.js new file mode 100644 index 00000000..b651a161 --- /dev/null +++ b/src/lexers/vue-template-lexer.js @@ -0,0 +1,49 @@ +/* + * @Author: Bin + * @Date: 2025-01-04 + * @FilePath: /i18next-parser/src/lexers/vue-template-lexer.js + */ +import HTMLLexer from './html-lexer.js' +import JavascriptLexer from './javascript-lexer.js' +import * as cheerio from 'cheerio' + +export default class VueTemplateLexer extends HTMLLexer { + constructor(options = {}) { + super(options) + this.vueBindAttr = options.vueBindAttr || true + this.jsLexer = new JavascriptLexer(options) + } + + extract(content, filename = '__default.vue') { + const $ = cheerio.load(content) + const jsLexer = this.jsLexer + let keys = [] + + // use HTMLLexer + const HTMLkeys = super.extract(content) + keys.push(...HTMLkeys) + + // use JavascriptLexer + const JSKeys = jsLexer.extract(content, filename) + keys.push(...JSKeys) + + // support vue bind attribute + if (this.vueBindAttr) { + // traverse all tags to filter bind attribute + $('*').each((_, node) => { + const attributes = node.attributes.filter( + (item) => item.name.startsWith(':') || item.name.startsWith('v-bind:') + ) + if (attributes.length > 0) { + // there are calculation attributes. + attributes.forEach((content) => { + const items = jsLexer.extract(content.value, filename) + keys.push(...items) + }) + } + }) + } + + return keys + } +} diff --git a/src/parser.js b/src/parser.js index e8d46cb4..9afeca78 100644 --- a/src/parser.js +++ b/src/parser.js @@ -4,6 +4,7 @@ import HandlebarsLexer from './lexers/handlebars-lexer.js' import HTMLLexer from './lexers/html-lexer.js' import JavascriptLexer from './lexers/javascript-lexer.js' import JsxLexer from './lexers/jsx-lexer.js' +import VueTemplateLexer from './lexers/vue-template-lexer.js' const lexers = { hbs: ['HandlebarsLexer'], @@ -18,7 +19,7 @@ const lexers = { jsx: ['JsxLexer'], tsx: ['JsxLexer'], - vue: ['JavascriptLexer'], + vue: ['VueTemplateLexer'], default: ['JavascriptLexer'], } @@ -28,6 +29,7 @@ const lexersMap = { HTMLLexer, JavascriptLexer, JsxLexer, + VueTemplateLexer, } export default class Parser extends EventEmitter { diff --git a/test/lexers/html-lexer.test.js b/test/lexers/html-lexer.test.js index ce33a9cd..2e688680 100644 --- a/test/lexers/html-lexer.test.js +++ b/test/lexers/html-lexer.test.js @@ -98,26 +98,4 @@ describe('HTMLLexer', () => { ]) done() }) - - it('supports vue bind attributes', (done) => { - const Lexer = new HTMLLexer({ - functions: ['t', '$t'], - vueBindAttr: true, - }) - const content = ` - - ` - assert.deepEqual(Lexer.extract(content), [ - { key: 'b_a_l', defaultValue: 'button aria label' }, - { key: 'button v-bind aria label' }, - ]) - done() - }) }) diff --git a/test/lexers/vue-template-lexer.test.js b/test/lexers/vue-template-lexer.test.js new file mode 100644 index 00000000..8db76a60 --- /dev/null +++ b/test/lexers/vue-template-lexer.test.js @@ -0,0 +1,31 @@ +/* + * @Author: Bin + * @Date: 2025-01-04 + * @FilePath: /i18next-parser/test/lexers/vue-template-lexer.test.js + */ +import { assert } from 'chai' +import VueTemplateLexer from '../../src/lexers/vue-template-lexer.js' + +describe('VueTemplateLexer', () => { + it('supports vue bind attributes', (done) => { + const Lexer = new VueTemplateLexer({ + functions: ['t', '$t'], + vueBindAttr: true, + }) + const content = ` + + ` + assert.deepEqual(Lexer.extract(content), [ + { key: 'b_a_l', defaultValue: 'button aria label' }, + { key: 'button v-bind aria label' }, + ]) + done() + }) +})