С angular 5 и webpack для "производственной" сборки я вижу "Пожалуйста, добавьте аннотацию @NgModule". за первый импортированный модуль

У меня есть большой проект, который хорошо работал под webpack, angular 4 и недавно перешел на angular 5, и улучшенный конфиг webpack. Сборка 'development' загружается просто отлично.

Но "производственная" сборка загружается и умирает с "Пожалуйста, добавьте аннотацию @NgModule" - блин, так как многие из них разбились на этом мелководье.

Поэтому я отключил mangle, чтобы посмотреть, какой компонент вызывает проблему. И я обнаружил, что все, что было перечислено в начале массива импорта: было [ ... ] раздел в @NgModule. Так ПОЧЕМУ это? и что может быть причиной этой милой ошибки?

Почему первый модуль, указанный в начале импорта, всегда не будет найден?

В версии, указанной ниже, ошибка:

Uncaught Error: Unexpected value 'FormsModule' imported by the module 'AppModule'. Please add a @NgModule annotation.

Вот раздел проекта, который, я надеюсь, может пролить некоторый свет:

@NgModule({
    bootstrap: [ AppComponent ],
    declarations: COMPONENTS,
    imports: [ // import Angular's modules
      FormsModule,
      ReactiveFormsModule,
      HttpModule,
      BrowserModule,
      RouterModule.forRoot(ROUTES, { useHash: true, preloadingStrategy: PreloadAllModules }),
      BsDropdownModule.forRoot(),
      AlertModule.forRoot(),
      DatepickerModule.forRoot(),
      TooltipModule.forRoot(),
      ModalModule.forRoot(),
      LocalStorageModule.withConfig({
        prefix: 'tracker',
        storageType: 'localStorage'
      }),
      A2Edatetimepicker,
      DndModule.forRoot(),
      NgPipesModule,
      ColorPickerModule
    ],
    providers: [ // expose our Services and Providers into Angular's dependency injection
      ENV_PROVIDERS,
      APP_PROVIDERS,
      {
        provide: AuthHttp,
        useFactory: (http: Http, localStorage: LocalStorageService) => {
          let conf: AuthConfig;
          conf = new AuthConfig({
            tokenName: 'token',
            tokenGetter: (() => {
              const t = localStorage.get<any>('id_token');
              // console.log(t);
              return t;
            }),
            globalHeaders: [{'Content-Type': 'application/json'}],
          });
          return new AuthHttp(conf, http);
        },
        deps: [Http, LocalStorageService]
      },
      {
        provide: SocketService,
        useFactory: (broadcast: BroadCaster,
                     authHttp: AuthHttp) => {
          return new SocketService(broadcast, authHttp);
        },
        deps: [BroadCaster, AuthHttp]
      },
      {
        provide: ImportUserData,
        useFactory: (authHttp: AuthHttp,
                     localStorage: LocalStorageService) => {
          return new ImportUserData(authHttp, localStorage);
        },
        deps: [AuthHttp, LocalStorageService]
      },
      {
        provide: AuthService,
        useFactory: (http: Http,
                     authHttp: AuthHttp,
                     localStorage: LocalStorageService) => {
          const as = new AuthService(http, authHttp, localStorage);
          return as;
        },
        deps: [Http, AuthHttp, LocalStorageService]
      },
      {
        provide: UserCondition,
        useFactory: (authHttp: AuthHttp,
                     broadcast: BroadCaster,
                     localStorage: LocalStorageService,
                     sanitizer: DomSanitizer,
                     zone: NgZone) => {
          return new UserCondition(authHttp, broadcast, localStorage, sanitizer, zone);
        },
        deps: [AuthHttp, BroadCaster, LocalStorageService, DomSanitizer, NgZone]
      },
      {
        provide: ProfileComponent,
        useFactory: (userCondtion: UserCondition,
                     sanitizer: DomSanitizer,
                     auth: AuthService) => {
          return new ProfileComponent(userCondtion, sanitizer, auth);
        },
        deps: [UserCondition, DomSanitizer, AuthService]
      },
      {
        provide: LogoutWarningComponent,
        useFactory: (userCondtion: UserCondition,
                     broadcaster: BroadCaster,
                     versionS: Version,
                     auth: AuthService) => {
          return new LogoutWarningComponent(userCondtion, broadcaster, versionS, auth);
        },
        deps: [UserCondition, BroadCaster, Version, AuthService]
      },
    ]
  })
  export class AppModule implements OnDestroy {
    private mVersion: string;
    ...
    ...
  } 

Обновление к этой проблеме:

Через несколько дней я добился немалых успехов.
Также я узнал несколько вещей, которые могут помочь другим - кто сталкивается с такой проблемой.

  1. закомментируйте почти все плагины в рабочей версии сборки веб-пакета.
  2. выключить название mangle.

