@parcel/utils 2.0.0-nightly.127 → 2.0.0-nightly.1274

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 (104) hide show
  1. package/.eslintrc.js +6 -6
  2. package/lib/index.js +36849 -307
  3. package/lib/index.js.map +1 -0
  4. package/package.json +45 -20
  5. package/src/BitSet.js +126 -0
  6. package/src/DefaultMap.js +1 -1
  7. package/src/PromiseQueue.js +16 -12
  8. package/src/alternatives.js +145 -0
  9. package/src/ansi-html.js +2 -2
  10. package/src/blob.js +2 -1
  11. package/src/bundle-url.js +1 -1
  12. package/src/collection.js +35 -15
  13. package/src/config.js +101 -36
  14. package/src/countLines.js +5 -2
  15. package/src/debounce.js +1 -1
  16. package/src/dependency-location.js +11 -6
  17. package/src/generateBuildMetrics.js +158 -0
  18. package/src/generateCertificate.js +1 -1
  19. package/src/getCertificate.js +1 -1
  20. package/src/getExisting.js +1 -4
  21. package/src/getModuleParts.js +23 -0
  22. package/src/getRootDir.js +1 -2
  23. package/src/glob.js +39 -10
  24. package/src/hash.js +49 -0
  25. package/src/http-server.js +29 -19
  26. package/src/index.js +57 -21
  27. package/src/is-url.js +1 -1
  28. package/src/isDirectoryInside.js +11 -0
  29. package/src/openInBrowser.js +64 -0
  30. package/src/path.js +38 -6
  31. package/src/prettyDiagnostic.js +70 -24
  32. package/src/progress-message.js +22 -0
  33. package/src/relativeBundlePath.js +8 -13
  34. package/src/replaceBundleReferences.js +85 -41
  35. package/src/schema.js +101 -44
  36. package/src/shared-buffer.js +24 -0
  37. package/src/sourcemap.js +135 -0
  38. package/src/stream.js +31 -1
  39. package/src/urlJoin.js +3 -1
  40. package/test/BitSet.test.js +119 -0
  41. package/test/DefaultMap.test.js +7 -4
  42. package/test/collection.test.js +13 -1
  43. package/test/config.test.js +98 -0
  44. package/test/input/config/.testrc +3 -0
  45. package/test/input/config/config.cjs +3 -0
  46. package/test/input/config/config.js +3 -0
  47. package/test/input/config/config.json +3 -0
  48. package/test/input/config/empty.json +0 -0
  49. package/test/input/config/empty.toml +0 -0
  50. package/test/input/sourcemap/referenced-min.js +2 -0
  51. package/test/input/sourcemap/referenced-min.js.map +6 -0
  52. package/test/input/sourcemap/source-root.js +2 -0
  53. package/test/input/sourcemap/source-root.js.map +7 -0
  54. package/test/replaceBundleReferences.test.js +268 -0
  55. package/test/sourcemap.test.js +207 -0
  56. package/test/throttle.test.js +1 -2
  57. package/test/urlJoin.test.js +37 -0
  58. package/lib/DefaultMap.js +0 -64
  59. package/lib/Deferred.js +0 -26
  60. package/lib/PromiseQueue.js +0 -133
  61. package/lib/TapStream.js +0 -41
  62. package/lib/ansi-html.js +0 -16
  63. package/lib/blob.js +0 -31
  64. package/lib/bundle-url.js +0 -43
  65. package/lib/collection.js +0 -62
  66. package/lib/config.js +0 -88
  67. package/lib/countLines.js +0 -18
  68. package/lib/debounce.js +0 -20
  69. package/lib/dependency-location.js +0 -21
  70. package/lib/escape-html.js +0 -24
  71. package/lib/generateBundleReport.js +0 -38
  72. package/lib/generateCertificate.js +0 -124
  73. package/lib/getCertificate.js +0 -19
  74. package/lib/getExisting.js +0 -23
  75. package/lib/getRootDir.js +0 -55
  76. package/lib/glob.js +0 -76
  77. package/lib/http-server.js +0 -64
  78. package/lib/is-url.js +0 -17
  79. package/lib/loadSourceMapUrl.js +0 -33
  80. package/lib/md5.js +0 -35
  81. package/lib/objectHash.js +0 -26
  82. package/lib/parseCSSImport.js +0 -16
  83. package/lib/path.js +0 -22
  84. package/lib/prettifyTime.js +0 -10
  85. package/lib/prettyDiagnostic.js +0 -57
  86. package/lib/promisify.js +0 -13
  87. package/lib/relativeBundlePath.js +0 -24
  88. package/lib/relativeUrl.js +0 -16
  89. package/lib/replaceBundleReferences.js +0 -151
  90. package/lib/resolve.js +0 -93
  91. package/lib/schema.js +0 -320
  92. package/lib/serializeObject.js +0 -28
  93. package/lib/stream.js +0 -51
  94. package/lib/throttle.js +0 -16
  95. package/lib/urlJoin.js +0 -27
  96. package/src/.babelrc +0 -3
  97. package/src/generateBundleReport.js +0 -51
  98. package/src/loadSourceMapUrl.js +0 -33
  99. package/src/md5.js +0 -44
  100. package/src/promisify.js +0 -13
  101. package/src/resolve.js +0 -135
  102. package/src/serializeObject.js +0 -22
  103. package/test/input/sourcemap/referenced.js +0 -7
  104. package/test/loadSourceMapUrl.test.js +0 -37
