2

I am trying to get Webpack to render a static html and export relative css, starting from a pug template and css modules using HtmlWebpackPlugin.
My setup is as follow

//webpack.config.js

...

plugins: [
    new CleanWebpackPlugin(),

    new HtmlWebpackPlugin({
        template: "src/templates/index.pug",
        excludeChunks: ["landing", "runtime"],
        excludeAssets: [/index.*.js/]
    })

    new HtmlWebpackExcludeAssetsPlugin(),

    new MiniCssExtractPlugin({
        filename: "[name].[contenthash].css",
        chunkFilename: "[name].[contenthash].css"
    })
]
module: {
    rules: [
        /**
         * pug
         */
        {
            test: /\.pug$/,
            exclude: /node_modules/,
            use: ["pug-loader"]
        },

        /**
         * scss
         */
        {
            test: /\.scss$/,
            exclude: /node_modules|_.+.scss/,
            use: [
                {
                    loader: MiniCssExtractPlugin.loader,
                    options: {
                        hmr: process.env.NODE_ENV === "development"
                    }
                },
                {
                    loader: "css-loader",
                    options: {
                        modules: true,
                        localsConvention: "camelCase",
                        sourceMap: true
                    }
                },
                "postcss-loader",
                "sass-loader"
            ]
        }
    ]
}

...

Then in my index.pug file I would like to do something like this:

- var styles = require("../styles/style.module.scss")

div(class=styles.someClass)

The problem is that if I leave the configuration as is, I get

ERROR in ./src/styles/style.module.scss
Module build failed (from ./node_modules/mini-css-extract-plugin/dist/loader.js):
TypeError: this[MODULE_TYPE] is not a function

I managed to correctly get the transformed class names in the pug template by doing

/**
 * scss
 */
{
    test: /\.scss$/,
    exclude: /node_modules|_.+.scss/,
    use: [
        //{
        //    loader: MiniCssExtractPlugin.loader,
        //    options: {
        //        hmr: process.env.NODE_ENV === "development"
        //    }
        //},
        {
            loader: "css-loader",
            options: {
                modules: true,
                localsConvention: "camelCase",
                sourceMap: true,
                onlyLocals: true <=====
            }
        },
        "postcss-loader",
        "sass-loader"
    ]
}

But by removing MiniCssExtractLoader from the chain I obviously don't get the exported css file.
By setting onlyLocals: true, classes in pug work as expected because css-loader exports the mappings. If I remove - var styles = require("../styles/style.module.scss") from pug template and leave MiniCssExtractPlugin in the loaders' chain I get the opposite: css file, but no mappings.

Any idea? Is there a way to export the css file and get mappings back directly in the pug template?

P.S. I have also come across this example from pug-loader's GitHub page

var template = require("pug-loader!./file.pug");
// => returns file.pug content as template function

// or, if you've bound .pug to pug-loader
var template = require("./file.pug");

var locals = { /* ... */ };

var html = template(locals);
// => the rendered HTML

Thought template(locals) would inject the locals into the template but I might have misinterpreted, since the html returned have unchanged classes' names, thus problem persists.

Graham
  • 7,431
  • 18
  • 59
  • 84
Sal
  • 814
  • 6
  • 12
  • Why don't you create a javascript entry point then importing the (s)css files inside it ? It's a way more usual method than importing files from pug templates. – serrulien Jul 30 '19 at 12:49
  • Because I need to access the generated class names from the pug template. If you know a better way I am happy to consider alternatives. – Sal Jul 31 '19 at 13:10
  • I now understand your needs. I've never used css modules with a custom made webpack configuration. Having to require the scss file then injecting class names for each templates is tedious. I didn't find (yet) any webpack plugin to automate that but it should not be hard to create a plugin that taps between css-loader/minicssextractplugin and htmlwebpackplugin.Thanks. – serrulien Jul 31 '19 at 18:41

1 Answers1

1

Just found out, all it takes is inlining the loaders applied in the pug template

- var styles = require("!css-loader?modules&onlyLocals&localsConvention=camelCase!postcss-loader!sass-loader!../styles/style.module.scss")

and having the Webpack configuration to export the css file

entry: {
    index: ["./src/styles/style.module.scss"]
},
module: {
    rules: [
        /**
        * scss
        */
        {
            test: /\.scss$/,
            exclude: /node_modules|_.+.scss/,
            use: [
                {
                    loader: MiniCssExtractPlugin.loader,
                    options: {
                        hmr: process.env.NODE_ENV === "development"
                    }
                },
                {
                    loader: "css-loader",
                    options: {
                        modules: true
                    }
                },
                "postcss-loader",
                "sass-loader"
            ]
        }
    ]
},

If anyone knows a better solution to make use of source maps please post it.

Sal
  • 814
  • 6
  • 12