Carregando dinamicamente um webpack externo incluído o ngModule como um manipulador de rota
Queremos dividir nossos grandes projetos de front-end em vários projetos implantados separadamente, que são mais fáceis de trabalhar. Estou tentando incluir um ngModule incluído para lidar com uma rota de outro aplicativo. Os aplicativos devem desconhecer a configuração um do outro. Os pacotes compartilharão algumas grandes dependências (como Angular) via globais. Não precisamos nos misturar e talvez apenas tenhamos que aceitar algumas dependências duplicadas.
O roteador raiz reclama que
Error: No NgModule metadata found for 'TestsetModule'.
o que me leva a acreditar que o módulo filho não está sendo angular compilado na carga ou não está registrando seu módulo por algum motivo. Eu acho que pode ser necessário compilar manualmente o módulo, mas não sei como usar issohttps://angular.io/api/core/Compiler#compileModuleAndAllComponentsAsync
O aplicativo raiz carrega o filho por uma rota:
import { ModuleWithProviders } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
const load = require("little-loader");
const routes: Routes = [
{ path: ``, loadChildren: () => new Promise(function (resolve) {
load('http://localhost:3100/testset-module-bundle.js',(err: any) => {
console.log('global loaded bundle is: ', (<any>global).TestsetModule )
resolve((<any>global).TestsetModule)
}
)
})}
];
export const HostRouting: ModuleWithProviders = RouterModule.forRoot(routes);
Eu também tentei usar a sintaxe de resolução de seqüência de caracteres do roteador angular em vez dessa coisa global estranha que você vê, mas tive problemas semelhantes.
Aqui está o módulo que está sendo carregado, muito padrão, exceto para a exportação global:
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HttpModule } from '@angular/http';
//import { MaterialModule } from '@angular/material';
import { FlexLayoutModule } from '@angular/flex-layout';
import { FormsModule } from '@angular/forms';
import { LoggerModule, Level } from '@churro/ngx-log';
import { FeatureLoggerConfig } from './features/logger/services/feature-logger-config';
import { TestsetComponent } from './features/testset/testset.component';
import { TestsetRouting } from './testset.routing';
@NgModule({
imports: [
CommonModule,
//MaterialModule,
FlexLayoutModule,
HttpModule,
FormsModule,
LoggerModule.forChild({
moduleName: 'Testset',
minLevel: Level.INFO
}),
TestsetRouting,
],
declarations: [TestsetComponent],
providers: [
/* TODO: Providers go here */
]
})
class TestsetModule { }
(<any>global).TestsetModule = TestsetModule
export {TestsetModule as default, TestsetModule};
Aqui está a configuração do webpack do pacote raiz. Observe as exportações globais por meio do nome "ProvidePlugin".
const webpack = require('webpack');
const AotPlugin = require('@ngtools/webpack').AotPlugin;
const path = require('path');
const BrowserSyncPlugin = require('browser-sync-webpack-plugin');
const IgnorePlugin = require('webpack/lib/IgnorePlugin');
const PolyfillsPlugin = require('webpack-polyfills-plugin');
const WebpackSystemRegister = require('webpack-system-register');
module.exports = (envOptions) => {
envOptions = envOptions || {};
const config = {
entry: {
'bundle': './root.ts'
},
output: {
libraryTarget: 'umd',
filename: '[name].js',//"bundle.[hash].js",
chunkFilename: '[name]-chunk.js',
path: __dirname
},
externals: {
},
resolve: {
extensions: ['.ts', '.js', '.html'],
},
module: {
rules: [
{ test: /\.html$/, loader: 'raw-loader' },
{ test: /\.css$/, loader: 'raw-loader' },
]
},
devtool: '#source-map',
plugins: [
new webpack.ProvidePlugin({
'angular': '@angular/core',
'ngrouter': '@angular/router',
'ngxlog':'@churro/ngx-log'
})
]
};
config.module.rules.push(
{ test: /\.ts$/, loaders: [
'awesome-typescript-loader',
'angular-router-loader',
'angular2-template-loader',
'source-map-loader'
] }
);
}
return config;
};
E aqui está a configuração do webpack do pacote filho. Observe os "externos" que procuram angular como um global.
module.exports = (envOptions) => {
envOptions = envOptions || {};
const config = {
entry: {
'testset-module-bundle': './src/index.ts'
},
output: {
//library: 'TestsetModule',
libraryTarget: 'umd',
filename: '[name].js',//"bundle.[hash].js",
chunkFilename: '[name]-chunk.js',
path: path.resolve(__dirname, "dist")
},
externals: {
//expect these to come from the app that imported us
// name to be required : name from global
'angular': '@angular/core',
'ngrouter': '@angular/router',
'ngxlog': '@churro/ngx-log'
},
resolve: {
extensions: ['.ts', '.js', '.html'],
},
module: {
rules: [
{ test: /\.html$/, loader: 'raw-loader' },
{ test: /\.css$/, loader: 'raw-loader' },
]
},
devtool: '#source-map',
plugins: [
]
};
config.module.rules.push(
{ test: /\.ts$/, loaders: [
'awesome-typescript-loader',
'angular-router-loader',
'angular2-template-loader',
'source-map-loader'
] }
);
}
return config;
};
E para uma boa medida, aqui está o meu arquivo tsconfig, que 'awesome-typescript-loader' lê.
{
"compilerOptions": {
"target": "es5",
"module": "es2015",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true,
"baseUrl": ".",
"rootDir": "src",
"outDir": "app",
"paths": {
"@capone/*": [
"*"
],
"@angular/*": [
"node_modules/@angular/*"
],
"rxjs/*": [
"node_modules/rxjs/*"
]
}
},
"exclude": ["node_modules", "src/node_modules", "compiled", "src/dev_wrapper_app"],
"angularCompilerOptions": {
"genDir": "./compiled",
"skipMetadataEmit": true
}
}
Se você ainda está lendo, incrível. Consegui fazer isso funcionar quando os dois pacotes configuráveis fazem parte da mesma configuração do webpack e o módulo filho é apenas uma parte. Angular é projetado para fazer isso. Mas nosso caso de uso é fazer com que os filhos e os pais ignorem um ao outro até o tempo de execução.