Andrew Bullock
Andrew Bullock

Reputation: 37378

How to use webpack to inline css and js into html and remove unused styles

I have a single HTML file and some SASS and JS, and for Production I want to bundle these together into a single HTML file which contains the SASS compiled into CSS into an inline <style> element, and the JS combined and minified into an inline <script> element. Not, where the css is embedded in the JS and added to the DOM via JS or in separate files.

I've got as far as getting the SASS compiled into .css and <linked to and the JS into a <script src="ed .js. I've also got PurgeCss working (I think - it's stripping a lot of probably unused selectors).

How do I inline the CSS and JS in their own tags inside the HTML?

Please don't just say to use https://www.npmjs.com/package/html-inline-css-webpack-plugin or reference Inline JavaScript and CSS with webpack, because this seems to be plagued with errors and version/dependency mismatches. If this is the best answer, how do you actually make it work? What versions of all the dependencies actually work together?

Here is my webpack.config which works as far as I've got:

const HtmlWebpackPlugin = require('html-webpack-plugin');
const PurgecssPlugin = require('purgecss-webpack-plugin')

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const path = require("path");
const glob = require('glob');

const purgecssFromHtml = require('purgecss-from-html');

const PATHS = {
  src: path.join(__dirname, 'src')
}

module.exports = {
  module: {
    rules: [
      {
        test: /\.scss$/i,
        use: [
          
            MiniCssExtractPlugin.loader,
            //'style-loader',
            {
                loader: 'css-loader',
                options: {
                //importLoaders: 1,
                url: false
                },
            },
            'sass-loader',
        ],
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: ["babel-loader"]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "src", "index.html")
    }),
    new MiniCssExtractPlugin({
      filename: "[name].css"
    }),
    new PurgecssPlugin({
      paths: glob.sync(`${PATHS.src}/*.*`),
      styleExtensions: ['.css'],
      whitelist: ['whitelisted'],
      extractors: [
        {
          extractor: purgecssFromHtml,
          extensions: ['html']
        }
      ]
    })
  ]
};

src/index.js:

import "./scss/theme.scss";
import "./file1.js"
import "./file2.js"

Upvotes: 2

Views: 4232

Answers (1)

Anthony
Anthony

Reputation: 1926

I've been playing around with Webpack for the last couple of weeks.

I'm still fairly new Webpack, but have a side HTML template project where I've been using it which may help: https://github.com/JustAGuyCoding/spotlight-webpack, especially since it's a working example.

The webpack config looks like this:

const path                      = require('path');
const HtmlWebpackPlugin         = require('html-webpack-plugin');
const { CleanWebpackPlugin }    = require('clean-webpack-plugin');
const MiniCssExtractPlugin      = require('mini-css-extract-plugin');
const StyleExtHtmlWebpackPlugin = require("style-ext-html-webpack-plugin");
const CopyPlugin                = require('copy-webpack-plugin');

module.exports = {

    entry: './src/spotlight.js',
    output: {
        path        : path.resolve(__dirname, 'dist'),
        filename    : 'spotlight.[contenthash].js',
    },

    /* Development - webmode. */
    // devtool: 'inline-source-map',
    // devServer: {
    //     contentBase: './dist',
    // },

    plugins: [

        new CleanWebpackPlugin({
            dry     : false,
            verbose : true,
        }),

        new HtmlWebpackPlugin({
            title           : 'Spotlight',
            filename        : 'index.html',
            template        : './src/spotlight.html',

            scriptLoading   : 'defer',
            inject          : false,
                /* Prevent automatic insertion of CSS link tag, as it will inlined, but specify postiion  of bundled.js in the lodash template. */

            /* _Lodash template params. */
            static: false
        }),

        new MiniCssExtractPlugin({
            filename        : 'spotlight.css',
            chunkFilename   : '[id].css',
        }),

        new StyleExtHtmlWebpackPlugin({
            position: 'head-bottom',
            minify: true
        }),

        new CopyPlugin({
            patterns: [
                { from: './license.txt', to: '../dist/license.txt' },
                { from: './notice.txt', to: '../dist/notice.txt' },
                { from: './readme_html.md', to: '../dist/readme.md' },
            ],
        }),
    ],
    module: {
        rules: [

            /* Images - uses require(lodash template), HtmlWebpackPlugin and file-loader to pickup and process(copy to dist).  */
            {
                test: /\.(png|svg|jpg|gif)$/,
                loader: 'file-loader',
                options: {
                    name: "images/[name].[ext]",
                    esModule: false
                    /* Fixes [object,object] appearing on spotlight.html template when require(file) processed by lodash in HtmlWebpackPlugin.*/
                }
            },

            /* SASS-CSS-Extract-Internalise\Inline - uses StyleExtHtmlWebpackPlugin*/
            {
                test: /\.s[ac]ss$/i,
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader,
                        options: {
                        },
                    },
                    'css-loader',
                    'sass-loader',
                ],
            },
        ],
    },
};

Upvotes: 2

Related Questions