@parcel/utils 2.0.0-nightly.97 → 2.0.0-nightly.982

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 (100) hide show
  1. package/.eslintrc.js +6 -6
  2. package/lib/DefaultMap.js +0 -8
  3. package/lib/Deferred.js +10 -2
  4. package/lib/PromiseQueue.js +21 -30
  5. package/lib/TapStream.js +10 -10
  6. package/lib/alternatives.js +134 -0
  7. package/lib/ansi-html.js +10 -2
  8. package/lib/blob.js +26 -8
  9. package/lib/collection.js +14 -11
  10. package/lib/config.js +145 -35
  11. package/lib/countLines.js +2 -2
  12. package/lib/dependency-location.js +3 -3
  13. package/lib/generateBuildMetrics.js +148 -0
  14. package/lib/generateCertificate.js +33 -8
  15. package/lib/getExisting.js +11 -3
  16. package/lib/getRootDir.js +18 -7
  17. package/lib/glob.js +53 -19
  18. package/lib/hash.js +44 -0
  19. package/lib/http-server.js +48 -10
  20. package/lib/index.js +298 -224
  21. package/lib/is-url.js +12 -2
  22. package/lib/isDirectoryInside.js +24 -0
  23. package/lib/objectHash.js +10 -2
  24. package/lib/openInBrowser.js +94 -0
  25. package/lib/path.js +33 -6
  26. package/lib/prettyDiagnostic.js +107 -25
  27. package/lib/relativeBundlePath.js +13 -7
  28. package/lib/relativeUrl.js +19 -3
  29. package/lib/replaceBundleReferences.js +96 -36
  30. package/lib/schema.js +104 -33
  31. package/lib/shared-buffer.js +31 -0
  32. package/lib/sourcemap.js +147 -0
  33. package/lib/stream.js +38 -3
  34. package/lib/urlJoin.js +25 -6
  35. package/package.json +27 -16
  36. package/src/DefaultMap.js +1 -1
  37. package/src/PromiseQueue.js +16 -12
  38. package/src/alternatives.js +143 -0
  39. package/src/ansi-html.js +2 -2
  40. package/src/blob.js +4 -3
  41. package/src/bundle-url.js +1 -1
  42. package/src/collection.js +14 -14
  43. package/src/config.js +100 -35
  44. package/src/countLines.js +5 -2
  45. package/src/debounce.js +1 -1
  46. package/src/dependency-location.js +11 -6
  47. package/src/generateBuildMetrics.js +158 -0
  48. package/src/generateCertificate.js +1 -1
  49. package/src/getCertificate.js +1 -1
  50. package/src/getExisting.js +1 -4
  51. package/src/getRootDir.js +1 -2
  52. package/src/glob.js +29 -11
  53. package/src/hash.js +34 -0
  54. package/src/http-server.js +10 -12
  55. package/src/index.js +52 -23
  56. package/src/is-url.js +1 -1
  57. package/src/isDirectoryInside.js +11 -0
  58. package/src/openInBrowser.js +64 -0
  59. package/src/path.js +38 -6
  60. package/src/prettyDiagnostic.js +58 -24
  61. package/src/relativeBundlePath.js +8 -13
  62. package/src/replaceBundleReferences.js +78 -40
  63. package/src/schema.js +101 -44
  64. package/src/shared-buffer.js +24 -0
  65. package/src/sourcemap.js +135 -0
  66. package/src/stream.js +31 -1
  67. package/src/urlJoin.js +3 -1
  68. package/test/DefaultMap.test.js +7 -4
  69. package/test/config.test.js +50 -0
  70. package/test/input/config/config.json +3 -0
  71. package/test/input/config/empty.json +0 -0
  72. package/test/input/config/empty.toml +0 -0
  73. package/test/input/sourcemap/referenced-min.js +2 -0
  74. package/test/input/sourcemap/referenced-min.js.map +6 -0
  75. package/test/input/sourcemap/source-root.js +2 -0
  76. package/test/input/sourcemap/source-root.js.map +7 -0
  77. package/test/objectHash.test.js +33 -0
  78. package/test/prettifyTime.test.js +17 -0
  79. package/test/replaceBundleReferences.test.js +268 -0
  80. package/test/sourcemap.test.js +207 -0
  81. package/test/throttle.test.js +1 -2
  82. package/test/urlJoin.test.js +37 -0
  83. package/lib/generateBundleReport.js +0 -38
  84. package/lib/loadSourceMapUrl.js +0 -33
  85. package/lib/md5.js +0 -35
  86. package/lib/prettyError.js +0 -43
  87. package/lib/promisify.js +0 -13
  88. package/lib/resolve.js +0 -93
  89. package/lib/serializeObject.js +0 -28
  90. package/src/.babelrc +0 -3
  91. package/src/generateBundleReport.js +0 -51
  92. package/src/loadSourceMapUrl.js +0 -33
  93. package/src/md5.js +0 -44
  94. package/src/prettyError.js +0 -54
  95. package/src/promisify.js +0 -13
  96. package/src/resolve.js +0 -123
  97. package/src/serializeObject.js +0 -22
  98. package/test/input/sourcemap/referenced.js +0 -7
  99. package/test/loadSourceMapUrl.test.js +0 -37
  100. package/test/prettyError.test.js +0 -104
