@parcel/utils 2.0.0-nightly.137 → 2.0.0-nightly.1370

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 +37566 -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 +132 -45
  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 +51 -10
  24. package/src/hash.js +49 -0
  25. package/src/http-server.js +29 -19
  26. package/src/index.js +69 -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 +74 -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 +100 -44
  36. package/src/shared-buffer.js +23 -0
  37. package/src/sourcemap.js +138 -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
@@ -1,12 +1,20 @@
1
1
  // @flow strict-local
2
2
 
3
3
  import type SourceMap from '@parcel/source-map';
4
- import type {Async, Blob, Bundle, BundleGraph, Dependency} from '@parcel/types';
4
+ import type {
5
+ Async,
6
+ Blob,
7
+ Bundle,
8
+ BundleGraph,
9
+ Dependency,
10
+ NamedBundle,
11
+ } from '@parcel/types';
5
12
 
6
13
  import {Readable} from 'stream';
7
14
  import nullthrows from 'nullthrows';
15
+ import invariant from 'assert';
8
16
  import URL from 'url';
9
- import {bufferStream, relativeBundlePath, urlJoin} from '../';
17
+ import {bufferStream, relativeBundlePath, urlJoin} from './';
10
18
 
11
19
  type ReplacementMap = Map<
12
20
  string /* dependency id */,
@@ -15,7 +23,7 @@ type ReplacementMap = Map<
15
23
 
