Search code examples
webpack.net-corewebpack-4mini-css-extract-plugin

Webpack 4 Upgrade - Suspected mini-css-extract-plugin - CSS Never Loads - Uncaught SyntaxError: Unexpected token


I've upgraded from webpack v2 to webpack v4 - because extract text plugin no longer exists I've replaced it with MiniCssExtract plugin and had to hold off upgrading so I could process filenames as a function.

Now that this is available I'm running webpack but then in my app nothing css oriented will run I will always get: Uncaught SyntaxError: Unexpected token . in the console.

I've tried even the simplest CSS and no css files will properly execute in the app.

I've tried to strip back to simple css-loader etc and they work fine so I'm thinking it's something to do with the way that mini-css-extract-plugin is configured or something stupid I've missed.

I have one file for my theme config:

const fs = require('fs');
const merge = require('webpack-merge');
const path = require('path');
const lessPlugins = require('../Theme/plugins/customLess');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

const themes = ['green', 'yellow'];
const getThemeConfig = (isDevBuild, themeNameToPassInToLess) => {
    return {
        test: /\.(css|less)$/,
        use: ['style-loader',
            {
                loader: MiniCssExtractPlugin.loader,
                options: {
                    importLoaders: 1,
                    hmr: isDevBuild
                }
            },
            'css-loader',
            'postcss-loader',
            {
                loader: "less-loader",
                options: {
                    minimize: false,
                    plugins: [
                        lessPlugins
                    ],
                    globalVars: {
                        themeName: themeNameToPassInToLess
                    }
                }
            }]
    };
};

module.exports = {
    getThemeConfig,
    getCurrentTheme: () => {
        const AppSettings = JSON.parse(stripJsonComments(fs.readFileSync('./appsettings.json').toString()));
        if (AppSettings && AppSettings.ThemeConfig && AppSettings.ThemeConfig.ThemeName) {
            return AppSettings.ThemeConfig.ThemeName;
        }
        return 'default';
    },
    getThemeConfigs: (isDevBuild, sharedConfig, bundleOutputDir) => {
        const result = [];

        for (const theme of themes) {
            result.push(merge({
                entry: {
                    [theme]: './Theme/sites/default.less'
                },
                output: {
                    path: path.join(__dirname, bundleOutputDir),
                    filename: '[name].[chunkhash].css',
                    publicPath: '/dist/'
                },
                module: {
                    rules: [getThemeConfig(isDevBuild, theme)]
                }
            }, sharedConfig));
        }

        return result;
    }
};

and the main webpack file is here:

const path = require('path');
const nodeExternals = require('webpack-node-externals');
const webpack = require('webpack');
const merge = require('webpack-merge');

const AutoPrefixer = require('autoprefixer');
const StatsWriterPlugin = require("webpack-stats-plugin").StatsWriterPlugin;
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const ForkTsCheckerNotifierWebpackPlugin = require('fork-ts-checker-notifier-webpack-plugin');

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const WebpackMd5Hash = require("webpack-md5-hash");

const bundleOutputDir = './wwwroot/dist';

const themeHelpers = require('./Webpack/themes');

let stats = {};

const cpus = require('os').cpus().length;
const settings = fs.existsSync('./webpackSettings.json') ?
    require('./webpackSettings.json') : {
        typeCheckerWorkers: Math.min(2, cpus),
        transpilerWorkers: Math.max(1, cpus - 3),
        typeCheckingOverlay: true,
    };

