Next-plugins: next-sass broken since Next.js 6

Created on 1 May 2018  路  14Comments  路  Source: vercel/next-plugins

I think there is something fundamentally wrong with next-sass since Next.js 6.

The issue seems to be that ExtractTextPlugin extracts css for each bundle, but only keeps the css from the last bundle in /_next/static/style.css, resulting in only one page being styled correctly, while styles are broken in all other pages.

There are some issues filed here that i think all describe the same problem slightly differently:

I isolated the problem in a barebones project using Next 6.0.0 with next-sass and CSS Modules:
https://github.com/MadeInHaus/next-6-sass-example

One hacky solution is implemented in this branch:
https://github.com/MadeInHaus/next-6-sass-example/tree/solution

I give a custom ExtractTextPlugin instance in next.config.js with a contenthash filename:

const withSass = require('@zeit/next-sass');

const ExtractTextPlugin = require('extract-text-webpack-plugin');
const extract = new ExtractTextPlugin({ filename: 'static/[contenthash].css' });

module.exports = withSass({
    cssModules: true,
    extractCSSPlugin: extract,
    webpack: config => {
        config.plugins.push(extract);
        return config;
    },
});

Then i pull in all generated css files in _document.js via this.props.buildManifest.css:

export default class MyDocument extends Document {
    render() {
        const { buildManifest } = this.props;
        const { css } = buildManifest;
        return (
            <html>
                <Head>
                    {css.map(file => {
                        return (
                            <link
                                rel="stylesheet"
                                href={`/_next/${file}`}
                                key={file}
                            />
                        );
                    })}
                </Head>
                <body>
                    <Main />
                    <NextScript />
                </body>
            </html>
        );
    }
}

This is a hack. There surely is a real solution but so far i wasn't able to come up with one.

Most helpful comment

@timneutkens Hey, actually, i got it to work with a slightly modified commonsChunkConfig, see source and comments below:

const withSass = require('@zeit/next-sass');

const commonsChunkConfig = (config, test = /\.css$/) => {
    config.plugins = config.plugins.map(plugin => {
        if (
            plugin.constructor.name === 'CommonsChunkPlugin' &&
            // disable filenameTemplate checks here because they never match
            // (plugin.filenameTemplate === 'commons.js' ||
            //     plugin.filenameTemplate === 'main.js')
            // do check for minChunks though, because this has to (should?) exist
            plugin.minChunks != null
        ) {
            const defaultMinChunks = plugin.minChunks;
            plugin.minChunks = (module, count) => {
                if (module.resource && module.resource.match(test)) {
                    return true;
                }
                return defaultMinChunks(module, count);
            };
        }
        return plugin;
    });
    return config;
};

module.exports = withSass({
    cssModules: true,
    webpack: config => {
        config = commonsChunkConfig(config, /\.(sass|scss|css)$/);
        return config;
    },
});

All 14 comments

When adding extractCSSPlugin you have to add the commonschunk config manually like this: https://github.com/zeit/next-plugins/issues/127#issuecomment-377905928

I'm planning to do a full rewrite of next-css etc.

@timneutkens Thanks, much appreciated! Anything we can do to help?

@claus did it work? 馃檹

@timneutkens you mean running commonsChunkConfig(config, /\.(sass|scss|css)$/)? No, that doesn't seem to fix the problem. Unless i am missing something

@timneutkens in fact, i'm not sure commonsChunkConfig is ever gonna do anything because there are no CommonsChunkPlugins with a filenameTemplate of either 'commons.js' or 'main.js' (at least in my test project). In the production build, there is a CommonsChunkPlugin with filenameTemplate of 'static/commons/main-[chunkhash].js'. In the dev build, there is a CommonsChunkPlugin with filenameTemplate of 'static/commons/main.js' and one with filenameTemplate of 'static/commons/manifest.js'.

Not sure if any of this is relevant to the original problem, which is that for each page bundle a css file is extracted, and as the files are all extracted into a file with the same name (static/style.css), they overwrite each other, so the final style.css only contains part of the app's css.

Or in other words, if you check out https://github.com/MadeInHaus/next-6-sass-example (the master branch, without that extractCSSPlugin hack) and npm i && npm run build && npm run start, the value of this.props.buildManifest.css in _document.js will be

[ 'static/style.css',
  'static/style.css',
  'static/style.css',
  'static/style.css' ]

And the file /_next/static/style.css will only contain css for one of the four bundles.

@timneutkens Hey, actually, i got it to work with a slightly modified commonsChunkConfig, see source and comments below:

const withSass = require('@zeit/next-sass');

const commonsChunkConfig = (config, test = /\.css$/) => {
    config.plugins = config.plugins.map(plugin => {
        if (
            plugin.constructor.name === 'CommonsChunkPlugin' &&
            // disable filenameTemplate checks here because they never match
            // (plugin.filenameTemplate === 'commons.js' ||
            //     plugin.filenameTemplate === 'main.js')
            // do check for minChunks though, because this has to (should?) exist
            plugin.minChunks != null
        ) {
            const defaultMinChunks = plugin.minChunks;
            plugin.minChunks = (module, count) => {
                if (module.resource && module.resource.match(test)) {
                    return true;
                }
                return defaultMinChunks(module, count);
            };
        }
        return plugin;
    });
    return config;
};

module.exports = withSass({
    cssModules: true,
    webpack: config => {
        config = commonsChunkConfig(config, /\.(sass|scss|css)$/);
        return config;
    },
});

next-css also has this issue, but only in production. I hope others are seeing this?

edit: looks like it's just serving the first imported css file

@claus - thanks, your fix seems to work!

Please send a pull request @claus

@claus thank you very much, commented.

I wonder if we could remove the commons chunk thing in the future.

Still having this issue as of [email protected] it seems 馃.
I'm unable to import two scss files in two different components, only the first one gets picked up and bundled
@claus 's workaround here https://github.com/zeit/next-plugins/issues/157#issuecomment-385885772 does make it work though !
Tell me if I can be of any help 馃槂

EDIT : I also have next-css enabled, here's my next config : https://gist.github.com/mininao/36d928f168a238d6dff3de0d4894b094

Was this page helpful?
0 / 5 - 0 ratings

Related issues

danilofuchs picture danilofuchs  路  12Comments

thibautRe picture thibautRe  路  14Comments

eugeneross picture eugeneross  路  34Comments

ghost picture ghost  路  29Comments

selique picture selique  路  12Comments