package/src/schema.js CHANGED
@@ -1,15 +1,20 @@
1
1
  // @flow strict-local
2
2
  import ThrowableDiagnostic, {
3
3
  generateJSONCodeHighlights,
4
+ escapeMarkdown,
5
+ encodeJSONKeyComponent,
4
6
  } from '@parcel/diagnostic';
5
- // $FlowFixMe untyped
6
- import levenshteinDistance from 'js-levenshtein';
7
+ import type {Mapping} from '@mischnic/json-sourcemap';
8
+ import nullthrows from 'nullthrows';
9
+ // flowlint-next-line untyped-import:off
10
+ import levenshtein from 'fastest-levenshtein';
7
11
 
8
12
  export type SchemaEntity =
9
13
  | SchemaObject
10
14
  | SchemaArray
11
15
  | SchemaBoolean
12
16
  | SchemaString
17
+ | SchemaNumber
13
18
  | SchemaEnum
14
19
  | SchemaOneOf
15
20
  | SchemaAllOf
@@ -40,6 +45,11 @@ export type SchemaString = {|
40
45
  __validate?: (val: string) => ?string,
41
46
  __type?: string,
42
47
  |};
48
+ export type SchemaNumber = {|
49
+ type: 'number',
50
+ enum?: Array<number>,
51
+ __type?: string,
52
+ |};
43
53
  export type SchemaEnum = {|
44
54
  enum: Array<mixed>,
45
55
  |};
@@ -88,7 +98,7 @@ export type SchemaError =
88
98
  prop: string,
89
99
  expectedProps: Array<string>,
90
100
  actualProps: Array<string>,
91
- dataType: null | 'key',
101
+ dataType: 'key' | 'value',
92
102
 
93
103
  dataPath: string,
94
104
  ancestors: Array<SchemaEntity>,
@@ -99,7 +109,6 @@ export type SchemaError =
99
109
  actualValue: mixed,
100
110
  dataType: ?'key' | 'value',
101
111
  message?: string,
102
-
103
112
  dataPath: string,
104
113
  ancestors: Array<SchemaEntity>,
105
114
  |};
@@ -159,8 +168,7 @@ function validateSchema(schema: SchemaEntity, data: mixed): Array<SchemaError> {
159
168
  }
