webpack 4.8.2 → 4.9.2

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.
Files changed (57) hide show
  1. package/README.md +95 -52
  2. package/bin/webpack.js +128 -43
  3. package/lib/AmdMainTemplatePlugin.js +10 -0
  4. package/lib/AsyncDependencyToInitialChunkError.js +12 -2
  5. package/lib/BannerPlugin.js +115 -101
  6. package/lib/CaseSensitiveModulesWarning.js +20 -2
  7. package/lib/Chunk.js +1 -0
  8. package/lib/ChunkGroup.js +465 -465
  9. package/lib/ChunkRenderError.js +8 -0
  10. package/lib/ChunkTemplate.js +71 -71
  11. package/lib/Compilation.js +1 -1
  12. package/lib/Compiler.js +2 -1
  13. package/lib/ContextModule.js +8 -8
  14. package/lib/DllPlugin.js +3 -1
  15. package/lib/DllReferencePlugin.js +2 -1
  16. package/lib/Entrypoint.js +54 -54
  17. package/lib/EvalSourceMapDevToolModuleTemplatePlugin.js +115 -115
  18. package/lib/ExportPropertyMainTemplatePlugin.js +13 -0
  19. package/lib/Generator.js +52 -52
  20. package/lib/HotModuleReplacement.runtime.js +633 -633
  21. package/lib/JsonParser.js +2 -1
  22. package/lib/LibManifestPlugin.js +9 -0
  23. package/lib/LibraryTemplatePlugin.js +66 -33
  24. package/lib/MainTemplate.js +468 -468
  25. package/lib/Module.js +3 -3
  26. package/lib/ModuleDependencyError.js +12 -2
  27. package/lib/NormalModuleFactory.js +5 -3
  28. package/lib/Parser.js +27 -23
  29. package/lib/ProgressPlugin.js +1 -1
  30. package/lib/RecordIdsPlugin.js +3 -1
  31. package/lib/RuntimeTemplate.js +1 -1
  32. package/lib/SetVarMainTemplatePlugin.js +12 -0
  33. package/lib/SourceMapDevToolPlugin.js +11 -13
  34. package/lib/Template.js +289 -290
  35. package/lib/UmdMainTemplatePlugin.js +67 -32
  36. package/lib/WebpackError.js +8 -2
  37. package/lib/compareLocations.js +20 -0
  38. package/lib/debug/ProfilingPlugin.js +416 -416
  39. package/lib/dependencies/ContextDependencyHelpers.js +142 -142
  40. package/lib/dependencies/WebpackMissingModule.js +2 -2
  41. package/lib/optimize/RemoveEmptyChunksPlugin.js +42 -40
  42. package/lib/optimize/RuntimeChunkPlugin.js +9 -5
  43. package/lib/optimize/SplitChunksPlugin.js +195 -124
  44. package/lib/util/Queue.js +46 -46
  45. package/lib/util/SetHelpers.js +48 -48
  46. package/lib/util/SortableSet.js +106 -106
  47. package/lib/util/StackedSetMap.js +128 -128
  48. package/lib/util/cachedMerge.js +13 -0
  49. package/lib/util/identifier.js +5 -0
  50. package/lib/util/objectToMap.js +16 -16
  51. package/lib/wasm/WebAssemblyGenerator.js +280 -280
  52. package/lib/wasm/WebAssemblyParser.js +79 -79
  53. package/lib/web/JsonpMainTemplatePlugin.js +2 -2
  54. package/package.json +21 -17
  55. package/schemas/WebpackOptions.json +12 -1
  56. package/schemas/plugins/BannerPlugin.json +96 -85
  57. package/schemas/plugins/DllPlugin.json +4 -0
