When I try to use the loader for mini-css-extract-plugin webpack returns the following error:
/node_modules/mini-css-extract-plugin/dist/loader.js:122
for (const asset of compilation.getAssets()) {
^
TypeError: compilation.getAssets(...) is not a function or its return value is not iterable
I am requiring the plugin and invoking the loader in my prod config file:
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const path = require("path");
const common = require("./webpack.common");
const merge = require("webpack-merge");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = merge(common, {
mode: "production",
output: {
filename: "[name].[contentHash].bundle.js",
path: path.resolve(__dirname, "dist")
},
plugins: [
new MiniCssExtractPlugin({filename: "[name].[contentHash].css"}),
new CleanWebpackPlugin()
],
module: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader, //3. Extract css into files
"css-loader", //2. Turns css into commonjs
"sass-loader" //1. Turns sass into css
],
},
],
},
});
I have it included in my devDependencies:
"mini-css-extract-plugin": "^1.3.6",
But still I receive the error. I haven't found anything in the [documentation][1] to indicate what could be happening. Is there something I'm overlooking with this code?
Why would methods from loader.js be getting flagged as 'not a function?'
[1]: https://www.npmjs.com/package/mini-css-extract-plugin
I understand getAssets is only available since webpack 4.40.0 https://webpack.js.org/api/compilation-object/#getassets
You could try:
Update webpack version to at least 4.40.0
Downgrade min-css-extract-plugin to 1.3.0
I tried the second one, and worked for me, but upgrading webpack should work too.
Related
I am trying to figure out webpack. I want to bundle a css and js file while also minimizing them at the same time. I am using optimize-css-assets-webpack-plugin and got it to work together with mini-css-extract-plugin. But, when bundling css into js with style-loader, the css is no longer minified. Does anyone have any ideas on what I can do to make this work?
Index js file:
import './styles.css';
// some js
Webpack config:
const path = require('path');
const OptimizeCss = require('optimize-css-assets-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const bundle = {
entry: './src/index.js',
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, './'),
},
module: {
rules: [
{
test: /\.css$/i,
use: [
'style-loader',
'css-loader'
],
},
],
},
optimization: {
minimizer: [
new OptimizeCss(),
new TerserPlugin()
]
},
mode: 'production',
watch: false
}
module.exports = bundle;
Edit: I am using webpack version 4.39.3
foo.js
foo.css
bar.css
foo.js includes foo.css.
And foo.css includes bar.css.
As result, I have bundle with bar.css. I want to exclude it. I am also using MiniCssExtractPlugin since I nee to separate css and js files.
What I already tried:
IgnorePlugin. It removes bar, but then MiniCssExtractPlugin fails because of it.
null-loader for bar.css ( I tried both test with regexp and include/exclude). It simply doesn't work (it could be that I miss something.
Two oneOf rules for /\.css$/: for with null-loader and another with everything else.
Nothing works.
What is the correct way to achieve it?
My webpack.config.js
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const webpack = require('webpack');
module.exports = {
plugins: [
new MiniCssExtractPlugin({ }),
new webpack.IgnorePlugin(/bar\.css$/)
],
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
'css-loader',
],
},
],
},
};
And the output
ERROR in ./src/foo.css
Module build failed (from ./node_modules/mini-css-extract-plugin/dist/loader.js):
Error: Cannot find module '-!../node_modules/css-loader/dist/cjs.js!./bar.css'
It seems that MiniCssExtractPlugin uses css-loader explicitly, so I can't skip module loading with configuration.
Solution is to use NormalModuleReplacementPlugin to replace bad file with empty one
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const webpack = require('webpack');
const pluginToIgnore = /bad_css\.css$/;
module.exports = {
plugins: [
new MiniCssExtractPlugin({}),
new webpack.NormalModuleReplacementPlugin(
pluginToIgnore,
'!./empty.css'
)
],
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
},
],
},
};
You can clone my minimum repo https://github.com/rockmandash/webpack-chunks-question
Or see the code below
page1.js
import 'core-js';
console.log('I am page1');
page2.js
import 'core-js';
import 'react';
import 'react-dom';
console.log('I am page2');
My webpack config:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const entriesFilePath = [
require.resolve('./src/page1.js'),
require.resolve('./src/page2.js'),
];
const mode = 'development';
const webpackConfig = entriesFilePath.map((entrieFilePath) => {
const fileName = path.basename(entrieFilePath, path.extname(entrieFilePath));
// fileName would be page1 and page2
return {
mode,
devtool: 'cheap-module-source-map',
entry: {
[fileName]: entrieFilePath,
},
output: {
filename: 'static/js/[name].js',
chunkFilename: 'static/js/[name].chunk.js',
},
optimization: {
splitChunks: {
chunks: 'all',
},
},
plugins: [
// Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin(
Object.assign(
{},
{
inject: true,
filename: `${fileName}.html`,
},
),
),
].filter(Boolean),
};
});
module.exports = webpackConfig; // I have to export an array, because in the future, I need to do something else.
The generated dist folder looks like this:
dist
/page1.html
/page2.html
/static
/js
/page1.js.map
/vendors~page2.chunk.js.map
/vendors~page1.chunk.js // this includes core-js !!!!
/page1.js
/page2.js
/vendors~page1.chunk.js.map
/vendors~page2.chunk.js // this includes core-js too !!!!
/page2.js.map
You see, the generated two chunks both includes core-js, how can I make my webpack config smart enough to automatically separate core-js or other common vendor files out of the box?
You do not need to import core-js anywhere. Create a .babelrc file.
{
"presets": [
[
"#babel/preset-env",
{
"debug": true,
"useBuiltIns": "usage",
"corejs": 3
}
]
]
}
Instal #babel/core, #babel/polyfill, #babel/preset-env, babel-loader
Add to webpack
const optimization = {
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
name: 'vendor',
chunks: 'all',
}
}
}
};
module: {rules: [{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
}
]},
Take a look at my solution, there is a complete code. Use of core-js. He uses only part polyfil when it is needed.
https://github.com/tomik23/webpack-babel-corejs/blob/master/webpack.config.js#L17
And the second solution needed in your code is to use spliChunks
https://github.com/tomik23/photoBlog/blob/master/config/webpack.config.js#L31
P.S. If you import 'core-js' in this way; you download the whole core-js if you use my method then core-js chooses only what is needed and packages are smaller.
I am trying to bundle one of our microservices which is using MQTT.js and I am struggling with really strange issue.
It is working fine without bundling, so ws is available in node_modules.
Stuff which I think matters:
error:
TypeError: WS is not a constructor
at WebSocketStream (dist/index.js:159329:16)
at createWebSocket (dist/index.js:147450:10)
at Object.buildBuilderBrowser (dist/index.js:147476:10)
at MqttClient.wrapper [as streamBuilder] (dist/index.js:147937:36)
at MqttClient._setupStream (dist/index.js:146471:22)
at new MqttClient (dist/index.js:146452:8)
at Function.connect (dist/index.js:147940:10)
webpack config:
const path = require('path');
const nodeExternals = require('webpack-node-externals');
const { NODE_ENV = 'production' } = process.env;
module.exports = {
entry: { index: './src/index.ts' },
mode: NODE_ENV,
target: 'node',
watch: NODE_ENV === 'development',
externals: [nodeExternals()],
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
},
resolve: {
extensions: ['.ts', '.js'],
},
node: {
__dirname: false,
},
module: {
rules: [
{
test: /\.ts$/,
use: [{ loader: 'ts-loader', options: { transpileOnly: true } }],
},
{
test: /(\.md|\.map)$/,
loader: 'null-loader',
},
],
},
};
Function where it happens:
createMqttClient(): MqttClient {
return mqtt.connect(this.mqttOptions.url, { ...this.mqttOptions.options });
}
The url is like: ssl://url-to-our-mqtt
Can anybody help please?
I also ran into same issue.
The problem for me was that I used
plugins: [
new webpack.NormalModuleReplacementPlugin(/^mqtt$/, "mqtt/dist/mqtt.js"),
],
in webpack.config.js order to fix the shebang error that comes with mqtt.js since it is a CLI tool.
Then instead I have used
{
test: [
/node_modules[/\\]mqtt[/\\]mqtt.js/,
/node_modules[/\\]mqtt[/\\]bin[/\\]sub.js/,
/node_modules[/\\]mqtt[/\\]bin[/\\]pub.js/,
],
loader: 'shebang-loader'
},
And my problem was fixed. Do you also use mqtt/dist/mqtt.js instead of mqtt in your imports or if you do something similar to mine, the shebang-loader rule I have posted above might solve your problem.
I experienced the same with Amazon aws-iot-device-sdk-js and Microsoft azure-iot-device-mqtt which both include mqtt.
The initial issue is the build error:
ERROR in ./node_modules/mqtt/mqtt.js Module parse failed: Unexpected character '#' (1:0)
This error is caused by the package mqtt. Three files (mqtt.js, pub.js and sub.js) contain a shebang line
#!/usr/bin/env node
The solution using module replacement suggested some places
plugins: [
new webpack.NormalModuleReplacementPlugin(/^mqtt$/, "mqtt/dist/mqtt.js"),
],
unfortunately changes the build error with the run time error
TypeError: WS is not a constructor
As mentioned in other answers, webpack can be configured (https://webpack.js.org/concepts/loaders/) to use the shebang loader (https://www.npmjs.com/package/shebang-loader)
TL;DR
Install shebang-loader
npm install shebang-loader --save
In webpack.config.js use the loader
module.exports = {
...
module: {
rules: [
{
test:
[
/.*mqtt\.js$/,
/.*sub\.js$/,
/.*pub\.js$/
],
use: 'shebang-loader'
}
]
}
}
I managed to compress / compile my js and scss files with webpack. The scss file gets extracted by the 'extract-text-webpack-plugin' into an external css file. Here is the code:
var path = require('path');
var webpack = require('webpack');
var ExtractTextPlugin = require("extract-text-webpack-plugin");
require('es6-promise').polyfill();
module.exports = {
entry: ['./js/app'],
output: {
path: path.join(__dirname, 'js'),
publicPath: 'js',
filename: 'app.min.js'
},
plugins: [
new webpack.optimize.UglifyJsPlugin({
compressor: {
warnings: false,
},
}),
new webpack.optimize.OccurrenceOrderPlugin(),
new ExtractTextPlugin("../css/styles.min.css", {allChunks: false})
],
module: {
loaders: [{
test: /\.scss$/,
loader: ExtractTextPlugin.extract("style-loader", "css-loader!sass-loader")
}]
}
}
When I run now "webpack-dev-server js/app.js" - it gets my changes in the js file itself (alert for example) but does not remark changes in the css file.
This is the app.js:
var $ = require('jquery');
require('../css/styles.scss');
alert('Hi');
I guess the problem is connected to the ExtractTextPlugin. Then again I have no clue how to workaround. Any tips or ideas?
Okay, I got that going. When starting the webpack-dev-server, I'm just asking if we are in production enviroment:
const live = process.env.NODE_ENV === "production";
if(live) {
...
} else {
...
}
If we are not in production, it misses the ExtractTextPlugin out.
Finally to deploy my changes I type:
NODE_ENV=production webpack -p
One can also use a npm script to shorten this.