parent
7fec1771fc
commit
f6aa60c03c
|
@ -9,7 +9,7 @@ import MagicString from 'magic-string';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
import {PathSegment, AbsoluteFsPath} from '../../../src/ngtsc/path';
|
import {PathSegment, AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||||
import {isDtsPath} from '../../../src/ngtsc/util/src/typescript';
|
import {isDtsPath} from '../../../src/ngtsc/util/src/typescript';
|
||||||
import {Import} from '../../../src/ngtsc/translator';
|
import {Import, ImportManager} from '../../../src/ngtsc/translator';
|
||||||
import {CompiledClass} from '../analysis/decoration_analyzer';
|
import {CompiledClass} from '../analysis/decoration_analyzer';
|
||||||
import {ExportInfo} from '../analysis/private_declarations_analyzer';
|
import {ExportInfo} from '../analysis/private_declarations_analyzer';
|
||||||
import {FileSystem} from '../file_system/file_system';
|
import {FileSystem} from '../file_system/file_system';
|
||||||
|
@ -35,7 +35,9 @@ export class EsmRenderer extends Renderer {
|
||||||
output.appendLeft(insertionPoint, renderedImports);
|
output.appendLeft(insertionPoint, renderedImports);
|
||||||
}
|
}
|
||||||
|
|
||||||
addExports(output: MagicString, entryPointBasePath: AbsoluteFsPath, exports: ExportInfo[]): void {
|
addExports(
|
||||||
|
output: MagicString, entryPointBasePath: AbsoluteFsPath, exports: ExportInfo[],
|
||||||
|
importManager: ImportManager, file: ts.SourceFile): void {
|
||||||
exports.forEach(e => {
|
exports.forEach(e => {
|
||||||
let exportFrom = '';
|
let exportFrom = '';
|
||||||
const isDtsFile = isDtsPath(entryPointBasePath);
|
const isDtsFile = isDtsPath(entryPointBasePath);
|
||||||
|
|
|
@ -127,6 +127,7 @@ export abstract class Renderer {
|
||||||
sourceFile: ts.SourceFile, compiledFile: CompiledFile|undefined,
|
sourceFile: ts.SourceFile, compiledFile: CompiledFile|undefined,
|
||||||
switchMarkerAnalysis: SwitchMarkerAnalysis|undefined,
|
switchMarkerAnalysis: SwitchMarkerAnalysis|undefined,
|
||||||
privateDeclarationsAnalyses: PrivateDeclarationsAnalyses): FileInfo[] {
|
privateDeclarationsAnalyses: PrivateDeclarationsAnalyses): FileInfo[] {
|
||||||
|
const isEntryPoint = sourceFile === this.bundle.src.file;
|
||||||
const input = this.extractSourceMap(sourceFile);
|
const input = this.extractSourceMap(sourceFile);
|
||||||
const outputText = new MagicString(input.source);
|
const outputText = new MagicString(input.source);
|
||||||
|
|
||||||
|
@ -135,11 +136,11 @@ export abstract class Renderer {
|
||||||
outputText, switchMarkerAnalysis.sourceFile, switchMarkerAnalysis.declarations);
|
outputText, switchMarkerAnalysis.sourceFile, switchMarkerAnalysis.declarations);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compiledFile) {
|
|
||||||
const importManager = new ImportManager(
|
const importManager = new ImportManager(
|
||||||
this.getImportRewriter(this.bundle.src.r3SymbolsFile, this.bundle.isFlatCore),
|
this.getImportRewriter(this.bundle.src.r3SymbolsFile, this.bundle.isFlatCore),
|
||||||
IMPORT_PREFIX);
|
IMPORT_PREFIX);
|
||||||
|
|
||||||
|
if (compiledFile) {
|
||||||
// TODO: remove constructor param metadata and property decorators (we need info from the
|
// TODO: remove constructor param metadata and property decorators (we need info from the
|
||||||
// handlers to do this)
|
// handlers to do this)
|
||||||
const decoratorsToRemove = this.computeDecoratorsToRemove(compiledFile.compiledClasses);
|
const decoratorsToRemove = this.computeDecoratorsToRemove(compiledFile.compiledClasses);
|
||||||
|
@ -154,19 +155,24 @@ export abstract class Renderer {
|
||||||
outputText,
|
outputText,
|
||||||
renderConstantPool(compiledFile.sourceFile, compiledFile.constantPool, importManager),
|
renderConstantPool(compiledFile.sourceFile, compiledFile.constantPool, importManager),
|
||||||
compiledFile.sourceFile);
|
compiledFile.sourceFile);
|
||||||
|
|
||||||
this.addImports(
|
|
||||||
outputText, importManager.getAllImports(compiledFile.sourceFile.fileName),
|
|
||||||
compiledFile.sourceFile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add exports to the entry-point file
|
// Add exports to the entry-point file
|
||||||
if (sourceFile === this.bundle.src.file) {
|
if (isEntryPoint) {
|
||||||
const entryPointBasePath = stripExtension(this.bundle.src.path);
|
const entryPointBasePath = stripExtension(this.bundle.src.path);
|
||||||
this.addExports(outputText, entryPointBasePath, privateDeclarationsAnalyses);
|
this.addExports(
|
||||||
|
outputText, entryPointBasePath, privateDeclarationsAnalyses, importManager, sourceFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isEntryPoint || compiledFile) {
|
||||||
|
this.addImports(outputText, importManager.getAllImports(sourceFile.fileName), sourceFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compiledFile || switchMarkerAnalysis || isEntryPoint) {
|
||||||
return this.renderSourceAndMap(sourceFile, input, outputText);
|
return this.renderSourceAndMap(sourceFile, input, outputText);
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderDtsFile(dtsFile: ts.SourceFile, renderInfo: DtsRenderInfo): FileInfo[] {
|
renderDtsFile(dtsFile: ts.SourceFile, renderInfo: DtsRenderInfo): FileInfo[] {
|
||||||
|
@ -189,7 +195,9 @@ export abstract class Renderer {
|
||||||
this.addModuleWithProvidersParams(outputText, renderInfo.moduleWithProviders, importManager);
|
this.addModuleWithProvidersParams(outputText, renderInfo.moduleWithProviders, importManager);
|
||||||
this.addImports(outputText, importManager.getAllImports(dtsFile.fileName), dtsFile);
|
this.addImports(outputText, importManager.getAllImports(dtsFile.fileName), dtsFile);
|
||||||
|
|
||||||
this.addExports(outputText, AbsoluteFsPath.fromSourceFile(dtsFile), renderInfo.privateExports);
|
this.addExports(
|
||||||
|
outputText, AbsoluteFsPath.fromSourceFile(dtsFile), renderInfo.privateExports,
|
||||||
|
importManager, dtsFile);
|
||||||
|
|
||||||
|
|
||||||
return this.renderSourceAndMap(dtsFile, input, outputText);
|
return this.renderSourceAndMap(dtsFile, input, outputText);
|
||||||
|
@ -251,7 +259,8 @@ export abstract class Renderer {
|
||||||
void;
|
void;
|
||||||
protected abstract addImports(output: MagicString, imports: Import[], sf: ts.SourceFile): void;
|
protected abstract addImports(output: MagicString, imports: Import[], sf: ts.SourceFile): void;
|
||||||
protected abstract addExports(
|
protected abstract addExports(
|
||||||
output: MagicString, entryPointBasePath: AbsoluteFsPath, exports: ExportInfo[]): void;
|
output: MagicString, entryPointBasePath: AbsoluteFsPath, exports: ExportInfo[],
|
||||||
|
importManager: ImportManager, file: ts.SourceFile): void;
|
||||||
protected abstract addDefinitions(
|
protected abstract addDefinitions(
|
||||||
output: MagicString, compiledClass: CompiledClass, definitions: string): void;
|
output: MagicString, compiledClass: CompiledClass, definitions: string): void;
|
||||||
protected abstract removeDecorators(
|
protected abstract removeDecorators(
|
||||||
|
|
|
@ -0,0 +1,207 @@
|
||||||
|
/**
|
||||||
|
* @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 {dirname, relative} from 'canonical-path';
|
||||||
|
import * as ts from 'typescript';
|
||||||
|
import MagicString from 'magic-string';
|
||||||
|
import {Import, ImportManager} from '../../../src/ngtsc/translator';
|
||||||
|
import {ExportInfo} from '../analysis/private_declarations_analyzer';
|
||||||
|
import {FileSystem} from '../file_system/file_system';
|
||||||
|
import {UmdReflectionHost} from '../host/umd_host';
|
||||||
|
import {Logger} from '../logging/logger';
|
||||||
|
import {EntryPointBundle} from '../packages/entry_point_bundle';
|
||||||
|
import {Esm5Renderer} from './esm5_renderer';
|
||||||
|
import {stripExtension} from './renderer';
|
||||||
|
|
||||||
|
type CommonJsConditional = ts.ConditionalExpression & {whenTrue: ts.CallExpression};
|
||||||
|
type AmdConditional = ts.ConditionalExpression & {whenTrue: ts.CallExpression};
|
||||||
|
|
||||||
|
export class UmdRenderer extends Esm5Renderer {
|
||||||
|
constructor(
|
||||||
|
fs: FileSystem, logger: Logger, protected umdHost: UmdReflectionHost, isCore: boolean,
|
||||||
|
bundle: EntryPointBundle) {
|
||||||
|
super(fs, logger, umdHost, isCore, bundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the imports at the top of the file
|
||||||
|
*/
|
||||||
|
addImports(output: MagicString, imports: Import[], file: ts.SourceFile): void {
|
||||||
|
// Assume there is only one UMD module in the file
|
||||||
|
const umdModule = this.umdHost.getUmdModule(file);
|
||||||
|
if (!umdModule) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapperFunction = umdModule.wrapperFn;
|
||||||
|
|
||||||
|
// We need to add new `require()` calls for each import in the CommonJS initializer
|
||||||
|
renderCommonJsDependencies(output, wrapperFunction, imports);
|
||||||
|
renderAmdDependencies(output, wrapperFunction, imports);
|
||||||
|
renderGlobalDependencies(output, wrapperFunction, imports);
|
||||||
|
renderFactoryParameters(output, wrapperFunction, imports);
|
||||||
|
}
|
||||||
|
|
||||||
|
addExports(
|
||||||
|
output: MagicString, entryPointBasePath: string, exports: ExportInfo[],
|
||||||
|
importManager: ImportManager, file: ts.SourceFile): void {
|
||||||
|
const umdModule = this.umdHost.getUmdModule(file);
|
||||||
|
if (!umdModule) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const factoryFunction = umdModule.factoryFn;
|
||||||
|
const lastStatement =
|
||||||
|
factoryFunction.body.statements[factoryFunction.body.statements.length - 1];
|
||||||
|
const insertionPoint =
|
||||||
|
lastStatement ? lastStatement.getEnd() : factoryFunction.body.getEnd() - 1;
|
||||||
|
exports.forEach(e => {
|
||||||
|
const basePath = stripExtension(e.from);
|
||||||
|
const relativePath = './' + relative(dirname(entryPointBasePath), basePath);
|
||||||
|
const namedImport = entryPointBasePath !== basePath ?
|
||||||
|
importManager.generateNamedImport(relativePath, e.identifier) :
|
||||||
|
{symbol: e.identifier, moduleImport: null};
|
||||||
|
const importNamespace = namedImport.moduleImport ? `${namedImport.moduleImport}.` : '';
|
||||||
|
const exportStr = `\nexports.${e.identifier} = ${importNamespace}${namedImport.symbol};`;
|
||||||
|
output.appendRight(insertionPoint, exportStr);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addConstants(output: MagicString, constants: string, file: ts.SourceFile): void {
|
||||||
|
if (constants === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const umdModule = this.umdHost.getUmdModule(file);
|
||||||
|
if (!umdModule) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const factoryFunction = umdModule.factoryFn;
|
||||||
|
const firstStatement = factoryFunction.body.statements[0];
|
||||||
|
const insertionPoint =
|
||||||
|
firstStatement ? firstStatement.getStart() : factoryFunction.body.getStart() + 1;
|
||||||
|
output.appendLeft(insertionPoint, '\n' + constants + '\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderCommonJsDependencies(
|
||||||
|
output: MagicString, wrapperFunction: ts.FunctionExpression, imports: Import[]) {
|
||||||
|
const conditional = find(wrapperFunction.body.statements[0], isCommonJSConditional);
|
||||||
|
if (!conditional) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const factoryCall = conditional.whenTrue;
|
||||||
|
const injectionPoint = factoryCall.getEnd() -
|
||||||
|
1; // Backup one char to account for the closing parenthesis on the call
|
||||||
|
imports.forEach(i => output.appendLeft(injectionPoint, `,require('${i.specifier}')`));
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderAmdDependencies(
|
||||||
|
output: MagicString, wrapperFunction: ts.FunctionExpression, imports: Import[]) {
|
||||||
|
const conditional = find(wrapperFunction.body.statements[0], isAmdConditional);
|
||||||
|
if (!conditional) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const dependencyArray = conditional.whenTrue.arguments[1];
|
||||||
|
if (!dependencyArray || !ts.isArrayLiteralExpression(dependencyArray)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const injectionPoint = dependencyArray.getEnd() -
|
||||||
|
1; // Backup one char to account for the closing square bracket on the array
|
||||||
|
imports.forEach(i => output.appendLeft(injectionPoint, `,'${i.specifier}'`));
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderGlobalDependencies(
|
||||||
|
output: MagicString, wrapperFunction: ts.FunctionExpression, imports: Import[]) {
|
||||||
|
const globalFactoryCall = find(wrapperFunction.body.statements[0], isGlobalFactoryCall);
|
||||||
|
if (!globalFactoryCall) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const injectionPoint = globalFactoryCall.getEnd() -
|
||||||
|
1; // Backup one char to account for the closing parenthesis on the call
|
||||||
|
imports.forEach(i => output.appendLeft(injectionPoint, `,global.${getGlobalIdentifier(i)}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderFactoryParameters(
|
||||||
|
output: MagicString, wrapperFunction: ts.FunctionExpression, imports: Import[]) {
|
||||||
|
const wrapperCall = wrapperFunction.parent as ts.CallExpression;
|
||||||
|
const secondArgument = wrapperCall.arguments[1];
|
||||||
|
if (!secondArgument) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Be resilient to the factory being inside parentheses
|
||||||
|
const factoryFunction =
|
||||||
|
ts.isParenthesizedExpression(secondArgument) ? secondArgument.expression : secondArgument;
|
||||||
|
if (!ts.isFunctionExpression(factoryFunction)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const parameters = factoryFunction.parameters;
|
||||||
|
const injectionPoint = parameters[parameters.length - 1].getEnd();
|
||||||
|
imports.forEach(i => output.appendLeft(injectionPoint, `,${i.qualifier}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCommonJSConditional(value: ts.Node): value is CommonJsConditional {
|
||||||
|
if (!ts.isConditionalExpression(value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!ts.isBinaryExpression(value.condition) ||
|
||||||
|
value.condition.operatorToken.kind !== ts.SyntaxKind.AmpersandAmpersandToken) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!oneOfBinaryConditions(value.condition, (exp) => isTypeOf(exp, 'exports', 'module'))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!ts.isCallExpression(value.whenTrue) || !ts.isIdentifier(value.whenTrue.expression)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return value.whenTrue.expression.text === 'factory';
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAmdConditional(value: ts.Node): value is AmdConditional {
|
||||||
|
if (!ts.isConditionalExpression(value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!ts.isBinaryExpression(value.condition) ||
|
||||||
|
value.condition.operatorToken.kind !== ts.SyntaxKind.AmpersandAmpersandToken) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!oneOfBinaryConditions(value.condition, (exp) => isTypeOf(exp, 'define'))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!ts.isCallExpression(value.whenTrue) || !ts.isIdentifier(value.whenTrue.expression)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return value.whenTrue.expression.text === 'define';
|
||||||
|
}
|
||||||
|
|
||||||
|
function isGlobalFactoryCall(value: ts.Node): value is ts.CallExpression {
|
||||||
|
if (ts.isCallExpression(value) && !!value.parent) {
|
||||||
|
// Be resilient to the value being inside parentheses
|
||||||
|
const expression = ts.isParenthesizedExpression(value.parent) ? value.parent : value;
|
||||||
|
return !!expression.parent && ts.isConditionalExpression(expression.parent) &&
|
||||||
|
expression.parent.whenFalse === expression;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGlobalIdentifier(i: Import) {
|
||||||
|
return i.specifier.replace('@angular/', 'ng.').replace(/^\//, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function find<T>(node: ts.Node, test: (node: ts.Node) => node is ts.Node & T): T|undefined {
|
||||||
|
return test(node) ? node : node.forEachChild(child => find<T>(child, test));
|
||||||
|
}
|
||||||
|
|
||||||
|
function oneOfBinaryConditions(
|
||||||
|
node: ts.BinaryExpression, test: (expression: ts.Expression) => boolean) {
|
||||||
|
return test(node.left) || test(node.right);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isTypeOf(node: ts.Expression, ...types: string[]): boolean {
|
||||||
|
return ts.isBinaryExpression(node) && ts.isTypeOfExpression(node.left) &&
|
||||||
|
ts.isIdentifier(node.left.expression) && types.indexOf(node.left.expression.text) !== -1;
|
||||||
|
}
|
|
@ -7,10 +7,13 @@
|
||||||
*/
|
*/
|
||||||
import MagicString from 'magic-string';
|
import MagicString from 'magic-string';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
import {NoopImportRewriter} from '../../../src/ngtsc/imports';
|
||||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||||
|
import {ImportManager} from '../../../src/ngtsc/translator';
|
||||||
import {DecorationAnalyzer} from '../../src/analysis/decoration_analyzer';
|
import {DecorationAnalyzer} from '../../src/analysis/decoration_analyzer';
|
||||||
import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry';
|
import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry';
|
||||||
import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer';
|
import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer';
|
||||||
|
import {IMPORT_PREFIX} from '../../src/constants';
|
||||||
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
|
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
|
||||||
import {EsmRenderer} from '../../src/rendering/esm_renderer';
|
import {EsmRenderer} from '../../src/rendering/esm_renderer';
|
||||||
import {makeTestEntryPointBundle} from '../helpers/utils';
|
import {makeTestEntryPointBundle} from '../helpers/utils';
|
||||||
|
@ -32,10 +35,11 @@ function setup(file: {name: AbsoluteFsPath, contents: string}) {
|
||||||
.analyzeProgram();
|
.analyzeProgram();
|
||||||
const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host).analyzeProgram(bundle.src.program);
|
const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host).analyzeProgram(bundle.src.program);
|
||||||
const renderer = new EsmRenderer(fs, logger, host, false, bundle);
|
const renderer = new EsmRenderer(fs, logger, host, false, bundle);
|
||||||
|
const importManager = new ImportManager(new NoopImportRewriter(), IMPORT_PREFIX);
|
||||||
return {
|
return {
|
||||||
host,
|
host,
|
||||||
program: bundle.src.program,
|
program: bundle.src.program,
|
||||||
sourceFile: bundle.src.file, renderer, decorationAnalyses, switchMarkerAnalyses
|
sourceFile: bundle.src.file, renderer, decorationAnalyses, switchMarkerAnalyses, importManager,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,14 +140,17 @@ import * as i1 from '@angular/common';`);
|
||||||
|
|
||||||
describe('addExports', () => {
|
describe('addExports', () => {
|
||||||
it('should insert the given exports at the end of the source file', () => {
|
it('should insert the given exports at the end of the source file', () => {
|
||||||
const {renderer} = setup(PROGRAM);
|
const {importManager, renderer, sourceFile} = setup(PROGRAM);
|
||||||
const output = new MagicString(PROGRAM.contents);
|
const output = new MagicString(PROGRAM.contents);
|
||||||
renderer.addExports(output, _(PROGRAM.name.replace(/\.js$/, '')), [
|
renderer.addExports(
|
||||||
|
output, _(PROGRAM.name.replace(/\.js$/, '')),
|
||||||
|
[
|
||||||
{from: _('/some/a.js'), dtsFrom: _('/some/a.d.ts'), identifier: 'ComponentA1'},
|
{from: _('/some/a.js'), dtsFrom: _('/some/a.d.ts'), identifier: 'ComponentA1'},
|
||||||
{from: _('/some/a.js'), dtsFrom: _('/some/a.d.ts'), identifier: 'ComponentA2'},
|
{from: _('/some/a.js'), dtsFrom: _('/some/a.d.ts'), identifier: 'ComponentA2'},
|
||||||
{from: _('/some/foo/b.js'), dtsFrom: _('/some/foo/b.d.ts'), identifier: 'ComponentB'},
|
{from: _('/some/foo/b.js'), dtsFrom: _('/some/foo/b.d.ts'), identifier: 'ComponentB'},
|
||||||
{from: PROGRAM.name, dtsFrom: PROGRAM.name, identifier: 'TopLevelComponent'},
|
{from: PROGRAM.name, dtsFrom: PROGRAM.name, identifier: 'TopLevelComponent'},
|
||||||
]);
|
],
|
||||||
|
importManager, sourceFile);
|
||||||
expect(output.toString()).toContain(`
|
expect(output.toString()).toContain(`
|
||||||
// Some other content
|
// Some other content
|
||||||
export {ComponentA1} from './a';
|
export {ComponentA1} from './a';
|
||||||
|
@ -153,14 +160,17 @@ export {TopLevelComponent};`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not insert alias exports in js output', () => {
|
it('should not insert alias exports in js output', () => {
|
||||||
const {renderer} = setup(PROGRAM);
|
const {importManager, renderer, sourceFile} = setup(PROGRAM);
|
||||||
const output = new MagicString(PROGRAM.contents);
|
const output = new MagicString(PROGRAM.contents);
|
||||||
renderer.addExports(output, _(PROGRAM.name.replace(/\.js$/, '')), [
|
renderer.addExports(
|
||||||
|
output, _(PROGRAM.name.replace(/\.js$/, '')),
|
||||||
|
[
|
||||||
{from: _('/some/a.js'), alias: 'eComponentA1', identifier: 'ComponentA1'},
|
{from: _('/some/a.js'), alias: 'eComponentA1', identifier: 'ComponentA1'},
|
||||||
{from: _('/some/a.js'), alias: 'eComponentA2', identifier: 'ComponentA2'},
|
{from: _('/some/a.js'), alias: 'eComponentA2', identifier: 'ComponentA2'},
|
||||||
{from: _('/some/foo/b.js'), alias: 'eComponentB', identifier: 'ComponentB'},
|
{from: _('/some/foo/b.js'), alias: 'eComponentB', identifier: 'ComponentB'},
|
||||||
{from: PROGRAM.name, alias: 'eTopLevelComponent', identifier: 'TopLevelComponent'},
|
{from: PROGRAM.name, alias: 'eTopLevelComponent', identifier: 'TopLevelComponent'},
|
||||||
]);
|
],
|
||||||
|
importManager, sourceFile);
|
||||||
const outputString = output.toString();
|
const outputString = output.toString();
|
||||||
expect(outputString).not.toContain(`{eComponentA1 as ComponentA1}`);
|
expect(outputString).not.toContain(`{eComponentA1 as ComponentA1}`);
|
||||||
expect(outputString).not.toContain(`{eComponentB as ComponentB}`);
|
expect(outputString).not.toContain(`{eComponentB as ComponentB}`);
|
||||||
|
|
|
@ -7,10 +7,13 @@
|
||||||
*/
|
*/
|
||||||
import MagicString from 'magic-string';
|
import MagicString from 'magic-string';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
import {NoopImportRewriter} from '../../../src/ngtsc/imports';
|
||||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||||
|
import {ImportManager} from '../../../src/ngtsc/translator';
|
||||||
import {DecorationAnalyzer} from '../../src/analysis/decoration_analyzer';
|
import {DecorationAnalyzer} from '../../src/analysis/decoration_analyzer';
|
||||||
import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry';
|
import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry';
|
||||||
import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer';
|
import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer';
|
||||||
|
import {IMPORT_PREFIX} from '../../src/constants';
|
||||||
import {Esm5ReflectionHost} from '../../src/host/esm5_host';
|
import {Esm5ReflectionHost} from '../../src/host/esm5_host';
|
||||||
import {Esm5Renderer} from '../../src/rendering/esm5_renderer';
|
import {Esm5Renderer} from '../../src/rendering/esm5_renderer';
|
||||||
import {makeTestEntryPointBundle, getDeclaration} from '../helpers/utils';
|
import {makeTestEntryPointBundle, getDeclaration} from '../helpers/utils';
|
||||||
|
@ -33,10 +36,11 @@ function setup(file: {name: AbsoluteFsPath, contents: string}) {
|
||||||
.analyzeProgram();
|
.analyzeProgram();
|
||||||
const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host).analyzeProgram(bundle.src.program);
|
const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host).analyzeProgram(bundle.src.program);
|
||||||
const renderer = new Esm5Renderer(fs, logger, host, false, bundle);
|
const renderer = new Esm5Renderer(fs, logger, host, false, bundle);
|
||||||
|
const importManager = new ImportManager(new NoopImportRewriter(), IMPORT_PREFIX);
|
||||||
return {
|
return {
|
||||||
host,
|
host,
|
||||||
program: bundle.src.program,
|
program: bundle.src.program,
|
||||||
sourceFile: bundle.src.file, renderer, decorationAnalyses, switchMarkerAnalyses
|
sourceFile: bundle.src.file, renderer, decorationAnalyses, switchMarkerAnalyses, importManager
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,8 +91,8 @@ var BadIife = (function() {
|
||||||
var compileNgModuleFactory = compileNgModuleFactory__PRE_R3__;
|
var compileNgModuleFactory = compileNgModuleFactory__PRE_R3__;
|
||||||
var badlyFormattedVariable = __PRE_R3__badlyFormattedVariable;
|
var badlyFormattedVariable = __PRE_R3__badlyFormattedVariable;
|
||||||
function compileNgModuleFactory__PRE_R3__(injector, options, moduleType) {
|
function compileNgModuleFactory__PRE_R3__(injector, options, moduleType) {
|
||||||
const compilerFactory = injector.get(CompilerFactory);
|
var compilerFactory = injector.get(CompilerFactory);
|
||||||
const compiler = compilerFactory.createCompiler([options]);
|
var compiler = compilerFactory.createCompiler([options]);
|
||||||
return compiler.compileModuleAsync(moduleType);
|
return compiler.compileModuleAsync(moduleType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,14 +178,17 @@ import * as i1 from '@angular/common';`);
|
||||||
|
|
||||||
describe('addExports', () => {
|
describe('addExports', () => {
|
||||||
it('should insert the given exports at the end of the source file', () => {
|
it('should insert the given exports at the end of the source file', () => {
|
||||||
const {renderer} = setup(PROGRAM);
|
const {importManager, renderer, sourceFile} = setup(PROGRAM);
|
||||||
const output = new MagicString(PROGRAM.contents);
|
const output = new MagicString(PROGRAM.contents);
|
||||||
renderer.addExports(output, _(PROGRAM.name.replace(/\.js$/, '')), [
|
renderer.addExports(
|
||||||
|
output, _(PROGRAM.name.replace(/\.js$/, '')),
|
||||||
|
[
|
||||||
{from: _('/some/a.js'), dtsFrom: _('/some/a.d.ts'), identifier: 'ComponentA1'},
|
{from: _('/some/a.js'), dtsFrom: _('/some/a.d.ts'), identifier: 'ComponentA1'},
|
||||||
{from: _('/some/a.js'), dtsFrom: _('/some/a.d.ts'), identifier: 'ComponentA2'},
|
{from: _('/some/a.js'), dtsFrom: _('/some/a.d.ts'), identifier: 'ComponentA2'},
|
||||||
{from: _('/some/foo/b.js'), dtsFrom: _('/some/foo/b.d.ts'), identifier: 'ComponentB'},
|
{from: _('/some/foo/b.js'), dtsFrom: _('/some/foo/b.d.ts'), identifier: 'ComponentB'},
|
||||||
{from: PROGRAM.name, dtsFrom: PROGRAM.name, identifier: 'TopLevelComponent'},
|
{from: PROGRAM.name, dtsFrom: PROGRAM.name, identifier: 'TopLevelComponent'},
|
||||||
]);
|
],
|
||||||
|
importManager, sourceFile);
|
||||||
expect(output.toString()).toContain(`
|
expect(output.toString()).toContain(`
|
||||||
export {A, B, C, NoIife, BadIife};
|
export {A, B, C, NoIife, BadIife};
|
||||||
export {ComponentA1} from './a';
|
export {ComponentA1} from './a';
|
||||||
|
@ -191,14 +198,17 @@ export {TopLevelComponent};`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not insert alias exports in js output', () => {
|
it('should not insert alias exports in js output', () => {
|
||||||
const {renderer} = setup(PROGRAM);
|
const {importManager, renderer, sourceFile} = setup(PROGRAM);
|
||||||
const output = new MagicString(PROGRAM.contents);
|
const output = new MagicString(PROGRAM.contents);
|
||||||
renderer.addExports(output, _(PROGRAM.name.replace(/\.js$/, '')), [
|
renderer.addExports(
|
||||||
|
output, _(PROGRAM.name.replace(/\.js$/, '')),
|
||||||
|
[
|
||||||
{from: _('/some/a.js'), alias: _('eComponentA1'), identifier: 'ComponentA1'},
|
{from: _('/some/a.js'), alias: _('eComponentA1'), identifier: 'ComponentA1'},
|
||||||
{from: _('/some/a.js'), alias: _('eComponentA2'), identifier: 'ComponentA2'},
|
{from: _('/some/a.js'), alias: _('eComponentA2'), identifier: 'ComponentA2'},
|
||||||
{from: _('/some/foo/b.js'), alias: _('eComponentB'), identifier: 'ComponentB'},
|
{from: _('/some/foo/b.js'), alias: _('eComponentB'), identifier: 'ComponentB'},
|
||||||
{from: PROGRAM.name, alias: 'eTopLevelComponent', identifier: 'TopLevelComponent'},
|
{from: PROGRAM.name, alias: 'eTopLevelComponent', identifier: 'TopLevelComponent'},
|
||||||
]);
|
],
|
||||||
|
importManager, sourceFile);
|
||||||
const outputString = output.toString();
|
const outputString = output.toString();
|
||||||
expect(outputString).not.toContain(`{eComponentA1 as ComponentA1}`);
|
expect(outputString).not.toContain(`{eComponentA1 as ComponentA1}`);
|
||||||
expect(outputString).not.toContain(`{eComponentB as ComponentB}`);
|
expect(outputString).not.toContain(`{eComponentB as ComponentB}`);
|
||||||
|
@ -214,11 +224,11 @@ export {TopLevelComponent};`);
|
||||||
throw new Error(`Could not find source file`);
|
throw new Error(`Could not find source file`);
|
||||||
}
|
}
|
||||||
const output = new MagicString(PROGRAM.contents);
|
const output = new MagicString(PROGRAM.contents);
|
||||||
renderer.addConstants(output, 'const x = 3;', file);
|
renderer.addConstants(output, 'var x = 3;', file);
|
||||||
expect(output.toString()).toContain(`
|
expect(output.toString()).toContain(`
|
||||||
import {Directive} from '@angular/core';
|
import {Directive} from '@angular/core';
|
||||||
|
|
||||||
const x = 3;
|
var x = 3;
|
||||||
var A = (function() {`);
|
var A = (function() {`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -229,13 +239,13 @@ var A = (function() {`);
|
||||||
throw new Error(`Could not find source file`);
|
throw new Error(`Could not find source file`);
|
||||||
}
|
}
|
||||||
const output = new MagicString(PROGRAM.contents);
|
const output = new MagicString(PROGRAM.contents);
|
||||||
renderer.addConstants(output, 'const x = 3;', file);
|
renderer.addConstants(output, 'var x = 3;', file);
|
||||||
renderer.addImports(output, [{specifier: '@angular/core', qualifier: 'i0'}], file);
|
renderer.addImports(output, [{specifier: '@angular/core', qualifier: 'i0'}], file);
|
||||||
expect(output.toString()).toContain(`
|
expect(output.toString()).toContain(`
|
||||||
import {Directive} from '@angular/core';
|
import {Directive} from '@angular/core';
|
||||||
import * as i0 from '@angular/core';
|
import * as i0 from '@angular/core';
|
||||||
|
|
||||||
const x = 3;
|
var x = 3;
|
||||||
var A = (function() {`);
|
var A = (function() {`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -13,7 +13,7 @@ import {Import} from '../../../src/ngtsc/translator';
|
||||||
import {CompiledClass, DecorationAnalyzer} from '../../src/analysis/decoration_analyzer';
|
import {CompiledClass, DecorationAnalyzer} from '../../src/analysis/decoration_analyzer';
|
||||||
import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry';
|
import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry';
|
||||||
import {ModuleWithProvidersAnalyzer} from '../../src/analysis/module_with_providers_analyzer';
|
import {ModuleWithProvidersAnalyzer} from '../../src/analysis/module_with_providers_analyzer';
|
||||||
import {PrivateDeclarationsAnalyzer} from '../../src/analysis/private_declarations_analyzer';
|
import {PrivateDeclarationsAnalyzer, ExportInfo} from '../../src/analysis/private_declarations_analyzer';
|
||||||
import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer';
|
import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer';
|
||||||
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
|
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
|
||||||
import {RedundantDecoratorMap, Renderer} from '../../src/rendering/renderer';
|
import {RedundantDecoratorMap, Renderer} from '../../src/rendering/renderer';
|
||||||
|
@ -35,10 +35,7 @@ class TestRenderer extends Renderer {
|
||||||
addImports(output: MagicString, imports: Import[], sf: ts.SourceFile) {
|
addImports(output: MagicString, imports: Import[], sf: ts.SourceFile) {
|
||||||
output.prepend('\n// ADD IMPORTS\n');
|
output.prepend('\n// ADD IMPORTS\n');
|
||||||
}
|
}
|
||||||
addExports(output: MagicString, baseEntryPointPath: string, exports: {
|
addExports(output: MagicString, baseEntryPointPath: string, exports: ExportInfo[]) {
|
||||||
identifier: string,
|
|
||||||
from: string
|
|
||||||
}[]) {
|
|
||||||
output.prepend('\n// ADD EXPORTS\n');
|
output.prepend('\n// ADD EXPORTS\n');
|
||||||
}
|
}
|
||||||
addConstants(output: MagicString, constants: string, file: ts.SourceFile): void {
|
addConstants(output: MagicString, constants: string, file: ts.SourceFile): void {
|
||||||
|
@ -76,8 +73,10 @@ function createTestRenderer(
|
||||||
const privateDeclarationsAnalyses =
|
const privateDeclarationsAnalyses =
|
||||||
new PrivateDeclarationsAnalyzer(host, referencesRegistry).analyzeProgram(bundle.src.program);
|
new PrivateDeclarationsAnalyzer(host, referencesRegistry).analyzeProgram(bundle.src.program);
|
||||||
const renderer = new TestRenderer(fs, logger, host, isCore, bundle);
|
const renderer = new TestRenderer(fs, logger, host, isCore, bundle);
|
||||||
|
spyOn(renderer, 'addExports').and.callThrough();
|
||||||
spyOn(renderer, 'addImports').and.callThrough();
|
spyOn(renderer, 'addImports').and.callThrough();
|
||||||
spyOn(renderer, 'addDefinitions').and.callThrough();
|
spyOn(renderer, 'addDefinitions').and.callThrough();
|
||||||
|
spyOn(renderer, 'addConstants').and.callThrough();
|
||||||
spyOn(renderer, 'removeDecorators').and.callThrough();
|
spyOn(renderer, 'removeDecorators').and.callThrough();
|
||||||
|
|
||||||
return {renderer,
|
return {renderer,
|
||||||
|
@ -117,9 +116,17 @@ describe('Renderer', () => {
|
||||||
'sourcesContent': [INPUT_PROGRAM.contents]
|
'sourcesContent': [INPUT_PROGRAM.contents]
|
||||||
});
|
});
|
||||||
|
|
||||||
const RENDERED_CONTENTS =
|
const RENDERED_CONTENTS = `
|
||||||
`\n// ADD EXPORTS\n\n// ADD IMPORTS\n\n// ADD CONSTANTS\n\n// ADD DEFINITIONS\n\n// REMOVE DECORATORS\n` +
|
// ADD IMPORTS
|
||||||
INPUT_PROGRAM.contents;
|
|
||||||
|
// ADD EXPORTS
|
||||||
|
|
||||||
|
// ADD CONSTANTS
|
||||||
|
|
||||||
|
// ADD DEFINITIONS
|
||||||
|
|
||||||
|
// REMOVE DECORATORS
|
||||||
|
` + INPUT_PROGRAM.contents;
|
||||||
|
|
||||||
const OUTPUT_PROGRAM_MAP = fromObject({
|
const OUTPUT_PROGRAM_MAP = fromObject({
|
||||||
'version': 3,
|
'version': 3,
|
||||||
|
@ -240,6 +247,22 @@ describe('Renderer', () => {
|
||||||
expect(values[0][0].getText())
|
expect(values[0][0].getText())
|
||||||
.toEqual(`{ type: Directive, args: [{ selector: '[a]' }] }`);
|
.toEqual(`{ type: Directive, args: [{ selector: '[a]' }] }`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should call renderImports after other abstract methods', () => {
|
||||||
|
// This allows the other methods to add additional imports if necessary
|
||||||
|
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
|
moduleWithProvidersAnalyses} = createTestRenderer('test-package', [INPUT_PROGRAM]);
|
||||||
|
const addExportsSpy = renderer.addExports as jasmine.Spy;
|
||||||
|
const addDefinitionsSpy = renderer.addDefinitions as jasmine.Spy;
|
||||||
|
const addConstantsSpy = renderer.addConstants as jasmine.Spy;
|
||||||
|
const addImportsSpy = renderer.addImports as jasmine.Spy;
|
||||||
|
renderer.renderProgram(
|
||||||
|
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||||
|
moduleWithProvidersAnalyses);
|
||||||
|
expect(addExportsSpy).toHaveBeenCalledBefore(addImportsSpy);
|
||||||
|
expect(addDefinitionsSpy).toHaveBeenCalledBefore(addImportsSpy);
|
||||||
|
expect(addConstantsSpy).toHaveBeenCalledBefore(addImportsSpy);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('source map merging', () => {
|
describe('source map merging', () => {
|
||||||
|
@ -355,7 +378,7 @@ describe('Renderer', () => {
|
||||||
moduleWithProvidersAnalyses);
|
moduleWithProvidersAnalyses);
|
||||||
|
|
||||||
const typingsFile = result.find(f => f.path === '/typings/file.d.ts') !;
|
const typingsFile = result.find(f => f.path === '/typings/file.d.ts') !;
|
||||||
expect(typingsFile.contents).toContain(`// ADD IMPORTS\nexport declare class A`);
|
expect(typingsFile.contents).toContain(`\n// ADD IMPORTS\n`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render exports into typings files', () => {
|
it('should render exports into typings files', () => {
|
||||||
|
@ -372,8 +395,7 @@ describe('Renderer', () => {
|
||||||
moduleWithProvidersAnalyses);
|
moduleWithProvidersAnalyses);
|
||||||
|
|
||||||
const typingsFile = result.find(f => f.path === '/typings/file.d.ts') !;
|
const typingsFile = result.find(f => f.path === '/typings/file.d.ts') !;
|
||||||
expect(typingsFile.contents)
|
expect(typingsFile.contents).toContain(`\n// ADD EXPORTS\n`);
|
||||||
.toContain(`// ADD EXPORTS\n\n// ADD IMPORTS\nexport declare class A`);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fixup functions/methods that return ModuleWithProviders structures', () => {
|
it('should fixup functions/methods that return ModuleWithProviders structures', () => {
|
||||||
|
|
|
@ -0,0 +1,490 @@
|
||||||
|
/**
|
||||||
|
* @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 MagicString from 'magic-string';
|
||||||
|
import * as ts from 'typescript';
|
||||||
|
import {NoopImportRewriter} from '../../../src/ngtsc/imports';
|
||||||
|
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||||
|
import {DecorationAnalyzer} from '../../src/analysis/decoration_analyzer';
|
||||||
|
import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry';
|
||||||
|
import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer';
|
||||||
|
import {UmdReflectionHost} from '../../src/host/umd_host';
|
||||||
|
import {ImportManager} from '../../../src/ngtsc/translator';
|
||||||
|
import {UmdRenderer} from '../../src/rendering/umd_renderer';
|
||||||
|
import {MockFileSystem} from '../helpers/mock_file_system';
|
||||||
|
import {MockLogger} from '../helpers/mock_logger';
|
||||||
|
import {getDeclaration, makeTestEntryPointBundle, createFileSystemFromProgramFiles} from '../helpers/utils';
|
||||||
|
|
||||||
|
const _ = AbsoluteFsPath.fromUnchecked;
|
||||||
|
|
||||||
|
function setup(file: {name: string, contents: string}) {
|
||||||
|
const fs = new MockFileSystem(createFileSystemFromProgramFiles([file]));
|
||||||
|
const logger = new MockLogger();
|
||||||
|
const bundle = makeTestEntryPointBundle('esm5', 'esm5', false, [file]);
|
||||||
|
const src = bundle.src;
|
||||||
|
const typeChecker = src.program.getTypeChecker();
|
||||||
|
const host = new UmdReflectionHost(logger, false, src.program, src.host);
|
||||||
|
const referencesRegistry = new NgccReferencesRegistry(host);
|
||||||
|
const decorationAnalyses = new DecorationAnalyzer(
|
||||||
|
fs, src.program, src.options, src.host, typeChecker, host,
|
||||||
|
referencesRegistry, [AbsoluteFsPath.fromUnchecked('/')], false)
|
||||||
|
.analyzeProgram();
|
||||||
|
const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host).analyzeProgram(src.program);
|
||||||
|
const renderer = new UmdRenderer(fs, logger, host, false, bundle);
|
||||||
|
const importManager = new ImportManager(new NoopImportRewriter(), 'i');
|
||||||
|
return {
|
||||||
|
decorationAnalyses,
|
||||||
|
host,
|
||||||
|
importManager,
|
||||||
|
program: src.program, renderer,
|
||||||
|
sourceFile: src.file, switchMarkerAnalyses
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const PROGRAM = {
|
||||||
|
name: _('/some/file.js'),
|
||||||
|
contents: `
|
||||||
|
/* A copyright notice */
|
||||||
|
(function (global, factory) {
|
||||||
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports,require('some-side-effect'),require('/local-dep'),require('@angular/core')) :
|
||||||
|
typeof define === 'function' && define.amd ? define('file', ['exports','some-side-effect','/local-dep','@angular/core'], factory) :
|
||||||
|
(factory(global.file,global.someSideEffect,global.localDep,global.ng.core));
|
||||||
|
}(this, (function (exports,someSideEffect,localDep,core) {'use strict';
|
||||||
|
var A = (function() {
|
||||||
|
function A() {}
|
||||||
|
A.decorators = [
|
||||||
|
{ type: core.Directive, args: [{ selector: '[a]' }] },
|
||||||
|
{ type: OtherA }
|
||||||
|
];
|
||||||
|
A.prototype.ngDoCheck = function() {
|
||||||
|
//
|
||||||
|
};
|
||||||
|
return A;
|
||||||
|
}());
|
||||||
|
|
||||||
|
var B = (function() {
|
||||||
|
function B() {}
|
||||||
|
B.decorators = [
|
||||||
|
{ type: OtherB },
|
||||||
|
{ type: core.Directive, args: [{ selector: '[b]' }] }
|
||||||
|
];
|
||||||
|
return B;
|
||||||
|
}());
|
||||||
|
|
||||||
|
var C = (function() {
|
||||||
|
function C() {}
|
||||||
|
C.decorators = [
|
||||||
|
{ type: core.Directive, args: [{ selector: '[c]' }] },
|
||||||
|
];
|
||||||
|
return C;
|
||||||
|
}());
|
||||||
|
|
||||||
|
function NoIife() {}
|
||||||
|
|
||||||
|
var BadIife = (function() {
|
||||||
|
function BadIife() {}
|
||||||
|
BadIife.decorators = [
|
||||||
|
{ type: core.Directive, args: [{ selector: '[c]' }] },
|
||||||
|
];
|
||||||
|
}());
|
||||||
|
|
||||||
|
var compileNgModuleFactory = compileNgModuleFactory__PRE_R3__;
|
||||||
|
var badlyFormattedVariable = __PRE_R3__badlyFormattedVariable;
|
||||||
|
function compileNgModuleFactory__PRE_R3__(injector, options, moduleType) {
|
||||||
|
var compilerFactory = injector.get(CompilerFactory);
|
||||||
|
var compiler = compilerFactory.createCompiler([options]);
|
||||||
|
return compiler.compileModuleAsync(moduleType);
|
||||||
|
}
|
||||||
|
|
||||||
|
function compileNgModuleFactory__POST_R3__(injector, options, moduleType) {
|
||||||
|
ngDevMode && assertNgModuleType(moduleType);
|
||||||
|
return Promise.resolve(new R3NgModuleFactory(moduleType));
|
||||||
|
}
|
||||||
|
// Some other content
|
||||||
|
exports.A = A;
|
||||||
|
exports.B = B;
|
||||||
|
exports.C = C;
|
||||||
|
exports.NoIife = NoIife;
|
||||||
|
exports.BadIife = BadIife;
|
||||||
|
})));`,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const PROGRAM_DECORATE_HELPER = {
|
||||||
|
name: '/some/file.js',
|
||||||
|
contents: `
|
||||||
|
/* A copyright notice */
|
||||||
|
(function (global, factory) {
|
||||||
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports,require('tslib'),require('@angular/core')) :
|
||||||
|
typeof define === 'function' && define.amd ? define('file', ['exports','/tslib','@angular/core'], factory) :
|
||||||
|
(factory(global.file,global.tslib,global.ng.core));
|
||||||
|
}(this, (function (exports,tslib,core) {'use strict';
|
||||||
|
var OtherA = function () { return function (node) { }; };
|
||||||
|
var OtherB = function () { return function (node) { }; };
|
||||||
|
var A = /** @class */ (function () {
|
||||||
|
function A() {
|
||||||
|
}
|
||||||
|
A = tslib.__decorate([
|
||||||
|
core.Directive({ selector: '[a]' }),
|
||||||
|
OtherA()
|
||||||
|
], A);
|
||||||
|
return A;
|
||||||
|
}());
|
||||||
|
export { A };
|
||||||
|
var B = /** @class */ (function () {
|
||||||
|
function B() {
|
||||||
|
}
|
||||||
|
B = tslib.__decorate([
|
||||||
|
OtherB(),
|
||||||
|
core.Directive({ selector: '[b]' })
|
||||||
|
], B);
|
||||||
|
return B;
|
||||||
|
}());
|
||||||
|
export { B };
|
||||||
|
var C = /** @class */ (function () {
|
||||||
|
function C() {
|
||||||
|
}
|
||||||
|
C = tslib.__decorate([
|
||||||
|
core.Directive({ selector: '[c]' })
|
||||||
|
], C);
|
||||||
|
return C;
|
||||||
|
}());
|
||||||
|
export { C };
|
||||||
|
var D = /** @class */ (function () {
|
||||||
|
function D() {
|
||||||
|
}
|
||||||
|
D_1 = D;
|
||||||
|
var D_1;
|
||||||
|
D = D_1 = tslib.__decorate([
|
||||||
|
core.Directive({ selector: '[d]', providers: [D_1] })
|
||||||
|
], D);
|
||||||
|
return D;
|
||||||
|
}());
|
||||||
|
exports.D = D;
|
||||||
|
// Some other content
|
||||||
|
})));`
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('UmdRenderer', () => {
|
||||||
|
|
||||||
|
describe('addImports', () => {
|
||||||
|
it('should append the given imports into the CommonJS factory call', () => {
|
||||||
|
const {renderer, program} = setup(PROGRAM);
|
||||||
|
const file = program.getSourceFile('some/file.js') !;
|
||||||
|
const output = new MagicString(PROGRAM.contents);
|
||||||
|
renderer.addImports(
|
||||||
|
output,
|
||||||
|
[
|
||||||
|
{specifier: '@angular/core', qualifier: 'i0'},
|
||||||
|
{specifier: '@angular/common', qualifier: 'i1'}
|
||||||
|
],
|
||||||
|
file);
|
||||||
|
expect(output.toString())
|
||||||
|
.toContain(
|
||||||
|
`typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports,require('some-side-effect'),require('/local-dep'),require('@angular/core'),require('@angular/core'),require('@angular/common')) :`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should append the given imports into the AMD initialization', () => {
|
||||||
|
const {renderer, program} = setup(PROGRAM);
|
||||||
|
const file = program.getSourceFile('some/file.js') !;
|
||||||
|
const output = new MagicString(PROGRAM.contents);
|
||||||
|
renderer.addImports(
|
||||||
|
output,
|
||||||
|
[
|
||||||
|
{specifier: '@angular/core', qualifier: 'i0'},
|
||||||
|
{specifier: '@angular/common', qualifier: 'i1'}
|
||||||
|
],
|
||||||
|
file);
|
||||||
|
expect(output.toString())
|
||||||
|
.toContain(
|
||||||
|
`typeof define === 'function' && define.amd ? define('file', ['exports','some-side-effect','/local-dep','@angular/core','@angular/core','@angular/common'], factory) :`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should append the given imports into the global initialization', () => {
|
||||||
|
const {renderer, program} = setup(PROGRAM);
|
||||||
|
const file = program.getSourceFile('some/file.js') !;
|
||||||
|
const output = new MagicString(PROGRAM.contents);
|
||||||
|
renderer.addImports(
|
||||||
|
output,
|
||||||
|
[
|
||||||
|
{specifier: '@angular/core', qualifier: 'i0'},
|
||||||
|
{specifier: '@angular/common', qualifier: 'i1'}
|
||||||
|
],
|
||||||
|
file);
|
||||||
|
expect(output.toString())
|
||||||
|
.toContain(
|
||||||
|
`(factory(global.file,global.someSideEffect,global.localDep,global.ng.core,global.ng.core,global.ng.common));`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should append the given imports as parameters into the factory function definition', () => {
|
||||||
|
const {renderer, program} = setup(PROGRAM);
|
||||||
|
const file = program.getSourceFile('some/file.js') !;
|
||||||
|
const output = new MagicString(PROGRAM.contents);
|
||||||
|
renderer.addImports(
|
||||||
|
output,
|
||||||
|
[
|
||||||
|
{specifier: '@angular/core', qualifier: 'i0'},
|
||||||
|
{specifier: '@angular/common', qualifier: 'i1'}
|
||||||
|
],
|
||||||
|
file);
|
||||||
|
expect(output.toString())
|
||||||
|
.toContain(`(function (exports,someSideEffect,localDep,core,i0,i1) {'use strict';`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('addExports', () => {
|
||||||
|
it('should insert the given exports at the end of the source file', () => {
|
||||||
|
const {importManager, renderer, sourceFile} = setup(PROGRAM);
|
||||||
|
const output = new MagicString(PROGRAM.contents);
|
||||||
|
const generateNamedImportSpy = spyOn(importManager, 'generateNamedImport').and.callThrough();
|
||||||
|
renderer.addExports(
|
||||||
|
output, PROGRAM.name.replace(/\.js$/, ''),
|
||||||
|
[
|
||||||
|
{from: _('/some/a.js'), identifier: 'ComponentA1'},
|
||||||
|
{from: _('/some/a.js'), identifier: 'ComponentA2'},
|
||||||
|
{from: _('/some/foo/b.js'), identifier: 'ComponentB'},
|
||||||
|
{from: PROGRAM.name, identifier: 'TopLevelComponent'},
|
||||||
|
],
|
||||||
|
importManager, sourceFile);
|
||||||
|
|
||||||
|
expect(output.toString()).toContain(`
|
||||||
|
exports.A = A;
|
||||||
|
exports.B = B;
|
||||||
|
exports.C = C;
|
||||||
|
exports.NoIife = NoIife;
|
||||||
|
exports.BadIife = BadIife;
|
||||||
|
exports.ComponentA1 = i0.ComponentA1;
|
||||||
|
exports.ComponentA2 = i0.ComponentA2;
|
||||||
|
exports.ComponentB = i1.ComponentB;
|
||||||
|
exports.TopLevelComponent = TopLevelComponent;
|
||||||
|
})));`);
|
||||||
|
|
||||||
|
expect(generateNamedImportSpy).toHaveBeenCalledWith('./a', 'ComponentA1');
|
||||||
|
expect(generateNamedImportSpy).toHaveBeenCalledWith('./a', 'ComponentA2');
|
||||||
|
expect(generateNamedImportSpy).toHaveBeenCalledWith('./foo/b', 'ComponentB');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not insert alias exports in js output', () => {
|
||||||
|
const {importManager, renderer, sourceFile} = setup(PROGRAM);
|
||||||
|
const output = new MagicString(PROGRAM.contents);
|
||||||
|
renderer.addExports(
|
||||||
|
output, PROGRAM.name.replace(/\.js$/, ''),
|
||||||
|
[
|
||||||
|
{from: _('/some/a.js'), alias: 'eComponentA1', identifier: 'ComponentA1'},
|
||||||
|
{from: _('/some/a.js'), alias: 'eComponentA2', identifier: 'ComponentA2'},
|
||||||
|
{from: _('/some/foo/b.js'), alias: 'eComponentB', identifier: 'ComponentB'},
|
||||||
|
{from: PROGRAM.name, alias: 'eTopLevelComponent', identifier: 'TopLevelComponent'},
|
||||||
|
],
|
||||||
|
importManager, sourceFile);
|
||||||
|
const outputString = output.toString();
|
||||||
|
expect(outputString).not.toContain(`eComponentA1`);
|
||||||
|
expect(outputString).not.toContain(`eComponentB`);
|
||||||
|
expect(outputString).not.toContain(`eTopLevelComponent`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('addConstants', () => {
|
||||||
|
it('should insert the given constants after imports in the source file', () => {
|
||||||
|
const {renderer, program} = setup(PROGRAM);
|
||||||
|
const file = program.getSourceFile('some/file.js');
|
||||||
|
if (file === undefined) {
|
||||||
|
throw new Error(`Could not find source file`);
|
||||||
|
}
|
||||||
|
const output = new MagicString(PROGRAM.contents);
|
||||||
|
renderer.addConstants(output, 'var x = 3;', file);
|
||||||
|
expect(output.toString()).toContain(`
|
||||||
|
}(this, (function (exports,someSideEffect,localDep,core) {
|
||||||
|
var x = 3;
|
||||||
|
'use strict';
|
||||||
|
var A = (function() {`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should insert constants after inserted imports',
|
||||||
|
() => {
|
||||||
|
// This test (from ESM5) is not needed as constants go in the body
|
||||||
|
// of the UMD IIFE, so cannot come before imports.
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('rewriteSwitchableDeclarations', () => {
|
||||||
|
it('should switch marked declaration initializers', () => {
|
||||||
|
const {renderer, program, sourceFile, switchMarkerAnalyses} = setup(PROGRAM);
|
||||||
|
const file = program.getSourceFile('some/file.js');
|
||||||
|
if (file === undefined) {
|
||||||
|
throw new Error(`Could not find source file`);
|
||||||
|
}
|
||||||
|
const output = new MagicString(PROGRAM.contents);
|
||||||
|
renderer.rewriteSwitchableDeclarations(
|
||||||
|
output, file, switchMarkerAnalyses.get(sourceFile) !.declarations);
|
||||||
|
expect(output.toString())
|
||||||
|
.not.toContain(`var compileNgModuleFactory = compileNgModuleFactory__PRE_R3__;`);
|
||||||
|
expect(output.toString())
|
||||||
|
.toContain(`var badlyFormattedVariable = __PRE_R3__badlyFormattedVariable;`);
|
||||||
|
expect(output.toString())
|
||||||
|
.toContain(`var compileNgModuleFactory = compileNgModuleFactory__POST_R3__;`);
|
||||||
|
expect(output.toString())
|
||||||
|
.toContain(`function compileNgModuleFactory__PRE_R3__(injector, options, moduleType) {`);
|
||||||
|
expect(output.toString())
|
||||||
|
.toContain(`function compileNgModuleFactory__POST_R3__(injector, options, moduleType) {`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('addDefinitions', () => {
|
||||||
|
it('should insert the definitions directly before the return statement of the class IIFE',
|
||||||
|
() => {
|
||||||
|
const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM);
|
||||||
|
const output = new MagicString(PROGRAM.contents);
|
||||||
|
const compiledClass =
|
||||||
|
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !;
|
||||||
|
renderer.addDefinitions(output, compiledClass, 'SOME DEFINITION TEXT');
|
||||||
|
expect(output.toString()).toContain(`
|
||||||
|
A.prototype.ngDoCheck = function() {
|
||||||
|
//
|
||||||
|
};
|
||||||
|
SOME DEFINITION TEXT
|
||||||
|
return A;
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should error if the compiledClass is not valid', () => {
|
||||||
|
const {renderer, sourceFile, program} = setup(PROGRAM);
|
||||||
|
const output = new MagicString(PROGRAM.contents);
|
||||||
|
|
||||||
|
const noIifeDeclaration =
|
||||||
|
getDeclaration(program, sourceFile.fileName, 'NoIife', ts.isFunctionDeclaration);
|
||||||
|
const mockNoIifeClass: any = {declaration: noIifeDeclaration, name: 'NoIife'};
|
||||||
|
expect(() => renderer.addDefinitions(output, mockNoIifeClass, 'SOME DEFINITION TEXT'))
|
||||||
|
.toThrowError(
|
||||||
|
'Compiled class declaration is not inside an IIFE: NoIife in /some/file.js');
|
||||||
|
|
||||||
|
const badIifeDeclaration =
|
||||||
|
getDeclaration(program, sourceFile.fileName, 'BadIife', ts.isVariableDeclaration);
|
||||||
|
const mockBadIifeClass: any = {declaration: badIifeDeclaration, name: 'BadIife'};
|
||||||
|
expect(() => renderer.addDefinitions(output, mockBadIifeClass, 'SOME DEFINITION TEXT'))
|
||||||
|
.toThrowError(
|
||||||
|
'Compiled class wrapper IIFE does not have a return statement: BadIife in /some/file.js');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('removeDecorators', () => {
|
||||||
|
|
||||||
|
it('should delete the decorator (and following comma) that was matched in the analysis', () => {
|
||||||
|
const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM);
|
||||||
|
const output = new MagicString(PROGRAM.contents);
|
||||||
|
const compiledClass =
|
||||||
|
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !;
|
||||||
|
const decorator = compiledClass.decorators[0];
|
||||||
|
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||||
|
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||||
|
renderer.removeDecorators(output, decoratorsToRemove);
|
||||||
|
expect(output.toString())
|
||||||
|
.not.toContain(`{ type: core.Directive, args: [{ selector: '[a]' }] },`);
|
||||||
|
expect(output.toString()).toContain(`{ type: OtherA }`);
|
||||||
|
expect(output.toString()).toContain(`{ type: core.Directive, args: [{ selector: '[b]' }] }`);
|
||||||
|
expect(output.toString()).toContain(`{ type: OtherB }`);
|
||||||
|
expect(output.toString()).toContain(`{ type: core.Directive, args: [{ selector: '[c]' }] }`);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should delete the decorator (but cope with no trailing comma) that was matched in the analysis',
|
||||||
|
() => {
|
||||||
|
const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM);
|
||||||
|
const output = new MagicString(PROGRAM.contents);
|
||||||
|
const compiledClass =
|
||||||
|
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !;
|
||||||
|
const decorator = compiledClass.decorators[0];
|
||||||
|
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||||
|
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||||
|
renderer.removeDecorators(output, decoratorsToRemove);
|
||||||
|
expect(output.toString())
|
||||||
|
.toContain(`{ type: core.Directive, args: [{ selector: '[a]' }] },`);
|
||||||
|
expect(output.toString()).toContain(`{ type: OtherA }`);
|
||||||
|
expect(output.toString())
|
||||||
|
.not.toContain(`{ type: core.Directive, args: [{ selector: '[b]' }] }`);
|
||||||
|
expect(output.toString()).toContain(`{ type: OtherB }`);
|
||||||
|
expect(output.toString())
|
||||||
|
.toContain(`{ type: core.Directive, args: [{ selector: '[c]' }] }`);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should delete the decorator (and its container if there are not other decorators left) that was matched in the analysis',
|
||||||
|
() => {
|
||||||
|
const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM);
|
||||||
|
const output = new MagicString(PROGRAM.contents);
|
||||||
|
const compiledClass =
|
||||||
|
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !;
|
||||||
|
const decorator = compiledClass.decorators[0];
|
||||||
|
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||||
|
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||||
|
renderer.removeDecorators(output, decoratorsToRemove);
|
||||||
|
renderer.addDefinitions(output, compiledClass, 'SOME DEFINITION TEXT');
|
||||||
|
expect(output.toString())
|
||||||
|
.toContain(`{ type: core.Directive, args: [{ selector: '[a]' }] },`);
|
||||||
|
expect(output.toString()).toContain(`{ type: OtherA }`);
|
||||||
|
expect(output.toString())
|
||||||
|
.toContain(`{ type: core.Directive, args: [{ selector: '[b]' }] }`);
|
||||||
|
expect(output.toString()).toContain(`{ type: OtherB }`);
|
||||||
|
expect(output.toString()).not.toContain(`C.decorators`);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('[__decorate declarations]', () => {
|
||||||
|
it('should delete the decorator (and following comma) that was matched in the analysis', () => {
|
||||||
|
const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM_DECORATE_HELPER);
|
||||||
|
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
||||||
|
const compiledClass =
|
||||||
|
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !;
|
||||||
|
const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
|
||||||
|
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||||
|
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||||
|
renderer.removeDecorators(output, decoratorsToRemove);
|
||||||
|
expect(output.toString()).not.toContain(`core.Directive({ selector: '[a]' }),`);
|
||||||
|
expect(output.toString()).toContain(`OtherA()`);
|
||||||
|
expect(output.toString()).toContain(`core.Directive({ selector: '[b]' })`);
|
||||||
|
expect(output.toString()).toContain(`OtherB()`);
|
||||||
|
expect(output.toString()).toContain(`core.Directive({ selector: '[c]' })`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should delete the decorator (but cope with no trailing comma) that was matched in the analysis',
|
||||||
|
() => {
|
||||||
|
const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM_DECORATE_HELPER);
|
||||||
|
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
||||||
|
const compiledClass =
|
||||||
|
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !;
|
||||||
|
const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
|
||||||
|
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||||
|
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||||
|
renderer.removeDecorators(output, decoratorsToRemove);
|
||||||
|
expect(output.toString()).toContain(`core.Directive({ selector: '[a]' }),`);
|
||||||
|
expect(output.toString()).toContain(`OtherA()`);
|
||||||
|
expect(output.toString()).not.toContain(`core.Directive({ selector: '[b]' })`);
|
||||||
|
expect(output.toString()).toContain(`OtherB()`);
|
||||||
|
expect(output.toString()).toContain(`core.Directive({ selector: '[c]' })`);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should delete the decorator (and its container if there are no other decorators left) that was matched in the analysis',
|
||||||
|
() => {
|
||||||
|
const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM_DECORATE_HELPER);
|
||||||
|
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
||||||
|
const compiledClass =
|
||||||
|
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !;
|
||||||
|
const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
|
||||||
|
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||||
|
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||||
|
renderer.removeDecorators(output, decoratorsToRemove);
|
||||||
|
expect(output.toString()).toContain(`core.Directive({ selector: '[a]' }),`);
|
||||||
|
expect(output.toString()).toContain(`OtherA()`);
|
||||||
|
expect(output.toString()).toContain(`core.Directive({ selector: '[b]' })`);
|
||||||
|
expect(output.toString()).toContain(`OtherB()`);
|
||||||
|
expect(output.toString()).not.toContain(`core.Directive({ selector: '[c]' })`);
|
||||||
|
expect(output.toString()).not.toContain(`C = tslib_1.__decorate([`);
|
||||||
|
expect(output.toString()).toContain(`function C() {\n }\n return C;`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue