webpack 5.81.0 → 5.82.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.

Potentially problematic release.


This version of webpack might be problematic. Click here for more details.

Files changed (40) hide show
  1. package/bin/webpack.js +13 -2
  2. package/lib/Compilation.js +2 -0
  3. package/lib/CssModule.js +39 -7
  4. package/lib/WebpackOptionsApply.js +33 -40
  5. package/lib/cache/MemoryWithGcCachePlugin.js +2 -0
  6. package/lib/config/defaults.js +1 -0
  7. package/lib/css/CssGenerator.js +4 -0
  8. package/lib/css/CssLoadingRuntimeModule.js +9 -2
  9. package/lib/css/CssModulesPlugin.js +136 -33
  10. package/lib/css/CssParser.js +144 -80
  11. package/lib/css/walkCssTokens.js +96 -20
  12. package/lib/debug/ProfilingPlugin.js +2 -0
  13. package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +1 -0
  14. package/lib/javascript/BasicEvaluatedExpression.js +108 -1
  15. package/lib/javascript/JavascriptParser.js +132 -11
  16. package/lib/json/JsonData.js +25 -0
  17. package/lib/json/JsonGenerator.js +15 -3
  18. package/lib/json/JsonModulesPlugin.js +1 -0
  19. package/lib/json/JsonParser.js +2 -1
  20. package/lib/library/ModuleLibraryPlugin.js +2 -1
  21. package/lib/runtime/GetChunkFilenameRuntimeModule.js +4 -0
  22. package/lib/runtime/GetTrustedTypesPolicyRuntimeModule.js +22 -3
  23. package/lib/schemes/DataUriPlugin.js +4 -0
  24. package/lib/schemes/HttpUriPlugin.js +38 -0
  25. package/lib/sharing/utils.js +293 -7
  26. package/lib/stats/DefaultStatsPrinterPlugin.js +25 -0
  27. package/lib/util/StackedCacheMap.js +6 -0
  28. package/lib/util/StringXor.js +51 -0
  29. package/lib/util/compileBooleanMatcher.js +31 -0
  30. package/lib/util/deprecation.js +8 -0
  31. package/lib/util/identifier.js +4 -0
  32. package/lib/util/numberHash.js +75 -21
  33. package/lib/util/propertyAccess.js +5 -0
  34. package/lib/wasm/EnableWasmLoadingPlugin.js +4 -0
  35. package/lib/wasm-async/AsyncWebAssemblyJavascriptGenerator.js +1 -0
  36. package/lib/wasm-async/AsyncWebAssemblyParser.js +1 -1
  37. package/package.json +1 -1
  38. package/schemas/WebpackOptions.check.js +1 -1
  39. package/schemas/WebpackOptions.json +25 -0
  40. package/types.d.ts +121 -26
@@ -13,6 +13,10 @@ const NormalModule = require("../NormalModule");
13
13
  // http://www.ietf.org/rfc/rfc2397.txt
14
14
  const URIRegEx = /^data:([^;,]+)?((?:;[^;,]+)*?)(?:;(base64))?,(.*)$/i;
15
15
 
16
+ /**
17
+ * @param {string} uri data URI
18
+ * @returns {Buffer|null} decoded data
19
+ */
16
20
  const decodeDataURI = uri => {
17
21
  const match = URIRegEx.exec(uri);
18
22
  if (!match) return null;
@@ -71,11 +71,19 @@ const validate = createSchemaValidation(
71
71
  }
72
72
  );
73
73
 
74
+ /**
75
+ * @param {string} str path
76
+ * @returns {string} safe path
77
+ */
74
78
  const toSafePath = str =>
75
79
  str
76
80
  .replace(/^[^a-zA-Z0-9]+|[^a-zA-Z0-9]+$/g, "")
77
81
  .replace(/[^a-zA-Z0-9._-]+/g, "_");
78
82
 
