@parcel/utils 2.0.0-beta.1 → 2.0.0-nightly.1002

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 (93) hide show
  1. package/.eslintrc.js +6 -6
  2. package/lib/index.js +35850 -337
  3. package/lib/index.js.map +1 -0
  4. package/package.json +42 -20
  5. package/src/DefaultMap.js +1 -1
  6. package/src/PromiseQueue.js +16 -12
  7. package/src/alternatives.js +143 -0
  8. package/src/ansi-html.js +2 -2
  9. package/src/blob.js +2 -1
  10. package/src/bundle-url.js +1 -1
  11. package/src/collection.js +14 -14
  12. package/src/config.js +93 -53
  13. package/src/countLines.js +5 -2
  14. package/src/debounce.js +1 -1
  15. package/src/dependency-location.js +11 -6
  16. package/src/generateBuildMetrics.js +5 -5
  17. package/src/generateCertificate.js +1 -1
  18. package/src/getCertificate.js +1 -1
  19. package/src/getExisting.js +1 -4
  20. package/src/getRootDir.js +1 -2
  21. package/src/glob.js +36 -5
  22. package/src/hash.js +34 -0
  23. package/src/http-server.js +4 -11
  24. package/src/index.js +47 -20
  25. package/src/is-url.js +1 -1
  26. package/src/isDirectoryInside.js +4 -1
  27. package/src/openInBrowser.js +3 -1
  28. package/src/path.js +17 -2
  29. package/src/prettyDiagnostic.js +39 -27
  30. package/src/relativeBundlePath.js +5 -7
  31. package/src/replaceBundleReferences.js +50 -34
  32. package/src/schema.js +96 -42
  33. package/src/shared-buffer.js +24 -0
  34. package/src/sourcemap.js +84 -10
  35. package/src/urlJoin.js +3 -1
  36. package/test/DefaultMap.test.js +7 -4
  37. package/test/config.test.js +50 -0
  38. package/test/input/config/config.json +3 -0
  39. package/test/input/config/empty.json +0 -0
  40. package/test/input/config/empty.toml +0 -0
  41. package/test/input/sourcemap/referenced-min.js +1 -1
  42. package/test/replaceBundleReferences.test.js +268 -0
  43. package/test/sourcemap.test.js +5 -9
  44. package/test/throttle.test.js +1 -2
  45. package/test/urlJoin.test.js +37 -0
  46. package/lib/DefaultMap.js +0 -64
  47. package/lib/Deferred.js +0 -26
  48. package/lib/PromiseQueue.js +0 -133
  49. package/lib/TapStream.js +0 -38
  50. package/lib/ansi-html.js +0 -16
  51. package/lib/blob.js +0 -31
  52. package/lib/bundle-url.js +0 -43
  53. package/lib/collection.js +0 -62
  54. package/lib/config.js +0 -109
  55. package/lib/countLines.js +0 -18
  56. package/lib/debounce.js +0 -20
  57. package/lib/dependency-location.js +0 -21
  58. package/lib/escape-html.js +0 -24
  59. package/lib/escape-markdown.js +0 -15
  60. package/lib/generateBuildMetrics.js +0 -124
  61. package/lib/generateCertificate.js +0 -124
  62. package/lib/getCertificate.js +0 -19
  63. package/lib/getExisting.js +0 -23
  64. package/lib/getRootDir.js +0 -55
  65. package/lib/glob.js +0 -69
  66. package/lib/http-server.js +0 -81
  67. package/lib/is-url.js +0 -17
  68. package/lib/isDirectoryInside.js +0 -16
  69. package/lib/md5.js +0 -40
  70. package/lib/objectHash.js +0 -26
  71. package/lib/openInBrowser.js +0 -70
  72. package/lib/parseCSSImport.js +0 -16
  73. package/lib/path.js +0 -30
  74. package/lib/prettifyTime.js +0 -10
  75. package/lib/prettyDiagnostic.js +0 -75
  76. package/lib/promisify.js +0 -13
  77. package/lib/relativeBundlePath.js +0 -18
  78. package/lib/relativeUrl.js +0 -16
  79. package/lib/replaceBundleReferences.js +0 -166
  80. package/lib/resolve.js +0 -108
  81. package/lib/schema.js +0 -321
  82. package/lib/serializeObject.js +0 -28
  83. package/lib/sourcemap.js +0 -58
  84. package/lib/stream.js +0 -78
  85. package/lib/throttle.js +0 -16
  86. package/lib/urlJoin.js +0 -27
  87. package/src/.babelrc +0 -3
  88. package/src/escape-markdown.js +0 -10
  89. package/src/md5.js +0 -49
  90. package/src/promisify.js +0 -13
  91. package/src/resolve.js +0 -216
  92. package/src/serializeObject.js +0 -22
  93. package/test/escapeMarkdown.test.js +0 -29
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';
7
+ import type {Mapping} from 'json-source-map';
8
+ import nullthrows from 'nullthrows';
5
9
  // flowlint-next-line untyped-import:off
