ci: add automate script to transform demos to add "Tiny" prefix (#1832)

This commit is contained in:
gimmyhehe 2024-08-07 09:27:25 +08:00 committed by GitHub
parent 47a74045f5
commit b32a7007f1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 180 additions and 1 deletions

View File

@ -7,6 +7,8 @@
"scripts": {
"start": "esno ./src/index.ts",
"scan": "esno ./src/demos-scan.ts",
"// ---------- 给文档组件加上Tiny前缀 ----------": "",
"doc-prefix": "esno ./src/doc-prefix.ts",
"diff": "esno ./src/aui-diff.ts",
"add-dep": "esno ./src/add-dependencies.ts",
"auto-build-pub": "esno ./src/publish/index.ts"
@ -22,7 +24,12 @@
"sheetjs-style": "^0.15.8",
"commander": "^10.0.0",
"semver": "^7.3.8",
"chalk": "2.4.2"
"chalk": "2.4.2",
"@babel/parser": "^7.18.13",
"@babel/traverse": "^7.18.13",
"@babel/generator": "^7.18.13",
"@babel/template": "^7.18.13",
"@vue/compiler-sfc": "^3.4.21"
},
"keywords": [],
"author": "",

View File

@ -0,0 +1,172 @@
import fg from 'fast-glob'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { parse } from '@vue/compiler-sfc'
import { parse as babelParse } from '@babel/parser'
import traverse from '@babel/traverse'
import generate from '@babel/generator'
import * as prettier from 'prettier'
import { writeFile } from 'node:fs/promises'
import { safeReadFile } from './utils/files'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const docPath = path.join(__dirname, '../../../examples/sites/demos')
const generateTraverse = traverse.default
const addPrefix = async (code) => {
const ast = babelParse(code, {
sourceType: 'module',
plugins: ['typescript', 'jsx']
})
const renameVar = {}
generateTraverse(
ast,
{
ImportDeclaration(path) {
// 解析@opentiny/vue的引入
const depName = path.node?.source?.value
if (depName === '@opentiny/vue') {
const specifiers = path.node.specifiers
specifiers?.forEach((importSpecifier) => {
const { local, imported } = importSpecifier
if (local.name.startsWith('Tiny')) {
if (!imported.name.startsWith('Tiny')) {
imported.name = 'Tiny' + imported.name
}
} else {
renameVar[local.name] = 'Tiny' + local.name
local.name = 'Tiny' + local.name
imported.name = 'Tiny' + imported.name
}
})
}
},
Identifier(path) {
const name = path.node.name
// 不能用renameVar[name]代替,否则代码中包含原型链上方法会报错
/* eslint-disable no-prototype-builtins */
if (renameVar.hasOwnProperty(name)) {
path.node.name = renameVar[name]
}
const parent = path.parentPath?.node
if (parent.type === 'ObjectProperty') {
const { key, value } = parent
if (key.name === value.name) {
parent.shorthand = true
}
}
},
ObjectProperty(path) {
const node = path.node
const { key, value } = node
if (key.name === value.name) {
node.shorthand = true
}
}
},
{}
)
let newCode
newCode = generate.default(ast, { retainLines: true }).code || ''
const formatCode = await prettier.format(newCode, {
semi: false,
parser: 'typescript',
singleQuote: true,
trailingComma: 'none',
'printWidth': 120,
'endOfLine': 'auto',
'bracketSpacing': true,
'jsxBracketSameLine': true
})
return formatCode
}
const getAttrsString = (attrs) => {
let attrStr = ''
if (!attrs) {
return attrStr
}
Object.keys(attrs).forEach((key) => {
const val = attrs[key]
if (val === true) {
attrStr += ` ${key}`
} else {
attrStr += ` ${key}="${val}"`
}
})
return attrStr
}
const generateTagContent = (tagDescriptor) => {
if (!tagDescriptor) {
return ''
}
const { attrs, content, type } = tagDescriptor
return `<${type}${getAttrsString(attrs)}>${content}</${type}>`
}
// 拼接完整的SFC文件
const generateSFC = (descriptor) => {
const { script, scriptSetup, styles = [], template } = descriptor
const contentArr = [
generateTagContent(template),
generateTagContent(script),
generateTagContent(scriptSetup),
...styles.map(generateTagContent)
]
return contentArr.filter((i) => i).join('\n\n') + '\n'
}
// 解析SFC文件获取其中的script标签内容后转换
const transformSFC = async (code) => {
const { descriptor } = parse(code)
const { script, scriptSetup } = descriptor
if (script) {
script.content = '\n' + (await addPrefix(script.content)) || script.content
}
if (scriptSetup) {
scriptSetup.content = '\n' + (await addPrefix(scriptSetup.content)) || scriptSetup.content
}
return generateSFC(descriptor)
}
const transformDemos = async () => {
const demoFiles = await fg(['**/*.vue'], {
dot: true,
cwd: docPath,
ignore: ['**/node_modules', '__test__']
})
let handelResolve
const len = demoFiles.length
let finishNum = 0
const promise = new Promise((resolve) => {
handelResolve = resolve
})
demoFiles.forEach(async (filename, index) => {
if (index >= len) return
const filePath = path.join(docPath, filename)
const fileContent = await safeReadFile(filePath)
const newContent = await transformSFC(fileContent)
await writeFile(filePath, newContent)
++finishNum
if (finishNum === len) {
handelResolve(finishNum)
}
})
return promise
}
transformDemos().then((finishNum) => {
/* eslint-disable no-console */
console.log(`${finishNum}个demos组件添加Tiny前缀任务完成`)
})