Skip to content

build --prod does not include metadata defined by decorator #5359

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
AnthonyTsang opened this issue Mar 10, 2017 · 5 comments
Closed

build --prod does not include metadata defined by decorator #5359

AnthonyTsang opened this issue Mar 10, 2017 · 5 comments

Comments

@AnthonyTsang
Copy link

Please provide us with the following information:

OS?

Windows 7, 8 or 10. Linux (which distribution). Mac OSX (Yosemite? El Capitan?)

MacOS Sierra

Versions.

Please run ng --version. If there's nothing outputted, please run in a Terminal: node --version and paste the result here:

@angular/cli: 1.0.0-rc.1
node: 7.6.0
os: darwin x64
@angular/cli: 1.0.0-rc.1
@angular/common: 2.4.9
@angular/compiler: 2.4.9
@angular/compiler-cli: 2.4.9
@angular/core: 2.4.9
@angular/forms: 2.4.9
@angular/http: 2.4.9
@angular/platform-browser: 2.4.9
@angular/platform-browser-dynamic: 2.4.9
@angular/router: 3.4.9

Repro steps.

Was this an app that wasn't created using the CLI? What change did you do on your code? etc.

I have a root component which will load a template from HTML and compile it dynamically.

@Component({
    selector: 'App',
    providers: [
        CountingTextareaComponent,
        MyPortfolioPanelComponent,
    ],
    template: ''
})
export class App {
    constructor(private viewContainerRef: ViewContainerRef, private compiler: Compiler) { }

    ngOnInit() {
        const compMetadata = new Component({
            selector: 'dynamic-html',
            template: document.getElementById('appContent').innerText,
        });

        const DynamicHtmlComponent = Component(compMetadata)(class {});

        const DynamicHtmlModule = NgModule({
            imports: [
                UtilityModule,
                MemberModule,
            ],
            declarations: [
                DynamicHtmlComponent,
            ]
        })(class DynamicHtmlModule { });

        this.compiler.compileModuleAndAllComponentsAsync(DynamicHtmlModule)
            .then((moduleWithComponentFactory: ModuleWithComponentFactories<any>) => {
                return moduleWithComponentFactory.componentFactories.find(x => x.componentType === DynamicHtmlComponent);
            })
            .then(factory => {
                const injector = ReflectiveInjector.fromResolvedProviders([], this.viewContainerRef.parentInjector);
                this.viewContainerRef.createComponent(factory, 0, injector, []);
            });
    }
}

The DynamicHtmlModule is not using decorator.
Both UtilityModule and MemberModule declares tones of components.

Here is the problem, when I build it by ng build everything is fine. The app is running without error.
The UtilityModule is built to be

"use strict";

var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var core_1 = __webpack_require__(0);
...

var UtilityModule = (function () {
    function UtilityModule() {
    }
    return UtilityModule;
}());
UtilityModule = __decorate([
    core_1.NgModule({
        imports: [
            ...
        ],
        declarations: [
            ...
        ],
        exports: [
            ...
        ],
    })
], UtilityModule);
exports.UtilityModule = UtilityModule;

But if I build it by ng build --prod, there will be an error

EXCEPTION: Unexpected value 'e' imported by the module 'e'

By my understanding, it means my UtilityModule is not a valid module since it does not contain any metadata.

Here is how it is built to be

"0gBB": function(e, t, n) {
        "use strict";
        var i = function() {
            function e() {}
            return e
        }();
        t.UtilityModule = i
    },

It seems normal to me since UtilityModule is just an empty class with decorator. However, I cannot find any compiled code for its decorator. In fact, it seems that all decorators are missing including all components (there is not selector and template).

Okay, let's get back to the root component App. My first error is not mentioned above. My first error is come from the DynamicHtmlModule which originally I was using decorator to define metadata. But it rises similar error so I switch to the current way.

This is how it is built to be now

return e.prototype.ngOnInit = function() {
                var e = this
                  , t = new i.Component({
                    selector: "dynamic-html",
                    template: document.getElementById("appContent").innerText
                })
                  , n = i.Component(t)(function() {
                    function e() {}
                    return e
                }())
                  , s = i.NgModule({
                    imports: [o.UtilityModule, r.MemberModule],
                    declarations: [n]
                })(function() {
                    function e() {}
                    return e
                }());
                this.compiler.compileModuleAndAllComponentsAsync(s).then(function(e) {
                    return e.componentFactories.find(function(e) {
                        return e.componentType === n
                    })
                }).then(function(t) {
                    var n = i.ReflectiveInjector.fromResolvedProviders([], e.viewContainerRef.parentInjector);
                    e.viewContainerRef.createComponent(t, 0, n, [])
                })
            }

The metadata is clear.

I found a guy on SO with similar problem http://stackoverflow.com/questions/42537138/angular2-cli-tree-shaking-removing-dynamically-created-ngmodule/42693400?noredirect=1#comment72521082_42693400

@filipesilva
Copy link
Contributor

I don't think your example can ever work with AOT. You can still have production builds with it turned off via ng build --prod --no-aot.

If you think AOT should support your case please open an issue on https://github.com/angular/angular instead.

@emilio-martinez
Copy link
Contributor

emilio-martinez commented Apr 30, 2017

@filipesilva I agree, the example above is definitely not AOT-friendly. However, I would like to note that the title as is, "build --prod does not include metadata defined by decorator", very much is true, which is why I stumbled upon this issue, regardless of the above example.

I've had a project with my own build process (non Angular CLI, using ngc and then Webpack v2.x) for a while now where I've been AOT compiling with metadata. My use case is that Reflect still has access to the component metadata regardless of components being compiled into factories, so for example, I'm able to access the component selector. Note that this doesn't require the compiler, differently than what is outlined above.

A couple weeks ago I wanted to prototype a new component in "isolation", i.e., only with the bare parts I would need from my main project's architecture, so I scaffolded a new project with Angular CLI (post v1.0.0) and simply copied the needed parts from my other application. As expected, I'm able to AOT build just fine using the CLI—no errors, but when I load the app in a browser environment, Reflect.getMetadata('annotations', [component class]) returns an empty array. Again, note that this doesn't require the compiler, differently than what is outlined above.

I tried a couple configurations by setting values to genDir and skipMetadataEmit on angularCompilerOptions, but rocking the boat that way ends up in a confusing error: ERROR in BrowserAnimationsModule is not an NgModule. My guess is that what Angular CLI is doing behind the scenes simply doesn't play nice with modifying genDir.

Wrapping up, none of these are issues in my main project which has been working with AOT just fine. It may be that these use cases are simply unsupported by Angular CLI, but just wanted to note it because it most definitely isn't an issue with Angular itself.

@janwo
Copy link

janwo commented Aug 1, 2017

@filipesilva FYI I got the same error BrowserAnimationsModule is not an NgModule as soon as I add genDir to the angularCompilerOptions using ngtools/webpack.

@aplocher
Copy link

@filipesilva when I try your example using CLI 6.2.7 I get the following error:

The `--build-optimizer` option cannot be used without `--aot`.
Error: The `--build-optimizer` option cannot be used without `--aot`.

image

Is there an alternative? Thanks

@angular-automatic-lock-bot
Copy link

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.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 8, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants