angular / universal

Server-side rendering and Prerendering for Angular
MIT License
4.04k stars 483 forks source link

Express Engine returns a 'file not found' error with Angular 9 RC 1, Universal Next 6 #1327

Closed justinappler closed 4 years ago

justinappler commented 4 years ago

Bug Report

When serving requests using the express engine and a ServerAppModule built with CLI/Core 9 RC1, the express engine callback contains an error that the component template file cannot be found.

What is the expected behavior?

Engine returns HTML

What is the current behavior?

Engine returns the following error: Error: ENOENT: no such file or directory, open 'auth-to-contribute.component.html' (where the component file is part of the ServerAppModule).

What modules are related to this issue?

- [ ] aspnetcore-engine
- [ ] common
- [X] express-engine
- [ ] hapi-engine
- [ ] module-map-ngfactory-loader

Minimal reproduction with instructions:

Don't have a minimal repro case unfortunately.

Environment:

@nguniversal versions

Package Version

@angular-devkit/architect 0.900.0-rc.1 @angular-devkit/build-angular 0.900.0-rc.1 @angular-devkit/build-optimizer 0.900.0-rc.1 @angular-devkit/core 9.0.0-rc.1 @angular-devkit/schematics 9.0.0-rc.1 @angular/cdk 9.0.0-rc.0 @angular/material 9.0.0-rc.0 @nguniversal/common 9.0.0-next.6 @nguniversal/express-engine 9.0.0-next.6 @nguniversal/module-map-ngfactory-loader 9.0.0-next.6 @schematics/angular 8.0.0 @schematics/update 0.900.0-rc.1 rxjs 6.5.3 typescript 3.6.4 webpack 4.40.2



#### Is there anything else we should know?
I started following the stacktrace for where this error is triggered, and it seems to be the if block in `_preParseTemplate` in `directive_normalizer.ts` (it ultimately fails to fetch the template). Interestingly, this doesn't appear to be a problem for library component templates (Material2 components don't try to fetch the template as its part of the `PrenormalizedTemplateMetadata`). For the components internal to my app, however, it tries and fails to find them on the filesystem.
alan-agius4 commented 4 years ago

Hi, I tried to reproduce locally and didn't manage. Can you please share a reproduction?

Also, are you update from an Angular 8 application? If that's the case, did you run ng update @nguniversal/express-engine@next?

Thanks.

justinappler commented 4 years ago

I tried to do a minimal reproduction and didn't encounter the issue. Apparently it's something specific to my configuration. I was, in fact, upgrading from Angular 8 and did the migration, but the migration is extremely optimistic (it assumes that I can use the server.ts generated by the migration, but my server.ts is more complicated than the out of the box one).

I did however incorporate what seem to be the major changes I've noticed so far going from 8 -> 9. 1) Removal of the ModuleMapLoader 2) Switching from bootstrapping the engine using the ServerAppModuleNgFactory to using the ServerAppModule directly

With those changes, I still get this error at the location I mentioned in the "Is there anything else we should know?" section.

vikerman commented 4 years ago

Hi - I think Point no.2 you mentioned is the issue

Switching from bootstrapping the engine using the ServerAppModuleNgFactory to using the ServerAppModule directly

You are ending up with JIT mode which is probably not what you intended. Can you share how you are building the server bundle. We need to make sure the server build is using AOT mode.

justinappler commented 4 years ago

@vikerman Here is my Angular.json https://gist.github.com/justinappler/aa371fcb9f2fcbbfd1aca071d1c6f95e

To clarify, in v9, I should be using the ServerAppModule, correct? Are you saying that modules can be built either in JIT or AOT mode in v9?

alan-agius4 commented 4 years ago

By any chance are you still using the webpack node script to compile your server.ts?

I also noticed that the server builder options are not updated.

Take a look at an example here: https://github.com/angular/universal/blob/b14ca211780f07ade0424a21e2f604319aa47d75/integration/express-engine-ivy/angular.json#L154

On Wed, 20 Nov 2019 at 20:35, Justin Appler notifications@github.com wrote:

@vikerman https://github.com/vikerman Here is my Angular.json https://gist.github.com/justinappler/aa371fcb9f2fcbbfd1aca071d1c6f95e

To clarify, in v9, I should be using the ServerAppModule, correct? Are you saying that modules can be built either in JIT or AOT mode in v9?

— You are receiving this because you commented.

Reply to this email directly, view it on GitHub https://github.com/angular/universal/issues/1327?email_source=notifications&email_token=AEF74WRCRO6NEAGKLYLWY23QUWGRDA5CNFSM4JLBOHSKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEEUFOEQ#issuecomment-556291858, or unsubscribe https://github.com/notifications/unsubscribe-auth/AEF74WVWEKH4QE7ZUK6U4ADQUWGRDANCNFSM4JLBOHSA .

alan-agius4 commented 4 years ago

I think what is happening is that you are compiling your server app entirely with webpack ts-loader which is ending up being compiled in JIT mode.

In v9, the external webpack-cli part was removed and is now part of ng run project:server.

During v9 update there are also a couple of changes apart from the changes mentioned above.

These are;

I would strongly recommend to use ng update and than update the server.ts with your logic.

Also, are you using Ivy or VE?

justinappler commented 4 years ago

@alan-agius4 So I'm actually building the server bundle with the CLI using the @angular-devkit/build-angular:server builder and the CLI, then building an Express server server.ts bundle using a custom webpack config (and require()ing the Angular server module bundle that was generated by the server builder into the express server.ts bundle). The custom webpack TS Loader for the express server does not compile any part of the Angular code.

It's not trivial for us to just move all the express server code we wrote over to the server.ts that is generated as part of the 9.0 migration. If that's what we need to do, our migration path just got a lot longer. What I'm trying to do is figure out the differences, and apply those differences to my express server.

I think I'm using Ivy, in that I'm using 9.0 and not turning Ivy off in the tsconfig. It seems possible though, per @vikerman 's comment, that my server bundle is not getting built in AOT mode (hence why it can't find the template files).

alan-agius4 commented 4 years ago

I have a hunch on what's happening.

Since you are using the CLI to build your server, your build is AOT because in the CLI we don't offer a way to build a server in JIT mode. What I think is happening is that you are fall backing to JIT mode during runtime.

I am going to assume a couple of things and kindly correct me if this is not the case in your project.

  1. You previously didn't bundle server dependencies via the bundleDependencies option
  2. In your server.ts file you have imports to @angular/<package> and/or @nguniversal/<package>

Node is unable to resolve the entrypoints generated by NGCC integration with the CLI. Since such entrypoints are created to be used by bundlers. One example of such entrypoints is the main field in package.json which points to the VE version of the code, while running NGCC via the CLI a new field will be created ex: main_ivy_ngcc points which points to the Ivy version of the code.

To keep your existing setup with a separate webpack compilation with ts-loader for your server.ts file.

You need to do a couple of things:

Nb: After doing the above changes you need to nuke your node_modules and install them again.

rm -rf node_modules
npm/yarn install

With this approach the entrypoints will be overridden with the Ivy equivalent which will cause Node to be able to resolve such entrypoints during runtime.

justinappler commented 4 years ago

Hi @alan-agius4 ,

I followed the steps you suggested, and did the following:

Modified the server target and set "bundleDependencies": "none". This doesn't appear to have been applied before as the server bundle went from ~20MB to ~7MB.

My package.json already had a postinstall script from the migration to Angular 9 with the following command:

ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points

This command runs fine. However, when I modified it to be simply ngcc, I get the following error and it doesn't finish:

Error: Error on worker #6: Error: Unable to write a reference to CdkCopyToClipboard in /Users/justinappler/crunchbase/client_app/node_modules/@angular/cdk/esm2015/clipboard/copy-to-clipboard.js from /Users/justinappler/crunchbase/client_app/node_modules/@angular/cdk/esm2015/clipboard/clipboard-module.js
    at ReferenceEmitter.emit (/Users/justinappler/crunchbase/client_app/node_modules/@angular/compiler-cli/src/ngtsc/imports/src/emitter.js:55:19)
    at Object.toR3Reference (/Users/justinappler/crunchbase/client_app/node_modules/@angular/compiler-cli/src/ngtsc/annotations/src/util.js:165:31)
    at NgModuleDecoratorHandler._toR3Reference (/Users/justinappler/crunchbase/client_app/node_modules/@angular/compiler-cli/src/ngtsc/annotations/src/ng_module.js:348:31)
    at /Users/justinappler/crunchbase/client_app/node_modules/@angular/compiler-cli/src/ngtsc/annotations/src/ng_module.js:173:83
    at Array.map (<anonymous>)
    at NgModuleDecoratorHandler.analyze (/Users/justinappler/crunchbase/client_app/node_modules/@angular/compiler-cli/src/ngtsc/annotations/src/ng_module.js:173:48)
    at Object.analyzeDecorators (/Users/justinappler/crunchbase/client_app/node_modules/@angular/compiler-cli/ngcc/src/analysis/util.js:75:38)
    at DecorationAnalyzer.analyzeClass (/Users/justinappler/crunchbase/client_app/node_modules/@angular/compiler-cli/ngcc/src/analysis/decoration_analyzer.js:138:40)
    at /Users/justinappler/crunchbase/client_app/node_modules/@angular/compiler-cli/ngcc/src/analysis/decoration_analyzer.js:131:55
    at Array.map (<anonymous>)
    at ClusterMaster.onWorkerMessage (/Users/justinappler/crunchbase/client_app/node_modules/@angular/compiler-cli/ngcc/src/execution/cluster/master.js:158:27)
    at /Users/justinappler/crunchbase/client_app/node_modules/@angular/compiler-cli/ngcc/src/execution/cluster/master.js:46:95
    at ClusterMaster.<anonymous> (/Users/justinappler/crunchbase/client_app/node_modules/@angular/compiler-cli/ngcc/src/execution/cluster/master.js:238:57)
    at step (/Users/justinappler/crunchbase/client_app/node_modules/tslib/tslib.js:136:27)
    at Object.next (/Users/justinappler/crunchbase/client_app/node_modules/tslib/tslib.js:117:57)
    at /Users/justinappler/crunchbase/client_app/node_modules/tslib/tslib.js:110:75
    at new Promise (<anonymous>)
    at Object.__awaiter (/Users/justinappler/crunchbase/client_app/node_modules/tslib/tslib.js:106:16)
    at EventEmitter.<anonymous> (/Users/justinappler/crunchbase/client_app/node_modules/@angular/compiler-cli/ngcc/src/execution/cluster/master.js:232:32)
    at EventEmitter.emit (events.js:210:5)

If I clear node_modules, yarn install, run the original ngcc command with the additional flags, build the server module bundle, build the server.ts bundle, then run it, I still get the 'cant find component file'.

Also, just want to say that I really appreciate your help in debugging this and providing so much direct assistance.

alan-agius4 commented 4 years ago

The below NGCC binary call will only work if you use the CLI to bundle all your bundles.

ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points

Note: that now we removed this has been removed and no longer gets added in project when they migrate.

The Error on worker #6: Error: Unable to write a reference error you are experiencing should be solved or at least it should happen far less often in RC 3 as per https://github.com/angular/angular/commit/14156bd26265fcc18fa3635566c65af37dd7a6a9

alan-agius4 commented 4 years ago

Any luck with RC 3?

justinappler commented 4 years ago

@alan-agius4 Same error unfortunately. I've started focusing my efforts on trying to move code out of the express server so that I can build it without a customized Webpack config (and thus move it under the purview of the CLI).

alan-agius4 commented 4 years ago

Awesome, if you have any questions or queries let me know 😊

justinappler commented 4 years ago

@alan-agius4 So I've now done the needed changes to get our app to use the CLI to build the server. The compilation looks like it worked fine (I get a single main.js bundle now that starts the express server properly). That said, the issue persists 😞 (rendering a page errors with an inability to fine a template HTML file).

One thing I've noticed: the CLI seems to be doing a replacement of process.env.NODE_ENV with the string development. It's not great that the CLI does this replacement, but setting that aside, it seems strange that the CLI's webpack is setting that variable to development when running this in the production configuration. What is it in the server devkit builder that handles putting the server bundle build into AOT/Prod mode, because that seems to not be working for me.

alan-agius4 commented 4 years ago

That replacement is done by webpack it reflects the mode that webpack is running.

To enable prod modeyou need to enable the optimization option: https://github.com/angular/universal/blob/b14ca211780f07ade0424a21e2f604319aa47d75/integration/express-engine-ivy/angular.json#L171

That being said, dev/prod mode shouldn’t cause that error.

justinappler commented 4 years ago

@alan-agius4 Interestingly, switching to optimization: :"true" changed the error. Now I'm getting a "No NgModule metadata found for 'ServerAppModule'." error.

alan-agius4 commented 4 years ago

Can you kindly send a snippet of the compiled a component/module ideally without optimizations please?

justinappler commented 4 years ago

@alan-agius4 Here you go: https://drive.google.com/file/d/1VX2Qp6sbsFT6O-I9IAhRj5xJy505HzL7/view?usp=sharing

Also happy to jump on a Hangout/Zoom or something if it helps, just let me know (you can reach out directly at justin (at) crunchbase.com)

alan-agius4 commented 4 years ago

@justinappler, thanks for the above.

Let me go through that and will reach out to you in a day or two.

Thanks once again.

alan-agius4 commented 4 years ago

Bdw, I couldn't help but notice but auth-to-contribute.component.html is not part of the bundle. How is this being loaded?

justinappler commented 4 years ago

@alan-agius4 It's definitely in there, I think Google Drive lazy loads the document as you scroll through:

Screen Shot 2019-11-26 at 10 40 32 AM
alan-agius4 commented 4 years ago

Silly me you are right, I'll download it and review it tomorrow. Thanks once again for your copereration.

alan-agius4 commented 4 years ago

2 questions;

  1. Does the error appear when using bundleDependencies: all or remove this setting (Which in version 9 is enabled by default), as I noticed that node_modules are external in your bundle?
    
    /***/ "@angular/core":
    /*!********************************!*\
    !*** external "@angular/core" ***!
    \********************************/
    /*! no static exports found */
    /***/ (function(module, exports) {

module.exports = require("@angular/core");

/***/ }),



2. Since you have node dependencies not bundled you are using the NGCC postinstall hook? If so, make sure you are not passing one of the following. `--first-only` or `--create-ivy-entry-points` as otherwise the resolve dependency would be resolved to the VE version. (Note: if you have these settings, you will need to do a fresh `npm install` after removing them.)

Thanks before hand.
alan-agius4 commented 4 years ago

Note: if remove bundleDependencies : none, you don't need to alter the ngcc postinstall hook or you might want to consider to remove it altogether.

justinappler commented 4 years ago

Progress! When I change the postinstall script to: ngcc --properties es2015 browser module main and keep bundleDependencies: "none" and optimization: true, requests no longer return an error. I get a new error that seems to prevent page from actually rendering on the server, but it appears unrelated to this issue:

ERROR Error: Multiple components match node with tagname mat-toolbar
    at throwMultipleComponentError (/node_modules/@angular/core/bundles/core.umd.js:5443:15)
    at findDirectiveMatches (/node_modules/@angular/core/bundles/core.umd.js:9633:29)
    at resolveDirectives (/node_modules/@angular/core/bundles/core.umd.js:9494:26)
    at Object.ɵɵelementStart (/node_modules/@angular/core/bundles/core.umd.js:15949:33)

So, if I'm not bundling dependencies (which I'd prefer not to for build speed and such), it seems I'll need to keep the postinstall script as it is above?

alan-agius4 commented 4 years ago

So, if I'm not bundling dependencies (which I'd prefer not to for build speed and such), it seems I'll need to keep the postinstall script as it is above?

Yes that is correct.

alan-agius4 commented 4 years ago

Indeed the error you are seeing doesn’t seem to be related to this issue. And now you are running in Ivy mode.

justinappler commented 4 years ago

So, if I'm not bundling dependencies (which I'd prefer not to for build speed and such), it seems I'll need to keep the postinstall script as it is above?

Yes that is correct.

OK, thats seems very doable.

Indeed the error you are seeing doesn’t seem to be related to this issue. And now you are running in Ivy mode.

Excellent, I'll look into that issue separately. Hopefully after that I can get a sense of the SSR performance with Ivy! Thanks for your help!

alan-agius4 commented 4 years ago

Closing as the original error seems to have been resolved.

Please feel free to open a new issue, if you have any further problems.

angular-automatic-lock-bot[bot] commented 4 years ago

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.