此篇介紹的 API 依序為:
關於 Angular 專案裡的 main.ts
相關操作的小工具
引入路徑:
import {
findBootstrapModuleCall,
findBootstrapModulePath,
getAppModulePath
} from '@schematics/angular/utility/ng-ast-utils';
用途:使用傳入的檔案樹與 main.ts
的路徑來取得 main.ts
裡, bootstrapModule
那一段的 TypeScript AST 節點的資料。
回傳值: main.ts
裡, bootstrapModule
那一段的 TypeScript AST 資料。
用法:
findBootstrapModuleCall(_tree, '/src/main.ts');
/*
platformBrowserDynamic().bootstrapModule(AppModule) 這段程式碼的 AST 節點的資料 ( CallExpression )
*/
用途:使用傳入的檔案樹與 main.ts
的路徑來取得初始啟動模組相對於 main.ts
的路徑(含檔名)。
回傳值:初始啟動模組相對於 main.ts
的路徑。
用法:
findBootstrapModulePath(_tree, '/src/main.ts'); // './app/app.module'
用途:使用傳入的檔案樹與 main.ts
的路徑來取得初始啟動模組在專案中的路徑(含檔名與附檔名)。
回傳值:初始啟動模組在專案中的路徑(含檔名與附檔名)。
用法:
getAppModulePath(_tree, '/src/main.ts'); // '/src/app/app.module.ts'
關於 TypeScript AST 相關操作的小工具(大部分都是 For Angular 用的)。
引入路徑:
import {
insertImport,
findNodes,
getSourceNodes,
findNode,
insertAfterLastOccurrence,
getContentOfKeyLiteral,
getDecoratorMetadata,
getFirstNgModuleName,
getMetadataField,
addSymbolToNgModuleMetadata,
addDeclarationToModule,
addImportToModule,
addProviderToModule,
addExportToModule,
addBootstrapToModule,
addEntryComponentToModule,
isImported,
getRouterModuleDeclaration,
addRouteDeclarationToModule
} from '@schematics/angular/utility/ast-utils';
用途:使用傳入的 要修改的檔案的 SourceFile 、要修改的檔案檔名、要引入的識別符名稱、要引入的識別符所在的檔案路徑與是否為預設來取得插入 import { XXXX } from '/path/to/the/fileName
的資訊的資料( Change ,後續會說明 )。
回傳值:取得插入 import { XXXX } from '/path/to/the/fileName
所需的資訊的資料 ( Change )。
用法:
const path = '/src/app/app.module.ts';
const content = _tree.read(path)!.toString();
const sourceFile = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true)!;
const change = insertImport(sourceFile, path, 'RouterModule', '@angular/router')
console.log(change);
/*
InsertChange {
path: '/src/app/app.module.ts',
pos: 148,
toAdd: ";\nimport { RouterModule } from '@angular/router'",
description: 'Inserted ;\n' +
"import { RouterModule } from '@angular/router' into position 148 of /src/app/app.module.ts",
order: 148
}
*/
用途:根據傳入的 TypeSCript AST 節點資料、要尋找的節點的類型、最多尋找個數上限(沒傳入就找完為止)與如果傳入的節點資料就是要尋找的節點類型,是否要再繼續往下找(預設為否)來取得該節點下所有節點類型為要尋找的節點類型的節點資料。
回傳值:該節點下所有節點類型為要尋找的節點類型的節點資料。
用法:
const path = '/src/app/app.module.ts';
const content = _tree.read(path)!.toString();
const sourceFile = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true)!;
const nodes = findNodes(sourceFile, ts.SyntaxKind.ImportDeclaration);
用途:根據傳入的 TypeSCript AST 根節點資料取得所有的節點資料(包含根節點自己)。
回傳值:所有的節點資料。
用法:
const path = '/src/app/app.module.ts';
const content = _tree.read(path)!.toString();
const sourceFile = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true)!;
const nodes = getSourceNodes(sourceFile);
用途:根據傳入的 TypeSCript AST 節點資料、要尋找的節點的類型與要尋找的節點的文字來取得該節點的資料。
回傳值:特定節點的資料。
用法:
const path = '/src/app/app.module.ts';
const content = _tree.read(path)!.toString();
const sourceFile = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true)!;
const node = findNode(sourceFile, ts.SyntaxKind.ImportDeclaration, `import { AppComponent } from './app.component';`);
用途:根據傳入的 TypeSCript AST 節點資料(陣列)、要插入的字串、檔案名稱(含路徑)、插入位置與節點類型(非必要)來取得用來插入變更的資訊的資料( Change )。
回傳值:用來插入變更的資訊的資料( Change )。
用法:
const path = './src/foo.ts';
const content = `const arr = ['foo'];`;
const sourceFile = ts.createSourceFile(path, content, ts.ScriptTarget.Latest, true)!;
const arrayNode = findNodes(
sourceFile.getChildren().shift() as ts.Node,
ts.SyntaxKind.ArrayLiteralExpression,
);
const elements = (arrayNode.pop() as ts.ArrayLiteralExpression).elements;
const change = insertAfterLastOccurrence(
elements as unknown as ts.Node[],
`, 'bar'`,
path,
elements.pos,
ts.SyntaxKind.StringLiteral,
);
console.log(change);
/*
InsertChange {
path: './src/foo.ts',
pos: 18,
toAdd: ", 'bar'",
description: "Inserted , 'bar' into position 18 of ./src/foo.ts",
order: 18
}
*/
用途:根據傳入的 TypeSCript AST 根節點資料與節點資料來取得該節點的字串。
回傳值:某個節點的所代表的字串。
注意,如果傳入的節點資料的類型不是
Indentifier
或是StringLiteral
的話,則此方法一定會回null
。
用法:
const path = './src/foo.ts';
const content = `const arr = ['foo'];`;
const sourceFile = ts.createSourceFile(path, content, ts.ScriptTarget.Latest, true)!;
const arrayNode = findNodes(
sourceFile.getChildren().shift() as ts.Node,
ts.SyntaxKind.ArrayLiteralExpression,
);
const element = (arrayNode.pop() as ts.ArrayLiteralExpression).elements[0];
getContentOfKeyLiteral(sourceFile, element) // 'foo'
用途:根據傳入的 TypeSCript AST 根節點資料、裝飾器的識別符名稱與 該裝飾器的引入路徑來取得該裝飾器函式內的所有參數的節點資料。
回傳值:特定裝飾器函式內的所有參數的節點資料(陣列)。
用法:
const path = '/src/app/app.module.ts';
const content = _tree.read(path)!.toString();
const sourceFile = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true)!;
getDecoratorMetadata(sourceFile, 'NgModule', '@angular/core');
用途:根據傳入的 TypeSCript AST 根節點資料來取得該檔案內第一個使用 NgModule
裝飾器函式的類別名稱。
回傳值:該檔案內第一個使用 NgModule
裝飾器函式的類別名稱。
用法:
const path = '/src/app/app.module.ts';
const content = _tree.read(path)!.toString();
const sourceFile = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true)!;
getFirstNgModuleName(sourceFile); // 'AppModule'
用途:根據傳入的 TypeSCript AST 節點資料與欲取得的 Metadata 的 Key 值名稱來取得該 Key 值區塊的節點資料。
回傳值:Metadata 的 Key 值區塊的節點資料(陣列)。
用法:
const path = '/src/app/app.module.ts';
const content = _tree.read(path)!.toString();
const sourceFile = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true)!;
const metadata = getDecoratorMetadata(sourceFile, 'NgModule', '@angular/core')[0] as ts.ObjectLiteralExpression;
getMetadataField(metadata, 'imports');
用途:根據傳入的 TypeSCript AST 根節點資料、欲加入識別符的檔案路徑(含檔名)、欲加入識別符的 Metadata 的 Key 值名稱、識別符名稱與該識別符的引入路徑(非必填)來取得該變動所需資訊之資料( Change )。
回傳值:在 NgModule 的 Metadata 裡加入某個識別符的變動所需之資料( Change )(陣列)。
用法:
const path = '/src/app/app.module.ts';
const content = _tree.read(path)!.toString();
const sourceFile = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true)!;
addSymbolToNgModuleMetadata(sourceFile, path, 'imports', 'RouterModule', '@angular/router');
用途:根據傳入的 TypeSCript AST 根節點資料、欲加入識別符的檔案路徑(含檔名)、欲加入的識別符名稱與該識別符的引入路徑來將識別符加入到 NgModule 的 Metadata 的 declarations
裡(其實就是用上面的 addSymbolToNgModuleMetadata
來處理)。
回傳值:在 NgModule 的 Metadata 的 declarations
裡加入某個識別符的變動所需之資料( Change )(陣列)。
用法:
const path = '/src/app/app.module.ts';
const content = _tree.read(path)!.toString();
const sourceFile = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true)!;
addDeclarationToModule(sourceFile, path, 'HomeComponent', './home/home.component');
用途:跟 addDeclarationToModule
雷同,就只是將原本要加在 declarations
換成加在 imports
而已。
回傳值:在 NgModule 的 Metadata 的 imports
裡加入某個識別符的變動所需之資料( Change )(陣列)。
用法:
const path = '/src/app/app.module.ts';
const content = _tree.read(path)!.toString();
const sourceFile = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true)!;
addImportToModule(sourceFile, path, 'RouterModule', '@angular/router');
用途:跟 addDeclarationToModule
雷同,就只是將原本要加在 declarations
換成加在 porviders
而已。
回傳值:在 NgModule 的 Metadata 的 imports
裡加入某個識別符的變動所需之資料( Change )(陣列)。
用法:
const path = '/src/app/app.module.ts';
const content = _tree.read(path)!.toString();
const sourceFile = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true)!;
addProviderToModule(sourceFile, path, 'HomeService', './home/home.service');
用途:跟 addDeclarationToModule
雷同,就只是將原本要加在 declarations
換成加在 exports
而已。
回傳值:在 NgModule 的 Metadata 的 imports
裡加入某個識別符的變動所需之資料( Change )(陣列)。
用法:
const path = '/src/app/app.module.ts';
const content = _tree.read(path)!.toString();
const sourceFile = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true)!;
addExportToModule(sourceFile, path, 'HomeService', './home/home.service');
測試原始碼 - Section 1
測試原始碼 - Section 2
用途:跟 addDeclarationToModule
雷同,就只是將原本要加在 declarations
換成加在 bootstrap
而已。
回傳值:在 NgModule 的 Metadata 的 imports
裡加入某個識別符的變動所需之資料( Change )(陣列)。
用法:
const path = '/src/app/app.module.ts';
const content = _tree.read(path)!.toString();
const sourceFile = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true)!;
addBootstrapToModule(sourceFile, path, 'BppComponent', './bpp.component');
用途:跟 addDeclarationToModule
雷同,就只是將原本要加在 declarations
換成加在 entryComponents
而已。
回傳值:在 NgModule 的 Metadata 的 imports
裡加入某個識別符的變動所需之資料( Change )(陣列)。
用法:
const path = '/src/app/app.module.ts';
const content = _tree.read(path)!.toString();
const sourceFile = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true)!;
addEntryComponentToModule(sourceFile, path, 'BppComponent', './bpp.component');
用途:根據傳入的 TypeSCript AST 根節點資料、欲加入的識別符名稱與該識別符的引入路徑來判斷該識別符有沒有被加入過。
回傳值: true
或 false
。
用法:
const path = '/src/app/app.module.ts';
const content = _tree.read(path)!.toString();
const sourceFile = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true)!;
isImported(sourceFile, 'AppComponent', './app.component'); // true
用途:根據傳入的 TypeSCript AST 根節點資料找出引入的 RouterModule.forRoot()
或者是 RouterModule.forChild()
的節點資料。
回傳值:有找到時,回傳 RouterModule.forRoot()
或者是 RouterModule.forChild()
的節點資料;沒有找到則回傳 undefined
。
用法:
const path = '/src/app/app.module.ts';
const content = _tree.read(path)!.toString();
const sourceFile = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true)!;
getRouterModuleDeclaration(sourceFile)
用途:根據傳入的 TypeSCript AST 根節點資料、要新增的檔案路徑與要新增的路由配置字串來取得將配置自動加入到路由配置裡的變動所需之資料。
回傳值:取得將配置自動加入到路由配置裡的變動所需之資料。
用法:
const path = '/src/app/app.module.ts';
const content = _tree.read(path)!.toString();
const sourceFile = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true)!;
const route = `{ path: 'foo', component: FooComponent }`;
addRouteDeclarationToModule(sourceFile, path, route);
ast-utils
有超多很好用的 API ,不過筆者寫到有點懷疑人生...。