-
Notifications
You must be signed in to change notification settings - Fork 92
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9d9b9ea
commit b4fc8a7
Showing
9 changed files
with
654 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,363 @@ | ||
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) { | ||
// 处理 vue html 里的变量 | ||
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) => { | ||
// { | ||
// source: ' title: `{{${code}.${autoSheetOptions(event)[0].value}}} 异常警告`,', | ||
// rewriteCode: ' title: j18n.expand(j18n.load('src__components__automationDrawer__model__action___199', code, autoSheetOptions(event)[0].value)),', | ||
// fileType: 'js', | ||
// type: 'CHAR', | ||
// isVueTag: false, | ||
// output: '`{{{0}.{1}}} 异常警告`', | ||
// filePath: '/Users/inkl/Desktop/work/console/src/components/automationDrawer/model/action.js', | ||
// line: 199, | ||
// code: 'j18n.expand(j18n.load('src__components__automationDrawer__model__action___199', code, autoSheetOptions(event)[0].value))' | ||
// variable?: ['code', 'autoSheetOptions(event)[0].value'], | ||
// src__components__automationDrawer__model__action___199: '`{{{0}.{1}}} 异常警告`', | ||
// key: 'src__components__automationDrawer__model__action___199' | ||
// } | ||
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 expressions | ||
filling = `, ${variable.join(', ')}` | ||
} | ||
if (fileType === 'tsx' || fileType === 'ts') { | ||
if (type === Type.CHAR && !isAttribute) { | ||
code = `j18n.load('${item.key}'${filling})` | ||
} else { | ||
// isJSXNodeExpression 表示当前内容是否为 {`xxx`} 模式 | ||
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 regexp = /["'`]?[\u4e00-\u9fa5]+(.+)?[\u4e00-\u9fa5]+(\?|!)?["'`)]?/; | ||
const double = /".*?"/g | ||
const single = /'.*?'/g | ||
// eslint-disable-next-line no-unused-vars | ||
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) => { | ||
// jsx / vue 中的语义处理 < 中的属性值(中文字符串),应该加上 ‘{}’ 符号 | ||
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) | ||
) { | ||
// vue template ExpressionStatement | ||
// 总共只有13处需要处理,手动替换 | ||
return charParser( | ||
lineContent, | ||
single, | ||
line, | ||
isAttribute, | ||
type, | ||
tagClose | ||
) | ||
} | ||
|
||
// 处理 <><> tag 中的中文 | ||
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 | ||
} | ||
} | ||
|
||
// 处理 jsx / vue 中的中文 | ||
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 [] | ||
} |
Oops, something went wrong.