Generated HMR metadata update function doesn't include proper imports when using defer blocks · Issue #59310 · angular/angular · GitHub
Skip to content

Generated HMR metadata update function doesn't include proper imports when using defer blocks #59310

Closed

Description

@brandonroberts

Which @angular/* package(s) are the source of the bug?

compiler

Is this a regression?

Yes

Description

When enabling HMR/Live Reload, if a defer block is used, an error is logged to the console after saving the component template.

Steps to reproduce

  1. Clone repo and run npm install
  2. Run ng serve
  3. Open Browser Devtools
  4. Make a change to app.component.html and save.
  5. Note error in browser devtools, and "lazy works" disappears.

Please provide a link to a minimal reproduction of the bug

https://github.com/brandonroberts/angular-hmr-defer

Please provide the exception or error you saw

Uncaught ReferenceError: LazyComponent is not defined
    at AppComponent_Defer_8_DepsFn (app.component.ts:1:157)
    at triggerResourceLoading (chunk-L7E6HT6X.js?v=287274a3:19516:48)
    at triggerDeferBlock (chunk-L7E6HT6X.js?v=287274a3:19586:7)
    at chunk-L7E6HT6X.js?v=287274a3:19458:38
    at callback (chunk-L7E6HT6X.js?v=287274a3:17478:9)
    at _ZoneDelegate.invoke (zone.js:369:28)
    at Object.onInvoke (chunk-L7E6HT6X.js?v=287274a3:8569:25)
    at _ZoneDelegate.invoke (zone.js:368:34)
    at ZoneImpl.run (zone.js:111:43)
    at _NgZone.run (chunk-L7E6HT6X.js?v=287274a3:8450:24)

Please provide the environment you discovered this bug in (run ng version)

Angular CLI: 19.1.0-next.2
Node: 22.5.1
Package Manager: npm 10.8.2
OS: darwin arm64

Angular: 19.1.0-next.4
... animations, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, platform-server
... router

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1901.0-next.2
@angular-devkit/build-angular   19.1.0-next.2
@angular-devkit/core            19.1.0-next.2
@angular-devkit/schematics      19.1.0-next.2
@angular/cli                    19.1.0-next.2
@angular/ssr                    19.1.0-next.2
@schematics/angular             19.1.0-next.2
rxjs                            7.8.1
typescript                      5.7.2
zone.js                         0.15.0

Anything else?

Generated HMR update metadata function

export default function AppComponent_UpdateMetadata(AppComponent, ɵɵnamespaces) {
    const ɵhmr0 = ɵɵnamespaces[0];
    const AppComponent_Defer_8_DepsFn = () => [LazyComponent]; // <-- error
    function AppComponent_Defer_7_Template(rf, ctx) {
        if (rf & 1) {
            ɵhmr0.ɵɵelement(0, "app-lazy");
        }
    }
    AppComponent.ɵfac = function AppComponent_Factory(__ngFactoryType__) {
        return new (__ngFactoryType__ || AppComponent)();
    }
    ;
    AppComponent.ɵcmp = /*@__PURE__*/
    ɵhmr0.ɵɵdefineComponent({
        type: AppComponent,
        selectors: [["app-root"]],
        features: [ɵhmr0.ɵɵExternalStylesFeature(["8911acd770bdc9e6b98613f574402426119ebd7d17ab3a9642918dbc80f2ddda.css"])],
        decls: 10,
        vars: 0,
        template: function AppComponent_Template(rf, ctx) {
            if (rf & 1) {
                ɵhmr0.ɵɵelementStart(0, "ol")(1, "li");
                ɵhmr0.ɵɵtext(2, " Open Browser Devtools ");
                ɵhmr0.ɵɵelementEnd();
                ɵhmr0.ɵɵelementStart(3, "li");
                ɵhmr0.ɵɵtext(4, " Make a change to app.component.html and save. ");
                ɵhmr0.ɵɵelementEnd();
                ɵhmr0.ɵɵelementStart(5, "li");
                ɵhmr0.ɵɵtext(6, " Note error in browser devtools, and \"lazy works\" disappears. ");
                ɵhmr0.ɵɵelementEnd()();
                ɵhmr0.ɵɵtemplate(7, AppComponent_Defer_7_Template, 1, 0);
                ɵhmr0.ɵɵdefer(8, 7, AppComponent_Defer_8_DepsFn);
                ɵhmr0.ɵɵdeferOnIdle();
            }
        }
    });
    ( () => {
        (typeof ngDevMode === "undefined" || ngDevMode) && ɵhmr0.ɵsetClassDebugInfo(AppComponent, {
            className: "AppComponent",
            filePath: "src/app/app.component.ts",
            lineNumber: 10
        });
    }
    )();
}

