@parcel/utils 2.0.0-beta.3 → 2.0.0-dev.1510

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 (79) hide show
  1. package/lib/index.js +37516 -542
  2. package/lib/index.js.map +1 -0
  3. package/package.json +41 -18
  4. package/src/DefaultMap.js +1 -1
  5. package/src/PromiseQueue.js +13 -0
  6. package/src/alternatives.js +21 -6
  7. package/src/ansi-html.js +1 -1
  8. package/src/blob.js +1 -0
  9. package/src/collection.js +35 -3
  10. package/src/config.js +77 -22
  11. package/src/debounce.js +1 -1
  12. package/src/dependency-location.js +5 -5
  13. package/src/getExisting.js +1 -4
  14. package/src/getModuleParts.js +23 -0
  15. package/src/glob.js +26 -3
  16. package/src/hash.js +49 -0
  17. package/src/http-server.js +19 -7
  18. package/src/index.js +25 -10
  19. package/src/path.js +11 -1
  20. package/src/prettyDiagnostic.js +90 -40
  21. package/src/progress-message.js +22 -0
  22. package/src/replaceBundleReferences.js +49 -25
  23. package/src/schema.js +20 -19
  24. package/src/shared-buffer.js +23 -0
  25. package/src/sourcemap.js +13 -7
  26. package/src/urlJoin.js +3 -1
  27. package/test/DefaultMap.test.js +7 -4
  28. package/test/PromiseQueue.test.js +28 -0
  29. package/test/collection.test.js +13 -1
  30. package/test/config.test.js +98 -0
  31. package/test/input/config/.testrc +3 -0
  32. package/test/input/config/config.cjs +3 -0
  33. package/test/input/config/config.js +3 -0
  34. package/test/input/config/config.json +3 -0
  35. package/test/input/config/empty.json +0 -0
  36. package/test/input/config/empty.toml +0 -0
  37. package/test/replaceBundleReferences.test.js +88 -4
  38. package/test/throttle.test.js +1 -1
  39. package/test/urlJoin.test.js +11 -0
  40. package/lib/DefaultMap.js +0 -64
  41. package/lib/Deferred.js +0 -34
  42. package/lib/PromiseQueue.js +0 -144
  43. package/lib/TapStream.js +0 -46
  44. package/lib/alternatives.js +0 -151
  45. package/lib/ansi-html.js +0 -32
  46. package/lib/blob.js +0 -47
  47. package/lib/bundle-url.js +0 -43
  48. package/lib/collection.js +0 -51
  49. package/lib/config.js +0 -159
  50. package/lib/countLines.js +0 -18
  51. package/lib/debounce.js +0 -20
  52. package/lib/dependency-location.js +0 -21
  53. package/lib/escape-html.js +0 -24
  54. package/lib/generateBuildMetrics.js +0 -156
  55. package/lib/generateCertificate.js +0 -149
  56. package/lib/getCertificate.js +0 -19
  57. package/lib/getExisting.js +0 -31
  58. package/lib/getRootDir.js +0 -74
  59. package/lib/glob.js +0 -118
  60. package/lib/http-server.js +0 -110
  61. package/lib/is-url.js +0 -27
  62. package/lib/isDirectoryInside.js +0 -24
  63. package/lib/md5.js +0 -61
  64. package/lib/objectHash.js +0 -34
  65. package/lib/openInBrowser.js +0 -94
  66. package/lib/parseCSSImport.js +0 -16
  67. package/lib/path.js +0 -44
  68. package/lib/prettifyTime.js +0 -10
  69. package/lib/prettyDiagnostic.js +0 -119
  70. package/lib/relativeBundlePath.js +0 -38
  71. package/lib/relativeUrl.js +0 -32
  72. package/lib/replaceBundleReferences.js +0 -184
  73. package/lib/schema.js +0 -391
  74. package/lib/sourcemap.js +0 -155
  75. package/lib/stream.js +0 -86
  76. package/lib/throttle.js +0 -16
  77. package/lib/urlJoin.js +0 -43
  78. package/src/.babelrc +0 -3
  79. package/src/md5.js +0 -56
