title: No NgModule metadata found for 'AppModule' Error in Angular 5 server-side rendering with --env.prod
body: <p>I'm trying to upgrade an ASP.NET Core + Angular 4 SPA project from .NET Core 2.0.0/Angular4 to .NET Core 2.0.3/Angular5. I managed to get everything working properly except for the server-side rendering in a production environment, i/e when I publish the app:</p>
  
  <blockquote>
    <p>An unhandled exception has occurred: No NgModule metadata found for 'AppModule'.<br>
    Error: No NgModule metadata found for 'AppModule'.</p>
  </blockquote>
  
  <p>The issue only happens when both of these two conditions are met:</p>
  
  <ul>
  <li>Webpack builds the packages using the <code>--env.prod</code> switch</li>
  <li><p>The <code>Index.cshtml</code> view file contains the <code>asp-prerender-module</code> parameter, just like in the following example:</p>
  
  <p><code>&lt;app asp-prerender-module="ClientApp/dist/main-server"&gt;Loading...&lt;/app&gt;</code></p></li>
  </ul>
  
  <p>If I remove the switch and/or the parameter, the problem disappears (together with SSR).</p>
  
  <p>There's a bunch of other info I can give:</p>
  
  <ul>
  <li>It's not something related to IIS, it happens at Kestrel-level.</li>
  <li>It's not related to the web server machine because I can even reproduce it with locally by manually launching Webpack with the <code>--end.prod</code> switch right before a <em>Debug</em> or <em>Release</em> run. </li>
  <li>It doesn't seem to have anything to do with the source code, as I can reproduce it even with a single-component sample app with very basic AppModule files and trivial code.</li>
  <li>The project was running perfectly fine with .NET Core 2.0.0 and Angular 4.3.x.</li>
  <li>The only major thing I changed in the <code>webpack.config.js</code> file is replacing the <code>AotPlugin</code> with the new, <a href="https://www.npmjs.com/package/@ngtools/webpack" rel="noreferrer">Angular5-specific</a> <code>AngularCompilerPlugin</code> provided by the <a href="https://www.npmjs.com/package/@ngtools/webpack" rel="noreferrer">@ngtools/webpack</a> package: I think that might as well be the cause, as the <code>--env.prod</code> switch makes use of that AOT compiler instead of the JIT one. That, or something related to the <a href="https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.SpaServices#server-side-prerendering" rel="noreferrer">.NET SpaServices package</a> - maybe not on-par with the new Angular5 and/or the new AoT compiler? </li>
  </ul>
  
  <p>Sadly, I can't revert to the former <code>AotPlugin</code> because it throws errors as well - which is perfectly understandable, as it's not meant to be used with Angular5.</p>
  
  <p><strong>Software Versions</strong></p>
  
  <ul>
  <li>.NET Core 2.0.3</li>
  <li>Angular 5.0.2</li>
  <li>@ngtools/webpack 1.8.2 (also tried with 1.8.1 - same outcome)</li>
  <li>WebPack 2.6.1 (also tried with 2.5.6 - same outcome)</li>
  </ul>
  
  <p><strong>webpack.config.js</strong></p>
  
  <pre><code>const path = require('path');
  const webpack = require('webpack');
  const merge = require('webpack-merge');
  const AotPlugin = require('@ngtools/webpack').AngularCompilerPlugin;
  const CheckerPlugin = require('awesome-typescript-loader').CheckerPlugin;
  
  module.exports = (env) =&gt; {
      // Configuration in common to both client-side and server-side bundles
      const isDevBuild = !(env &amp;&amp; env.prod);
      const sharedConfig = {
          stats: { modules: false },
          context: __dirname,
          resolve: { extensions: [ '.js', '.ts' ] },
          output: {
              filename: '[name].js',
              publicPath: 'dist/' // Webpack dev middleware, if enabled, handles requests for this URL prefix
          },
          module: {
              rules: [
                  { test: /(?:\.ngfactory\.js|\.ngstyle\.js|\.ts)$/, include: /ClientApp/, use: isDevBuild ? ['awesome-typescript-loader?silent=true', 'angular2-template-loader'] : '@ngtools/webpack' },
                  { test: /\.html$/, use: 'html-loader?minimize=false' },
                  { test: /\.css$/, use: [ 'to-string-loader', isDevBuild ? 'css-loader' : 'css-loader?minimize' ] },
                  { test: /\.(png|jpg|jpeg|gif|svg)$/, use: 'url-loader?limit=25000' }
              ]
          },
          plugins: [new CheckerPlugin()]
      };
  
      // Configuration for client-side bundle suitable for running in browsers
      const clientBundleOutputDir = './wwwroot/dist';
      const clientBundleConfig = merge(sharedConfig, {
          entry: { 'main-client': './ClientApp/boot.browser.ts' },
          output: { path: path.join(__dirname, clientBundleOutputDir) },
          plugins: [
              new webpack.DllReferencePlugin({
                  context: __dirname,
                  manifest: require('./wwwroot/dist/vendor-manifest.json')
              })
          ].concat(isDevBuild ? [
              // Plugins that apply in development builds only
              new webpack.SourceMapDevToolPlugin({
                  filename: '[file].map', // Remove this line if you prefer inline source maps
                  moduleFilenameTemplate: path.relative(clientBundleOutputDir, '[resourcePath]') // Point sourcemap entries to the original file locations on disk
              })
          ] : [
              // Plugins that apply in production builds only
              new webpack.optimize.UglifyJsPlugin(),
              new AotPlugin({
                  tsConfigPath: './tsconfig.json',
                  entryModule: path.join(__dirname, 'ClientApp/app/app.module.browser#AppModule'),
                  exclude: ['./**/*.server.ts']
              })
          ])
      });
  
      // Configuration for server-side (prerendering) bundle suitable for running in Node
      const serverBundleConfig = merge(sharedConfig, {
          resolve: { mainFields: ['main'] },
          entry: { 'main-server': './ClientApp/boot.server.ts' },
          plugins: [
              new webpack.DllReferencePlugin({
                  context: __dirname,
                  manifest: require('./ClientApp/dist/vendor-manifest.json'),
                  sourceType: 'commonjs2',
                  name: './vendor'
              })
          ].concat(isDevBuild ? [] : [
              // Plugins that apply in production builds only
              new AotPlugin({
                  tsConfigPath: './tsconfig.json',
                  entryModule: path.join(__dirname, 'ClientApp/app/app.module.server#AppModule'),
                  exclude: ['./**/*.browser.ts']
              })
          ]),
          output: {
              libraryTarget: 'commonjs',
              path: path.join(__dirname, './ClientApp/dist')
          },
          target: 'node',
          devtool: 'inline-source-map'
      });
  
      return [clientBundleConfig, serverBundleConfig];
  };
  </code></pre>