Angular-universal getting error: You must pass in

2019-06-15 02:01发布

问题:

I converted my existing angular-cli application to angular-universal by following this guide.

You can look at my complete source code here.

I am able to build both browser and client projects but I get following error when I view the app in the browser:

Error: You must pass in a NgModule or NgModuleFactory to be bootstrapped at View.engine (D:\ng-ssr-demo\dist\server.js:359545:23)

The issue is in my server.ts file where AppServerModuleNgFactory is being undefined and as this factory is used for bootstraping the app in the express backend, the bootstrapping is failing.

./server.ts:

const MockBrowser = require('mock-browser').mocks.MockBrowser;
const mock = new MockBrowser();

// 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');

// Fix for window error:
const domino = require('domino');
const fs = require('fs');
const path = require('path');
const template = fs.readFileSync(path.resolve('./', 'dist', 'browser/', 'index.html')).toString();
const win = domino.createWindow(template);

// workaround for leaflet
global['window'] = win;
global['document'] = win.document;

// workaround for nex-charts
win.screen = { deviceXDPI: 0, logicalXDPI: 0 };
global['MouseEvent'] = win.MouseEvent;
global['navigator'] = mock.getNavigator();


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

// AppServerModuleNgFactory is undefined
console.log('AppServerModuleNgFactory', AppServerModuleNgFactory);

// This is injected
console.log('LAZY_MODULE_MAP', LAZY_MODULE_MAP);

// 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)
  ]
}));

./webpack.server.config.js:

module.exports = {
  entry: {
    // This is our Express server for Dynamic universal
    server: './server.ts',
    // This is an example of Static prerendering (generative)
    prerender: './prerender.ts'
  },
  target: 'node',
  resolve: { extensions: ['.ts', '.js'] },
  // Make sure we include all node_modules etc
  externals: [/node_modules/],
  output: { path: path.join(__dirname, 'dist'), filename: '[name].js' },
  module: { rules: [{ test: /\.ts$/, loader: 'ts-loader'}] },
  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'), {}
    )
  ]
}

./src/tsconfig.server.json:

{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "outDir": "../out-tsc/app",
    "module": "commonjs",
    "baseUrl": "./",
    "types": ["node"],
    "typeRoots": ["../node_modules/@types"],
    "paths": {
      "@angular/*": [
        "../node_modules/@angular/*"
      ],
      "@nebular/*": [
        "../node_modules/@nebular/*"
      ]
    }
  },
  "exclude": [
    "test.ts",
    "**/*.spec.ts"
  ],
  "angularCompilerOptions": {
    "entryModule": "app/app.server.module#AppServerModule",
    "platform": 1
  }
}

./src/main.server.ts:

export { AppServerModule } from './app/app.server.module';

./src/app/app.module.ts:

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule.withServerTransition({appId: 'my-app'}),
    BrowserAnimationsModule,
    HttpModule,
    AppRoutingModule,

    NgbModule.forRoot(),
    ThemeModule.forRoot(),
    CoreModule.forRoot(),
    environment.production ? ServiceWorkerModule.register('./ngsw-worker.js') : [],
  ],
  bootstrap: [AppComponent],
  providers: [
    { provide: APP_BASE_HREF, useValue: '/' }, WebWorkerService,
  ],
})
export class AppModule {
}

./src/app/app.server.module.ts:

@NgModule({
  imports: [
    AppModule,
    ServerModule,
    ModuleMapLoaderModule
  ],
  bootstrap: [AppComponent],
})
export class AppServerModule {}

回答1:

I checked out your repo and was able to view the dist fine without that error in the browser. Perhaps you forgot to add the flag -prod when running build? Please try this

ng build --prod

You can also remove the dist completely and/or remove node_modules, do npm cache clean, run npm install before trying to build again.

If you are using npm scripts, I notice that your /server build is missing the --prod flag. Please try this

"build:server": "ng build --prod --app 1 --output-hashing=false",


回答2:

    const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main.bundle');

can you please check 'AppServerModuleNgFactory' is exist in your main.bundle. It may be in other names, so just check with only "ModuleNgFactory" find out which NgFactory is exporting your App, just replace it the factory name.



回答3:

use mode: 'development', for webpack.config.js