refactor(compiler): wrap large strings in function (#38253)
Large strings constants are now wrapped in a function which is called whenever used. This works around a unique limitation of Closure, where it will **always** inline string literals at **every** usage, regardless of how large the string literal is or how many times it is used.The workaround is to use a function rather than a string literal. Closure has differently inlining semantics for functions, where it will check the length of the function and the number of times it is used before choosing to inline it. By using a function, `ngtsc` makes Closure more conservative about inlining large strings, and avoids blowing up the bundle size.This optimization is only used if the constant is a large string. A wrapping function is not included for other use cases, since it would just increase the bundle size and add unnecessary runtime performance overhead. PR Close #38253
This commit is contained in:

committed by
Alex Rickabaugh

parent
103a95c182
commit
887c350f9d
@ -249,7 +249,7 @@ function transformIvySourceFile(
|
||||
importRewriter: ImportRewriter, file: ts.SourceFile, isCore: boolean,
|
||||
isClosureCompilerEnabled: boolean,
|
||||
defaultImportRecorder: DefaultImportRecorder): ts.SourceFile {
|
||||
const constantPool = new ConstantPool();
|
||||
const constantPool = new ConstantPool(isClosureCompilerEnabled);
|
||||
const importManager = new ImportManager(importRewriter);
|
||||
|
||||
// The transformation process consists of 2 steps:
|
||||
|
@ -6685,6 +6685,66 @@ export const Foo = Foo__PRE_R3__;
|
||||
// remains a string in the `styles` array.
|
||||
'"img[_ngcontent-%COMP%] { background: url(/b/some-very-very-long-path.png); }"]');
|
||||
});
|
||||
|
||||
it('large strings are wrapped in a function for Closure', () => {
|
||||
env.tsconfig({
|
||||
annotateForClosureCompiler: true,
|
||||
});
|
||||
|
||||
env.write('test.ts', `
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'comp-a',
|
||||
template: 'Comp A',
|
||||
styles: [
|
||||
'div { background: url(/a.png); }',
|
||||
'div { background: url(/some-very-very-long-path.png); }',
|
||||
]
|
||||
})
|
||||
export class CompA {}
|
||||
|
||||
@Component({
|
||||
selector: 'comp-b',
|
||||
template: 'Comp B',
|
||||
styles: [
|
||||
'div { background: url(/b.png); }',
|
||||
'div { background: url(/some-very-very-long-path.png); }',
|
||||
]
|
||||
})
|
||||
export class CompB {}
|
||||
`);
|
||||
|
||||
env.driveMain();
|
||||
const jsContents = env.getContents('test.js');
|
||||
|
||||
// Verify that long strings are extracted to a separate var. This should be wrapped in a
|
||||
// function to trick Closure not to inline the contents for very large strings.
|
||||
// See: https://github.com/angular/angular/pull/38253.
|
||||
expect(jsContents)
|
||||
.toContain(
|
||||
'_c0 = function () {' +
|
||||
' return "div[_ngcontent-%COMP%] {' +
|
||||
' background: url(/some-very-very-long-path.png);' +
|
||||
' }";' +
|
||||
' };');
|
||||
|
||||
expect(jsContents)
|
||||
.toContain(
|
||||
'styles: [' +
|
||||
// Check styles for component A.
|
||||
'"div[_ngcontent-%COMP%] { background: url(/a.png); }", ' +
|
||||
// Large string should be called from function definition.
|
||||
'_c0()]');
|
||||
|
||||
expect(jsContents)
|
||||
.toContain(
|
||||
'styles: [' +
|
||||
// Check styles for component B.
|
||||
'"div[_ngcontent-%COMP%] { background: url(/b.png); }", ' +
|
||||
// Large string should be called from function definition.
|
||||
'_c0()]');
|
||||
});
|
||||
});
|
||||
|
||||
describe('non-exported classes', () => {
|
||||
|
Reference in New Issue
Block a user