webpack-router-generator
Version:
A routing list is generated automatically according to the information defined in the file, which is used in webpack.
178 lines (165 loc) • 5.2 kB
JavaScript
const path = require("path")
const chokidar = require("chokidar")
const { writeFileSync } = require("fs")
const { getNormaPath, getRelativePath, getTagName, log, isExists } = require("./utils");
const {
getObjectPropty,
getArrowFunctionExpression,
getCallExpression,
getIdentifierExpression,
getStringExpression,
getJsxTag,
getImportDeclaration,
getAstCodeStr
} = require("./generator");
const { getRouteInfoNode, getRouterString, isObjectExpression, sortNode } = require("./parse");
const options = {
keyWord: "route",
fileDir: path.join(process.cwd(), "./src/pages"),
comKey: "component",
outputFile: path.join(process.cwd(), "./src/router.js"),
exts: [".js", ".jsx", ".tsx"],
insertBeforeStr: '',
insertAfterStr: '',
isLazy: true
}
module.exports = class WebpackRouterGenerator {
constructor(o) {
const opt = Object.assign(options, o)
this.keyWord = opt.keyWord;
this.fileDir = opt.fileDir;
this.outputFile = opt.outputFile
this.comKey = opt.comKey
this.exts = opt.exts
this.watchFile = [];
this.routerVar = "routes";
this.insertBeforeStr = opt.insertBeforeStr;
this.insertAfterStr = opt.insertAfterStr;
this.isLazy = opt.isLazy
this.nodes = [];
this.nodeMap = new Map()
this.timer = null
this.watcher = null
}
getLazyCompnentNode(filePath) {
return getObjectPropty(
this.comKey,
getArrowFunctionExpression(
[],
getCallExpression(
getIdentifierExpression("import"),
[getStringExpression(filePath)]
)
)
)
}
getCompnentNode(tagName) {
return getObjectPropty(this.comKey, getJsxTag(tagName))
}
setCurrentPageNode(filePath) {
const routerNode = getRouteInfoNode(filePath, this.keyWord)
if (!routerNode) {
this.nodeMap.set(getNormaPath(filePath), null)
return null
}
const relative = getNormaPath(getRelativePath(this.outputFile, filePath), false)
const tagName = getTagName(relative)
const componentNode = this.isLazy ? this.getLazyCompnentNode(relative) : this.getCompnentNode(tagName)
const isObjectNode = isObjectExpression(routerNode)
if (!isObjectNode) {
log("warn", "提取的路由信息必须为Object类型,例如:{ key:val }")
return null
}
routerNode.properties.push(componentNode)
const mapData = {
node: routerNode,
filePath,
relative
}
this.nodeMap.set(getNormaPath(filePath), mapData)
return routerNode
}
getImportStr() {
if (this.isLazy) {
return ''
}
const nodes = []
this.nodeMap.forEach((value) => {
if (value) {
nodes.push(value)
}
})
const importNode = nodes.map(item => {
const tagName = getTagName(item.relative)
return getImportDeclaration(tagName, item.relative)
})
return getAstCodeStr(importNode)
}
getTmpStr(routerStr, importStr) {
return `// 本文件为脚本自动生成,请勿修改
${importStr}
${this.insertBeforeStr || ''}
${routerStr}
${this.insertAfterStr || ''}
export default ${this.routerVar}`
}
setNodes() {
const { nodeMap } = this
const nodes = []
nodeMap.forEach((value) => {
if (value) {
nodes.push(value.node)
}
})
nodes.sort((a, b) => sortNode(a, "order") - sortNode(b, "order"))
this.nodes = nodes
}
writeFile() {
const { outputFile, routerVar, nodes } = this
if (nodes.length === 0) {
log("warn", "未提取出文件暴露的路由信息!")
return
}
const genStr = getRouterString(nodes, routerVar) || `const ${routerVar} = []`
const genImportStr = this.getImportStr()
const tmpStr = this.getTmpStr(genStr, genImportStr)
try {
writeFileSync(outputFile, tmpStr, "utf8");
} catch (error) {
log("warn", "写入路由信息失败:" + error)
}
}
watch() {
const hasDir = isExists(this.fileDir)
if (!hasDir) {
return log("warn", "未发现目标监听文件")
}
const watchFileType = path.join(
this.fileDir,
`**/*.{${this.exts.map((i) => i.replace(".", "")).join(",")}}`
);
const watchEvent = ["add", "unlink", "change"];
this.watcher = chokidar.watch([watchFileType], {}).on("all", (eventName, filePath) => {
if (!watchEvent.includes(eventName)) return
this.setCurrentPageNode(filePath)
clearTimeout(this.timer)
log("log", `${eventName} ${filePath} 1s后更新路由文件!`)
this.timer = setTimeout(() => {
this.setNodes()
this.writeFile()
}, 1000);
})
}
apply(compiler) {
compiler.hooks.environment.tap("WebpackRouterGenerator", () => {
this.watch()
});
compiler.hooks.done.tap("WebpackRouterGenerator", () => {
const mode = (process.env.NODE_ENV || process.env.BABEL_ENV)
if (this.watcher && this.watcher.close && mode === "production") {
console.log("关闭监听");
this.watcher.close()
}
})
}
}