webpack 4.28.4 → 4.29.0

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.
@@ -1051,6 +1051,10 @@ export interface OutputOptions {
1051
1051
  * Specifies the name of each output file on disk. You must **not** specify an absolute path here! The `output.path` option determines the location on disk the files are written to, filename is used solely for naming the individual files.
1052
1052
  */
1053
1053
  filename?: string | Function;
1054
+ /**
1055
+ * Use the future version of asset emitting logic, which is allows freeing memory of assets after emitting. It could break plugins which assume that assets are still readable after emitting. Will be the new default in the next major version.
1056
+ */
1057
+ futureEmitAssets?: boolean;
1054
1058
  /**
1055
1059
  * An expression which is used to address the global object/scope in runtime code
1056
1060
  */
@@ -491,6 +491,8 @@ class Compilation extends Tapable {
491
491
  this._buildingModules = new Map();
492
492
  /** @private @type {Map<Module, Callback[]>} */
493
493
  this._rebuildingModules = new Map();
494
+ /** @type {Set<string>} */
495
+ this.emittedAssets = new Set();
494
496
  }
495
497
 
496
498
  getStats() {
package/lib/Compiler.js CHANGED
@@ -7,6 +7,7 @@
7
7
  const parseJson = require("json-parse-better-errors");
8
8
  const asyncLib = require("neo-async");
9
9
  const path = require("path");
10
+ const { Source } = require("webpack-sources");
10
11
  const util = require("util");
11
12
  const {
12
13
  Tapable,
@@ -188,6 +189,11 @@ class Compiler extends Tapable {
188
189
 
189
190
  /** @type {boolean} */
190
191
  this.watchMode = false;
192
+
193
+ /** @private @type {WeakMap<Source, { sizeOnlySource: SizeOnlySource, writtenTo: Map<string, number> }>} */
194
+ this._assetEmittingSourceCache = new WeakMap();
195
+ /** @private @type {Map<string, number>} */
196
+ this._assetEmittingWrittenFiles = new Map();
191
197
  }
192
198
 
193
199
  watch(watchOptions, handler) {
@@ -312,8 +318,9 @@ class Compiler extends Tapable {
312
318
  const emitFiles = err => {
313
319
  if (err) return callback(err);
314
320
 
315
- asyncLib.forEach(
321
+ asyncLib.forEachLimit(
316
322
  compilation.assets,
323
+ 15,
317
324
  (source, file, callback) => {
318
325
  let targetFile = file;
319
326
  const queryStringIdx = targetFile.indexOf("?");
@@ -327,19 +334,86 @@ class Compiler extends Tapable {
327
334
  outputPath,
328
335
  targetFile
329
336
  );
330
- if (source.existsAt === targetPath) {
331
- source.emitted = false;
332
- return callback();
333
- }
334
- let content = source.source();
335
-
336
- if (!Buffer.isBuffer(content)) {
337
- content = Buffer.from(content, "utf8");
337
+ // TODO webpack 5 remove futureEmitAssets option and make it on by default
338
+ if (this.options.output.futureEmitAssets) {
339
+ // check if the target file has already been written by this Compiler
340
+ const targetFileGeneration = this._assetEmittingWrittenFiles.get(
341
+ targetPath
342
+ );
343
+
344
+ // create an cache entry for this Source if not already existing
345
+ let cacheEntry = this._assetEmittingSourceCache.get(source);
346
+ if (cacheEntry === undefined) {
347
+ cacheEntry = {
348
+ sizeOnlySource: undefined,
349
+ writtenTo: new Map()
350
+ };
351
+ this._assetEmittingSourceCache.set(source, cacheEntry);
352
+ }
353
+
354
+ // if the target file has already been written
355
+ if (targetFileGeneration !== undefined) {
356
+ // check if the Source has been written to this target file
357
+ const writtenGeneration = cacheEntry.writtenTo.get(targetPath);
358
+ if (writtenGeneration === targetFileGeneration) {
359
+ // if yes, we skip writing the file
360
+ // as it's already there
361
+ // (we assume one doesn't remove files while the Compiler is running)
362
+ return callback();
363
+ }
364
+ }
365
+
366
+ // get the binary (Buffer) content from the Source
367
+ /** @type {Buffer} */
368
+ let content;
369
+ if (typeof source.buffer === "function") {
370
+ content = source.buffer();
371
+ } else {
372
+ const bufferOrString = source.source();
373
+ if (Buffer.isBuffer(bufferOrString)) {
374
+ content = bufferOrString;
375
+ } else {
376
+ content = Buffer.from(bufferOrString, "utf8");
377
+ }
378
+ }
379
+
380
+ // Create a replacement resource which only allows to ask for size
381
+ // This allows to GC all memory allocated by the Source
382
+ // (expect when the Source is stored in any other cache)
383
+ cacheEntry.sizeOnlySource = new SizeOnlySource(content.length);
384
+ compilation.assets[file] = cacheEntry.sizeOnlySource;
385
+
386
+ // Write the file to output file system
387
+ this.outputFileSystem.writeFile(targetPath, content, err => {
388
+ if (err) return callback(err);
389
+
390
+ // information marker that the asset has been emitted
391
+ compilation.emittedAssets.add(file);
392
+
393
+ // cache the information that the Source has been written to that location
394
+ const newGeneration =
395
+ targetFileGeneration === undefined
396
+ ? 1
397
+ : targetFileGeneration + 1;
398
+ cacheEntry.writtenTo.set(targetPath, newGeneration);
399
+ this._assetEmittingWrittenFiles.set(targetPath, newGeneration);
400
+ callback();
401
+ });
402
+ } else {
403
+ if (source.existsAt === targetPath) {
404
+ source.emitted = false;
405
+ return callback();
406
+ }
407
+ let content = source.source();
408
+
409
+ if (!Buffer.isBuffer(content)) {
410
+ content = Buffer.from(content, "utf8");
411
+ }
412
+
413
+ source.existsAt = targetPath;
414
+ source.emitted = true;
415
+ this.outputFileSystem.writeFile(targetPath, content, callback);
338
416
  }
339
-
340
- source.existsAt = targetPath;
341
- source.emitted = true;
342
- this.outputFileSystem.writeFile(targetPath, content, callback);
343
417
  };
344
418
 
345
419
  if (targetFile.match(/\/|\\/)) {
@@ -562,3 +636,48 @@ class Compiler extends Tapable {
562
636
  }
563
637
 
564
638
  module.exports = Compiler;
639
+
640
+ class SizeOnlySource extends Source {
641
+ constructor(size) {
642
+ super();
643
+ this._size = size;
644
+ }
645
+
646
+ _error() {
647
+ return new Error(
648
+ "Content and Map of this Source is no longer available (only size() is supported)"
649
+ );
650
+ }
651
+
652
+ size() {
653
+ return this._size;
654
+ }
655
+
656
+ /**
657
+ * @param {any} options options
658
+ * @returns {string} the source
659
+ */
660
+ source(options) {
661
+ throw this._error();
662
+ }
663
+
664
+ node() {
665
+ throw this._error();
666
+ }
667
+
668
+ listMap() {
669
+ throw this._error();
670
+ }
671
+
672
+ map() {
673
+ throw this._error();
674
+ }
675
+
676
+ listNode() {
677
+ throw this._error();
678
+ }
679
+
680
+ updateHash() {
681
+ throw this._error();
682
+ }
683
+ }
package/lib/Parser.js CHANGED
@@ -6,7 +6,8 @@
6
6
 
7
7
  // Syntax: https://developer.mozilla.org/en/SpiderMonkey/Parser_API
8
8
 
9
- const acorn = require("acorn-dynamic-import").default;
9
+ const acorn = require("acorn");
10
+ const acornDynamicImport = require("acorn-dynamic-import").default;
10
11
  const { Tapable, SyncBailHook, HookMap } = require("tapable");
11
12
  const util = require("util");
12
13
  const vm = require("vm");
@@ -14,6 +15,8 @@ const BasicEvaluatedExpression = require("./BasicEvaluatedExpression");
14
15
  const StackedSetMap = require("./util/StackedSetMap");
15
16
  const TrackingSet = require("./util/TrackingSet");
16
17
 
18
+ const acornParser = acorn.Parser.extend(acornDynamicImport);
19
+
17
20
  const joinRanges = (startRange, endRange) => {
18
21
  if (!endRange) return startRange;
19
22
  if (!startRange) return endRange;
@@ -25,10 +28,7 @@ const defaultParserOptions = {
25
28
  locations: true,
26
29
  ecmaVersion: 2019,
27
30
  sourceType: "module",
28
- onComment: null,
29
- plugins: {
30
- dynamicImport: true
31
- }
31
+ onComment: null
32
32
  };
33
33
 
34
34
  // regexp to match at lease one "magic comment"
@@ -2133,9 +2133,13 @@ class Parser extends Tapable {
2133
2133
  sourceType: this.sourceType,
2134
2134
  locations: false
2135
2135
  });
2136
+ // TODO(https://github.com/acornjs/acorn/issues/741)
2137
+ // @ts-ignore
2136
2138
  if (ast.body.length !== 1 || ast.body[0].type !== "ExpressionStatement") {
2137
2139
  throw new Error("evaluate: Source is not a expression");
2138
2140
  }
2141
+ // TODO(https://github.com/acornjs/acorn/issues/741)
2142
+ // @ts-ignore
2139
2143
  return this.evaluateExpression(ast.body[0].expression);
2140
2144
  }
2141
2145
 
@@ -2226,7 +2230,7 @@ class Parser extends Tapable {
2226
2230
  let error;
2227
2231
  let threw = false;
2228
2232
  try {
2229
- ast = acorn.parse(code, parserOptions);
2233
+ ast = acornParser.parse(code, parserOptions);
2230
2234
  } catch (e) {
2231
2235
  error = e;
2232
2236
  threw = true;
@@ -2238,7 +2242,7 @@ class Parser extends Tapable {
2238
2242
  parserOptions.onComment.length = 0;
2239
2243
  }
2240
2244
  try {
2241
- ast = acorn.parse(code, parserOptions);
2245
+ ast = acornParser.parse(code, parserOptions);
2242
2246
  threw = false;
2243
2247
  } catch (e) {
2244
2248
  threw = true;
package/lib/Stats.js CHANGED
@@ -400,7 +400,10 @@ class Stats {
400
400
  size: compilation.assets[asset].size(),
401
401
  chunks: [],
402
402
  chunkNames: [],
403
- emitted: compilation.assets[asset].emitted
403
+ // TODO webpack 5: remove .emitted
404
+ emitted:
405
+ compilation.assets[asset].emitted ||
406
+ compilation.emittedAssets.has(asset)
404
407
  };
405
408
 
406
409
  if (showPerformance) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webpack",
3
- "version": "4.28.4",
3
+ "version": "4.29.0",
4
4
  "author": "Tobias Koppers @sokra",
5
5
  "description": "Packs CommonJs/AMD modules for the browser. Allows to split your codebase into multiple bundles, which can be loaded on demand. Support loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.",
6
6
  "license": "MIT",
@@ -9,8 +9,8 @@
9
9
  "@webassemblyjs/helper-module-context": "1.7.11",
10
10
  "@webassemblyjs/wasm-edit": "1.7.11",
11
11
  "@webassemblyjs/wasm-parser": "1.7.11",
12
- "acorn": "^5.6.2",
13
- "acorn-dynamic-import": "^3.0.0",
12
+ "acorn": "^6.0.5",
13
+ "acorn-dynamic-import": "^4.0.0",
14
14
  "ajv": "^6.1.0",
15
15
  "ajv-keywords": "^3.1.0",
16
16
  "chrome-trace-event": "^1.0.0",
@@ -867,6 +867,10 @@
867
867
  }
868
868
  ]
869
869
  },
870
+ "futureEmitAssets": {
871
+ "description": "Use the future version of asset emitting logic, which is allows freeing memory of assets after emitting. It could break plugins which assume that assets are still readable after emitting. Will be the new default in the next major version.",
872
+ "type": "boolean"
873
+ },
870
874
  "globalObject": {
871
875
  "description": "An expression which is used to address the global object/scope in runtime code",
872
876
  "type": "string",