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
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.
The configuration assumes that the following node packages are installed:
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()
.