Related
I'm having some issues. I keep getting this error
Module parse failed: Unexpected character '#' (1:0) You may need an
appropriate loader to handle this file type, currently, no loaders are
configured to process this file. See
https://webpack.js.org/concepts#loaders
#import url("https://cloud.typography.com/7374818/6819812/css/fonts.css");
I've tried some of the suggested solutions I've found online and none seems to work.
This is what my Webpack config looks like
const path = require('path');
const webpack = require('webpack');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const devMode = process.env.NODE_ENV !== 'production';
module.exports = {
entry: {
main: path.resolve(__dirname, './src/App.tsx'),
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'public'),
sourceMapFilename: "[name].js.map"
},
externals: {
'react': 'React',
'react-dom': 'ReactDOM',
},
devtool: 'source-map',
module: {
rules: [
{
test: /\.(js|jsx|ts|tsx)$/,
exclude: /node_modules/,
include: [
path.resolve(__dirname, './src'),
],
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env', '#babel/preset-react', '#babel/preset-typescript'],
},
},
{
test: /\.(scss|css)$/,
include: path.resolve(__dirname, './src'),
use: [
MiniCssExtractPlugin.loader,
'style-loader',
'css-loader',
{
loader: "postcss-loader",
options: {
plugins: () => [
require("autoprefixer")()
],
},
},
'sass-loader',
],
},
{
test: /\.(png|woff|woff2|eot|ttf|otf|svg)$/,
loader: 'url-loader',
options: {
limit: 100000,
}
},
],
},
resolve: {
extensions: ['.js', '.jsx', 'json', 'ts', 'tsx', '.scss'],
alias: {
src: path.resolve(__dirname, './src/'),
},
},
devServer: {
historyApiFallback: true,
contentBase: './public',
port: 3030,
open: true,
compress: true,
hot: true,
},
optimization: {
splitChunks: {
chunks: 'all',
},
},
plugins: [
new MiniCssExtractPlugin({
filename: devMode ? '[name].css' : '[name].[hash].css',
chunkFilename: devMode ? '[id].css' : '[id].[hash].css',
}),
new CleanWebpackPlugin(),
new webpack.HotModuleReplacementPlugin(),
],
};
The component looks like this. uilib is a local dependency I'm linking using yarn link
import React from 'react';
import ReactDOM from 'react-dom';
import { Header, Footer } from 'uilib';
import 'uilib/index.css';
export const App: React.FC<{}> = () => (
<>
<Header/>
<div>Hello in oct</div>
<Footer/>
</>
);
ReactDOM.render(
<App />,
document.getElementById('root'),
);
The uilib/index.css looks like this
#import url("https://cloud.typography.com/7374818/6819812/css/fonts.css");
.header {
height: 70px;
}
I feel that ulib folder is perhaps not inside src directory.
Can you try changing your webpack loader config for scss|css to be like so:
{
test: /\.(scss|css)$/,
include: path.resolve(__dirname) // <---- leave it __dirname or remove the prop completely
....
....
}
I am hoping this will let webpack look at project's root which can help it parse the file accordingly.
Also, unless ulib is in node_modules, you can exclude it from loader with exclude: 'node_modules'
I would like to setup my js and scss assets like this:
/src/_assets/js/app.js
/src/_assets/js/development.js
/src/_assets/scss/app.scss
And then I would like to end up with these bundled static assets:
/_site/js/app.js
/_site/js/development.js
/_site/css/app.css
I'm all the way on the js side, but I'm having a hard time getting my scss file to turn into css successfully. The CSS file does generate if I force mode: 'production', but the first 100 lines or so are replaced with a bunch of commented js code. And the whole CSS file is js code if I allow mode: 'development'.
What am I doing wrong?
package.json:
{
"devDependencies": {
"#11ty/eleventy": "^0.7.1",
"#babel/core": "^7.0.0",
"#babel/preset-env": "^7.0.0",
"babel-loader": "^8.0.5",
"css-loader": "^2.1.0",
"mini-css-extract-plugin": "^0.5.0",
"node-sass": "^4.11.0",
"sass-loader": "^7.1.0",
"style-loader": "^0.23.1",
"webpack": "^4.28.4",
"webpack-cli": "^3.2.1"
}
}
webpack.config.js:
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const devMode = process.env.NODE_ENV !== 'production';
module.exports = {
name: devMode ? 'development' : 'production',
mode: devMode ? 'development' : 'production',
entry: {
// JS
'js/app.js': './src/_assets/js/app.js',
'js/development.js': './src/_assets/js/development.js',
// SCSS
'css/app.css': './src/_assets/scss/app.scss',
},
output: {
path: __dirname + '/src',
filename: '[name]',
},
module: {
rules: [
{
test: /\.js/,
loader: 'babel-loader',
include: __dirname + '/src/_assets/js'
},
{
test: /\.(sa|sc|c)ss$/,
use: [
devMode ? 'style-loader' : MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader',
],
include: __dirname + '/src/_assets/scss'
}
],
},
plugins: [
new MiniCssExtractPlugin(
{
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: "./[name]",
chunkFilename: "./[id].css"
}
)
],
};
I figured it out. Here are the changes I made:
webpack.config.js:
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
name: process.env.NODE_ENV == 'production' ? 'production' : 'development',
mode: process.env.NODE_ENV == 'production' ? 'production' : 'development',
entry: {
'app': './src/_assets/js/app.js',
'development': './src/_assets/js/development.js',
},
output: {
path: __dirname + '/src',
filename: './js/[name].js',
},
module: {
rules: [
{
test: /\.js/,
loader: 'babel-loader',
include: __dirname + '/src/_assets/js'
},
{
test: /\.(sa|sc|c)ss$/,
use: [
'style-loader',
MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader',
],
}
],
},
plugins: [
new MiniCssExtractPlugin(
{
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: "./css/[name].css",
chunkFilename: "./css/[id].css"
}
)
],
};
/src/_assets/js/app.js:
import './../scss/app.scss';
I'm putting together a project with webpack 4. Virtually all of it working as it should, I'm just having trouble with minifying CSS.
If in my entrypoint i include a.css or .less file then everything works correctly. It also works correctly if i use #include './myfile.less'. However if I use #include './myfile.css' that section is imported without being minified.
How do I get imported CSS files to work?
webpack.config
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin'); //installed via npm
const webpack = require('webpack'); //to access built-in plugins
const CopyWebpackPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
entry: {
'app': './src/app/app.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].bundle.js'
},
module: {
rules: [
{
test: /\.html/,
use: 'html-loader'
},
{
test: /\.less$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
"css-loader",
"less-loader"
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: ['file-loader?name=assets/images/[path][name].[ext]?[hash]']
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader?name=assets/fonts/[name].[ext]?[hash]'
]
}
],
},
plugins: [
new HtmlWebpackPlugin({ template: './src/public/index.html' }),
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: "[name].bundle.css",
chunkFilename: "[id].css"
}),
]
};
While webpack 5 is likely to come with a CSS minimizer built-in, with webpack 4 you need to bring your own. To minify the output, use a plugin like optimize-css-assets-webpack-plugin. Setting optimization.minimizer overrides the defaults provided by webpack, so make sure to also specify a JS minimizer
webpack.config.js
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
module.exports = {
optimization: {
minimizer: [
new UglifyJsPlugin({
cache: true,
parallel: true,
sourceMap: true // set to true if you want JS source maps
}),
new OptimizeCSSAssetsPlugin({})
]
},
plugins: [
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
})
],
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader"
]
}
]
}
}
Reference
I found notes on MiniCssExtractPlugin production usage.
Following these fixed the issue:
specifically adding:
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
...
module.exports = {
...
optimization: {
minimizer: [
new UglifyJsPlugin({
cache: true,
parallel: true,
sourceMap: true // set to true if you want JS source maps
}),
new OptimizeCSSAssetsPlugin({})
]
},
}
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
minimize: true
}
}
]
I'm trying to build a new Angular5/ASP.NET SPA on Visual Studio 2017. Therefore i created a .NET Core->ASP.NET Core-Web Application with Angular, which results in a project containing an Angular4 sample application.
Running this application is no problem at all, the problems start when i try to go on Angular 5 (5.0.1 or 5.0.0, does not matter) with this application.
After doing all necessary steps, the app runs fine in Debug mode. But trying to build and start it in Release (or deploy it to azure) leads to the following error:
An unhandled exception occurred while processing the request.
NodeInvocationException: No ResourceLoader implementation has been provided. Can't read the url "app.component.html"
Error: No ResourceLoader implementation has been provided. Can't read the url "app.component.html"
at Object.get (E:\angular4_spielwiese\vs spielwiese\myAngularApp\myAngularApp\ClientApp\dist\vendor.js:98069:15)
at DirectiveNormalizer.module.exports.DirectiveNormalizer._fetch (E:\angular4_spielwiese\vs spielwiese\myAngularApp\myAngularApp\ClientApp\dist\vendor.js:44087:43)
at DirectiveNormalizer.module.exports.DirectiveNormalizer._preParseTemplate (E:\angular4_spielwiese\vs spielwiese\myAngularApp\myAngularApp\ClientApp\dist\vendor.js:44142:29)
at DirectiveNormalizer.module.exports.DirectiveNormalizer.normalizeTemplate (E:\angular4_spielwiese\vs spielwiese\myAngularApp\myAngularApp\ClientApp\dist\vendor.js:44122:36)
at CompileMetadataResolver.module.exports.CompileMetadataResolver.loadDirectiveMetadata (E:\angular4_spielwiese\vs spielwiese\myAngularApp\myAngularApp\ClientApp\dist\vendor.js:55794:75)
at E:\angular4_spielwiese\vs spielwiese\myAngularApp\myAngularApp\ClientApp\dist\vendor.js:74510:72
at Array.forEach (native)
at E:\angular4_spielwiese\vs spielwiese\myAngularApp\myAngularApp\ClientApp\dist\vendor.js:74509:72
at Array.forEach (native)
at JitCompiler.module.exports.JitCompiler._loadModules (E:\angular4_spielwiese\vs spielwiese\myAngularApp\myAngularApp\ClientApp\dist\vendor.js:74506:75)
Microsoft.AspNetCore.NodeServices.HostingModels.HttpNodeInstance+<InvokeExportAsync>d__7.MoveNext()
What i do for moving to Angular5 is:
Change Versions in package.json for all Angular-Modules to 5.0.1, also go to newer version for typescript, rxjs, angular/cli and #ngtools/webpack (1.5.0 -> 1.8.0)
So my new package.json looks like this:
{
"name": "myAngularApp",
"private": true,
"version": "0.0.0",
"scripts": {
"test": "karma start ClientApp/test/karma.conf.js"
},
"dependencies": {
"#angular/animations": "^5.0.1",
"#angular/common": "^5.0.1",
"#angular/compiler": "^5.0.1",
"#angular/core": "^5.0.1",
"#angular/forms": "^5.0.1",
"#angular/http": "^5.0.1",
"#angular/platform-browser": "^5.0.1",
"#angular/platform-browser-dynamic": "^5.0.1",
"#angular/platform-server": "^5.0.1",
"#angular/router": "^5.0.1",
"#types/webpack-env": "^1.13.0",
"angular2-template-loader": "^0.6.2",
"aspnet-prerendering": "^3.0.1",
"aspnet-webpack": "^2.0.1",
"awesome-typescript-loader": "^3.2.1",
"bootstrap": "^3.3.7",
"css": "^2.2.1",
"css-loader": "^0.28.7",
"es6-shim": "^0.35.3",
"event-source-polyfill": "0.0.9",
"expose-loader": "^0.7.3",
"extract-text-webpack-plugin": "^3.0.2",
"file-loader": "^1.1.5",
"html-loader": "^0.5.1",
"html-webpack-plugin": "^2.30.1",
"isomorphic-fetch": "^2.2.1",
"jquery": "^3.2.1",
"json-loader": "^0.5.4",
"preboot": "^5.1.7",
"raw-loader": "^0.5.1",
"reflect-metadata": "^0.1.10",
"rxjs": "^5.5.2",
"style-loader": "^0.19.0",
"to-string-loader": "^1.1.5",
"typescript": "^2.6.1",
"zone.js": "^0.8.18"
},
"devDependencies": {
"#angular/cli": "1.5.0",
"#angular/compiler-cli": "^5.0.1",
"#ngtools/webpack": "1.8.0",
"#types/chai": "4.0.1",
"#types/jasmine": "2.6.3",
"chai": "4.0.2",
"jasmine-core": "2.6.4",
"karma": "1.7.0",
"karma-chai": "0.1.0",
"karma-chrome-launcher": "2.2.0",
"karma-cli": "1.0.1",
"karma-jasmine": "1.1.0",
"karma-webpack": "2.0.3",
"url-loader": "0.6.2",
"webpack": "3.8.1",
"webpack-hot-middleware": "2.20.0",
"webpack-merge": "4.1.1"
}
}
Then i change AotPlugin in webpack.config.js to AngularCompilerPlugin
This is my webpack.config.js:
const path = require('path');
const webpack = require('webpack');
const merge = require('webpack-merge');
const AngularCompilerPlugin = require('#ngtools/webpack').AngularCompilerPlugin;
const CheckerPlugin = require('awesome-typescript-loader').CheckerPlugin;
module.exports = (env) => {
// Configuration in common to both client-side and server-side bundles
const isDevBuild = !(env && 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: /\.ts$/, include: /ClientApp/, use: isDevBuild ? ['awesome-typescript-loader?silent=true', 'angular2-template-loader'] : ['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 AngularCompilerPlugin({
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 AngularCompilerPlugin({
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];
};
When i now start the application (performing npm install, then run webpack with --env.prod switch) with Release-config out of VS2017 i get the stacktrace above. The same thing happens when i deploy the application to Azure.
On localhost if i wait a few seconds and force-reload my browser, the application suddenly works. This does not work on Azure, which is kinda strange to me.
Do you have any suggestions what i might have done wrong or what i am missing?
I had the same issues for few days, I found a VS2017 - Angular 5 project in GitHub (don't have the exact URL), from which I have copied the webpack.config.js
I have also updated my Angular to 5.0.3
I than ran the 'dotnet publish' which worked (or 'dotnet publish -c Release')
The only problem I faced (and still facing) is during the complication, the compiler messes up the main-server.js, so as a workaround I have copied the main-server.js before the complication (10MB vs 2MB).
When running 'dotnet mydll.dll' - works great.
The webpack.config.js:
/*
* Webpack (JavaScriptServices) with a few changes & updates
* - This is to keep us inline with JSServices, and help those using that template to add things from this one
*
* Things updated or changed:
* module -> rules []
* .ts$ test : Added 'angular2-router-loader' for lazy-loading in development
* added ...sharedModuleRules (for scss & font-awesome loaders)
*/
const path = require('path');
const webpack = require('webpack');
const merge = require('webpack-merge');
const AngularCompilerPlugin = require('#ngtools/webpack').AngularCompilerPlugin;
const CheckerPlugin = require('awesome-typescript-loader').CheckerPlugin;
//const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = (env) => {
// Configuration in common to both client-side and server-side bundles
const isDevBuild = !(env && 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: /\.ts$/, use: isDevBuild ? ['awesome-typescript-loader?silent=true', 'angular2-template-loader', 'angular2-router-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
})
] : [
// new BundleAnalyzerPlugin(),
// Plugins that apply in production builds only
new webpack.optimize.UglifyJsPlugin(),
new AngularCompilerPlugin({
tsConfigPath: './tsconfig.json',
entryModule: path.join(__dirname, 'ClientApp/app/app.module.browser#AppModule'),
exclude: ['./**/*.server.ts']
})
]),
devtool: isDevBuild ? 'cheap-eval-source-map' : false,
node: {
fs: "empty"
}
});
// 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'
}),
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'),
{}
)
].concat(isDevBuild ? [] : [
new webpack.optimize.UglifyJsPlugin({
compress: false,
mangle: false
}),
// Plugins that apply in production builds only
new AngularCompilerPlugin({
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',
// switch to "inline-source-map" if you want to debug the TS during SSR
devtool: isDevBuild ? 'cheap-eval-source-map' : false
});
return [clientBundleConfig, serverBundleConfig];
};
EDIT -
In addition to the changes on the webpack.config.js, I did the following two changes which solved my problem!:
In index.cshtml:
change from
<app asp-prerender-module="ClientApp/dist/main-server">Loading...</app>
to:
<app>Loading...</app>
In boot.server.ts:
change from:
const zone = moduleRef.injector.get(NgZone);
to:
const zone: NgZone = moduleRef.injector.get(NgZone);
Read http://www.talkingdotnet.com/upgrade-angular-4-app-angular-5-visual-studio-2017/ for more info.
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:
An unhandled exception has occurred: No NgModule metadata found for 'AppModule'.
Error: No NgModule metadata found for 'AppModule'.
The issue only happens when both of these two conditions are met:
Webpack builds the packages using the --env.prod switch
The Index.cshtml view file contains the asp-prerender-module parameter, just like in the following example:
<app asp-prerender-module="ClientApp/dist/main-server">Loading...</app>
If I remove the switch and/or the parameter, the problem disappears (together with SSR).
There's a bunch of other info I can give:
It's not something related to IIS, it happens at Kestrel-level.
It's not related to the web server machine because I can even reproduce it with locally by manually launching Webpack with the --end.prod switch right before a Debug or Release run.
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.
The project was running perfectly fine with .NET Core 2.0.0 and Angular 4.3.x.
The only major thing I changed in the webpack.config.js file is replacing the AotPlugin with the new, Angular5-specific AngularCompilerPlugin provided by the #ngtools/webpack package: I think that might as well be the cause, as the --env.prod switch makes use of that AOT compiler instead of the JIT one. That, or something related to the .NET SpaServices package - maybe not on-par with the new Angular5 and/or the new AoT compiler?
Sadly, I can't revert to the former AotPlugin because it throws errors as well - which is perfectly understandable, as it's not meant to be used with Angular5.
Software Versions
.NET Core 2.0.3
Angular 5.0.2
#ngtools/webpack 1.8.2 (also tried with 1.8.1 - same outcome)
WebPack 2.6.1 (also tried with 2.5.6 - same outcome)
webpack.config.js
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) => {
// Configuration in common to both client-side and server-side bundles
const isDevBuild = !(env && 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];
};
The #ngtool/webpack configuration for Angular 5 is somewhat different form Angular 2/4. So, I've changed the webpack.config.js as following,
const path = require('path');
const webpack = require('webpack');
const merge = require('webpack-merge');
const AngularCompilerPlugin = require('#ngtools/webpack').AngularCompilerPlugin;
const CheckerPlugin = require('awesome-typescript-loader').CheckerPlugin;
module.exports = (env) => {
// Configuration in common to both client-side and server-side bundles
const isDevBuild = !(env && 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: /\.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' },
{
test: /(?:\.ngfactory\.js|\.ngstyle\.js)$/,
loader: '#ngtools/webpack',
options: {
tsConfigPath: '/tsconfig.json',
}
}
]
},
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 AngularCompilerPlugin({
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 AngularCompilerPlugin({
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];
};
And here is my package.json file,
{
"dependencies": {
"#angular/animations": "^5.0.2",
"#angular/cdk": "^5.0.0-rc0",
"#angular/cli": "^1.6.0-beta.2",
"#angular/common": "^5.0.2",
"#angular/compiler": "^5.0.2",
"#angular/compiler-cli": "^5.0.2",
"#angular/core": "^5.0.2",
"#angular/forms": "^5.0.2",
"#angular/http": "^5.0.2",
"#angular/material": "^5.0.0-rc0",
"#angular/platform-browser": "^5.0.2",
"#angular/platform-browser-dynamic": "^5.0.2",
"#angular/platform-server": "^5.0.2",
"#angular/router": "^5.0.2",
"#ngtools/webpack": "^1.8.3",
"#types/webpack-env": "^1.13.2",
"angular2-template-loader": "0.6.2",
"aspnet-prerendering": "^3.0.1",
"aspnet-webpack": "^2.0.1",
"awesome-typescript-loader": "^3.4.0",
"bootstrap": "3.3.7",
"css": "2.2.1",
"css-loader": "^0.28.7",
"es6-shim": "0.35.3",
"event-source-polyfill": "0.0.12",
"expose-loader": "0.7.4",
"extract-text-webpack-plugin": "^3.0.2",
"file-loader": "^1.1.5",
"html-loader": "^0.5.1",
"isomorphic-fetch": "2.2.1",
"jquery": "3.2.1",
"json-loader": "^0.5.7",
"preboot": "^5.1.7",
"raw-loader": "0.5.1",
"reflect-metadata": "0.1.10",
"request": "^2.83.0",
"rxjs": "^5.5.2",
"style-loader": "^0.19.0",
"to-string-loader": "1.1.5",
"typescript": "^2.6.1",
"url-loader": "^0.6.2",
"webpack": "^3.8.1",
"webpack-hot-middleware": "^2.20.0",
"webpack-merge": "^4.1.1",
"zone.js": "^0.8.18"
},
"devDependencies": {
"#types/chai": "^4.0.5",
"#types/jasmine": "^2.8.2",
"#types/node": "^8.0.53",
"chai": "^4.1.2",
"jasmine-core": "2.8.0",
"karma": "1.7.1",
"karma-chai": "0.1.0",
"karma-chrome-launcher": "2.2.0",
"karma-cli": "1.0.1",
"karma-jasmine": "1.1.0",
"karma-webpack": "2.0.6"
},
"name": "aspnetcoreangularspa",
"private": true,
"scripts": {
"test": "karma start ClientApp/test/karma.conf.js"
},
"version": "0.0.0"
}
If you still having problems running the solution, please have a look at this repository for a working solution.
Hope it helps :)
As others suggested, the temporary solution is to remove the SSR (Server Side Rendering). That means opening the Home/Index.cshtml file and change
<app asp-prerender-module="ClientApp/dist/main-server">Loading...</app>
to
<app>Loading...</app>
I found your Issue while searching for a solution to my problem described here:
https://github.com/aspnet/JavaScriptServices/issues/1388
So i tried your webpack-config and also tried to run the app from the repository, but when i do "dotnet publish" and then run the app i get this error:
Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware[0]
An unhandled exception has occurred: No NgModule metadata found for 'AppModule'.
Error: No NgModule metadata found for 'AppModule'.
at NgModuleResolver.module.exports.NgModuleResolver.resolve
solution from mak0t0san working for me and ssr still working (angular 5.2.0),
update ngtoolwebpack to 1.10.2
typescript 2.6.2
and change webpack.config.js with that
const path = require('path');
const webpack = require('webpack');
const { DllReferencePlugin, SourceMapDevToolPlugin} = require('webpack');
const merge = require('webpack-merge');
const {AngularCompilerPlugin, PLATFORM} = require('#ngtools/webpack');
const CheckerPlugin = require('awesome-typescript-loader').CheckerPlugin;
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = (env) => {
// Configuration in common to both client-side and server-side bundles
const isDevBuild = !(env && env.prod);
const sharedConfig = {
stats: {modules: false},
context: __dirname,
resolve: {extensions: ['.js', '.ts']},
output: {
filename: '[name].js',
chunkFilename:'[id].chunk.js',
publicPath: 'dist/' // Webpack dev middleware, if enabled, handles requests for this URL prefix
},
module: {
rules: [
{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'},
{
test: /\.(scss)$/,
use: [{
loader: 'style-loader', // inject CSS to page
}, {
loader: 'css-loader', // translates CSS into CommonJS modules
}, {
loader: 'postcss-loader', // Run post css actions
options: {
plugins: function () { // post css plugins, can be exported to postcss.config.js
return [
require('precss'),
require('autoprefixer')
];
}
}
}, {
loader: 'sass-loader' // compiles Sass to CSS
}]
}
]
},
plugins: [new CheckerPlugin()]
};
// Configuration for client-side bundle suitable for running in browsers
const clientBundleOutputDir = './wwwroot/dist';
const clientBundleConfig = merge(sharedConfig, {
module:{
rules:[
{
test: /\.ts$/,
use: ['#ngtools/webpack']
},
]
},
entry: {'main-client': './ClientApp/boot.browser.ts'},
output: {path: path.join(__dirname, clientBundleOutputDir)},
plugins: [
new DllReferencePlugin({
context: __dirname,
manifest: require('./wwwroot/dist/vendor-manifest.json')
}),
new AngularCompilerPlugin({
"mainPath": path.join(__dirname, 'ClientApp/boot.browser.ts'),
platform: PLATFORM.Browser,
tsConfigPath: './tsconfig.json',
entryModule: path.join(__dirname, 'ClientApp/app/app.browser.module#AppModule'),
sourceMap: true
})
].concat(isDevBuild ? [
// Plugins that apply in development builds only
new 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 UglifyJsPlugin({
sourceMap: true
})
])
});
// Configuration for server-side (prerendering) bundle suitable for running in Node
const serverBundleConfig = merge(sharedConfig, {
module:{
rules:[
{ test: /\.ts$/, use: ['awesome-typescript-loader?silent=true', 'angular2-template-loader', 'angular2-router-loader'] },
]
},
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'
})
],
output: {
libraryTarget: 'commonjs',
path: path.join(__dirname, './ClientApp/dist')
},
target: 'node',
devtool: 'inline-source-map'
});
return [clientBundleConfig, serverBundleConfig];
};
As written at the #MMiebach's link:
Angular 5 includes breaking changes (versus Angular 4) meaning that the code for server-side rendering has to be very different.
Luckily, Microsoft released their new SPA project template for Angular 5. It can be installed with
dotnet new --install Microsoft.DotNet.Web.Spa.ProjectTemplates::2.0.0
After that command dotnet new angular will use the 5th Angular instead of 4th.
By default, server-side rendering is not turned on, but there are instructions how to do it in Docs by MS.