@@ -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.
@@ -27,41 +35,49 @@ export function replaceURLReferences({
27
35
  map,
28
36
  relative = true,
29
37
  }: {|
30
- bundle: Bundle,
31
- bundleGraph: BundleGraph,
38
+ bundle: NamedBundle,
39
+ bundleGraph: BundleGraph<NamedBundle>,
32
40
  contents: string,
33
41
  relative?: boolean,
34
42
  map?: ?SourceMap,
35
43
  |}): {|+contents: string, +map: ?SourceMap|} {
36
44
  let replacements = new Map();
45
+ let urlDependencies = [];
46
+ bundle.traverse(node => {
47
+ if (node.type === 'dependency' && node.value.specifierType === 'url') {
48
+ urlDependencies.push(node.value);
49
+ }
50
+ });
37
51
 
38
- for (let dependency of bundleGraph.getExternalDependencies(bundle)) {
39
- if (!dependency.isURL) {
52
+ for (let dependency of urlDependencies) {
53
+ if (dependency.specifierType !== 'url') {
40
54
  continue;
41
55
  }
42
56
 
43
- let bundleGroup = bundleGraph.resolveExternalDependency(dependency);
44
- if (bundleGroup == null) {
45
- replacements.set(dependency.id, {
46
- from: dependency.id,
47
- to: dependency.moduleSpecifier,
57
+ let placeholder = dependency.meta?.placeholder ?? dependency.id;
58
+ invariant(typeof placeholder === 'string');
59
+
60
+ let resolved = bundleGraph.getReferencedBundle(dependency, bundle);
61
+ if (resolved == null) {
62
+ replacements.set(placeholder, {
63
+ from: placeholder,
64
+ to: dependency.specifier,
48
65
  });
49
66
  continue;
50
67
  }
51
68
 
52
- let [entryBundle] = bundleGraph.getBundlesInBundleGroup(bundleGroup);
53
- if (entryBundle.isInline) {
69
+ if (resolved.bundleBehavior === 'inline') {
54
70
  // If a bundle is inline, it should be replaced with inline contents,
55
71
  // not a URL.
56
72
  continue;
57
73
  }
58
74
 
59
75
  replacements.set(
60
- dependency.id,
76
+ placeholder,
61
77
  getURLReplacement({
62
78
  dependency,
63
79
  fromBundle: bundle,
64
- toBundle: entryBundle,
80
+ toBundle: resolved,
65
81
  relative,
66
82
  }),
67
83
  );
@@ -83,7 +99,7 @@ export async function replaceInlineReferences({
83
99
  getInlineBundleContents,
84
100
  }: {|
85
101
  bundle: Bundle,
86
- bundleGraph: BundleGraph,
102
+ bundleGraph: BundleGraph<NamedBundle>,
87
103
  contents: string,
88
104
  getInlineReplacement: (
89
105
  Dependency,
@@ -92,20 +108,22 @@ export async function replaceInlineReferences({
92
108
  ) => {|from: string, to: string|},
93
109
  getInlineBundleContents: (
94
110
  Bundle,
95
- BundleGraph,
96
- ) => Async<{|contents: Blob, map: ?(Readable | string)|}>,
111
+ BundleGraph<NamedBundle>,
112
+ ) => Async<{|contents: Blob|}>,
97
113
  map?: ?SourceMap,
98
114
  |}): Promise<{|+contents: string, +map: ?SourceMap|}> {
99
115
  let replacements = new Map();
100
116
 
101
- for (let dependency of bundleGraph.getExternalDependencies(bundle)) {
102
- let bundleGroup = bundleGraph.resolveExternalDependency(dependency);
103
- if (bundleGroup == null) {
104
- continue;
117
+ let dependencies = [];
118
+ bundle.traverse(node => {
119
+ if (node.type === 'dependency') {
120
+ dependencies.push(node.value);
105
121
  }
122
+ });
106
123
 
107
- let [entryBundle] = bundleGraph.getBundlesInBundleGroup(bundleGroup);
108
- if (!entryBundle.isInline) {
124
+ for (let dependency of dependencies) {
125
+ let entryBundle = bundleGraph.getReferencedBundle(dependency, bundle);
126
+ if (entryBundle?.bundleBehavior !== 'inline') {
109
127
  continue;
110
128
  }
111
129
 
@@ -113,15 +131,18 @@ export async function replaceInlineReferences({
113
131
  entryBundle,
114
132
  bundleGraph,
115
133
  );
116
- let packagedContents = (packagedBundle.contents instanceof Readable
117
- ? await bufferStream(packagedBundle.contents)
118
- : packagedBundle.contents
134
+ let packagedContents = (
135
+ packagedBundle.contents instanceof Readable
136
+ ? await bufferStream(packagedBundle.contents)
137
+ : packagedBundle.contents
119
138
  ).toString();
120
139
 
121
140
  let inlineType = nullthrows(entryBundle.getMainEntry()).meta.inlineType;
122
141
  if (inlineType == null || inlineType === 'string') {
142
+ let placeholder = dependency.meta?.placeholder ?? dependency.id;
143
+ invariant(typeof placeholder === 'string');
123
144
  replacements.set(
124
- dependency.id,
145
+ placeholder,
125
146
  getInlineReplacement(dependency, inlineType, packagedContents),
126
147
  );
127
148
  }
@@ -130,31 +151,48 @@ export async function replaceInlineReferences({
130
151
  return performReplacement(replacements, contents, map);
131
152
  }
132
153
 
133
- function getURLReplacement({
154
+ export function getURLReplacement({
134
155
  dependency,
135
156
  fromBundle,
136
157
  toBundle,
137
158
  relative,
138
159
  }: {|
139
160
  dependency: Dependency,
140
- fromBundle: Bundle,
141
- toBundle: Bundle,
161
+ fromBundle: NamedBundle,
162
+ toBundle: NamedBundle,
142
163
  relative: boolean,
143
- |}) {
144
- let url = URL.parse(dependency.moduleSpecifier);
164
+ |}): {|from: string, to: string|} {
145
165
  let to;
166
+
167
+ let orig = URL.parse(dependency.specifier);
168
+
146
169
  if (relative) {
147
- url.pathname = relativeBundlePath(fromBundle, toBundle, {
148
- leadingDotSlash: false,
170
+ to = URL.format({
171
+ pathname: relativeBundlePath(fromBundle, toBundle, {
172
+ leadingDotSlash: false,
173
+ }),
174
+ hash: orig.hash,
149
175
  });
150
- to = URL.format(url);
176
+
177
+ // If the resulting path includes a colon character and doesn't start with a ./ or ../
178
+ // we need to add one so that the first part before the colon isn't parsed as a URL protocol.
179
+ if (to.includes(':') && !to.startsWith('./') && !to.startsWith('../')) {
180
+ to = './' + to;
181
+ }
151
182
  } else {
152
- url.pathname = nullthrows(toBundle.name);
153
- to = urlJoin(nullthrows(toBundle.target.publicUrl), URL.format(url));
183
+ to = urlJoin(
184
+ toBundle.target.publicUrl,
185
+ URL.format({
186
+ pathname: nullthrows(toBundle.name),
187
+ hash: orig.hash,
188
+ }),
189
+ );
154
190
  }
155
191
 
192
+ let placeholder = dependency.meta?.placeholder ?? dependency.id;
193
+ invariant(typeof placeholder === 'string');
156
194
  return {
157
- from: dependency.id,
195
+ from: placeholder,
158
196
  to,
159
197
  };
160
198
  }
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 'json-source-map';
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.source 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;