fix(@schematics/angular): wrap bootstrapping code in an HMR compatible manner

With this change we update the universal schematic bootstrap code to handle HMR properly. Previously, the bootstrapping code was called only on `DOMContentLoaded` which is not triggered when running in HMR.

Closes #21932
This commit is contained in:
Alan Agius 2021-10-12 11:55:53 +02:00
parent 35c40212c9
commit 08687d1475
2 changed files with 15 additions and 9 deletions

View File

@ -141,9 +141,17 @@ function wrapBootstrapCall(mainFile: string): Rule {
// indent contents // indent contents
const triviaWidth = bootstrapCall.getLeadingTriviaWidth(); const triviaWidth = bootstrapCall.getLeadingTriviaWidth();
const beforeText = const beforeText =
`document.addEventListener('DOMContentLoaded', () => {\n` + `function bootstrap() {\n` + ' '.repeat(triviaWidth > 2 ? triviaWidth + 1 : triviaWidth);
' '.repeat(triviaWidth > 2 ? triviaWidth + 1 : triviaWidth); const afterText =
const afterText = `\n${triviaWidth > 2 ? ' '.repeat(triviaWidth - 1) : ''}});`; `\n${triviaWidth > 2 ? ' '.repeat(triviaWidth - 1) : ''}};\n` +
`
if (document.readyState === 'complete') {
bootstrap();
} else {
document.addEventListener('DOMContentLoaded', bootstrap);
}
`;
// in some cases we need to cater for a trailing semicolon such as; // in some cases we need to cater for a trailing semicolon such as;
// bootstrap().catch(err => console.log(err)); // bootstrap().catch(err => console.log(err));
@ -236,8 +244,8 @@ export default function (options: UniversalOptions): Rule {
throw targetBuildNotFoundError(); throw targetBuildNotFoundError();
} }
const clientBuildOptions = ((clientBuildTarget.options || const clientBuildOptions = (clientBuildTarget.options ||
{}) as unknown) as BrowserBuilderOptions; {}) as unknown as BrowserBuilderOptions;
const clientTsConfig = normalize(clientBuildOptions.tsConfig); const clientTsConfig = normalize(clientBuildOptions.tsConfig);
const tsConfigExtends = basename(clientTsConfig); const tsConfigExtends = basename(clientTsConfig);

View File

@ -191,7 +191,7 @@ describe('Universal Schematic', () => {
.toPromise(); .toPromise();
const filePath = '/projects/bar/src/main.ts'; const filePath = '/projects/bar/src/main.ts';
const contents = tree.readContent(filePath); const contents = tree.readContent(filePath);
expect(contents).toMatch(/document.addEventListener\('DOMContentLoaded', \(\) => {/); expect(contents).toContain(`document.addEventListener('DOMContentLoaded', bootstrap);`);
}); });
it('should wrap the bootstrap declaration in a DOMContentLoaded event handler', async () => { it('should wrap the bootstrap declaration in a DOMContentLoaded event handler', async () => {
@ -221,9 +221,7 @@ describe('Universal Schematic', () => {
.runSchematicAsync('universal', defaultOptions, appTree) .runSchematicAsync('universal', defaultOptions, appTree)
.toPromise(); .toPromise();
const contents = tree.readContent(filePath); const contents = tree.readContent(filePath);
expect(contents).toMatch( expect(contents).toContain(`document.addEventListener('DOMContentLoaded', bootstrap);`);
/document.addEventListener\('DOMContentLoaded', \(\) => {[\n\r\s]+bootstrap\(\)/,
);
}); });
it('should install npm dependencies', async () => { it('should install npm dependencies', async () => {