Skip to content
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

Angular Universal build prerender error #370

Closed
1 of 3 tasks
kmturley opened this issue Jan 8, 2019 · 23 comments
Closed
1 of 3 tasks

Angular Universal build prerender error #370

kmturley opened this issue Jan 8, 2019 · 23 comments
Labels

Comments

@kmturley
Copy link

kmturley commented Jan 8, 2019

  • I'm submitting a ...

    • bug report
    • feature request
    • support request
  • Do you want to request a feature or report a bug?
    Bug with your library and Angular Universal build prerender functionality

  • What is the current behavior?
    When you run the command:
    npm run build:prerender

it runs:
"generate:prerender": "cd dist && node prerender",

Then fails with the error:

(function (exports, require, module, __filename, __dirname) { import { NgModule } from '@angular/core';
                                                              ^^^^^^

SyntaxError: Unexpected token import
    at createScript (vm.js:80:10)
    at Object.runInThisContext (vm.js:139:10)
    at Module._compile (module.js:617:28)
    at Object.Module._extensions..js (module.js:664:10)
    at Module.load (module.js:566:32)
    at tryModuleLoad (module.js:506:12)
    at Function.Module._load (module.js:498:3)
    at Module.require (module.js:597:17)
    at require (internal/module.js:11:18)
    at Object.ng-lazyload-image/src/lazyload-image.module (/dist/server/main.js:1350:18)
  • provide the steps to reproduce
  1. Duplicate the Angular Universal starter project from:
    https://github.com/angular/universal-starter

  2. Add your library, following install instructions:
    https://github.com/tjoskar/ng-lazyload-image

  3. Run the Angular build command to see the error:
    npm run build:prerender

  • What is the expected behavior?
    No error, and to continue building.

  • What is the motivation / use case for changing the behavior?
    Otherwise your plugin cannot be used with Angular Universal, which means no static site generation :(

  • Please tell us about your environment:

    • MacOS 10.13.6
    • node 8.9.1
    • ng-cli 6.0.0 and tested with 7.1.4
    • angular 6.0.0 and tested with 7.1.4
    • nguniversal 6.0.0 and tested with 7.0.2
  • Other information

Looks like other people have had similar problems with Angular Universal and third-party libraries such as yours:
angular/angular-cli#7200 (comment)

They say the third-party libraries aren't being built correctly, which means Angular Universal fails:
angular/angular-cli#7200 (comment)

for example they suggest adding to your package.json

"module": "./quickstart-lib.es5.js",
"es2015": "./quickstart-lib.js",
"typings": "./quickstart-lib.d.ts",

Approach 1
Patch your plugin root:
npm install @babel/cli @babel/core @babel/preset-env @babel/plugin-transform-modules-umd

Adding a .babelrc file in the root of your plugin folder:

{
  "plugins": [["@babel/plugin-proposal-decorators", { "decoratorsBeforeExport": true }]],
  "presets": ["@babel/preset-env"]
}

Updating your plugins package.json

"main": "./lib-umd/index.js",
"module": "./lib-es5/index.js",
"es2015": "./lib/index.js",
"typings": "./lib/index.d.ts",
"scripts": {
  "build:umd": "babel ./lib/*.js --out-dir ./lib-umd --plugins @babel/plugin-transform-modules-umd",
  "build:es5": "babel ./lib/*.js --out-dir ./lib-es5"
}

Then running the build:
npm run build:es5 && npm run build:umd

And adding to my own project tsconfig.json

"compilerOptions": {
  "paths": { "@angular/*": ["../node_modules/@angular/*"] },
}

But still getting the same error with Angular Universal :(

Approach 2
Use the Typescript build options for the example project at:
https://github.com/filipesilva/angular-quickstart-lib

@xmasuku
Copy link

xmasuku commented Jan 11, 2019

I also got the same error, I have angular universal installed

(function (exports, require, module, __filename, __dirname) { import { LazyLoadImageDirective } from './src/lazyload-image.directive';
                                                              ^^^^^^

SyntaxError: Unexpected token import
    at createScript (vm.js:80:10)
    at Object.runInThisContext (vm.js:139:10)
    at Module._compile (module.js:617:28)
    at Object.Module._extensions..js (module.js:664:10)
    at Module.load (module.js:566:32)
    at tryModuleLoad (module.js:506:12)
    at Function.Module._load (module.js:498:3)
    at Module.require (module.js:597:17)
    at require (internal/module.js:11:18)
    at eval (webpack:///external_%22ng-lazyload-image%22?:1:18) ```

@kmturley
Copy link
Author

I ended up writing my own directive which works with Universal:

import { Directive, ElementRef, Inject, Input, OnInit, PLATFORM_ID} from '@angular/core';
import { isPlatformBrowser } from '@angular/common';

@Directive({
  selector: '[appLazyLoadImage]'
})
export class LazyLoadImageDirective implements OnInit {
  @Input() srcLazy: string;

  constructor(
    private el: ElementRef,
    @Inject(PLATFORM_ID) private platformId: Object,
  ) { }

  ngOnInit() {
    // only run lazy image loading in the browser
    if (isPlatformBrowser(this.platformId)) {
      // if browser supports IntersectionObserver
      if ('IntersectionObserver' in window) {
        const lazyImageObserver = new IntersectionObserver((entries, observer) => {
          entries.forEach((entry) => {
            if (entry.isIntersecting) {
              entry.target.setAttribute('src', this.srcLazy);
              entry.target.classList.add('lazy-loaded');
              lazyImageObserver.unobserve(entry.target);
            }
          });
        });
        lazyImageObserver.observe(this.el.nativeElement);
      } else {
        // Otherwise replace image by default
        this.el.nativeElement.setAttribute('src', this.srcLazy);
      }
    }
  }

}

import it into your Module:

import { LazyLoadImageDirective } from './lazy-load-image.directive';
...
@NgModule({
  imports: [
    CommonModule
  ],
  declarations: [
    LazyLoadImageDirective
  ],
  exports: [
    CommonModule,
    LazyLoadImageDirective
  ]
})
...etc

and use on an image with:
<img src="../assets/placeholder.jpg" srcLazy="../assets/myimage.jpg" alt="Example" appLazyLoadImage />

@xmasuku
Copy link

xmasuku commented Jan 14, 2019

I also got the same error, I have angular universal installed

(function (exports, require, module, __filename, __dirname) { import { LazyLoadImageDirective } from './src/lazyload-image.directive';
                                                              ^^^^^^

SyntaxError: Unexpected token import
    at createScript (vm.js:80:10)
    at Object.runInThisContext (vm.js:139:10)
    at Module._compile (module.js:617:28)
    at Object.Module._extensions..js (module.js:664:10)
    at Module.load (module.js:566:32)
    at tryModuleLoad (module.js:506:12)
    at Function.Module._load (module.js:498:3)
    at Module.require (module.js:597:17)
    at require (internal/module.js:11:18)
    at eval (webpack:///external_%22ng-lazyload-image%22?:1:18) ```

works with this

added this on webpack config

        whitelist: [
            /^ng-lazyload-image/,
          ]
    }) ```

@tjoskar
Copy link
Owner

tjoskar commented Jan 25, 2019

I guess angular universal only can import ComonJS modules and since ng-lazyload-image only have ES-modules as target angular universal fails.

I guess we have to set up multiple targets for ng-lazyload-image, as @kmturley describe. But that would also mean that tree shaking won't work.

@xmasuku, can you give a more detail example of your webpack config? Where do you use whitelist?

@xmasuku
Copy link

xmasuku commented Feb 5, 2019

@tjoskar I used my custom webpack build, I was not using angular cli

@xmasuku
Copy link

xmasuku commented Feb 5, 2019

I have generated a cli project and I get the same error

@tjoskar tjoskar added the bug label Feb 5, 2019
@tjoskar
Copy link
Owner

tjoskar commented Feb 5, 2019

Okay, I will try to take a look at it tonight

@Loutrinos
Copy link

I experience the same issue while trying to build with angular universal. @tjoskar have you thought about using ng-packagr to build your library? You could build and bundle your library in FESM2015, FESM5, and UMD formats.
Have a look here: https://github.com/ng-packagr/ng-packaged/blob/master/package.json#L11 (example project using ng-packagr).

@xmasuku
Copy link

xmasuku commented Feb 13, 2019

// Looks like its the common issue for universal eg. angular/angular-cli#7200
// I have this setup on my project
// replace this: --- "compile:server": "tsc -p server.tsconfig.json"
// with this: --- "compile:server": "node --max_old_space_size=3072 node_modules/webpack/bin/webpack.js --config webpack.server.config.js --progress --colors"

// then create webpack.server.config.js add code below:

const path = require('path');
const webpack = require('webpack');
const nodeExternals = require('webpack-node-externals');

module.exports = {
  mode: 'none',
  entry: {
    // This is our Express server for Dynamic universal
    server: './server.ts'
  },
  target: 'node',
  resolve: { extensions: ['.ts', '.js'] },
  externals: [ nodeExternals({
    whitelist: [
        /^ng-lazyload-image/,
    ]
  }), /.*?webpack.*?/i ],
  optimization: {
    minimize: false
  },
  output: {
    // Puts the output at the root of the dist folder
    path: path.join(__dirname, 'dist'),
    filename: '[name].js'
  },
  module: {
    rules: [
      { test: /\.ts$/, loader: 'ts-loader' },
      {
        // Mark files inside `@angular/core` as using SystemJS style dynamic imports.
        // Removing this will cause deprecation warnings to appear.
        test: /(\\|\/)@angular(\\|\/)core(\\|\/).+\.js$/,
        parser: { system: true },
      },
    ]
  },
  plugins: [
    new webpack.ContextReplacementPlugin(
      // fixes WARNING Critical dependency: the request of a dependency is an expression
      /(.+)?angular(\\|\/)core(.+)?/,
      path.join(__dirname, 'src'), // location of your src
      {} // a map of your routes
    ),
    new webpack.ContextReplacementPlugin(
      // fixes WARNING Critical dependency: the request of a dependency is an expression
      /(.+)?express(\\|\/)(.+)?/,
      path.join(__dirname, 'src'),
      {}
    )
  ]
};

// my server.ts

import 'zone.js/dist/zone-node';
import {enableProdMode} from '@angular/core';
// Express Engine
import {ngExpressEngine} from '@nguniversal/express-engine';
// Import module map for lazy loading
import {provideModuleMap} from '@nguniversal/module-map-ngfactory-loader';

import * as express from 'express';
import {join} from 'path';

// Faster server renders w/ Prod mode (dev mode never needed)
enableProdMode();

// Express server
const app = express();

const PORT = process.env.PORT || 4000;
const DIST_FOLDER = join(process.cwd(), 'dist/browser');

// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const {AppServerModuleNgFactory, LAZY_MODULE_MAP} = require('./dist/server/main');

// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
app.engine('html', ngExpressEngine({
  bootstrap: AppServerModuleNgFactory,
  providers: [
    provideModuleMap(LAZY_MODULE_MAP)
  ]
}));

app.set('view engine', 'html');
app.set('views', DIST_FOLDER);

// Example Express Rest API endpoints
// app.get('/api/**', (req, res) => { });
// Serve static files from /browser
app.get('*.*', express.static(DIST_FOLDER, {
  maxAge: '1y'
}));

// All regular routes use the Universal engine
app.get('*', (req, res) => {
  res.render('index', { req });
});

// Start up the Node server
app.listen(PORT, () => {
  console.log(`Node Express server listening on http://localhost:${PORT}`);
});

@dockleryxk
Copy link

For now, this should work.

tjoskar added a commit that referenced this issue Feb 17, 2019
@Loutrinos
Copy link

@tjoskar I used your branch and it seems to fix the issue with the build error. Do you have a plan when you are going to merge and release it?

@tjoskar
Copy link
Owner

tjoskar commented Feb 27, 2019

@Loutrinos, Sorry for the delay. Can you please try to install [email protected] (beta) to see if that solves the problem. Thanks.

@Loutrinos
Copy link

Loutrinos commented Feb 28, 2019

@tjoskar I see in the 5.1.0 release that the structure is different. This is ok for now as it's a beta release.
With 5.1.0 Angular Universal doesn't break anymore :P

@tjoskar
Copy link
Owner

tjoskar commented Mar 6, 2019

@Loutrinos,

I see in the 5.1.0 release that the structure is different

Sorry about that, I created a new release ([email protected]) with the same structure as before. My previous weeks have been quite hectic but now I'm back to business so let me know how it works for you.

@agustintarifa
Copy link

After installed 5.1.1 version and I'm getting this error:
ReferenceError: IntersectionObserver is not defined

@tjoskar
Copy link
Owner

tjoskar commented Mar 11, 2019

@agustintarifa How does your tsconfig.json file looks like?

I just created this repo: https://github.com/tjoskar/ng-lazyload-image-bugs/tree/master/370-universal-starter-compile and it seams to work fine. I cloned https://github.com/angular/universal-starter, installed ng-lazyload-image (npm install [email protected]), added an image, compiled with npm run build:prerender and then started the server: node dist/server.js.

@agustintarifa
Copy link

Thanks for the answer @tjoskar
It's working now, I changed my modules.
In the app.module use: LazyLoadImageModule.forRoot({}),
And in the other modules: LazyLoadImageModule.forRoot({ preset: intersectionObserverPreset, }),

The only problem, the offset is not working at the top to see the first image I need to scroll 1px at least and then the other one loads when are visible, I want to load the images 200px before

@tjoskar
Copy link
Owner

tjoskar commented Mar 13, 2019

Humm.. In my example above I only declare LazyLoadImageModule in app.module and it is working fine and I can't really reproduce the error. It would be super helpful if some one could create a small repo to reproduce the error (or provide step by step instructions).

@agustintarifa, Regarding the offset problem. Can you create a new issue for that?

@vytautas-pranskunas-
Copy link

Hi, when can we expect v5.1.1 as a stable not beta? Because i have this problem with SSR too.

@tjoskar
Copy link
Owner

tjoskar commented Apr 1, 2019

@vytautas-pranskunas-, I will release it tomorrow with some other smaller fixes.

@vytautas-pranskunas-
Copy link

vytautas-pranskunas- commented Apr 1, 2019 via email

tjoskar pushed a commit that referenced this issue Apr 2, 2019
@tjoskar
Copy link
Owner

tjoskar commented Apr 3, 2019

[email protected] is now out. Let me know it the error still occurs.

@tjoskar tjoskar closed this as completed Apr 3, 2019
@felipefialho
Copy link
Contributor

felipefialho commented Jul 29, 2019

@tjoskar I've been using 6.0.0 and the problem continues here.

// app.module.ts

LazyLoadImageModule.forRoot({
  preset: intersectionObserverPreset
}),

And when I start the SSR server I got:

ReferenceError: IntersectionObserver is not defined

ref #396

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

8 participants