module.exports = (env, argv) => {
    console.log(env);
    const isDevBuild = !(env && env.prod);
    const sharedConfig = {
        devtool: isDevBuild ? 'source-map' : false,
        mode: isDevBuild ? 'development' : 'production',
        optimization: { minimize: !isDevBuild },
        stats: {
            modules: false
        },

        resolve: {
            extensions: ['.js', '.jsx', 'json', '.ts', '.tsx', '.modern'],
            modules: ['.', './', 'node_modules'],
            alias: {
                '../../theme.config$': path.join(__dirname, 'Theme/theme.config')
            }
        },
        externals: [nodeExternals()],
        module: {
            rules: [
                {
                    test: /\.(png|jpg|jpeg|gif)$/,
                    use: 'url-loader?limit=25000'
                },
                {
                    test: /\.(ttf|otf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
                    loader: 'file-loader?name=public/fonts/[name].[ext]'
                }
            ]
        },

        plugins: [new MiniCssExtractPlugin({
                disable: isDevBuild,
                filename: (chunkData) => {
                    const name = chunkData.chunk.name.replace('js/build/', '').replace('components', 'base');
                    if (name.includes('admin') || name.includes('client') || name.includes('login')) {
                        return name + '-app.css';
                    }
                    return name + '.css';
                },
                chunkFilename: '[name].css'
            }),
            new ForkTsCheckerWebpackPlugin({
                workers: settings.typeCheckerWorkers,
                async: !settings.typeCheckingOverlay
            }),
            new webpack.LoaderOptionsPlugin({
                minimize: !isDevBuild,
                options: {
                    postcss: [AutoPrefixer]
                }
            }),
            new WebpackMd5Hash()
        ]
            .concat(settings.typeCheckingOverlay ? [] : [new ForkTsCheckerNotifierWebpackPlugin()])
            .concat(isDevBuild ? [
                // Plugins that apply in development builds only
                new webpack.DefinePlugin({
                    "process.env": {
                        NODE_ENV: JSON.stringify("development")
                    }
                })
            ] : [
                    // Plugins that apply in production builds only
                    new webpack.DefinePlugin({
                        "process.env": {
                            NODE_ENV: JSON.stringify("production")
                        }
                    })
                ])
    };

    const clientConfig = merge({
        entry: {
            'main-client': './ClientApp/boot.tsx',
            'login': './LoginApp/boot.tsx',
            'admin': './AdminApp/boot.tsx'
        },
        module: {
            rules: [{
                test: /\.tsx?$/,
                exclude: /node_modules/,
                include: /ClientApp|LoginApp|AdminApp|CommonApp/,
                use: [
                    `ifdef-loader?isDevBuild=${isDevBuild}`,
                    {
                        loader: 'awesome-typescript-loader',
                        options: {
                            silent: true,
                            transpileOnly: true,
                            useCache: true
                        }
                    }]
            },
            themeHelpers.getThemeConfig(isDevBuild, themeHelpers.getCurrentTheme())
            ]
        },
        output: {
            path: path.join(__dirname, bundleOutputDir),
            filename: isDevBuild ? '[name].js' : '[name].[chunkhash].js',
            publicPath: '/dist/'
        },
        plugins: [
            new webpack.DllReferencePlugin({
                context: __dirname,
                manifest: require('./wwwroot/dist/vendor-manifest.json')
            }),
            new webpack.LoaderOptionsPlugin({
                minimize: !isDevBuild,
                options: {
                    postcss: [AutoPrefixer]
                }
            }),
            new StatsWriterPlugin({
                filename: '../../webpackstats.json',
                transform(data) {
                    stats.assetsByChunkName = Object.assign({}, stats.assetsByChunkName, data.assetsByChunkName);
                    return JSON.stringify(stats, null, 4);
                }
            }),// Used by ScriptTagHelper
            new WebpackMd5Hash()
        ]

    }, sharedConfig);

    if (isDevBuild) {
        return clientConfig;
    }

    const themeConfigs = themeHelpers.getThemeConfigs(isDevBuild, sharedConfig, '.' + bundleOutputDir);
    console.log('ThemeConfigs Rules', themeConfigs[0].module.rules[0]);
    return [...themeConfigs, clientConfig];
};

Solution

  • Have you tried the moduleFilename option?

    Change

    new MiniCssExtractPlugin({
      disable: isDevBuild,
      filename: (chunkData) => {
          const name = chunkData.chunk.name.replace('js/build/', '').replace('components', 'base');
          if (name.includes('admin') || name.includes('client') || name.includes('login')) {
              return name + '-app.css';
          }
          return name + '.css';
      },
      chunkFilename: '[name].css'
    }),
    

    to

    new MiniCssExtractPlugin({
      disable: isDevBuild,
      moduleFilename: (chunkData) => {
          const name = chunkData.name.replace('js/build/', '').replace('components', 'base');
          if (name.includes('admin') || name.includes('client') || name.includes('login')) {
              return name + '-app.css';
          }
          return name + '.css';
      }
    }),