I have an image that shows up on the main page of my Angular application during development, but when I do a production release, the image doesn't display. I've been looking at other discussions (this one looked promising but didn't solve my problem) but still haven't worked out the solution. Any ideas what I'm doing wrong?
My image tag looks like this:
<img alt="Rocket" src="../../images/rocket.gif" />
In production, it outputs this:
<img alt="Rocket" src="dist/0f686f4440348c6e453c282baa10b674.gif">
I did take a look in my dist folder and the file does exist, but seems to be corrupt. There is another file that exists there, 5743b3dcb9db968dd0e484850845aa54.gif, that contains my image. Not sure if that provides a hint.
My webpack.config.js file looks like this:
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 isDevBuild = false;
const sharedConfig = {
stats: { modules: false },
context: __dirname,
resolve: {
extensions: [".ts", ".js"],
"alias": {
"jquery-ui":"jquery-ui-dist/jquery-ui",
"scripts": path.resolve(__dirname, "./ClientApp/scripts"),
"node_modules": path.resolve(__dirname,"./node_modules")
}
},
output: {
filename: "[name].js",
publicPath: "dist/" // Webpack dev middleware, if enabled, handles requests for this URL prefix
},
module: {
rules: [
{ test: /\.ts$/, include: /ClientApp/, use: ["awesome-typescript-loader?silent=true", "angular2-template-loader", "angular-router-loader"] },
{ test: /\.html$/, use: "html-loader?minimize=false" },
{ test: /\.scss$/, include: /ClientApp/, use: ["raw-loader", "sass-loader"] },
{ 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(),
new webpack.ProvidePlugin({
jQuery: "jquery",
$: "jquery",
jquery: "jquery",
"window.jQuery": "jquery'",
"window.$": "jquery"
})]
};
// 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({
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 AotPlugin({
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 my webpack.config.vendor.js file looks like this:
const path = require("path");
const webpack = require("webpack");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const merge = require("webpack-merge");
const treeShakableModules = [
"@angular/animations",
"@angular/cdk",
"@angular/common",
"@angular/compiler",
"@angular/core",
"@angular/forms",
"@angular/http",
"@angular/material",
"@angular/platform-browser",
"@angular/platform-browser-dynamic",
"@angular/router",
"@asymmetrik/ngx-leaflet",
"@progress/kendo-angular-charts",
"@progress/kendo-angular-inputs",
"@progress/kendo-angular-intl",
"@progress/kendo-angular-l10n",
"@progress/kendo-drawing",
"@progress/kendo-file-saver",
"zone.js"
];
const nonTreeShakableModules = [
"@progress/kendo-theme-default/dist/all.css",
"ag-grid-angular",
"ag-grid",
"ag-grid-enterprise/dist/ag-grid-enterprise.js",
"ag-grid/dist/styles/ag-grid.css",
"ag-grid/src/styles/ag-theme-material.scss",
"bootstrap",
"bootstrap/dist/css/bootstrap.css",
"es6-promise",
"es6-shim",
"event-source-polyfill",
"exceljs/dist/exceljs.min.js",
"file-saver",
"font-awesome/css/font-awesome.css",
"hammerjs",
"jquery",
"jquery-ui-dist/jquery-ui",
"leaflet",
"leaflet/dist/leaflet.css",
"lodash",
"moment",
"ngx-bootstrap",
"tether",
"sheetjs",
"xlsx",
"./ClientApp/theme/material/js/material.js",
"./ClientApp/theme/material/js/ripples.js",
"./ClientApp/theme/material/css/bootstrap-material-design.css",
"./ClientApp/theme/material/css/ripples.css"
];
const allModules = treeShakableModules.concat(nonTreeShakableModules);
module.exports = (env) => {
const extractCSS = new ExtractTextPlugin("vendor.css");
const isDevBuild = !(env && env.prod);
//const isDevBuild = false;
const sharedConfig = {
stats: { modules: false },
resolve: {
extensions: [".js"]
},
module: {
rules: [
{ test: /\.(png|gif|jpg|jpeg|woff|woff2|eot|ttf|svg)(\?|$)/, use: "url-loader?limit=100000" },
{ test: /\.scss$/, use: ["raw-loader", "sass-loader"] }
]
},
output: {
publicPath: "dist/",
filename: "[name].js",
library: "[name]_[hash]"
},
plugins: [
new webpack.ProvidePlugin({ $: "jquery", jQuery: "jquery", jquery: "jquery", "window.jQuery": "jquery", "_": "lodash" }), // Maps these identifiers to the jQuery package (because Bootstrap expects it to be a global variable)
new webpack.ContextReplacementPlugin(/\@angular\b.*\b(bundles|linker)/, path.join(__dirname, "./ClientApp")), // Workaround for https://github.com/angular/angular/issues/11580
new webpack.ContextReplacementPlugin(/\@angular(\\|\/)core(\\|\/)esm5/, path.join(__dirname, "./ClientApp")),
new webpack.ContextReplacementPlugin(/angular(\\|\/)core(\\|\/)@angular/, path.join(__dirname, "./ClientApp")), // Workaround for https://github.com/angular/angular/issues/14898
new webpack.IgnorePlugin(/^vertx$/) // Workaround for https://github.com/stefanpenner/es6-promise/issues/100
]
};
const clientBundleConfig = merge(sharedConfig, {
entry: {
// To keep development builds fast, include all vendor dependencies in the vendor bundle.
// But for production builds, leave the tree-shakable ones out so the AOT compiler can produce a smaller bundle.
vendor: isDevBuild ? allModules : nonTreeShakableModules
},
output: { path: path.join(__dirname, "wwwroot", "dist") },
module: {
rules: [
{ test: /\.css(\?|$)/, use: extractCSS.extract({ use: isDevBuild ? "css-loader" : "css-loader?minimize" }) }
]
},
plugins: [
extractCSS,
new webpack.DllPlugin({
path: path.join(__dirname, "wwwroot", "dist", "[name]-manifest.json"),
name: "[name]_[hash]"
})
].concat(isDevBuild ? [] : [
new webpack.optimize.UglifyJsPlugin()
])
});
const serverBundleConfig = merge(sharedConfig, {
target: "node",
resolve: { mainFields: ["main"] },
entry: { vendor: allModules.concat(["aspnet-prerendering"]) },
output: {
path: path.join(__dirname, "ClientApp", "dist"),
libraryTarget: "commonjs2"
},
module: {
rules: [{ test: /\.css(\?|$)/, use: ["to-string-loader", isDevBuild ? "css-loader" : "css-loader?minimize"] }]
},
plugins: [
new webpack.DllPlugin({
path: path.join(__dirname, "ClientApp", "dist", "[name]-manifest.json"),
name: "[name]_[hash]"
})
]
});
return [clientBundleConfig, serverBundleConfig];
}
I have solved this problem. It turns out that I needed to add a reference to the image to the non tree shakeable section in the webpack.config.vendor.js file.
I added the following line, fixed the reference in the component html page, and it's all working in an aot published application! Awesome!
"./ClientApp/images/rocket.gif"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With