import { parser, winPath } from '@fesjs/utils'; import { readdirSync, readFileSync, statSync } from 'fs'; import { join } from 'path'; /** * 获取文件夹所有JS文件路径 * @param {string} dir */ function getDirFilePaths(dir) { const dirs = readdirSync(dir); let pathList = []; for (const name of dirs) { const path = winPath(join(dir, name)); const info = statSync(path); if (info.isDirectory()) { pathList = pathList.concat(getDirFilePaths(path)); } else if (path.endsWith('.js')) { pathList.push(path); } } return pathList; } /** * 路径转驼峰 * @param {*} path */ function pathToHump(path, root) { return path.replace(root, '') .replace('.js', '') .replace(RegExp('(/|\\.|-|_)\\S', 'g'), text => text[1].toUpperCase()) .replace(/\S/, text => text.toLowerCase()); } /** * 获取vuex模块的mutations、actions、getters类型 * @param {*} ast * @param {*} name */ function getModelTypes(ast, name, namespace = '') { const types = { mutations: {}, actions: {}, getters: {} }; let namespaced = false; if (ast.type !== 'ObjectExpression') return types; ast.properties.forEach((node) => { if (node.key.name === 'namespaced' && node.value.value) { namespaced = true; return; } if (Object.keys(types).includes(node.key.name)) { let type = types[node.key.name]; if (namespaced) { type = types[node.key.name][name]; if (!type) { // eslint-disable-next-line no-multi-assign type = types[node.key.name][name] = {}; } } node.value.properties.forEach((prop) => { const key = prop.key && prop.key.name; if (key) { type[key] = `${namespace}${namespaced ? `${name}/` : ''}${key}`; } }); return; } if (node.key.name === 'modules') { node.value.properties.forEach((prop) => { const subTypes = getModelTypes(prop.value, prop.key.name, `${namespace}${namespaced ? `${name}/` : ''}`); Object.keys(types).forEach((key) => { if (namespaced) { types[key][name] = { ...subTypes[key], ...types[key][name] }; } else { types[key] = { ...subTypes[key], ...types[key] }; } }); }); } }); return types; } /** * 解析模块 * @param {*} paths * @param {*} root */ function parseModel(paths = [], root) { const modules = []; const importModules = []; let MUTATION_TYPES = {}; let ACTION_TYPES = {}; let GETTER_TYPES = {}; paths.forEach((path) => { const moduleName = pathToHump(path, root); importModules.push(`import ${moduleName} from '${path}'`); modules.push(moduleName); const content = readFileSync(path).toString('utf-8'); let ast = parser.parse(content, { sourceType: 'module', plugins: ['jsx', 'typescript'] }); ast = ast.program.body.filter(body => body.type === 'ExportDefaultDeclaration')[0]; if (ast) { const { mutations, actions, getters } = getModelTypes(ast.declaration, moduleName); MUTATION_TYPES = { ...mutations, ...MUTATION_TYPES }; ACTION_TYPES = { ...actions, ...ACTION_TYPES }; GETTER_TYPES = { ...getters, ...GETTER_TYPES }; } }); return { modules, importModules, MUTATION_TYPES, ACTION_TYPES, GETTER_TYPES }; } function parsePlugin(paths = [], root) { const plugins = []; const importPlugins = []; paths.forEach((path) => { const moduleName = pathToHump(path, root); importPlugins.push(`import ${moduleName} from '${path}'`); plugins.push(moduleName); }); return { plugins, importPlugins }; } export function parseStore(root) { const paths = getDirFilePaths(root); const modelPaths = []; const pluginPaths = []; paths.forEach((path) => { if (path.indexOf('plugin') > -1) { pluginPaths.push(path); } else { modelPaths.push(path); } }); return { ...parsePlugin(pluginPaths, root), ...parseModel(modelPaths, root) }; }