Merge pull request #30 from kawwong/generate-ng-module

Add function for generateNgModule
This commit is contained in:
Ka-wai Wong 2019-03-07 18:07:40 -08:00 коммит произвёл GitHub
Родитель 15d7180f89 df6611e665
Коммит 8b7f315350
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
7 изменённых файлов: 192 добавлений и 1 удалений

Просмотреть файл

@ -0,0 +1,19 @@
// This file is being ignored by typescript because it is only used in tests.
// @ts-ignore
import { AngularPerfModule, RoutingService } from '@microsoft/mezzurite-angular';
// @ts-ignore
@NgModule({
imports: [
AngularPerfModule.forRoot()
]
})
// @ts-ignore
export class InstrumentedModule {
// @ts-ignore
constructor (@Inject(RoutingService) private router: typeof RoutingService) {
// @ts-ignore
router.start();
}
}

Просмотреть файл

@ -0,0 +1,19 @@
// This file is being ignored by typescript because it is only used in tests.
// @ts-ignore
import { RoutingService } from 'not-Mezzurite';
// @ts-ignore
@NgModule({
imports: [
// @ts-ignore
nothing.forRoot()
]
})
// @ts-ignore
export class NotInstrumentedModule {
constructor () {
// @ts-ignore
dummy.blah();
}
}

Просмотреть файл

@ -0,0 +1,52 @@
import { join } from 'path';
import Project from 'ts-morph';
import generateNgModule from './generateNgModule';
describe('generateNgModule.ts', () => {
const project = new Project({
addFilesFromTsConfig: false
});
it('should return null when filePath is null', () => {
expect(generateNgModule(null, null)).toBeNull();
});
it('should return null when sourceFile is null', () => {
expect(generateNgModule('filePath', null)).toBeNull();
});
it('should generate a Mezzurite component from an ngModule file passing all the checks', () => {
const filePath = join('.', 'server', 'src', 'utilities', 'generateComponent', 'helpers', '__mocks__', 'ngModuleInstrumented.ts');
const sourceFile = project.addExistingSourceFile(filePath);
expect(generateNgModule(filePath, sourceFile))
.toMatchObject({
checks: {
hasAngularPerfModule: true,
hasImport: true,
hasRoutingServiceStart: true
},
filePath,
name: 'InstrumentedModule',
type: 'ngModule'
});
project.removeSourceFile(sourceFile);
});
it('should generate a Mezzurite component from an ngModule file passing none of the checks', () => {
const filePath = join('.', 'server', 'src', 'utilities', 'generateComponent', 'helpers', '__mocks__', 'ngModuleNotInstrumented.ts');
const sourceFile = project.addExistingSourceFile(filePath);
expect(generateNgModule(filePath, sourceFile))
.toMatchObject({
checks: {
hasAngularPerfModule: false,
hasImport: false,
hasRoutingServiceStart: false
},
filePath,
name: 'NotInstrumentedModule',
type: 'ngModule'
});
project.removeSourceFile(sourceFile);
});
});

Просмотреть файл

@ -0,0 +1,91 @@
import { ClassDeclaration, Node, SyntaxKind, SourceFile } from 'ts-morph';
import MezzuriteComponent from '../../../models/MezzuriteComponent';
function generateNgModule (filePath: string, sourceFile: SourceFile): MezzuriteComponent {
let component = null;
if (filePath != null && sourceFile != null) {
// TODO: Handle multiple classes in a file.
const sourceClass = sourceFile.getClasses()[0];
const hasAngularPerfModule = containsAngularPerfImport(sourceClass);
const hasImport = sourceFile.getImportDeclaration('@microsoft/mezzurite-angular') != null;
const hasRoutingServiceStart = containsRoutingServiceStart(sourceClass);
const name = sourceClass.getName();
component = {
checks: {
hasAngularPerfModule,
hasImport,
hasRoutingServiceStart
},
filePath,
name,
type: 'ngModule'
};
}
return component;
}
function containsAngularPerfImport (sourceClass: ClassDeclaration): boolean {
const ngModuleDecorator = sourceClass.getDecorator('NgModule');
let containsAngularPerfImport = false;
if (ngModuleDecorator != null) {
const decoratorArgument = ngModuleDecorator.getArguments().find((child: Node) => {
return child.getKind() === SyntaxKind.ObjectLiteralExpression;
});
if (decoratorArgument != null) {
const decoratorArgumentParameters = decoratorArgument.getFirstChildByKind(SyntaxKind.SyntaxList).getChildren();
const importNode = decoratorArgumentParameters.find((argument: Node) => {
const isPropertyAssignment = argument.getKind() === SyntaxKind.PropertyAssignment;
let hasImports = false;
if (isPropertyAssignment) {
const identifier = argument.getFirstChildByKind(SyntaxKind.Identifier);
if (identifier.getText() === 'imports') {
hasImports = true;
}
}
return hasImports;
});
if (importNode != null) {
const imports = importNode.getFirstChildByKind(SyntaxKind.ArrayLiteralExpression);
containsAngularPerfImport = imports.getText().indexOf('AngularPerfModule.forRoot()') > -1;
}
}
}
return containsAngularPerfImport;
}
function containsRoutingServiceStart (sourceClass: ClassDeclaration): boolean {
let containsRoutingServiceStart = false;
const constructors = sourceClass.getConstructors();
if (constructors.length > 0) {
// TODO: How do you handle multiple constructors?
const constructorParameters = constructors[0].getFirstChildByKind(SyntaxKind.Parameter);
if (constructorParameters != null) {
const routingServiceType = constructorParameters.getFirstChildByKind(SyntaxKind.TypeQuery);
if (constructorParameters != null) {
if (routingServiceType.getText().indexOf('RoutingService') > -1) {
const constructorBlock = constructors[0].getFirstChildByKind(SyntaxKind.Block);
// TODO: Make this checking more robust.
containsRoutingServiceStart = constructorBlock.getChildren().find((child: Node) => {
return child.getText().indexOf('start()') > -1;
}) != null;
}
}
}
}
return containsRoutingServiceStart;
}
export default generateNgModule;

Просмотреть файл

@ -1,5 +1,7 @@
import generateNgComponent from './generateNgComponent'; import generateNgComponent from './generateNgComponent';
import generateNgModule from './generateNgModule';
export default { export default {
generateNgComponent generateNgComponent,
generateNgModule
}; };

Просмотреть файл

@ -42,4 +42,10 @@ describe('index.ts', () => {
helpers.generateNgComponent = jest.fn(() => component); helpers.generateNgComponent = jest.fn(() => component);
expect(generateComponent('ngComponent', 'filePath', sourceFile)).toMatchObject(component); expect(generateComponent('ngComponent', 'filePath', sourceFile)).toMatchObject(component);
}); });
it('should return the component when generateNgModule is not null', () => {
helpers.generateNgModule = jest.fn(() => component);
expect(generateComponent('ngModule', 'filePath', sourceFile)).toMatchObject(component);
});
}); });

Просмотреть файл

@ -9,6 +9,8 @@ function generateComponent (componentType: string, filePath: string, sourceFile:
if (componentType != null && filePath != null && sourceFile != null) { if (componentType != null && filePath != null && sourceFile != null) {
if (componentType === 'ngComponent') { if (componentType === 'ngComponent') {
component = helpers.generateNgComponent(filePath, sourceFile); component = helpers.generateNgComponent(filePath, sourceFile);
} else if (componentType === 'ngModule') {
component = helpers.generateNgModule(filePath, sourceFile);
} }
} }