@ttsc/playground 0.19.3 → 0.20.0
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/README.md +6 -0
- package/lib/src/npm/collectExternalPackageNames.js +436 -73
- package/lib/src/npm/collectExternalPackageNames.js.map +1 -1
- package/lib/src/npm/installPlaygroundDependencies.d.ts +5 -3
- package/lib/src/npm/installPlaygroundDependencies.js +230 -27
- package/lib/src/npm/installPlaygroundDependencies.js.map +1 -1
- package/lib/src/npm/internal/npmRegistry.d.ts +13 -2
- package/lib/src/npm/internal/npmRegistry.js +73 -50
- package/lib/src/npm/internal/npmRegistry.js.map +1 -1
- package/lib/src/npm/packageNameFromSpecifier.js +17 -4
- package/lib/src/npm/packageNameFromSpecifier.js.map +1 -1
- package/lib/src/react/PlaygroundShell.js +68 -61
- package/lib/src/react/PlaygroundShell.js.map +1 -1
- package/lib/src/react/createCompilerClient.d.ts +3 -3
- package/lib/src/react/createCompilerClient.js +32 -54
- package/lib/src/react/createCompilerClient.js.map +1 -1
- package/lib/src/sandbox/createSandboxRequire.js +117 -54
- package/lib/src/sandbox/createSandboxRequire.js.map +1 -1
- package/lib/src/structures/IPlaygroundDependencyInstallOptions.d.ts +13 -1
- package/lib/src/structures/IPlaygroundDependencyInstallResult.d.ts +4 -0
- package/lib/src/structures/IPlaygroundDependencyPackage.d.ts +2 -0
- package/lib/src/structures/IPlaygroundDependencyRequest.d.ts +9 -0
- package/lib/src/structures/IPlaygroundDependencyRequest.js +3 -0
- package/lib/src/structures/IPlaygroundDependencyRequest.js.map +1 -0
- package/lib/src/structures/IPlaygroundInstalledDependency.d.ts +12 -0
- package/lib/src/structures/IPlaygroundInstalledDependency.js +3 -0
- package/lib/src/structures/IPlaygroundInstalledDependency.js.map +1 -0
- package/lib/src/structures/index.d.ts +2 -0
- package/lib/src/structures/index.js +2 -0
- package/lib/src/structures/index.js.map +1 -1
- package/package.json +6 -4
- package/src/npm/collectExternalPackageNames.ts +503 -68
- package/src/npm/installPlaygroundDependencies.ts +288 -29
- package/src/npm/internal/npmRegistry.ts +99 -38
- package/src/npm/packageNameFromSpecifier.ts +16 -3
- package/src/react/PlaygroundShell.tsx +86 -72
- package/src/react/createCompilerClient.ts +37 -51
- package/src/sandbox/createSandboxRequire.ts +140 -49
- package/src/structures/IPlaygroundDependencyInstallOptions.ts +13 -1
- package/src/structures/IPlaygroundDependencyInstallResult.ts +4 -0
- package/src/structures/IPlaygroundDependencyPackage.ts +2 -0
- package/src/structures/IPlaygroundDependencyRequest.ts +9 -0
- package/src/structures/IPlaygroundInstalledDependency.ts +13 -0
- package/src/structures/index.ts +2 -0
package/README.md
CHANGED
|
@@ -127,6 +127,8 @@ The typia pack itself is built by the site (typically with a `pack-typia-sources
|
|
|
127
127
|
|
|
128
128
|
When the user types `import {v4} from "uuid"`, the shell auto-fetches `uuid` (and its transitive deps) from the npm registry, unpacks the tgz in the browser, and mounts the files into the wasm MemFS, no proxy server needed.
|
|
129
129
|
|
|
130
|
+
Discovery covers static quoted specifiers in imports, exports, dynamic imports, `require(...)`, and direct `require?.(...)` calls, using their cooked JavaScript string values while ignoring comments, regular expressions, template quasis, and computed arguments.
|
|
131
|
+
|
|
130
132
|
```ts
|
|
131
133
|
import {
|
|
132
134
|
collectExternalPackageNames,
|
|
@@ -140,6 +142,10 @@ const installed = await installPlaygroundDependencies(names, {
|
|
|
140
142
|
// mount installed.compilerFiles into the wasm MemFS via service.installDependencies
|
|
141
143
|
```
|
|
142
144
|
|
|
145
|
+
For an additive edit, pass the prior call's `resolvedDependencies` as `installedDependencies`; the installer validates every new range against the mounted registry identity and exact version, and it returns only newly downloaded tarballs in `packages` plus the complete updated exact state in `resolvedDependencies`.
|
|
146
|
+
|
|
147
|
+
When a direct source import is removed, solve the complete current root list without `installedDependencies` and atomically replace the compiler, editor, and runtime file maps so files from the obsolete graph cannot survive.
|
|
148
|
+
|
|
143
149
|
`PlaygroundShell` wires this automatically on every keystroke, debounced 900 ms, with an abort signal on source change.
|
|
144
150
|
|
|
145
151
|
## Tailwind setup
|
|
@@ -20,7 +20,7 @@ function collectExternalPackageNames(source, ignoredPackages = BUILT_IN_PLAYGROU
|
|
|
20
20
|
/**
|
|
21
21
|
* Collect the string specifiers of every executable module-loading construct in
|
|
22
22
|
* `source`: `import`/`export ... from`, side-effect `import "x"`, dynamic
|
|
23
|
-
* `import("x")`, and `require("x")` calls.
|
|
23
|
+
* `import("x")`, `require("x")`, and `require?.("x")` calls.
|
|
24
24
|
*
|
|
25
25
|
* The scan tokenizes `source` first so import/export/require lookalikes that
|
|
26
26
|
* live inside comments, string or template contents, or regular-expression
|
|
@@ -34,7 +34,10 @@ function collectModuleSpecifiers(source) {
|
|
|
34
34
|
const out = [];
|
|
35
35
|
const asString = (token) => token && token.kind === "string" ? token.value : null;
|
|
36
36
|
const isOpenParen = (token) => token !== undefined && token.kind === "punct" && token.value === "(";
|
|
37
|
-
const isMemberAccess = (token) => token !== undefined &&
|
|
37
|
+
const isMemberAccess = (token) => token !== undefined &&
|
|
38
|
+
token.kind === "punct" &&
|
|
39
|
+
(token.value === "." || token.value === "?.");
|
|
40
|
+
const isOptionalChain = (token) => token !== undefined && token.kind === "punct" && token.value === "?.";
|
|
38
41
|
for (let i = 0; i < tokens.length; i++) {
|
|
39
42
|
const token = tokens[i];
|
|
40
43
|
if (!token || token.kind !== "word")
|
|
@@ -43,8 +46,9 @@ function collectModuleSpecifiers(source) {
|
|
|
43
46
|
// `obj.require(...)` is an unrelated method call, not CommonJS require.
|
|
44
47
|
if (isMemberAccess(tokens[i - 1]))
|
|
45
48
|
continue;
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
const optional = isOptionalChain(tokens[i + 1]) && isOpenParen(tokens[i + 2]);
|
|
50
|
+
if (isOpenParen(tokens[i + 1]) || optional) {
|
|
51
|
+
const spec = asString(tokens[i + (optional ? 3 : 2)]);
|
|
48
52
|
if (spec !== null)
|
|
49
53
|
out.push(spec);
|
|
50
54
|
}
|
|
@@ -114,11 +118,159 @@ const REGEX_PRECEDING_KEYWORDS = new Set([
|
|
|
114
118
|
"void",
|
|
115
119
|
"do",
|
|
116
120
|
"else",
|
|
121
|
+
"extends",
|
|
117
122
|
"yield",
|
|
118
123
|
"await",
|
|
119
124
|
"case",
|
|
120
125
|
"throw",
|
|
121
126
|
]);
|
|
127
|
+
const CONTROL_HEADER_KEYWORDS = new Set([
|
|
128
|
+
"if",
|
|
129
|
+
"while",
|
|
130
|
+
"for",
|
|
131
|
+
"with",
|
|
132
|
+
"switch",
|
|
133
|
+
"catch",
|
|
134
|
+
]);
|
|
135
|
+
const BLOCK_PRECEDING_KEYWORDS = new Set(["else", "try", "finally", "do"]);
|
|
136
|
+
/**
|
|
137
|
+
* Retain just enough delimiter context to distinguish a regex statement after a
|
|
138
|
+
* control header or block from division after an expression value. The
|
|
139
|
+
* collector is not a parser: this state only decides whether a following slash
|
|
140
|
+
* is opaque regex text or executable code worth scanning for module calls.
|
|
141
|
+
*/
|
|
142
|
+
class LexicalContext {
|
|
143
|
+
tokens = [];
|
|
144
|
+
parens = [];
|
|
145
|
+
braces = [];
|
|
146
|
+
pendingFunctionExpression;
|
|
147
|
+
pendingClassExpressions = [];
|
|
148
|
+
isRegexAllowed() {
|
|
149
|
+
return isRegexAllowedAfter(this.tokens[this.tokens.length - 1]);
|
|
150
|
+
}
|
|
151
|
+
pushWord(value) {
|
|
152
|
+
if (value === "function" &&
|
|
153
|
+
!isMemberAccess(this.tokens[this.tokens.length - 1]))
|
|
154
|
+
this.pendingFunctionExpression = isExpressionPosition(this.tokens);
|
|
155
|
+
else if (value === "class" &&
|
|
156
|
+
!isMemberAccess(this.tokens[this.tokens.length - 1]))
|
|
157
|
+
this.pendingClassExpressions.push(isExpressionPosition(this.tokens));
|
|
158
|
+
this.tokens.push({ kind: "word", value });
|
|
159
|
+
}
|
|
160
|
+
pushOther() {
|
|
161
|
+
this.tokens.push({ kind: "other" });
|
|
162
|
+
}
|
|
163
|
+
pushPunct(value) {
|
|
164
|
+
if (value === ":") {
|
|
165
|
+
const previous = this.tokens[this.tokens.length - 1];
|
|
166
|
+
if (previous?.kind === "word" &&
|
|
167
|
+
!isMemberAccess(this.tokens[this.tokens.length - 2])) {
|
|
168
|
+
if (previous.value === "function")
|
|
169
|
+
this.pendingFunctionExpression = undefined;
|
|
170
|
+
else if (previous.value === "class")
|
|
171
|
+
this.pendingClassExpressions.pop();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (value === "(") {
|
|
175
|
+
const functionIsExpression = this.pendingFunctionExpression;
|
|
176
|
+
this.pendingFunctionExpression = undefined;
|
|
177
|
+
this.parens.push(functionIsExpression === undefined
|
|
178
|
+
? {
|
|
179
|
+
kind: isControlHeader(this.tokens) ? "control" : "normal",
|
|
180
|
+
}
|
|
181
|
+
: { kind: "function", functionIsExpression });
|
|
182
|
+
this.tokens.push({ kind: "punct", value });
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (value === ")") {
|
|
186
|
+
const context = this.parens.pop();
|
|
187
|
+
this.tokens.push({
|
|
188
|
+
kind: "punct",
|
|
189
|
+
value,
|
|
190
|
+
regexAllowedAfter: context?.kind === "control",
|
|
191
|
+
functionBodyIsExpression: context?.kind === "function"
|
|
192
|
+
? context.functionIsExpression
|
|
193
|
+
: undefined,
|
|
194
|
+
});
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (value === "{") {
|
|
198
|
+
this.braces.push(this.braceAllowsRegexAfter());
|
|
199
|
+
this.tokens.push({ kind: "punct", value });
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (value === "}") {
|
|
203
|
+
this.tokens.push({
|
|
204
|
+
kind: "punct",
|
|
205
|
+
value,
|
|
206
|
+
regexAllowedAfter: this.braces.pop() ?? false,
|
|
207
|
+
});
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
this.tokens.push({ kind: "punct", value });
|
|
211
|
+
}
|
|
212
|
+
braceAllowsRegexAfter() {
|
|
213
|
+
const previous = this.tokens[this.tokens.length - 1];
|
|
214
|
+
if (previous?.kind === "punct" &&
|
|
215
|
+
previous.value === ")" &&
|
|
216
|
+
previous.functionBodyIsExpression !== undefined)
|
|
217
|
+
return !previous.functionBodyIsExpression;
|
|
218
|
+
if (this.classBodyPrecedes(previous))
|
|
219
|
+
return !this.pendingClassExpressions.pop();
|
|
220
|
+
if (!previous)
|
|
221
|
+
return true;
|
|
222
|
+
if (previous.kind === "word")
|
|
223
|
+
return BLOCK_PRECEDING_KEYWORDS.has(previous.value);
|
|
224
|
+
if (previous.kind !== "punct")
|
|
225
|
+
return false;
|
|
226
|
+
if (previous.value === ")")
|
|
227
|
+
return previous.regexAllowedAfter === true;
|
|
228
|
+
return (previous.value === ";" || previous.value === "{" || previous.value === "}");
|
|
229
|
+
}
|
|
230
|
+
classBodyPrecedes(previous) {
|
|
231
|
+
if (this.pendingClassExpressions.length === 0 || this.parens.length !== 0)
|
|
232
|
+
return false;
|
|
233
|
+
if (!previous)
|
|
234
|
+
return false;
|
|
235
|
+
if (previous.kind === "word" || previous.kind === "other")
|
|
236
|
+
return true;
|
|
237
|
+
return (previous.kind === "punct" &&
|
|
238
|
+
(previous.value === ")" ||
|
|
239
|
+
previous.value === "]" ||
|
|
240
|
+
previous.value === "}"));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function isControlHeader(tokens) {
|
|
244
|
+
const previous = tokens[tokens.length - 1];
|
|
245
|
+
const beforePrevious = tokens[tokens.length - 2];
|
|
246
|
+
if (previous?.kind === "word" &&
|
|
247
|
+
CONTROL_HEADER_KEYWORDS.has(previous.value) &&
|
|
248
|
+
!isMemberAccess(beforePrevious))
|
|
249
|
+
return true;
|
|
250
|
+
return (previous?.kind === "word" &&
|
|
251
|
+
previous.value === "await" &&
|
|
252
|
+
beforePrevious?.kind === "word" &&
|
|
253
|
+
beforePrevious.value === "for");
|
|
254
|
+
}
|
|
255
|
+
function isMemberAccess(token) {
|
|
256
|
+
return (token?.kind === "punct" && (token.value === "." || token.value === "?."));
|
|
257
|
+
}
|
|
258
|
+
function isExpressionPosition(tokens) {
|
|
259
|
+
let index = tokens.length - 1;
|
|
260
|
+
// `async function` inherits the context before `async`: it is a declaration
|
|
261
|
+
// at a statement boundary and an expression after an assignment or return.
|
|
262
|
+
const latest = tokens[index];
|
|
263
|
+
if (latest?.kind === "word" && latest.value === "async")
|
|
264
|
+
index--;
|
|
265
|
+
const previous = tokens[index];
|
|
266
|
+
if (!previous)
|
|
267
|
+
return false;
|
|
268
|
+
if (previous.kind === "word")
|
|
269
|
+
return REGEX_PRECEDING_KEYWORDS.has(previous.value);
|
|
270
|
+
if (previous.kind !== "punct")
|
|
271
|
+
return false;
|
|
272
|
+
return ![")", "]", "}", ";", "{", "++", "--"].includes(previous.value);
|
|
273
|
+
}
|
|
122
274
|
/**
|
|
123
275
|
* Lexically tokenize `source` into the coarse token stream the specifier
|
|
124
276
|
* collector needs. Comments are dropped; strings, templates, regex literals,
|
|
@@ -126,23 +278,14 @@ const REGEX_PRECEDING_KEYWORDS = new Set([
|
|
|
126
278
|
* grammar match.
|
|
127
279
|
*/
|
|
128
280
|
function tokenize(source) {
|
|
129
|
-
const
|
|
281
|
+
const context = new LexicalContext();
|
|
282
|
+
const tokens = context.tokens;
|
|
130
283
|
const n = source.length;
|
|
131
284
|
const isIdStart = (c) => (c >= "a" && c <= "z") || (c >= "A" && c <= "Z") || c === "_" || c === "$";
|
|
132
285
|
const isIdPart = (c) => isIdStart(c) || (c >= "0" && c <= "9");
|
|
133
286
|
const isDigit = (c) => c >= "0" && c <= "9";
|
|
134
287
|
// A `/` opens a regex only in operator/statement position — never right after
|
|
135
288
|
// a value (identifier, number, string, template, regex, `)` or `]`).
|
|
136
|
-
const regexAllowed = () => {
|
|
137
|
-
const prev = tokens[tokens.length - 1];
|
|
138
|
-
if (!prev)
|
|
139
|
-
return true;
|
|
140
|
-
if (prev.kind === "string" || prev.kind === "other")
|
|
141
|
-
return false;
|
|
142
|
-
if (prev.kind === "word")
|
|
143
|
-
return REGEX_PRECEDING_KEYWORDS.has(prev.value);
|
|
144
|
-
return prev.value !== ")" && prev.value !== "]";
|
|
145
|
-
};
|
|
146
289
|
let i = 0;
|
|
147
290
|
while (i < n) {
|
|
148
291
|
const c = source[i];
|
|
@@ -172,83 +315,47 @@ function tokenize(source) {
|
|
|
172
315
|
continue;
|
|
173
316
|
}
|
|
174
317
|
// Regular-expression literal.
|
|
175
|
-
if (c === "/" &&
|
|
176
|
-
i
|
|
177
|
-
|
|
178
|
-
while (i < n) {
|
|
179
|
-
const d = source[i];
|
|
180
|
-
if (d === "\\") {
|
|
181
|
-
i += 2;
|
|
182
|
-
continue;
|
|
183
|
-
}
|
|
184
|
-
if (d === "\n")
|
|
185
|
-
break;
|
|
186
|
-
if (d === "[")
|
|
187
|
-
inClass = true;
|
|
188
|
-
else if (d === "]")
|
|
189
|
-
inClass = false;
|
|
190
|
-
else if (d === "/" && !inClass) {
|
|
191
|
-
i++;
|
|
192
|
-
break;
|
|
193
|
-
}
|
|
194
|
-
i++;
|
|
195
|
-
}
|
|
196
|
-
while (i < n && isIdPart(source[i]))
|
|
197
|
-
i++; // flags
|
|
198
|
-
tokens.push({ kind: "other" });
|
|
318
|
+
if (c === "/" && context.isRegexAllowed()) {
|
|
319
|
+
i = skipRegularExpression(source, i, isIdPart);
|
|
320
|
+
context.pushOther();
|
|
199
321
|
continue;
|
|
200
322
|
}
|
|
201
323
|
// String literal.
|
|
202
324
|
if (c === '"' || c === "'") {
|
|
203
|
-
i
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
i += 2;
|
|
210
|
-
continue;
|
|
211
|
-
}
|
|
212
|
-
if (d === c) {
|
|
213
|
-
i++;
|
|
214
|
-
break;
|
|
215
|
-
}
|
|
216
|
-
if (d === "\n")
|
|
217
|
-
break; // unterminated single-line string
|
|
218
|
-
value += d;
|
|
219
|
-
i++;
|
|
220
|
-
}
|
|
221
|
-
tokens.push({ kind: "string", value });
|
|
325
|
+
const quoted = readQuotedString(source, i, c);
|
|
326
|
+
i = quoted.end;
|
|
327
|
+
if (quoted.value === null)
|
|
328
|
+
context.pushOther();
|
|
329
|
+
else
|
|
330
|
+
tokens.push({ kind: "string", value: quoted.value });
|
|
222
331
|
continue;
|
|
223
332
|
}
|
|
224
|
-
// Template
|
|
225
|
-
//
|
|
333
|
+
// Template quasis are opaque, but `${...}` substitutions are executable
|
|
334
|
+
// JavaScript and must receive the same lexical treatment as top-level code.
|
|
226
335
|
if (c === "`") {
|
|
227
336
|
i++;
|
|
228
|
-
let depth = 0;
|
|
229
337
|
while (i < n) {
|
|
230
338
|
const d = source[i];
|
|
231
339
|
if (d === "\\") {
|
|
232
340
|
i += 2;
|
|
233
341
|
continue;
|
|
234
342
|
}
|
|
235
|
-
if (
|
|
343
|
+
if (d === "`") {
|
|
236
344
|
i++;
|
|
237
345
|
break;
|
|
238
346
|
}
|
|
239
347
|
if (d === "$" && source[i + 1] === "{") {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
i++;
|
|
348
|
+
const start = i + 2;
|
|
349
|
+
const end = findTemplateSubstitutionEnd(source, start);
|
|
350
|
+
context.pushOther();
|
|
351
|
+
tokens.push(...tokenize(source.slice(start, end)));
|
|
352
|
+
context.pushOther();
|
|
353
|
+
i = end < n ? end + 1 : end;
|
|
247
354
|
continue;
|
|
248
355
|
}
|
|
249
356
|
i++;
|
|
250
357
|
}
|
|
251
|
-
|
|
358
|
+
context.pushOther();
|
|
252
359
|
continue;
|
|
253
360
|
}
|
|
254
361
|
// Identifier / keyword.
|
|
@@ -256,7 +363,7 @@ function tokenize(source) {
|
|
|
256
363
|
let j = i + 1;
|
|
257
364
|
while (j < n && isIdPart(source[j]))
|
|
258
365
|
j++;
|
|
259
|
-
|
|
366
|
+
context.pushWord(source.slice(i, j));
|
|
260
367
|
i = j;
|
|
261
368
|
continue;
|
|
262
369
|
}
|
|
@@ -265,14 +372,270 @@ function tokenize(source) {
|
|
|
265
372
|
let j = i + 1;
|
|
266
373
|
while (j < n && /[0-9a-fA-FxXoObBeE._]/.test(source[j]))
|
|
267
374
|
j++;
|
|
268
|
-
|
|
375
|
+
context.pushOther();
|
|
269
376
|
i = j;
|
|
270
377
|
continue;
|
|
271
378
|
}
|
|
379
|
+
// Compound punctuation whose identity affects the following slash or
|
|
380
|
+
// direct optional-call recognition.
|
|
381
|
+
if ((c === "+" || c === "-") && source[i + 1] === c) {
|
|
382
|
+
context.pushPunct(c + c);
|
|
383
|
+
i += 2;
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
if (c === "?" && source[i + 1] === "." && !isDigit(source[i + 2] ?? "")) {
|
|
387
|
+
context.pushPunct("?.");
|
|
388
|
+
i += 2;
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
272
391
|
// Single punctuation character.
|
|
273
|
-
|
|
392
|
+
context.pushPunct(c);
|
|
274
393
|
i++;
|
|
275
394
|
}
|
|
276
395
|
return tokens;
|
|
277
396
|
}
|
|
397
|
+
function findTemplateSubstitutionEnd(source, start) {
|
|
398
|
+
let depth = 1;
|
|
399
|
+
const context = new LexicalContext();
|
|
400
|
+
const isIdentifierStart = (character) => /[A-Za-z_$]/.test(character);
|
|
401
|
+
const isIdentifierPart = (character) => /[A-Za-z0-9_$]/.test(character);
|
|
402
|
+
for (let i = start; i < source.length; i++) {
|
|
403
|
+
const c = source[i];
|
|
404
|
+
if (/\s/.test(c))
|
|
405
|
+
continue;
|
|
406
|
+
if (c === "\\") {
|
|
407
|
+
i++;
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
if (c === "'" || c === '"') {
|
|
411
|
+
i = skipQuoted(source, i, c);
|
|
412
|
+
context.tokens.push({ kind: "string", value: "" });
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
if (c === "`") {
|
|
416
|
+
i = skipTemplate(source, i);
|
|
417
|
+
context.pushOther();
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
if (c === "/" && source[i + 1] === "/") {
|
|
421
|
+
i += 2;
|
|
422
|
+
while (i < source.length && source[i] !== "\n")
|
|
423
|
+
i++;
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
if (c === "/" && source[i + 1] === "*") {
|
|
427
|
+
i += 2;
|
|
428
|
+
while (i < source.length && !(source[i] === "*" && source[i + 1] === "/"))
|
|
429
|
+
i++;
|
|
430
|
+
i++;
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
if (c === "/" && context.isRegexAllowed()) {
|
|
434
|
+
i = skipRegularExpression(source, i, (character) => /[A-Za-z0-9_$]/.test(character));
|
|
435
|
+
i--;
|
|
436
|
+
context.pushOther();
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
if (isIdentifierStart(c)) {
|
|
440
|
+
let end = i + 1;
|
|
441
|
+
while (end < source.length && isIdentifierPart(source[end]))
|
|
442
|
+
end++;
|
|
443
|
+
context.pushWord(source.slice(i, end));
|
|
444
|
+
i = end - 1;
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
if (c >= "0" && c <= "9") {
|
|
448
|
+
let end = i + 1;
|
|
449
|
+
while (end < source.length && /[0-9a-fA-FxXoObBeE._]/.test(source[end]))
|
|
450
|
+
end++;
|
|
451
|
+
context.pushOther();
|
|
452
|
+
i = end - 1;
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
455
|
+
if ((c === "+" || c === "-") && source[i + 1] === c) {
|
|
456
|
+
context.pushPunct(c + c);
|
|
457
|
+
i++;
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
if (c === "?" &&
|
|
461
|
+
source[i + 1] === "." &&
|
|
462
|
+
!/[0-9]/.test(source[i + 2] ?? "")) {
|
|
463
|
+
context.pushPunct("?.");
|
|
464
|
+
i++;
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
if (c === "{")
|
|
468
|
+
depth++;
|
|
469
|
+
else if (c === "}" && --depth === 0)
|
|
470
|
+
return i;
|
|
471
|
+
context.pushPunct(c);
|
|
472
|
+
}
|
|
473
|
+
return source.length;
|
|
474
|
+
}
|
|
475
|
+
function isRegexAllowedAfter(previous) {
|
|
476
|
+
if (!previous)
|
|
477
|
+
return true;
|
|
478
|
+
if (previous.kind === "string" || previous.kind === "other")
|
|
479
|
+
return false;
|
|
480
|
+
if (previous.kind === "word")
|
|
481
|
+
return REGEX_PRECEDING_KEYWORDS.has(previous.value);
|
|
482
|
+
if (previous.value === ")" || previous.value === "}")
|
|
483
|
+
return previous.regexAllowedAfter === true;
|
|
484
|
+
return (previous.value !== "]" && previous.value !== "++" && previous.value !== "--");
|
|
485
|
+
}
|
|
486
|
+
/** Read one quoted literal and compute its JavaScript StringValue. */
|
|
487
|
+
function readQuotedString(source, start, quote) {
|
|
488
|
+
let value = "";
|
|
489
|
+
for (let index = start + 1; index < source.length;) {
|
|
490
|
+
const character = source[index];
|
|
491
|
+
if (character === quote) {
|
|
492
|
+
return { end: index + 1, value };
|
|
493
|
+
}
|
|
494
|
+
if (isLineTerminator(character)) {
|
|
495
|
+
return { end: index, value: null };
|
|
496
|
+
}
|
|
497
|
+
if (character !== "\\") {
|
|
498
|
+
value += character;
|
|
499
|
+
index++;
|
|
500
|
+
continue;
|
|
501
|
+
}
|
|
502
|
+
const escaped = source[index + 1];
|
|
503
|
+
if (escaped === undefined) {
|
|
504
|
+
return { end: source.length, value: null };
|
|
505
|
+
}
|
|
506
|
+
if (isLineTerminator(escaped)) {
|
|
507
|
+
index += escaped === "\r" && source[index + 2] === "\n" ? 3 : 2;
|
|
508
|
+
continue;
|
|
509
|
+
}
|
|
510
|
+
const simple = SIMPLE_STRING_ESCAPES[escaped];
|
|
511
|
+
if (simple !== undefined) {
|
|
512
|
+
value += simple;
|
|
513
|
+
index += 2;
|
|
514
|
+
continue;
|
|
515
|
+
}
|
|
516
|
+
if (escaped === "0") {
|
|
517
|
+
if (/[0-9]/.test(source[index + 2] ?? "")) {
|
|
518
|
+
return invalidQuotedString(source, start, quote);
|
|
519
|
+
}
|
|
520
|
+
value += "\0";
|
|
521
|
+
index += 2;
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
if (escaped >= "1" && escaped <= "9") {
|
|
525
|
+
return invalidQuotedString(source, start, quote);
|
|
526
|
+
}
|
|
527
|
+
if (escaped === "x") {
|
|
528
|
+
const digits = source.slice(index + 2, index + 4);
|
|
529
|
+
if (digits.length !== 2 || !/^[0-9a-fA-F]{2}$/.test(digits)) {
|
|
530
|
+
return invalidQuotedString(source, start, quote);
|
|
531
|
+
}
|
|
532
|
+
value += String.fromCharCode(Number.parseInt(digits, 16));
|
|
533
|
+
index += 4;
|
|
534
|
+
continue;
|
|
535
|
+
}
|
|
536
|
+
if (escaped === "u") {
|
|
537
|
+
if (source[index + 2] === "{") {
|
|
538
|
+
const close = source.indexOf("}", index + 3);
|
|
539
|
+
const digits = close < 0 ? "" : source.slice(index + 3, close);
|
|
540
|
+
const significantDigits = digits.replace(/^0+/, "") || "0";
|
|
541
|
+
const codePoint = Number.parseInt(digits, 16);
|
|
542
|
+
if (close < 0 ||
|
|
543
|
+
!/^[0-9a-fA-F]+$/.test(digits) ||
|
|
544
|
+
significantDigits.length > 6 ||
|
|
545
|
+
codePoint > 0x10ffff) {
|
|
546
|
+
return invalidQuotedString(source, start, quote);
|
|
547
|
+
}
|
|
548
|
+
value += String.fromCodePoint(codePoint);
|
|
549
|
+
index = close + 1;
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
const digits = source.slice(index + 2, index + 6);
|
|
553
|
+
if (digits.length !== 4 || !/^[0-9a-fA-F]{4}$/.test(digits)) {
|
|
554
|
+
return invalidQuotedString(source, start, quote);
|
|
555
|
+
}
|
|
556
|
+
value += String.fromCharCode(Number.parseInt(digits, 16));
|
|
557
|
+
index += 6;
|
|
558
|
+
continue;
|
|
559
|
+
}
|
|
560
|
+
// IdentityEscape / NonEscapeCharacter: the slash is discarded and the
|
|
561
|
+
// escaped source character contributes directly to the StringValue.
|
|
562
|
+
value += escaped;
|
|
563
|
+
index += 2;
|
|
564
|
+
}
|
|
565
|
+
return { end: source.length, value: null };
|
|
566
|
+
}
|
|
567
|
+
const SIMPLE_STRING_ESCAPES = {
|
|
568
|
+
"'": "'",
|
|
569
|
+
'"': '"',
|
|
570
|
+
"\\": "\\",
|
|
571
|
+
b: "\b",
|
|
572
|
+
f: "\f",
|
|
573
|
+
n: "\n",
|
|
574
|
+
r: "\r",
|
|
575
|
+
t: "\t",
|
|
576
|
+
v: "\v",
|
|
577
|
+
};
|
|
578
|
+
function invalidQuotedString(source, start, quote) {
|
|
579
|
+
const end = skipQuoted(source, start, quote);
|
|
580
|
+
return {
|
|
581
|
+
end: end < source.length && source[end] === quote ? end + 1 : end,
|
|
582
|
+
value: null,
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
function isLineTerminator(character) {
|
|
586
|
+
return (character === "\n" ||
|
|
587
|
+
character === "\r" ||
|
|
588
|
+
character === "\u2028" ||
|
|
589
|
+
character === "\u2029");
|
|
590
|
+
}
|
|
591
|
+
function skipRegularExpression(source, start, isIdentifierPart) {
|
|
592
|
+
let index = start + 1;
|
|
593
|
+
let inClass = false;
|
|
594
|
+
while (index < source.length) {
|
|
595
|
+
const character = source[index];
|
|
596
|
+
if (character === "\\") {
|
|
597
|
+
index += 2;
|
|
598
|
+
continue;
|
|
599
|
+
}
|
|
600
|
+
if (character === "\n")
|
|
601
|
+
break;
|
|
602
|
+
if (character === "[")
|
|
603
|
+
inClass = true;
|
|
604
|
+
else if (character === "]")
|
|
605
|
+
inClass = false;
|
|
606
|
+
else if (character === "/" && !inClass) {
|
|
607
|
+
index++;
|
|
608
|
+
break;
|
|
609
|
+
}
|
|
610
|
+
index++;
|
|
611
|
+
}
|
|
612
|
+
while (index < source.length && isIdentifierPart(source[index]))
|
|
613
|
+
index++;
|
|
614
|
+
return index;
|
|
615
|
+
}
|
|
616
|
+
function skipQuoted(source, start, quote) {
|
|
617
|
+
for (let i = start + 1; i < source.length; i++) {
|
|
618
|
+
if (source[i] === "\\") {
|
|
619
|
+
i++;
|
|
620
|
+
continue;
|
|
621
|
+
}
|
|
622
|
+
if (source[i] === quote || isLineTerminator(source[i]))
|
|
623
|
+
return i;
|
|
624
|
+
}
|
|
625
|
+
return source.length;
|
|
626
|
+
}
|
|
627
|
+
function skipTemplate(source, start) {
|
|
628
|
+
for (let i = start + 1; i < source.length; i++) {
|
|
629
|
+
if (source[i] === "\\") {
|
|
630
|
+
i++;
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
if (source[i] === "`")
|
|
634
|
+
return i;
|
|
635
|
+
if (source[i] === "$" && source[i + 1] === "{") {
|
|
636
|
+
i = findTemplateSubstitutionEnd(source, i + 2);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
return source.length;
|
|
640
|
+
}
|
|
278
641
|
//# sourceMappingURL=collectExternalPackageNames.js.map
|