简体   繁体   English

我需要读取用TypeScript(An Angular Module)编写的文件,并以编程方式将代码添加到原始文件中。 关于我该怎么做的任何想法?

[英]I need to read a file written in TypeScript (An Angular Module) and programatically add code to the original file. Any ideas on how can I do it?

This is the app.module.ts file I want to read (Written In TypeScript). 这是我想要阅读的app.module.ts文件(用TypeScript编写)。

    import { NgModule }             from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { DashboardComponent }   from './dashboard/dashboard.component';
import { HeroesComponent }      from './heroes/heroes.component';
import { HeroDetailComponent }  from './hero-detail/hero-detail.component';

const routes: Routes = [
  { path: '', redirectTo: '/dashboard', pathMatch: 'full' },
  { path: 'dashboard', component: DashboardComponent },
  { path: 'detail/:id', component: HeroDetailComponent },
  { path: 'heroes', component: HeroesComponent }
];


@NgModule({
  imports: [ RouterModule.forRoot(routes) ],
  exports: [ RouterModule ]
})
export class AppRoutingModule {}

Then, basically I want to programatically add another component like this: 然后,基本上我想以编程方式添加另一个组件,如下所示:

import { NgModule }             from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { DashboardComponent }   from './dashboard/dashboard.component';
import { HeroesComponent }      from './heroes/heroes.component';
import { ShowsComponent }      from './shows/shows.component';//** NEW LINE
import { HeroDetailComponent }  from './hero-detail/hero-detail.component';

const routes: Routes = [
  { path: '', redirectTo: '/dashboard', pathMatch: 'full' },
  { path: 'dashboard', component: DashboardComponent },
  { path: 'detail/:id', component: HeroDetailComponent },
  { path: 'heroes', component: HeroesComponent },
  { path: 'shows', component: ShowsComponent }//** New Line
];


@NgModule({
  imports: [ RouterModule.forRoot(routes) ],
  exports: [ RouterModule ]
})
export class AppRoutingModule {}

So, the program would receive the original file as input and then when finish the original file would be modified with the new code. 因此,程序将接收原始文件作为输入,然后在完成时将使用新代码修改原始文件。 The "component" to be added can be received as a parameter. 可以接收要添加的“组件”作为参数。

How would you go about solving this? 你会怎么解决这个问题?

PD: I want to do it so I detect the symbols properly. PD:我想这样做,所以我正确地检测了符号。 I mean, the code has to work as long as the syntax of the target TS file is valid. 我的意思是,只要目标TS文件的语法有效,代码就必须工作。

PD2: I've been checking the Compiler Api https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API https://www.npmjs.com/package/ts-node PD2:我一直在检查编译器Api https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API https://www.npmjs.com/package/ts-node

Thanks! 谢谢!

I would go with typescript compiler API. 我会使用typescript编译器API。

Let's say we add import at the end of all other imports and also add route at the end of all other routes. 假设我们在所有其他导入的末尾添加导入,并在所有其他路由的末尾添加路由。

Here's a simplified version . 这是一个简化版本 You can improve it if: 您可以在以下情况下改进它

  • you need to check if there is already added route. 你需要检查是否已经添加了路线。

  • or for instance if there is added import path then we do not need to add new import but rather add clause to exising. 或者例如,如果添加了导入路径,那么我们不需要添加新的导入,而是添加子句来进行导出。

or something else depending on your demands. 或其他取决于您的要求。

interface Replacement {
  atPosition: number;
  toInsert: string;
}

function replace(content: string) {
  const sourceFile = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true);

  const replacements: Replacement[] = [
    addRoute({ path: 'shows', component: 'ShowsComponent' }, sourceFile),
    addImport({ name: 'ShowsComponent', path: './shows/shows.component'}, sourceFile)
  ]

  for (const replacement of replacements) {
    content = content.substring(0, replacement.atPosition) + replacement.toInsert + content.substring(replacement.atPosition);
  }

  return content;
}

function addRoute(route: { path: string, component: string }, sourceFile: ts.SourceFile): Replacement {
  const routesDefinition = getRoutesArrayNode(sourceFile);
  const routes = findNodes(routesDefinition, ts.SyntaxKind.ObjectLiteralExpression);

  const toInsert = `,\n  { path: '${route.path}', component: ${route.component} }`;
  return insertToTheEnd(routes, toInsert);
}