Activity

  1. crisbeto commented on Dec 27, 2024

    @crisbeto
    Member

    EDIT: looks like Dep is being captured because of setClassMetadata in our tests. If I disable it through supportTestBed: false, it stops being captured.

    Interesting, we have a test that's identical to your repro and seems to work as expected. I wonder if something isn't getting lost when integrating with the CLI.

  2. self-assigned this
    on Dec 27, 2024
  3. added a commit that references this issue on Dec 27, 2024
    83488e3
  4. brandonroberts commented on Dec 27, 2024

    @brandonroberts
    ContributorAuthor

    Thanks @crisbeto. Is there a workaround until this patch is released?

  5. crisbeto commented on Dec 27, 2024

    @crisbeto
    Member

    Assuming the CLI doesn't override it somewhere, you should be able to set angularCompilerOptions.supportTestBed: true in your tsconfig.

  6. brandonroberts commented on Dec 27, 2024

    @brandonroberts
    ContributorAuthor

    Ok, it must be overriden internally. I tried that but it's still set to false.

  7. brandonroberts commented on Dec 27, 2024

    @brandonroberts
    ContributorAuthor

    This also fails with metadata that is provided as an import

    import { Component } from '@angular/core';
    import ExternalComponent from './external.page';
    import { NgIcon, provideIcons } from '@ng-icons/core';
    
    import { heroUsers } from '@ng-icons/heroicons/outline';
    
    @Component({
      selector: 'app-root',
      imports: [ExternalComponent, NgIcon],
      viewProviders: [provideIcons({ heroUsers })],
      templateUrl: './app.component.html',
      styleUrl: './app.component.css'
    })
    export class AppComponent {
      title = 'angular-hmr-defer';
    }

    Generated HMR:

    export default function AppComponent_UpdateMetadata(AppComponent, ɵɵnamespaces, provideIcons, NgIcon, Component, ExternalComponent) {
        const ɵhmr0 = ɵɵnamespaces[0];
        const AppComponent_Defer_8_DepsFn = () => [ExternalComponent];
        function AppComponent_Defer_7_Template(rf, ctx) {
            if (rf & 1) {
                ɵhmr0.ɵɵelement(0, "app-external");
            }
        }
        AppComponent.ɵfac = function AppComponent_Factory(__ngFactoryType__) {
            return new (__ngFactoryType__ || AppComponent)();
        }
        ;
        AppComponent.ɵcmp = /*@__PURE__*/
        ɵhmr0.ɵɵdefineComponent({
            type: AppComponent,
            selectors: [["app-root"]],
            features: [ɵhmr0.ɵɵProvidersFeature([], [provideIcons({
                heroUsers // <-- error
            })]), ɵhmr0.ɵɵExternalStylesFeature(["8911acd770bdc9e6b98613f574402426119ebd7d17ab3a9642918dbc80f2ddda.css"])],
            decls: 11,
            vars: 0,
            consts: [["name", "heroUsers"]],
            template: function AppComponent_Template(rf, ctx) {
                if (rf & 1) {
                    ɵhmr0.ɵɵelementStart(0, "ol")(1, "li");
                    ɵhmr0.ɵɵtext(2, " Open Browser Devtools ");
                    ɵhmr0.ɵɵelementEnd();
                    ɵhmr0.ɵɵelementStart(3, "li");
                    ɵhmr0.ɵɵtext(4, " Make a change to app.component.html and save. ");
                    ɵhmr0.ɵɵelementEnd();
                    ɵhmr0.ɵɵelementStart(5, "li");
                    ɵhmr0.ɵɵtext(6, " Note error in browser devtools, and \"lazy works\" disappears. ");
                    ɵhmr0.ɵɵelementEnd()();
                    ɵhmr0.ɵɵtemplate(7, AppComponent_Defer_7_Template, 1, 0);
                    ɵhmr0.ɵɵdefer(8, 7, AppComponent_Defer_8_DepsFn);
                    ɵhmr0.ɵɵdeferOnIdle();
                    ɵhmr0.ɵɵelement(10, "ng-icon", 0);
                }
            },
            dependencies: [NgIcon]
        });
        ( () => {
            (typeof ngDevMode === "undefined" || ngDevMode) && ɵhmr0.ɵsetClassMetadata(AppComponent, [{
                type: Component,
                args: [{
                    selector: 'app-root',
                    imports: [ExternalComponent, NgIcon],
                    viewProviders: [provideIcons({
                        heroUsers
                    })],
                    template: "<ol>\n  <li>\n    Open Browser Devtools\n  </li>\n  <li>\n    Make a change to app.component.html and save.\n  </li>\n  <li>\n    Note error in browser devtools, and \"lazy works\" disappears.\n  </li>  \n</ol>\n\n@defer {\n  <app-external />\n}\n\n<ng-icon name=\"heroUsers\" />\n"
                }]
            }], null, null);
        }
        )();
        ( () => {
            (typeof ngDevMode === "undefined" || ngDevMode) && ɵhmr0.ɵsetClassDebugInfo(AppComponent, {
                className: "AppComponent",
                filePath: "src/app/app.component.ts",
                lineNumber: 15
            });
        }
        )();
    }
  8. crisbeto commented on Dec 27, 2024

    @crisbeto
    Member

    Definitely a bug. I suspect it's because it's written as provideIcons({heroUsers}). Does it work if you change it to provideIcons({heroUsers: heroUsers})?

  9. brandonroberts commented on Dec 27, 2024

    @brandonroberts
    ContributorAuthor

    Yep, that works

  10. crisbeto commented on Dec 28, 2024

    @crisbeto
    Member

    Sent a fix at #59323.

  11. added this to the needsTriage milestone on Jan 6, 2025
  12. added a commit that references this issue on Jan 6, 2025
    17fb20f
  13. added a commit that references this issue on Jan 6, 2025
    ee99879
  14. angular-automatic-lock-bot commented on Feb 6, 2025

    @angular-automatic-lock-bot

    This issue has been automatically locked due to inactivity.
    Please file a new issue if you are encountering a similar or related problem.

    Read more about our automatic conversation locking policy.

    This action has been performed automatically by a bot.

  15. locked and limited conversation to collaborators on Feb 6, 2025
  16. added a commit that references this issue on Feb 7, 2025
    ae1568c
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions