angular-cli: Universal server bundle is not working properly
Bug Report or Feature Request (mark with an x
)
- [x] bug report -> please search issues before submitting
- [ ] feature request
Versions.
@angular/cli: 1.3.0-rc.1 node: 6.10.3 os: darwin x64
Repro steps.
Follow these steps here:
https://github.com/angular/angular-cli/wiki/stories-universal-rendering
Add https://github.com/ngx-translate/core to the app.
Build a server bundle.
Test it and you will see the error below.
The log given by the failure.
<some-excluded-path>/ng-boilerplate/node_modules/@ngx-translate/core/src/translate.store.js:1
(function (exports, require, module, __filename, __dirname) { import { EventEmitter } from "@angular/core";
^^^^^^
SyntaxError: Unexpected token import
at createScript (vm.js:56:10)
at Object.runInThisContext (vm.js:97:10)
at Module._compile (module.js:542:28)
at Object.Module._extensions..js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Module.require (module.js:497:17)
at require (internal/module.js:20:19)
at Object.17 (<some-excluded-path>/ng-boilerplate/dist-server/main.75d7fa7aeab5f9b24f61.bundle.js:1:6560)
at e (<some-excluded-path>/ng-boilerplate/dist-server/main.75d7fa7aeab5f9b24f61.bundle.js:1:149)
at Object.2cGb (<some-excluded-path>/chrillewoodz/ng-boilerplate/dist-server/main.75d7fa7aeab5f9b24f61.bundle.js:1:7952)
at e (<some-excluded-path>/ng-boilerplate/dist-server/main.75d7fa7aeab5f9b24f61.bundle.js:1:149)
at Object.Zq8w (<some-excluded-path>/ng-boilerplate/dist-server/main.75d7fa7aeab5f9b24f61.bundle.js:1:28947)
at e (<some-excluded-path>/ng-boilerplate/dist-server/main.75d7fa7aeab5f9b24f61.bundle.js:1:149)
Desired functionality.
Basically the issue here is that node is resolving the wrong module. It’s looking inside of the node_modules
folder instead of in the vendor
file in the server bundle.
Mention any other details that might be useful.
You can download a project with these steps already done:
https://github.com/chrillewoodz/ng-boilerplate/tree/universal
So simply run npm run universal
and you will see the error.
About this issue
- Original URL
- State: closed
- Created 7 years ago
- Reactions: 37
- Comments: 80 (19 by maintainers)
I tried workaround mentioned by @Toanzzz and quickly ran into this exception:
reflect-metadata shim is required when using class decorators
I tried importing thereflect-metadata
package which seemed to help with this issue, but I quickly ran into another one:Cannot read property 'subscribe' of undefined
. I also tried the solution supplied by @harshes53, but this one didn’t work at all to me and once again I got syntaxt error on theimport
token. At this point I called it quits - this issue is quite frustrating and I was even considering dropping use of angular-cli for the sake of some seed project which had universal support built-in. I’m still evaluating Angular 4.x and CLI, so I’m not working under the time pressure, but having such issues with integration of 3rd party libraries is a major concern to me at this point. Anyone has some other ideas how to resolve this?More investigation!
So I think I have something more definitive this time. I noticed that a lot of the packages had UMD bundles, however for some reason they were not being resolved into the Universal application. TLDR: libs that don’t produce a FESM build casue cli to produce deep imports where the umd bundle cannot be resolved.
I made a sample repository here showing casing 2 libs. lib1 - a properly formatted lib which can work with universal without problem lib2 - the average kind of lib that you see which runs into Universal issues
The big difference between them is that
lib2
will produce a build output where the JS files that useimport
/export
are not concatenated together or in other words are not flat es modules,lib1
does produce a flat esm build.If you run the demo with the command below (sorry about all the dependency installs) then you’ll notice that lib2 throws the error we’ve seen so much in this thread.
Well if you take a look at
app/dist/dist-server/main.bundle.js
and actrl + f
you’ll see that there have 2 entries:require("lib2/src/my-module")
andrequire("lib1")
AHA! Now we’re getting somewhere. So we know thatrequire("lib1")
will resolve to the umd bundle but a deep import likerequire("lib2/src/my-module")
has no option to be to resolve the es module, therefore casuing our issue!What can be done?
https://github.com/Toxicable/universal-deep-import
@intellix No, I gave up on SSR with angular. Went with prerendering approach (instead of SSR), used Puppeteer to go through all URLs, render the page, save resulting html file, then served html files as static. Drawback is that prerendering is significantly slower (even in multi-threaded environment).
For future project I’ll consider a framework (not Angular) that has SSR fixed (e.g React/Next.js, or stenciljs, or VueJS).
Is there any resolution on this issue yet? Or an ETA? Neither the
require('import-export')
or the webpack workarounds suggested above have worked for me; the first throws errors when using the@ng-bootstrap
datepicker module (Unexpected token export
from the npm), and the second throws various@NgModule annotation
errors that I can’t seem to work around at all (I’ve played whack-a-mole with them for hours now).We really need a way to bundle all node_modules for server-side rendering.
We are working on a fix with the libraries themselves. In the meantime, you can use (this is not a supported solution, just a fix) something like https://www.npmjs.com/package/import-export. Alternatively, you can disable AOT in the meantime.
I’m keeping this issue open in the meantime for keeping the discussion.
Here is the workaround that I’m currently using in my project: recompile the server output with webpack so that other 3rd party libs got transpile to commonjs for node to read
npm i webpack --save-dev
Hope it help
Even when I do my build with the ‘–no-aot’ flag, I still get the same error.
Has anyone else managed to work around this problem? I’d appreciate any help as this is currently completely blocking my plans to go live with my changes.
I’m seeing the same with
ng-bootstrap
. Any ideas how to fix this? I’m currently investigating using universal for our large project and the issue with integrating 3rd party libraries puts this work on hold.Just tried to use
import-export
, it passes the error place but gives another error:We’ve had the same issue with ng2-page-scroll. In that case, they have a umd version in a ‘bundles’ subfolder within the node module.
We wrote a simple node script to check main.bundle.js (created when we build for the server) for references to versions of ng2-page-scroll and replace them with the umd version.
This seems to fix the “unexpected token import” error at least- though I’m not sure if it actually works for the end result, as we have many more errors even after getting past this one. Could be worth a try.
The script we used is in this gist for reference: https://gist.github.com/J2D2Development/6f520dd991a6a33c1152aabcdf346790
for @MarkPieszak and anyone interested in using universal along with i18n translations in aot mode, as when compiling the server.ts to server.js it does not work (since the compilation adds the html strings into the resulting server.js file) here is how i manged to implement it:
first, in order to handle the unexpected token issue mentioned here earlier, i am using the solution @AnthonyNahas wrote here: SebastianM/angular-google-maps#1052 . this solution is specific to the problematic package and therfore does not require to compile the server.ts to server.js
regarding compilation, i am compiling the server packaged and browser pacakge the same as in the universal starter cli exmaple however i am doing it for every language while adding each language compilation into its own direcotry. the resulting structure is as follows: as you can see, i am also copying the assest folder into the dist folder (i am doing this with script off-course)
the last step after i have everything compiled is to run the node server using the server.js file with the following server.js file:
regarding all the static files, i am handling them as part of my hosting however you can also add them to the server.js file so that the node server will handle them.
Good Luck!
One workaround for now is to basically make your server.js file a TypeScript file, and have Webpack setup to process
/.ts$/
included yourexternals
, which are things likenode_modules / etc
.Take a look at the comment I made here for more info: https://github.com/angular/universal-starter/issues/428#issuecomment-331558400
Hopefully we can have this as an example in the universal-starter CLI demo as well soon.
We solved this issue by transforming
ngx-translate
with babel with a pre-script in our package.json.Eg. package.json
And the then in your components/services/whatver you can simply:
import { TranslateService } from '@ngx-translate/core';
At the moment no guidelines were provided regarding the “packaging format” making a lib working with Angular Universal. If someone could write these guidelines, I would be happy to help filling these guidelines and provide PRs to some libs to move things along. Most of Angular application needs Universal working to go live, we need to fill this SSR gap as soon as possible. @Toxicable do you have at least some bullet points to provide as you deeply investigate the problem? Thank you for your clear explanations by the way
Same issue. @hansl any updates or ETA? @AnthonyNahas’s answer is a good work around
I was having the issue where 3rd party dependencies (like zone.js) weren’t getting included (the main.bundle.js was using a
require
statement to grab it from node_modules which isn’t on our prod environment) I was able to resolve it by adding the--bundle-dependencies=all
argument to myng build
command. For Angular 6 you would need to add the"bundleDependencies": "all"
property to theconfigurations
property in theangular.json
file. Not sure if that helps anyone else, just thought I’d share it as I didn’t see mention of it here.Thanks for looking into this Toxicable
Are you saying that there is no intention for the angular/cli team to allow packaging of 3rd party node_modules imported into a project to run using Angular Universal and that library maintainers will need to convert all packages to commonjs format for distribution in order for them to be used in server side rendering? Because that’s a pretty big ask of the vendors, and will, for a team like ours who are using multiple vendor packages, mean we won’t be able to go live with our Angular Universal project for potentially months whilst the 3rd parties sort this out
same issue with here with
ng-bootstrap
@Toanzzz I’m going to try your workaround now. One comment: by using
--output-hashing=none
you can avoidgetHash()
function, cause bundle name is alwaysmain.bundle.js
.Just tried, I get the same error (the first one).
So you know, there was the exact same error with
@angular/flex-layout
beta 8, but with the last builds, it’s gone, because they updated to the official way Angular modules are build. One main difference is that there is now aes2015
entry point in theirpackage.json
, but I don’t know if it’s what solved the problem, or a change in their build config.One point which surprised me first is that the server bundle only contains the app code. So Angular packages (
@angular/core
and so on) and all other packages need to be installed on the server project too. And the error seems to show that the server bundle requests the packages fromnode_modules
in the wrong format (it’s es2015, while it should be commonjs).Shouldn’t the CLI produce a vendor bundle too, to be sure everything is here in the good format ?
yeah, I’m still waiting for universal working smooooothly 😢
@dannyrevenant Please don’t twist what i’ve said into your own opinion, No that is not what I’m saying.
My words are only of my own opinion, I do not speak on behalf of any groups, I am just a regular dev that enjoys contributing to Angular and surrounding communities, most notably Universal as of recent.
As for what the CLI team can do; The CLI does support importing modules from npm into a Universal project, take Material for example, that works with Universal fine since it’s correctly packaged, exemplifying that this isn’t an issue with the CLI, instead an issue with the 3rd parties packaging format. However I believe there is some webpack magic that they might be able to configure to allow for importing incorrectly formatted packages but it’s not obvious, simple or a good solution, I did not investigate this route since I was not looking for a solution by changing webpack config.
For your mention on packaging formats; no I am not suggesting that libs package their packages solely for Universal, if you would take some time to lookup how libs should be distributing packages then you’d find that you can and should distribute your package in multiple formats so that different formats can be correctly resolved for different situations.
The best thing you can do to getting a resolution to this issue is ask maintainers of the projects with incorrect packaging formats to correct it, or better yet, whip up a PR youreself, I’m sure they’ll appreciate it.
I’ve been doing a bit of investigation on this the last few days trying a few solutions but havn’t come up with anything that’ll do it easily.
Just to reiterate the actual issue here; The Library being used is for packaged/formatted correctly. For Universal to be able to use a node package is must be commonjs formatted, meaning that it uses require not import since node does not currently support esm modules (import/export). Which means this is not an issue with the CLI, it’s an issue with the libraries not packaging their modules correctly.
Work around i’ve tried: require(‘import-export’)
Node nightly build
re-packaging libs
tsc
to override libs into the correctcommonjs
format and successfully used that lib in a Universal project. However this is not a good solution at all, it worked for the lib I was testing on but I don’t know if it will work for all, and it’s a pretty ugly lots of scriptsFrom my investigations my conclusion is that the best way to work around this is getting libraries to correctly format their packages so that they can be consumed in a node environment without issue
Just tryied @Toanzzz and @harshes53 solutions and I finally received this exception:
Sounds great 😃 Any early estimate on when this could be resolved? Just so I can plan ahead a bit.
If it can help, I did a blog post with full explanations.
First it will help you about the hash (no need of it), and second the first thing I would consider is to stick to node. ts-node is a great tool, but in this case it adds more complexity to an already complex configuration, you can do the same with just node.
@MarkPieszak thanks you for your suggestion, I’m successful at creating webpack config. That so cool 😃
Can you update to the latest universal-starter? Using a server.ts file gives you the ability to have your own webpack file (when making that server bundle) so you’ll be all set 😀 @hiepxanh
Hey. @MarkPieszak solution works fine (as seen in universal starter) but it has one problem. if you are working with angular i18 for translation and aot, the translations are part of the compiled html. in @MarkPieszak solution, the server.js output file includes all the htmls inside and therefore makes it impossible to create few packages for different language. @AnthonyNahas solution solves the problem specifically for each package during the package download time and by that allows working with i18 inside universal. you can check the solution here: https://github.com/SebastianM/angular-google-maps/issues/1052 Thanks Anthony
Not to mention a lot of third party authors are lazy and don’t care. So it would require a pull request from an outsider and that is an even bigger ask.
same issue with ng-cookies. hello ,i followed your advice ,but not working @harshes53
externals: [nodeExternals({whitelist: [/ngx-cookie/]})],
@kirillgroshkov did you ever manage to fix the issue that you get after installing
import-export
? Getting the same thing about the unexpected string regarding:/node_modules/@angular/compiler/bundles/compiler.umd.js:19810
Interestingly, this is the code that it’s complaining about:
i fix similar issue with another npm module (@ngrx). take a look to my solution (only as work around)
#581
if you are using webpack, just whitelist the package causing
import
syntax error using webpack’sexternals
property. Letts-loader
compile the package… not the best workaround but it works for me! I had the same issue when usingngx-cookie
package. seems like these packages are using ES6 modules syntax while Node.js still usescommon.js
module syntax.e.g:
Hope it helps…
There, I’ve pushed the latest changes. Now you should be able to find a
"universal": "ng build --prod && ng build --prod --app 1 && ts-node server",
script in the package json and also the cli at the latest rc.3 release.