@@ -1,416 +1,416 @@
1
- const fs = require("fs");
2
- const { Tracer } = require("chrome-trace-event");
3
- const validateOptions = require("schema-utils");
4
- const schema = require("../../schemas/plugins/debug/ProfilingPlugin.json");
5
- let inspector = undefined;
6
-
7
- try {
8
- inspector = require("inspector"); // eslint-disable-line node/no-missing-require
9
- } catch (e) {
10
- console.log("Unable to CPU profile in < node 8.0");
11
- }
12
-
13
- class Profiler {
14
- constructor(inspector) {
15
- this.session = undefined;
16
- this.inspector = inspector;
17
- }
18
-
19
- hasSession() {
20
- return this.session !== undefined;
21
- }
22
-
23
- startProfiling() {
24
- if (this.inspector === undefined) {
25
- return Promise.resolve();
26
- }
27
-
28
- try {
29
- this.session = new inspector.Session();
30
- this.session.connect();
31
- } catch (_) {
32
- this.session = undefined;
33
- return Promise.resolve();
34
- }
35
-
36
- return Promise.all([
37
- this.sendCommand("Profiler.setSamplingInterval", {
38
- interval: 100
39
- }),
40
- this.sendCommand("Profiler.enable"),
41
- this.sendCommand("Profiler.start")
42
- ]);
43
- }
44
-
45
- sendCommand(method, params) {
46
- if (this.hasSession()) {
47
- return new Promise((res, rej) => {
48
- return this.session.post(method, params, (err, params) => {
49
- if (err !== null) {
50
- rej(err);
51
- } else {
52
- res(params);
53
- }
54
- });
55
- });
56
- } else {
57
- return Promise.resolve();
58
- }
59
- }
60
-
61
- destroy() {
62
- if (this.hasSession()) {
63
- this.session.disconnect();
64
- }
65
-
66
- return Promise.resolve();
67
- }
68
-
69
- stopProfiling() {
70
- return this.sendCommand("Profiler.stop");
71
- }
72
- }
73
-
74
- /**
75
- * @param {string} outputPath The location where to write the log.
76
- * @returns {{trace: ?, counter: number, profiler: Profiler, end: Function}} The trace object
77
- */
78
- function createTrace(outputPath) {
79
- const trace = new Tracer({
80
- noStream: true
81
- });
82
- const profiler = new Profiler(inspector);
83
- const fsStream = fs.createWriteStream(outputPath);
84
-
85
- let counter = 0;
86
-
87
- trace.pipe(fsStream);
88
- // These are critical events that need to be inserted so that tools like
89
- // chrome dev tools can load the profile.
90
- trace.instantEvent({
91
- name: "TracingStartedInPage",
92
- id: ++counter,
93
- cat: ["disabled-by-default-devtools.timeline"],
94
- args: {
95
- data: {
96
- sessionId: "-1",
97
- page: "0xfff",
98
- frames: [
99
- {
100
- frame: "0xfff",
101
- url: "webpack",
102
- name: ""
103
- }
104
- ]
105
- }
106
- }
107
- });
108
-
109
- trace.instantEvent({
110
- name: "TracingStartedInBrowser",
111
- id: ++counter,
112
- cat: ["disabled-by-default-devtools.timeline"],
113
- args: {
114
- data: {
115
- sessionId: "-1"
116
- }
117
- }
118
- });
119
-
120
- return {
121
- trace,
122
- counter,
123
- profiler,
124
- end: callback => fsStream.end(callback)
125
- };
126
- }
127
-
128
- const pluginName = "ProfilingPlugin";
129
-
130
- class ProfilingPlugin {
131
- constructor(opts) {
132
- validateOptions(schema, opts || {}, "Profiling plugin");
133
- opts = opts || {};
134
- this.outputPath = opts.outputPath || "events.json";
135
- }
136
-
137
- apply(compiler) {
138
- const tracer = createTrace(this.outputPath);
139
- tracer.profiler.startProfiling();
140
-
141
- // Compiler Hooks
142
- Object.keys(compiler.hooks).forEach(hookName => {
143
- compiler.hooks[hookName].intercept(
144
- makeInterceptorFor("Compiler", tracer)(hookName)
145
- );
146
- });
147
-
148
- Object.keys(compiler.resolverFactory.hooks).forEach(hookName => {
149
- compiler.resolverFactory.hooks[hookName].intercept(
150
- makeInterceptorFor("Resolver", tracer)(hookName)
151
- );
152
- });
153
-
154
- compiler.hooks.compilation.tap(
155
- pluginName,
156
- (compilation, { normalModuleFactory, contextModuleFactory }) => {
157
- interceptAllHooksFor(compilation, tracer, "Compilation");
158
- interceptAllHooksFor(
159
- normalModuleFactory,
160
- tracer,
161
- "Normal Module Factory"
162
- );
163
- interceptAllHooksFor(
164
- contextModuleFactory,
165
- tracer,
166
- "Context Module Factory"
167
- );
168
- interceptAllParserHooks(normalModuleFactory, tracer);
169
- interceptTemplateInstancesFrom(compilation, tracer);
170
- }
171
- );
172
-
173
- // We need to write out the CPU profile when we are all done.
174
- compiler.hooks.done.tapAsync(
175
- {
176
- name: pluginName,
177
- stage: Infinity
178
- },
179
- (stats, callback) => {
180
- tracer.profiler.stopProfiling().then(parsedResults => {
181
- if (parsedResults === undefined) {
182
- tracer.profiler.destroy();
183
- tracer.trace.flush();
184
- tracer.end(callback);
185
- return;
186
- }
187
-
188
- const cpuStartTime = parsedResults.profile.startTime;
189
- const cpuEndTime = parsedResults.profile.endTime;
190
-
191
- tracer.trace.completeEvent({
192
- name: "TaskQueueManager::ProcessTaskFromWorkQueue",
193
- id: ++tracer.counter,
194
- cat: ["toplevel"],
195
- ts: cpuStartTime,
196
- args: {
197
- src_file: "../../ipc/ipc_moji_bootstrap.cc",
198
- src_func: "Accept"
199
- }
200
- });
201
-
202
- tracer.trace.completeEvent({
203
- name: "EvaluateScript",
204
- id: ++tracer.counter,
205
- cat: ["devtools.timeline"],
206
- ts: cpuStartTime,
207
- dur: cpuEndTime - cpuStartTime,
208
- args: {
209
- data: {
210
- url: "webpack",
211
- lineNumber: 1,
212
- columnNumber: 1,
213
- frame: "0xFFF"
214
- }
215
- }
216
- });
217
-
218
- tracer.trace.instantEvent({
219
- name: "CpuProfile",
220
- id: ++tracer.counter,
221
- cat: ["disabled-by-default-devtools.timeline"],
222
- ts: cpuEndTime,
223
- args: {
224
- data: {
225
- cpuProfile: parsedResults.profile
226
- }
227
- }
228
- });
229
-
230
- tracer.profiler.destroy();
231
- tracer.trace.flush();
232
- tracer.end(callback);
233
- });
234
- }
235
- );
236
- }
237
- }
238
-
239
- const interceptTemplateInstancesFrom = (compilation, tracer) => {
240
- const {
241
- mainTemplate,
242
- chunkTemplate,
243
- hotUpdateChunkTemplate,
244
- moduleTemplates
245
- } = compilation;
246
-
247
- const { javascript, webassembly } = moduleTemplates;
248
-
249
- [
250
- {
251
- instance: mainTemplate,
252
- name: "MainTemplate"
253
- },
254
- {
255
- instance: chunkTemplate,
256
- name: "ChunkTemplate"
257
- },
258
- {
259
- instance: hotUpdateChunkTemplate,
260
- name: "HotUpdateChunkTemplate"
261
- },
262
- {
263
- instance: javascript,
264
- name: "JavaScriptModuleTemplate"
265
- },
266
- {
267
- instance: webassembly,
268
- name: "WebAssemblyModuleTemplate"
269
- }
270
- ].forEach(templateObject => {
271
- Object.keys(templateObject.instance.hooks).forEach(hookName => {
272
- templateObject.instance.hooks[hookName].intercept(
273
- makeInterceptorFor(templateObject.name, tracer)(hookName)
274
- );
275
- });
276
- });
277
- };
278
-
279
- const interceptAllHooksFor = (instance, tracer, logLabel) => {
280
- if (Reflect.has(instance, "hooks")) {
281
- Object.keys(instance.hooks).forEach(hookName => {
282
- instance.hooks[hookName].intercept(
283
- makeInterceptorFor(logLabel, tracer)(hookName)
284
- );
285
- });
286
- }
287
- };
288
-
289
- const interceptAllParserHooks = (moduleFactory, tracer) => {
290
- const moduleTypes = [
291
- "javascript/auto",
292
- "javascript/dynamic",
293
- "javascript/esm",
294
- "json",
295
- "webassembly/experimental"
296
- ];
297
-
298
- moduleTypes.forEach(moduleType => {
299
- moduleFactory.hooks.parser
300
- .for(moduleType)
301
- .tap("ProfilingPlugin", (parser, parserOpts) => {
302
- interceptAllHooksFor(parser, tracer, "Parser");
303
- });
304
- });
305
- };
306
-
307
- const makeInterceptorFor = (instance, tracer) => hookName => ({
308
- register: ({ name, type, context, fn }) => {
309
- const newFn = makeNewProfiledTapFn(hookName, tracer, {
310
- name,
311
- type,
312
- fn
313
- });
314
- return {
315
- name,
316
- type,
317
- context,
318
- fn: newFn
319
- };
320
- }
321
- });
322
-
323
- // TODO improve typing
324
- /** @typedef {(...args: TODO[]) => void | Promise<TODO>} PluginFunction */
325
-
326
- /**
327
- * @param {string} hookName Name of the hook to profile.
328
- * @param {Tracer} tracer Instance of tracer.
329
- * @param {object} options Options for the profiled fn.
330
- * @param {string} options.name Plugin name
331
- * @param {string} options.type Plugin type (sync | async | promise)
332
- * @param {PluginFunction} options.fn Plugin function
333
- * @returns {PluginFunction} Chainable hooked function.
334
- */
335
- const makeNewProfiledTapFn = (hookName, tracer, { name, type, fn }) => {
336
- const defaultCategory = ["blink.user_timing"];
337
-
338
- switch (type) {
339
- case "promise":
340
- return (...args) => {
341
- // eslint-disable-line
342
- const id = ++tracer.counter;
343
- tracer.trace.begin({
344
- name,
345
- id,
346
- cat: defaultCategory
347
- });
348
- const promise = /** @type {Promise<*>} */ (fn(...args));
349
- return promise.then(r => {
350
- tracer.trace.end({
351
- name,
352
- id,
353
- cat: defaultCategory
354
- });
355
- return r;
356
- });
357
- };
358
- case "async":
359
- return (...args) => {
360
- // eslint-disable-line
361
- const id = ++tracer.counter;
362
- tracer.trace.begin({
363
- name,
364
- id,
365
- cat: defaultCategory
366
- });
367
- fn(...args, (...r) => {
368
- const callback = args.pop();
369
- tracer.trace.end({
370
- name,
371
- id,
372
- cat: defaultCategory
373
- });
374
- callback(...r);
375
- });
376
- };
377
- case "sync":
378
- return (...args) => {
379
- // eslint-disable-line
380
- const id = ++tracer.counter;
381
- // Do not instrument ourself due to the CPU
382
- // profile needing to be the last event in the trace.
383
- if (name === pluginName) {
384
- return fn(...args);
385
- }
386
-
387
- tracer.trace.begin({
388
- name,
389
- id,
390
- cat: defaultCategory
391
- });
392
- let r;
393
- try {
394
- r = fn(...args);
395
- } catch (error) {
396
- tracer.trace.end({
397
- name,
398
- id,
399
- cat: defaultCategory
400
- });
401
- throw error;
402
- }
403
- tracer.trace.end({
404
- name,
405
- id,
406
- cat: defaultCategory
407
- });
408
- return r;
409
- };
410
- default:
411
- break;
412
- }
413
- };
414
-
415
- module.exports = ProfilingPlugin;
416
- module.exports.Profiler = Profiler;
1
+ const fs = require("fs");
2
+ const { Tracer } = require("chrome-trace-event");
3
+ const validateOptions = require("schema-utils");
4
+ const schema = require("../../schemas/plugins/debug/ProfilingPlugin.json");
5
+ let inspector = undefined;
6
+
7
+ try {
8
+ inspector = require("inspector"); // eslint-disable-line node/no-missing-require
9
+ } catch (e) {
10
+ console.log("Unable to CPU profile in < node 8.0");
11
+ }
12
+
13
+ class Profiler {
14
+ constructor(inspector) {
15
+ this.session = undefined;
16
+ this.inspector = inspector;
17
+ }
18
+
19
+ hasSession() {
20
+ return this.session !== undefined;
21
+ }
22
+
23
+ startProfiling() {
24
+ if (this.inspector === undefined) {
25
+ return Promise.resolve();
26
+ }
27
+
28
+ try {
29
+ this.session = new inspector.Session();
30
+ this.session.connect();
31
+ } catch (_) {
32
+ this.session = undefined;
33
+ return Promise.resolve();
34
+ }
35
+
36
+ return Promise.all([
37
+ this.sendCommand("Profiler.setSamplingInterval", {
38
+ interval: 100
39
+ }),
40
+ this.sendCommand("Profiler.enable"),
41
+ this.sendCommand("Profiler.start")
42
+ ]);
43
+ }
44
+
45
+ sendCommand(method, params) {
46
+ if (this.hasSession()) {
47
+ return new Promise((res, rej) => {
48
+ return this.session.post(method, params, (err, params) => {
49
+ if (err !== null) {
50
+ rej(err);
51
+ } else {
52
+ res(params);
53
+ }
54
+ });
55
+ });
56
+ } else {
57
+ return Promise.resolve();
58
+ }
59
+ }
60
+
61
+ destroy() {
62
+ if (this.hasSession()) {
63
+ this.session.disconnect();
64
+ }
65
+
66
+ return Promise.resolve();
67
+ }
68
+
69
+ stopProfiling() {
70
+ return this.sendCommand("Profiler.stop");
71
+ }
72
+ }
73
+
74
+ /**
75
+ * @param {string} outputPath The location where to write the log.
76
+ * @returns {{trace: ?, counter: number, profiler: Profiler, end: Function}} The trace object
77
+ */
78
+ function createTrace(outputPath) {
79
+ const trace = new Tracer({
80
+ noStream: true
81
+ });
82
+ const profiler = new Profiler(inspector);
83
+ const fsStream = fs.createWriteStream(outputPath);
84
+
85
+ let counter = 0;
86
+
87
+ trace.pipe(fsStream);
88
+ // These are critical events that need to be inserted so that tools like
89
+ // chrome dev tools can load the profile.
90
+ trace.instantEvent({
91
+ name: "TracingStartedInPage",
92
+ id: ++counter,
93
+ cat: ["disabled-by-default-devtools.timeline"],
94
+ args: {
95
+ data: {
96
+ sessionId: "-1",
97
+ page: "0xfff",
98
+ frames: [
99
+ {
100
+ frame: "0xfff",
101
+ url: "webpack",
102
+ name: ""
103
+ }
104
+ ]
105
+ }
106
+ }
107
+ });
108
+
109
+ trace.instantEvent({
110
+ name: "TracingStartedInBrowser",
111
+ id: ++counter,
112
+ cat: ["disabled-by-default-devtools.timeline"],
113
+ args: {
114
+ data: {
115
+ sessionId: "-1"
116
+ }
117
+ }
118
+ });
119
+
120
+ return {
121
+ trace,
122
+ counter,
123
+ profiler,
124
+ end: callback => fsStream.end(callback)
125
+ };
126
+ }
127
+
128
+ const pluginName = "ProfilingPlugin";
129
+
130
+ class ProfilingPlugin {
131
+ constructor(opts) {
132
+ validateOptions(schema, opts || {}, "Profiling plugin");
133
+ opts = opts || {};
134
+ this.outputPath = opts.outputPath || "events.json";
135
+ }
136
+
137
+ apply(compiler) {
138
+ const tracer = createTrace(this.outputPath);
139
+ tracer.profiler.startProfiling();
140
+
141
+ // Compiler Hooks
142
+ Object.keys(compiler.hooks).forEach(hookName => {
143
+ compiler.hooks[hookName].intercept(
144
+ makeInterceptorFor("Compiler", tracer)(hookName)
145
+ );
146
+ });
147
+
148
+ Object.keys(compiler.resolverFactory.hooks).forEach(hookName => {
149
+ compiler.resolverFactory.hooks[hookName].intercept(
150
+ makeInterceptorFor("Resolver", tracer)(hookName)
151
+ );
152
+ });
153
+
154
+ compiler.hooks.compilation.tap(
155
+ pluginName,
156
+ (compilation, { normalModuleFactory, contextModuleFactory }) => {
157
+ interceptAllHooksFor(compilation, tracer, "Compilation");
158
+ interceptAllHooksFor(
159
+ normalModuleFactory,
160
+ tracer,
161
+ "Normal Module Factory"
162
+ );
163
+ interceptAllHooksFor(
164
+ contextModuleFactory,
165
+ tracer,
166
+ "Context Module Factory"
167
+ );
168
+ interceptAllParserHooks(normalModuleFactory, tracer);
169
+ interceptTemplateInstancesFrom(compilation, tracer);
170
+ }
171
+ );
172
+
173
+ // We need to write out the CPU profile when we are all done.
174
+ compiler.hooks.done.tapAsync(
175
+ {
176
+ name: pluginName,
177
+ stage: Infinity
178
+ },
179
+ (stats, callback) => {
180
+ tracer.profiler.stopProfiling().then(parsedResults => {
181
+ if (parsedResults === undefined) {
182
+ tracer.profiler.destroy();
183
+ tracer.trace.flush();
184
+ tracer.end(callback);
185
+ return;
186
+ }
187
+
188
+ const cpuStartTime = parsedResults.profile.startTime;
189
+ const cpuEndTime = parsedResults.profile.endTime;
190
+
191
+ tracer.trace.completeEvent({
192
+ name: "TaskQueueManager::ProcessTaskFromWorkQueue",
193
+ id: ++tracer.counter,
194
+ cat: ["toplevel"],
195
+ ts: cpuStartTime,
196
+ args: {
197
+ src_file: "../../ipc/ipc_moji_bootstrap.cc",
198
+ src_func: "Accept"
199
+ }
200
+ });
201
+
202
+ tracer.trace.completeEvent({
203
+ name: "EvaluateScript",
204
+ id: ++tracer.counter,
205
+ cat: ["devtools.timeline"],
206
+ ts: cpuStartTime,
207
+ dur: cpuEndTime - cpuStartTime,
208
+ args: {
209
+ data: {
210
+ url: "webpack",
211
+ lineNumber: 1,
212
+ columnNumber: 1,
213
+ frame: "0xFFF"
214
+ }
215
+ }
216
+ });
217
+
218
+ tracer.trace.instantEvent({
219
+ name: "CpuProfile",
220
+ id: ++tracer.counter,
221
+ cat: ["disabled-by-default-devtools.timeline"],
222
+ ts: cpuEndTime,
223
+ args: {
224
+ data: {
225
+ cpuProfile: parsedResults.profile
226
+ }
227
+ }
228
+ });
229
+
230
+ tracer.profiler.destroy();
231
+ tracer.trace.flush();
232
+ tracer.end(callback);
233
+ });
234
+ }
235
+ );
236
+ }
237
+ }
238
+
239
+ const interceptTemplateInstancesFrom = (compilation, tracer) => {
240
+ const {
241
+ mainTemplate,
242
+ chunkTemplate,
243
+ hotUpdateChunkTemplate,
244
+ moduleTemplates
245
+ } = compilation;
246
+
247
+ const { javascript, webassembly } = moduleTemplates;
248
+
249
+ [
250
+ {
251
+ instance: mainTemplate,
252
+ name: "MainTemplate"
253
+ },
254
+ {
255
+ instance: chunkTemplate,
256
+ name: "ChunkTemplate"
257
+ },
258
+ {
259
+ instance: hotUpdateChunkTemplate,
260
+ name: "HotUpdateChunkTemplate"
261
+ },
262
+ {
263
+ instance: javascript,
264
+ name: "JavaScriptModuleTemplate"
265
+ },
266
+ {
267
+ instance: webassembly,
268
+ name: "WebAssemblyModuleTemplate"
269
+ }
270
+ ].forEach(templateObject => {
271
+ Object.keys(templateObject.instance.hooks).forEach(hookName => {
272
+ templateObject.instance.hooks[hookName].intercept(
273
+ makeInterceptorFor(templateObject.name, tracer)(hookName)
274
+ );
275
+ });
276
+ });
277
+ };
278
+
279
+ const interceptAllHooksFor = (instance, tracer, logLabel) => {
280
+ if (Reflect.has(instance, "hooks")) {
281
+ Object.keys(instance.hooks).forEach(hookName => {
282
+ instance.hooks[hookName].intercept(
283
+ makeInterceptorFor(logLabel, tracer)(hookName)
284
+ );
285
+ });
286
+ }
287
+ };
288
+
289
+ const interceptAllParserHooks = (moduleFactory, tracer) => {
290
+ const moduleTypes = [
291
+ "javascript/auto",
292
+ "javascript/dynamic",
293
+ "javascript/esm",
294
+ "json",
295
+ "webassembly/experimental"
296
+ ];
297
+
298
+ moduleTypes.forEach(moduleType => {
299
+ moduleFactory.hooks.parser
300
+ .for(moduleType)
301
+ .tap("ProfilingPlugin", (parser, parserOpts) => {
302
+ interceptAllHooksFor(parser, tracer, "Parser");
303
+ });
304
+ });
305
+ };
306
+
307
+ const makeInterceptorFor = (instance, tracer) => hookName => ({
308
+ register: ({ name, type, context, fn }) => {
309
+ const newFn = makeNewProfiledTapFn(hookName, tracer, {
310
+ name,
311
+ type,
312
+ fn
313
+ });
314
+ return {
315
+ name,
316
+ type,
317
+ context,
318
+ fn: newFn
319
+ };
320
+ }
321
+ });
322
+
323
+ // TODO improve typing
324
+ /** @typedef {(...args: TODO[]) => void | Promise<TODO>} PluginFunction */
325
+
326
+ /**
327
+ * @param {string} hookName Name of the hook to profile.
328
+ * @param {Tracer} tracer Instance of tracer.
329
+ * @param {object} options Options for the profiled fn.
330
+ * @param {string} options.name Plugin name
331
+ * @param {string} options.type Plugin type (sync | async | promise)
332
+ * @param {PluginFunction} options.fn Plugin function
333
+ * @returns {PluginFunction} Chainable hooked function.
334
+ */
335
+ const makeNewProfiledTapFn = (hookName, tracer, { name, type, fn }) => {
336
+ const defaultCategory = ["blink.user_timing"];
337
+
338
+ switch (type) {
339
+ case "promise":
340
+ return (...args) => {
341
+ // eslint-disable-line
342
+ const id = ++tracer.counter;
343
+ tracer.trace.begin({
344
+ name,
345
+ id,
346
+ cat: defaultCategory
347
+ });
348
+ const promise = /** @type {Promise<*>} */ (fn(...args));
349
+ return promise.then(r => {
350
+ tracer.trace.end({
351
+ name,
352
+ id,
353
+ cat: defaultCategory
354
+ });
355
+ return r;
356
+ });
357
+ };
358
+ case "async":
359
+ return (...args) => {
360
+ // eslint-disable-line
361
+ const id = ++tracer.counter;
362
+ tracer.trace.begin({
363
+ name,
364
+ id,
365
+ cat: defaultCategory
366
+ });
367
+ fn(...args, (...r) => {
368
+ const callback = args.pop();
369
+ tracer.trace.end({
370
+ name,
371
+ id,
372
+ cat: defaultCategory
373
+ });
374
+ callback(...r);
375
+ });
376
+ };
377
+ case "sync":
378
+ return (...args) => {
379
+ // eslint-disable-line
380
+ const id = ++tracer.counter;
381
+ // Do not instrument ourself due to the CPU
382
+ // profile needing to be the last event in the trace.
383
+ if (name === pluginName) {
384
+ return fn(...args);
385
+ }
386
+
387
+ tracer.trace.begin({
388
+ name,
389
+ id,
390
+ cat: defaultCategory
391
+ });
392
+ let r;
393
+ try {
394
+ r = fn(...args);
395
+ } catch (error) {
396
+ tracer.trace.end({
397
+ name,
398
+ id,
399
+ cat: defaultCategory
400
+ });
401
+ throw error;
402
+ }
403
+ tracer.trace.end({
404
+ name,
405
+ id,
406
+ cat: defaultCategory
407
+ });
408
+ return r;
409
+ };
410
+ default:
411
+ break;
412
+ }
413
+ };
414
+
415
+ module.exports = ProfilingPlugin;
416
+ module.exports.Profiler = Profiler;