83
+ /**
84
+ * @param {Buffer} content content
85
+ * @returns {string} integrity
86
+ */
79
87
  const computeIntegrity = content => {
80
88
  const hash = createHash("sha512");
81
89
  hash.update(content);
@@ -83,6 +91,11 @@ const computeIntegrity = content => {
83
91
  return integrity;
84
92
  };
85
93
 
94
+ /**
95
+ * @param {Buffer} content content
96
+ * @param {string} integrity integrity
97
+ * @returns {boolean} true, if integrity matches
98
+ */
86
99
  const verifyIntegrity = (content, integrity) => {
87
100
  if (integrity === "ignore") return true;
88
101
  return computeIntegrity(content) === integrity;
@@ -110,6 +123,11 @@ const parseKeyValuePairs = str => {
110
123
  return result;
111
124
  };
112
125
 
126
+ /**
127
+ * @param {string | undefined} cacheControl Cache-Control header
128
+ * @param {number} requestTime timestamp of request
129
+ * @returns {{storeCache: boolean, storeLock: boolean, validUntil: number}} Logic for storing in cache and lockfile cache
130
+ */
113
131
  const parseCacheControl = (cacheControl, requestTime) => {
114
132
  // When false resource is not stored in cache
115
133
  let storeCache = true;
@@ -147,6 +165,10 @@ const areLockfileEntriesEqual = (a, b) => {
147
165
  );
148
166
  };
149
167
 
168
+ /**
169
+ * @param {LockfileEntry} entry lockfile entry
170
+ * @returns {`resolved: ${string}, integrity: ${string}, contentType: ${*}`} stringified entry
171
+ */
150
172
  const entryToString = entry => {
151
173
  return `resolved: ${entry.resolved}, integrity: ${entry.integrity}, contentType: ${entry.contentType}`;
152
174
  };
@@ -158,6 +180,10 @@ class Lockfile {
158
180
  this.entries = new Map();
159
181
  }
160
182
 
183
+ /**
184
+ * @param {string} content content of the lockfile
185
+ * @returns {Lockfile} lockfile
186
+ */
161
187
  static parse(content) {
162
188
  // TODO handle merge conflicts
163
189
  const data = JSON.parse(content);
@@ -180,6 +206,9 @@ class Lockfile {
180
206
  return lockfile;
181
207
  }
182
208
 
209
+ /**
210
+ * @returns {string} stringified lockfile
211
+ */
183
212
  toString() {
184
213
  let str = "{\n";
185
214
  const entries = Array.from(this.entries).sort(([a], [b]) =>
@@ -342,6 +371,7 @@ class HttpUriPlugin {
342
371
  const fs = compilation.inputFileSystem;
343
372
  const cache = compilation.getCache("webpack.HttpUriPlugin");
344
373
  const logger = compilation.getLogger("webpack.HttpUriPlugin");
374
+ /** @type {string} */
345
375
  const lockfileLocation =
346
376
  this._lockfileLocation ||
347
377
  join(
@@ -351,6 +381,7 @@ class HttpUriPlugin {
351
381
  ? `${toSafePath(compiler.name)}.webpack.lock`
352
382
  : "webpack.lock"
353
383
  );
384
+ /** @type {string | false} */
354
385
  const cacheLocation =
355
386
  this._cacheLocation !== undefined
356
387
  ? this._cacheLocation
@@ -364,6 +395,7 @@ class HttpUriPlugin {
364
395
 
365
396
  let warnedAboutEol = false;
366
397
 
398
+ /** @type {Map<string, string>} */
367
399
  const cacheKeyCache = new Map();
368
400
  /**
369
401
  * @param {string} url the url
@@ -447,6 +479,12 @@ class HttpUriPlugin {
447
479
 
448
480
  /** @type {Map<string, LockfileEntry | "ignore" | "no-cache"> | undefined} */
449
481
  let lockfileUpdates = undefined;
482
+
483
+ /**
484
+ * @param {Lockfile} lockfile lockfile instance
485
+ * @param {string} url url to store
486
+ * @param {LockfileEntry | "ignore" | "no-cache"} entry lockfile entry
487
+ */
450
488
  const storeLockEntry = (lockfile, url, entry) => {
451
489
  const oldEntry = lockfile.entries.get(url);
452
490
  if (lockfileUpdates === undefined) lockfileUpdates = new Map();
@@ -9,13 +9,299 @@ const { join, dirname, readJson } = require("../util/fs");
9
9
 
10
10
  /** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */
11
11
 
12
+ // Extreme shorthand only for github. eg: foo/bar
13
+ const RE_URL_GITHUB_EXTREME_SHORT = /^[^/@:.\s][^/@:\s]*\/[^@:\s]*[^/@:\s]#\S+/;
14
+
15
+ // Short url with specific protocol. eg: github:foo/bar
16
+ const RE_GIT_URL_SHORT = /^(github|gitlab|bitbucket|gist):\/?[^/.]+\/?/i;
17
+
18
+ // Currently supported protocols
19
+ const RE_PROTOCOL =
20
+ /^((git\+)?(ssh|https?|file)|git|github|gitlab|bitbucket|gist):$/i;
21
+
22
+ // Has custom protocol
23
+ const RE_CUSTOM_PROTOCOL = /^((git\+)?(ssh|https?|file)|git):\/\//i;
24
+
25
+ // Valid hash format for npm / yarn ...
26
+ const RE_URL_HASH_VERSION = /#(?:semver:)?(.+)/;
27
+
28
+ // Simple hostname validate
29
+ const RE_HOSTNAME = /^(?:[^/.]+(\.[^/]+)+|localhost)$/;
30
+
31
+ // For hostname with colon. eg: ssh://user@github.com:foo/bar
32
+ const RE_HOSTNAME_WITH_COLON =
33
+ /([^/@#:.]+(?:\.[^/@#:.]+)+|localhost):([^#/0-9]+)/;
34
+
35
+ // Reg for url without protocol
36
+ const RE_NO_PROTOCOL = /^([^/@#:.]+(?:\.[^/@#:.]+)+)/;
37
+
38
+ // RegExp for version string
39
+ const VERSION_PATTERN_REGEXP = /^([\d^=v<>~]|[*xX]$)/;
40
+
41
+ // Specific protocol for short url without normal hostname
42
+ const PROTOCOLS_FOR_SHORT = [
43
+ "github:",
44
+ "gitlab:",
45
+ "bitbucket:",
46
+ "gist:",
47
+ "file:"
48
+ ];
49
+
50
+ // Default protocol for git url
51
+ const DEF_GIT_PROTOCOL = "git+ssh://";
52
+
53
+ // thanks to https://github.com/npm/hosted-git-info/blob/latest/git-host-info.js
54
+ const extractCommithashByDomain = {
55
+ "github.com": (pathname, hash) => {
56
+ let [, user, project, type, commithash] = pathname.split("/", 5);
57
+ if (type && type !== "tree") {
58
+ return;
59
+ }
60
+
61
+ if (!type) {
62
+ commithash = hash;
63
+ } else {
64
+ commithash = "#" + commithash;
65
+ }
66
+
67
+ if (project && project.endsWith(".git")) {
68
+ project = project.slice(0, -4);
69
+ }
70
+
71
+ if (!user || !project) {
72
+ return;
73
+ }
74
+
75
+ return commithash;
76
+ },
77
+ "gitlab.com": (pathname, hash) => {
78
+ const path = pathname.slice(1);
79
+ if (path.includes("/-/") || path.includes("/archive.tar.gz")) {
80
+ return;
81
+ }
82
+
83
+ const segments = path.split("/");
84
+ let project = segments.pop();
85
+ if (project.endsWith(".git")) {
86
+ project = project.slice(0, -4);
87
+ }
88
+
89
+ const user = segments.join("/");
90
+ if (!user || !project) {
91
+ return;
92
+ }
93
+
94
+ return hash;
95
+ },
96
+ "bitbucket.org": (pathname, hash) => {
97
+ let [, user, project, aux] = pathname.split("/", 4);
98
+ if (["get"].includes(aux)) {
99
+ return;
100
+ }
101
+
102
+ if (project && project.endsWith(".git")) {
103
+ project = project.slice(0, -4);
104
+ }
105
+
106
+ if (!user || !project) {
107
+ return;
108
+ }
109
+
110
+ return hash;
111
+ },
112
+ "gist.github.com": (pathname, hash) => {
113
+ let [, user, project, aux] = pathname.split("/", 4);
114
+ if (aux === "raw") {
115
+ return;
116
+ }
117
+
118
+ if (!project) {
119
+ if (!user) {
120
+ return;
121
+ }
122
+
123
+ project = user;
124
+ user = null;
125
+ }
126
+
127
+ if (project.endsWith(".git")) {
128
+ project = project.slice(0, -4);
129
+ }
130
+
131
+ return hash;
132
+ }
133
+ };
134
+
135
+ /**
136
+ * extract commit hash from parsed url
137
+ *
138
+ * @inner
139
+ * @param {Object} urlParsed parsed url
140
+ * @returns {string} commithash
141
+ */
142
+ function getCommithash(urlParsed) {
143
+ let { hostname, pathname, hash } = urlParsed;
144
+ hostname = hostname.replace(/^www\./, "");
145
+
146
+ try {
147
+ hash = decodeURIComponent(hash);
148
+ // eslint-disable-next-line no-empty
149
+ } catch (e) {}
150
+
151
+ if (extractCommithashByDomain[hostname]) {
152
+ return extractCommithashByDomain[hostname](pathname, hash) || "";
153
+ }
154
+
155
+ return hash;
156
+ }
157
+
158
+ /**
159
+ * make url right for URL parse
160
+ *
161
+ * @inner
162
+ * @param {string} gitUrl git url
163
+ * @returns {string} fixed url
164
+ */
165
+ function correctUrl(gitUrl) {
166
+ // like:
167
+ // proto://hostname.com:user/repo -> proto://hostname.com/user/repo
168
+ return gitUrl.replace(RE_HOSTNAME_WITH_COLON, "$1/$2");
169
+ }
170
+
171
+ /**
172
+ * make url protocol right for URL parse
173
+ *
174
+ * @inner
175
+ * @param {string} gitUrl git url
176
+ * @returns {string} fixed url
177
+ */
178
+ function correctProtocol(gitUrl) {
179
+ // eg: github:foo/bar#v1.0. Should not add double slash, in case of error parsed `pathname`
180
+ if (RE_GIT_URL_SHORT.test(gitUrl)) {
181
+ return gitUrl;
182
+ }
183
+
184
+ // eg: user@github.com:foo/bar
185
+ if (!RE_CUSTOM_PROTOCOL.test(gitUrl)) {
186
+ return `${DEF_GIT_PROTOCOL}${gitUrl}`;
187
+ }
188
+
189
+ return gitUrl;
190
+ }
191
+
192
+ /**
193
+ * extract git dep version from hash
194
+ *
195
+ * @inner
196
+ * @param {string} hash hash
197
+ * @returns {string} git dep version
198
+ */
199
+ function getVersionFromHash(hash) {
200
+ const matched = hash.match(RE_URL_HASH_VERSION);
201
+
202
+ return (matched && matched[1]) || "";
203
+ }
204
+
205
+ /**
206
+ * if string can be decoded
207
+ *
208
+ * @inner
209
+ * @param {string} str str to be checked
210
+ * @returns {boolean} if can be decoded
211
+ */
212
+ function canBeDecoded(str) {
213
+ try {
214
+ decodeURIComponent(str);
215
+ } catch (e) {
216
+ return false;
217
+ }
218
+
219
+ return true;
220
+ }
221
+
222
+ /**
223
+ * get right dep version from git url
224
+ *
225
+ * @inner
226
+ * @param {string} gitUrl git url
227
+ * @returns {string} dep version
228
+ */
229
+ function getGitUrlVersion(gitUrl) {
230
+ let oriGitUrl = gitUrl;
231
+ // github extreme shorthand
232
+ if (RE_URL_GITHUB_EXTREME_SHORT.test(gitUrl)) {
233
+ gitUrl = "github:" + gitUrl;
234
+ } else {
235
+ gitUrl = correctProtocol(gitUrl);
236
+ }
237
+
238
+ gitUrl = correctUrl(gitUrl);
239
+
240
+ let parsed;
241
+ try {
242
+ parsed = new URL(gitUrl);
243
+ // eslint-disable-next-line no-empty
244
+ } catch (e) {}
245
+
246
+ if (!parsed) {
247
+ return "";
248
+ }
249
+
250
+ const { protocol, hostname, pathname, username, password } = parsed;
251
+ if (!RE_PROTOCOL.test(protocol)) {
252
+ return "";
253
+ }
254
+
255
+ // pathname shouldn't be empty or URL malformed
256
+ if (!pathname || !canBeDecoded(pathname)) {
257
+ return "";
258
+ }
259
+
260
+ // without protocol, there should have auth info
261
+ if (RE_NO_PROTOCOL.test(oriGitUrl) && !username && !password) {
262
+ return "";
263
+ }
264
+
265
+ if (!PROTOCOLS_FOR_SHORT.includes(protocol.toLowerCase())) {
266
+ if (!RE_HOSTNAME.test(hostname)) {
267
+ return "";
268
+ }
269
+
270
+ const commithash = getCommithash(parsed);
271
+ return getVersionFromHash(commithash) || commithash;
272
+ }
273
+
274
+ // for protocol short
275
+ return getVersionFromHash(gitUrl);
276
+ }
277
+
12
278
  /**
13
279
  * @param {string} str maybe required version
14
280
  * @returns {boolean} true, if it looks like a version
15
281
  */
16
- exports.isRequiredVersion = str => {
17
- return /^([\d^=v<>~]|[*xX]$)/.test(str);
18
- };
282
+ function isRequiredVersion(str) {
283
+ return VERSION_PATTERN_REGEXP.test(str);
284
+ }
285
+
286
+ exports.isRequiredVersion = isRequiredVersion;
287
+
288
+ /**
289
+ * @see https://docs.npmjs.com/cli/v7/configuring-npm/package-json#urls-as-dependencies
290
+ * @param {string} versionDesc version to be normalized
291
+ * @returns {string} normalized version
292
+ */
293
+ function normalizeVersion(versionDesc) {
294
+ versionDesc = (versionDesc && versionDesc.trim()) || "";
295
+
296
+ if (isRequiredVersion(versionDesc)) {
297
+ return versionDesc;
298
+ }
299
+
300
+ // add handle for URL Dependencies
301
+ return getGitUrlVersion(versionDesc.toLowerCase());
302
+ }
303
+
304
+ exports.normalizeVersion = normalizeVersion;
19
305
 
20
306
  /**
21
307
  *
@@ -64,27 +350,27 @@ exports.getRequiredVersionFromDescriptionFile = (data, packageName) => {
64
350
  typeof data.optionalDependencies === "object" &&
65
351
  packageName in data.optionalDependencies
66
352
  ) {
67
- return data.optionalDependencies[packageName];
353
+ return normalizeVersion(data.optionalDependencies[packageName]);
68
354
  }
69
355
  if (
70
356
  data.dependencies &&
71
357
  typeof data.dependencies === "object" &&
72
358
  packageName in data.dependencies
73
359
  ) {
74
- return data.dependencies[packageName];
360
+ return normalizeVersion(data.dependencies[packageName]);
75
361
  }
76
362
  if (
77
363
  data.peerDependencies &&
78
364
  typeof data.peerDependencies === "object" &&
79
365
  packageName in data.peerDependencies
80
366
  ) {
81
- return data.peerDependencies[packageName];
367
+ return normalizeVersion(data.peerDependencies[packageName]);
82
368
  }
83
369
  if (
84
370
  data.devDependencies &&
85
371
  typeof data.devDependencies === "object" &&
86
372
  packageName in data.devDependencies
87
373
  ) {
88
- return data.devDependencies[packageName];
374
+ return normalizeVersion(data.devDependencies[packageName]);
89
375
  }
90
376
  };
@@ -12,6 +12,12 @@
12
12
  const DATA_URI_CONTENT_LENGTH = 16;
13
13
  const MAX_MODULE_IDENTIFIER_LENGTH = 80;
14
14
 
15
+ /**
16
+ * @param {number} n a number
17
+ * @param {string} singular singular
18
+ * @param {string} plural plural
19
+ * @returns {string} if n is 1, singular, else plural
20
+ */
15
21
  const plural = (n, singular, plural) => (n === 1 ? singular : plural);
16
22
 
17
23
  /**
@@ -29,6 +35,10 @@ const printSizes = (sizes, { formatSize = n => `${n}` }) => {
29
35
  }
30
36
  };
31
37
 
38
+ /**
39
+ * @param {string} resource resource
40
+ * @returns {string} resource name for display
41
+ */
32
42
  const getResourceName = resource => {
33
43
  const dataUrl = /^data:[^,]+,/.exec(resource);
34
44
  if (!dataUrl) return resource;
@@ -41,6 +51,10 @@ const getResourceName = resource => {
41
51
  )}..`;
42
52
  };
43
53
 
54
+ /**
55
+ * @param {string} name module name
56
+ * @returns {[string,string]} prefix and module name
57
+ */
44
58
  const getModuleName = name => {
45
59
  const [, prefix, resource] = /^(.*!)?([^!]*)$/.exec(name);
46
60
 
@@ -59,6 +73,11 @@ const getModuleName = name => {
59
73
  return [prefix, getResourceName(resource)];
60
74
  };
61
75
 
76
+ /**
77
+ * @param {string} str string
78
+ * @param {function(string): string} fn function to apply to each line
79
+ * @returns {string} joined string
80
+ */
62
81
  const mapLines = (str, fn) => str.split("\n").map(fn).join("\n");
63
82
 
64
83
  /**
@@ -71,6 +90,12 @@ const isValidId = id => {
71
90
  return typeof id === "number" || id;
72
91
  };
73
92
 
93
+ /**
94
+ * @template T
95
+ * @param {Array<T>} list of items
96
+ * @param {number} count number of items to show
97
+ * @returns {string} string representation of list
98
+ */
74
99
  const moreCount = (list, count) => {
75
100
  return list && list.length > 0 ? `+ ${count}` : `${count}`;
76
101
  };
@@ -83,6 +83,9 @@ class StackedCacheMap {
83
83
  this.map.clear();
84
84
  }
85
85
 
86
+ /**
87
+ * @returns {number} size of the map
88
+ */
86
89
  get size() {
87
90
  let size = this.map.size;
88
91
  for (const map of this.stack) {
@@ -91,6 +94,9 @@ class StackedCacheMap {
91
94
  return size;
92
95
  }
93
96
 
97
+ /**
98
+ * @returns {Iterator<[K, V]>} iterator
99
+ */
94
100
  [Symbol.iterator]() {
95
101
  const iterators = this.stack.map(map => map[Symbol.iterator]());
96
102
  let current = this.map[Symbol.iterator]();
@@ -5,12 +5,46 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ /** @typedef {import("../util/Hash")} Hash */
9
+
10
+ /**
11
+ * StringXor class provides methods for performing
12
+ * [XOR operations](https://en.wikipedia.org/wiki/Exclusive_or) on strings. In this context
13
+ * we operating on the character codes of two strings, which are represented as
14
+ * [Buffer](https://nodejs.org/api/buffer.html) objects.
15
+ *
16
+ * We use [StringXor in webpack](https://github.com/webpack/webpack/commit/41a8e2ea483a544c4ccd3e6217bdfb80daffca39)
17
+ * to create a hash of the current state of the compilation. By XOR'ing the Module hashes, it
18
+ * doesn't matter if the Module hashes are sorted or not. This is useful because it allows us to avoid sorting the
19
+ * Module hashes.
20
+ *
21
+ * @example
22
+ * ```js
23
+ * const xor = new StringXor();
24
+ * xor.add('hello');
25
+ * xor.add('world');
26
+ * console.log(xor.toString());
27
+ * ```
28
+ *
29
+ * @example
30
+ * ```js
31
+ * const xor = new StringXor();
32
+ * xor.add('foo');
33
+ * xor.add('bar');
34
+ * const hash = createHash('sha256');
35
+ * hash.update(xor.toString());
36
+ * console.log(hash.digest('hex'));
37
+ * ```
38
+ */
8
39
  class StringXor {
9
40
  constructor() {
41
+ /** @type {Buffer|undefined} */
10
42
  this._value = undefined;
11
43
  }
12
44
 
13
45
  /**
46
+ * Adds a string to the current StringXor object.
47
+ *
14
48
  * @param {string} str string
15
49
  * @returns {void}
16
50
  */
@@ -18,6 +52,10 @@ class StringXor {
18
52
  const len = str.length;
19
53
  const value = this._value;
20
54
  if (value === undefined) {
55
+ /**
56
+ * We are choosing to use Buffer.allocUnsafe() because it is often faster than Buffer.alloc() because
57
+ * it allocates a new buffer of the specified size without initializing the memory.
58
+ */
21
59
  const newValue = (this._value = Buffer.allocUnsafe(len));
22
60
  for (let i = 0; i < len; i++) {
23
61
  newValue[i] = str.charCodeAt(i);
@@ -41,11 +79,24 @@ class StringXor {
41
79
  }
42
80
  }
43
81
 
82
+ /**
83
+ * Returns a string that represents the current state of the StringXor object. We chose to use "latin1" encoding
84
+ * here because "latin1" encoding is a single-byte encoding that can represent all characters in the
85
+ * [ISO-8859-1 character set](https://en.wikipedia.org/wiki/ISO/IEC_8859-1). This is useful when working
86
+ * with binary data that needs to be represented as a string.
87
+ *
88
+ * @returns {string} Returns a string that represents the current state of the StringXor object.
89
+ */
44
90
  toString() {
45
91
  const value = this._value;
46
92
  return value === undefined ? "" : value.toString("latin1");
47
93
  }
48
94
 
95
+ /**
96
+ * Updates the hash with the current state of the StringXor object.
97
+ *
98
+ * @param {Hash} hash Hash instance
99
+ */
49
100
  updateHash(hash) {
50
101
  const value = this._value;
51
102
  if (value !== undefined) hash.update(value);
@@ -5,10 +5,18 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ /**
9
+ * @param {string} str string
10
+ * @returns {string} quoted meta
11
+ */
8
12
  const quoteMeta = str => {
9
13
  return str.replace(/[-[\]\\/{}()*+?.^$|]/g, "\\$&");
10
14
  };
11
15
 
16
+ /**
17
+ * @param {string} str string
18
+ * @returns {string} string
19
+ */
12
20
  const toSimpleString = str => {
13
21
  if (`${+str}` === str) {
14
22
  return str;
@@ -49,19 +57,28 @@ const compileBooleanMatcherFromLists = (positiveItems, negativeItems) => {
49
57
  }
50
58
  };
51
59
 
60
+ /**
61
+ * @param {Set<string>} itemsSet items set
62
+ * @param {(str: string) => string | false} getKey get key function
63
+ * @param {(str: Array<string>) => boolean} condition condition
64
+ * @returns {Array<Array<string>>} list of common items
65
+ */
52
66
  const popCommonItems = (itemsSet, getKey, condition) => {
67
+ /** @type {Map<string, Array<string>>} */
53
68
  const map = new Map();
54
69
  for (const item of itemsSet) {
55
70
  const key = getKey(item);
56
71
  if (key) {
57
72
  let list = map.get(key);
58
73
  if (list === undefined) {
74
+ /** @type {Array<string>} */
59
75
  list = [];
60
76
  map.set(key, list);
61
77
  }
62
78
  list.push(item);
63
79
  }
64
80
  }
81
+ /** @type {Array<Array<string>>} */
65
82
  const result = [];
66
83
  for (const list of map.values()) {
67
84
  if (condition(list)) {
@@ -74,6 +91,10 @@ const popCommonItems = (itemsSet, getKey, condition) => {
74
91
  return result;
75
92
  };
76
93
 
94
+ /**
95
+ * @param {Array<string>} items items
96
+ * @returns {string} common prefix
97
+ */
77
98
  const getCommonPrefix = items => {
78
99
  let prefix = items[0];
79
100
  for (let i = 1; i < items.length; i++) {
@@ -88,6 +109,10 @@ const getCommonPrefix = items => {
88
109
  return prefix;
89
110
  };
90
111
 
112
+ /**
113
+ * @param {Array<string>} items items
114
+ * @returns {string} common suffix
115
+ */
91
116
  const getCommonSuffix = items => {
92
117
  let suffix = items[0];
93
118
  for (let i = 1; i < items.length; i++) {
@@ -102,10 +127,15 @@ const getCommonSuffix = items => {
102
127
  return suffix;
103
128
  };
104
129
 
130
+ /**
131
+ * @param {Array<string>} itemsArr array of items
132
+ * @returns {string} regexp
133
+ */
105
134
  const itemsToRegexp = itemsArr => {
106
135
  if (itemsArr.length === 1) {
107
136
  return quoteMeta(itemsArr[0]);
108
137
  }
138
+ /** @type {Array<string>} */
109
139
  const finishedItems = [];
110
140
 
111
141
  // merge single char items: (a|b|c|d|ef) => ([abcd]|ef)
@@ -146,6 +176,7 @@ const itemsToRegexp = itemsArr => {
146
176
 
147
177
  // special case for 2 items with common suffix
148
178
  if (finishedItems.length === 0 && items.size === 2) {
179
+ /** @type {Iterator<string>} */
149
180
  const it = items[Symbol.iterator]();
150
181
  const a = it.next().value;
151
182
  const b = it.next().value;