I'm using min-cc-extract-plugin with webpack 4, but it seems doesn't work. Could you please give me some help? My config is:
/**
* ------------------------------------------------------------
webpack.prod.conf.js
* ------------------------------------------------------------
*/
const path = require('path')
const webpack = require('webpack')
const merge = require('webpack-merge')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin')
const SwRegisterWebpackPlugin = require('sw-register-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const utils = require('./utils')
const config = require('../config')
const entries = require('./entries')
let baseWebpackConfig = require('./webpack.base.conf')
let prodConfig = merge(baseWebpackConfig, {
mode: 'production',
module: {
rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap,
extract: true,
usePostCSS: true
})
},
devtool: config.build.productionSourceMap ? '#source-map' : false,
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash:7].js'),
chunkFilename: utils.assetsPath('js/[name].[chunkhash:7].js')
},
optimization: {
runtimeChunk: {
name: 'manifest'
},
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: -20,
chunks: 'all'
}
}
},
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false
}
},
sourceMap: config.build.productionSourceMap,
parallel: true
}),
new OptimizeCSSAssetsPlugin({
cssProcessorOptions: {
discardComments: {
removeAll: true
},
safe: true
}
})
]
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: utils.assetsPath('css/[name].[contenthash:7].css'),
chunkFilename: utils.assetsPath('css/[name].[contenthash:7].css'),
}),
new SWPrecacheWebpackPlugin(config.swPrecache.build),
new SwRegisterWebpackPlugin({
prefix: '/',
filePath: path.resolve(__dirname, '../src/sw-register.js')
})
]
})
Object.keys(entries).forEach(function (entry) {
const chunks = ['manifest', 'vendors', entry]
baseWebpackConfig.plugins.push(
new HtmlWebpackPlugin({
isProd: true,
chunks,
filename: entry + '/index.html',
template: 'src/template/index.html',
inject: true,
chunksSortMode(chunk1, chunk2) {
const orders = chunks
const order1 = orders.indexOf(chunk1.names[0])
const order2 = orders.indexOf(chunk2.names[0])
return order1 - order2
},
})
)
})
module.exports = prodConfig
/**
* ------------------------------------------------------------
webpack.base.conf.js
* ------------------------------------------------------------
*/
const path = require('path')
const webpack = require('webpack')
const {
VueLoaderPlugin
} = require('vue-loader')
const config = require('../config')
const utils = require('./utils')
const entries = require('./entries')
const projectRoot = path.resolve(__dirname, '../')
const isProd = process.env.NODE_ENV === 'production'
utils.buildExtensions();
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
let rootDir = process.cwd()
// let fabricModule = path.join(rootDir, 'node_modules', 'fabric');
// let fabric = path.join(fabricModule, 'dist', 'fabric.js');
const baseWebpackConfig = {
entry: {
vendors: ['vue']
},
output: {
path: config.build.assetsRoot,
publicPath: isProd ? config.build.assetsPublicPath : config.dev.assetsPublicPath,
filename: '[name].js',
chunkFilename: '[name].js'
},
externals: {
jquery: 'jQuery',
},
resolve: {
extensions: ['.js', '.vue'],
modules: [path.join(__dirname, '../node_modules')],
alias: {
'~': resolve('src'),
}
},
resolveLoader: {
modules: [path.join(__dirname, '../node_modules')]
},
module: {
rules: [
{
test: /pixi\.js/,
loader: "expose-loader?PIXI"
},
{
test: /phaser-split\.js/,
loader: "expose-loader?Phaser"
},
{
test: /p2\.js/,
loader: "expose-loader?p2"
},
{
test: /jtopo\.js/,
loader: "exports-loader?window.JTopo!script-loader"
},
// {
// test: /fabric/,
// loader: "exports-loader?window.fabric!script-loader"
// },
{
test: /\.vue$/,
loader: 'eslint-loader',
enforce: 'pre',
include: projectRoot,
exclude: /node_modules/
},
{
test: /\.js$/,
loader: 'eslint-loader',
enforce: 'pre',
include: projectRoot,
exclude: /node_modules/
},
{
test: /\.vue$/,
use: [{
loader: 'vue-loader',
options: {
compilerOptions: {
preserveWhitespace: true
}
}
}]
},
{
test: /\.js$/,
loader: 'babel-loader',
include: projectRoot,
exclude: /node_modules/
},
{
test: /\.(jpg|png|gif|eot|svg|ttf|woff|woff2)$/,
use: [{
loader: 'url-loader',
options: {
name: utils.assetsPath('img/[name].[hash:7].[ext]'),
limit: 1
}
}]
}
]
},
plugins: [
new VueLoaderPlugin(),
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery',
})
]
}
baseWebpackConfig.entry = Object.assign(baseWebpackConfig.entry, entries)
module.exports = baseWebpackConfig
/**
* ------------------------------------------------------------
utils.js
* ------------------------------------------------------------
*/
'use strict'
const path = require('path')
const fs = require('fs')
const chalk = require('chalk')
const glob = require('glob')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const config = require('../config')
const isProd = process.env.NODE_ENV === 'production'
exports.assetsPath = function (newPath) {
const assetsSubDirectory =
process.env.NODE_ENV === 'production' ? config.build.assetsSubDirectory : config.dev.assetsSubDirectory
return path.posix.join(assetsSubDirectory, newPath)
}
exports.cssLoaders = function (options) {
options = options || {}
const cssLoader = {
loader: 'css-loader',
options: {
minimize: process.env.NODE_ENV === 'production',
sourceMap: options.sourceMap
}
}
const postcssLoader = {
loader: 'postcss-loader',
options: {
sourceMap: options.sourceMap
}
}
function generateLoaders(loader, loaderOptions) {
const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
if (loader) {
loaders.push({
loader: loader + '-loader',
options: Object.assign({}, loaderOptions, {
sourceMap: options.sourceMap
})
})
}
if (options.extract) {
return [
{
use: [MiniCssExtractPlugin.loader, ...loaders]
},
{
use: ['vue-style-loader', ...loaders]
}
]
}
return [
{
use: ['vue-style-loader', ...loaders]
}
]
}
// https://vue-loader.vuejs.org/en/configurations/extract-css.html
return {
css: generateLoaders(),
postcss: generateLoaders(),
less: generateLoaders('less')
// sass: generateLoaders('sass', { indentedSyntax: true }),
// scss: generateLoaders('sass'),
// stylus: generateLoaders('stylus'),
// styl: generateLoaders('stylus'),
}
}
// Generate loaders for standalone style files (outside of .vue)
exports.styleLoaders = function (options) {
const output = []
const loaders = exports.cssLoaders(options)
Object.keys(loaders).forEach(function (extension) {
output.push({
test: new RegExp('\\.' + extension + '$'),
oneOf: loaders[extension]
})
})
return output
}
/**
* get relative path
*/
exports.joinPath = function (currentDir, subDir) {
if(arguments.length > 1){
return path.join(currentDir, subDir);
} else {
return path.join(__dirname, '..', currentDir);
}
}
exports.buildExtensions = () => {
function getExtensions() {
const extensions = config.base.extensions;
let result = {};
extensions.map(extension => {
result[extension.type] = {};
let globInstance = new glob.Glob('*/' + extension.filename, {
cwd: exports.joinPath('./src/' + extension.folder),
sync: true
});
globInstance.found.forEach(extensionPath => {
let extensionName = extensionPath.replace(new RegExp('\/' + extension.filename), '')
result[extension.type][extensionName] = exports.joinPath('./src/' + extension.folder, extensionPath);
});
})
return result;
}
const extensions = getExtensions();
Object.keys(extensions).map(function (type) {
const lineEnd = '\n';
let entryFileContent = [];
let importContent = [];
let registerContent = [];
let exportContent = [];
Object.keys(extensions[type]).map(extensionName => {
const fileName = extensions[type][extensionName].replace(/\//g, "\/").replace(/\\/g, "\\\\");
const camelCaseKey = extensionName.replace(/-([a-z])?/g, (all, letter) => {
return letter.toUpperCase();
});
if(type === 'mixin') {
importContent.push(`import ${camelCaseKey} from '../../${fileName}';`);
registerContent.push(`Vue.${type}(${camelCaseKey});`);
} else if(type === 'filter' || type === 'directive') {
importContent.push(`import ${camelCaseKey} from '../../${fileName}';`);
registerContent.push(`Vue.${type}('${camelCaseKey}', ${camelCaseKey});`);
} else {
importContent.push(`import {${camelCaseKey}} from '../../${fileName}';`);
}
exportContent.push(`${camelCaseKey},`);
});
if(type === 'util') {
entryFileContent.push(importContent.join(lineEnd));
} else {
entryFileContent.push("import Vue from 'vue';");
entryFileContent.push(importContent.join(lineEnd));
entryFileContent.push(registerContent.join(lineEnd));
}
entryFileContent.push('export default {');
entryFileContent.push(exportContent.join(lineEnd));
entryFileContent.push('};');
fs.writeFile(exports.joinPath(`./src/${type}s/index.js`), entryFileContent.join(lineEnd), function (err) {
if (err) throw err;
console.log(chalk.green(`>>>>>>>>>>\nbuild file锛歴rc\/${type}s\/index.js is saved!`));
});
});
}
/**
* ------------------------------------------------------------
entries.js
* ------------------------------------------------------------
*/
const fs = require('fs')
const path = require('path')
const entryPath = path.resolve(__dirname, '../src/pages')
const entries = fs.readdirSync(entryPath).reduce(function (o, dirname) {
o[dirname] = path.join(entryPath, dirname)
return o
}, {})
module.exports = entries
@mobilesite it is not QA service, please use gitter or slack for this, if you want help, please create minimum reproducible test repo and reopen issue
Well, I've worked around it by myself. It was the "sideEffects": false option in the package.json which caused the problem. After removing it, everything is OK. Thank you all the same!
@mobilesite holy crap, I had the same issue and spent hours scratching my head on it.
I don't know if I would have figured this out without the tip. What does "sideEffects" have to do with the css not outputting, I wonder? Uglify maybe decides the css is not imported? Seems like a bug!
same here. I spent hours to find out that sideEffects: false causes my stylus file not converted into a CSS file.
very weird. it is a bug.
Most helpful comment
Well, I've worked around it by myself. It was the
"sideEffects": falseoption in the package.json which caused the problem. After removing it, everything is OK. Thank you all the same!