Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't get image to show in production release in Angular 5 with WebPack

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];
}
like image 918
tone Avatar asked Feb 08 '18 22:02

tone


1 Answers

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"
like image 65
tone Avatar answered Nov 10 '22 00:11

tone