美文网首页
使用 ts ast 解析并动态添加路由配置项

使用 ts ast 解析并动态添加路由配置项

作者: niccky | 来源:发表于2020-08-25 22:39 被阅读0次

方式一

添加路由配置项到 AppModule 中的 RouterModule.forRoot([...]) 里面

function addAppFieldToRouterModule(fileToAdd: string, routeLiterial: string) {
    const tpl = fs.readFileSync(fileToAdd).toString();
    const sourceFile = ts.createSourceFile(fileToAdd,tpl,ts.ScriptTarget.ES2015,true,ts.ScriptKind.TS);
    const app: ts.ClassDeclaration = findNodes(sourceFile, ts.SyntaxKind.ClassDeclaration).find(r=> (r as ts.ClassDeclaration).name.text === 'AppModule') as ts.ClassDeclaration;
    const decoraor: ts.Decorator = app.decorators[0] as ts.Decorator;;
    const callExp = decoraor.expression as ts.CallExpression;
    const allExp = callExp.arguments[0] as ts.ObjectLiteralExpression;
    const importExp = allExp.properties.find(r=>r.kind === ts.SyntaxKind.PropertyAssignment && r.name.getText() === 'imports') as ts.PropertyAssignment;
    const routeExp = importExp.initializer as ts.ArrayLiteralExpression;
    const route = routeExp.elements.find(r=>r.kind === ts.SyntaxKind.CallExpression && (r as ts.CallExpression).expression.kind === ts.SyntaxKind.PropertyAccessExpression && r.getText().includes('RouterModule.forRoot')) as ts.CallExpression;
    const routes = route.arguments[0] as ts.ArrayLiteralExpression;
    const lastExp = routes.elements[routes.elements.length - 1];
    const text = lastExp.getFullText();
    const textMatch = text.match(/\r?\n(\r?)\s*/) || [];
    const textLiterial = `${textMatch[0] ?? ' '}${routeLiterial}`;
    const pos = lastExp.end;
    const routeText = `,${textLiterial}`;

    const rawText = fs.readFileSync(fileToAdd).toString('utf-8');    
    const prefix = rawText.substring(0, pos);
    const suffix = rawText.substring(pos);
    const fullText = `${prefix}${routeText}${suffix}`;
    console.log(fullText);
    const dest = __dirname+'/demo.ts';
   saveAs(dest, fullText);
}

saveAs

保存文件到本地

function saveAs(fileToAdd: string, content:string) {
    if(fs.existsSync(fileToAdd)) {
        fs.unlinkSync(fileToAdd);
    }

    fs.writeFileSync(fileToAdd, content);
}

findNodes

通过遍历 ast 节点,找到条件对应的 ast

function findNodes(node: ts.Node, kind: ts.SyntaxKind): ts.Node[] {
    const ans: ts.Node[] = [];
    if(node.kind === kind) {
        ans.push(node);
    } else {
        for(let child of node.getChildren()) {
            findNodes(child, kind).forEach((node: ts.Node) => {
                ans.push(node);
            });
        }
    }
    return ans;
}

addAppFieldToRouterModule(__dirname+'/demo.tpl', 'demoApp');

方式二

function addAppToRouterModule() {
    const tpl = fs.readFileSync(__dirname+'/demo.tpl').toString();
    const sourceFile = ts.createSourceFile(__dirname+'/demo.ts', tpl, ts.ScriptTarget.ES2015,true,ts.ScriptKind.TS);

    let ans: ts.CallExpression;
    function visitor(node: ts.Node): void {
        if(!ans && node) {
            if(ts.isPropertyAccessExpression(node) && node.getText().includes('RouterModule')) {
                ans = node.parent as ts.CallExpression;
                return;
            }
            node.forEachChild(visitor);
        }
    }
    ts.forEachChild(sourceFile, visitor);
    const exp = ans.arguments[0] as ts.ArrayLiteralExpression;
    const elems = exp.elements;
    const lastExp:any = elems[elems.length-1];
    const rawText = fs.readFileSync(__dirname+'/demo.tpl').toString();
    const text = lastExp.getFullText();
    const textMatch = text.match(/\r?\n(\r?)\s*/) || [];
    const textLiterial = `${textMatch[0] ?? ''}demoApp`;
    const pos = lastExp.end;
    const routeText = `,${textLiterial}`;
    const prefix = rawText.substring(0, pos);
    const suffix = rawText.substring(pos);
    const result = `${prefix}${routeText}${suffix}`;
    console.log(result);
}

方式三

if(ts.isIdentifier(lastExp)) {
        const declaration = getAppDeclaration(sourceFile,lastExp.getText()) as ts.VariableStatement;
        text = declaration.getText();
        text = `\r\nconst demoApp = {
            name: 'app.device',
            url: '/app/device'
        }`;
        prefix = result.substring(0, declaration.end);
        suffix = result.substring(declaration.end);
        result = `${prefix}${text}${suffix}`;
    }

    console.log(result);

getAppDeclaration


function getAppDeclaration(node: ts.SourceFile, name: string): ts.VariableStatement | null {
    const ans: ts.VariableStatement[] = [];
    for(let child of node.statements) {
        if(ts.isVariableStatement(child)) {
            ans.push(child);
        }
    }

    for(let vs of ans) {
        const vd = vs.declarationList.declarations[0] as ts.VariableDeclaration; 
        for(let cd of vd.getChildren()) {
            if(cd.getText() === name) {
                return vs as ts.VariableStatement;
            }
        }
    }
    
    return null;
}

相关文章

网友评论

      本文标题:使用 ts ast 解析并动态添加路由配置项

      本文链接:https://www.haomeiwen.com/subject/mqjtsktx.html