16
24
  /*
17
25
  * Replaces references to dependency ids for URL dependencies with:
18
- * - in the case of an unresolvable url dependency, the original moduleSpecifier.
26
+ * - in the case of an unresolvable url dependency, the original specifier.
19
27
  * These are external requests that Parcel did not bundle.
20
28
  * - in the case of a reference to another bundle, the relative url to that
21
29
  * bundle from the current bundle.
@@ -25,44 +33,55 @@ export function replaceURLReferences({
25
33
  bundleGraph,
26
34
  contents,
27
35
  map,
36
+ getReplacement = s => s,
28
37
  relative = true,
29
38
  }: {|
30
- bundle: Bundle,
31
- bundleGraph: BundleGraph,
39
+ bundle: NamedBundle,
40
+ bundleGraph: BundleGraph<NamedBundle>,
32
41
  contents: string,
33
42
  relative?: boolean,
34
43
  map?: ?SourceMap,
44
+ getReplacement?: string => string,
35
45
  |}): {|+contents: string, +map: ?SourceMap|} {
36
46
  let replacements = new Map();
47
+ let urlDependencies = [];
48
+ bundle.traverse(node => {
49
+ if (node.type === 'dependency' && node.value.specifierType === 'url') {
50
+ urlDependencies.push(node.value);
51
+ }
52
+ });
37
53
 
38
- for (let dependency of bundleGraph.getExternalDependencies(bundle)) {
39
- if (!dependency.isURL) {
54
+ for (let dependency of urlDependencies) {
55
+ if (dependency.specifierType !== 'url') {
40
56
  continue;
41
57
  }
42
58
 
43
- let bundleGroup = bundleGraph.resolveExternalDependency(dependency);
44
- if (bundleGroup == null) {
45
- replacements.set(dependency.id, {
46
- from: dependency.id,
47
- to: dependency.moduleSpecifier,
59
+ let placeholder = dependency.meta?.placeholder ?? dependency.id;
60
+ invariant(typeof placeholder === 'string');
61
+
62
+ let resolved = bundleGraph.getReferencedBundle(dependency, bundle);
63
+ if (resolved == null) {
64
+ replacements.set(placeholder, {
65
+ from: placeholder,
66
+ to: getReplacement(dependency.specifier),
48
67
  });
49
68
  continue;
50
69
  }
51
70
 
52
- let [entryBundle] = bundleGraph.getBundlesInBundleGroup(bundleGroup);
53
- if (entryBundle.isInline) {
71
+ if (resolved.bundleBehavior === 'inline') {
54
72
  // If a bundle is inline, it should be replaced with inline contents,
55
73
  // not a URL.
56
74
  continue;
57
75
  }
58
76
 
59
77
  replacements.set(
60
- dependency.id,
78
+ placeholder,
61
79
  getURLReplacement({
62
80
  dependency,
63
81
  fromBundle: bundle,
64
- toBundle: entryBundle,
82
+ toBundle: resolved,
65
83
  relative,
84
+ getReplacement,
66
85
  }),
67
86
  );
68
87
  }
@@ -83,7 +102,7 @@ export async function replaceInlineReferences({
83
102
  getInlineBundleContents,
84
103
  }: {|
85
104
  bundle: Bundle,
86
- bundleGraph: BundleGraph,
105
+ bundleGraph: BundleGraph<NamedBundle>,
87
106
  contents: string,
88
107
  getInlineReplacement: (
89
108
  Dependency,
@@ -92,20 +111,22 @@ export async function replaceInlineReferences({
92
111
  ) => {|from: string, to: string|},
93
112
  getInlineBundleContents: (
94
113
  Bundle,
95
- BundleGraph,
96
- ) => Async<{|contents: Blob, map: ?(Readable | string)|}>,
114
+ BundleGraph<NamedBundle>,
115
+ ) => Async<{|contents: Blob|}>,
97
116
  map?: ?SourceMap,
98
117
  |}): Promise<{|+contents: string, +map: ?SourceMap|}> {
99
118
  let replacements = new Map();
100
119
 
101
- for (let dependency of bundleGraph.getExternalDependencies(bundle)) {
102
- let bundleGroup = bundleGraph.resolveExternalDependency(dependency);
103
- if (bundleGroup == null) {
104
- continue;
120
+ let dependencies = [];
121
+ bundle.traverse(node => {
122
+ if (node.type === 'dependency') {
123
+ dependencies.push(node.value);
105
124
  }
125
+ });
106
126
 
107
- let [entryBundle] = bundleGraph.getBundlesInBundleGroup(bundleGroup);
108
- if (!entryBundle.isInline) {
127
+ for (let dependency of dependencies) {
128
+ let entryBundle = bundleGraph.getReferencedBundle(dependency, bundle);
129
+ if (entryBundle?.bundleBehavior !== 'inline') {
109
130
  continue;
110
131
  }
111
132
 
@@ -113,15 +134,18 @@ export async function replaceInlineReferences({
113
134
  entryBundle,
114
135
  bundleGraph,
115
136
  );
116
- let packagedContents = (packagedBundle.contents instanceof Readable
117
- ? await bufferStream(packagedBundle.contents)
118
- : packagedBundle.contents
137
+ let packagedContents = (
138
+ packagedBundle.contents instanceof Readable
139
+ ? await bufferStream(packagedBundle.contents)
140
+ : packagedBundle.contents
119
141
  ).toString();
120
142
 
121
143
  let inlineType = nullthrows(entryBundle.getMainEntry()).meta.inlineType;
122
144
  if (inlineType == null || inlineType === 'string') {
145
+ let placeholder = dependency.meta?.placeholder ?? dependency.id;
146
+ invariant(typeof placeholder === 'string');
123
147
  replacements.set(
124
- dependency.id,
148
+ placeholder,
125
149
  getInlineReplacement(dependency, inlineType, packagedContents),
126
150
  );
127
151
  }
@@ -130,32 +154,52 @@ export async function replaceInlineReferences({
130
154
  return performReplacement(replacements, contents, map);
131
155
  }
132
156
 
133
- function getURLReplacement({
157
+ export function getURLReplacement({
134
158
  dependency,
135
159
  fromBundle,
136
160
  toBundle,
137
161
  relative,
162
+ getReplacement,
138
163
  }: {|
139
164
  dependency: Dependency,
140
- fromBundle: Bundle,
141
- toBundle: Bundle,
165
+ fromBundle: NamedBundle,
166
+ toBundle: NamedBundle,
142
167
  relative: boolean,
143
- |}) {
144
- let url = URL.parse(dependency.moduleSpecifier);
168
+ getReplacement?: string => string,
169
+ |}): {|from: string, to: string|} {
145
170
  let to;
171
+
172
+ let orig = URL.parse(dependency.specifier);
173
+
146
174
  if (relative) {
147
- url.pathname = relativeBundlePath(fromBundle, toBundle, {
148
- leadingDotSlash: false,
175
+ to = URL.format({
176
+ pathname: relativeBundlePath(fromBundle, toBundle, {
177
+ leadingDotSlash: false,
178
+ }),
179
+ hash: orig.hash,
149
180
  });
150
- to = URL.format(url);
181
+
182
+ // If the resulting path includes a colon character and doesn't start with a ./ or ../
183
+ // we need to add one so that the first part before the colon isn't parsed as a URL protocol.
184
+ if (to.includes(':') && !to.startsWith('./') && !to.startsWith('../')) {
185
+ to = './' + to;
186
+ }
151
187
  } else {
152
- url.pathname = nullthrows(toBundle.name);
153
- to = urlJoin(toBundle.target.publicUrl, URL.format(url));
188
+ to = urlJoin(
189
+ toBundle.target.publicUrl,
190
+ URL.format({
191
+ pathname: nullthrows(toBundle.name),
192
+ hash: orig.hash,
193
+ }),
194
+ );
154
195
  }
155
196
 
197
+ let placeholder = dependency.meta?.placeholder ?? dependency.id;
198
+ invariant(typeof placeholder === 'string');
199
+
156
200
  return {
157
- from: dependency.id,
158
- to,
201
+ from: placeholder,
202
+ to: getReplacement ? getReplacement(to) : to,
159
203
  };
160
204
  }
161
205
 
package/src/schema.js CHANGED
@@ -1,15 +1,19 @@
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
+ import * as levenshtein from 'fastest-levenshtein';
7
10
 
8
11
  export type SchemaEntity =
9
12
  | SchemaObject
10
13
  | SchemaArray
11
14
  | SchemaBoolean
12
15
  | SchemaString
16
+ | SchemaNumber
13
17
  | SchemaEnum
14
18
  | SchemaOneOf
15
19
  | SchemaAllOf
@@ -40,6 +44,11 @@ export type SchemaString = {|
40
44
  __validate?: (val: string) => ?string,
41
45
  __type?: string,
42
46
  |};
47
+ export type SchemaNumber = {|
48
+ type: 'number',
49
+ enum?: Array<number>,
50
+ __type?: string,
51
+ |};
43
52
  export type SchemaEnum = {|
44
53
  enum: Array<mixed>,
45
54
  |};
@@ -88,7 +97,7 @@ export type SchemaError =
88
97
  prop: string,
89
98
  expectedProps: Array<string>,
90
99
  actualProps: Array<string>,
91
- dataType: null | 'key',
100
+ dataType: 'key' | 'value',
92
101
 
93
102
  dataPath: string,
94
103
  ancestors: Array<SchemaEntity>,
@@ -99,7 +108,6 @@ export type SchemaError =
99
108
  actualValue: mixed,
100
109
  dataType: ?'key' | 'value',
101
110
  message?: string,
102
-
103
111
  dataPath: string,
104
112
  ancestors: Array<SchemaEntity>,
105
113
  |};
@@ -159,8 +167,7 @@ function validateSchema(schema: SchemaEntity, data: mixed): Array<SchemaError> {
159
167
  }
160
168
  } else if (schemaNode.__validate) {
161
169
  let validationError = schemaNode.__validate(value);
162
- // $FlowFixMe
163
- if (validationError) {
170
+ if (typeof validationError == 'string') {
164
171
  return {
165
172
  type: 'other',
166
173
  dataType: 'value',
@@ -173,6 +180,23 @@ function validateSchema(schema: SchemaEntity, data: mixed): Array<SchemaError> {
173
180
  }
174
181
  break;
175
182
  }
183
+ case 'number': {
184
+ // $FlowFixMe type was already checked
185
+ let value: number = dataNode;
186
+ if (schemaNode.enum) {
187
+ if (!schemaNode.enum.includes(value)) {
188
+ return {
189
+ type: 'enum',
190
+ dataType: 'value',
191
+ dataPath,
192
+ expectedValues: schemaNode.enum,
193
+ actualValue: value,
194
+ ancestors: schemaAncestors,
195
+ };
196
+ }
197
+ }
198
+ break;
199
+ }
176
200
  case 'object': {
177
201
  let results: Array<Array<SchemaError> | SchemaError> = [];
178
202
  let invalidProps;
@@ -187,7 +211,7 @@ function validateSchema(schema: SchemaEntity, data: mixed): Array<SchemaError> {
187
211
  k =>
188
212
  ({
189
213
  type: 'forbidden-prop',
190
- dataPath: dataPath + '/' + k,
214
+ dataPath: dataPath + '/' + encodeJSONKeyComponent(k),
191
215
  dataType: 'key',
192
216
  prop: k,
193
217
  expectedProps: Object.keys(schemaNode.properties),
@@ -209,7 +233,7 @@ function validateSchema(schema: SchemaEntity, data: mixed): Array<SchemaError> {
209
233
  ({
210
234
  type: 'missing-prop',
211
235
  dataPath,
212
- dataType: null,
236
+ dataType: 'value',
213
237
  prop: k,
214
238
  expectedProps: schemaNode.required,
215
239
  actualProps: keys,
@@ -230,7 +254,7 @@ function validateSchema(schema: SchemaEntity, data: mixed): Array<SchemaError> {
230
254
  [schemaNode.properties[k]].concat(schemaAncestors),
231
255
  // $FlowFixMe type was already checked
232
256
  dataNode[k],
233
- dataPath + '/' + k,
257
+ dataPath + '/' + encodeJSONKeyComponent(k),
234
258
  );
235
259
  if (result) results.push(result);
236
260
  } else {
@@ -239,7 +263,7 @@ function validateSchema(schema: SchemaEntity, data: mixed): Array<SchemaError> {
239
263
  results.push({
240
264
  type: 'enum',
241
265
  dataType: 'key',
242
- dataPath: dataPath + '/' + k,
266
+ dataPath: dataPath + '/' + encodeJSONKeyComponent(k),
243
267
  expectedValues: Object.keys(
244
268
  schemaNode.properties,
245
269
  ).filter(
@@ -256,7 +280,7 @@ function validateSchema(schema: SchemaEntity, data: mixed): Array<SchemaError> {
256
280
  [additionalProperties].concat(schemaAncestors),
257
281
  // $FlowFixMe type was already checked
258
282
  dataNode[k],
259
- dataPath + '/' + k,
283
+ dataPath + '/' + encodeJSONKeyComponent(k),
260
284
  );
261
285
  if (result) results.push(result);
262
286
  }
@@ -339,9 +363,12 @@ function validateSchema(schema: SchemaEntity, data: mixed): Array<SchemaError> {
339
363
  }
340
364
  export default validateSchema;
341
365
 
342
- function fuzzySearch(expectedValues: Array<string>, actualValue: string) {
366
+ export function fuzzySearch(
367
+ expectedValues: Array<string>,
368
+ actualValue: string,
369
+ ): Array<string> {
343
370
  let result = expectedValues
344
- .map(exp => [exp, levenshteinDistance(exp, actualValue)])
371
+ .map(exp => [exp, levenshtein.distance(exp, actualValue)])
345
372
  .filter(
346
373
  // Remove if more than half of the string would need to be changed
347
374
  ([, d]) => d * 2 < actualValue.length,
@@ -350,22 +377,43 @@ function fuzzySearch(expectedValues: Array<string>, actualValue: string) {
350
377
  return result.map(([v]) => v);
351
378
  }
352
379
 
353
- validateSchema.diagnostic = function(
380
+ validateSchema.diagnostic = function (
354
381
  schema: SchemaEntity,
355
- data: mixed,
356
- dataContentsPath?: ?string,
357
- dataContents: string | mixed,
382
+ data: {|
383
+ ...
384
+ | {|
385
+ source?: ?string,
386
+ data?: mixed,
387
+ |}
388
+ | {|
389
+ source: string,
390
+ map: {|
391
+ data: mixed,
392
+ pointers: {|[key: string]: Mapping|},
393
+ |},
394
+ |},
395
+ filePath?: ?string,
396
+ prependKey?: ?string,
397
+ |},
358
398
  origin: string,
359
- prependKey: string,
360
399
  message: string,
361
400
  ): void {
362
- let errors = validateSchema(schema, data);
401
+ if (
402
+ 'source' in data &&
403
+ 'data' in data &&
404
+ typeof data.source !== 'string' &&
405
+ !data
406
+ ) {
407
+ throw new Error(
408
+ 'At least one of data.source and data.data must be defined!',
409
+ );
410
+ }
411
+ let object = data.map
412
+ ? data.map.data
413
+ : // $FlowFixMe we can assume it's a JSON object
414
+ data.data ?? JSON.parse(data.source);
415
+ let errors = validateSchema(schema, object);
363
416
  if (errors.length) {
364
- let dataContentsString: string =
365
- typeof dataContents === 'string'
366
- ? dataContents
367
- : // $FlowFixMe
368
- JSON.stringify(dataContents, null, '\t');
369
417
  let keys = errors.map(e => {
370
418
  let message;
371
419
  if (e.type === 'enum') {
@@ -403,10 +451,8 @@ validateSchema.diagnostic = function(
403
451
  let {prop, actualProps} = e;
404
452
  let likely = fuzzySearch(actualProps, prop);
405
453
  if (likely.length > 0) {
406
- message = `Did you mean ${likely
407
- .map(v => JSON.stringify(v))
408
- .join(', ')}?`;
409
- e.dataPath += '/' + prop;
454
+ message = `Did you mean ${JSON.stringify(prop)}?`;
455
+ e.dataPath += '/' + likely[0];
410
456
  e.dataType = 'key';
411
457
  } else {
412
458
  message = `Missing property ${prop}`;
@@ -422,26 +468,36 @@ validateSchema.diagnostic = function(
422
468
  }
423
469
  return {key: e.dataPath, type: e.dataType, message};
424
470
  });
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
- };
471
+ let map, code;
472
+ if (data.map) {
473
+ map = data.map;
474
+ code = data.source;
475
+ } else {
476
+ // $FlowFixMe we can assume that data is valid JSON
477
+ map = data.source ?? JSON.stringify(nullthrows(data.data), 0, '\t');
478
+ code = map;
479
+ }
480
+ let codeFrames = [
481
+ {
482
+ filePath: data.filePath ?? undefined,
483
+ language: 'json',
484
+ code,
485
+ codeHighlights: generateJSONCodeHighlights(
486
+ map,
487
+ keys.map(({key, type, message}) => ({
488
+ key: (data.prependKey ?? '') + key,
489
+ type: type,
490
+ message: message != null ? escapeMarkdown(message) : message,
491
+ })),
492
+ ),
493
+ },
494
+ ];
436
495
 
437
496
  throw new ThrowableDiagnostic({
438
497
  diagnostic: {
439
- message,
498
+ message: message,
440
499
  origin,
441
- // $FlowFixMe should be a sketchy string check
442
- filePath: dataContentsPath || undefined,
443
- language: 'json',
444
- codeFrame,
500
+ codeFrames,
445
501
  },
446
502
  });
447
503
  }
@@ -0,0 +1,23 @@
1
+ // @flow
2
+
3
+ export let SharedBuffer: Class<ArrayBuffer> | Class<SharedArrayBuffer>;
4
+
5
+ // $FlowFixMe[prop-missing]
6
+ if (process.browser) {
7
+ SharedBuffer = ArrayBuffer;
8
+ // Safari has removed the constructor
9
+ if (typeof SharedArrayBuffer !== 'undefined') {
10
+ let channel = new MessageChannel();
11
+ try {
12
+ // Firefox might throw when sending the Buffer over a MessagePort
13
+ channel.port1.postMessage(new SharedArrayBuffer(0));
14
+ SharedBuffer = SharedArrayBuffer;
15
+ } catch (_) {
16
+ // NOOP
17
+ }
18
+ channel.port1.close();
19
+ channel.port2.close();
20
+ }
21
+ } else {
22
+ SharedBuffer = SharedArrayBuffer;
23
+ }
@@ -0,0 +1,138 @@
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 - 1);
98
+ let end = originalMap.findClosestMapping(endLine, endCol - 1);
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++; // source map columns are 0-based
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
+ } else if (endLine === startLine && startCol === endCol && lineDiff === 0) {
119
+ // Prevent 0-length ranges
120
+ endCol = startCol + 1;
121
+ }
122
+ } else {
123
+ endLine = startLine;
124
+ endCol = startCol;
125
+ }
126
+
127
+ return {
128
+ filePath,
129
+ start: {
130
+ line: startLine,
131
+ column: startCol,
132
+ },
133
+ end: {
134
+ line: endLine,
135
+ column: endCol,
136
+ },
137
+ };
138
+ }
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
+ }