6
- import levenshteinDistance from 'js-levenshtein';
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
  }
@@ -344,7 +369,7 @@ export function fuzzySearch(
344
369
  actualValue: string,
345
370
  ): Array<string> {
346
371
  let result = expectedValues
347
- .map(exp => [exp, levenshteinDistance(exp, actualValue)])
372
+ .map(exp => [exp, levenshtein.distance(exp, actualValue)])
348
373
  .filter(
349
374
  // Remove if more than half of the string would need to be changed
350
375
  ([, d]) => d * 2 < actualValue.length,
@@ -353,22 +378,43 @@ export function fuzzySearch(
353
378
  return result.map(([v]) => v);
354
379
  }
355
380
 
356
- validateSchema.diagnostic = function(
381
+ validateSchema.diagnostic = function (
357
382
  schema: SchemaEntity,
358
- data: mixed,
359
- dataContentsPath?: ?string,
360
- 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
+ |},
361
399
  origin: string,
362
- prependKey: string,
363
400
  message: string,
364
401
  ): void {
365
- 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);
366
417
  if (errors.length) {
367
- let dataContentsString: string =
368
- typeof dataContents === 'string'
369
- ? dataContents
370
- : // $FlowFixMe
371
- JSON.stringify(dataContents, null, '\t');
372
418
  let keys = errors.map(e => {
373
419
  let message;
374
420
  if (e.type === 'enum') {
@@ -406,10 +452,8 @@ validateSchema.diagnostic = function(
406
452
  let {prop, actualProps} = e;
407
453
  let likely = fuzzySearch(actualProps, prop);
408
454
  if (likely.length > 0) {
409
- message = `Did you mean ${likely
410
- .map(v => JSON.stringify(v))
411
- .join(', ')}?`;
412
- e.dataPath += '/' + prop;
455
+ message = `Did you mean ${JSON.stringify(prop)}?`;
456
+ e.dataPath += '/' + likely[0];
413
457
  e.dataType = 'key';
414
458
  } else {
415
459
  message = `Missing property ${prop}`;
@@ -425,26 +469,36 @@ validateSchema.diagnostic = function(
425
469
  }
426
470
  return {key: e.dataPath, type: e.dataType, message};
427
471
  });
428
- let codeFrame = {
429
- code: dataContentsString,
430
- codeHighlights: generateJSONCodeHighlights(
431
- dataContentsString,
432
- keys.map(({key, type, message}) => ({
433
- key: prependKey + key,
434
- type: type,
435
- message,
436
- })),
437
- ),
438
- };
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
+ ];
439
496
 
440
497
  throw new ThrowableDiagnostic({
441
498
  diagnostic: {
442
- message,
499
+ message: message,
443
500
  origin,
444
- // $FlowFixMe should be a sketchy string check
445
- filePath: dataContentsPath || undefined,
446
- language: 'json',
447
- codeFrame,
501
+ codeFrames,
448
502
  },
449
503
  });
450
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
+ }
package/src/sourcemap.js CHANGED
@@ -1,13 +1,27 @@
1
1
  // @flow
2
+ import type {SourceLocation} from '@parcel/types';
2
3
  import type {FileSystem} from '@parcel/fs';
3
4
  import SourceMap from '@parcel/source-map';
4
5
  import path from 'path';
5
- import {normalizeSeparators} from './path';
6
+ import {normalizeSeparators, isAbsolute} from './path';
6
7
 
7
- const SOURCEMAP_RE = /(?:\/\*|\/\/)\s*[@#]\s*sourceMappingURL\s*=\s*([^\s*]+)(?:\s*\*\/)?\s*$/;
8
+ export const SOURCEMAP_RE: RegExp =
9
+ /(?:\/\*|\/\/)\s*[@#]\s*sourceMappingURL\s*=\s*([^\s*]+)(?:\s*\*\/)?\s*$/;
8
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
+ ]);
9
21
 
10
- export function matchSourceMappingURL(contents: string) {
22
+ export function matchSourceMappingURL(
23
+ contents: string,
24
+ ): null | RegExp$matchResult {
11
25
  return contents.match(SOURCEMAP_RE);
12
26
  }
13
27
 
@@ -15,20 +29,29 @@ export async function loadSourceMapUrl(
15
29
  fs: FileSystem,
16
30
  filename: string,
17
31
  contents: string,
18
- ) {
32
+ ): Promise<?{|filename: string, map: any, url: string|}> {
19
33
  let match = matchSourceMappingURL(contents);
20
34
  if (match) {
21
35
  let url = match[1].trim();
22
36
  let dataURLMatch = url.match(DATA_URL_RE);
23
- filename = dataURLMatch ? filename : path.join(path.dirname(filename), url);
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
+ }
24
47
 
25
48
  return {
26
49
  url,
27
- filename,
50
+ filename: mapFilePath,
28
51
  map: JSON.parse(
29
52
  dataURLMatch
30
53
  ? Buffer.from(dataURLMatch[1], 'base64').toString()
31
- : await fs.readFile(filename, 'utf8'),
54
+ : await fs.readFile(mapFilePath, 'utf8'),
32
55
  ),
33
56
  };
34
57
  }
@@ -49,13 +72,64 @@ export async function loadSourceMap(
49
72
  mapSourceRoot = path.join(mapSourceRoot, foundMap.map.sourceRoot);
50
73
  }
51
74
 
52
- let sourcemapInstance = new SourceMap();
53
- sourcemapInstance.addRawMappings({
75
+ let sourcemapInstance = new SourceMap(options.projectRoot);
76
+ sourcemapInstance.addVLQMap({
54
77
  ...foundMap.map,
55
78
  sources: foundMap.map.sources.map(s => {
56
- return path.relative(options.projectRoot, path.join(mapSourceRoot, s));
79
+ return path.join(mapSourceRoot, s);
57
80
  }),
58
81
  });
59
82
  return sourcemapInstance;
60
83
  }
61
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/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],
@@ -0,0 +1,50 @@
1
+ // @flow strict-local
2
+
3
+ import assert from 'assert';
4
+ import {loadConfig} from '../src/config';
5
+ import {inputFS as fs} from '@parcel/test-utils';
6
+ import path from 'path';
7
+
8
+ describe('loadConfig', () => {
9
+ it('load config with json', async () => {
10
+ assert.deepEqual(
11
+ (
12
+ await loadConfig(
13
+ fs,
14
+ path.join(__dirname, './input/config/config.json'),
15
+ ['config.json'],
16
+ path.join(__dirname, './input/config/'),
17
+ )
18
+ )?.config,
19
+ {
20
+ hoge: 'fuga',
21
+ },
22
+ );
23
+ });
24
+
25
+ it('should throw error with empty string json', async () => {
26
+ // $FlowFixMe[prop-missing]
27
+ await assert.rejects(async () => {
28
+ await loadConfig(
29
+ fs,
30
+ path.join(__dirname, './input/config/empty.json'),
31
+ ['empty.json'],
32
+ path.join(__dirname, './input/config/'),
33
+ );
34
+ });
35
+ });
36
+
37
+ it('should load with empty string config toml', async () => {
38
+ assert.deepEqual(
39
+ (
40
+ await loadConfig(
41
+ fs,
42
+ path.join(__dirname, './input/config/empty.toml'),
43
+ ['empty.toml'],
44
+ path.join(__dirname, './input/config/'),
45
+ )
46
+ )?.config,
47
+ {},
48
+ );
49
+ });
50
+ });
@@ -0,0 +1,3 @@
1
+ {
2
+ "hoge": "fuga"
3
+ }
File without changes
File without changes
@@ -1,2 +1,2 @@
1
1
  function hello(){var l="Hello",o="world";console.log(l+" "+o+"!")}hello();
2
- //# sourceMappingURL=referenced-min.js.map
2
+ //# sourceMappingURL=file://referenced-min.js.map