package/src/index.js CHANGED
@@ -1,14 +1,17 @@
1
1
  // @flow strict-local
2
2
  export type * from './config';
3
+ export type * from './Deferred';
3
4
  export type * from './generateBuildMetrics';
5
+ export type * from './http-server';
6
+ export type * from './path';
4
7
  export type * from './prettyDiagnostic';
5
8
  export type * from './schema';
6
- export type * from './http-server';
7
9
 
8
10
  export {default as countLines} from './countLines';
9
11
  export {default as generateBuildMetrics} from './generateBuildMetrics';
10
12
  export {default as generateCertificate} from './generateCertificate';
11
13
  export {default as getCertificate} from './getCertificate';
14
+ export {default as getModuleParts} from './getModuleParts';
12
15
  export {default as getRootDir} from './getRootDir';
13
16
  export {default as isDirectoryInside} from './isDirectoryInside';
14
17
  export {default as isURL} from './is-url';
@@ -33,21 +36,32 @@ export {
33
36
  objectSortedEntries,
34
37
  objectSortedEntriesDeep,
35
38
  setDifference,
39
+ setEqual,
40
+ setIntersect,
41
+ setUnion,
36
42
  } from './collection';
37
- export {resolveConfig, resolveConfigSync, loadConfig} from './config';
43
+ export {
44
+ resolveConfig,
45
+ resolveConfigSync,
46
+ loadConfig,
47
+ readConfig,
48
+ } from './config';
38
49
  export {DefaultMap, DefaultWeakMap} from './DefaultMap';
39
50
  export {makeDeferredWithPromise} from './Deferred';
40
- export {isGlob, isGlobMatch, globSync, glob} from './glob';
51
+ export {getProgressMessage} from './progress-message.js';
41
52
  export {
42
- md5FromString,
43
- md5FromReadableStream,
44
- md5FromObject,
45
- md5FromOrderedObject,
46
- md5FromFilePath,
47
- } from './md5';
53
+ isGlob,
54
+ isGlobMatch,
55
+ globMatch,
56
+ globSync,
57
+ glob,
58
+ globToRegex,
59
+ } from './glob';
60
+ export {hashStream, hashObject, hashFile} from './hash';
61
+ export {SharedBuffer} from './shared-buffer';
48
62
  export {fuzzySearch} from './schema';
49
63
  export {createHTTPServer} from './http-server';
