zelda-ast
Version:
zelda static analysis based on javascript ast.
116 lines (98 loc) • 3.13 kB
JavaScript
import j from 'jscodeshift';
import Collection from 'jscodeshift/src/Collection';
import once from 'lodash.once';
import assert from 'assert';
import Helper from './Helper';
Helper.register();
const methods = {
findRouters() {
if (!this.hasModule('dva/router')) return Collection.fromPaths([], this);
return this.find(j.JSXElement, {
openingElement: {
name: {
type: 'JSXIdentifier',
name: 'Router',
},
},
});
},
// TODO: support config router with JavaScript Object
getRouterInfo() {
const routeByIds = {};
const ROUTER_COMPONENTS = ['Router', 'Route', 'Switch', 'Redirect', 'IndexRedirect', 'IndexRoute'];
function parse(node, parentPath = '', parentId, parentDepth = -1) {
assert(
node.type === 'JSXElement',
`getRouterTree: node should be JSXElement, but got ${node.type}`
);
const name = node.openingElement.name.name;
assert(
ROUTER_COMPONENTS.indexOf(name) > -1,
`getRouterTree: component should be one of ${ROUTER_COMPONENTS.join(', ')}`
);
const ret = { type: name };
ret.depth = parentDepth + 1;
ret.attributes = j(node.openingElement)
.find(j.JSXAttribute)
.simpleMap(path => {
const node = path.node;
return {
name: node.name.name,
value: getAttributeValue(node.value),
};
})
.reduce((memo, { name, value }) => {
memo[name] = value;
return memo;
}, {});
const path = ret.attributes.path;
if (path) {
ret.absolutePath = path.charAt(0) === '/' ? path : `${parentPath}/${path}`;
}
if (ret.absolutePath) {
ret.id = `${ret.type}-${ret.absolutePath}`;
} else if (parentId) {
ret.id = `${ret.type}-parentId_${parentId}`;
} else {
ret.id = `${ret.type}-root`;
}
// 有些特殊情况 id 会重复(同样的父子路由,出现在多处,可能父亲的父亲不一样)
if (routeByIds[ret.id]) {
ret.id = `${ret.id}_${Math.random()}`;
}
if (node.children) {
ret.children = node.children
.filter(node => node.type === 'JSXElement')
.map(node => parse(node, ret.attributes.path, ret.id, ret.depth));
}
routeByIds[ret.id] = ret;
return {
id: ret.id,
children: ret.children,
};
}
function getAttributeValue(node) {
if (node) {
if (node.type === 'Literal') {
return node.value;
} else if (node.expression.type === 'Identifier') {
return node.expression.name;
} else if (node.type === 'JSXExpressionContainer') {
return j(node.expression).toSource();
}
throw new Error(`getRouterTree: unsupported attribute type`);
}
return null;
}
return this.simpleMap(path => ({
tree: parse(path.node),
routeByIds,
}));
},
};
function register(jscodeshift = j) {
jscodeshift.registerMethods(methods);
}
export default {
register: once(register),
};