Javascript 我需要读取一个用TypeScript(一个角度模块)编写的文件,并通过编程将代码添加到原始文件中。我该怎么做呢?

Javascript 我需要读取一个用TypeScript(一个角度模块)编写的文件,并通过编程将代码添加到原始文件中。我该怎么做呢?,javascript,node.js,angular,typescript,Javascript,Node.js,Angular,Typescript,这是我要读取的app.module.ts文件(用TypeScript编写) 然后,基本上我想通过编程方式添加另一个组件,如下所示: import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { DashboardComponent } from './dashboard/dashboard.component'; i

这是我要读取的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 { 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 {}
因此,程序将接收原始文件作为输入,然后在完成时使用新代码修改原始文件。要添加的“组件”可以作为参数接收

你将如何着手解决这个问题

PD:我想这样做,以便正确地检测符号。我的意思是,只要目标TS文件的语法有效,代码就必须工作

PD2:我一直在检查编译器Api


谢谢

我会选择typescript编译器API

假设我们在所有其他导入的末尾添加导入,并在所有其他路由的末尾添加路由

这里有一个。您可以在以下情况下进行改进:

  • 您需要检查是否已经添加了路由

  • 或者,例如,如果有添加的导入路径,那么我们不需要添加新的导入,而是向exising添加子句

或者根据你的要求做其他事情

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;
}
接口替换{
位置:数字;
toInsert:字符串;
}
函数替换(内容:字符串){
const sourceFile=ts.createSourceFile(“”,content,ts.ScriptTarget.Latest,true);
常量替换:替换[]=[
addRoute({path:'shows',component:'ShowsComponent'},sourceFile),
addImport({name:'ShowsComponent',路径:'./shows/shows.component'},源文件)
]
用于(替换件的常量替换){
content=content.substring(0,replacement.atPosition)+replacement.toInsert+content.substring(replacement.atPosition);
}
返回内容;
}
函数addRoute(路由:{path:string,component:string},sourceFile:ts.sourceFile):替换{
const routesDefinition=getRoutesArrayNode(源文件);
常量routes=findNodes(routesDefinition,ts.SyntaxKind.ObjectLiteralExpression);
const toInsert=`,\n{path:'${route.path}',component:${route.component}`;
返回insertToTheEnd(路线、插入);
}
函数addImport(toImport:{name:string,path:string},sourceFile:ts.sourceFile):替换{
const allImports=findNodes(源文件,ts.SyntaxKind.ImportDeclaration);
const toInsert=`\nimport{${toImport.name}来自${toImport.path};`;
返回insertToTheEnd(allImports,toInsert);;
}
函数insertToTheEnd(节点:any[],toInsert:string):替换{
const lastItem=nodes.sort((第一个:ts.Node,第二个:ts.Node):number=>first.getStart()-second.getStart()).pop();
常量atPosition:number=lastItem.getEnd();
返回{atPosition,toInsert};
}
函数getRoutesArrayNode(源文件:ts.sourceFile):ts.Node{
let结果:ts.Node | null=null;
ts.forEachChild(源文件,(节点)=>{
if(node.kind==ts.SyntaxKind.VariableStatement){
const variableStatement=节点;
for(variableStatement.declarationList.declarations的常量variableDeclaration){
if(variableDeclaration.name.kind==ts.SyntaxKind.Identifier&&variableDeclaration.initializer){
const initializerNode=variableDeclaration.initializer;
if(initializerNode.kind==ts.SyntaxKind.ArrayLiteralExpression){
if(isRoutesArray(可变声明)){
结果=初始化节点;
}
}
}
}
}
});
返回结果;
}
函数isRoutesArray(节点:ts.node):布尔值{
让结果=假;
ts.forEachChild(节点,子节点=>{
if(child.kind==ts.SyntaxKind.TypeReference){
const typeReferenceNode=子节点;
const typeNameNode=typeReferenceNode.typeName;
if(typeNameNode.text==='Routes'){
结果=真;
}
}
});
返回结果;
}
函数findNodes(节点:ts.node,种类:ts.SyntaxKind):任意[]{
常量arr:any[]=[];
如果(node.kind==种类){
arr.push(节点);
}
for(node.getChildren()的常量子级){
findNodes(子节点,类).forEach(节点=>{
arr.push(节点);
});
}
返回arr;
}
这些链接也可能对您有所帮助:


p.S.如果您在node.js上,那么我想您知道如何使用
fs.readFile
fs.writeFile
:)

您可以使用TypeScript编译器,看到你在浏览器中工作了吗?看起来你可以在node.js中将文件读入字符串,然后使用正则表达式在插入新内容之前找到行。获得索引后,可以使用
.slice()
创建一个新字符串,该字符串由插入点之前的内容、新内容和插入点之后的内容组成。然后,将新字符串写入文件。谢谢大家的评论。是的,我一直在检查编译器选项yurzui。jfriend,这当然是一个选项,但我需要确保无论TypeScript文件中的代码或变量的名称如何,它都能工作。那么,您最近添加的问题是否意味着您必须实际解释代码?
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;
}