333 lines
15 KiB
JavaScript
333 lines
15 KiB
JavaScript
const fs = require('fs');
|
||
const { relative, join, resolve, parse } = require('path');
|
||
const t = require('@babel/types');
|
||
const assert = require('assert');
|
||
const AppPaths = require('../web/paths');
|
||
const { parseSync, transformFromAstSync } = require('@babel/core');
|
||
const NodeWatch = require('node-watch');
|
||
const cloneDeep = require('lodash/cloneDeep');
|
||
const { unset } = require('lodash');
|
||
|
||
const pageFiles = {};
|
||
const routerFiles = {};
|
||
|
||
/**
|
||
* 对nss所标识出的namespace下的(pages文件下的)文件,一旦有变更就重编译filename
|
||
* @param {*} filename
|
||
* @param {*} nss
|
||
*/
|
||
function addFileWatcher(filename, nss) {
|
||
if (process.env.NODE_ENV === 'development') {
|
||
nss.forEach(
|
||
(ns) => {
|
||
if (routerFiles[ns]) {
|
||
routerFiles[ns].push(filename);
|
||
}
|
||
else {
|
||
routerFiles[ns] = [filename];
|
||
const pageSrcDir = join(AppPaths.appRootSrc, 'pages', ns);
|
||
const isExists = fs.existsSync(pageSrcDir);
|
||
if (isExists) {
|
||
NodeWatch(pageSrcDir, { recursive: true }, (evt, name) => {
|
||
const { dir } = parse(name);
|
||
const indexJsonFile = join(dir, 'index.json');
|
||
const indexTsxFile = join(dir, 'index.tsx');
|
||
const webTsxFile = join(dir, 'web.tsx');
|
||
const webPcTsxFile = join(dir, 'web.pc.tsx');
|
||
if (evt === 'update' && !pageFiles[ns].hasOwnProperty(dir)) {
|
||
// 处理新增文件事件,删除事件webpack会自己处理,不处理也没什么问题
|
||
if (fs.existsSync(indexTsxFile) || fs.existsSync(webTsxFile) || fs.existsSync(webPcTsxFile)) {
|
||
let oakDisablePulldownRefresh = false;
|
||
if (fs.existsSync(indexJsonFile)) {
|
||
const {
|
||
enablePullDownRefresh = true,
|
||
} = require(indexJsonFile);
|
||
oakDisablePulldownRefresh = !enablePullDownRefresh;
|
||
}
|
||
const relativePath = relative(pageSrcDir, dir);
|
||
const newPageItem = {
|
||
path: relativePath.replace(/\\/g, '/'),
|
||
oakDisablePulldownRefresh,
|
||
};
|
||
pageFiles[ns][dir] = newPageItem;
|
||
|
||
const now = new Date();
|
||
routerFiles[ns].forEach(
|
||
(file) => fs.utimes(file, now, now, () => undefined)
|
||
);
|
||
}
|
||
}
|
||
else if (evt === 'remove' && pageFiles.hasOwnProperty(dir)) {
|
||
if (!(fs.existsSync(indexTsxFile) || fs.existsSync(webTsxFile) || fs.existsSync(webPcTsxFile))) {
|
||
unset(pageFiles[ns], dir);
|
||
|
||
const now = new Date();
|
||
routerFiles[ns].forEach(
|
||
(file) => fs.utimes(file, now, now, () => undefined)
|
||
);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
}
|
||
}
|
||
)
|
||
}
|
||
}
|
||
|
||
function makeRouterItem(page, ns, namespacePath, first) {
|
||
const { path, oakDisablePulldownRefresh } = page;
|
||
return t.objectExpression(
|
||
[
|
||
t.objectProperty(
|
||
t.identifier('path'),
|
||
t.stringLiteral(path)
|
||
),
|
||
t.objectProperty(
|
||
t.identifier('namespace'),
|
||
t.stringLiteral(namespacePath),
|
||
),
|
||
t.objectProperty(
|
||
t.identifier('meta'),
|
||
t.objectExpression(
|
||
[
|
||
t.objectProperty(
|
||
t.identifier('oakDisablePulldownRefresh'),
|
||
t.booleanLiteral(oakDisablePulldownRefresh)
|
||
),
|
||
]
|
||
)
|
||
),
|
||
t.objectProperty(
|
||
t.identifier('Component'),
|
||
t.callExpression(
|
||
t.memberExpression(t.identifier('React'), t.identifier('lazy')),
|
||
[
|
||
t.arrowFunctionExpression(
|
||
[],
|
||
t.callExpression(t.import(), [
|
||
t.stringLiteral(join(AppPaths.appRootSrc, 'pages', ns, path, 'index').replace(/\\/g, '/'))
|
||
])
|
||
),
|
||
]
|
||
)
|
||
),
|
||
t.objectProperty(
|
||
t.identifier('isFirst'),
|
||
t.booleanLiteral(first === path)
|
||
)
|
||
]
|
||
);
|
||
}
|
||
|
||
function traversePages(nss) {
|
||
// pages从相应目录遍历获得
|
||
nss.forEach(
|
||
(ns) => {
|
||
if (!pageFiles.hasOwnProperty(ns)) {
|
||
pageFiles[ns] = {};
|
||
const pageSrcDir = join(AppPaths.appRootSrc, 'pages', ns);
|
||
|
||
const traverse = (dir, relativePath) => {
|
||
const files = fs.readdirSync(dir);
|
||
files.forEach((file) => {
|
||
const filepath = join(dir, file);
|
||
const stat = fs.statSync(filepath);
|
||
if (
|
||
stat.isFile() &&
|
||
['index.tsx', 'web.tsx', 'web.pc.tsx'].includes(
|
||
file
|
||
) &&
|
||
!pageFiles[ns].hasOwnProperty(dir)
|
||
) {
|
||
const indexJsonFile = join(dir, 'index.json');
|
||
let oakDisablePulldownRefresh = false;
|
||
if (fs.existsSync(indexJsonFile)) {
|
||
const {
|
||
enablePullDownRefresh = true,
|
||
} = require(indexJsonFile);
|
||
oakDisablePulldownRefresh =
|
||
!enablePullDownRefresh;
|
||
}
|
||
pageFiles[ns][dir] = {
|
||
path: relativePath.replace(/\\/g, '/'),
|
||
oakDisablePulldownRefresh,
|
||
};
|
||
} else if (stat.isDirectory()) {
|
||
const dir2 = join(dir, file);
|
||
const relativePath2 = join(relativePath, file);
|
||
traverse(dir2, relativePath2);
|
||
}
|
||
});
|
||
};
|
||
const isExists = fs.existsSync(pageSrcDir);
|
||
if (isExists) {
|
||
traverse(pageSrcDir, '');
|
||
}
|
||
|
||
}
|
||
}
|
||
);
|
||
}
|
||
|
||
module.exports = () => {
|
||
return {
|
||
visitor: {
|
||
Program(path, state) {
|
||
const { cwd, filename } = state;
|
||
const rel = relative(cwd, filename).replace(/\\/g, '/');
|
||
if (
|
||
/([\\/]*[a-zA-Z0-9_-])*[\\/]src[\\/]app([\\/]*[a-zA-Z0-9_-])*[\\/]router[\\/]index.ts$/.test(
|
||
rel
|
||
) &&
|
||
rel.endsWith(`/router/index.ts`)
|
||
) {
|
||
// console.log('recompile', filename);
|
||
const { body } = path.node;
|
||
|
||
// namespace从相应目录查询获取
|
||
const dir = parse(filename).dir;
|
||
const namespaceDir = join(dir, '..', 'namespaces');
|
||
const nss = fs.readdirSync(namespaceDir);
|
||
|
||
const namespaceConfig = {};
|
||
nss.forEach(
|
||
(ns) => {
|
||
const dir = join(namespaceDir, ns);
|
||
const stat = fs.statSync(dir);
|
||
if (stat.isDirectory()) {
|
||
namespaceConfig[ns] = {};
|
||
const indexJsonFile = join(dir, 'index.json');
|
||
if (fs.existsSync(indexJsonFile)) {
|
||
const json = require(indexJsonFile);
|
||
let { first, notFound, path } = json;
|
||
if (first) {
|
||
if (first.startsWith('/')) {
|
||
first = first.slice(1);
|
||
}
|
||
namespaceConfig[ns].first = first.replace(/\\/g, '/');
|
||
}
|
||
if (notFound) {
|
||
if (notFound.startsWith('/')) {
|
||
notFound = notFound.slice(1);
|
||
}
|
||
namespaceConfig[ns].notFound = notFound.replace(/\\/g, '/');
|
||
}
|
||
if (path) {
|
||
if (!path.startsWith('/')) {
|
||
path = `/${path}`;
|
||
}
|
||
namespaceConfig[ns].path = path.replace(/\\/g, '/');
|
||
}
|
||
else {
|
||
namespaceConfig[ns].path = `/${ns}`;
|
||
}
|
||
}
|
||
else {
|
||
namespaceConfig[ns].path = `/${ns}`;
|
||
}
|
||
}
|
||
}
|
||
);
|
||
|
||
traversePages(nss);
|
||
|
||
const node = body.find(
|
||
ele => t.isVariableDeclaration(ele) && t.isIdentifier(ele.declarations[0].id) && ele.declarations[0].id.name === 'allRouters'
|
||
);
|
||
|
||
assert(node, `${filename}中没有定义allRouters`);
|
||
const declaration = node.declarations[0];
|
||
const { init } = declaration;
|
||
assert(t.isArrayExpression(init) && init.elements.length === 0);
|
||
|
||
nss.forEach(
|
||
(ns) => {
|
||
const { path, notFound, first } = namespaceConfig[ns];
|
||
|
||
const children = t.arrayExpression(
|
||
Object.values(pageFiles[ns]).map(
|
||
(page) => makeRouterItem(page, ns, path, first)
|
||
)
|
||
);
|
||
if (notFound) {
|
||
children.elements.push(
|
||
t.objectExpression(
|
||
[
|
||
t.objectProperty(
|
||
t.identifier('path'),
|
||
t.stringLiteral('*')
|
||
),
|
||
t.objectProperty(
|
||
t.identifier('namespace'),
|
||
t.stringLiteral(path),
|
||
),
|
||
t.objectProperty(
|
||
t.identifier('Component'),
|
||
t.callExpression(
|
||
t.memberExpression(t.identifier('React'), t.identifier('lazy')),
|
||
[
|
||
t.arrowFunctionExpression(
|
||
[],
|
||
t.callExpression(t.import(), [
|
||
t.stringLiteral(join(AppPaths.appRootSrc, 'pages', ns, notFound, 'index').replace(/\\/g, '/'))
|
||
])
|
||
),
|
||
]
|
||
)
|
||
)
|
||
]
|
||
)
|
||
)
|
||
}
|
||
|
||
init.elements.push(
|
||
t.objectExpression([
|
||
t.objectProperty(
|
||
t.identifier('path'),
|
||
t.stringLiteral(path),
|
||
),
|
||
t.objectProperty(
|
||
t.identifier('namespace'),
|
||
t.stringLiteral(path),
|
||
),
|
||
t.objectProperty(
|
||
t.identifier('Component'),
|
||
t.callExpression(
|
||
t.memberExpression(t.identifier('React'), t.identifier('lazy')),
|
||
[
|
||
t.arrowFunctionExpression(
|
||
[],
|
||
t.callExpression(t.import(), [
|
||
t.stringLiteral(join('.', '..', 'namespaces', ns).replace(/\\/g, '/')),
|
||
])
|
||
),
|
||
]
|
||
)
|
||
),
|
||
t.objectProperty(
|
||
t.identifier('children'),
|
||
children
|
||
)
|
||
])
|
||
);
|
||
}
|
||
);
|
||
|
||
body.unshift(
|
||
t.importDeclaration(
|
||
[t.importDefaultSpecifier(t.identifier('React'))],
|
||
t.stringLiteral('react')
|
||
)
|
||
);
|
||
addFileWatcher(filename, nss);
|
||
|
||
/* const { code } = transformFromAstSync(path.container);
|
||
|
||
console.log(code); */
|
||
}
|
||
},
|
||
},
|
||
};
|
||
}; |