feat(angular): add ast utils for standalone decorators (#14108)
This commit is contained in:
parent
666e057a50
commit
64d7ceb447
154
packages/angular/src/utils/nx-devkit/ast-utils.spec.ts
Normal file
154
packages/angular/src/utils/nx-devkit/ast-utils.spec.ts
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
import {
|
||||||
|
addImportToComponent,
|
||||||
|
addImportToDirective,
|
||||||
|
addImportToModule,
|
||||||
|
addImportToPipe,
|
||||||
|
} from './ast-utils';
|
||||||
|
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
|
||||||
|
import { createSourceFile, ScriptTarget } from 'typescript';
|
||||||
|
|
||||||
|
describe('Angular AST Utils', () => {
|
||||||
|
it('should correctly add the imported symbol to the NgModule', () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
const pathToModule = `my.module.ts`;
|
||||||
|
const originalContents = `import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
@NgModule({})
|
||||||
|
export class MyModule {}
|
||||||
|
`;
|
||||||
|
|
||||||
|
tree.write(pathToModule, originalContents);
|
||||||
|
|
||||||
|
const symbolToAdd = `CommonModule`;
|
||||||
|
|
||||||
|
const sourceText = tree.read(pathToModule, 'utf-8');
|
||||||
|
const tsSourceFile = createSourceFile(
|
||||||
|
pathToModule,
|
||||||
|
sourceText,
|
||||||
|
ScriptTarget.Latest,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
addImportToModule(tree, tsSourceFile, pathToModule, symbolToAdd);
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
expect(tree.read(pathToModule, 'utf-8')).toMatchInlineSnapshot(`
|
||||||
|
"import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
@NgModule({ imports: [CommonModule]
|
||||||
|
})
|
||||||
|
export class MyModule {}
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should correctly add the imported symbol to the Component', () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
const pathToFile = `my.component.ts`;
|
||||||
|
const originalContents = `import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({})
|
||||||
|
export class MyComponent {}
|
||||||
|
`;
|
||||||
|
|
||||||
|
tree.write(pathToFile, originalContents);
|
||||||
|
|
||||||
|
const symbolToAdd = `CommonModule`;
|
||||||
|
|
||||||
|
const sourceText = tree.read(pathToFile, 'utf-8');
|
||||||
|
const tsSourceFile = createSourceFile(
|
||||||
|
pathToFile,
|
||||||
|
sourceText,
|
||||||
|
ScriptTarget.Latest,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
addImportToComponent(tree, tsSourceFile, pathToFile, symbolToAdd);
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
expect(tree.read(pathToFile, 'utf-8')).toMatchInlineSnapshot(`
|
||||||
|
"import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({ imports: [CommonModule]
|
||||||
|
})
|
||||||
|
export class MyComponent {}
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should correctly add the imported symbol to the Directive', () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
const pathToFile = `my.directive.ts`;
|
||||||
|
const originalContents = `import { Directive } from '@angular/core';
|
||||||
|
|
||||||
|
@Directive({})
|
||||||
|
export class MyDirective {}
|
||||||
|
`;
|
||||||
|
|
||||||
|
tree.write(pathToFile, originalContents);
|
||||||
|
|
||||||
|
const symbolToAdd = `CommonModule`;
|
||||||
|
|
||||||
|
const sourceText = tree.read(pathToFile, 'utf-8');
|
||||||
|
const tsSourceFile = createSourceFile(
|
||||||
|
pathToFile,
|
||||||
|
sourceText,
|
||||||
|
ScriptTarget.Latest,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
addImportToDirective(tree, tsSourceFile, pathToFile, symbolToAdd);
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
expect(tree.read(pathToFile, 'utf-8')).toMatchInlineSnapshot(`
|
||||||
|
"import { Directive } from '@angular/core';
|
||||||
|
|
||||||
|
@Directive({ imports: [CommonModule]
|
||||||
|
})
|
||||||
|
export class MyDirective {}
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should correctly add the imported symbol to the Pipe', () => {
|
||||||
|
// ARRANGE
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
const pathToFile = `my.pipe.ts`;
|
||||||
|
const originalContents = `import { Pipe } from '@angular/core';
|
||||||
|
|
||||||
|
@Pipe({})
|
||||||
|
export class MyPipe {}
|
||||||
|
`;
|
||||||
|
|
||||||
|
tree.write(pathToFile, originalContents);
|
||||||
|
|
||||||
|
const symbolToAdd = `CommonModule`;
|
||||||
|
|
||||||
|
const sourceText = tree.read(pathToFile, 'utf-8');
|
||||||
|
const tsSourceFile = createSourceFile(
|
||||||
|
pathToFile,
|
||||||
|
sourceText,
|
||||||
|
ScriptTarget.Latest,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
addImportToPipe(tree, tsSourceFile, pathToFile, symbolToAdd);
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
expect(tree.read(pathToFile, 'utf-8')).toMatchInlineSnapshot(`
|
||||||
|
"import { Pipe } from '@angular/core';
|
||||||
|
|
||||||
|
@Pipe({ imports: [CommonModule]
|
||||||
|
})
|
||||||
|
export class MyPipe {}
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -128,14 +128,15 @@ export function getDecoratorMetadata(
|
|||||||
.map((expr) => expr.arguments[0] as ts.ObjectLiteralExpression);
|
.map((expr) => expr.arguments[0] as ts.ObjectLiteralExpression);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _addSymbolToNgModuleMetadata(
|
function _addSymbolToDecoratorMetadata(
|
||||||
host: Tree,
|
host: Tree,
|
||||||
source: ts.SourceFile,
|
source: ts.SourceFile,
|
||||||
ngModulePath: string,
|
filePath: string,
|
||||||
metadataField: string,
|
metadataField: string,
|
||||||
expression: string
|
expression: string,
|
||||||
|
decoratorName: 'Component' | 'Directive' | 'NgModule' | 'Pipe'
|
||||||
): ts.SourceFile {
|
): ts.SourceFile {
|
||||||
const nodes = getDecoratorMetadata(source, 'NgModule', '@angular/core');
|
const nodes = getDecoratorMetadata(source, decoratorName, '@angular/core');
|
||||||
let node: any = nodes[0]; // tslint:disable-line:no-any
|
let node: any = nodes[0]; // tslint:disable-line:no-any
|
||||||
|
|
||||||
// Find the decorator declaration.
|
// Find the decorator declaration.
|
||||||
@ -187,7 +188,7 @@ function _addSymbolToNgModuleMetadata(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return insertChange(host, source, ngModulePath, position, toInsert);
|
return insertChange(host, source, filePath, position, toInsert);
|
||||||
}
|
}
|
||||||
|
|
||||||
const assignment = matchingProperties[0] as ts.PropertyAssignment;
|
const assignment = matchingProperties[0] as ts.PropertyAssignment;
|
||||||
@ -259,7 +260,24 @@ function _addSymbolToNgModuleMetadata(
|
|||||||
toInsert = `, ${expression}`;
|
toInsert = `, ${expression}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return insertChange(host, source, ngModulePath, position, toInsert);
|
return insertChange(host, source, filePath, position, toInsert);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _addSymbolToNgModuleMetadata(
|
||||||
|
host: Tree,
|
||||||
|
source: ts.SourceFile,
|
||||||
|
ngModulePath: string,
|
||||||
|
metadataField: string,
|
||||||
|
expression: string
|
||||||
|
): ts.SourceFile {
|
||||||
|
return _addSymbolToDecoratorMetadata(
|
||||||
|
host,
|
||||||
|
source,
|
||||||
|
ngModulePath,
|
||||||
|
metadataField,
|
||||||
|
expression,
|
||||||
|
'NgModule'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function removeFromNgModule(
|
export function removeFromNgModule(
|
||||||
@ -294,6 +312,54 @@ export function removeFromNgModule(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function addImportToComponent(
|
||||||
|
host: Tree,
|
||||||
|
source: ts.SourceFile,
|
||||||
|
componentPath: string,
|
||||||
|
symbolName: string
|
||||||
|
): ts.SourceFile {
|
||||||
|
return _addSymbolToDecoratorMetadata(
|
||||||
|
host,
|
||||||
|
source,
|
||||||
|
componentPath,
|
||||||
|
'imports',
|
||||||
|
symbolName,
|
||||||
|
'Component'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addImportToDirective(
|
||||||
|
host: Tree,
|
||||||
|
source: ts.SourceFile,
|
||||||
|
directivePath: string,
|
||||||
|
symbolName: string
|
||||||
|
): ts.SourceFile {
|
||||||
|
return _addSymbolToDecoratorMetadata(
|
||||||
|
host,
|
||||||
|
source,
|
||||||
|
directivePath,
|
||||||
|
'imports',
|
||||||
|
symbolName,
|
||||||
|
'Directive'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addImportToPipe(
|
||||||
|
host: Tree,
|
||||||
|
source: ts.SourceFile,
|
||||||
|
pipePath: string,
|
||||||
|
symbolName: string
|
||||||
|
): ts.SourceFile {
|
||||||
|
return _addSymbolToDecoratorMetadata(
|
||||||
|
host,
|
||||||
|
source,
|
||||||
|
pipePath,
|
||||||
|
'imports',
|
||||||
|
symbolName,
|
||||||
|
'Pipe'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function addImportToModule(
|
export function addImportToModule(
|
||||||
host: Tree,
|
host: Tree,
|
||||||
source: ts.SourceFile,
|
source: ts.SourceFile,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user