R3InjectorError(AppServerModule) 没有 InjectionToken compilerOptions 的提供者

时间:2021-04-09 08:39:47

标签: node.js angular server-side-rendering

我收到以下错误:

render error Error [NullInjectorError]: R3InjectorError(AppServerModule)[RouterModule -> Router -> NgModuleFactoryLoader -> Compiler -> InjectionToken compilerOptions -> InjectionToken compilerOptions -> InjectionToken compilerOptions]:
  NullInjectorError: No provider for InjectionToken compilerOptions!

我按照 https://angular.io/guide/universal 提供的说明进行操作,但似乎我需要添加一些调整,例如使用 domino 模拟 document 对象工作。让我很难找出问题出在哪里的问题之一是错误在 main.js

render error Error [NullInjectorError]: R3InjectorError(AppServerModule)[RouterModule -> Router -> NgModuleFactoryLoader -> Compiler -> InjectionToken compilerOptions -> InjectionToken compilerOptions -> InjectionToken compilerOptions]:
  NullInjectorError: No provider for InjectionToken compilerOptions!
    at ɵɵinject (<project-dir>\dist\apps\ui\server\main.js:219196:57)
    at injectArgs (<project-dir>\dist\apps\ui\server\main.js:219291:23)
    at Object.factory (<project-dir>\dist\apps\ui\server\main.js:229671:52)
    at R3Injector.hydrate (<project-dir>\dist\apps\ui\server\main.js:229582:35)
    at R3Injector.get (<project-dir>\dist\apps\ui\server\main.js:229404:33) {
  ngTempTokenPath: null,
  ngTokenPath: [
    'RouterModule',
    'Router',
    'NgModuleFactoryLoader',
    'Compiler',
    'InjectionToken compilerOptions',
    'InjectionToken compilerOptions',
    'InjectionToken compilerOptions'
  ]
} undefined

我无法让 ng run dev:ssr 改为显示打字稿。

这是条目 server.ts

import { ngExpressEngine } from '@nguniversal/express-engine';
import 'zone.js/dist/zone-node';
// import * as express from 'express';
const express = require('express');
import { APP_BASE_HREF } from '@angular/common';
import { Express, Request, Response } from 'express';
import { existsSync } from 'fs';
import { join } from 'path';
import { uiTransports } from 'src/server/transports/transports.winston';
import * as winston from 'winston';
const domino = require('domino');
const fs = require('fs');
import 'localstorage-polyfill';

const winstonLogger = winston.createLogger({
  transports: uiTransports,
});

winstonLogger.info('Starting up UI Server');

// Simulate browser
const distFolder = join(process.cwd(), `dist/apps/ui/browser`);

winstonLogger.info(`Browser dist folder is located at ${distFolder}`);

const templateA = fs.readFileSync(join(distFolder, 'index.html')).toString();
const win = domino.createWindow(templateA);
win.Object = Object;
win.Math = Math;
// (global as any).console = winstonLogger;
(global as any).window = win;
(global as any).document = win.document;
(global as any).branch = null;
(global as any).object = win.object;
(global as any).HTMLElement = win.HTMLElement;
(global as any).navigator = win.navigator;
(global as any).localStorage = localStorage;
(global as any).sessionStorage = localStorage;
(global as any).getComputedStyle = () => {
  return {
    getPropertyValue() {
      return '';
    },
  };
};

// Browser simulation end
import { AppServerModule } from './src/main.server';

// The Express app is exported so that it can be used by serverless Functions.
export function app(): Express {
  const server: Express = express();
  const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';

  winstonLogger.info(`Index HTML located at ${indexHtml}`);

  // Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
  server.engine(
    'html',
    ngExpressEngine({
      bootstrap: AppServerModule,
    }),
  );

  server.set('view engine', 'html');
  server.set('views', distFolder);

  winstonLogger.info('Engine setup completed');

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

  // All regular routes use the Universal engine
  server.get('*', (req: Request, res: Response) => {
    try {
      winstonLogger.info(`Rendering for request ${req.url} started`);
      res.render(
        indexHtml,
        // templateA,
        {
          req,
          res,
          // url: req.url,
          // cookies: req.cookies,
          // bootstrap: AppServerModule,
          providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }],
        },
        (err: Error, html: string) => {
          console.error('render error', err, html);
          res.status(html ? 200 : 500).send(html || err.message);
        },
      );
      winstonLogger.info(`Rendering for request ${req.url} completed`);
    } catch (ex) {
      winstonLogger.error('error from server', ex);
    }
  });

  return server;
}

function run(): void {
  const port = process.env.PORT || 4000;

  // Start up the Node server
  const server = app();
  winstonLogger.info(`Server is about to start to listen to port ${port}`);
  server.listen(port, () => {
    winstonLogger.info(`Node Express server listening on http://localhost:${port}`);
  });
}

// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = (mainModule && mainModule.filename) || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
  run();
}

export * from './src/main.server';

这是app.server.module.ts

import { NgModule } from '@angular/core';
import { ServerModule } from '@angular/platform-server';

import { IonicServerModule } from '@ionic/angular-server'; // i added this thinking it might help
import { AppComponent } from './app.component';
import { AppModule } from './app.module';

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

任何建议将不胜感激!

编辑: 事实证明,错误来自下面的 deps: [[Optional(), COMPILER_OPTIONS]],

@NgModule({
  declarations: [DynamicComponentDirective],
  exports: [DynamicComponentDirective],
})
export class DynamicComponentModule {
  static forRoot(metadata: NgModule): ModuleWithProviders<DynamicComponentModule> {
    console.log('compiler options');
    return {
      ngModule: DynamicComponentModule,
      providers: [
        {
          provide: Compiler,
          useFactory: createJitCompiler,
          /**
           * This leads to the following error:
           * R3InjectorError(AppServerModule)[RouterModule -> Router -> NgModuleFactoryLoader -> Compiler -> InjectionToken compilerOptions -> InjectionToken compilerOptions -> InjectionToken compilerOptions]: NullInjectorError: No provider for InjectionToken compilerOptions!
           */
          // deps: [[Optional(), COMPILER_OPTIONS]],
        },
        {
          provide: DynamicComponentOptions,
          useValue: {
            ngModuleMetadata: metadata,
          },
        },
      ],
    };
  }
}

我假设我需要将它注入 AppServerModule

@NgModule({
  imports: [
    ...
  ],
  bootstrap: [...],
  providers: [/* I think i need to add a provider for it here */],
})
export class AppServerModule {}

不知道怎么做,有人可以帮忙吗?

1 个答案:

答案 0 :(得分:0)

事实证明这是 SSR 的 angular.json 配置的问题,遵循与 https://github.com/Angular-RU/universal-starter/blob/master/angular.json 相同的配置解决了该问题。