После того, как я это сделал, я узнал, что проблема возникла в "производственной" настройке раздела конфигурации из проекта: angular-starter. Место, где возникла проблема, было в файле 'build-utils.js':

    const ts = require('typescript');
    const path = require('path');
    const fs = require('fs');
    const helpers = require('./helpers');

    const DEFAULT_METADATA = {
      title: 'Tracker',
      baseUrl: '/',
      isDevServer: helpers.isWebpackDevServer(),
      HMR: helpers.hasProcessFlag('hot'),
      AOT: process.env.BUILD_AOT || helpers.hasNpmFlag('aot'),
      E2E: !!process.env.BUILD_E2E,
      WATCH: helpers.hasProcessFlag('watch'),
      tsConfigPath: 'tsconfig.webpack.json',

      /**
       * This suffix is added to the environment.ts file, if not set the default environment file is loaded (development)
       * To disable environment files set this to null
       */
      envFileSuffix: ''
    };

    function supportES2015(tsConfigPath) {
      if (!supportES2015.hasOwnProperty('supportES2015')) {
        const tsTarget = readTsConfig(tsConfigPath).options.target;
        supportES2015['supportES2015'] = tsTarget !== ts.ScriptTarget.ES3 && tsTarget !== ts.ScriptTarget.ES5;
      }
      return supportES2015['supportES2015'];
    }

    function readTsConfig(tsConfigPath) {
      const configResult = ts.readConfigFile(tsConfigPath, ts.sys.readFile);
      return ts.parseJsonConfigFileContent(configResult.config, ts.sys,
        path.dirname(tsConfigPath), undefined, tsConfigPath);
    }

    function getEnvFile(suffix) {
      if (suffix && suffix[0] !== '.') {
        suffix = '.' + suffix;
      }

      if (suffix === null) {
        return;
      }

      let fileName = helpers.root(`src/environments/environment${suffix}.ts`);
      if (fs.existsSync(fileName)) {
        return fileName;
      } else if (fs.existsSync(fileName = helpers.root('src/environments/environment.ts'))) {
        console.warn(`Could not find environment file with suffix ${suffix}, loading default environment file`);
        return fileName;
      } else {
        throw new Error('Environment file not found.')
      }
    }

    /**
     * Read the tsconfig to determine if we should prefer ES2015 modules.
     * Load rxjs path aliases.
     * https://github.com/ReactiveX/rxjs/blob/master/doc/lettable-operators.md#build-and-treeshaking
     * @param supportES2015 Set to true when the output of typescript is >= ES6
     */
    function rxjsAlias(supportES2015) {
      try {
        const rxjsPathMappingImport = supportES2015 ? 'rxjs/_esm2015/path-mapping' : 'rxjs/_esm5/path-mapping';
        const rxPaths = require(rxjsPathMappingImport);
        return rxPaths(helpers.root('node_modules'));
      } catch (e) {
        return {};
      }
    }

    function ngcWebpackSetup(prod, metadata) {
      if (!metadata) {
        metadata = DEFAULT_METADATA;
      }

      // TURNED OFF THE buildOptimizer here:
      // const buildOptimizer = prod;
      const buildOptimizer = false;
      const sourceMap = true; // TODO: apply based on tsconfig value?
      const ngcWebpackPluginOptions = {
        skipCodeGeneration: !metadata.AOT,
        sourceMap
      };

      const environment = getEnvFile(metadata.envFileSuffix);
      // console.log('environment:', environment);
      if (environment) {
        ngcWebpackPluginOptions.hostReplacementPaths = {
          [helpers.root('src/environments/environment.ts')]: environment
        }
      }

      if (!prod && metadata.WATCH) {
        // Force commonjs module format for TS on dev watch builds.
        ngcWebpackPluginOptions.compilerOptions = {
          module: 'commonjs'
        };
      }

      const buildOptimizerLoader = {
        loader: '@angular-devkit/build-optimizer/webpack-loader',
        options: {
          sourceMap
        }
      };

      const loaders = [
        {
          test: /(?:\.ngfactory\.js|\.ngstyle\.js|\.ts)$/,
          use: metadata.AOT && buildOptimizer ? [ buildOptimizerLoader, '@ngtools/webpack' ] : [ '@ngtools/webpack' ]
        },
        ...buildOptimizer
          ? [ { test: /\.js$/, use: [ buildOptimizerLoader ] } ]
          : []
      ];
      console.log('loaders:', loaders);
      // console.log('ngcWebpackPluginOptions:', ngcWebpackPluginOptions);

      return {
        loaders,
        plugin: ngcWebpackPluginOptions
      };
    }


    exports.DEFAULT_METADATA = DEFAULT_METADATA;
    exports.supportES2015 = supportES2015;
    exports.readTsConfig = readTsConfig;
    exports.getEnvFile = getEnvFile;
    exports.rxjsAlias = rxjsAlias;
    exports.ngcWebpackSetup = ngcWebpackSetup;

Здесь мне нужно было установить: 'buildOptimizer' в false внутри функции ngcWebpackSetup() выше. Недостатком является то, что свернутый код вырос с 3,59 МБ до 4,04 МБ. НО код теперь загружается и работает правильно в браузере!

Надеюсь, я узнаю, в чем здесь проблема, и сообщу о ней, и, возможно, опубликую исправление в проекте "angular-starter".

0 ответов

Другие вопросы по тегам