fix(ivy): use a single constant pool per source file (#25392)

Previously, ngtsc used a new ConstantPool for each decorator
compilation. This could result in collisions between constants in the
top-level scope.

Now, ngtsc uses a single ConstantPool for each source file being
compiled, and merges the constant statements into the file after the
import section.

PR Close #25392
This commit is contained in:
Alex Rickabaugh
2018-06-27 08:41:11 -07:00
committed by Ben Lesh
parent 9c92a6fc7a
commit fba276d3d1
9 changed files with 115 additions and 25 deletions

View File

@ -56,6 +56,36 @@ A.decorators = [
});
describe('addConstants', () => {
it('should insert the given constants after imports in the source file', () => {
const PROGRAM = {
name: 'some/file.js',
contents: `
/* A copyright notice */
import {Directive} from '@angular/core';
export class A {}
A.decorators = [
{ type: Directive, args: [{ selector: '[a]' }] },
{ type: Other }
];
// Some other content`
};
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, 'const x = 3;', file);
expect(output.toString()).toContain(`
import {Directive} from '@angular/core';
const x = 3;
export class A {}`);
});
});
describe('addDefinitions', () => {
it('should insert the definitions directly after the class declaration', () => {
const PROGRAM = {

View File

@ -20,6 +20,9 @@ class TestRenderer extends Renderer {
addImports(output: MagicString, imports: {name: string, as: string}[]) {
output.prepend('\n// ADD IMPORTS\n');
}
addConstants(output: MagicString, constants: string, file: ts.SourceFile): void {
output.prepend('\n// ADD CONSTANTS\n');
}
addDefinitions(output: MagicString, analyzedClass: AnalyzedClass, definitions: string) {
output.prepend('\n// ADD DEFINITIONS\n');
}
@ -65,7 +68,8 @@ describe('Renderer', () => {
]
});
const RENDERED_CONTENTS =
`\n// REMOVE DECORATORS\n\n// ADD IMPORTS\n\n// ADD DEFINITIONS\n` + INPUT_PROGRAM.contents;
`\n// REMOVE DECORATORS\n\n// ADD IMPORTS\n\n// ADD CONSTANTS\n\n// ADD DEFINITIONS\n` +
INPUT_PROGRAM.contents;
const OUTPUT_PROGRAM_MAP = fromObject({
'version': 3,
'file': '/output_file.js',
@ -74,14 +78,14 @@ describe('Renderer', () => {
'import { Directive } from \'@angular/core\';\nexport class A {\n foo(x) {\n return x;\n }\n}\nA.decorators = [\n { type: Directive, args: [{ selector: \'[a]\' }] }\n];\n'
],
'names': [],
'mappings': ';;;;;;AAAA;;;;;;;;;'
'mappings': ';;;;;;;;AAAA;;;;;;;;;'
});
const MERGED_OUTPUT_PROGRAM_MAP = fromObject({
'version': 3,
'sources': ['/file.ts'],
'names': [],
'mappings': ';;;;;;AAAA',
'mappings': ';;;;;;;;AAAA',
'file': '/output_file.js',
'sourcesContent': [
'import { Directive } from \'@angular/core\';\nexport class A {\n foo(x: string): string {\n return x;\n }\n static decorators = [\n { type: Directive, args: [{ selector: \'[a]\' }] }\n ];\n}'