Skip to content

Commit

Permalink
feat: add vue-template-lexer #617
Browse files Browse the repository at this point in the history
  • Loading branch information
PBK-B committed Jan 4, 2025
1 parent b616178 commit 330955e
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 53 deletions.
13 changes: 5 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
},
],
]
}
```

Expand Down
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
22 changes: 0 additions & 22 deletions src/lexers/html-lexer.js
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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) {
Expand Down Expand Up @@ -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
}
}
49 changes: 49 additions & 0 deletions src/lexers/vue-template-lexer.js
Original file line number Diff line number Diff line change
@@ -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
}
}
4 changes: 3 additions & 1 deletion src/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'],
Expand All @@ -18,7 +19,7 @@ const lexers = {
jsx: ['JsxLexer'],
tsx: ['JsxLexer'],

vue: ['JavascriptLexer'],
vue: ['VueTemplateLexer'],

default: ['JavascriptLexer'],
}
Expand All @@ -28,6 +29,7 @@ const lexersMap = {
HTMLLexer,
JavascriptLexer,
JsxLexer,
VueTemplateLexer,
}

export default class Parser extends EventEmitter {
Expand Down
22 changes: 0 additions & 22 deletions test/lexers/html-lexer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,26 +98,4 @@ describe('HTMLLexer', () => {
])
done()
})

it('supports vue bind attributes', (done) => {
const Lexer = new HTMLLexer({
functions: ['t', '$t'],
vueBindAttr: true,
})
const content = `
<template>
<button :aria-label="t('b_a_l', 'button aria label')">
button label
</button>
<button v-bind:aria-label="$t('button v-bind aria label')">
button label form v-bind
</button>
</template>
`
assert.deepEqual(Lexer.extract(content), [
{ key: 'b_a_l', defaultValue: 'button aria label' },
{ key: 'button v-bind aria label' },
])
done()
})
})
31 changes: 31 additions & 0 deletions test/lexers/vue-template-lexer.test.js
Original file line number Diff line number Diff line change
@@ -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 = `
<template>
<button :aria-label="t('b_a_l', 'button aria label')">
button label
</button>
<button v-bind:aria-label="$t('button v-bind aria label')">
button label form v-bind
</button>
</template>
`
assert.deepEqual(Lexer.extract(content), [
{ key: 'b_a_l', defaultValue: 'button aria label' },
{ key: 'button v-bind aria label' },
])
done()
})
})

0 comments on commit 330955e

Please sign in to comment.