160
169
  } else if (schemaNode.__validate) {
161
170
  let validationError = schemaNode.__validate(value);
162
- // $FlowFixMe
163
- if (validationError) {
171
+ if (typeof validationError == 'string') {
164
172
  return {
165
173
  type: 'other',
166
174
  dataType: 'value',
@@ -173,6 +181,23 @@ function validateSchema(schema: SchemaEntity, data: mixed): Array<SchemaError> {
173
181
  }
174
182
  break;
175
183
  }
184
+ case 'number': {
185
+ // $FlowFixMe type was already checked
186
+ let value: number = dataNode;
187
+ if (schemaNode.enum) {
188
+ if (!schemaNode.enum.includes(value)) {
189
+ return {
190
+ type: 'enum',
191
+ dataType: 'value',
192
+ dataPath,
193
+ expectedValues: schemaNode.enum,
194
+ actualValue: value,
195
+ ancestors: schemaAncestors,
196
+ };
197
+ }
198
+ }
199
+ break;
200
+ }
176
201
  case 'object': {
177
202
  let results: Array<Array<SchemaError> | SchemaError> = [];
178
203
  let invalidProps;
@@ -187,7 +212,7 @@ function validateSchema(schema: SchemaEntity, data: mixed): Array<SchemaError> {
187
212
  k =>
188
213
  ({
189
214
  type: 'forbidden-prop',
190
- dataPath: dataPath + '/' + k,
215
+ dataPath: dataPath + '/' + encodeJSONKeyComponent(k),
191
216
  dataType: 'key',
192
217
  prop: k,
193
218
  expectedProps: Object.keys(schemaNode.properties),
@@ -209,7 +234,7 @@ function validateSchema(schema: SchemaEntity, data: mixed): Array<SchemaError> {
209
234
  ({
210
235
  type: 'missing-prop',
211
236
  dataPath,
212
- dataType: null,
237
+ dataType: 'value',
213
238
  prop: k,
214
239
  expectedProps: schemaNode.required,
215
240
  actualProps: keys,
@@ -230,7 +255,7 @@ function validateSchema(schema: SchemaEntity, data: mixed): Array<SchemaError> {
230
255
  [schemaNode.properties[k]].concat(schemaAncestors),
231
256
  // $FlowFixMe type was already checked
232
257
  dataNode[k],
233
- dataPath + '/' + k,
258
+ dataPath + '/' + encodeJSONKeyComponent(k),
234
259
  );
235
260
  if (result) results.push(result);
236
261
  } else {
@@ -239,7 +264,7 @@ function validateSchema(schema: SchemaEntity, data: mixed): Array<SchemaError> {
239
264
  results.push({
240
265
  type: 'enum',
241
266
  dataType: 'key',
242
- dataPath: dataPath + '/' + k,
267
+ dataPath: dataPath + '/' + encodeJSONKeyComponent(k),
243
268
  expectedValues: Object.keys(
244
269
  schemaNode.properties,
245
270
  ).filter(
@@ -256,7 +281,7 @@ function validateSchema(schema: SchemaEntity, data: mixed): Array<SchemaError> {
256
281
  [additionalProperties].concat(schemaAncestors),
257
282
  // $FlowFixMe type was already checked
258
283
  dataNode[k],
259
- dataPath + '/' + k,
284
+ dataPath + '/' + encodeJSONKeyComponent(k),
260
285
  );
261
286
  if (result) results.push(result);
262
287
  }
@@ -339,9 +364,12 @@ function validateSchema(schema: SchemaEntity, data: mixed): Array<SchemaError> {
339
364
  }
340
365
  export default validateSchema;
341
366
 
342
- function fuzzySearch(expectedValues: Array<string>, actualValue: string) {
367
+ export function fuzzySearch(
368
+ expectedValues: Array<string>,
369
+ actualValue: string,
370
+ ): Array<string> {
343
371
  let result = expectedValues
344
- .map(exp => [exp, levenshteinDistance(exp, actualValue)])
372
+ .map(exp => [exp, levenshtein.distance(exp, actualValue)])
345
373
  .filter(
346
374
  // Remove if more than half of the string would need to be changed
347
375
  ([, d]) => d * 2 < actualValue.length,
@@ -350,22 +378,43 @@ function fuzzySearch(expectedValues: Array<string>, actualValue: string) {
350
378
  return result.map(([v]) => v);
351
379
  }
352
380
 
353
- validateSchema.diagnostic = function(
381
+ validateSchema.diagnostic = function (
354
382
  schema: SchemaEntity,
355
- data: mixed,
356
- dataContentsPath?: ?string,
357
- dataContents: string | mixed,
383
+ data: {|
384
+ ...
385
+ | {|
386
+ source?: ?string,
387
+ data?: mixed,
388
+ |}
389
+ | {|
390
+ source: string,
391
+ map: {|
392
+ data: mixed,
393
+ pointers: {|[key: string]: Mapping|},
394
+ |},
395
+ |},
396
+ filePath?: ?string,
397
+ prependKey?: ?string,
398
+ |},
358
399
  origin: string,
359
- prependKey: string,
360
400
  message: string,
361
401
  ): void {
362
- let errors = validateSchema(schema, data);
402
+ if (
403
+ 'source' in data &&
404
+ 'data' in data &&
405
+ typeof data.source !== 'string' &&
406
+ !data
407
+ ) {
408
+ throw new Error(
409
+ 'At least one of data.source and data.data must be defined!',
410
+ );
411
+ }
412
+ let object = data.map
413
+ ? data.map.data
414
+ : // $FlowFixMe we can assume it's a JSON object
415
+ data.data ?? JSON.parse(data.source);
416
+ let errors = validateSchema(schema, object);
363
417
  if (errors.length) {
364
- let dataContentsString: string =
365
- typeof dataContents === 'string'
366
- ? dataContents
367
- : // $FlowFixMe
368
- JSON.stringify(dataContents, null, '\t');
369
418
  let keys = errors.map(e => {
370
419
  let message;
371
420
  if (e.type === 'enum') {
@@ -403,10 +452,8 @@ validateSchema.diagnostic = function(
403
452
  let {prop, actualProps} = e;
404
453
  let likely = fuzzySearch(actualProps, prop);
405
454
  if (likely.length > 0) {
406
- message = `Did you mean ${likely
407
- .map(v => JSON.stringify(v))
408
- .join(', ')}?`;
409
- e.dataPath += '/' + prop;
455
+ message = `Did you mean ${JSON.stringify(prop)}?`;
456
+ e.dataPath += '/' + likely[0];
410
457
  e.dataType = 'key';
411
458
  } else {
412
459
  message = `Missing property ${prop}`;
@@ -422,26 +469,36 @@ validateSchema.diagnostic = function(
422
469
  }
423
470
  return {key: e.dataPath, type: e.dataType, message};
424
471
  });
425
- let codeFrame = {
426
- code: dataContentsString,
427
- codeHighlights: generateJSONCodeHighlights(
428
- dataContentsString,
429
- keys.map(({key, type, message}) => ({
430
- key: prependKey + key,
431
- type: type,
432
- message,
433
- })),
434
- ),
435
- };
472
+ let map, code;
473
+ if (data.map) {
474
+ map = data.map;
475
+ code = data.source;
476
+ } else {
477
+ // $FlowFixMe we can assume that data is valid JSON
478
+ map = data.source ?? JSON.stringify(nullthrows(data.data), 0, '\t');
479
+ code = map;
480
+ }
481
+ let codeFrames = [
482
+ {
483
+ filePath: data.filePath ?? undefined,
484
+ language: 'json',
485
+ code,
486
+ codeHighlights: generateJSONCodeHighlights(
487
+ map,
488
+ keys.map(({key, type, message}) => ({
489
+ key: (data.prependKey ?? '') + key,
490
+ type: type,
491
+ message: message != null ? escapeMarkdown(message) : message,
492
+ })),
493
+ ),
494
+ },
495
+ ];
436
496
 
437
497
  throw new ThrowableDiagnostic({
438
498
  diagnostic: {
439
- message,
499
+ message: message,
440
500
  origin,
441
- // $FlowFixMe should be a sketchy string check
442
- filePath: dataContentsPath || undefined,
443
- language: 'json',
444
- codeFrame,
501
+ codeFrames,
445
502
  },
446
503
  });
447
504
  }
@@ -0,0 +1,24 @@
1
+ // @flow
2
+ /* global MessageChannel:readonly */
3
+
4
+ export let SharedBuffer: Class<ArrayBuffer> | Class<SharedArrayBuffer>;
5
+
6
+ // $FlowFixMe[prop-missing]
7
+ if (process.browser) {
8
+ SharedBuffer = ArrayBuffer;
9
+ // Safari has removed the constructor
10
+ if (typeof SharedArrayBuffer !== 'undefined') {
11
+ let channel = new MessageChannel();
12
+ try {
13
+ // Firefox might throw when sending the Buffer over a MessagePort
14
+ channel.port1.postMessage(new SharedArrayBuffer(0));
15
+ SharedBuffer = SharedArrayBuffer;
16
+ } catch (_) {
17
+ // NOOP
18
+ }
19
+ channel.port1.close();
20
+ channel.port2.close();
21
+ }
22
+ } else {
23
+ SharedBuffer = SharedArrayBuffer;
24
+ }
@@ -0,0 +1,135 @@
1
+ // @flow
2
+ import type {SourceLocation} from '@parcel/types';
3
+ import type {FileSystem} from '@parcel/fs';
4
+ import SourceMap from '@parcel/source-map';
5
+ import path from 'path';
6
+ import {normalizeSeparators, isAbsolute} from './path';
7
+
8
+ export const SOURCEMAP_RE: RegExp =
9
+ /(?:\/\*|\/\/)\s*[@#]\s*sourceMappingURL\s*=\s*([^\s*]+)(?:\s*\*\/)?\s*$/;
10
+ const DATA_URL_RE = /^data:[^;]+(?:;charset=[^;]+)?;base64,(.*)/;
11
+ export const SOURCEMAP_EXTENSIONS: Set<string> = new Set<string>([
12
+ 'css',
13
+ 'es',
14
+ 'es6',
15
+ 'js',
16
+ 'jsx',
17
+ 'mjs',
18
+ 'ts',
19
+ 'tsx',
20
+ ]);
21
+
22
+ export function matchSourceMappingURL(
23
+ contents: string,
24
+ ): null | RegExp$matchResult {
25
+ return contents.match(SOURCEMAP_RE);
26
+ }
27
+
28
+ export async function loadSourceMapUrl(
29
+ fs: FileSystem,
30
+ filename: string,
31
+ contents: string,
32
+ ): Promise<?{|filename: string, map: any, url: string|}> {
33
+ let match = matchSourceMappingURL(contents);
34
+ if (match) {
35
+ let url = match[1].trim();
36
+ let dataURLMatch = url.match(DATA_URL_RE);
37
+
38
+ let mapFilePath;
39
+ if (dataURLMatch) {
40
+ mapFilePath = filename;
41
+ } else {
42
+ mapFilePath = url.replace(/^file:\/\//, '');
43
+ mapFilePath = isAbsolute(mapFilePath)
44
+ ? mapFilePath
45
+ : path.join(path.dirname(filename), mapFilePath);
46
+ }
47
+
48
+ return {
49
+ url,
50
+ filename: mapFilePath,
51
+ map: JSON.parse(
52
+ dataURLMatch
53
+ ? Buffer.from(dataURLMatch[1], 'base64').toString()
54
+ : await fs.readFile(mapFilePath, 'utf8'),
55
+ ),
56
+ };
57
+ }
58
+ }
59
+
60
+ export async function loadSourceMap(
61
+ filename: string,
62
+ contents: string,
63
+ options: {fs: FileSystem, projectRoot: string, ...},
64
+ ): Promise<?SourceMap> {
65
+ let foundMap = await loadSourceMapUrl(options.fs, filename, contents);
66
+ if (foundMap) {
67
+ let mapSourceRoot = path.dirname(filename);
68
+ if (
69
+ foundMap.map.sourceRoot &&
70
+ !normalizeSeparators(foundMap.map.sourceRoot).startsWith('/')
71
+ ) {
72
+ mapSourceRoot = path.join(mapSourceRoot, foundMap.map.sourceRoot);
73
+ }
74
+
75
+ let sourcemapInstance = new SourceMap(options.projectRoot);
76
+ sourcemapInstance.addVLQMap({
77
+ ...foundMap.map,
78
+ sources: foundMap.map.sources.map(s => {
79
+ return path.join(mapSourceRoot, s);
80
+ }),
81
+ });
82
+ return sourcemapInstance;
83
+ }
84
+ }
85
+
86
+ export function remapSourceLocation(
87
+ loc: SourceLocation,
88
+ originalMap: SourceMap,
89
+ ): SourceLocation {
90
+ let {
91
+ filePath,
92
+ start: {line: startLine, column: startCol},
93
+ end: {line: endLine, column: endCol},
94
+ } = loc;
95
+ let lineDiff = endLine - startLine;
96
+ let colDiff = endCol - startCol;
97
+ let start = originalMap.findClosestMapping(startLine, startCol);
98
+ let end = originalMap.findClosestMapping(endLine, endCol);
99
+
100
+ if (start?.original) {
101
+ if (start.source) {
102
+ filePath = start.source;
103
+ }
104
+
105
+ ({line: startLine, column: startCol} = start.original);
106
+ startCol++; // source map columns are 0-based
107
+ }
108
+
109
+ if (end?.original) {
110
+ ({line: endLine, column: endCol} = end.original);
111
+ endCol++;
112
+
113
+ if (endLine < startLine) {
114
+ endLine = startLine;
115
+ endCol = startCol;
116
+ } else if (endLine === startLine && endCol < startCol && lineDiff === 0) {
117
+ endCol = startCol + colDiff;
118
+ }
119
+ } else {
120
+ endLine = startLine;
121
+ endCol = startCol;
122
+ }
123
+
124
+ return {
125
+ filePath,
126
+ start: {
127
+ line: startLine,
128
+ column: startCol,
129
+ },
130
+ end: {
131
+ line: endLine,
132
+ column: endCol,
133
+ },
134
+ };
135
+ }
package/src/stream.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // @flow strict-local
2
2
 
3
- import {Readable} from 'stream';
3
+ import {Readable, PassThrough} from 'stream';
4
4
  import type {Blob} from '@parcel/types';
5
5
 
6
6
  export function measureStreamLength(stream: Readable): Promise<number> {
@@ -42,3 +42,33 @@ export function blobToStream(blob: Blob): Readable {
42
42
 
43
43
  return readableFromStringOrBuffer(blob);
44
44
  }
45
+
46
+ export function streamFromPromise(promise: Promise<Blob>): Readable {
47
+ const stream = new PassThrough();
48
+ promise.then(blob => {
49
+ if (blob instanceof Readable) {
50
+ blob.pipe(stream);
51
+ } else {
52
+ stream.end(blob);
53
+ }
54
+ });
55
+
56
+ return stream;
57
+ }
58
+
59
+ export function fallbackStream(
60
+ stream: Readable,
61
+ fallback: () => Readable,
62
+ ): Readable {
63
+ const res = new PassThrough();
64
+ stream.on('error', err => {
65
+ if (err.code === 'ENOENT') {
66
+ fallback().pipe(res);
67
+ } else {
68
+ res.emit('error', err);
69
+ }
70
+ });
71
+
72
+ stream.pipe(res);
73
+ return res;
74
+ }
package/src/urlJoin.js CHANGED
@@ -9,7 +9,9 @@ import path from 'path';
9
9
  */
10
10
  export default function urlJoin(publicURL: string, assetPath: string): string {
11
11
  const url = URL.parse(publicURL, false, true);
12
- const assetUrl = URL.parse(assetPath);
12
+ // Leading / ensures that paths with colons are not parsed as a protocol.
13
+ let p = assetPath.startsWith('/') ? assetPath : '/' + assetPath;
14
+ const assetUrl = URL.parse(p);
13
15
  url.pathname = path.posix.join(url.pathname, assetUrl.pathname);
14
16
  url.search = assetUrl.search;
15
17
  url.hash = assetUrl.hash;
@@ -0,0 +1,119 @@
1
+ // @flow strict-local
2
+
3
+ import assert from 'assert';
4
+ import {BitSet} from '../src/BitSet';
5
+
6
+ function assertValues<Item>(set: BitSet<Item>, values: Array<Item>) {
7
+ let setValues = set.values();
8
+
9
+ for (let value of values) {
10
+ assert(set.has(value), 'Set.has returned false');
11
+ assert(
12
+ setValues.some(v => v === value),
13
+ 'Set values is missing value',
14
+ );
15
+ }
16
+
17
+ assert(
18
+ setValues.length === values.length,
19
+ `Expected ${values.length} values but got ${setValues.length}`,
20
+ );
21
+ }
22
+
23
+ describe('BitSet', () => {
24
+ it('cloneEmpty should return an empty set', () => {
25
+ let set1 = BitSet.from([1, 2, 3, 4, 5]);
26
+ set1.add(1);
27
+ set1.add(3);
28
+
29
+ let set2 = set1.cloneEmpty();
30
+
31
+ assertValues(set2, []);
32
+ });
33
+
34
+ it('clone should return a set with the same values', () => {
35
+ let set1 = BitSet.from([1, 2, 3, 4, 5]);
36
+ set1.add(1);
37
+ set1.add(3);
38
+
39
+ let set2 = set1.clone();
40
+
41
+ assertValues(set2, [1, 3]);
42
+ });
43
+
44
+ it('clear should remove all values from the set', () => {
45
+ let set1 = BitSet.from([1, 2, 3, 4, 5]);
46
+ set1.add(1);
47
+ set1.add(3);
48
+
49
+ set1.clear();
50
+
51
+ assertValues(set1, []);
52
+ });
53
+
54
+ it('delete should remove values from the set', () => {
55
+ let set1 = BitSet.from([1, 2, 3, 4, 5]);
56
+ set1.add(1);
57
+ set1.add(3);
58
+ set1.add(5);
59
+
60
+ set1.delete(3);
61
+
62
+ assertValues(set1, [1, 5]);
63
+ });
64
+
65
+ it('should intersect with another BitSet', () => {
66
+ let set1 = BitSet.from([1, 2, 3, 4, 5]);
67
+ set1.add(1);
68
+ set1.add(3);
69
+
70
+ let set2 = set1.cloneEmpty();
71
+ set2.add(3);
72
+ set2.add(5);
73
+
74
+ set1.intersect(set2);
75
+ assertValues(set1, [3]);
76
+ });
77
+
78
+ it('should union with another BitSet', () => {
79
+ let set1 = BitSet.from([1, 2, 3, 4, 5]);
80
+ set1.add(1);
81
+ set1.add(3);
82
+
83
+ let set2 = set1.cloneEmpty();
84
+ set2.add(3);
85
+ set2.add(5);
86
+
87
+ set1.union(set2);
88
+ assertValues(set1, [1, 3, 5]);
89
+ });
90
+
91
+ it('BitSet.union should create a new BitSet with the union', () => {
92
+ let set1 = BitSet.from([1, 2, 3, 4, 5]);
93
+ set1.add(1);
94
+ set1.add(3);
95
+
96
+ let set2 = set1.cloneEmpty();
97
+ set2.add(3);
98
+ set2.add(5);
99
+
100
+ let set3 = BitSet.union(set1, set2);
101
+ assertValues(set1, [1, 3]);
102
+ assertValues(set2, [3, 5]);
103
+ assertValues(set3, [1, 3, 5]);
104
+ });
105
+
106
+ it('returns an array of all values', () => {
107
+ let set = BitSet.from([1, 2, 3, 4]);
108
+ set.add(1);
109
+ set.add(3);
110
+
111
+ assertValues(set, [3, 1]);
112
+ });
113
+
114
+ it('should return an error if a new item is added', () => {
115
+ let set = BitSet.from([1, 2, 3, 4]);
116
+
117
+ assert.throws(() => set.add(5), /Item is missing from BitSet/);
118
+ });
119
+ });
@@ -5,10 +5,13 @@ import {DefaultMap} from '../src/DefaultMap';
5
5
 
6
6
  describe('DefaultMap', () => {
7
7
  it('constructs with entries just like Map', () => {
8
- let map = new DefaultMap(k => k, [
9
- [1, 3],
10
- [2, 27],
11
- ]);
8
+ let map = new DefaultMap(
9
+ k => k,
10
+ [
11
+ [1, 3],
12
+ [2, 27],
13
+ ],
14
+ );
12
15
  assert.equal(map.get(1), 3);
13
16
  assert.deepEqual(Array.from(map.entries()), [
14
17
  [1, 3],
@@ -1,7 +1,11 @@
1
1
  // @flow
2
2
 
3
3
  import assert from 'assert';
4
- import {objectSortedEntries, objectSortedEntriesDeep} from '../src/collection';
4
+ import {
5
+ objectSortedEntries,
6
+ objectSortedEntriesDeep,
7
+ setDifference,
8
+ } from '../src/collection';
5
9
 
6
10
  describe('objectSortedEntries', () => {
7
11
  it('returns a sorted list of key/value tuples', () => {
@@ -38,3 +42,11 @@ describe('objectSortedEntriesDeep', () => {
38
42
  );
39
43
  });
40
44
  });
45
+ describe('setDifference', () => {
46
+ it('returns a setDifference of two sets of T type', () => {
47
+ assert.deepEqual(
48
+ setDifference(new Set([1, 2, 3]), new Set([3, 4, 5])),
49
+ new Set([1, 2, 4, 5]),
50
+ );
51
+ });
52
+ });