function addImport(toImport: { name: string, path: string }, sourceFile: ts.SourceFile): Replacement {
  const allImports = findNodes(sourceFile, ts.SyntaxKind.ImportDeclaration);

  const toInsert = `\nimport { ${toImport.name} } from '${toImport.path}';`;
  return insertToTheEnd(allImports, toInsert);;
}

function insertToTheEnd(nodes: any[], toInsert: string): Replacement {
  const lastItem = nodes.sort((first: ts.Node, second: ts.Node): number => first.getStart() - second.getStart()).pop();
  const atPosition: number = lastItem.getEnd();

  return { atPosition, toInsert };
}

function getRoutesArrayNode(sourceFile: ts.SourceFile): ts.Node {
  let result: ts.Node | null = null;
  ts.forEachChild(sourceFile, (node) => {
    if (node.kind === ts.SyntaxKind.VariableStatement) {
      const variableStatement = <ts.VariableStatement>node;
      for (const variableDeclaration of variableStatement.declarationList.declarations) {
        if (variableDeclaration.name.kind == ts.SyntaxKind.Identifier && variableDeclaration.initializer) {
          const initializerNode = variableDeclaration.initializer;
          if (initializerNode.kind === ts.SyntaxKind.ArrayLiteralExpression) {
            if (isRoutesArray(variableDeclaration)) {
              result = initializerNode;
            }
          }
        }
      }
    }
  });
  return result;
}

function isRoutesArray(node: ts.Node): boolean {
  let result = false;
  ts.forEachChild(node, child => {
    if (child.kind === ts.SyntaxKind.TypeReference) {
      const typeReferenceNode = <ts.TypeReferenceNode>child;
      const typeNameNode = typeReferenceNode.typeName;
      if (typeNameNode.text === 'Routes') {
        result = true;
      }
    }
  });
  return result;
}


function findNodes(node: ts.Node, kind: ts.SyntaxKind): any[] {
  const arr: any[] = [];
  if (node.kind === kind) {
    arr.push(node);
  }

  for (const child of node.getChildren()) {
    findNodes(child, kind).forEach(node => {
      arr.push(node);
    });
  }

  return arr;
}

These links might be also helpful for you: 这些链接也可能对您有所帮助:

PS If you're on node.js then I suppose you know how work with fs.readFile and fs.writeFile :) PS如果你在node.js上,那么我想你知道如何使用fs.readFilefs.writeFile :)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 我可以添加ajaxComplete .js文件吗? - Can I add in ajaxComplete a .js file.? 我的JavaScript用作内联代码,但不作为包含文件。 任何想法为什么以及如何解决它? - My JavaScript works as inline code but not as an include file. Any ideas why and how to fix it? 我需要在.js文件,.html和.css文件之间传递的变量。 我该怎么做呢? - I need variables to pass between a .js file, an .html and a .css file. How do I do this? 量角器打字稿在tsConfig文件中将ts转换为js文件时出现问题。 我该如何解决? - Protractor Typescript Problem converting ts to js file in tsConfig file. How do I fix this? 如何在用 TypeScript 编写的 VS Code 扩展中导入 ES6 JavaScript 模块? - How do I import an ES6 JavaScript module in my VS Code extension written in TypeScript? 我正在创建一个Mac应用程序,该应用程序需要读取和写入HTML文件。 我该如何实现? - I'm creating a Mac application that needs to read and write to/from an HTML file. How do I achieve this? 如何分析SonarQube中.php文件中编写的JavaScript代码 - How do I analyze JavaScript code written in .php file in SonarQube 如何在Angular中读取JSON文件? - How do I read a JSON file in Angular? 当我单击按钮时,我在html中有textarea,我需要将textarea内的内容另存为pdf文件。 我该怎么做? - im having textarea in html when i click the button i need to save the content inside the textarea as pdf file. how can i do it? 我在 HBS 文件中有一个按钮。 如何向链接到外部站点的按钮添加变量? - I have a button in an HBS file. how can I add a variable to the button that links to an external site?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM