less.js
less.js copied to clipboard
Out of memory when trying to bundle AdminLTE and Bootstrap
I'm trying to bundle [email protected] and [email protected] with [email protected] via [email protected].
My webpack.config.js looks like this:
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { VueLoaderPlugin } = require('vue-loader');
module.exports = (env, argv) => {
const distPath = path.resolve('dist');
const srcPath = path.resolve('src');
const config = {
entry: {
'bundle': path.join(srcPath, 'js/index.js'),
},
output: {
chunkFilename: 'assets/[name].js',
filename: '[name].js',
path: distPath
},
module: {
rules: [
{
test: /\.vue$/,
exclude: /(node_modules|bower_components)/,
use: 'vue-loader',
},
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
plugins: [
"@babel/plugin-syntax-dynamic-import",
"@babel/plugin-syntax-object-rest-spread",
],
presets: [
'@babel/preset-env',
],
compact: false,
}
}
},
{
test: /\.css$/,
exclude: /(node_modules|bower_components)/,
oneOf: [
// this matches `<style module>`
{
resourceQuery: /module/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[local]_[hash:base64:5]'
}
}
]
},
// this matches plain `<style>` or `<style scoped>`
{
use: [
MiniCssExtractPlugin.loader,
'css-loader'
]
}
]
},
{
test: /\.less$/,
exclude: /(node_modules|bower_components)/,
oneOf: [
// this matches `<style module>`
{
resourceQuery: /module/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[local]_[hash:base64:5]'
},
},
'less-loader',
]
},
// this matches plain `<style>` or `<style scoped>`
{
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader',
]
}
]
},
{
test: /\.woff($|\?)|\.woff2($|\?)|\.ttf($|\?)|\.eot($|\?)|\.svg($|\?)/,
use: "file-loader"
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: 'bundle.css',
}),
new VueLoaderPlugin(),
],
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js',
}
},
};
return config;
};
and my main.less like this:
@import "~bootstrap/less/bootstrap.less";
@import "~admin-lte/build/less/AdminLTE.less";
When I try to build my project I get:
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
1: 00007FF704A3285F napi_wrap+119263
2: 00007FF7049D9526 v8::internal::OrderedHashTable<v8::internal::OrderedHashSet,1>::NextTableOffset+38102
3: 00007FF7049DA326 node::OnFatalError+438
4: 00007FF705217AAE v8::Isolate::ReportExternalAllocationLimitReached+94
5: 00007FF7051FFC61 v8::SharedArrayBuffer::Externalize+833
6: 00007FF7050B143C v8::internal::Heap::EphemeronKeyWriteBarrierFromCode+1436
7: 00007FF7050BC680 v8::internal::Heap::ProtectUnprotectedMemoryChunks+1312
8: 00007FF7050B9194 v8::internal::Heap::PageFlagsAreConsistent+3204
9: 00007FF7050AE993 v8::internal::Heap::CollectGarbage+1283
10: 00007FF7050AD004 v8::internal::Heap::AddRetainedMap+2500
11: 00007FF7050CE48B v8::internal::Factory::NewFixedArrayWithFiller+107
12: 00007FF7050C7317 v8::internal::Factory::InternalizeString<unsigned short>+471
13: 00007FF704F1DD3A v8::internal::HashTable<v8::internal::NumberDictionary,v8::internal::NumberDictionaryShape>::New+122
14: 00007FF7051CA05E v8::internal::Builtins::builtin_handle+317374
15: 00007FF7051C54D8 v8::internal::Builtins::builtin_handle+298040
16: 00007FF7051C503E v8::internal::Builtins::builtin_handle+296862
17: 00007FF70566CD5D v8::internal::SetupIsolateDelegate::SetupHeap+547181
18: 000001C9E13154E6
There doesn't seem to be any way of debugging this. But what I managed to figure out is that LESS seems to get stuck in an endless loop in ~bootstrap/less/mixins/grid-framework.less. This mixin seems to be the culprit:
.loop-grid-columns(@index, @class, @type) when (@index >= 0) {
.calc-grid-column(@index, @class, @type);
// next iteration
.loop-grid-columns((@index - 1), @class, @type);
}
which is weird because of it having a when -guard operator.
Thanks for issue report, we will investigate it later.
@brainz80 It looks like work as expected.
https://cdn.jsdelivr.net/npm/[email protected]/less/mixins/grid-framework.less
.loop-grid-columns(@index, @class, @type) when (@index >= 0) {
.calc-grid-column(@index, @class, @type);
// next iteration
.loop-grid-columns((@index - 1), @class, @type);
}
which is weird because of it having a when -guard operator.
Not weird, If a loop no when guard, we will get a infinite loop.
Ok, managed to figure out what the problem is.
Here's a simple setup:
main.less:
@import 'module1.less';
@import 'module2.less';
module1.less:
.iterate(@index) when (@index >= 0) {
&.item-@{index} {
color: rgb(255 - @index, 0, 0);
}
.iterate(@index - 1);
}
b {
.iterate(10);
}
module2.less:
.iterate(@index) when (@index >= 0) {
&.item-@{index} {
color: rgb(255 - @index, 0, 0);
}
.iterate(@index - 1);
}
a {
.iterate(20);
}
the problem is that both module1.less and module2.less define the mixin .iterate(). The same thing happens with Bootstrap and AdminLTE. AdminLTE uses a bundled version of Bootstrap and therefore overrides the one imported by:
@import "~bootstrap/less/bootstrap.less";
I'm not sure if this should be the desired result. Maybe the later definition of .iterate() should take precedence?
@brainz80 Thanks for detailed description, more clear for me.