Угловая универсальная ошибка получения: вы должны пройти в NgModule или NgModuleFactory для загрузки

Следуя этому руководству, я преобразовал существующее приложение углового cli в угловое универсальное.

Вы можете посмотреть на мой полный исходный код здесь.

Я могу создать как браузер, так и клиентские проекты, но я вижу следующую ошибку при просмотре приложения в браузере:

Ошибка: вы должны передать NgModule или NgModuleFactory для загрузки в View.engine(D:\ng-ssr-demo\dist\server.js: 359545: 23)

Проблема заключается в моем файле server.ts, где AppServerModuleNgFactory не определено, и поскольку эта фабрика используется для загрузки приложения в экспресс-бэкэнд, самонастройка не выполняется.

./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

Я проверил ваше репо и смог просмотреть тон dist без этой ошибки в браузере. Возможно, вы забыли добавить флаг -prod при запуске сборки? Попробуйте это

ng build --prod

Вы также можете полностью удалить dist и/или удалить node_modules, npm cache clean, запустить npm install прежде чем пытаться построить снова.

Если вы используете скрипты npm, я замечаю, что в вашей/серверной сборке отсутствует флаг --prod. Попробуйте это

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

Ответ 2

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

можете ли вы проверить, что "AppServerModuleNgFactory" существует в вашем main.bundle. Это может быть иными именами, поэтому просто проверьте только "ModuleNgFactory", узнать, какой NgFactory экспортирует ваше приложение, просто замените его заводским именем.

Ответ 3

mode: 'development', использования mode: 'development', для webpack.config.js

Ответ 4

УГЛОВОЕ ОБНОВЛЕНИЕ 8 (v8.0.1 - по состоянию на июнь 2019 г.)

Для меня я запустил универсальную настройку, используя Angular CLI, и она не работала прямо из коробки. После нескольких часов чтения я обнаружил, что пакеты не совпадали. У меня был Angular 8, работающий над проектом, но nguniversal пакеты в моем package.json были указаны в v7.

Я бы порекомендовал обновить их до той же версии Angular, которую вы установили. CLI должен действительно делать это по умолчанию, но я не думаю (пока?).

Для Angular 8 на момент написания (июнь 2019 г.) это версия @next или @8.0.0-rc.1, поэтому для обновления выполните следующую команду:

npm i --save @nguniversal/[email protected] @nguniversal/[email protected]

После того, как я обновил это, я все еще получал ошибку, и мне удалось идентифицировать другую проблему. Мне также пришлось отключить компилятор Ivy для серверного приложения. Для этого я добавил следующую строку в tsconfig.server.json:

{
    "extends": "./tsconfig.app.json",
    ...
    "angularCompilerOptions": {
        ...
        "enableIvy": false
    }
    ...
}

Ivy по умолчанию отключен в Angular 8, но поскольку мой tsconfig.server.json расширяет tsconfig.app.json, а в конфигурации приложения был включен Ivy, мне пришлось явно отключить его для конфигурации сервера.

После всего этого серверные запросы на контент фактически начали работать для меня.

Если это не поможет, я бы порекомендовал загрузить универсальный пример проекта, упомянутый в угловых документах:

Загрузить: https://angular.io/generated/zips/universal/universal.zip Документы: https://angular.io/guide/universal

После загрузки сравните все соответствующие файлы, чтобы убедиться, что они совпадают. Если вы по-прежнему получаете ошибки в своем собственном проекте, но пример работает, попробуйте переместить файлы настроек, модули и компоненты и т.д. В пример проекта по одному и посмотреть, что его нарушает. Таким образом я смог определить, что это был мой файл tsconfig.server.json который сломал его.