const Type = {
CHAR: 'CHAR',
HTML: 'HTML',
JSX: 'JSX'
}
const renderKey = (filePath, rootPath) => {
const key = filePath
.replace(rootPath, '')
.slice(1)
.replace(/\//g, '__')
.replace(/\..+$/, '')
return key
}
const checkInExpression = (type, content, start) => {
const isVue = false
const beforeContent = content.slice(0, start)
const isAttributeExpression = isVue
? /:[a-zA-Z-]*=".*/.test(beforeContent)
: /[a-zA-Z]={/.test(beforeContent)
return isAttributeExpression
}
const checkJSXExpression = (type, content, start) => {
const beforeContent = content.slice(0, start)
return /.*{$/.test(beforeContent)
}
const charParser = (content, regexp, line, isAttribute, type, cb) => {
const match = content.match(regexp)
const variable = []
const chinese = /[\u4e00-\u9fa5]/
const attr = isAttribute > 0
let i = 0
if (!match) {
return null
}
const r = match
.filter(item => chinese.test(item))
.map(item => {
const start = content.indexOf(item)
const end = start + item.length
const isAttributeExpression = attr
? checkInExpression(type, content, start)
: false
const isJSXNodeExpression = !attr
? checkJSXExpression(type, content, start)
: false
const output = item.replace(/\$\{.+?\}/g, match => {
variable.push(match.slice(2, -1))
return `{${i++}}`
})
return {
source: content,
line,
output,
variable,
start,
end,
type: Type.CHAR,
isAttribute: attr,
isAttributeExpression,
isJSXNodeExpression
}
})
cb()
return r
}
const tagParser = (content, regexp, isVue, type, line, isAttribute, cb) => {
const variable = []
let i = 0
const match = content.match(regexp)
let output = match[0]
const start = match.index
const end = start + output.length
if (isVue) {
output = output.replace(/\{\{.+?\}\}/g, match => {
variable.push(match.slice(2, -2))
return `{${i++}}`
})
}
cb()
return {
source: content,
line,
output,
variable,
start,
end,
isVueTag: isVue,
type: type,
isAttribute: isAttribute > 0
}
}
const renderVueExpressionCode = (source, code, start, end) => {
const tags = source
.split('')
.map((item, index) => {
if (item === ' ') {
return index
}
return null
})
.filter(item => typeof item === 'number')
.filter(item => item < start)
const tagIndex = tags[tags.length - 1]
const rewriteCode =
source.slice(0, tagIndex) +
' :' +
source.slice(tagIndex + 1, start) +
code +
source.slice(end)
return rewriteCode
}
exports.renderOutputCode = (normalized, file) => {
if (normalized.length) {
const code = file.split('\n')
for (let index = 0; index < normalized.length; index++) {
const item = normalized[index]
if (normalized[index + 1] && item.line === normalized[index + 1].line) {
const [current, next] = [item, normalized[index + 1]]
const { source, start, end } = current
const rCode =
source.slice(0, start) +
current.code +
source.slice(end, next.start) +
next.code +
next.source.slice(next.end)
code[item.line] = rCode
index++
} else {
code[item.line] = item.rewriteCode
}
}
return code.join('\n')
}
return null
}
exports.trackNormalized = (filetrack, rootPath) => {
const json = filetrack.map((track, index) => {
const prefixKey = renderKey(track.filePath, rootPath)
const output = track.output.replace(/^["'`]/, '').replace(/["'`]$/, '')
if (filetrack[index + 1] && track.line === filetrack[index + 1].line) {
const key = `${prefixKey}___${track.line}____${index}`
return {
[key]: output,
key: key,
output
}
}
const key = `${prefixKey}___${track.line}`
return {
[key]: output,
key: key,
output
}
})
return json.map((item, index) => {
let code,
filling = '',
rewriteCode = ''
const itemTrack = filetrack[index]
const {
variable,
fileType,
type,
start,
end,
source,
isVueTag,
isAttribute,
isAttributeExpression,
isJSXNodeExpression
} = itemTrack
if (Array.isArray(variable) && variable.length) {
filling = `, ${variable.join(', ')}`
}
if (fileType === 'tsx' || fileType === 'ts') {
if (type === Type.CHAR && !isAttribute) {
code = `j18n.load('${item.key}'${filling})`
} else {
if ((isAttribute && isAttributeExpression) || isJSXNodeExpression) {
code = `j18n.load('${item.key}'${filling})`
} else {
code = `{ j18n.load('${item.key}'${filling}) }`
}
}
}
rewriteCode =
rewriteCode || source.slice(0, start) + code + source.slice(end)
return {
...itemTrack,
...item,
code: code,
rewriteCode: rewriteCode
}
})
}
exports.parserCore = (content, path) => {
const double = /".*?"/g
const single = /'.*?'/g
const backtick = /`.*?`/g
const charRegexp = /["'`].*?["'`]/g
const htmlChineseRegexp = /(?<=>).{0,}[\u4e00-\u9fa5].{0,}(?=<)/
const allChineseRegexp = /[\u4e00-\u9fa5]{1,}.*?[\u4e00-\u9fa5]{1,}/
const type = path.match(/\.(tsx|ts|js)$/)[0].slice(1)
const isVue = false
let isAttribute = 0,
processed = false
if (
charRegexp.test(content) ||
htmlChineseRegexp.test(content) ||
allChineseRegexp.test(content)
) {
return content
.split('\n')
.map((lineContent, line) => {
if (/<[a-zA-Z]+/.test(lineContent)) {
isAttribute++
}
const tagClose = () => {
const close =
/<.+>/.test(lineContent) ||
/^>/.test(lineContent.trim()) ||
/\/>/.test(lineContent)
if (close) {
isAttribute = isAttribute > 0 ? isAttribute - 1 : 0
}
}
if (
isVue &&
isAttribute &&
double.test(lineContent) &&
single.test(lineContent)
) {
return charParser(
lineContent,
single,
line,
isAttribute,
type,
tagClose
)
}
if (htmlChineseRegexp.test(lineContent)) {
return tagParser(
lineContent,
htmlChineseRegexp,
isVue,
Type.HTML,
line,
isAttribute,
tagClose
)
}
if (backtick.test(lineContent)) {
return charParser(
lineContent,
backtick,
line,
isAttribute,
type,
tagClose
)
}
if (charRegexp.test(lineContent)) {
const parser = charParser(
lineContent,
charRegexp,
line,
isAttribute,
type,
tagClose
)
processed = true
if (parser.length) {
return parser
}
}
if (
allChineseRegexp.test(lineContent) &&
!lineContent.includes('//') &&
!lineContent.includes('* ')
) {
return tagParser(
lineContent,
allChineseRegexp,
isVue,
Type.JSX,
line,
isAttribute,
tagClose
)
}
!processed && tagClose()
processed = false
return null
})
.filter(item => {
if (Array.isArray(item)) {
return item.length > 0
}
return !!item
})
.flat()
.map(item => ({ ...item, filePath: path, fileType: type }))
}
return []
}