skip to Main Content

I have a html page, including some css and typescript which I use to prettify some logs.
I would like to bundle all the files (css, html, javascript generated from typescript) into a single file, so that the prettified logs are fully selfcontained in a single file and can be exchanged easily.

I have looked into webpack, but stuggle with telling webpack to transform the template, so that the compiled typescript, and referenced css files are inlined. I am not tied to webpack, inc ase anyone knows a better tool for the job.

The main reason for my index.html below is that my build so far was to run tsc -w while building out the components.

MWE:

This should be enough of a MWE skeleton to get the issues across. I would like <link rel="stylesheet" href="..."> and <script src="..."> to be inlined (also to run tsc on the typescript files which generate the js)

index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>LogView</title>
    <link rel="stylesheet" href="logview.css">
    <link rel="stylesheet" href="eyesymbols.css">
    <link rel="stylesheet" href="DropDownButtonSymbolFonts.css"> <!-- apparently adding a font into a shadowdom does not work, so add it to head -->
</head>

<body>
    <div id="logview"></div>
    <div id="controls"></div>
    <script src="logview.js" async defer></script> <!-- Actual logic, logview.ts -->
    <script src="DropDownButton.js" async defer></script> <!-- Customelement, backed by DropDownButton.ts -->
</body>

</html>

Webpack config:

const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const HTMLInlineCSSWebpackPlugin = require("html-inline-css-webpack-plugin").default;

module.exports = {
    mode: "development", // TODO: how should we switch this between development and production?
    devtool: false, // TODO: what is the correct setting here, to either do no transform or fast builds?
    entry: {
        dropdown: "./src/DropDownButton.ts",
        logview: "./src/logview.ts",
        //index: "./src/logview.html",
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: "[name].css",
            chunkFilename: "[id].css",
        }),
        new HtmlWebpackPlugin({
            filename: "logview.html",
            template: "./src/logview.html",
            chunks: [],
            inlineSource: ".(js|css)$",
        }),
        new HTMLInlineCSSWebpackPlugin(),//{filter: () => { return true; }}),
    ],
    module: {
        rules: [
            {
                test: /.tsx?$/,
                use: "ts-loader",
                exclude: /node_modules/,
            },
            {
                test: /.css$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    //"style-loader",
                    "css-loader",
                ],
                type: "asset/inline",
            }
        ],
    },
    resolve: {
        extensions: [ ".tsx", ".ts", ".js" ],
    },
    output: {
        filename: "[name].js",
        path: path.resolve(__dirname, "dist"),
        clean: true,
    },
};

2

Answers


  1. Chosen as BEST ANSWER

    Turns out that there is a new webpack5 plugin which does exactly what I want: https://github.com/webdiscus/html-bundler-webpack-plugin. (The turn over rate of new plugins and depreceated plugins for webpack is stunning for someone not in the field, half the answers I found where outdated since they use deprecated or webpack4 plugins).

    Either way, here is a configuration which for webpack which takes a html file as shown above and inlines all js/css. Also js is build from ts as needed.

    const path = require("path");
    const HtmlBundlerPlugin = require("html-bundler-webpack-plugin");
    
    module.exports = {
        mode: "development", // TODO: how should we switch this between development and production?
        devtool: false, // TODO: what is the correct setting here, to either do no transform or fast builds?
        entry: {
            index: "./src/logview.html",
        },
        plugins: [
            new HtmlBundlerPlugin({
                css:{inline:true},
                js:{inline:true},
            }),
        ],
        module: {
            rules: [
                {
                    test: /.tsx?$/,
                    use: "ts-loader",
                    exclude: /node_modules/,
                },
                {
                    test: /.css$/,
                    use: ["css-loader"],
                },
            ],
        },
        resolve: {
            extensions: [ ".tsx", ".ts", ".js" ],
            extensionAlias: {
                ".js": [".ts", ".js"],
            },
        },
        output: {
            filename: "[name].js",
            path: path.resolve(__dirname, "dist"),
            clean: true,
        },
    };
    

    The configuration assumes that the following node packages are installed:

    ├── [email protected]
    ├── [email protected]
    ├── [email protected]
    ├── [email protected]
    ├── [email protected]
    ├── [email protected]
    └── [email protected]
    

  2. Best practice would probably be to design a template for the HTML that already embeds CSS and JavaScript files as inline text correctly (see Embedded JavaScript templates for example).

    Otherwise you would be required to run a "browser controller" to open the files, correctly parse the DOM and resolve the hyperlinks. But beware: Solutions in this direction require a disproportionate amount of server resources.

    I am not aware of any streamlined solutions that insert CSS and JS as inline text. But if an MHTML file would be acceptable, you could easily use Puppeteer and call page.target().createCDPSession().

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search