webpack-bundle-analyzer 2.13.0 → 2.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -14,6 +14,17 @@ _Note: Gaps between patch versions are faulty, broken or test releases._
14
14
 
15
15
  <!-- Add changelog entries for new changes under this section -->
16
16
 
17
+ ## 2.13.1
18
+
19
+ * **Improvement**
20
+ * Pretty-format the generated stats.json ([#180](https://github.com/webpack-contrib/webpack-bundle-analyzer/pull/180)) [@edmorley](https://github.com/edmorley))
21
+
22
+ * **Bug Fix**
23
+ * Properly parse Webpack 4 async chunk with `Array.concat` optimization ([#184](https://github.com/webpack-contrib/webpack-bundle-analyzer/pull/184), fixes [#183](https://github.com/webpack-contrib/webpack-bundle-analyzer/issues/183))
24
+
25
+ * **Internal**
26
+ * Refactor bundle parsing logic ([#184](https://github.com/webpack-contrib/webpack-bundle-analyzer/pull/184))
27
+
17
28
  ## 2.13.0
18
29
 
19
30
  * **Improvement**
@@ -98,6 +98,7 @@ var BundleAnalyzerPlugin = function () {
98
98
 
99
99
  try {
100
100
  yield bfj.write(statsFilepath, stats, {
101
+ space: 2,
101
102
  promises: 'ignore',
102
103
  buffers: 'ignore',
103
104
  maps: 'ignore',
package/lib/parseUtils.js CHANGED
@@ -25,40 +25,31 @@ function parseBundle(bundlePath) {
25
25
 
26
26
  walk.recursive(ast, walkState, {
27
27
  CallExpression(node, state, c) {
28
- if (state.sizes) return;
28
+ if (state.locations) return;
29
29
 
30
30
  var args = node.arguments;
31
31
 
32
- // Additional bundle without webpack loader.
33
- // Modules are stored in second argument, after chunk ids:
34
- // webpackJsonp([<chunks>], <modules>, ...)
35
- // As function name may be changed with `output.jsonpFunction` option we can't rely on it's default name.
36
- if (node.callee.type === 'Identifier' && args.length >= 2 && isArgumentContainsChunkIds(args[0]) && isArgumentContainsModulesList(args[1])) {
37
- state.locations = getModulesLocationFromFunctionArgument(args[1]);
32
+ // Main chunk with webpack loader.
33
+ // Modules are stored in first argument:
34
+ // (function (...) {...})(<modules>)
35
+ if (node.callee.type === 'FunctionExpression' && !node.callee.id && args.length === 1 && isSimpleModulesList(args[0])) {
36
+ state.locations = getModulesLocations(args[0]);
38
37
  return;
39
38
  }
40
39
 
41
- // Additional bundle without webpack loader, with module IDs optimized.
42
- // Modules are stored in second arguments Array(n).concat() call
43
- // webpackJsonp([<chunks>], Array([minimum ID]).concat([<module>, <module>, ...]))
40
+ // Async Webpack < v4 chunk without webpack loader.
41
+ // webpackJsonp([<chunks>], <modules>, ...)
44
42
  // As function name may be changed with `output.jsonpFunction` option we can't rely on it's default name.
45
- if (node.callee.type === 'Identifier' && (args.length === 2 || args.length === 3) && isArgumentContainsChunkIds(args[0]) && isArgumentArrayConcatContainingChunks(args[1])) {
46
- state.locations = getModulesLocationFromArrayConcat(args[1]);
47
- return;
48
- }
49
-
50
- // Main bundle with webpack loader
51
- // Modules are stored in first argument:
52
- // (function (...) {...})(<modules>)
53
- if (node.callee.type === 'FunctionExpression' && !node.callee.id && args.length === 1 && isArgumentContainsModulesList(args[0])) {
54
- state.locations = getModulesLocationFromFunctionArgument(args[0]);
43
+ if (node.callee.type === 'Identifier' && mayBeAsyncChunkArguments(args) && isModulesList(args[1])) {
44
+ state.locations = getModulesLocations(args[1]);
55
45
  return;
56
46
  }
57
47
 
58
- // Additional bundles with webpack 4 are loaded with:
59
- // (window.webpackJsonp=window.webpackJsonp||[]).push([[chunkId], [<module>, <module>], [[optional_entries]]]);
60
- if (isAsyncChunkPushExpression(node) && args.length === 1 && isArgumentContainingChunkIdsAndModulesList(args[0])) {
61
- state.locations = getModulesLocationFromFunctionArgument(args[0].elements[1]);
48
+ // Async Webpack v4 chunk without webpack loader.
49
+ // (window.webpackJsonp=window.webpackJsonp||[]).push([[<chunks>], <modules>, ...]);
50
+ // As function name may be changed with `output.jsonpFunction` option we can't rely on it's default name.
51
+ if (isAsyncChunkPushExpression(node)) {
52
+ state.locations = getModulesLocations(args[0].elements[1]);
62
53
  return;
63
54
  }
64
55
 
@@ -86,60 +77,45 @@ function parseBundle(bundlePath) {
86
77
  };
87
78
  }
88
79
 
89
- function isArgumentContainsChunkIds(arg) {
90
- // Array of numeric or string ids. Chunk IDs are strings when NamedChunksPlugin is used
91
- return arg.type === 'ArrayExpression' && _.every(arg.elements, isModuleId);
80
+ function isModulesList(node) {
81
+ return isSimpleModulesList(node) ||
82
+ // Modules are contained in expression `Array([minimum ID]).concat([<module>, <module>, ...])`
83
+ isOptimizedModulesArray(node);
92
84
  }
93
85
 
94
- function isArgumentContainsModulesList(arg) {
95
- if (arg.type === 'ObjectExpression') {
96
- return _(arg.properties).map('value').every(isModuleWrapper);
97
- }
98
-
99
- if (arg.type === 'ArrayExpression') {
100
- // Modules are contained in array.
101
- // Array indexes are module ids
102
- return _.every(arg.elements, function (elem) {
103
- return (
104
- // Some of array items may be skipped because there is no module with such id
105
- !elem || isModuleWrapper(elem)
106
- );
107
- });
108
- }
86
+ function isSimpleModulesList(node) {
87
+ return (
88
+ // Modules are contained in hash. Keys are module ids.
89
+ isModulesHash(node) ||
90
+ // Modules are contained in array. Indexes are module ids.
91
+ isModulesArray(node)
92
+ );
93
+ }
109
94
 
110
- return false;
95
+ function isModulesHash(node) {
96
+ return node.type === 'ObjectExpression' && _(node.properties).map('value').every(isModuleWrapper);
111
97
  }
112
98
 
113
- function isArgumentContainingChunkIdsAndModulesList(arg) {
114
- if (arg.type === 'ArrayExpression' && arg.elements.length >= 2 && isArgumentContainsChunkIds(arg.elements[0]) && isArgumentContainsModulesList(arg.elements[1])) {
115
- return true;
116
- }
117
- return false;
99
+ function isModulesArray(node) {
100
+ return node.type === 'ArrayExpression' && _.every(node.elements, function (elem) {
101
+ return (
102
+ // Some of array items may be skipped because there is no module with such id
103
+ !elem || isModuleWrapper(elem)
104
+ );
105
+ });
118
106
  }
119
107
 
120
- function isArgumentArrayConcatContainingChunks(arg) {
121
- if (arg.type === 'CallExpression' && arg.callee.type === 'MemberExpression' &&
108
+ function isOptimizedModulesArray(node) {
109
+ // Checking whether modules are contained in `Array(<minimum ID>).concat(...modules)` array:
110
+ // https://github.com/webpack/webpack/blob/v1.14.0/lib/Template.js#L91
111
+ // The `<minimum ID>` + array indexes are module ids
112
+ return node.type === 'CallExpression' && node.callee.type === 'MemberExpression' &&
122
113
  // Make sure the object called is `Array(<some number>)`
123
- arg.callee.object.type === 'CallExpression' && arg.callee.object.callee.type === 'Identifier' && arg.callee.object.callee.name === 'Array' && arg.callee.object.arguments.length === 1 && isNumericId(arg.callee.object.arguments[0]) &&
114
+ node.callee.object.type === 'CallExpression' && node.callee.object.callee.type === 'Identifier' && node.callee.object.callee.name === 'Array' && node.callee.object.arguments.length === 1 && isNumericId(node.callee.object.arguments[0]) &&
124
115
  // Make sure the property X called for `Array(<some number>).X` is `concat`
125
- arg.callee.property.type === 'Identifier' && arg.callee.property.name === 'concat' &&
116
+ node.callee.property.type === 'Identifier' && node.callee.property.name === 'concat' &&
126
117
  // Make sure exactly one array is passed in to `concat`
127
- arg.arguments.length === 1 && arg.arguments[0].type === 'ArrayExpression') {
128
- // Modules are contained in `Array(<minimum ID>).concat(` array:
129
- // https://github.com/webpack/webpack/blob/v1.14.0/lib/Template.js#L91
130
- // The `<minimum ID>` + array indexes are module ids
131
- return true;
132
- }
133
-
134
- return false;
135
- }
136
-
137
- function isAsyncChunkPushExpression(node) {
138
- var callee = node.callee;
139
-
140
- return callee.type === 'MemberExpression' && callee.property.name === 'push' && callee.object.type === 'AssignmentExpression' && (callee.object.left.object.name === 'window' ||
141
- // Webpack 4 uses `this` instead of `window`
142
- callee.object.left.object.type === 'ThisExpression');
118
+ node.arguments.length === 1 && isModulesArray(node.arguments[0]);
143
119
  }
144
120
 
145
121
  function isModuleWrapper(node) {
@@ -161,9 +137,29 @@ function isNumericId(node) {
161
137
  return node.type === 'Literal' && Number.isInteger(node.value) && node.value >= 0;
162
138
  }
163
139
 
164
- function getModulesLocationFromFunctionArgument(arg) {
165
- if (arg.type === 'ObjectExpression') {
166
- var modulesNodes = arg.properties;
140
+ function isChunkIds(node) {
141
+ // Array of numeric or string ids. Chunk IDs are strings when NamedChunksPlugin is used
142
+ return node.type === 'ArrayExpression' && _.every(node.elements, isModuleId);
143
+ }
144
+
145
+ function isAsyncChunkPushExpression(node) {
146
+ var callee = node.callee,
147
+ args = node.arguments;
148
+
149
+
150
+ return callee.type === 'MemberExpression' && callee.property.name === 'push' && callee.object.type === 'AssignmentExpression' && callee.object.left.object && (callee.object.left.object.name === 'window' ||
151
+ // Webpack 4 uses `this` instead of `window`
152
+ callee.object.left.object.type === 'ThisExpression') && args.length === 1 && args[0].type === 'ArrayExpression' && mayBeAsyncChunkArguments(args[0].elements) && isModulesList(args[0].elements[1]);
153
+ }
154
+
155
+ function mayBeAsyncChunkArguments(args) {
156
+ return args.length >= 2 && isChunkIds(args[0]);
157
+ }
158
+
159
+ function getModulesLocations(node) {
160
+ if (node.type === 'ObjectExpression') {
161
+ // Modules hash
162
+ var modulesNodes = node.properties;
167
163
 
168
164
  return _.transform(modulesNodes, function (result, moduleNode) {
169
165
  var moduleId = moduleNode.key.name || moduleNode.key.value;
@@ -172,35 +168,31 @@ function getModulesLocationFromFunctionArgument(arg) {
172
168
  }, {});
173
169
  }
174
170
 
175
- if (arg.type === 'ArrayExpression') {
176
- var _modulesNodes = arg.elements;
171
+ var isOptimizedArray = node.type === 'CallExpression';
172
+
173
+ if (node.type === 'ArrayExpression' || isOptimizedArray) {
174
+ // Modules array or optimized array
175
+ var minId = isOptimizedArray ?
176
+ // Get the [minId] value from the Array() call first argument literal value
177
+ node.callee.object.arguments[0].value :
178
+ // `0` for simple array
179
+ 0;
180
+ var _modulesNodes = isOptimizedArray ?
181
+ // The modules reside in the `concat()` function call arguments
182
+ node.arguments[0].elements : node.elements;
177
183
 
178
184
  return _.transform(_modulesNodes, function (result, moduleNode, i) {
179
185
  if (!moduleNode) return;
180
-
181
- result[i] = getModuleLocation(moduleNode);
186
+ result[i + minId] = getModuleLocation(moduleNode);
182
187
  }, {});
183
188
  }
184
189
 
185
190
  return {};
186
191
  }
187
192
 
188
- function getModulesLocationFromArrayConcat(arg) {
189
- // arg(CallExpression) =
190
- // Array([minId]).concat([<minId module>, <minId+1 module>, ...])
191
- //
192
- // Get the [minId] value from the Array() call first argument literal value
193
- var minId = arg.callee.object.arguments[0].value;
194
- // The modules reside in the `concat()` function call arguments
195
- var modulesNodes = arg.arguments[0].elements;
196
-
197
- return _.transform(modulesNodes, function (result, moduleNode, i) {
198
- if (!moduleNode) return;
199
-
200
- result[i + minId] = getModuleLocation(moduleNode);
201
- }, {});
202
- }
203
-
204
193
  function getModuleLocation(node) {
205
- return _.pick(node, 'start', 'end');
194
+ return {
195
+ start: node.start,
196
+ end: node.end
197
+ };
206
198
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webpack-bundle-analyzer",
3
- "version": "2.13.0",
3
+ "version": "2.13.1",
4
4
  "description": "Webpack plugin and CLI utility that represents bundle content as convenient interactive zoomable treemap",
5
5
  "author": "Yury Grunin <grunin.ya@ya.ru>",
6
6
  "license": "MIT",
@@ -74,6 +74,7 @@ class BundleAnalyzerPlugin {
74
74
 
75
75
  try {
76
76
  await bfj.write(statsFilepath, stats, {
77
+ space: 2,
77
78
  promises: 'ignore',
78
79
  buffers: 'ignore',
79
80
  maps: 'ignore',
package/src/parseUtils.js CHANGED
@@ -26,59 +26,40 @@ function parseBundle(bundlePath) {
26
26
  walkState,
27
27
  {
28
28
  CallExpression(node, state, c) {
29
- if (state.sizes) return;
29
+ if (state.locations) return;
30
30
 
31
31
  const args = node.arguments;
32
32
 
33
- // Additional bundle without webpack loader.
34
- // Modules are stored in second argument, after chunk ids:
35
- // webpackJsonp([<chunks>], <modules>, ...)
36
- // As function name may be changed with `output.jsonpFunction` option we can't rely on it's default name.
37
- if (
38
- node.callee.type === 'Identifier' &&
39
- args.length >= 2 &&
40
- isArgumentContainsChunkIds(args[0]) &&
41
- isArgumentContainsModulesList(args[1])
42
- ) {
43
- state.locations = getModulesLocationFromFunctionArgument(args[1]);
44
- return;
45
- }
46
-
47
- // Additional bundle without webpack loader, with module IDs optimized.
48
- // Modules are stored in second arguments Array(n).concat() call
49
- // webpackJsonp([<chunks>], Array([minimum ID]).concat([<module>, <module>, ...]))
50
- // As function name may be changed with `output.jsonpFunction` option we can't rely on it's default name.
51
- if (
52
- node.callee.type === 'Identifier' &&
53
- (args.length === 2 || args.length === 3) &&
54
- isArgumentContainsChunkIds(args[0]) &&
55
- isArgumentArrayConcatContainingChunks(args[1])
56
- ) {
57
- state.locations = getModulesLocationFromArrayConcat(args[1]);
58
- return;
59
- }
60
-
61
- // Main bundle with webpack loader
33
+ // Main chunk with webpack loader.
62
34
  // Modules are stored in first argument:
63
35
  // (function (...) {...})(<modules>)
64
36
  if (
65
37
  node.callee.type === 'FunctionExpression' &&
66
38
  !node.callee.id &&
67
39
  args.length === 1 &&
68
- isArgumentContainsModulesList(args[0])
40
+ isSimpleModulesList(args[0])
69
41
  ) {
70
- state.locations = getModulesLocationFromFunctionArgument(args[0]);
42
+ state.locations = getModulesLocations(args[0]);
71
43
  return;
72
44
  }
73
45
 
74
- // Additional bundles with webpack 4 are loaded with:
75
- // (window.webpackJsonp=window.webpackJsonp||[]).push([[chunkId], [<module>, <module>], [[optional_entries]]]);
46
+ // Async Webpack < v4 chunk without webpack loader.
47
+ // webpackJsonp([<chunks>], <modules>, ...)
48
+ // As function name may be changed with `output.jsonpFunction` option we can't rely on it's default name.
76
49
  if (
77
- isAsyncChunkPushExpression(node) &&
78
- args.length === 1 &&
79
- isArgumentContainingChunkIdsAndModulesList(args[0])
50
+ node.callee.type === 'Identifier' &&
51
+ mayBeAsyncChunkArguments(args) &&
52
+ isModulesList(args[1])
80
53
  ) {
81
- state.locations = getModulesLocationFromFunctionArgument(args[0].elements[1]);
54
+ state.locations = getModulesLocations(args[1]);
55
+ return;
56
+ }
57
+
58
+ // Async Webpack v4 chunk without webpack loader.
59
+ // (window.webpackJsonp=window.webpackJsonp||[]).push([[<chunks>], <modules>, ...]);
60
+ // As function name may be changed with `output.jsonpFunction` option we can't rely on it's default name.
61
+ if (isAsyncChunkPushExpression(node)) {
62
+ state.locations = getModulesLocations(args[0].elements[1]);
82
63
  return;
83
64
  }
84
65
 
@@ -105,80 +86,62 @@ function parseBundle(bundlePath) {
105
86
  };
106
87
  }
107
88
 
108
- function isArgumentContainsChunkIds(arg) {
109
- // Array of numeric or string ids. Chunk IDs are strings when NamedChunksPlugin is used
110
- return (arg.type === 'ArrayExpression' && _.every(arg.elements, isModuleId));
89
+ function isModulesList(node) {
90
+ return (
91
+ isSimpleModulesList(node) ||
92
+ // Modules are contained in expression `Array([minimum ID]).concat([<module>, <module>, ...])`
93
+ isOptimizedModulesArray(node)
94
+ );
111
95
  }
112
96
 
113
- function isArgumentContainsModulesList(arg) {
114
- if (arg.type === 'ObjectExpression') {
115
- return _(arg.properties)
97
+ function isSimpleModulesList(node) {
98
+ return (
99
+ // Modules are contained in hash. Keys are module ids.
100
+ isModulesHash(node) ||
101
+ // Modules are contained in array. Indexes are module ids.
102
+ isModulesArray(node)
103
+ );
104
+ }
105
+
106
+ function isModulesHash(node) {
107
+ return (
108
+ node.type === 'ObjectExpression' &&
109
+ _(node.properties)
116
110
  .map('value')
117
- .every(isModuleWrapper);
118
- }
111
+ .every(isModuleWrapper)
112
+ );
113
+ }
119
114
 
120
- if (arg.type === 'ArrayExpression') {
121
- // Modules are contained in array.
122
- // Array indexes are module ids
123
- return _.every(arg.elements, elem =>
115
+ function isModulesArray(node) {
116
+ return (
117
+ node.type === 'ArrayExpression' &&
118
+ _.every(node.elements, elem =>
124
119
  // Some of array items may be skipped because there is no module with such id
125
120
  !elem ||
126
121
  isModuleWrapper(elem)
127
- );
128
- }
129
-
130
- return false;
131
- }
132
-
133
- function isArgumentContainingChunkIdsAndModulesList(arg) {
134
- if (
135
- arg.type === 'ArrayExpression' &&
136
- arg.elements.length >= 2 &&
137
- isArgumentContainsChunkIds(arg.elements[0]) &&
138
- isArgumentContainsModulesList(arg.elements[1])
139
- ) {
140
- return true;
141
- }
142
- return false;
122
+ )
123
+ );
143
124
  }
144
125
 
145
- function isArgumentArrayConcatContainingChunks(arg) {
146
- if (
147
- arg.type === 'CallExpression' &&
148
- arg.callee.type === 'MemberExpression' &&
126
+ function isOptimizedModulesArray(node) {
127
+ // Checking whether modules are contained in `Array(<minimum ID>).concat(...modules)` array:
128
+ // https://github.com/webpack/webpack/blob/v1.14.0/lib/Template.js#L91
129
+ // The `<minimum ID>` + array indexes are module ids
130
+ return (
131
+ node.type === 'CallExpression' &&
132
+ node.callee.type === 'MemberExpression' &&
149
133
  // Make sure the object called is `Array(<some number>)`
150
- arg.callee.object.type === 'CallExpression' &&
151
- arg.callee.object.callee.type === 'Identifier' &&
152
- arg.callee.object.callee.name === 'Array' &&
153
- arg.callee.object.arguments.length === 1 &&
154
- isNumericId(arg.callee.object.arguments[0]) &&
134
+ node.callee.object.type === 'CallExpression' &&
135
+ node.callee.object.callee.type === 'Identifier' &&
136
+ node.callee.object.callee.name === 'Array' &&
137
+ node.callee.object.arguments.length === 1 &&
138
+ isNumericId(node.callee.object.arguments[0]) &&
155
139
  // Make sure the property X called for `Array(<some number>).X` is `concat`
156
- arg.callee.property.type === 'Identifier' &&
157
- arg.callee.property.name === 'concat' &&
140
+ node.callee.property.type === 'Identifier' &&
141
+ node.callee.property.name === 'concat' &&
158
142
  // Make sure exactly one array is passed in to `concat`
159
- arg.arguments.length === 1 &&
160
- arg.arguments[0].type === 'ArrayExpression'
161
- ) {
162
- // Modules are contained in `Array(<minimum ID>).concat(` array:
163
- // https://github.com/webpack/webpack/blob/v1.14.0/lib/Template.js#L91
164
- // The `<minimum ID>` + array indexes are module ids
165
- return true;
166
- }
167
-
168
- return false;
169
- }
170
-
171
- function isAsyncChunkPushExpression(node) {
172
- const { callee } = node;
173
- return (
174
- callee.type === 'MemberExpression' &&
175
- callee.property.name === 'push' &&
176
- callee.object.type === 'AssignmentExpression' &&
177
- (
178
- callee.object.left.object.name === 'window' ||
179
- // Webpack 4 uses `this` instead of `window`
180
- callee.object.left.object.type === 'ThisExpression'
181
- )
143
+ node.arguments.length === 1 &&
144
+ isModulesArray(node.arguments[0])
182
145
  );
183
146
  }
184
147
 
@@ -201,9 +164,48 @@ function isNumericId(node) {
201
164
  return (node.type === 'Literal' && Number.isInteger(node.value) && node.value >= 0);
202
165
  }
203
166
 
204
- function getModulesLocationFromFunctionArgument(arg) {
205
- if (arg.type === 'ObjectExpression') {
206
- const modulesNodes = arg.properties;
167
+ function isChunkIds(node) {
168
+ // Array of numeric or string ids. Chunk IDs are strings when NamedChunksPlugin is used
169
+ return (
170
+ node.type === 'ArrayExpression' &&
171
+ _.every(node.elements, isModuleId)
172
+ );
173
+ }
174
+
175
+ function isAsyncChunkPushExpression(node) {
176
+ const {
177
+ callee,
178
+ arguments: args
179
+ } = node;
180
+
181
+ return (
182
+ callee.type === 'MemberExpression' &&
183
+ callee.property.name === 'push' &&
184
+ callee.object.type === 'AssignmentExpression' &&
185
+ callee.object.left.object &&
186
+ (
187
+ callee.object.left.object.name === 'window' ||
188
+ // Webpack 4 uses `this` instead of `window`
189
+ callee.object.left.object.type === 'ThisExpression'
190
+ ) &&
191
+ args.length === 1 &&
192
+ args[0].type === 'ArrayExpression' &&
193
+ mayBeAsyncChunkArguments(args[0].elements) &&
194
+ isModulesList(args[0].elements[1])
195
+ );
196
+ }
197
+
198
+ function mayBeAsyncChunkArguments(args) {
199
+ return (
200
+ args.length >= 2 &&
201
+ isChunkIds(args[0])
202
+ );
203
+ }
204
+
205
+ function getModulesLocations(node) {
206
+ if (node.type === 'ObjectExpression') {
207
+ // Modules hash
208
+ const modulesNodes = node.properties;
207
209
 
208
210
  return _.transform(modulesNodes, (result, moduleNode) => {
209
211
  const moduleId = moduleNode.key.name || moduleNode.key.value;
@@ -212,35 +214,32 @@ function getModulesLocationFromFunctionArgument(arg) {
212
214
  }, {});
213
215
  }
214
216
 
215
- if (arg.type === 'ArrayExpression') {
216
- const modulesNodes = arg.elements;
217
+ const isOptimizedArray = (node.type === 'CallExpression');
218
+
219
+ if (node.type === 'ArrayExpression' || isOptimizedArray) {
220
+ // Modules array or optimized array
221
+ const minId = isOptimizedArray ?
222
+ // Get the [minId] value from the Array() call first argument literal value
223
+ node.callee.object.arguments[0].value :
224
+ // `0` for simple array
225
+ 0;
226
+ const modulesNodes = isOptimizedArray ?
227
+ // The modules reside in the `concat()` function call arguments
228
+ node.arguments[0].elements :
229
+ node.elements;
217
230
 
218
231
  return _.transform(modulesNodes, (result, moduleNode, i) => {
219
232
  if (!moduleNode) return;
220
-
221
- result[i] = getModuleLocation(moduleNode);
233
+ result[i + minId] = getModuleLocation(moduleNode);
222
234
  }, {});
223
235
  }
224
236
 
225
237
  return {};
226
238
  }
227
239
 
228
- function getModulesLocationFromArrayConcat(arg) {
229
- // arg(CallExpression) =
230
- // Array([minId]).concat([<minId module>, <minId+1 module>, ...])
231
- //
232
- // Get the [minId] value from the Array() call first argument literal value
233
- const minId = arg.callee.object.arguments[0].value;
234
- // The modules reside in the `concat()` function call arguments
235
- const modulesNodes = arg.arguments[0].elements;
236
-
237
- return _.transform(modulesNodes, (result, moduleNode, i) => {
238
- if (!moduleNode) return;
239
-
240
- result[i + minId] = getModuleLocation(moduleNode);
241
- }, {});
242
- }
243
-
244
240
  function getModuleLocation(node) {
245
- return _.pick(node, 'start', 'end');
241
+ return {
242
+ start: node.start,
243
+ end: node.end
244
+ };
246
245
  }