Skip to content

Commit 0e9aa5a

Browse files
committed
[BUGFIX] fix error - Maximum call stack size exceeded.
1 parent a2ed536 commit 0e9aa5a

File tree

31 files changed

+4096
-1458
lines changed

31 files changed

+4096
-1458
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
.idea
2+
build
23
node_modules
34
test/outputs

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 0.6.4 (Dec 19, 2020)
2+
- Bugfix the error: `Maximum call stack size exceeded` with webpack setting `optimization.concatenateModules: true`and usage in script imports from `react` and `redux`.
3+
- Add test case for single style without a scripts in webpack config.
4+
- Add silent mode in tests to suppress output log info in the console.
5+
- The option `ignore` can be the array of string or RegExp. Add default value of `ignore` as `['/node_modules/']` to ignore resources from `node_modules` path.
6+
- Update npm packages.
7+
18
## 0.6.3 (Oct 25, 2020)
29
Fix BREAKING CHANGE in Webpack 5: No more changes should happen to `Compilation.assets`. Update code accord new API.
310

README.md

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,39 @@
1-
[![npm version](https://badge.fury.io/js/webpack-fix-style-only-entries.svg)](https://www.npmjs.com/package/webpack-fix-style-only-entries)
1+
[![npm version](https://badge.fury.io/js/webpack-remove-empty-scripts.svg)](https://www.npmjs.com/package/webpack-remove-empty-scripts)
22

3-
# [webpack-fix-style-only-entries](https://www.npmjs.com/package/webpack-fix-style-only-entries)
3+
# [webpack-remove-empty-scripts](https://www.npmjs.com/package/webpack-remove-empty-scripts)
44

5-
This is a small plugin developed to solve the problem of having a style only entry (css/sass/less/stylus) generating an extra js file.
5+
6+
The plugin remove empty scripts generated by usage only a style (css/scss/sass/less/stylus) without a js script in entry.
67

78
You can find more info by reading the following issues:
89

910
- https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/518
1011
- https://github.com/webpack-contrib/mini-css-extract-plugin/issues/151
1112

12-
View on: [Github](https://github.com/fqborges/webpack-fix-style-only-entries) - [npm](https://www.npmjs.com/package/webpack-fix-style-only-entries)
13+
This is a fork of original plugin https://github.com/fqborges/webpack-fix-style-only-entries (ver. 0.6.0).
14+
In this fork fixed some deprecation messages and integration tests for Webpack 5. See the details in [changelog](https://github.com/webdiscus/webpack-remove-empty-scripts/blob/master/CHANGELOG.md#061-oct-20-2020).
15+
16+
The plugin support only **Webpack 5** using new ChunkGraph and ModuleGraph APIs.
17+
For **Webpack 4** use original [plugin](https://github.com/fqborges/webpack-fix-style-only-entries).
1318

14-
## How it works
15-
It just find js files from chunks of css only entries and remove the js file from the compilation.
19+
View on: [GitHub](https://github.com/webdiscus/webpack-remove-empty-scripts), [npmjs](https://www.npmjs.com/package/webpack-remove-empty-scripts)
1620

1721
## How to use
18-
install using your package manager of choice:
19-
- npm: `npm install -D webpack-fix-style-only-entries`
20-
- yarn: `yarn add -D webpack-fix-style-only-entries`
22+
Install `npm install -D webpack-remove-empty-scripts`.
2123

2224
Require and add to webpack.config plugins.
2325

24-
Warning: this plugin does not load styles or split your bundles, it just fix chunks of css only entries by removing the (almost) empty js file.
25-
2626
```javascript
27-
// ... other plugins
28-
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
29-
const FixStyleOnlyEntriesPlugin = require("webpack-fix-style-only-entries");
27+
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
28+
const RemoveEmptyScriptsPlugin = require('webpack-remove-empty-scripts');
3029

3130
module.exports = {
3231
entry: {
33-
"main" : "./app/main.js"
34-
"styles": ["./common/styles.css", "./app/styles.css"]
32+
'main' : './app/main.js',
33+
'styles': ['./common/styles.css', './app/styles.css']
3534
},
3635
module: {
36+
rules: [
3737
{
3838
test: /\.css$/,
3939
use: [
@@ -44,30 +44,37 @@ module.exports = {
4444
]
4545
},
4646
plugins: [
47-
new FixStyleOnlyEntriesPlugin(),
47+
new RemoveEmptyScriptsPlugin(),
4848
new MiniCssExtractPlugin({
49-
filename: "[name].[chunkhash:8].css",
49+
filename: '[name].[chunkhash:8].css',
5050
}),
5151
],
5252
};
5353
```
5454

5555
## Options
5656

57-
| Name | Type | Default | Description |
58-
|------------|--------------- |----------------------------------------|-------------|
59-
| extensions | Array[string] | ["less", "scss", "css", "styl","sass"] | file extensions for styles |
60-
| silent | boolean | false | supress logs to console |
61-
| ignore | string or RegExp | undefined | match resource to be ignored |
57+
| Name | Type | Default | Description |
58+
|------------|------------------|-----------------------------------------|-------------|
59+
| silent | boolean | false | supress logs to console |
60+
| extensions | Array[string] | ['css', 'scss', 'sass', 'less', 'styl'] | file extensions for styles |
61+
| ignore | string or RegExp or Array[string] or Array[RegExp] | ['/node_modules/'] | match resource path to be ignored, defaults the resources from `node_modules` are ignored|
6262

6363
### Example config:
64-
// to identify only 'foo' and 'bar' extensions as styles
65-
new FixStyleOnlyEntriesPlugin({ extensions:['foo', 'bar'] }),
64+
```JavaScript
65+
// supress logs to console, use it for production
66+
new RemoveEmptyScriptsPlugin({ silent: true })
67+
```
68+
69+
```JavaScript
70+
// to identify only 'foo' and 'bar' extensions as styles
71+
new RemoveEmptyScriptsPlugin({ extensions:['foo', 'bar'] })
72+
```
6673

6774
## Recipes
6875

6976
### I use a javascript entry to styles:
70-
Give an especial extension to your file (`.css.js` for example) and configure `new FixStyleOnlyEntriesPlugin({ extensions:['css.js'] })`. See: https://github.com/fqborges/webpack-fix-style-only-entries/issues/8.
77+
Give an especial extension to your file (`.css.js` for example) and configure `new RemoveEmptyScriptsPlugin({ extensions:['css.js'] })`.
7178

7279
### I use webpack-hot-middleware:
73-
Configure this plugin as `new FixStyleOnlyEntriesPlugin({ ignore: 'webpack-hot-middleware' })`. See: https://github.com/fqborges/webpack-fix-style-only-entries/issues/12 and https://github.com/fqborges/webpack-fix-style-only-entries/blob/master/test/cases/css-entry-with-ignored-hmr/webpack.config.js.
80+
Configure this plugin as `new RemoveEmptyScriptsPlugin({ ignore: 'webpack-hot-middleware' })`. See: https://github.com/webdiscus/webpack-remove-empty-scripts/blob/master/test/cases/css-entry-with-ignored-hmr/webpack.config.js

index.js

Lines changed: 114 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -5,117 +5,141 @@
55
const NAME = 'webpack-remove-empty-scripts';
66

77
const defaultOptions = {
8-
extensions: ['css', 'scss', 'sass', 'less', 'styl'],
9-
scriptExtensions: ['js', 'mjs'],
10-
silent: false,
11-
ignore: undefined,
8+
extensions: ['css', 'scss', 'sass', 'less', 'styl'],
9+
scriptExtensions: ['js', 'mjs'],
10+
silent: false,
11+
ignore: [
12+
'/node_modules/'
13+
],
1214
};
1315

1416
class WebpackRemoveEmptyScriptsPlugin {
15-
constructor(options) {
16-
this.apply = this.apply.bind(this);
17-
this.options = Object.assign({}, defaultOptions, options);
18-
}
19-
20-
apply(compiler) {
21-
const extensionsWithoutDots = this.options.extensions.map(e =>
22-
e[0] === '.' ? e.substring(1) : e
23-
);
24-
25-
const patternOneOfExtensions = extensionsWithoutDots
26-
.map(ext => escapeRegExp(ext))
27-
.join('|');
28-
29-
const reStylesResource = new RegExp(
30-
`[.](${patternOneOfExtensions})([?].*)?$`
31-
);
32-
33-
compiler.hooks.compilation.tap(NAME, compilation => {
34-
const resourcesCache = [];
35-
36-
compilation.hooks.chunkAsset.tap(NAME, (chunk, file) => {
37-
const isNotScript = defaultOptions.scriptExtensions.every((ext) => file.lastIndexOf('.' + ext) < 0);
38-
if (isNotScript) return;
39-
40-
// has entry modules
41-
if (compilation.chunkGraph.getNumberOfEntryModules(chunk) < 1) return;
42-
const entryModules = Array.from(compilation.chunkGraph.getChunkEntryModulesIterable(chunk));
43-
if (entryModules.length < 1) return;
44-
45-
const entryModule = entryModules[0];
46-
const entryResources = collectEntryResources(compilation, entryModule, resourcesCache);
47-
48-
const resources = this.options.ignore
49-
? entryResources.filter(res => !res.match(this.options.ignore))
50-
: entryResources;
51-
52-
const isStyleOnly =
53-
resources.length &&
54-
resources.every(resource => reStylesResource.test(resource));
55-
56-
if (isStyleOnly) {
57-
if (!this.options.silent) {
58-
console.log('[remove-empty-scripts] remove empty js from style only entry: ' + file);
59-
}
60-
61-
chunk.files.delete(file);
62-
compilation.deleteAsset(file);
17+
constructor(options) {
18+
this.apply = this.apply.bind(this);
19+
this.options = Object.assign({}, defaultOptions, options);
20+
this.options.ignore = defaultOptions.ignore;
21+
22+
// default ignore resource plus customer ignores
23+
if (options && options.hasOwnProperty('ignore')) {
24+
let optionIgnore = Array.isArray(options.ignore)
25+
? options.ignore
26+
: [options.ignore];
27+
28+
this.options.ignore = this.options.ignore.concat(optionIgnore);
6329
}
64-
});
65-
});
66-
}
67-
}
30+
}
6831

69-
function collectEntryResources(compilation, module, cache) {
70-
const index = compilation.moduleGraph.getPreOrderIndex(module),
71-
resources = [];
32+
apply(compiler) {
33+
const customIgnore = this.options.ignore;
7234

73-
// index of module is unique per compilation
74-
// module.id can be null, not used here
75-
if (cache[index] !== undefined) {
35+
const extensionsWithoutDots = this.options.extensions.map(e =>
36+
e[0] === '.' ? e.substring(1) : e
37+
);
7638

77-
return cache[index];
78-
}
39+
const patternOneOfExtensions = extensionsWithoutDots
40+
.map(ext => escapeRegExp(ext))
41+
.join('|');
7942

80-
if (typeof module.resource == 'string') {
81-
const resources = [module.resource];
82-
cache[index] = resources;
43+
const reStylesResource = new RegExp(
44+
`[.](${patternOneOfExtensions})([?].*)?$`
45+
);
8346

84-
return resources;
85-
}
47+
compiler.hooks.compilation.tap(NAME, compilation => {
48+
const resourcesCache = [];
8649

87-
if (module.dependencies) {
88-
module.dependencies.forEach(dep => {
89-
if(dep) {
90-
const module = compilation.moduleGraph.getModule(dep),
91-
originModule = compilation.moduleGraph.getParentModule(dep),
92-
nextModule = module || originModule;
50+
compilation.hooks.chunkAsset.tap(NAME, (chunk, file) => {
51+
const isNotScript = defaultOptions.scriptExtensions.every((ext) => file.lastIndexOf('.' + ext) < 0);
52+
if (isNotScript) return;
9353

94-
if (nextModule) {
95-
const depResources = collectEntryResources(compilation, nextModule, cache);
54+
// has entry modules
55+
if (compilation.chunkGraph.getNumberOfEntryModules(chunk) < 1) return;
56+
const entryModules = Array.from(compilation.chunkGraph.getChunkEntryModulesIterable(chunk));
57+
if (entryModules.length < 1) return;
9658

97-
for (let index = 0, length = depResources.length; index !== length; index++) {
98-
resources.push(depResources[index]);
99-
}
100-
}
101-
}
102-
});
103-
}
104-
cache[index] = resources;
59+
const entryModule = entryModules[0];
60+
const entryResources = collectEntryResources(compilation, entryModule, resourcesCache);
61+
62+
const resources = customIgnore.length > 0
63+
? entryResources.filter(res => customIgnore.every(ignore => !res.match(ignore)))
64+
: entryResources;
65+
66+
const isStyleOnly =
67+
resources.length &&
68+
resources.every(resource => reStylesResource.test(resource));
10569

106-
return resources;
70+
if (isStyleOnly) {
71+
if (!this.options.silent) {
72+
console.log('[remove-empty-scripts] remove empty js from style only entry: ' + file);
73+
}
74+
75+
chunk.files.delete(file);
76+
compilation.deleteAsset(file);
77+
}
78+
});
79+
});
80+
}
81+
}
82+
83+
function collectEntryResources(compilation, module, cache) {
84+
const index = compilation.moduleGraph.getPreOrderIndex(module),
85+
resources = [];
86+
87+
// the index can be null
88+
if (index == null) {
89+
return resources;
90+
}
91+
92+
// index of module is unique per compilation
93+
// module.id can be null, not used here
94+
if (cache[index] !== undefined) {
95+
return cache[index];
96+
}
97+
98+
if (typeof module.resource === 'string') {
99+
const resources = [module.resource];
100+
cache[index] = resources;
101+
102+
return resources;
103+
}
104+
105+
if (module.dependencies) {
106+
module.dependencies.forEach(dep => {
107+
if (dep) {
108+
const module = compilation.moduleGraph.getModule(dep),
109+
originModule = compilation.moduleGraph.getParentModule(dep),
110+
nextModule = module || originModule;
111+
112+
if (nextModule && (!dep.hasOwnProperty('decorator') || dep['decorator'].indexOf('__webpack_require__') < 0)) {
113+
const depResources = collectEntryResources(compilation, nextModule, cache);
114+
115+
for (let i = 0, length = depResources.length; i !== length; i++) {
116+
let depFile = depResources[i];
117+
if (resources.indexOf(depFile) < 0) {
118+
resources.push(depFile);
119+
}
120+
}
121+
}
122+
}
123+
});
124+
}
125+
126+
if (resources.length > 0) {
127+
cache[index] = resources;
128+
}
129+
130+
return resources;
107131
}
108132

109133
// https://github.com/lodash/lodash/blob/4.17.11/lodash.js#L14274
110134
const reRegExpChar = /[\\^$.*+?()[\]{}|]/g;
111135
const reHasRegExpChar = RegExp(reRegExpChar.source);
112136

113137
function escapeRegExp(string) {
114-
string = String(string);
138+
string = String(string);
115139

116-
return string && reHasRegExpChar.test(string)
117-
? string.replace(reRegExpChar, '\\$&')
118-
: string;
140+
return string && reHasRegExpChar.test(string)
141+
? string.replace(reRegExpChar, '\\$&')
142+
: string;
119143
}
120144

121145
module.exports = WebpackRemoveEmptyScriptsPlugin;

0 commit comments

Comments
 (0)