64d16dee02
The "enableIvy" compiler option is the initial implementation of the Render3 (or Ivy) code generation. This commit enables generation generating "Hello, World" (example in the test) but not much else. It is currenly only useful for internal Ivy testing as Ivy is in development. PR Close #21427
164 lines
5.7 KiB
TypeScript
164 lines
5.7 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
|
|
import {PartialModule} from '@angular/compiler';
|
|
import * as o from '@angular/compiler/src/output/output_ast';
|
|
import {MockAotCompilerHost} from 'compiler/test/aot/test_util';
|
|
import {initDomAdapter} from 'platform-browser/src/browser';
|
|
import * as ts from 'typescript';
|
|
|
|
import {getAngularClassTransformerFactory} from '../../src/transformers/r3_transform';
|
|
import {Directory, MockAotContext, MockCompilerHost} from '../mocks';
|
|
|
|
const someGenFilePath = '/somePackage/someGenFile';
|
|
const someGenFileName = someGenFilePath + '.ts';
|
|
|
|
describe('r3_transform_spec', () => {
|
|
let context: MockAotContext;
|
|
let host: MockCompilerHost;
|
|
|
|
beforeEach(() => {
|
|
context = new MockAotContext('/', FILES);
|
|
host = new MockCompilerHost(context);
|
|
});
|
|
|
|
it('should be able to generate a simple identity function', () => {
|
|
expect(emitStaticMethod(new o.ReturnStatement(o.variable('v')), ['v']))
|
|
.toContain('static someMethod(v) { return v; }');
|
|
});
|
|
|
|
it('should be able to generate a static field declaration',
|
|
() => { expect(emitStaticField(o.literal(10))).toContain('SomeClass.someField = 10'); });
|
|
|
|
it('should be able to import a symbol', () => {
|
|
expect(emitStaticMethod(new o.ReturnStatement(
|
|
o.importExpr(new o.ExternalReference('@angular/core', 'Component')))))
|
|
.toContain('static someMethod() { return i0.Component; } }');
|
|
});
|
|
|
|
it('should be able to modify multiple classes in the same module', () => {
|
|
const result = emit(getAngularClassTransformerFactory([{
|
|
fileName: someGenFileName,
|
|
statements: [
|
|
classMethod(new o.ReturnStatement(o.variable('v')), ['v'], 'someMethod', 'SomeClass'),
|
|
classMethod(
|
|
new o.ReturnStatement(o.variable('v')), ['v'], 'someOtherMethod', 'SomeOtherClass')
|
|
]
|
|
}]));
|
|
expect(result).toContain('static someMethod(v) { return v; }');
|
|
expect(result).toContain('static someOtherMethod(v) { return v; }');
|
|
});
|
|
|
|
it('should insert imports after existing imports', () => {
|
|
context = context.override({
|
|
somePackage: {
|
|
'someGenFile.ts': `
|
|
import {Component} from '@angular/core';
|
|
|
|
@Component({selector: 'some-class', template: 'hello!'})
|
|
export class SomeClass {}
|
|
|
|
export class SomeOtherClass {}
|
|
`
|
|
}
|
|
});
|
|
host = new MockCompilerHost(context);
|
|
|
|
expect(emitStaticMethod(new o.ReturnStatement(
|
|
o.importExpr(new o.ExternalReference('@angular/core', 'Component')))))
|
|
.toContain('const core_1 = require("@angular/core"); const i0 = require("@angular/core");');
|
|
});
|
|
|
|
function emit(factory: ts.TransformerFactory<ts.SourceFile>): string {
|
|
let result: string = '';
|
|
const program = ts.createProgram(
|
|
[someGenFileName], {module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2017}, host);
|
|
const moduleSourceFile = program.getSourceFile(someGenFileName);
|
|
const transformers: ts.CustomTransformers = {before: [factory]};
|
|
const emitResult = program.emit(
|
|
moduleSourceFile, (fileName, data, writeByteOrderMark, onError, sourceFiles) => {
|
|
if (fileName.startsWith(someGenFilePath)) {
|
|
result = data;
|
|
}
|
|
}, undefined, undefined, transformers);
|
|
return normalizeResult(result);
|
|
}
|
|
|
|
function emitStaticMethod(
|
|
stmt: o.Statement | o.Statement[], parameters: string[] = [],
|
|
methodName: string = 'someMethod', className: string = 'SomeClass'): string {
|
|
const module: PartialModule = {
|
|
fileName: someGenFileName,
|
|
statements: [classMethod(stmt, parameters, methodName, className)]
|
|
};
|
|
return emit(getAngularClassTransformerFactory([module]));
|
|
}
|
|
|
|
function emitStaticField(
|
|
initializer: o.Expression, fieldName: string = 'someField',
|
|
className: string = 'SomeClass'): string {
|
|
const module: PartialModule = {
|
|
fileName: someGenFileName,
|
|
statements: [classField(initializer, fieldName, className)]
|
|
};
|
|
return emit(getAngularClassTransformerFactory([module]));
|
|
}
|
|
});
|
|
|
|
const FILES: Directory = {
|
|
somePackage: {
|
|
'someGenFile.ts': `
|
|
|
|
export class SomeClass {}
|
|
|
|
export class SomeOtherClass {}
|
|
`
|
|
}
|
|
};
|
|
|
|
function classMethod(
|
|
stmt: o.Statement | o.Statement[], parameters: string[] = [], methodName: string = 'someMethod',
|
|
className: string = 'SomeClass'): o.ClassStmt {
|
|
const statements = Array.isArray(stmt) ? stmt : [stmt];
|
|
return new o.ClassStmt(
|
|
/* name */ className,
|
|
/* parent */ null,
|
|
/* fields */[],
|
|
/* getters */[],
|
|
/* constructorMethod */ new o.ClassMethod(null, [], []),
|
|
/* methods */[new o.ClassMethod(
|
|
methodName, parameters.map(name => new o.FnParam(name)), statements, null,
|
|
[o.StmtModifier.Static])]);
|
|
}
|
|
|
|
function classField(
|
|
initializer: o.Expression, fieldName: string = 'someField',
|
|
className: string = 'SomeClass'): o.ClassStmt {
|
|
return new o.ClassStmt(
|
|
/* name */ className,
|
|
/* parent */ null,
|
|
/* fields */[new o.ClassField(fieldName, null, [o.StmtModifier.Static], initializer)],
|
|
/* getters */[],
|
|
/* constructorMethod */ new o.ClassMethod(null, [], []),
|
|
/* methods */[]);
|
|
}
|
|
|
|
function normalizeResult(result: string): string {
|
|
// Remove TypeScript prefixes
|
|
// Remove new lines
|
|
// Squish adjacent spaces
|
|
// Remove prefix and postfix spaces
|
|
return result.replace('"use strict";', ' ')
|
|
.replace('exports.__esModule = true;', ' ')
|
|
.replace('Object.defineProperty(exports, "__esModule", { value: true });', ' ')
|
|
.replace(/\n/g, ' ')
|
|
.replace(/ +/g, ' ')
|
|
.replace(/^ /g, '')
|
|
.replace(/ $/g, '');
|
|
}
|