
Currently the ImportManager class handles various rewriting actions of imports when compiling @angular/core. This is required as code compiled within @angular/core cannot import from '@angular/core'. To work around this, imports are rewritten to get core symbols from a particular file, r3_symbols.ts. In this refactoring, this rewriting logic is moved out of the ImportManager and put behind an interface, ImportRewriter. There are three implementers of the interface: * NoopImportRewriter, used for compiling all non-core packages. * R3SymbolsImportRewriter, used when ngtsc compiles @angular/core. * NgccFlatImportRewriter, used when ngcc compiles @angular/core (special logic is needed because ngcc has to rewrite imports in flat bundles differently than in non-flat bundles). This is a precursor to using this rewriting logic in other contexts besides the ImportManager. PR Close #27998
96 lines
3.0 KiB
TypeScript
96 lines
3.0 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 * as ts from 'typescript';
|
|
|
|
import {NoopImportRewriter} from '../../imports';
|
|
import {TypeScriptReflectionHost} from '../../reflection';
|
|
import {getDeclaration, makeProgram} from '../../testing/in_memory_typescript';
|
|
import {ImportManager, translateStatement} from '../../translator';
|
|
|
|
import {generateSetClassMetadataCall} from '../src/metadata';
|
|
|
|
const CORE = {
|
|
name: 'node_modules/@angular/core/index.d.ts',
|
|
contents: `
|
|
export declare function Input(...args: any[]): any;
|
|
export declare function Inject(...args: any[]): any;
|
|
export declare function Component(...args: any[]): any;
|
|
export declare class Injector {}
|
|
`
|
|
};
|
|
|
|
describe('ngtsc setClassMetadata converter', () => {
|
|
it('should convert decorated class metadata', () => {
|
|
const res = compileAndPrint(`
|
|
import {Component} from '@angular/core';
|
|
|
|
@Component('metadata') class Target {}
|
|
`);
|
|
expect(res).toEqual(
|
|
`/*@__PURE__*/ i0.ɵsetClassMetadata(Target, [{ type: Component, args: ['metadata'] }], null, null);`);
|
|
});
|
|
|
|
it('should convert decorated class constructor parameter metadata', () => {
|
|
const res = compileAndPrint(`
|
|
import {Component, Inject, Injector} from '@angular/core';
|
|
const FOO = 'foo';
|
|
|
|
@Component('metadata') class Target {
|
|
constructor(@Inject(FOO) foo: any, bar: Injector) {}
|
|
}
|
|
`);
|
|
expect(res).toContain(
|
|
`function () { return [{ type: undefined, decorators: [{ type: Inject, args: [FOO] }] }, { type: Injector }]; }, null);`);
|
|
});
|
|
|
|
it('should convert decorated field metadata', () => {
|
|
const res = compileAndPrint(`
|
|
import {Component, Input} from '@angular/core';
|
|
|
|
@Component('metadata') class Target {
|
|
@Input() foo: string;
|
|
|
|
@Input('value') bar: string;
|
|
|
|
notDecorated: string;
|
|
}
|
|
`);
|
|
expect(res).toContain(`{ foo: [{ type: Input }], bar: [{ type: Input, args: ['value'] }] })`);
|
|
});
|
|
|
|
it('should not convert non-angular decorators to metadata', () => {
|
|
const res = compileAndPrint(`
|
|
declare function NotAComponent(...args: any[]): any;
|
|
|
|
@NotAComponent('metadata') class Target {}
|
|
`);
|
|
expect(res).toBe('');
|
|
});
|
|
});
|
|
|
|
function compileAndPrint(contents: string): string {
|
|
const {program} = makeProgram([
|
|
CORE, {
|
|
name: 'index.ts',
|
|
contents,
|
|
}
|
|
]);
|
|
const host = new TypeScriptReflectionHost(program.getTypeChecker());
|
|
const target = getDeclaration(program, 'index.ts', 'Target', ts.isClassDeclaration);
|
|
const call = generateSetClassMetadataCall(target, host, false);
|
|
if (call === null) {
|
|
return '';
|
|
}
|
|
const sf = program.getSourceFile('index.ts') !;
|
|
const im = new ImportManager(new NoopImportRewriter(), 'i');
|
|
const tsStatement = translateStatement(call, im);
|
|
const res = ts.createPrinter().printNode(ts.EmitHint.Unspecified, tsStatement, sf);
|
|
return res.replace(/\s+/g, ' ');
|
|
}
|