Skip to content

Commit

Permalink
fix(compiler-cli): insert constant statements after the first group o…
Browse files Browse the repository at this point in the history
…f imports (angular#56431)

The linker inserts the constant statements that are needed to support compiled templates
after the import statements of an ESM file, but it failed to account for import statements
that are not at the top of the file. This is typically seen in FESM files where multiple
individual ESMs have been concatenated into a single ESM file, with imports in various places.
The linker would then find the very last import statement to insert the constant statements
after, but this may result in TDZ errors for component templates that have been emitted
earlier in the file.

This commit updates the Babel linker plugin to insert constant statements after the last
import of the first import group, therefore avoiding the TDZ error.

Fixes angular#56403

PR Close angular#56431
  • Loading branch information
JoostK authored and AndrewKushnir committed Jun 13, 2024
1 parent 126d33a commit 0b867e8
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,12 @@ function insertIntoFunction(
*/
function insertIntoProgram(program: NodePath<t.Program>, statements: t.Statement[]): void {
const body = program.get('body');
const importStatements = body.filter((statement) => statement.isImportDeclaration());
if (importStatements.length === 0) {
const insertBeforeIndex = body.findIndex((statement) => !statement.isImportDeclaration());

if (insertBeforeIndex === -1) {
program.unshiftContainer('body', statements);
} else {
importStatements[importStatements.length - 1].insertAfter(statements);
body[insertBeforeIndex].insertBefore(statements);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ describe('createEs2015LinkerPlugin()', () => {
);
const fileSystem = new MockFileSystemNative();
const logger = new MockLogger();
const plugin = createEs2015LinkerPlugin({fileSystem, logger});

babel.transformSync(
[
Expand Down Expand Up @@ -115,7 +114,6 @@ describe('createEs2015LinkerPlugin()', () => {
);
const fileSystem = new MockFileSystemNative();
const logger = new MockLogger();
const plugin = createEs2015LinkerPlugin({fileSystem, logger});
const result = babel.transformSync(
[
'var core;',
Expand All @@ -138,7 +136,6 @@ describe('createEs2015LinkerPlugin()', () => {
spyOnLinkPartialDeclarationWithConstants(o.literal('REPLACEMENT'));
const fileSystem = new MockFileSystemNative();
const logger = new MockLogger();
const plugin = createEs2015LinkerPlugin({fileSystem, logger});
const result = babel.transformSync(
[
"import * as core from 'some-module';",
Expand All @@ -159,11 +156,35 @@ describe('createEs2015LinkerPlugin()', () => {
);
});

it('should return a Babel plugin that adds shared statements after the first group of imports', () => {
spyOnLinkPartialDeclarationWithConstants(o.literal('REPLACEMENT'));
const fileSystem = new MockFileSystemNative();
const logger = new MockLogger();
const result = babel.transformSync(
[
"import * as core from 'some-module';",
"import {id} from 'other-module';",
`ɵɵngDeclareDirective({minVersion: '0.0.0-PLACEHOLDER', version: '0.0.0-PLACEHOLDER', ngImport: core})`,
`ɵɵngDeclareDirective({minVersion: '0.0.0-PLACEHOLDER', version: '0.0.0-PLACEHOLDER', ngImport: core})`,
`ɵɵngDeclareDirective({minVersion: '0.0.0-PLACEHOLDER', version: '0.0.0-PLACEHOLDER', ngImport: core})`,
"import {second} from 'second-module';",
].join('\n'),
{
plugins: [createEs2015LinkerPlugin({fileSystem, logger})],
filename: '/test.js',
parserOpts: {sourceType: 'unambiguous'},
generatorOpts: {compact: true},
},
);
expect(result!.code).toEqual(
'import*as core from\'some-module\';import{id}from\'other-module\';const _c0=[1];const _c1=[2];const _c2=[3];"REPLACEMENT";"REPLACEMENT";"REPLACEMENT";import{second}from\'second-module\';',
);
});

it('should return a Babel plugin that adds shared statements at the start of the program if it is an ECMAScript Module and there are no imports', () => {
spyOnLinkPartialDeclarationWithConstants(o.literal('REPLACEMENT'));
const fileSystem = new MockFileSystemNative();
const logger = new MockLogger();
const plugin = createEs2015LinkerPlugin({fileSystem, logger});
const result = babel.transformSync(
[
'var core;',
Expand All @@ -188,7 +209,6 @@ describe('createEs2015LinkerPlugin()', () => {
spyOnLinkPartialDeclarationWithConstants(o.literal('REPLACEMENT'));
const fileSystem = new MockFileSystemNative();
const logger = new MockLogger();
const plugin = createEs2015LinkerPlugin({fileSystem, logger});
const result = babel.transformSync(
[
'function run(core) {',
Expand All @@ -213,7 +233,6 @@ describe('createEs2015LinkerPlugin()', () => {
spyOnLinkPartialDeclarationWithConstants(o.literal('REPLACEMENT'));
const fileSystem = new MockFileSystemNative();
const logger = new MockLogger();
const plugin = createEs2015LinkerPlugin({fileSystem, logger});
const result = babel.transformSync(
[
'function run() {',
Expand Down Expand Up @@ -244,7 +263,6 @@ describe('createEs2015LinkerPlugin()', () => {
spyOnLinkPartialDeclarationWithConstants(o.fn([], [], null, null, 'FOO'));
const fileSystem = new MockFileSystemNative();
const logger = new MockLogger();
const plugin = createEs2015LinkerPlugin({fileSystem, logger});
const result = babel.transformSync(
[
`ɵɵngDeclareDirective({minVersion: '0.0.0-PLACEHOLDER', version: '0.0.0-PLACEHOLDER', ngImport: core}); FOO;`,
Expand Down Expand Up @@ -298,7 +316,6 @@ describe('createEs2015LinkerPlugin()', () => {

const fileSystem = new MockFileSystemNative();
const logger = new MockLogger();
const plugin = createEs2015LinkerPlugin({fileSystem, logger});
const result = babel.transformSync(
[
"import * as core from 'some-module';",
Expand Down

0 comments on commit 0b867e8

Please sign in to comment.