0

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];
};
Tom
  • 73
  • 1
  • 8
  • It would appear that part of the output produced are ..css e.g yellow.baa1638b21372ac5c21a.css (see This file see line 7 of the first code snippet for theme names. These files get output but actually contain webpackBootstrap js code so subsequent CSS processing fails because the contents of the files aren't valid css. I'd love to know if anyone else has come across this. It is driving me insane – Tom Jul 02 '19 at 07:13

1 Answers1

1

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';
  }
}),
KevinChappell
  • 458
  • 5
  • 9
  • Hi Kevin, Thanks I hadn't seen that option available. – Tom Jul 03 '19 at 01:34
  • Unfortunately that doesn't seem to work it never gets called.. if I console log in there and run webpack nothing ever gets produced i.e. ``` moduleFilename: (chunkData) => { console.log('--- MODULE CHUNK DATA ---', chunkData); if (chunkData.name.includes('admin') || chunkData.name.includes('client') || chunkData.name.includes('login')) { return 'app.css'; } return chunkData.name + '.css'; }``` – Tom Jul 03 '19 at 05:02
  • `moduleFilename` is a new option published only a month ago, you may need to update mini-css-extract-plugin to `0.7.0` to get the new feature. – KevinChappell Jul 03 '19 at 18:04
  • Thanks Kevin that is great.. Previously using extract-text-plugin I was able to do something like this to merge all entry points in to one css file.. doesn't seem to be the case for minicss if (chunkData.name.includes('admin') || chunkData.name.includes('client') || chunkData.name.includes('login')) { return 'app.css'; } – Tom Jul 04 '19 at 01:41
  • oh, to merge the files I think your logic would have to be in the SplitChunksPlugin using the `chunks` or `cacheGroup` option. ``` { optimization: { splitChunks: { cacheGroups: { app: { test: /admin|client|logion/, name: 'app', chunks: 'all', } } }, }, } ``` – KevinChappell Jul 08 '19 at 16:32