50
- export {normalizeSeparators, normalizePath, relativePath} from './path';
64
+ export {normalizePath, normalizeSeparators, relativePath} from './path';
51
65
  export {
52
66
  replaceURLReferences,
53
67
  replaceInlineReferences,
@@ -71,3 +85,4 @@ export {
71
85
  loadSourceMap,
72
86
  remapSourceLocation,
73
87
  } from './sourcemap';
88
+ export {default as stripAnsi} from 'strip-ansi';
package/src/path.js CHANGED
@@ -22,7 +22,12 @@ export function normalizePath(
22
22
  filePath: FilePath,
23
23
  leadingDotSlash: boolean = true,
24
24
  ): FilePath {
25
- if (leadingDotSlash && filePath[0] !== '.' && filePath[0] !== '/') {
25
+ if (
26
+ leadingDotSlash &&
27
+ (filePath[0] !== '.' ||
28
+ (filePath[1] !== '.' && filePath[1] !== '/' && filePath[1] !== '\\')) &&
29
+ !path.isAbsolute(filePath)
30
+ ) {
26
31
  return normalizeSeparators('./' + filePath);
27
32
  } else {
28
33
  return normalizeSeparators(filePath);
@@ -34,5 +39,10 @@ export function relativePath(
34
39
  to: string,
35
40
  leadingDotSlash: boolean = true,
36
41
  ): FilePath {
42
+ // Fast path
43
+ if (to.startsWith(from + '/')) {
44
+ return (leadingDotSlash ? './' : '') + to.slice(from.length + 1);
45
+ }
46
+
37
47
  return normalizePath(path.relative(from, to), leadingDotSlash);
38
48
  }
@@ -3,86 +3,136 @@ import type {Diagnostic} from '@parcel/diagnostic';
3
3
  import type {PluginOptions} from '@parcel/types';
4
4
 
5
5
  import formatCodeFrame from '@parcel/codeframe';
6
- import mdAnsi from '@parcel/markdown-ansi';
7
- import chalk from 'chalk';
6
+ import _mdAnsi from '@parcel/markdown-ansi';
7
+ import _chalk from 'chalk';
8
8
  import path from 'path';
9
- import nullthrows from 'nullthrows';
9
+ // $FlowFixMe
10
+ import _terminalLink from 'terminal-link';
11
+
12
+ /* eslint-disable import/no-extraneous-dependencies */
13
+ // $FlowFixMe
14
+ import snarkdown from 'snarkdown';
15
+ /* eslint-enable import/no-extraneous-dependencies */
16
+
17
+ export type FormattedCodeFrame = {|
18
+ location: string,
19
+ code: string,
20
+ |};
10
21
 
11
22
  export type AnsiDiagnosticResult = {|
12
23
  message: string,
13
24
  stack: string,
25
+ /** A formatted string containing all code frames, including their file locations. */
14
26
  codeframe: string,
27
+ /** A list of code frames with highlighted code and file locations separately. */
28
+ frames: Array<FormattedCodeFrame>,
15
29
  hints: Array<string>,
30
+ documentation: string,
16
31
  |};
17
32
 
18
33
  export default async function prettyDiagnostic(
19
34
  diagnostic: Diagnostic,
20
35
  options?: PluginOptions,
21
36
  terminalWidth?: number,
37
+ format: 'ansi' | 'html' = 'ansi',
22
38
  ): Promise<AnsiDiagnosticResult> {
23
39
  let {
24
40
  origin,
25
41
  message,
26
42
  stack,
27
- codeFrame,
43
+ codeFrames,
28
44
  hints,
29
- filePath,
30
- language,
31
45
  skipFormatting,
46
+ documentationURL,
32
47
  } = diagnostic;
33
48
 
34
- if (filePath != null && options && !path.isAbsolute(filePath)) {
35
- filePath = path.join(options.projectRoot, filePath);
36
- }
49
+ const md = format === 'ansi' ? _mdAnsi : snarkdown;
50
+ const terminalLink =
51
+ format === 'ansi'
52
+ ? _terminalLink
53
+ : // eslint-disable-next-line no-unused-vars
54
+ (text, url, _) => `<a href="${url}">${text}</a>`;
55
+ const chalk =
56
+ format === 'ansi'
57
+ ? _chalk
58
+ : {
59
+ gray: {
60
+ underline: v =>
61
+ `<span style="color: grey; text-decoration: underline;">${v}</span>`,
62
+ },
63
+ };
37
64
 
38
65
  let result = {
39
66
  message:
40
- mdAnsi(`**${origin ?? 'unknown'}**: `) +
41
- (skipFormatting ? message : mdAnsi(message)),
67
+ md(`**${origin ?? 'unknown'}**: `) +
68
+ (skipFormatting ? message : md(message)),
42
69
  stack: '',
43
70
  codeframe: '',
71
+ frames: [],
44
72
  hints: [],
73
+ documentation: '',
45
74
  };
46
75
 
47
- if (codeFrame !== undefined) {
48
- let highlights = codeFrame.codeHighlights;
49
- let code =
50
- codeFrame.code ??
51
- (options &&
52
- (await options.inputFS.readFile(nullthrows(filePath), 'utf8')));
53
-
54
- let formattedCodeFrame = '';
55
- if (code != null) {
56
- formattedCodeFrame = formatCodeFrame(code, highlights, {
57
- useColor: true,
58
- syntaxHighlighting: true,
59
- language:
60
- // $FlowFixMe sketchy null checks do not matter here...
61
- language || (filePath ? path.extname(filePath).substr(1) : undefined),
62
- terminalWidth,
76
+ if (codeFrames != null) {
77
+ for (let codeFrame of codeFrames) {
78
+ let filePath = codeFrame.filePath;
79
+ if (filePath != null && options && !path.isAbsolute(filePath)) {
80
+ filePath = path.join(options.projectRoot, filePath);
81
+ }
82
+
83
+ let highlights = codeFrame.codeHighlights;
84
+ let code = codeFrame.code;
85
+ if (code == null && options && filePath != null) {
86
+ code = await options.inputFS.readFile(filePath, 'utf8');
87
+ }
88
+
89
+ let formattedCodeFrame = '';
90
+ if (code != null) {
91
+ formattedCodeFrame = formatCodeFrame(code, highlights, {
92
+ useColor: true,
93
+ syntaxHighlighting: true,
94
+ language:
95
+ // $FlowFixMe sketchy null checks do not matter here...
96
+ codeFrame.language ||
97
+ (filePath != null ? path.extname(filePath).substr(1) : undefined),
98
+ terminalWidth,
99
+ });
100
+ }
101
+
102
+ let location;
103
+ if (typeof filePath !== 'string') {
104
+ location = '';
105
+ } else if (highlights.length === 0) {
106
+ location = filePath;
107
+ } else {
108
+ location = `${filePath}:${highlights[0].start.line}:${highlights[0].start.column}`;
109
+ }
110
+ result.codeframe += location ? chalk.gray.underline(location) + '\n' : '';
111
+ result.codeframe += formattedCodeFrame;
112
+ if (codeFrame !== codeFrames[codeFrames.length - 1]) {
113
+ result.codeframe += '\n\n';
114
+ }
115
+
116
+ result.frames.push({
117
+ location,
118
+ code: formattedCodeFrame,
63
119
  });
64
120
  }
65
-
66
- result.codeframe +=
67
- typeof filePath !== 'string'
68
- ? ''
69
- : chalk.underline(
70
- `${filePath}:${highlights[0].start.line}:${highlights[0].start.column}\n`,
71
- );
72
- result.codeframe += formattedCodeFrame;
73
- } else if (typeof filePath === 'string') {
74
- result.codeframe += chalk.underline(filePath);
75
121
  }
76
122
 
77
123
  if (stack != null) {
78
124
  result.stack = stack;
79
- } else if (filePath != null && result.codeframe == null) {
80
- result.stack = filePath;
81
125
  }
82
126
 
83
127
  if (Array.isArray(hints) && hints.length) {
84
128
  result.hints = hints.map(h => {
85
- return mdAnsi(h);
129
+ return md(h);
130
+ });
131
+ }
132
+
133
+ if (documentationURL != null) {
134
+ result.documentation = terminalLink('Learn more', documentationURL, {
135
+ fallback: (text, url) => `${text}: ${url}`,
86
136
  });
87
137
  }
88
138
 
@@ -0,0 +1,22 @@
1
+ // @flow strict-local
2
+ import type {BuildProgressEvent} from '@parcel/types';
3
+
4
+ import path from 'path';
5
+
6
+ export function getProgressMessage(event: BuildProgressEvent): ?string {
7
+ switch (event.phase) {
8
+ case 'transforming':
9
+ return `Building ${path.basename(event.filePath)}...`;
10
+
11
+ case 'bundling':
12
+ return 'Bundling...';
13
+
14
+ case 'packaging':
15
+ return `Packaging ${event.bundle.displayName}...`;
16
+
17
+ case 'optimizing':
18
+ return `Optimizing ${event.bundle.displayName}...`;
19
+ }
20
+
21
+ return null;
22
+ }
@@ -12,6 +12,7 @@ import type {
12
12
 
13
13
  import {Readable} from 'stream';
14
14
  import nullthrows from 'nullthrows';
15
+ import invariant from 'assert';
15
16
  import URL from 'url';
16
17
  import {bufferStream, relativeBundlePath, urlJoin} from './';
17
18
 
@@ -22,7 +23,7 @@ type ReplacementMap = Map<
22
23
 
23
24
  /*
24
25
  * Replaces references to dependency ids for URL dependencies with:
25
- * - in the case of an unresolvable url dependency, the original moduleSpecifier.
26
+ * - in the case of an unresolvable url dependency, the original specifier.
26
27
  * These are external requests that Parcel did not bundle.
27
28
  * - in the case of a reference to another bundle, the relative url to that
28
29
  * bundle from the current bundle.
@@ -32,6 +33,7 @@ export function replaceURLReferences({
32
33
  bundleGraph,
33
34
  contents,
34
35
  map,
36
+ getReplacement = s => s,
35
37
  relative = true,
36
38
  }: {|
37
39
  bundle: NamedBundle,
@@ -39,42 +41,47 @@ export function replaceURLReferences({
39
41
  contents: string,
40
42
  relative?: boolean,
41
43
  map?: ?SourceMap,
44
+ getReplacement?: string => string,
42
45
  |}): {|+contents: string, +map: ?SourceMap|} {
43
46
  let replacements = new Map();
44
47
  let urlDependencies = [];
45
48
  bundle.traverse(node => {
46
- if (node.type === 'dependency' && node.value.isURL) {
49
+ if (node.type === 'dependency' && node.value.specifierType === 'url') {
47
50
  urlDependencies.push(node.value);
48
51
  }
49
52
  });
50
53
 
51
54
  for (let dependency of urlDependencies) {
52
- if (!dependency.isURL) {
55
+ if (dependency.specifierType !== 'url') {
53
56
  continue;
54
57
  }
55
58
 
59
+ let placeholder = dependency.meta?.placeholder ?? dependency.id;
60
+ invariant(typeof placeholder === 'string');
61
+
56
62
  let resolved = bundleGraph.getReferencedBundle(dependency, bundle);
57
63
  if (resolved == null) {
58
- replacements.set(dependency.id, {
59
- from: dependency.id,
60
- to: dependency.moduleSpecifier,
64
+ replacements.set(placeholder, {
65
+ from: placeholder,
66
+ to: getReplacement(dependency.specifier),
61
67
  });
62
68
  continue;
63
69
  }
64
70
 
65
- if (!resolved || resolved.isInline) {
71
+ if (resolved.bundleBehavior === 'inline') {
66
72
  // If a bundle is inline, it should be replaced with inline contents,
67
73
  // not a URL.
68
74
  continue;
69
75
  }
70
76
 
71
77
  replacements.set(
72
- dependency.id,
78
+ placeholder,
73
79
  getURLReplacement({
74
80
  dependency,
75
81
  fromBundle: bundle,
76
82
  toBundle: resolved,
77
83
  relative,
84
+ getReplacement,
78
85
  }),
79
86
  );
80
87
  }
@@ -119,7 +126,7 @@ export async function replaceInlineReferences({
119
126
 
120
127
  for (let dependency of dependencies) {
121
128
  let entryBundle = bundleGraph.getReferencedBundle(dependency, bundle);
122
- if (!entryBundle?.isInline) {
129
+ if (entryBundle?.bundleBehavior !== 'inline') {
123
130
  continue;
124
131
  }
125
132
 
@@ -127,16 +134,18 @@ export async function replaceInlineReferences({
127
134
  entryBundle,
128
135
  bundleGraph,
129
136
  );
130
- let packagedContents = (packagedBundle.contents instanceof Readable
131
- ? await bufferStream(packagedBundle.contents)
132
- : packagedBundle.contents
137
+ let packagedContents = (
138
+ packagedBundle.contents instanceof Readable
139
+ ? await bufferStream(packagedBundle.contents)
140
+ : packagedBundle.contents
133
141
  ).toString();
134
142
 
135
- let inlineType = nullthrows(entryBundle.getEntryAssets()[0]).meta
136
- .inlineType;
143
+ let inlineType = nullthrows(entryBundle.getMainEntry()).meta.inlineType;
137
144
  if (inlineType == null || inlineType === 'string') {
145
+ let placeholder = dependency.meta?.placeholder ?? dependency.id;
146
+ invariant(typeof placeholder === 'string');
138
147
  replacements.set(
139
- dependency.id,
148
+ placeholder,
140
149
  getInlineReplacement(dependency, inlineType, packagedContents),
141
150
  );
142
151
  }
@@ -150,32 +159,47 @@ export function getURLReplacement({
150
159
  fromBundle,
151
160
  toBundle,
152
161
  relative,
162
+ getReplacement,
153
163
  }: {|
154
164
  dependency: Dependency,
155
165
  fromBundle: NamedBundle,
156
166
  toBundle: NamedBundle,
157
167
  relative: boolean,
168
+ getReplacement?: string => string,
158
169
  |}): {|from: string, to: string|} {
159
170
  let to;
160
171
 
172
+ let orig = URL.parse(dependency.specifier);
173
+
161
174
  if (relative) {
162
- to = URL.format(
163
- URL.parse(
164
- relativeBundlePath(fromBundle, toBundle, {
165
- leadingDotSlash: false,
166
- }),
167
- ),
168
- );
175
+ to = URL.format({
176
+ pathname: relativeBundlePath(fromBundle, toBundle, {
177
+ leadingDotSlash: false,
178
+ }),
179
+ hash: orig.hash,
180
+ });
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
+ }
169
187
  } else {
170
188
  to = urlJoin(
171
189
  toBundle.target.publicUrl,
172
- URL.format(URL.parse(nullthrows(toBundle.name))),
190
+ URL.format({
191
+ pathname: nullthrows(toBundle.name),
192
+ hash: orig.hash,
193
+ }),
173
194
  );
174
195
  }
175
196
 
197
+ let placeholder = dependency.meta?.placeholder ?? dependency.id;
198
+ invariant(typeof placeholder === 'string');
199
+
176
200
  return {
177
- from: dependency.id,
178
- to,
201
+ from: placeholder,
202
+ to: getReplacement ? getReplacement(to) : to,
179
203
  };
180
204
  }
181
205
 
package/src/schema.js CHANGED
@@ -4,10 +4,9 @@ import ThrowableDiagnostic, {
4
4
  escapeMarkdown,
5
5
  encodeJSONKeyComponent,
6
6
  } from '@parcel/diagnostic';
7
- import type {Mapping} from 'json-source-map';
7
+ import type {Mapping} from '@mischnic/json-sourcemap';
8
8
  import nullthrows from 'nullthrows';
9
- // flowlint-next-line untyped-import:off
10
- import levenshtein from 'fastest-levenshtein';
9
+ import * as levenshtein from 'fastest-levenshtein';
11
10
 
12
11
  export type SchemaEntity =
13
12
  | SchemaObject
@@ -378,7 +377,7 @@ export function fuzzySearch(
378
377
  return result.map(([v]) => v);
379
378
  }
380
379
 
381
- validateSchema.diagnostic = function(
380
+ validateSchema.diagnostic = function (
382
381
  schema: SchemaEntity,
383
382
  data: {|
384
383
  ...
@@ -406,7 +405,7 @@ validateSchema.diagnostic = function(
406
405
  !data
407
406
  ) {
408
407
  throw new Error(
409
- 'At least one of data.source and data.source must be defined!',
408
+ 'At least one of data.source and data.data must be defined!',
410
409
  );
411
410
  }
412
411
  let object = data.map
@@ -478,25 +477,27 @@ validateSchema.diagnostic = function(
478
477
  map = data.source ?? JSON.stringify(nullthrows(data.data), 0, '\t');
479
478
  code = map;
480
479
  }
481
- let codeFrame = {
482
- code,
483
- codeHighlights: generateJSONCodeHighlights(
484
- map,
485
- keys.map(({key, type, message}) => ({
486
- key: (data.prependKey ?? '') + key,
487
- type: type,
488
- message: message != null ? escapeMarkdown(message) : message,
489
- })),
490
- ),
491
- };
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
+ ];
492
495
 
493
496
  throw new ThrowableDiagnostic({
494
497
  diagnostic: {
495
498
  message: message,
496
499
  origin,
497
- filePath: data.filePath ?? undefined,
498
- language: 'json',
499
- codeFrame,
500
+ codeFrames,
500
501
  },
501
502
  });
502
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
+ }
package/src/sourcemap.js CHANGED
@@ -5,15 +5,18 @@ import SourceMap from '@parcel/source-map';
5
5
  import path from 'path';
6
6
  import {normalizeSeparators, isAbsolute} from './path';
7
7
 
8
- export const SOURCEMAP_RE: RegExp = /(?:\/\*|\/\/)\s*[@#]\s*sourceMappingURL\s*=\s*([^\s*]+)(?:\s*\*\/)?\s*$/;
8
+ export const SOURCEMAP_RE: RegExp =
9
+ /(?:\/\*|\/\/)\s*[@#]\s*sourceMappingURL\s*=\s*([^\s*]+)(?:\s*\*\/)?\s*$/;
9
10
  const DATA_URL_RE = /^data:[^;]+(?:;charset=[^;]+)?;base64,(.*)/;
10
11
  export const SOURCEMAP_EXTENSIONS: Set<string> = new Set<string>([
12
+ 'css',
13
+ 'es',
14
+ 'es6',
11
15
  'js',
12
16
  'jsx',
13
17
  'mjs',
14
- 'es',
15
- 'es6',
16
- 'css',
18
+ 'ts',
19
+ 'tsx',
17
20
  ]);
18
21
 
19
22
  export function matchSourceMappingURL(
@@ -91,8 +94,8 @@ export function remapSourceLocation(
91
94
  } = loc;
92
95
  let lineDiff = endLine - startLine;
93
96
  let colDiff = endCol - startCol;
94
- let start = originalMap.findClosestMapping(startLine, startCol);
95
- let end = originalMap.findClosestMapping(endLine, endCol);
97
+ let start = originalMap.findClosestMapping(startLine, startCol - 1);
98
+ let end = originalMap.findClosestMapping(endLine, endCol - 1);
96
99
 
97
100
  if (start?.original) {
98
101
  if (start.source) {
@@ -105,13 +108,16 @@ export function remapSourceLocation(
105
108
 
106
109
  if (end?.original) {
107
110
  ({line: endLine, column: endCol} = end.original);
108
- endCol++;
111
+ endCol++; // source map columns are 0-based
109
112
 
110
113
  if (endLine < startLine) {
111
114
  endLine = startLine;
112
115
  endCol = startCol;
113
116
  } else if (endLine === startLine && endCol < startCol && lineDiff === 0) {
114
117
  endCol = startCol + colDiff;
118
+ } else if (endLine === startLine && startCol === endCol && lineDiff === 0) {
119
+ // Prevent 0-length ranges
120
+ endCol = startCol + 1;
115
121
  }
116
122
  } else {
117
123
  endLine = startLine;
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;
@@ -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],
@@ -3,6 +3,7 @@ import assert from 'assert';
3
3
  import randomInt from 'random-int';
4
4
 
5
5
  import PromiseQueue from '../src/PromiseQueue';
6
+ import sinon from 'sinon';
6
7
 
7
8
  describe('PromiseQueue', () => {
8
9
  it('run() should resolve when all async functions in queue have completed', async () => {
@@ -72,4 +73,31 @@ describe('PromiseQueue', () => {
72
73
 
73
74
  await queue.run();
74
75
  });
76
+
77
+ it('.add() should notify subscribers', async () => {
78
+ const queue = new PromiseQueue();
79
+
80
+ const subscribedFn = sinon.spy();
81
+ queue.subscribeToAdd(subscribedFn);
82
+
83
+ const promise = queue.add(() => Promise.resolve());
84
+ await queue.run();
85
+ await promise;
86
+
87
+ assert(subscribedFn.called);
88
+ });
89
+
90
+ it('.subscribeToAdd() should allow unsubscribing', async () => {
91
+ const queue = new PromiseQueue();
92
+
93
+ const subscribedFn = sinon.spy();
94
+ const unsubscribe = queue.subscribeToAdd(subscribedFn);
95
+ unsubscribe();
96
+
97
+ const promise = queue.add(() => Promise.resolve());
98
+ await queue.run();
99
+ await promise;
100
+
101
+ assert(!subscribedFn.called);
102
+ });
75
103
  });