@parcel/utils 2.0.0-nightly.97 → 2.0.0-nightly.970
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.
- package/.eslintrc.js +6 -6
- package/lib/DefaultMap.js +0 -8
- package/lib/Deferred.js +10 -2
- package/lib/PromiseQueue.js +21 -30
- package/lib/TapStream.js +10 -10
- package/lib/alternatives.js +134 -0
- package/lib/ansi-html.js +10 -2
- package/lib/blob.js +26 -8
- package/lib/collection.js +14 -11
- package/lib/config.js +145 -35
- package/lib/countLines.js +2 -2
- package/lib/dependency-location.js +3 -3
- package/lib/generateBuildMetrics.js +148 -0
- package/lib/generateCertificate.js +33 -8
- package/lib/getExisting.js +11 -3
- package/lib/getRootDir.js +18 -7
- package/lib/glob.js +53 -19
- package/lib/hash.js +44 -0
- package/lib/http-server.js +48 -10
- package/lib/index.js +298 -224
- package/lib/is-url.js +12 -2
- package/lib/isDirectoryInside.js +24 -0
- package/lib/objectHash.js +10 -2
- package/lib/openInBrowser.js +94 -0
- package/lib/path.js +33 -6
- package/lib/prettyDiagnostic.js +107 -25
- package/lib/relativeBundlePath.js +13 -7
- package/lib/relativeUrl.js +19 -3
- package/lib/replaceBundleReferences.js +91 -35
- package/lib/schema.js +104 -33
- package/lib/shared-buffer.js +31 -0
- package/lib/sourcemap.js +147 -0
- package/lib/stream.js +38 -3
- package/lib/urlJoin.js +25 -6
- package/package.json +27 -16
- package/src/DefaultMap.js +1 -1
- package/src/PromiseQueue.js +16 -12
- package/src/alternatives.js +143 -0
- package/src/ansi-html.js +2 -2
- package/src/blob.js +4 -3
- package/src/bundle-url.js +1 -1
- package/src/collection.js +14 -14
- package/src/config.js +100 -35
- package/src/countLines.js +5 -2
- package/src/debounce.js +1 -1
- package/src/dependency-location.js +11 -6
- package/src/generateBuildMetrics.js +158 -0
- package/src/generateCertificate.js +1 -1
- package/src/getCertificate.js +1 -1
- package/src/getExisting.js +1 -4
- package/src/getRootDir.js +1 -2
- package/src/glob.js +29 -11
- package/src/hash.js +34 -0
- package/src/http-server.js +10 -12
- package/src/index.js +52 -23
- package/src/is-url.js +1 -1
- package/src/isDirectoryInside.js +11 -0
- package/src/openInBrowser.js +64 -0
- package/src/path.js +38 -6
- package/src/prettyDiagnostic.js +58 -24
- package/src/relativeBundlePath.js +8 -13
- package/src/replaceBundleReferences.js +75 -39
- package/src/schema.js +101 -44
- package/src/shared-buffer.js +24 -0
- package/src/sourcemap.js +135 -0
- package/src/stream.js +31 -1
- package/src/urlJoin.js +3 -1
- package/test/DefaultMap.test.js +7 -4
- package/test/config.test.js +50 -0
- package/test/input/config/config.json +3 -0
- package/test/input/config/empty.json +0 -0
- package/test/input/config/empty.toml +0 -0
- package/test/input/sourcemap/referenced-min.js +2 -0
- package/test/input/sourcemap/referenced-min.js.map +6 -0
- package/test/input/sourcemap/source-root.js +2 -0
- package/test/input/sourcemap/source-root.js.map +7 -0
- package/test/objectHash.test.js +33 -0
- package/test/prettifyTime.test.js +17 -0
- package/test/replaceBundleReferences.test.js +268 -0
- package/test/sourcemap.test.js +207 -0
- package/test/throttle.test.js +1 -2
- package/test/urlJoin.test.js +37 -0
- package/lib/generateBundleReport.js +0 -38
- package/lib/loadSourceMapUrl.js +0 -33
- package/lib/md5.js +0 -35
- package/lib/prettyError.js +0 -43
- package/lib/promisify.js +0 -13
- package/lib/resolve.js +0 -93
- package/lib/serializeObject.js +0 -28
- package/src/.babelrc +0 -3
- package/src/generateBundleReport.js +0 -51
- package/src/loadSourceMapUrl.js +0 -33
- package/src/md5.js +0 -44
- package/src/prettyError.js +0 -54
- package/src/promisify.js +0 -13
- package/src/resolve.js +0 -123
- package/src/serializeObject.js +0 -22
- package/test/input/sourcemap/referenced.js +0 -7
- package/test/loadSourceMapUrl.test.js +0 -37
- 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 {
|
|
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
|
|
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:
|
|
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
|
|
39
|
-
if (
|
|
52
|
+
for (let dependency of urlDependencies) {
|
|
53
|
+
if (dependency.specifierType !== 'url') {
|
|
40
54
|
continue;
|
|
41
55
|
}
|
|
42
56
|
|
|
43
|
-
let
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
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
|
-
|
|
76
|
+
placeholder,
|
|
61
77
|
getURLReplacement({
|
|
62
78
|
dependency,
|
|
63
79
|
fromBundle: bundle,
|
|
64
|
-
toBundle:
|
|
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
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
if (
|
|
104
|
-
|
|
117
|
+
let dependencies = [];
|
|
118
|
+
bundle.traverse(node => {
|
|
119
|
+
if (node.type === 'dependency') {
|
|
120
|
+
dependencies.push(node.value);
|
|
105
121
|
}
|
|
122
|
+
});
|
|
106
123
|
|
|
107
|
-
|
|
108
|
-
|
|
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,9 +131,10 @@ export async function replaceInlineReferences({
|
|
|
113
131
|
entryBundle,
|
|
114
132
|
bundleGraph,
|
|
115
133
|
);
|
|
116
|
-
let packagedContents = (
|
|
117
|
-
|
|
118
|
-
|
|
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;
|
|
@@ -130,31 +149,48 @@ export async function replaceInlineReferences({
|
|
|
130
149
|
return performReplacement(replacements, contents, map);
|
|
131
150
|
}
|
|
132
151
|
|
|
133
|
-
function getURLReplacement({
|
|
152
|
+
export function getURLReplacement({
|
|
134
153
|
dependency,
|
|
135
154
|
fromBundle,
|
|
136
155
|
toBundle,
|
|
137
156
|
relative,
|
|
138
157
|
}: {|
|
|
139
158
|
dependency: Dependency,
|
|
140
|
-
fromBundle:
|
|
141
|
-
toBundle:
|
|
159
|
+
fromBundle: NamedBundle,
|
|
160
|
+
toBundle: NamedBundle,
|
|
142
161
|
relative: boolean,
|
|
143
|
-
|}) {
|
|
144
|
-
let url = URL.parse(dependency.moduleSpecifier);
|
|
162
|
+
|}): {|from: string, to: string|} {
|
|
145
163
|
let to;
|
|
164
|
+
|
|
165
|
+
let orig = URL.parse(dependency.specifier);
|
|
166
|
+
|
|
146
167
|
if (relative) {
|
|
147
|
-
|
|
148
|
-
|
|
168
|
+
to = URL.format({
|
|
169
|
+
pathname: relativeBundlePath(fromBundle, toBundle, {
|
|
170
|
+
leadingDotSlash: false,
|
|
171
|
+
}),
|
|
172
|
+
hash: orig.hash,
|
|
149
173
|
});
|
|
150
|
-
|
|
174
|
+
|
|
175
|
+
// If the resulting path includes a colon character and doesn't start with a ./ or ../
|
|
176
|
+
// we need to add one so that the first part before the colon isn't parsed as a URL protocol.
|
|
177
|
+
if (to.includes(':') && !to.startsWith('./') && !to.startsWith('../')) {
|
|
178
|
+
to = './' + to;
|
|
179
|
+
}
|
|
151
180
|
} else {
|
|
152
|
-
|
|
153
|
-
|
|
181
|
+
to = urlJoin(
|
|
182
|
+
toBundle.target.publicUrl,
|
|
183
|
+
URL.format({
|
|
184
|
+
pathname: nullthrows(toBundle.name),
|
|
185
|
+
hash: orig.hash,
|
|
186
|
+
}),
|
|
187
|
+
);
|
|
154
188
|
}
|
|
155
189
|
|
|
190
|
+
let placeholder = dependency.meta?.placeholder ?? dependency.id;
|
|
191
|
+
invariant(typeof placeholder === 'string');
|
|
156
192
|
return {
|
|
157
|
-
from:
|
|
193
|
+
from: placeholder,
|
|
158
194
|
to,
|
|
159
195
|
};
|
|
160
196
|
}
|
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
|
-
|
|
6
|
-
import
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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(
|
|
367
|
+
export function fuzzySearch(
|
|
368
|
+
expectedValues: Array<string>,
|
|
369
|
+
actualValue: string,
|
|
370
|
+
): Array<string> {
|
|
343
371
|
let result = expectedValues
|
|
344
|
-
.map(exp => [exp,
|
|
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:
|
|
356
|
-
|
|
357
|
-
|
|
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
|
-
|
|
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 ${
|
|
407
|
-
|
|
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
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
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
|
-
|
|
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
|
+
}
|
package/src/sourcemap.js
ADDED
|
@@ -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
|
-
|
|
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;
|