@adep/web-container 0.1.1 → 0.2.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/dist/completion.d.ts +1 -1
- package/dist/container.d.ts +17 -0
- package/dist/function-fetch-proxy.d.ts +54 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +2044 -882
- package/dist/{preview.d.ts → preview-server.d.ts} +6 -1
- package/dist/relay-fetch.d.ts +32 -0
- package/dist/shell.d.ts +45 -0
- package/dist/vite-dev.d.ts +94 -0
- package/package.json +22 -17
- package/dist/web-container.esm.js +0 -5628
- package/dist/web-container.iife.js +0 -5646
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ var __export = (target, all) => {
|
|
|
8
8
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
//
|
|
11
|
+
// src/path.ts
|
|
12
12
|
var path_exports = {};
|
|
13
13
|
__export(path_exports, {
|
|
14
14
|
SEP: () => SEP,
|
|
@@ -73,15 +73,802 @@ function isValidSegment(name) {
|
|
|
73
73
|
}
|
|
74
74
|
var SEP;
|
|
75
75
|
var init_path = __esm({
|
|
76
|
-
"
|
|
76
|
+
"src/path.ts"() {
|
|
77
77
|
"use strict";
|
|
78
78
|
SEP = "/";
|
|
79
79
|
}
|
|
80
80
|
});
|
|
81
81
|
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
// src/node-resolve.ts
|
|
83
|
+
function parsePackageMeta(content, context) {
|
|
84
|
+
let parsed;
|
|
85
|
+
try {
|
|
86
|
+
parsed = JSON.parse(content);
|
|
87
|
+
} catch {
|
|
88
|
+
throw new ResolveError(`${context} \u4E0D\u662F\u5408\u6CD5 JSON`);
|
|
89
|
+
}
|
|
90
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
91
|
+
throw new ResolveError(`${context} \u9876\u5C42\u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
92
|
+
}
|
|
93
|
+
return parsed;
|
|
94
|
+
}
|
|
95
|
+
function withinPackage(pkgDirAbs, rel, context) {
|
|
96
|
+
if (typeof rel !== "string" || rel === "") {
|
|
97
|
+
throw new ResolveError(`${context}\uFF1A\u5165\u53E3\u8DEF\u5F84\u5FC5\u987B\u662F\u5B57\u7B26\u4E32`);
|
|
98
|
+
}
|
|
99
|
+
if (!rel.startsWith("./") && !rel.startsWith("../") && rel.startsWith("/")) {
|
|
100
|
+
throw new ResolveError(`${context}\uFF1A\u5165\u53E3\u8DEF\u5F84 "${rel}" \u5FC5\u987B\u662F\u5305\u5185\u76F8\u5BF9\u8DEF\u5F84\uFF08\u4EE5 ./ \u5F00\u5934\uFF09`);
|
|
101
|
+
}
|
|
102
|
+
const abs = join(pkgDirAbs, rel.startsWith(".") ? rel : `./${rel}`);
|
|
103
|
+
if (!abs.startsWith(`${pkgDirAbs}/`)) {
|
|
104
|
+
throw new ResolveError(`${context}\uFF1A\u5165\u53E3\u8DEF\u5F84 "${rel}" \u8D8A\u51FA\u5305\u76EE\u5F55\uFF0C\u5DF2\u62D2\u7EDD`);
|
|
105
|
+
}
|
|
106
|
+
return abs;
|
|
107
|
+
}
|
|
108
|
+
function resolvePackageEntry(meta, pkgDirAbs, condition) {
|
|
109
|
+
const context = `\u5305 ${meta.name ?? meta.version ?? pkgDirAbs} \u7684 exports/main \u58F0\u660E`;
|
|
110
|
+
const exp = meta.exports;
|
|
111
|
+
if (exp !== void 0 && exp !== null) {
|
|
112
|
+
if (typeof exp === "string") return withinPackage(pkgDirAbs, exp, context);
|
|
113
|
+
if (typeof exp !== "object" || Array.isArray(exp)) {
|
|
114
|
+
throw new ResolveError(`${context} \u5F62\u6001\u4E0D\u652F\u6301\uFF08exports \u53EA\u80FD\u662F\u5B57\u7B26\u4E32\u6216\u5BF9\u8C61\uFF09`);
|
|
115
|
+
}
|
|
116
|
+
const dot = exp["."];
|
|
117
|
+
if (dot === void 0) {
|
|
118
|
+
throw new ResolveError(
|
|
119
|
+
`${context} \u53EA\u58F0\u660E\u4E86\u5B50\u8DEF\u5F84\u6620\u5C04\u3001\u6CA1\u6709 "." \u5165\u53E3\uFF0C\u5F53\u524D\u4EC5\u652F\u6301 "." \u4E0E\u5B57\u7B26\u4E32\u5F62\u6001`
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
if (typeof dot === "string") return withinPackage(pkgDirAbs, dot, context);
|
|
123
|
+
if (dot === null)
|
|
124
|
+
throw new ResolveError(`${context}\uFF1Aexports["."] \u4E3A null\uFF08\u8BE5\u6761\u4EF6\u88AB\u663E\u5F0F\u6392\u9664\uFF09\uFF0C\u65E0\u6CD5\u89E3\u6790\u5165\u53E3`);
|
|
125
|
+
if (typeof dot !== "object" || Array.isArray(dot)) {
|
|
126
|
+
throw new ResolveError(`${context}\uFF1Aexports["."] \u5F62\u6001\u4E0D\u652F\u6301\uFF08\u4EC5\u63A5\u53D7\u5B57\u7B26\u4E32\u6216\u6761\u4EF6\u5BF9\u8C61\uFF09`);
|
|
127
|
+
}
|
|
128
|
+
const conditions = dot;
|
|
129
|
+
const order = condition === "import" ? ["import", "node", "default"] : ["require", "node", "default"];
|
|
130
|
+
for (const key of order) {
|
|
131
|
+
const value = conditions[key];
|
|
132
|
+
if (typeof value === "string") return withinPackage(pkgDirAbs, value, context);
|
|
133
|
+
if (value !== void 0 && (typeof value !== "object" || value === null)) {
|
|
134
|
+
throw new ResolveError(
|
|
135
|
+
`${context}\uFF1Aexports["."]["${key}"] \u5F62\u6001\u4E0D\u652F\u6301\uFF08\u4EC5\u63A5\u53D7\u5B57\u7B26\u4E32\u6216 null\uFF09`
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
throw new ResolveError(
|
|
140
|
+
`${context}\uFF1Aexports["."] \u6761\u4EF6\u5BF9\u8C61\u91CC\u6CA1\u6709 ${condition}/node/default \u5B57\u7B26\u4E32\u5165\u53E3\uFF08\u5B50\u8DEF\u5F84/\u5D4C\u5957\u6761\u4EF6\u4E0D\u652F\u6301\uFF09`
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
if (meta.main !== void 0) {
|
|
144
|
+
if (typeof meta.main !== "string") {
|
|
145
|
+
throw new ResolveError(`${context}\uFF1Amain \u5FC5\u987B\u662F\u5B57\u7B26\u4E32`);
|
|
146
|
+
}
|
|
147
|
+
return withinPackage(pkgDirAbs, meta.main, context);
|
|
148
|
+
}
|
|
149
|
+
return join(pkgDirAbs, "index.js");
|
|
150
|
+
}
|
|
151
|
+
function splitPackageSpecifier(spec) {
|
|
152
|
+
if (spec.startsWith("@")) {
|
|
153
|
+
const first = spec.indexOf("/");
|
|
154
|
+
if (first === -1) return { name: spec, subpath: "" };
|
|
155
|
+
const second = spec.indexOf("/", first + 1);
|
|
156
|
+
if (second === -1) return { name: spec, subpath: "" };
|
|
157
|
+
return { name: spec.slice(0, second), subpath: spec.slice(second + 1) };
|
|
158
|
+
}
|
|
159
|
+
const slash = spec.indexOf("/");
|
|
160
|
+
if (slash === -1) return { name: spec, subpath: "" };
|
|
161
|
+
return { name: spec.slice(0, slash), subpath: spec.slice(slash + 1) };
|
|
162
|
+
}
|
|
163
|
+
function asFile(fs, baseAbs) {
|
|
164
|
+
if (fs.exists(baseAbs) && !fsIsDirectory(fs, baseAbs)) return baseAbs;
|
|
165
|
+
for (const ext of RESOLVE_EXTENSIONS) {
|
|
166
|
+
const candidate = `${baseAbs}${ext}`;
|
|
167
|
+
if (fs.exists(candidate) && !fsIsDirectory(fs, candidate)) return candidate;
|
|
168
|
+
}
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
function asDirectory(fs, dirAbs) {
|
|
172
|
+
if (!fs.exists(dirAbs) || !fsIsDirectory(fs, dirAbs)) return null;
|
|
173
|
+
const manifest = `${dirAbs}/package.json`;
|
|
174
|
+
if (fs.exists(manifest)) {
|
|
175
|
+
const meta = parsePackageMeta(fs.readFile(manifest), manifest);
|
|
176
|
+
const entry = resolvePackageEntry(meta, dirAbs, "require");
|
|
177
|
+
const hit = asFile(fs, entry);
|
|
178
|
+
if (hit !== null) return hit;
|
|
179
|
+
}
|
|
180
|
+
return asFile(fs, join(dirAbs, "index"));
|
|
181
|
+
}
|
|
182
|
+
function fsIsDirectory(fs, path) {
|
|
183
|
+
const probe = fs;
|
|
184
|
+
return probe.isDirectory?.(path) ?? false;
|
|
185
|
+
}
|
|
186
|
+
function resolveImport(fs, specifier, fromFileAbs, condition = "require") {
|
|
187
|
+
if (specifier === "") throw new ResolveError("\u7A7A\u8BF4\u660E\u7B26\uFF1A\u65E0\u6CD5\u89E3\u6790\u6A21\u5757");
|
|
188
|
+
if (specifier.startsWith("./") || specifier.startsWith("../")) {
|
|
189
|
+
const base = join(dirname(fromFileAbs), specifier);
|
|
190
|
+
const hit = asFile(fs, base) ?? asDirectory(fs, base);
|
|
191
|
+
if (hit === null) {
|
|
192
|
+
throw new ResolveError(
|
|
193
|
+
`\u627E\u4E0D\u5230\u76F8\u5BF9\u6A21\u5757 "${specifier}"\uFF08\u81EA ${dirname(fromFileAbs)}\uFF0C\u8BD5\u8FC7\u6269\u5C55\u540D ${RESOLVE_EXTENSIONS.join("/")} \u4E0E\u76EE\u5F55\u7D22\u5F15\uFF09`
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
return hit;
|
|
197
|
+
}
|
|
198
|
+
if (specifier.startsWith("/")) {
|
|
199
|
+
const hit = asFile(fs, specifier) ?? asDirectory(fs, specifier);
|
|
200
|
+
if (hit === null) throw new ResolveError(`\u627E\u4E0D\u5230\u7EDD\u5BF9\u8DEF\u5F84\u6A21\u5757 "${specifier}"`);
|
|
201
|
+
return hit;
|
|
202
|
+
}
|
|
203
|
+
const { name, subpath } = splitPackageSpecifier(specifier);
|
|
204
|
+
if (name.startsWith("node:")) {
|
|
205
|
+
throw new ResolveError(`\u6D4F\u89C8\u5668\u6C99\u7BB1\u4E0D\u63D0\u4F9B Node \u5185\u5EFA\u6A21\u5757 "${specifier}"`);
|
|
206
|
+
}
|
|
207
|
+
let dir = dirname(fromFileAbs);
|
|
208
|
+
for (; ; ) {
|
|
209
|
+
const pkgDir = join(dir, "node_modules", name);
|
|
210
|
+
if (fs.exists(pkgDir) && fsIsDirectory(fs, pkgDir)) {
|
|
211
|
+
if (subpath === "") {
|
|
212
|
+
const manifest = `${pkgDir}/package.json`;
|
|
213
|
+
if (fs.exists(manifest)) {
|
|
214
|
+
const meta = parsePackageMeta(fs.readFile(manifest), manifest);
|
|
215
|
+
const entry = resolvePackageEntry(meta, pkgDir, condition);
|
|
216
|
+
const hit = asFile(fs, entry) ?? asDirectory(fs, entry);
|
|
217
|
+
if (hit !== null) return hit;
|
|
218
|
+
} else {
|
|
219
|
+
const hit = asFile(fs, join(pkgDir, "index")) ?? asDirectory(fs, pkgDir);
|
|
220
|
+
if (hit !== null) return hit;
|
|
221
|
+
}
|
|
222
|
+
} else {
|
|
223
|
+
const base = join(pkgDir, subpath);
|
|
224
|
+
const hit = asFile(fs, base) ?? asDirectory(fs, base);
|
|
225
|
+
if (hit !== null) return hit;
|
|
226
|
+
}
|
|
227
|
+
throw new ResolveError(
|
|
228
|
+
`\u5728 ${pkgDir} \u627E\u5230\u4E86\u5305 "${name}"\uFF0C\u4F46\u89E3\u6790\u8BF4\u660E\u7B26 "${specifier}" \u5931\u8D25\uFF08\u5B50\u8DEF\u5F84\u4E0D\u5B58\u5728\uFF0C\u6216 exports/main \u6307\u5411\u7684\u5165\u53E3\u6587\u4EF6\u7F3A\u5931\uFF09`
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
const parent = dirname(dir);
|
|
232
|
+
if (parent === dir) break;
|
|
233
|
+
dir = parent;
|
|
234
|
+
}
|
|
235
|
+
throw new ResolveError(
|
|
236
|
+
`\u627E\u4E0D\u5230\u5305 "${specifier}"\uFF1A${dirname(fromFileAbs)} \u53CA\u5176\u5404\u7EA7\u7236\u76EE\u5F55\u4E0B\u90FD\u6CA1\u6709 node_modules/${name}\uFF08\u5148\u5728\u7EC8\u7AEF npm install ${name}\uFF09`
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
var RESOLVE_EXTENSIONS, ResolveError;
|
|
240
|
+
var init_node_resolve = __esm({
|
|
241
|
+
"src/node-resolve.ts"() {
|
|
242
|
+
"use strict";
|
|
243
|
+
init_path();
|
|
244
|
+
RESOLVE_EXTENSIONS = [".js", ".mjs", ".cjs", ".json"];
|
|
245
|
+
ResolveError = class extends Error {
|
|
246
|
+
constructor(message) {
|
|
247
|
+
super(message);
|
|
248
|
+
this.name = "ResolveError";
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// src/strip-types.ts
|
|
255
|
+
function isAnnotationLeader(token) {
|
|
256
|
+
return ANNOTATION_LEADERS.has(token) || TS_MODIFIERS.has(token);
|
|
257
|
+
}
|
|
258
|
+
function stripTypes(source) {
|
|
259
|
+
return new Eraser(source).run();
|
|
260
|
+
}
|
|
261
|
+
var UnsupportedTsSyntaxError, IDENT_START, IDENT_PART, DECL_KEYWORDS, TS_MODIFIERS, ANNOTATION_LEADERS, Eraser;
|
|
262
|
+
var init_strip_types = __esm({
|
|
263
|
+
"src/strip-types.ts"() {
|
|
264
|
+
"use strict";
|
|
265
|
+
UnsupportedTsSyntaxError = class extends Error {
|
|
266
|
+
constructor(construct, line) {
|
|
267
|
+
super(`\u4E0D\u53D7\u652F\u6301\u7684 TypeScript \u8BED\u6CD5\uFF1A${construct}\uFF08\u7B2C ${line} \u884C\uFF09\u2014\u2014\u6D4F\u89C8\u5668\u6D4B\u8BD5\u6C99\u7BB1\u53EA\u64E6\u9664\u7C7B\u578B\u6807\u6CE8`);
|
|
268
|
+
this.construct = construct;
|
|
269
|
+
this.line = line;
|
|
270
|
+
this.name = "UnsupportedTsSyntaxError";
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
IDENT_START = /[A-Za-z_$]/;
|
|
274
|
+
IDENT_PART = /[A-Za-z0-9_$]/;
|
|
275
|
+
DECL_KEYWORDS = /* @__PURE__ */ new Set(["const", "let", "var"]);
|
|
276
|
+
TS_MODIFIERS = /* @__PURE__ */ new Set([
|
|
277
|
+
"private",
|
|
278
|
+
"protected",
|
|
279
|
+
"public",
|
|
280
|
+
"readonly",
|
|
281
|
+
"abstract",
|
|
282
|
+
"override",
|
|
283
|
+
"declare"
|
|
284
|
+
]);
|
|
285
|
+
ANNOTATION_LEADERS = /* @__PURE__ */ new Set(["(", ",", "{", ";", "erased", "modifier"]);
|
|
286
|
+
Eraser = class {
|
|
287
|
+
constructor(src) {
|
|
288
|
+
this.src = src;
|
|
289
|
+
this.chars = [...src];
|
|
290
|
+
}
|
|
291
|
+
chars;
|
|
292
|
+
tokens = [];
|
|
293
|
+
frames = [];
|
|
294
|
+
/** 刚消费 `class` 头:下一个 `{` 是类体起始。 */
|
|
295
|
+
pendingClassBody = false;
|
|
296
|
+
/** 刚消费 `=>`:其后的 `(` 是箭头函数参数列表(声明位)。 */
|
|
297
|
+
afterArrow = false;
|
|
298
|
+
/** 刚消费 `function` 关键字:其后的标识符是函数名,再后是参数列表。 */
|
|
299
|
+
afterFunctionKeyword = false;
|
|
300
|
+
/** 最近一次 `)` 闭合的是不是参数列表(返回值标注判据)。 */
|
|
301
|
+
justClosedDeclareParen = false;
|
|
302
|
+
i = 0;
|
|
303
|
+
run() {
|
|
304
|
+
while (this.i < this.src.length) {
|
|
305
|
+
if (/\s/.test(this.ch())) {
|
|
306
|
+
this.i++;
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
if (this.copyTrivia()) continue;
|
|
310
|
+
if (this.copyLiteral()) continue;
|
|
311
|
+
if (this.copyRegexIfNeeded()) continue;
|
|
312
|
+
if (this.readNumber()) continue;
|
|
313
|
+
const word = this.readWord();
|
|
314
|
+
if (word !== null) this.handleWord(word);
|
|
315
|
+
else this.handlePunctuation();
|
|
316
|
+
}
|
|
317
|
+
return this.chars.join("");
|
|
318
|
+
}
|
|
319
|
+
/* ———————————————————— 游标原语 ———————————————————— */
|
|
320
|
+
ch(offset = 0) {
|
|
321
|
+
return this.src[this.i + offset] ?? "";
|
|
322
|
+
}
|
|
323
|
+
/** 回看第 `back` 个显著 token(1 = 最近一个)。 */
|
|
324
|
+
tokenAt(back) {
|
|
325
|
+
return this.tokens[this.tokens.length - back] ?? "";
|
|
326
|
+
}
|
|
327
|
+
/** 擦除结果里 `from` 往前的最后一个非空白字符(看的是已擦除文本,不是原文)。 */
|
|
328
|
+
prevSignificantChar(from) {
|
|
329
|
+
for (let j = from; j >= 0; j--) {
|
|
330
|
+
const c = this.chars[j];
|
|
331
|
+
if (c !== " " && c !== " " && c !== "\n" && c !== "\r") return c;
|
|
332
|
+
}
|
|
333
|
+
return "";
|
|
334
|
+
}
|
|
335
|
+
/** 擦除 `[from, to)`:填等长空格,换行照抄——擦完行号与原文件一致。 */
|
|
336
|
+
erase(from, to) {
|
|
337
|
+
for (let j = Math.max(from, 0); j < to; j++) this.chars[j] = this.src[j] === "\n" ? "\n" : " ";
|
|
338
|
+
}
|
|
339
|
+
pushToken(token) {
|
|
340
|
+
this.afterArrow = token === "=>";
|
|
341
|
+
this.afterFunctionKeyword = token === "function";
|
|
342
|
+
this.tokens.push(token);
|
|
343
|
+
}
|
|
344
|
+
/** 数字面量整段消费(否则 `1.5` 会被拆成 `1` `.` `5`,`.` 参与判据时误判)。 */
|
|
345
|
+
readNumber() {
|
|
346
|
+
if (!/[0-9]/.test(this.ch())) return false;
|
|
347
|
+
let end = this.i;
|
|
348
|
+
while (end < this.src.length && /[0-9a-fA-FxXoObB._]/.test(this.src[end])) end++;
|
|
349
|
+
this.i = end;
|
|
350
|
+
this.pushToken("number");
|
|
351
|
+
return true;
|
|
352
|
+
}
|
|
353
|
+
readWord() {
|
|
354
|
+
if (!IDENT_START.test(this.ch())) return null;
|
|
355
|
+
let end = this.i;
|
|
356
|
+
while (end < this.src.length && IDENT_PART.test(this.src[end])) end++;
|
|
357
|
+
const word = this.src.slice(this.i, end);
|
|
358
|
+
this.i = end;
|
|
359
|
+
this.pushToken(word);
|
|
360
|
+
return word;
|
|
361
|
+
}
|
|
362
|
+
/* —————————————— 字面量与注释:整段跳过(内容永不参与判定) —————————————— */
|
|
363
|
+
copyTrivia() {
|
|
364
|
+
const head = this.ch();
|
|
365
|
+
const next = this.ch(1);
|
|
366
|
+
if (head === "/" && next === "/") {
|
|
367
|
+
while (this.i < this.src.length && this.ch() !== "\n") this.i++;
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
if (head === "/" && next === "*") {
|
|
371
|
+
const end = this.src.indexOf("*/", this.i + 2);
|
|
372
|
+
this.i = end === -1 ? this.src.length : end + 2;
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
copyLiteral() {
|
|
378
|
+
const head = this.ch();
|
|
379
|
+
if (head !== "'" && head !== '"' && head !== "`") return false;
|
|
380
|
+
this.i = head === "`" ? this.scanTemplate(this.i) : this.scanQuoted(this.i);
|
|
381
|
+
this.pushToken(head === "`" ? "template" : "string");
|
|
382
|
+
return true;
|
|
383
|
+
}
|
|
384
|
+
/** 引号串结束下标(未闭合则到行尾,把真实的语法错误留给求值期)。 */
|
|
385
|
+
scanQuoted(start) {
|
|
386
|
+
const quote = this.src[start];
|
|
387
|
+
let j = start + 1;
|
|
388
|
+
while (j < this.src.length) {
|
|
389
|
+
const c = this.src[j];
|
|
390
|
+
if (c === "\\") j += 2;
|
|
391
|
+
else if (c === quote) return j + 1;
|
|
392
|
+
else if (c === "\n") return j;
|
|
393
|
+
else j++;
|
|
394
|
+
}
|
|
395
|
+
return this.src.length;
|
|
396
|
+
}
|
|
397
|
+
/** 模板串结束下标(`${}` 内按大括号配平,内层字符串 / 模板一并识别)。 */
|
|
398
|
+
scanTemplate(start) {
|
|
399
|
+
let j = start + 1;
|
|
400
|
+
while (j < this.src.length) {
|
|
401
|
+
const c = this.src[j];
|
|
402
|
+
if (c === "\\") j += 2;
|
|
403
|
+
else if (c === "`") return j + 1;
|
|
404
|
+
else if (c === "$" && this.src[j + 1] === "{") j = this.scanInterpolation(j + 2);
|
|
405
|
+
else j++;
|
|
406
|
+
}
|
|
407
|
+
return this.src.length;
|
|
408
|
+
}
|
|
409
|
+
scanInterpolation(start) {
|
|
410
|
+
let depth = 1;
|
|
411
|
+
let j = start;
|
|
412
|
+
while (j < this.src.length) {
|
|
413
|
+
const c = this.src[j];
|
|
414
|
+
if (c === "\\") j += 2;
|
|
415
|
+
else if (c === "'" || c === '"') j = this.scanQuoted(j);
|
|
416
|
+
else if (c === "`") j = this.scanTemplate(j);
|
|
417
|
+
else if (c === "{") {
|
|
418
|
+
depth++;
|
|
419
|
+
j++;
|
|
420
|
+
} else if (c === "}") {
|
|
421
|
+
depth--;
|
|
422
|
+
j++;
|
|
423
|
+
if (depth === 0) return j;
|
|
424
|
+
} else j++;
|
|
425
|
+
}
|
|
426
|
+
return this.src.length;
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* 正则字面量:`/` 只在「运算符位」才是正则起始(前一个显著字符不是值结尾)。
|
|
430
|
+
* 判不准时按除号处理——除号后面不会有需要擦除的类型标注,最坏只是漏擦。
|
|
431
|
+
* 有了这条,`const url = /https:\/\//` 里的 `:` 与 `//` 才不会被人当语法(恒等用例之一)。
|
|
432
|
+
*/
|
|
433
|
+
copyRegexIfNeeded() {
|
|
434
|
+
if (this.ch() !== "/") return false;
|
|
435
|
+
const prev = this.prevSignificantChar(this.i - 1);
|
|
436
|
+
const valueEnding = prev !== "" && (IDENT_PART.test(prev) || prev === ")" || prev === "]" || prev === "}" || prev === '"' || prev === "'" || prev === "`");
|
|
437
|
+
if (valueEnding) return false;
|
|
438
|
+
const end = this.scanRegex(this.i);
|
|
439
|
+
if (end === null) return false;
|
|
440
|
+
this.i = end;
|
|
441
|
+
this.pushToken("regex");
|
|
442
|
+
return true;
|
|
443
|
+
}
|
|
444
|
+
scanRegex(start) {
|
|
445
|
+
let j = start + 1;
|
|
446
|
+
let inClass = false;
|
|
447
|
+
while (j < this.src.length) {
|
|
448
|
+
const c = this.src[j];
|
|
449
|
+
if (c === "\\") {
|
|
450
|
+
j += 2;
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
if (c === "\n") return null;
|
|
454
|
+
if (c === "[") inClass = true;
|
|
455
|
+
else if (c === "]") inClass = false;
|
|
456
|
+
else if (c === "/" && !inClass) {
|
|
457
|
+
j++;
|
|
458
|
+
while (j < this.src.length && /[a-z]/i.test(this.src[j])) j++;
|
|
459
|
+
return j;
|
|
460
|
+
}
|
|
461
|
+
j++;
|
|
462
|
+
}
|
|
463
|
+
return null;
|
|
464
|
+
}
|
|
465
|
+
/* ———————————————— 关键字与声明结构 ———————————————— */
|
|
466
|
+
handleWord(word) {
|
|
467
|
+
const start = this.i - word.length;
|
|
468
|
+
if (this.tokenAt(2) === ".") {
|
|
469
|
+
this.consumeGenericsAfterName();
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
switch (word) {
|
|
473
|
+
case "interface": {
|
|
474
|
+
const after = this.skipWhitespaceFrom(this.skipNameAndParams(start + word.length));
|
|
475
|
+
if (after === null || this.src[after] !== "{") return;
|
|
476
|
+
const end = this.scanBalanced(after, "{", "}");
|
|
477
|
+
this.erase(start, end);
|
|
478
|
+
this.i = end;
|
|
479
|
+
this.pushToken("erased");
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
case "type": {
|
|
483
|
+
const after = this.skipWhitespaceFrom(this.skipNameAndParams(this.i));
|
|
484
|
+
if (after === null || this.src[after] !== "=") return;
|
|
485
|
+
const end = this.scanTypeExpression(after + 1, true);
|
|
486
|
+
this.erase(start, end);
|
|
487
|
+
this.i = end;
|
|
488
|
+
this.pushToken("erased");
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
case "import":
|
|
492
|
+
case "export": {
|
|
493
|
+
if (!/^\s+type\b/.test(this.src.slice(this.i))) return;
|
|
494
|
+
const after = this.skipNameAndParams(this.i);
|
|
495
|
+
if (after === null) return;
|
|
496
|
+
const end = this.scanStatementEnd(after);
|
|
497
|
+
this.erase(start, end);
|
|
498
|
+
this.i = end;
|
|
499
|
+
this.pushToken("erased");
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
case "enum":
|
|
503
|
+
case "namespace":
|
|
504
|
+
throw new UnsupportedTsSyntaxError(word, this.lineAt(start));
|
|
505
|
+
case "implements": {
|
|
506
|
+
let j = this.i;
|
|
507
|
+
while (j < this.src.length && this.src[j] !== "{" && this.src[j] !== ";") j++;
|
|
508
|
+
this.erase(start, j);
|
|
509
|
+
this.i = j;
|
|
510
|
+
this.pushToken("erased");
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
case "class":
|
|
514
|
+
this.pendingClassBody = true;
|
|
515
|
+
return;
|
|
516
|
+
case "as":
|
|
517
|
+
case "satisfies": {
|
|
518
|
+
if (this.tokenAt(2) === "*" || this.tokenAt(2) === "type") return;
|
|
519
|
+
const nextChar = this.skipWhitespaceFrom(this.i);
|
|
520
|
+
if (nextChar === null || "=,:]})".includes(this.src[nextChar])) return;
|
|
521
|
+
const end = this.scanTypeExpression(this.i, false);
|
|
522
|
+
this.erase(start, end);
|
|
523
|
+
this.i = end;
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
default: {
|
|
527
|
+
if (TS_MODIFIERS.has(word) && this.inClassBody()) {
|
|
528
|
+
const next = this.peekWord();
|
|
529
|
+
if (next === "" || /[{(=]/.test(next[0])) return;
|
|
530
|
+
this.erase(start, this.i);
|
|
531
|
+
this.pushToken("modifier");
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
this.consumeGenericsAfterName();
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
peekWord() {
|
|
539
|
+
let j = this.i;
|
|
540
|
+
while (j < this.src.length && /\s/.test(this.src[j])) j++;
|
|
541
|
+
let end = j;
|
|
542
|
+
while (end < this.src.length && IDENT_PART.test(this.src[end])) end++;
|
|
543
|
+
return this.src.slice(j, end);
|
|
544
|
+
}
|
|
545
|
+
lineAt(index) {
|
|
546
|
+
let line = 1;
|
|
547
|
+
for (let j = 0; j < index; j++) if (this.src[j] === "\n") line++;
|
|
548
|
+
return line;
|
|
549
|
+
}
|
|
550
|
+
/** `:` 之前紧邻的是不是 `)`(返回值标注位;用于决定是否允许顶层 `=>` 续读)。 */
|
|
551
|
+
lastSignificantCharIsCloseParen(at) {
|
|
552
|
+
return this.prevSignificantChar(at - 1) === ")";
|
|
553
|
+
}
|
|
554
|
+
inClassBody() {
|
|
555
|
+
return this.frames[this.frames.length - 1]?.classBody === true;
|
|
556
|
+
}
|
|
557
|
+
/** 当前帧是不是声明位(参数列表 / 类体)——只有这里才允许把 `:` 判成标注。 */
|
|
558
|
+
inDeclarePosition() {
|
|
559
|
+
const frame = this.frames[this.frames.length - 1];
|
|
560
|
+
return frame !== void 0 && (frame.declare || frame.classBody);
|
|
561
|
+
}
|
|
562
|
+
/* ———————————————— 标点与括号帧 ———————————————— */
|
|
563
|
+
handlePunctuation() {
|
|
564
|
+
const c = this.ch();
|
|
565
|
+
const start = this.i;
|
|
566
|
+
if (c === "(" || c === "{" || c === "[") {
|
|
567
|
+
const frame = {
|
|
568
|
+
kind: c === "(" ? "paren" : c === "{" ? "brace" : "bracket",
|
|
569
|
+
declare: false,
|
|
570
|
+
classBody: false
|
|
571
|
+
};
|
|
572
|
+
if (c === "{" && this.pendingClassBody) {
|
|
573
|
+
frame.classBody = true;
|
|
574
|
+
this.pendingClassBody = false;
|
|
575
|
+
}
|
|
576
|
+
if (c === "(" && this.isDeclareParen(start)) frame.declare = true;
|
|
577
|
+
this.frames.push(frame);
|
|
578
|
+
this.i++;
|
|
579
|
+
this.pushToken(c);
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
if (c === ")" || c === "}" || c === "]") {
|
|
583
|
+
const closed = this.frames.pop();
|
|
584
|
+
if (closed?.kind === "paren") this.justClosedDeclareParen = closed.declare;
|
|
585
|
+
this.i++;
|
|
586
|
+
this.pushToken(c);
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
if (c === ":") {
|
|
590
|
+
if (this.ch(1) === ":") {
|
|
591
|
+
this.i++;
|
|
592
|
+
this.pushToken(":");
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
const returnType = this.isAnnotationColon(start) && this.lastSignificantCharIsCloseParen(start);
|
|
596
|
+
if (returnType || this.isAnnotationColon(start)) {
|
|
597
|
+
const end = this.scanTypeExpression(start + 1, false, returnType);
|
|
598
|
+
this.erase(start, end);
|
|
599
|
+
this.i = end;
|
|
600
|
+
this.pushToken("erased");
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
this.i++;
|
|
604
|
+
this.pushToken(":");
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
if (c === "?" && this.ch(1) !== "?" && this.ch(1) !== ".") {
|
|
608
|
+
let j = start + 1;
|
|
609
|
+
while (j < this.src.length && (this.src[j] === " " || this.src[j] === " ")) j++;
|
|
610
|
+
if (this.src[j] === ":" && this.inDeclarePosition() && IDENT_PART.test(this.prevSignificantChar(start - 1))) {
|
|
611
|
+
const end = this.scanTypeExpression(j + 1, false);
|
|
612
|
+
this.erase(start, end);
|
|
613
|
+
this.i = end;
|
|
614
|
+
this.pushToken("erased");
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
this.i++;
|
|
618
|
+
this.pushToken("?");
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
if (c === "!" && this.ch(1) !== "=" && this.isPostfixBang(start)) {
|
|
622
|
+
this.erase(start, start + 1);
|
|
623
|
+
this.i++;
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
if (c === "=" && this.ch(1) === ">") {
|
|
627
|
+
this.i += 2;
|
|
628
|
+
this.pushToken("=>");
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
if (c === "<" && this.consumeLeadingGenerics(start)) return;
|
|
632
|
+
this.i++;
|
|
633
|
+
this.pushToken(c);
|
|
634
|
+
}
|
|
635
|
+
/** 空白跳跃(供声明结构找下一个非空白字符;不改游标,纯函数)。 */
|
|
636
|
+
skipWhitespaceFrom(index) {
|
|
637
|
+
if (index === null) return null;
|
|
638
|
+
let j = index;
|
|
639
|
+
while (j < this.src.length && /\s/.test(this.src[j])) j++;
|
|
640
|
+
return j;
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* 运算符位上的泛型参数列表(`const f = <T, >(x: T) => x`、`arr.map<T2>(...)` 之外的箭头形态):
|
|
644
|
+
* 前一个显著字符不是值结尾(否则是 `a < b` 比较),且 `>` 之后紧跟 `(` 才擦。
|
|
645
|
+
*/
|
|
646
|
+
consumeLeadingGenerics(at) {
|
|
647
|
+
if (!"([{,=;:>!&|?".includes(this.prevSignificantChar(at - 1))) return false;
|
|
648
|
+
const end = this.scanTypeParams(at);
|
|
649
|
+
if (end === null) return false;
|
|
650
|
+
const after = this.skipWhitespaceFrom(end);
|
|
651
|
+
if (after === null || this.src[after] !== "(") return false;
|
|
652
|
+
this.erase(at, end);
|
|
653
|
+
this.i = end;
|
|
654
|
+
return true;
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* `(` 是否为参数列表(声明位):`function f(`、类体里的方法 `m(`、箭头 `=> (`,
|
|
658
|
+
* 以及出现在 `(` `,` `=` `>` `:` `[` `{` `}` `;` 之后(如 `map((v: T) => v)` 的内层括号)。
|
|
659
|
+
* 调用位 `foo(` 与控制流 `if (` `for (` 都不算。
|
|
660
|
+
* 误判成声明位也不足以擦掉什么:`:` 还要过 `isAnnotationColon` 的「名字前驱必须是
|
|
661
|
+
* `(` `,` `{` `;` 修饰符」这一关,而三元表达式的中间段前面必然是 `?`。
|
|
662
|
+
*/
|
|
663
|
+
isDeclareParen(at) {
|
|
664
|
+
if (this.afterFunctionKeyword) return true;
|
|
665
|
+
if (this.tokenAt(2) === "function" && IDENT_START.test(this.tokenAt(1))) return true;
|
|
666
|
+
if (this.afterArrow) return true;
|
|
667
|
+
if (this.inClassBody() && IDENT_PART.test(this.prevSignificantChar(at - 1))) return true;
|
|
668
|
+
const prev = this.prevSignificantChar(at - 1);
|
|
669
|
+
return prev === "" || "(,=>:[{};".includes(prev);
|
|
670
|
+
}
|
|
671
|
+
/** `:` 是类型标注吗:看名字前驱 token 与所在帧(对象键、三元、`case` 一律排除)。 */
|
|
672
|
+
isAnnotationColon(at) {
|
|
673
|
+
const prev = this.prevSignificantChar(at - 1);
|
|
674
|
+
if (prev === ")") return this.justClosedDeclareParen;
|
|
675
|
+
if (prev === "}" || prev === "]") {
|
|
676
|
+
const frame = this.frames[this.frames.length - 1];
|
|
677
|
+
return frame?.kind === "paren" && frame.declare;
|
|
678
|
+
}
|
|
679
|
+
if (!IDENT_PART.test(prev)) return false;
|
|
680
|
+
if (this.tokenAt(1) === "number" || this.tokenAt(1) === "string") return false;
|
|
681
|
+
if (this.inDeclarePosition() && isAnnotationLeader(this.tokenAt(2))) return true;
|
|
682
|
+
return DECL_KEYWORDS.has(this.tokenAt(2));
|
|
683
|
+
}
|
|
684
|
+
/** 非空断言 `x!`:前一个显著字符是值结尾,且后一个非空白字符不是操作数起始。 */
|
|
685
|
+
isPostfixBang(at) {
|
|
686
|
+
const prev = this.prevSignificantChar(at - 1);
|
|
687
|
+
if (!(IDENT_PART.test(prev) || prev === ")" || prev === "]")) return false;
|
|
688
|
+
let j = at + 1;
|
|
689
|
+
while (j < this.src.length && (this.src[j] === " " || this.src[j] === " ")) j++;
|
|
690
|
+
const next = this.src[j] ?? "";
|
|
691
|
+
if (next === "") return true;
|
|
692
|
+
return !(IDENT_START.test(next) || /[0-9]/.test(next) || "(![~".includes(next));
|
|
693
|
+
}
|
|
694
|
+
/* ———————————————— 区间扫描(全部字符串 / 注释安全) ———————————————— */
|
|
695
|
+
/** 跳过「名字 + 泛型参数」;没有合法名字时返回 null(`{ type: 1 }` 因此落空)。 */
|
|
696
|
+
skipNameAndParams(from) {
|
|
697
|
+
let j = from;
|
|
698
|
+
while (j < this.src.length && /\s/.test(this.src[j])) j++;
|
|
699
|
+
const nameStart = j;
|
|
700
|
+
while (j < this.src.length && IDENT_PART.test(this.src[j])) j++;
|
|
701
|
+
if (j === nameStart) return null;
|
|
702
|
+
const generic = this.scanTypeParams(j);
|
|
703
|
+
return generic === null ? j : generic;
|
|
704
|
+
}
|
|
705
|
+
/** `<T, U extends X>`:返回 `>` 之后的下标;跨到 `)` `]` `;` 换行前配不平则 null。 */
|
|
706
|
+
scanTypeParams(from) {
|
|
707
|
+
if (this.src[from] !== "<") return null;
|
|
708
|
+
let depth = 0;
|
|
709
|
+
let j = from;
|
|
710
|
+
for (; j < this.src.length; j++) {
|
|
711
|
+
const c = this.src[j];
|
|
712
|
+
if (c === "<") depth++;
|
|
713
|
+
else if (c === ">") {
|
|
714
|
+
depth--;
|
|
715
|
+
if (depth === 0) return j + 1;
|
|
716
|
+
} else if (c === ")" || c === "]" || c === ";" || c === "\n") return null;
|
|
717
|
+
else if (c === "'" || c === '"') j = this.scanQuoted(j) - 1;
|
|
718
|
+
else if (c === "`") j = this.scanTemplate(j) - 1;
|
|
719
|
+
}
|
|
720
|
+
return null;
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* 消费「刚读出的标识符之后」的泛型实参 / 类型参数(`function f<T>(`、方法 `m<T>(`、`f<T>(x)`)。
|
|
724
|
+
* 只有 `>` 之后紧跟 `(` / `{` / `implements` 才算——`if (a < b) {` 里 `>` 之后是 `)`,故安全。
|
|
725
|
+
*/
|
|
726
|
+
consumeGenericsAfterName() {
|
|
727
|
+
if (this.ch() !== "<") return;
|
|
728
|
+
const end = this.scanTypeParams(this.i);
|
|
729
|
+
if (end === null) return;
|
|
730
|
+
let j = end;
|
|
731
|
+
while (j < this.src.length && (this.src[j] === " " || this.src[j] === " ")) j++;
|
|
732
|
+
const after = this.src.slice(j);
|
|
733
|
+
if (!/^[({]/.test(after) && !/^implements\b/.test(after)) return;
|
|
734
|
+
this.erase(this.i, end);
|
|
735
|
+
this.i = end;
|
|
736
|
+
}
|
|
737
|
+
/** 括号配平:返回闭括号之后的下标(未配平则到文件尾)。 */
|
|
738
|
+
scanBalanced(from, open, close) {
|
|
739
|
+
let depth = 0;
|
|
740
|
+
let j = from;
|
|
741
|
+
while (j < this.src.length) {
|
|
742
|
+
const c = this.src[j];
|
|
743
|
+
if (c === "'" || c === '"') j = this.scanQuoted(j);
|
|
744
|
+
else if (c === "`") j = this.scanTemplate(j);
|
|
745
|
+
else if (c === "/" && (this.src[j + 1] === "/" || this.src[j + 1] === "*"))
|
|
746
|
+
j = this.skipComment(j);
|
|
747
|
+
else if (c === open) {
|
|
748
|
+
depth++;
|
|
749
|
+
j++;
|
|
750
|
+
} else if (c === close) {
|
|
751
|
+
depth--;
|
|
752
|
+
j++;
|
|
753
|
+
if (depth === 0) return j;
|
|
754
|
+
} else j++;
|
|
755
|
+
}
|
|
756
|
+
return this.src.length;
|
|
757
|
+
}
|
|
758
|
+
skipComment(from) {
|
|
759
|
+
if (this.src[from + 1] === "/") {
|
|
760
|
+
const end2 = this.src.indexOf("\n", from);
|
|
761
|
+
return end2 === -1 ? this.src.length : end2;
|
|
762
|
+
}
|
|
763
|
+
const end = this.src.indexOf("*/", from + 2);
|
|
764
|
+
return end === -1 ? this.src.length : end + 2;
|
|
765
|
+
}
|
|
766
|
+
/** 语句结束(`;` 或括号已配平时的行尾),用于 `import type …`。 */
|
|
767
|
+
scanStatementEnd(from) {
|
|
768
|
+
let depth = 0;
|
|
769
|
+
let j = from;
|
|
770
|
+
while (j < this.src.length) {
|
|
771
|
+
const c = this.src[j];
|
|
772
|
+
if (c === "'" || c === '"') j = this.scanQuoted(j);
|
|
773
|
+
else if (c === "`") j = this.scanTemplate(j);
|
|
774
|
+
else if (c === "{" || c === "(" || c === "[") {
|
|
775
|
+
depth++;
|
|
776
|
+
j++;
|
|
777
|
+
} else if (c === "}" || c === ")" || c === "]") {
|
|
778
|
+
depth--;
|
|
779
|
+
j++;
|
|
780
|
+
} else if (c === ";" || c === "\n" && depth <= 0) break;
|
|
781
|
+
else j++;
|
|
782
|
+
}
|
|
783
|
+
return j;
|
|
784
|
+
}
|
|
785
|
+
/**
|
|
786
|
+
* 类型表达式结束下标:消费标识符 / 字符串字面量类型 / 泛型实参 / 元组 / 对象类型 /
|
|
787
|
+
* 函数类型(含 `=>` 返回值)/ 联合与交叉;在深度 0 的 `= , ; ) ]` 与「已结束的行」处停下。
|
|
788
|
+
*
|
|
789
|
+
* 两处歧义的处理:
|
|
790
|
+
* - `function f(): { a: number } { … }`——还没消费过类型原子时遇到的 `{` 属于类型,
|
|
791
|
+
* 之后再遇 `{` 才是函数体;`type X = A` 的初始值花括号整体算类型(`leadingBraceIsType`)。
|
|
792
|
+
* - `const x: Foo = { … }`——在 `=` 处停,不会把右侧对象字面量吞掉。
|
|
793
|
+
*/
|
|
794
|
+
scanTypeExpression(from, leadingBraceIsType, topLevelArrowEnds = false) {
|
|
795
|
+
let depth = 0;
|
|
796
|
+
let j = from;
|
|
797
|
+
let sawAtom = false;
|
|
798
|
+
while (j < this.src.length) {
|
|
799
|
+
const c = this.src[j];
|
|
800
|
+
if (c === "'" || c === '"') {
|
|
801
|
+
j = this.scanQuoted(j);
|
|
802
|
+
sawAtom = true;
|
|
803
|
+
} else if (c === "`") {
|
|
804
|
+
j = this.scanTemplate(j);
|
|
805
|
+
sawAtom = true;
|
|
806
|
+
} else if (c === "/" && (this.src[j + 1] === "/" || this.src[j + 1] === "*")) {
|
|
807
|
+
j = this.skipComment(j);
|
|
808
|
+
} else if (c === "(" || c === "[") {
|
|
809
|
+
depth++;
|
|
810
|
+
sawAtom = true;
|
|
811
|
+
j++;
|
|
812
|
+
} else if (c === ")" || c === "]") {
|
|
813
|
+
if (depth === 0) break;
|
|
814
|
+
depth--;
|
|
815
|
+
j++;
|
|
816
|
+
} else if (c === "{") {
|
|
817
|
+
if (depth === 0 && sawAtom && !leadingBraceIsType) break;
|
|
818
|
+
j = this.scanBalanced(j, "{", "}");
|
|
819
|
+
sawAtom = true;
|
|
820
|
+
} else if (c === "}") {
|
|
821
|
+
if (depth === 0) break;
|
|
822
|
+
depth--;
|
|
823
|
+
j++;
|
|
824
|
+
} else if (depth === 0 && (c === "," || c === ";")) break;
|
|
825
|
+
else if (depth === 0 && c === "=") {
|
|
826
|
+
if (this.src[j + 1] === ">" && !topLevelArrowEnds) {
|
|
827
|
+
j += 2;
|
|
828
|
+
continue;
|
|
829
|
+
}
|
|
830
|
+
break;
|
|
831
|
+
} else if (depth === 0 && c === "\n") {
|
|
832
|
+
if (!this.typeContinuesAt(j)) break;
|
|
833
|
+
j++;
|
|
834
|
+
} else if (c === "<") {
|
|
835
|
+
const generic = this.scanTypeParams(j);
|
|
836
|
+
if (generic === null) break;
|
|
837
|
+
j = generic;
|
|
838
|
+
sawAtom = true;
|
|
839
|
+
} else {
|
|
840
|
+
if (IDENT_PART.test(c)) sawAtom = true;
|
|
841
|
+
j++;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
return j;
|
|
845
|
+
}
|
|
846
|
+
/**
|
|
847
|
+
* 行尾处类型是否仍在继续:`type Id = string\n | number` 要接得上,
|
|
848
|
+
* 而 `const x: Foo\nconst y = 1` 必须停。判据是两侧的非空邻字——
|
|
849
|
+
* 前一个是连接符(`| & < > = , . ? : (`)或后一个是连接符 / 闭合符(`| & , . ? : ) ] } >`)。
|
|
850
|
+
*/
|
|
851
|
+
typeContinuesAt(index) {
|
|
852
|
+
for (let j = index - 1; j >= 0; j--) {
|
|
853
|
+
const c = this.src[j];
|
|
854
|
+
if (/\s/.test(c)) continue;
|
|
855
|
+
if ("|&<>=,.?:(".includes(c)) return true;
|
|
856
|
+
break;
|
|
857
|
+
}
|
|
858
|
+
for (let j = index + 1; j < this.src.length; j++) {
|
|
859
|
+
const c = this.src[j];
|
|
860
|
+
if (/\s/.test(c)) continue;
|
|
861
|
+
return "|&,.?:)]}>".includes(c);
|
|
862
|
+
}
|
|
863
|
+
return false;
|
|
864
|
+
}
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
});
|
|
868
|
+
|
|
869
|
+
// src/preview-server.ts
|
|
870
|
+
var preview_server_exports = {};
|
|
871
|
+
__export(preview_server_exports, {
|
|
85
872
|
buildPreviewDocument: () => buildPreviewDocument,
|
|
86
873
|
createPreviewServer: () => createPreviewServer,
|
|
87
874
|
inlineAssets: () => inlineAssets
|
|
@@ -177,15 +964,658 @@ function createPreviewServer(vfs, options = {}) {
|
|
|
177
964
|
};
|
|
178
965
|
}
|
|
179
966
|
var EXTERNAL_SRC;
|
|
180
|
-
var
|
|
181
|
-
"
|
|
967
|
+
var init_preview_server = __esm({
|
|
968
|
+
"src/preview-server.ts"() {
|
|
182
969
|
"use strict";
|
|
183
970
|
init_path();
|
|
184
971
|
EXTERNAL_SRC = /(?:^https?:)?\/\//i;
|
|
185
972
|
}
|
|
186
973
|
});
|
|
187
974
|
|
|
188
|
-
//
|
|
975
|
+
// src/function-fetch-proxy.ts
|
|
976
|
+
function parseBodyByContentType(rawBody, headers) {
|
|
977
|
+
if (rawBody === null) return void 0;
|
|
978
|
+
const contentType = headers["content-type"] ?? headers["Content-Type"] ?? headers["CONTENT-TYPE"] ?? "";
|
|
979
|
+
if (!/json/i.test(contentType)) return rawBody;
|
|
980
|
+
try {
|
|
981
|
+
return JSON.parse(rawBody);
|
|
982
|
+
} catch {
|
|
983
|
+
return rawBody;
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
function createFunctionFetchHandler(options) {
|
|
987
|
+
return async (event) => {
|
|
988
|
+
const data = event.data;
|
|
989
|
+
if (!data || data.__adepFnRequest !== true) return;
|
|
990
|
+
const source = event.source;
|
|
991
|
+
const post = source?.postMessage;
|
|
992
|
+
if (typeof post !== "function") return;
|
|
993
|
+
const reply = (payload) => {
|
|
994
|
+
try {
|
|
995
|
+
post.call(source, { __adepFnResponse: true, ...payload }, "*");
|
|
996
|
+
} catch {
|
|
997
|
+
}
|
|
998
|
+
};
|
|
999
|
+
try {
|
|
1000
|
+
const id = data.id;
|
|
1001
|
+
const method = String(data.method ?? "GET").toUpperCase();
|
|
1002
|
+
const url = String(data.url ?? "");
|
|
1003
|
+
const headers = typeof data.headers === "object" && data.headers !== null ? data.headers : {};
|
|
1004
|
+
const rawBody = typeof data.body === "string" ? data.body : data.body === null || data.body === void 0 ? null : String(data.body);
|
|
1005
|
+
const qIndex = url.indexOf("?");
|
|
1006
|
+
const pathPart = qIndex === -1 ? url : url.slice(0, qIndex);
|
|
1007
|
+
const queryPart = qIndex === -1 ? "" : url.slice(qIndex + 1);
|
|
1008
|
+
const match = pathPart.match(/^\/api\/([^/]+)(\/.*)?$/);
|
|
1009
|
+
if (match === null) {
|
|
1010
|
+
reply({
|
|
1011
|
+
id,
|
|
1012
|
+
status: 404,
|
|
1013
|
+
headers: { "content-type": "application/json" },
|
|
1014
|
+
body: JSON.stringify({
|
|
1015
|
+
error: { code: "FN_NOT_FOUND", message: `\u65E0\u6CD5\u89E3\u6790\u7684\u4E91\u51FD\u6570\u8DEF\u5F84: ${url}` }
|
|
1016
|
+
})
|
|
1017
|
+
});
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
const fnName = decodeURIComponent(match[1] ?? "");
|
|
1021
|
+
const subPath = match[2] ?? "/";
|
|
1022
|
+
const query = {};
|
|
1023
|
+
if (queryPart !== "") {
|
|
1024
|
+
try {
|
|
1025
|
+
new URLSearchParams(queryPart).forEach((value, key) => {
|
|
1026
|
+
query[key] = value;
|
|
1027
|
+
});
|
|
1028
|
+
} catch {
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
const files = await options.getFunctionFiles(fnName);
|
|
1032
|
+
if (files === null || Object.keys(files).length === 0) {
|
|
1033
|
+
reply({
|
|
1034
|
+
id,
|
|
1035
|
+
status: 404,
|
|
1036
|
+
headers: { "content-type": "application/json" },
|
|
1037
|
+
body: JSON.stringify({
|
|
1038
|
+
error: {
|
|
1039
|
+
code: "FN_NOT_FOUND",
|
|
1040
|
+
message: `\u4E91\u51FD\u6570 "${fnName}" \u4E0D\u5B58\u5728\u6216\u5C1A\u672A\u52A0\u8F7D\u8349\u7A3F`
|
|
1041
|
+
}
|
|
1042
|
+
})
|
|
1043
|
+
});
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
const entry = files["index.ts"] !== void 0 ? "index.ts" : Object.keys(files)[0] ?? "";
|
|
1047
|
+
if (entry === "") {
|
|
1048
|
+
reply({
|
|
1049
|
+
id,
|
|
1050
|
+
status: 500,
|
|
1051
|
+
headers: { "content-type": "application/json" },
|
|
1052
|
+
body: JSON.stringify({
|
|
1053
|
+
error: { code: "FN_NO_ENTRY", message: `\u4E91\u51FD\u6570 "${fnName}" \u6CA1\u6709\u53EF\u6267\u884C\u6587\u4EF6` }
|
|
1054
|
+
})
|
|
1055
|
+
});
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1058
|
+
const body = parseBodyByContentType(rawBody, headers);
|
|
1059
|
+
const result = await options.runner.run({
|
|
1060
|
+
files,
|
|
1061
|
+
entry,
|
|
1062
|
+
request: {
|
|
1063
|
+
method,
|
|
1064
|
+
path: subPath,
|
|
1065
|
+
query,
|
|
1066
|
+
headers,
|
|
1067
|
+
...body === void 0 ? {} : { body }
|
|
1068
|
+
},
|
|
1069
|
+
bundle: options.bundle
|
|
1070
|
+
});
|
|
1071
|
+
let status = result.status;
|
|
1072
|
+
let outBody;
|
|
1073
|
+
let outContentType = "application/json";
|
|
1074
|
+
if (result.error !== void 0) {
|
|
1075
|
+
if (status === 200) status = 500;
|
|
1076
|
+
outBody = JSON.stringify({ error: result.error });
|
|
1077
|
+
} else if (typeof result.body === "string") {
|
|
1078
|
+
outBody = result.body;
|
|
1079
|
+
outContentType = "text/plain; charset=utf-8";
|
|
1080
|
+
} else {
|
|
1081
|
+
outBody = JSON.stringify(result.body ?? null);
|
|
1082
|
+
}
|
|
1083
|
+
reply({ id, status, headers: { "content-type": outContentType }, body: outBody });
|
|
1084
|
+
} catch (error) {
|
|
1085
|
+
reply({
|
|
1086
|
+
id: data?.id,
|
|
1087
|
+
status: 500,
|
|
1088
|
+
headers: { "content-type": "application/json" },
|
|
1089
|
+
body: JSON.stringify({
|
|
1090
|
+
error: {
|
|
1091
|
+
code: "FN_PROXY_ERROR",
|
|
1092
|
+
message: error instanceof Error ? error.message : String(error)
|
|
1093
|
+
}
|
|
1094
|
+
})
|
|
1095
|
+
});
|
|
1096
|
+
}
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
1099
|
+
var FN_FETCH_INTERCEPTOR_SCRIPT;
|
|
1100
|
+
var init_function_fetch_proxy = __esm({
|
|
1101
|
+
"src/function-fetch-proxy.ts"() {
|
|
1102
|
+
"use strict";
|
|
1103
|
+
FN_FETCH_INTERCEPTOR_SCRIPT = (function() {
|
|
1104
|
+
var src = [
|
|
1105
|
+
"(function () {",
|
|
1106
|
+
" if (window.__adepFnProxyInstalled) return;",
|
|
1107
|
+
" window.__adepFnProxyInstalled = true;",
|
|
1108
|
+
" var ORIGINAL_FETCH = window.fetch.bind(window);",
|
|
1109
|
+
" var pending = {};",
|
|
1110
|
+
" var nextId = 1;",
|
|
1111
|
+
" var TIMEOUT_MS = 15000;",
|
|
1112
|
+
"",
|
|
1113
|
+
' window.addEventListener("message", function (event) {',
|
|
1114
|
+
" var data = event.data;",
|
|
1115
|
+
" if (!data || data.__adepFnResponse !== true) return;",
|
|
1116
|
+
" var entry = pending[data.id];",
|
|
1117
|
+
" if (!entry) return;",
|
|
1118
|
+
" delete pending[data.id];",
|
|
1119
|
+
" clearTimeout(entry.timer);",
|
|
1120
|
+
" var headers;",
|
|
1121
|
+
" try { headers = new Headers(data.headers || {}); }",
|
|
1122
|
+
' catch (e) { headers = new Headers({ "content-type": "text/plain" }); }',
|
|
1123
|
+
" var body = data.body == null ? null : data.body;",
|
|
1124
|
+
" try {",
|
|
1125
|
+
' entry.resolve(new Response(body, { status: data.status || 200, statusText: "", headers: headers }));',
|
|
1126
|
+
" } catch (e2) {",
|
|
1127
|
+
' entry.resolve(new Response(String(body), { status: data.status || 200, headers: { "content-type": "text/plain" } }));',
|
|
1128
|
+
" }",
|
|
1129
|
+
" });",
|
|
1130
|
+
"",
|
|
1131
|
+
" function readBody(input, init) {",
|
|
1132
|
+
' if (typeof Request !== "undefined" && input instanceof Request) {',
|
|
1133
|
+
" return input.text().catch(function () { return null; });",
|
|
1134
|
+
" }",
|
|
1135
|
+
" var body = init && init.body !== undefined ? init.body : input;",
|
|
1136
|
+
" if (body == null) return Promise.resolve(null);",
|
|
1137
|
+
' if (typeof body === "string") return Promise.resolve(body);',
|
|
1138
|
+
' if (typeof ArrayBuffer !== "undefined" && body instanceof ArrayBuffer) {',
|
|
1139
|
+
" return Promise.resolve(new TextDecoder().decode(body));",
|
|
1140
|
+
" }",
|
|
1141
|
+
' if (typeof Blob !== "undefined" && body instanceof Blob) return body.text();',
|
|
1142
|
+
' if (typeof URLSearchParams !== "undefined" && body instanceof URLSearchParams) {',
|
|
1143
|
+
" return Promise.resolve(body.toString());",
|
|
1144
|
+
" }",
|
|
1145
|
+
" try { return Promise.resolve(String(body)); } catch (e) { return Promise.resolve(null); }",
|
|
1146
|
+
" }",
|
|
1147
|
+
"",
|
|
1148
|
+
" function collectHeaders(input, init) {",
|
|
1149
|
+
" var out = {};",
|
|
1150
|
+
" try { input.headers.forEach(function (v, k) { out[k] = v; }); } catch (e) {}",
|
|
1151
|
+
" if (init && init.headers) {",
|
|
1152
|
+
" try { new Headers(init.headers).forEach(function (v, k) { out[k] = v; }); } catch (e2) {}",
|
|
1153
|
+
" }",
|
|
1154
|
+
" return out;",
|
|
1155
|
+
" }",
|
|
1156
|
+
"",
|
|
1157
|
+
" window.fetch = function (input, init) {",
|
|
1158
|
+
" var urlStr;",
|
|
1159
|
+
" var method;",
|
|
1160
|
+
" var headers = {};",
|
|
1161
|
+
' var isRequest = typeof Request !== "undefined" && input instanceof Request;',
|
|
1162
|
+
" if (isRequest) {",
|
|
1163
|
+
" urlStr = String(input.url);",
|
|
1164
|
+
' method = (init && init.method) || input.method || "GET";',
|
|
1165
|
+
" headers = collectHeaders(input, init);",
|
|
1166
|
+
' } else if (typeof input === "string" || (typeof URL !== "undefined" && input instanceof URL)) {',
|
|
1167
|
+
" urlStr = String(input);",
|
|
1168
|
+
' method = (init && init.method) || "GET";',
|
|
1169
|
+
" try { new Headers(init && init.headers || {}).forEach(function (v, k) { headers[k] = v; }); } catch (e) {}",
|
|
1170
|
+
" } else {",
|
|
1171
|
+
" return ORIGINAL_FETCH(input, init);",
|
|
1172
|
+
" }",
|
|
1173
|
+
" var u;",
|
|
1174
|
+
" try { u = new URL(urlStr, document.baseURI); }",
|
|
1175
|
+
" catch (e) { return ORIGINAL_FETCH(input, init); }",
|
|
1176
|
+
' if (u.pathname.indexOf("/api/") !== 0 && u.pathname !== "/api") {',
|
|
1177
|
+
" return ORIGINAL_FETCH(input, init);",
|
|
1178
|
+
" }",
|
|
1179
|
+
" if (window.parent === window) return ORIGINAL_FETCH(input, init);",
|
|
1180
|
+
' method = (method || "GET").toUpperCase();',
|
|
1181
|
+
" var target = u.pathname + u.search;",
|
|
1182
|
+
" return readBody(input, init).then(function (textBody) {",
|
|
1183
|
+
" return new Promise(function (resolve) {",
|
|
1184
|
+
" var id = nextId++;",
|
|
1185
|
+
" var timer = setTimeout(function () {",
|
|
1186
|
+
" delete pending[id];",
|
|
1187
|
+
` resolve(new Response('{"error":{"code":"FN_PROXY_TIMEOUT","message":"\\u4e91\\u51fd\\u6570\\u6267\\u884c\\u8d85\\u65f6"}}', { status: 504, headers: { "content-type": "application/json" } }));`,
|
|
1188
|
+
" }, TIMEOUT_MS);",
|
|
1189
|
+
" pending[id] = { resolve: resolve, timer: timer };",
|
|
1190
|
+
' window.parent.postMessage({ __adepFnRequest: true, id: id, method: method, url: target, headers: headers, body: textBody }, "*");',
|
|
1191
|
+
" });",
|
|
1192
|
+
" });",
|
|
1193
|
+
" };",
|
|
1194
|
+
"})();"
|
|
1195
|
+
].join("\n");
|
|
1196
|
+
return src;
|
|
1197
|
+
})();
|
|
1198
|
+
}
|
|
1199
|
+
});
|
|
1200
|
+
|
|
1201
|
+
// src/vite-dev.ts
|
|
1202
|
+
var vite_dev_exports = {};
|
|
1203
|
+
__export(vite_dev_exports, {
|
|
1204
|
+
ViteDevError: () => ViteDevError,
|
|
1205
|
+
createViteServer: () => createViteServer,
|
|
1206
|
+
scanEsmSpecifiers: () => scanEsmSpecifiers
|
|
1207
|
+
});
|
|
1208
|
+
function tokenize2(source) {
|
|
1209
|
+
const tokens = [];
|
|
1210
|
+
let i = 0;
|
|
1211
|
+
const n = source.length;
|
|
1212
|
+
while (i < n) {
|
|
1213
|
+
const ch = source[i];
|
|
1214
|
+
if (ch === " " || ch === " " || ch === "\n" || ch === "\r") {
|
|
1215
|
+
i++;
|
|
1216
|
+
continue;
|
|
1217
|
+
}
|
|
1218
|
+
if (ch === "/" && source[i + 1] === "/") {
|
|
1219
|
+
i = source.indexOf("\n", i);
|
|
1220
|
+
if (i === -1) break;
|
|
1221
|
+
continue;
|
|
1222
|
+
}
|
|
1223
|
+
if (ch === "/" && source[i + 1] === "*") {
|
|
1224
|
+
const close = source.indexOf("*/", i + 2);
|
|
1225
|
+
if (close === -1) break;
|
|
1226
|
+
i = close + 2;
|
|
1227
|
+
continue;
|
|
1228
|
+
}
|
|
1229
|
+
if (ch === "'" || ch === '"') {
|
|
1230
|
+
let j = i + 1;
|
|
1231
|
+
while (j < n && source[j] !== ch) {
|
|
1232
|
+
if (source[j] === "\\") j++;
|
|
1233
|
+
j++;
|
|
1234
|
+
}
|
|
1235
|
+
tokens.push({ kind: "string", text: source.slice(i, j + 1), start: i, end: j + 1 });
|
|
1236
|
+
i = j + 1;
|
|
1237
|
+
continue;
|
|
1238
|
+
}
|
|
1239
|
+
if (ch === "`") {
|
|
1240
|
+
let j = i + 1;
|
|
1241
|
+
while (j < n && source[j] !== "`") {
|
|
1242
|
+
if (source[j] === "\\") j++;
|
|
1243
|
+
j++;
|
|
1244
|
+
}
|
|
1245
|
+
tokens.push({ kind: "template", text: source.slice(i, j + 1), start: i, end: j + 1 });
|
|
1246
|
+
i = j + 1;
|
|
1247
|
+
continue;
|
|
1248
|
+
}
|
|
1249
|
+
if (ch === "/") {
|
|
1250
|
+
const prev = tokens[tokens.length - 1];
|
|
1251
|
+
const isRegex = prev === void 0 || prev.kind === "punct" && REGEX_PRECEDING_CHARS.has(prev.text) || prev.kind === "ident" && REGEX_PRECEDING_KEYWORDS.has(prev.text);
|
|
1252
|
+
if (isRegex) {
|
|
1253
|
+
let j = i + 1;
|
|
1254
|
+
let inClass = false;
|
|
1255
|
+
while (j < n) {
|
|
1256
|
+
const c = source[j];
|
|
1257
|
+
if (c === "\\") j++;
|
|
1258
|
+
else if (c === "[") inClass = true;
|
|
1259
|
+
else if (c === "]") inClass = false;
|
|
1260
|
+
else if (c === "/" && !inClass) break;
|
|
1261
|
+
else if (c === "\n") break;
|
|
1262
|
+
j++;
|
|
1263
|
+
}
|
|
1264
|
+
tokens.push({ kind: "punct", text: "/", start: i, end: i + 1 });
|
|
1265
|
+
i = j + 1;
|
|
1266
|
+
continue;
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
if (isIdentStart(ch)) {
|
|
1270
|
+
let j = i + 1;
|
|
1271
|
+
while (j < n && isIdentPart(source[j])) j++;
|
|
1272
|
+
tokens.push({ kind: "ident", text: source.slice(i, j), start: i, end: j });
|
|
1273
|
+
i = j;
|
|
1274
|
+
continue;
|
|
1275
|
+
}
|
|
1276
|
+
tokens.push({ kind: "punct", text: ch, start: i, end: i + 1 });
|
|
1277
|
+
i++;
|
|
1278
|
+
}
|
|
1279
|
+
return tokens;
|
|
1280
|
+
}
|
|
1281
|
+
function scanEsmSpecifiers(source) {
|
|
1282
|
+
const tokens = tokenize2(source);
|
|
1283
|
+
const hits = [];
|
|
1284
|
+
for (let i = 1; i < tokens.length; i++) {
|
|
1285
|
+
const tok = tokens[i];
|
|
1286
|
+
if (tok.kind !== "string") continue;
|
|
1287
|
+
const prev = tokens[i - 1];
|
|
1288
|
+
const prev2 = tokens[i - 2];
|
|
1289
|
+
if (prev.kind !== "ident" && prev.kind !== "punct") continue;
|
|
1290
|
+
if (prev.kind === "ident" && prev.text === "import") {
|
|
1291
|
+
hits.push({ specifier: tok.text.slice(1, -1), start: tok.start, end: tok.end });
|
|
1292
|
+
continue;
|
|
1293
|
+
}
|
|
1294
|
+
if (prev.kind === "ident" && prev.text === "from" && !(prev2?.kind === "punct" && ["=", ":", ",", "(", "["].includes(prev2.text))) {
|
|
1295
|
+
hits.push({ specifier: tok.text.slice(1, -1), start: tok.start, end: tok.end });
|
|
1296
|
+
continue;
|
|
1297
|
+
}
|
|
1298
|
+
if (prev.kind === "punct" && prev.text === "(" && prev2?.kind === "ident" && prev2.text === "import") {
|
|
1299
|
+
hits.push({ specifier: tok.text.slice(1, -1), start: tok.start, end: tok.end });
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
return hits;
|
|
1303
|
+
}
|
|
1304
|
+
function cssModule(css) {
|
|
1305
|
+
return [
|
|
1306
|
+
`const css = ${JSON.stringify(css)};`,
|
|
1307
|
+
'const el = document.createElement("style");',
|
|
1308
|
+
'el.setAttribute("data-adep-vite", "");',
|
|
1309
|
+
"el.textContent = css;",
|
|
1310
|
+
"document.head.appendChild(el);",
|
|
1311
|
+
"export default css;"
|
|
1312
|
+
].join("\n");
|
|
1313
|
+
}
|
|
1314
|
+
function jsonModule(source, abs) {
|
|
1315
|
+
try {
|
|
1316
|
+
JSON.parse(source);
|
|
1317
|
+
} catch {
|
|
1318
|
+
throw new ViteDevError("JSON_PARSE", `${abs} \u4E0D\u662F\u5408\u6CD5 JSON`);
|
|
1319
|
+
}
|
|
1320
|
+
return `export default ${source.trim()};`;
|
|
1321
|
+
}
|
|
1322
|
+
function resolveWithSuffixes(vfs, base) {
|
|
1323
|
+
if (vfs.exists(base) && !vfs.isDirectory(base)) return base;
|
|
1324
|
+
const asFile2 = VITE_RESOLVE_SUFFIXES.map((suffix) => `${base}${suffix}`);
|
|
1325
|
+
const asIndex = VITE_RESOLVE_SUFFIXES.map((suffix) => join(base, `index${suffix}`));
|
|
1326
|
+
return asFile2.find((p) => vfs.exists(p) && !vfs.isDirectory(p)) ?? asIndex.find((p) => vfs.exists(p) && !vfs.isDirectory(p)) ?? base;
|
|
1327
|
+
}
|
|
1328
|
+
function hmrScript(name) {
|
|
1329
|
+
return `<script>(function(){if(typeof BroadcastChannel==='undefined')return;var bc=new BroadcastChannel(${JSON.stringify(
|
|
1330
|
+
name
|
|
1331
|
+
)});bc.onmessage=function(e){var d=e.data;if(d&&d.type==='update'&&d.url&&d.url!==location.href){location.replace(d.url);}};})();</script>`;
|
|
1332
|
+
}
|
|
1333
|
+
function createViteServer(vfs, options = {}) {
|
|
1334
|
+
const root = normalize(options.root ?? "/");
|
|
1335
|
+
const entry = options.entry ?? "index.html";
|
|
1336
|
+
const sfcCompiler = options.sfcCompiler;
|
|
1337
|
+
const createObjectURL = options.createObjectURL ?? ((blob) => URL.createObjectURL(blob));
|
|
1338
|
+
const revokeObjectURL = options.revokeObjectURL ?? ((url) => URL.revokeObjectURL(url));
|
|
1339
|
+
const channelName = `adep:vite:${Math.random().toString(36).slice(2)}`;
|
|
1340
|
+
const port = 5173;
|
|
1341
|
+
let moduleUrls = /* @__PURE__ */ new Map();
|
|
1342
|
+
const inFlight = /* @__PURE__ */ new Set();
|
|
1343
|
+
const stableUrls = /* @__PURE__ */ new Map();
|
|
1344
|
+
let inlineSources = /* @__PURE__ */ new Map();
|
|
1345
|
+
let currentUrl = "";
|
|
1346
|
+
let lastDoc = null;
|
|
1347
|
+
let bc = null;
|
|
1348
|
+
let touchTimer = null;
|
|
1349
|
+
let inlineSeq = 0;
|
|
1350
|
+
const scheduleRebuild = () => {
|
|
1351
|
+
if (touchTimer !== null) clearTimeout(touchTimer);
|
|
1352
|
+
touchTimer = setTimeout(() => {
|
|
1353
|
+
touchTimer = null;
|
|
1354
|
+
void rebuild();
|
|
1355
|
+
}, 100);
|
|
1356
|
+
};
|
|
1357
|
+
const broadcast = (url) => {
|
|
1358
|
+
try {
|
|
1359
|
+
bc ??= new BroadcastChannel(channelName);
|
|
1360
|
+
bc.postMessage({ type: "update", url });
|
|
1361
|
+
} catch {
|
|
1362
|
+
}
|
|
1363
|
+
};
|
|
1364
|
+
function transformModule2(abs, source) {
|
|
1365
|
+
if (abs.endsWith(".css")) return cssModule(source);
|
|
1366
|
+
if (abs.endsWith(".json")) return jsonModule(source, abs);
|
|
1367
|
+
if (abs.endsWith(".vue")) {
|
|
1368
|
+
if (sfcCompiler === void 0)
|
|
1369
|
+
throw new ViteDevError(
|
|
1370
|
+
"SFC_NO_COMPILER",
|
|
1371
|
+
`${abs} \u662F Vue SFC\uFF0C\u4F46\u5F53\u524D\u73AF\u5883\u672A\u88C5\u914D SFC \u7F16\u8BD1\u5668\uFF08IDE \u4FA7\u9700\u6CE8\u5165 vue/compiler-sfc\uFF09`
|
|
1372
|
+
);
|
|
1373
|
+
const compiled = sfcCompiler(source, abs);
|
|
1374
|
+
return [...compiled.styles.map((css) => cssModule(css)), compiled.script].join("\n");
|
|
1375
|
+
}
|
|
1376
|
+
if (abs.endsWith(".tsx") || abs.endsWith(".jsx"))
|
|
1377
|
+
throw new ViteDevError(
|
|
1378
|
+
"JSX_UNSUPPORTED",
|
|
1379
|
+
`${abs} \u4F7F\u7528 JSX\uFF1A\u6D4F\u89C8\u5668\u5185 vite \u53EA\u64E6\u9664 TS \u7C7B\u578B\u6807\u6CE8\uFF0C\u4E0D\u8F6C\u8BD1 JSX\uFF08\u8BF7\u7528 h() \u6E32\u67D3\u51FD\u6570\u6216 .vue SFC\uFF09`
|
|
1380
|
+
);
|
|
1381
|
+
if (abs.endsWith(".ts") || abs.endsWith(".mts")) return stripTypes(source);
|
|
1382
|
+
return source;
|
|
1383
|
+
}
|
|
1384
|
+
async function resolveSpecifier(spec, importerAbs) {
|
|
1385
|
+
if (EXTERNAL_SPECIFIER.test(spec) || DATA_OR_BLOB.test(spec)) return spec;
|
|
1386
|
+
let target;
|
|
1387
|
+
if (spec.startsWith("./") || spec.startsWith("../") || spec.startsWith("/")) {
|
|
1388
|
+
const base = spec.startsWith("/") ? spec : join(dirname(importerAbs), spec);
|
|
1389
|
+
target = resolveWithSuffixes(vfs, base);
|
|
1390
|
+
if (!vfs.exists(target) || vfs.isDirectory(target))
|
|
1391
|
+
throw new ViteDevError(
|
|
1392
|
+
"RESOLVE",
|
|
1393
|
+
`\u627E\u4E0D\u5230\u6A21\u5757 "${spec}"\uFF08\u81EA ${importerAbs}\uFF1B\u8BD5\u8FC7\u6269\u5C55\u540D ${VITE_RESOLVE_SUFFIXES.join("/")} \u4E0E\u76EE\u5F55 index\uFF09`
|
|
1394
|
+
);
|
|
1395
|
+
} else {
|
|
1396
|
+
try {
|
|
1397
|
+
target = resolveImport(vfs, spec, importerAbs, "import");
|
|
1398
|
+
} catch (error) {
|
|
1399
|
+
if (error instanceof ResolveError)
|
|
1400
|
+
throw new ViteDevError(
|
|
1401
|
+
"RESOLVE",
|
|
1402
|
+
`${error.message}
|
|
1403
|
+
\uFF08\u5728 ${importerAbs} \u4E2D import "${spec}"\uFF09`
|
|
1404
|
+
);
|
|
1405
|
+
throw error;
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
return loadModuleUrl(target);
|
|
1409
|
+
}
|
|
1410
|
+
async function loadModuleUrl(abs) {
|
|
1411
|
+
const cached = moduleUrls.get(abs);
|
|
1412
|
+
if (cached !== void 0) return cached;
|
|
1413
|
+
if (inFlight.has(abs))
|
|
1414
|
+
throw new ViteDevError(
|
|
1415
|
+
"CYCLE",
|
|
1416
|
+
`\u5FAA\u73AF\u4F9D\u8D56\uFF1A${[...inFlight, abs].join(" \u2192 ")}\uFF08blob \u6A21\u5757\u94FE\u7684\u6539\u5199\u53D1\u751F\u5728\u52A0\u8F7D\u671F\uFF0C\u73AF\u65E0\u6CD5\u6210\u94FE\uFF1B\u8BF7\u62C6\u6389\u73AF\u6216\u6539\u7528\u52A8\u6001 import()\uFF09`
|
|
1417
|
+
);
|
|
1418
|
+
inFlight.add(abs);
|
|
1419
|
+
const pending = (async () => {
|
|
1420
|
+
const source = inlineSources.get(abs) ?? vfs.readFile(abs);
|
|
1421
|
+
const code = transformModule2(abs, source);
|
|
1422
|
+
const hits = scanEsmSpecifiers(code);
|
|
1423
|
+
const targets = await Promise.all(hits.map((hit) => resolveSpecifier(hit.specifier, abs)));
|
|
1424
|
+
let out = code;
|
|
1425
|
+
for (let i = hits.length - 1; i >= 0; i--) {
|
|
1426
|
+
const hit = hits[i];
|
|
1427
|
+
out = out.slice(0, hit.start) + JSON.stringify(targets[i]) + out.slice(hit.end);
|
|
1428
|
+
}
|
|
1429
|
+
const key = `${abs}\0${out}`;
|
|
1430
|
+
let url = stableUrls.get(key);
|
|
1431
|
+
if (url === void 0) {
|
|
1432
|
+
url = createObjectURL(new Blob([out], { type: "text/javascript" }));
|
|
1433
|
+
stableUrls.set(key, url);
|
|
1434
|
+
}
|
|
1435
|
+
return url;
|
|
1436
|
+
})();
|
|
1437
|
+
moduleUrls.set(abs, pending);
|
|
1438
|
+
try {
|
|
1439
|
+
return await pending;
|
|
1440
|
+
} catch (error) {
|
|
1441
|
+
moduleUrls.delete(abs);
|
|
1442
|
+
throw error;
|
|
1443
|
+
} finally {
|
|
1444
|
+
inFlight.delete(abs);
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
const revokeStableUrls = () => {
|
|
1448
|
+
for (const url of stableUrls.values()) revokeObjectURL(url);
|
|
1449
|
+
stableUrls.clear();
|
|
1450
|
+
};
|
|
1451
|
+
function directoryListing2() {
|
|
1452
|
+
const rows = vfs.listDirectory(root).map((e) => `<li>${e.type === "file" ? "\u{1F4C4}" : "\u{1F4C1}"} <code>${e.path}</code></li>`).join("");
|
|
1453
|
+
return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><title>vite \xB7 ${root}</title></head>
|
|
1454
|
+
<body style="font-family:system-ui;padding:24px"><h3>vite dev server \xB7 <code>${root}</code></h3>
|
|
1455
|
+
<p>\u672A\u627E\u5230\u5165\u53E3 <code>${entry}</code>\u3002\u5F53\u524D\u76EE\u5F55\u5185\u5BB9\uFF1A</p><ul>${rows}</ul>
|
|
1456
|
+
<p style="color:#64748b">\u5728 root \u4E0B\u521B\u5EFA ${entry}\uFF0C\u7528 <script type="module" src="./main.ts"> \u5F15\u5165\u6A21\u5757\u5165\u53E3\u3002</p></body></html>`;
|
|
1457
|
+
}
|
|
1458
|
+
function errorDocument(error) {
|
|
1459
|
+
const reason = error instanceof ViteDevError ? error.reason : "BUILD_FAILED";
|
|
1460
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1461
|
+
return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><title>vite error</title></head>
|
|
1462
|
+
<body style="font-family:ui-monospace,monospace;background:#1a1c24;color:#e2e8f0;padding:24px">
|
|
1463
|
+
<h2 style="color:#f43f5e;margin-top:0">[vite] ${reason}</h2>
|
|
1464
|
+
<pre style="white-space:pre-wrap;word-break:break-all;line-height:1.7">${message.replace(/&/g, "&").replace(/</g, "<")}</pre>
|
|
1465
|
+
<p style="color:#94a3b8">\u4FEE\u590D\u4EE3\u7801\u540E\u9884\u89C8\u81EA\u52A8\u91CD\u5EFA\uFF1B\u4E5F\u53EF\u5728\u7EC8\u7AEF\u6267\u884C vite stop / vite <dir> \u91CD\u542F\u3002</p>
|
|
1466
|
+
</body></html>`;
|
|
1467
|
+
}
|
|
1468
|
+
async function buildDocument() {
|
|
1469
|
+
const entryAbs = join(root, entry);
|
|
1470
|
+
if (!vfs.exists(entryAbs) || vfs.isDirectory(entryAbs)) return directoryListing2();
|
|
1471
|
+
let html = vfs.readFile(entryAbs);
|
|
1472
|
+
html = inlineAssets(html, vfs, dirname(entryAbs));
|
|
1473
|
+
const entryModuleUrls = [];
|
|
1474
|
+
html = html.replace(
|
|
1475
|
+
MODULE_SCRIPT_SRC_RE,
|
|
1476
|
+
(_whole, pre, _q1, mid, _q2, src, post) => {
|
|
1477
|
+
const base = src.startsWith("/") ? normalize(src) : join(dirname(entryAbs), src);
|
|
1478
|
+
const abs = resolveWithSuffixes(vfs, base);
|
|
1479
|
+
if (!vfs.exists(abs) || vfs.isDirectory(abs))
|
|
1480
|
+
throw new ViteDevError(
|
|
1481
|
+
"RESOLVE",
|
|
1482
|
+
`\u5165\u53E3 module script \u627E\u4E0D\u5230 "${src}"\uFF08root: ${root}\uFF1B\u8BD5\u8FC7\u6269\u5C55\u540D ${VITE_RESOLVE_SUFFIXES.join("/")} \u4E0E\u76EE\u5F55 index\uFF09`
|
|
1483
|
+
);
|
|
1484
|
+
entryModuleUrls.push(loadModuleUrl(abs));
|
|
1485
|
+
return `<script${pre}${mid}src="__adep_vite_entry_${entryModuleUrls.length - 1}__"${post}></script>`;
|
|
1486
|
+
}
|
|
1487
|
+
);
|
|
1488
|
+
html = html.replace(
|
|
1489
|
+
MODULE_SCRIPT_INLINE_RE,
|
|
1490
|
+
(whole, pre, _q, post, body) => {
|
|
1491
|
+
if (/\bsrc\s*=/.test(pre + post)) return whole;
|
|
1492
|
+
const abs = `${INLINE_PREFIX}${inlineSeq++}.mjs`;
|
|
1493
|
+
inlineSources.set(abs, body);
|
|
1494
|
+
entryModuleUrls.push(loadModuleUrl(abs));
|
|
1495
|
+
return `<script${pre}${post}src="__adep_vite_entry_${entryModuleUrls.length - 1}__"></script>`;
|
|
1496
|
+
}
|
|
1497
|
+
);
|
|
1498
|
+
const urls = await Promise.all(entryModuleUrls);
|
|
1499
|
+
html = html.replace(/__adep_vite_entry_(\d+)__/g, (_m, idx) => urls[Number(idx)] ?? "");
|
|
1500
|
+
const interceptor = `<script>${FN_FETCH_INTERCEPTOR_SCRIPT}</script>`;
|
|
1501
|
+
html = /<head[^>]*>/i.test(html) ? html.replace(/<head[^>]*>/i, (m) => `${m}${interceptor}`) : `${interceptor}${html}`;
|
|
1502
|
+
const hmr = hmrScript(channelName);
|
|
1503
|
+
return /<\/body>/i.test(html) ? html.replace(/<\/(body)>/i, `${hmr}</$1>`) : html + hmr;
|
|
1504
|
+
}
|
|
1505
|
+
async function rebuild() {
|
|
1506
|
+
moduleUrls = /* @__PURE__ */ new Map();
|
|
1507
|
+
inlineSources = /* @__PURE__ */ new Map();
|
|
1508
|
+
inlineSeq = 0;
|
|
1509
|
+
let doc;
|
|
1510
|
+
try {
|
|
1511
|
+
doc = await buildDocument();
|
|
1512
|
+
} catch (error) {
|
|
1513
|
+
doc = errorDocument(error);
|
|
1514
|
+
}
|
|
1515
|
+
const changed = lastDoc !== null && doc !== lastDoc;
|
|
1516
|
+
lastDoc = doc;
|
|
1517
|
+
if (currentUrl !== "") revokeObjectURL(currentUrl);
|
|
1518
|
+
currentUrl = createObjectURL(new Blob([doc], { type: "text/html" }));
|
|
1519
|
+
if (changed) broadcast(currentUrl);
|
|
1520
|
+
}
|
|
1521
|
+
const server = {
|
|
1522
|
+
get url() {
|
|
1523
|
+
return currentUrl;
|
|
1524
|
+
},
|
|
1525
|
+
get port() {
|
|
1526
|
+
return port;
|
|
1527
|
+
},
|
|
1528
|
+
get root() {
|
|
1529
|
+
return root;
|
|
1530
|
+
},
|
|
1531
|
+
ready: rebuild(),
|
|
1532
|
+
touch() {
|
|
1533
|
+
scheduleRebuild();
|
|
1534
|
+
},
|
|
1535
|
+
async stop() {
|
|
1536
|
+
if (touchTimer !== null) {
|
|
1537
|
+
clearTimeout(touchTimer);
|
|
1538
|
+
touchTimer = null;
|
|
1539
|
+
}
|
|
1540
|
+
unsubscribeMutate();
|
|
1541
|
+
moduleUrls = /* @__PURE__ */ new Map();
|
|
1542
|
+
revokeStableUrls();
|
|
1543
|
+
if (currentUrl !== "") revokeObjectURL(currentUrl);
|
|
1544
|
+
currentUrl = "";
|
|
1545
|
+
bc?.close();
|
|
1546
|
+
bc = null;
|
|
1547
|
+
}
|
|
1548
|
+
};
|
|
1549
|
+
const unsubscribeMutate = vfs.onMutate(scheduleRebuild);
|
|
1550
|
+
return server;
|
|
1551
|
+
}
|
|
1552
|
+
var ViteDevError, REGEX_PRECEDING_CHARS, REGEX_PRECEDING_KEYWORDS, isIdentStart, isIdentPart, VITE_RESOLVE_SUFFIXES, INLINE_PREFIX, EXTERNAL_SPECIFIER, DATA_OR_BLOB, MODULE_SCRIPT_SRC_RE, MODULE_SCRIPT_INLINE_RE;
|
|
1553
|
+
var init_vite_dev = __esm({
|
|
1554
|
+
"src/vite-dev.ts"() {
|
|
1555
|
+
"use strict";
|
|
1556
|
+
init_path();
|
|
1557
|
+
init_node_resolve();
|
|
1558
|
+
init_strip_types();
|
|
1559
|
+
init_preview_server();
|
|
1560
|
+
init_function_fetch_proxy();
|
|
1561
|
+
ViteDevError = class extends Error {
|
|
1562
|
+
constructor(reason, message) {
|
|
1563
|
+
super(message);
|
|
1564
|
+
this.reason = reason;
|
|
1565
|
+
this.name = "ViteDevError";
|
|
1566
|
+
}
|
|
1567
|
+
};
|
|
1568
|
+
REGEX_PRECEDING_CHARS = /* @__PURE__ */ new Set([
|
|
1569
|
+
"=",
|
|
1570
|
+
"(",
|
|
1571
|
+
",",
|
|
1572
|
+
"[",
|
|
1573
|
+
"!",
|
|
1574
|
+
"&",
|
|
1575
|
+
"|",
|
|
1576
|
+
"?",
|
|
1577
|
+
":",
|
|
1578
|
+
";",
|
|
1579
|
+
"{",
|
|
1580
|
+
"}",
|
|
1581
|
+
"^",
|
|
1582
|
+
"~",
|
|
1583
|
+
"<",
|
|
1584
|
+
">",
|
|
1585
|
+
"+",
|
|
1586
|
+
"-",
|
|
1587
|
+
"*",
|
|
1588
|
+
"/",
|
|
1589
|
+
"%"
|
|
1590
|
+
]);
|
|
1591
|
+
REGEX_PRECEDING_KEYWORDS = /* @__PURE__ */ new Set([
|
|
1592
|
+
"return",
|
|
1593
|
+
"typeof",
|
|
1594
|
+
"instanceof",
|
|
1595
|
+
"in",
|
|
1596
|
+
"of",
|
|
1597
|
+
"new",
|
|
1598
|
+
"delete",
|
|
1599
|
+
"void",
|
|
1600
|
+
"throw",
|
|
1601
|
+
"case",
|
|
1602
|
+
"do",
|
|
1603
|
+
"else",
|
|
1604
|
+
"yield",
|
|
1605
|
+
"await"
|
|
1606
|
+
]);
|
|
1607
|
+
isIdentStart = (ch) => /[A-Za-z_$]/.test(ch);
|
|
1608
|
+
isIdentPart = (ch) => /[A-Za-z0-9_$]/.test(ch);
|
|
1609
|
+
VITE_RESOLVE_SUFFIXES = [".ts", ".js", ".mjs", ".mts", ".vue", ".json", ".css"];
|
|
1610
|
+
INLINE_PREFIX = "/__vite_inline_";
|
|
1611
|
+
EXTERNAL_SPECIFIER = /^(?:https?:)?\/\//i;
|
|
1612
|
+
DATA_OR_BLOB = /^(?:data|blob):/i;
|
|
1613
|
+
MODULE_SCRIPT_SRC_RE = /<script\b([^>]*?)\btype\s*=\s*(["'])module\2([^>]*?)\bsrc\s*=\s*(["'])(.*?)\4([^>]*)><\/script>/gi;
|
|
1614
|
+
MODULE_SCRIPT_INLINE_RE = /<script\b([^>]*?)\btype\s*=\s*(["'])module\2([^>]*)>([\s\S]*?)<\/script>/gi;
|
|
1615
|
+
}
|
|
1616
|
+
});
|
|
1617
|
+
|
|
1618
|
+
// src/vfs.ts
|
|
189
1619
|
init_path();
|
|
190
1620
|
var ensureAbs = (path) => path.startsWith("/") ? normalize(path) : `/${normalize(path).replace(/^\/+/, "")}`;
|
|
191
1621
|
var ROOT = "/";
|
|
@@ -395,10 +1825,13 @@ var VirtualFileSystem = class {
|
|
|
395
1825
|
}
|
|
396
1826
|
};
|
|
397
1827
|
|
|
398
|
-
//
|
|
1828
|
+
// src/container.ts
|
|
1829
|
+
init_path();
|
|
1830
|
+
|
|
1831
|
+
// src/shell.ts
|
|
399
1832
|
init_path();
|
|
400
1833
|
|
|
401
|
-
//
|
|
1834
|
+
// src/completion.ts
|
|
402
1835
|
init_path();
|
|
403
1836
|
var SHELL_COMMANDS = [
|
|
404
1837
|
"pwd",
|
|
@@ -414,9 +1847,12 @@ var SHELL_COMMANDS = [
|
|
|
414
1847
|
"cd",
|
|
415
1848
|
"head",
|
|
416
1849
|
"tail",
|
|
1850
|
+
"curl",
|
|
1851
|
+
"wget",
|
|
417
1852
|
"test",
|
|
418
1853
|
"node",
|
|
419
1854
|
"js",
|
|
1855
|
+
"vite",
|
|
420
1856
|
"help"
|
|
421
1857
|
];
|
|
422
1858
|
var EXTRA_COMMANDS = ["npm"];
|
|
@@ -507,7 +1943,7 @@ function helpText() {
|
|
|
507
1943
|
`;
|
|
508
1944
|
}
|
|
509
1945
|
|
|
510
|
-
//
|
|
1946
|
+
// src/shell.ts
|
|
511
1947
|
function tokenize(line) {
|
|
512
1948
|
const tokens = [];
|
|
513
1949
|
let current = "";
|
|
@@ -721,6 +2157,33 @@ var Shell = class {
|
|
|
721
2157
|
}
|
|
722
2158
|
break;
|
|
723
2159
|
}
|
|
2160
|
+
case "curl": {
|
|
2161
|
+
const outcome = await this.runCurl(argv.slice(1));
|
|
2162
|
+
stdout = outcome.stdout;
|
|
2163
|
+
stderr = outcome.stderr;
|
|
2164
|
+
exitCode = outcome.code;
|
|
2165
|
+
break;
|
|
2166
|
+
}
|
|
2167
|
+
case "wget": {
|
|
2168
|
+
const outcome = await this.runWget(argv.slice(1));
|
|
2169
|
+
stdout = outcome.stdout;
|
|
2170
|
+
stderr = outcome.stderr;
|
|
2171
|
+
exitCode = outcome.code;
|
|
2172
|
+
break;
|
|
2173
|
+
}
|
|
2174
|
+
case "vite": {
|
|
2175
|
+
if (this.ctx.vite === void 0)
|
|
2176
|
+
return {
|
|
2177
|
+
code: 1,
|
|
2178
|
+
stdout: "",
|
|
2179
|
+
stderr: "vite: \u672A\u88C5\u914D\u524D\u7AEF\u5F00\u53D1\u670D\u52A1\u5668\uFF08bootstrap \u4F1A\u6CE8\u5165\uFF1B\u88F8 Shell \u9700\u81EA\u884C\u7ED9 ctx.vite\uFF09\n"
|
|
2180
|
+
};
|
|
2181
|
+
const outcome = await this.runVite(argv.slice(1));
|
|
2182
|
+
stdout = outcome.stdout;
|
|
2183
|
+
stderr = outcome.stderr;
|
|
2184
|
+
exitCode = outcome.code;
|
|
2185
|
+
break;
|
|
2186
|
+
}
|
|
724
2187
|
default:
|
|
725
2188
|
stderr = `${command}: command not found
|
|
726
2189
|
`;
|
|
@@ -743,6 +2206,168 @@ var Shell = class {
|
|
|
743
2206
|
}
|
|
744
2207
|
return out;
|
|
745
2208
|
}
|
|
2209
|
+
/**
|
|
2210
|
+
* `curl [选项] <url>`(常用旗标最小子集):
|
|
2211
|
+
* - 响应体默认写 stdout(可 `|` 管道 / `>` 重定向);`-o <file>` 写 VFS 文件、`-o -` 显式 stdout;
|
|
2212
|
+
* - `-i/--include` 输出响应头 + 体;`-I/--head` 发 HEAD 且只输出响应头;
|
|
2213
|
+
* - `-X/--request`、`-H/--header`(可多次)、`-d/--data`(可多次,`&` 拼接,隐含 POST 与表单头)、
|
|
2214
|
+
* `-A/--user-agent`、`-u/--user`(转 Basic 认证)、`-f/--fail`(HTTP ≥400 退出码 22);
|
|
2215
|
+
* - `-s/-S/-L/-k/-g/-N/--compressed` 等常见旗标接受但无效果(仿真环境本就静默/跟随重定向/不校验证书);
|
|
2216
|
+
* - 裸主机名(`example.com/x`)按 curl 语义补 `http://`;网络故障退出码 7,用法错误 2。
|
|
2217
|
+
*/
|
|
2218
|
+
async runCurl(args) {
|
|
2219
|
+
const flags = parseCurlArgs(args);
|
|
2220
|
+
if (flags.url === null) {
|
|
2221
|
+
return { code: 2, stdout: "", stderr: "curl: \u672A\u63D0\u4F9B URL\uFF08\u7528\u6CD5\uFF1Acurl [\u9009\u9879] <url>\uFF09\n" };
|
|
2222
|
+
}
|
|
2223
|
+
const url = normalizeUrl(flags.url);
|
|
2224
|
+
if (url === null)
|
|
2225
|
+
return { code: 3, stdout: "", stderr: `curl: (3) URL \u683C\u5F0F\u9519\u8BEF\uFF1A${flags.url}
|
|
2226
|
+
` };
|
|
2227
|
+
const method = flags.head ? "HEAD" : flags.method ?? (flags.data.length > 0 ? "POST" : "GET");
|
|
2228
|
+
const headers = { ...flags.headers };
|
|
2229
|
+
const body = flags.data.length > 0 ? flags.data.join("&") : void 0;
|
|
2230
|
+
if (body !== void 0 && !("content-type" in headers)) {
|
|
2231
|
+
headers["content-type"] = "application/x-www-form-urlencoded";
|
|
2232
|
+
}
|
|
2233
|
+
let res;
|
|
2234
|
+
try {
|
|
2235
|
+
res = await this.netRequest(url, {
|
|
2236
|
+
method,
|
|
2237
|
+
headers,
|
|
2238
|
+
...body === void 0 ? {} : { body }
|
|
2239
|
+
});
|
|
2240
|
+
} catch (error) {
|
|
2241
|
+
if (error instanceof NetCommandError && error.kind === "no-net")
|
|
2242
|
+
return { code: 1, stdout: "", stderr: `curl: ${error.message}
|
|
2243
|
+
` };
|
|
2244
|
+
return {
|
|
2245
|
+
code: 7,
|
|
2246
|
+
stdout: "",
|
|
2247
|
+
stderr: `curl: (7) \u65E0\u6CD5\u8FDE\u63A5\u4E3B\u673A\uFF1A${error instanceof Error ? error.message : String(error)}
|
|
2248
|
+
`
|
|
2249
|
+
};
|
|
2250
|
+
}
|
|
2251
|
+
const head = flags.head || flags.include ? formatResponseHead(res) : "";
|
|
2252
|
+
let stdout;
|
|
2253
|
+
if (flags.output !== null && flags.output !== "-") {
|
|
2254
|
+
this.ctx.vfs.writeFile(this.resolve(flags.output), res.body);
|
|
2255
|
+
stdout = head;
|
|
2256
|
+
} else {
|
|
2257
|
+
stdout = head + (flags.head ? "" : res.body);
|
|
2258
|
+
}
|
|
2259
|
+
if (flags.fail && !res.ok) {
|
|
2260
|
+
return { code: 22, stdout, stderr: `curl: (22) HTTP \u8BF7\u6C42\u8FD4\u56DE\u9519\u8BEF\u7801 ${res.status}
|
|
2261
|
+
` };
|
|
2262
|
+
}
|
|
2263
|
+
return { code: 0, stdout, stderr: "" };
|
|
2264
|
+
}
|
|
2265
|
+
/**
|
|
2266
|
+
* `wget [选项] <url>`(常用旗标最小子集):
|
|
2267
|
+
* - 默认按 URL 末级路径名落 VFS 文件(目录形态落 `index.html`),过程信息走 stderr;
|
|
2268
|
+
* - `-O <file>` 指定文件名、`-O -` 输出到 stdout;`-q/--quiet` 静默;`-S/--server-response` 打印响应头;
|
|
2269
|
+
* - HTTP ≥400 / 网络失败退出码 1 且不落文件(与 GNU wget 默认一致)。
|
|
2270
|
+
*/
|
|
2271
|
+
async runWget(args) {
|
|
2272
|
+
const flags = parseWgetArgs(args);
|
|
2273
|
+
if (flags.url === null) {
|
|
2274
|
+
return { code: 1, stdout: "", stderr: "wget: \u672A\u63D0\u4F9B URL\uFF08\u7528\u6CD5\uFF1Awget [\u9009\u9879] <url>\uFF09\n" };
|
|
2275
|
+
}
|
|
2276
|
+
const url = normalizeUrl(flags.url);
|
|
2277
|
+
if (url === null) return { code: 1, stdout: "", stderr: `wget: URL \u683C\u5F0F\u9519\u8BEF\uFF1A${flags.url}
|
|
2278
|
+
` };
|
|
2279
|
+
let parsed;
|
|
2280
|
+
try {
|
|
2281
|
+
parsed = new URL(url);
|
|
2282
|
+
} catch {
|
|
2283
|
+
return { code: 1, stdout: "", stderr: `wget: URL \u683C\u5F0F\u9519\u8BEF\uFF1A${url}
|
|
2284
|
+
` };
|
|
2285
|
+
}
|
|
2286
|
+
let res;
|
|
2287
|
+
try {
|
|
2288
|
+
res = await this.netRequest(url, { method: "GET" });
|
|
2289
|
+
} catch (error) {
|
|
2290
|
+
if (error instanceof NetCommandError && error.kind === "no-net")
|
|
2291
|
+
return { code: 1, stdout: "", stderr: `wget: ${error.message}
|
|
2292
|
+
` };
|
|
2293
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
2294
|
+
return { code: 1, stdout: "", stderr: `wget: \u65E0\u6CD5\u4E0B\u8F7D ${url}\uFF1A${reason}
|
|
2295
|
+
` };
|
|
2296
|
+
}
|
|
2297
|
+
const toStdout = flags.output === "-";
|
|
2298
|
+
const fileName = flags.output !== null && flags.output !== "-" ? flags.output : wgetFileName(parsed);
|
|
2299
|
+
let stderr = "";
|
|
2300
|
+
if (!flags.quiet) {
|
|
2301
|
+
const type = res.headers["content-type"] ?? "application/octet-stream";
|
|
2302
|
+
const length = res.headers["content-length"] ?? String(new TextEncoder().encode(res.body).byteLength);
|
|
2303
|
+
stderr += `\u6B63\u5728\u8FDE\u63A5 ${parsed.hostname}...
|
|
2304
|
+
`;
|
|
2305
|
+
stderr += `HTTP \u8BF7\u6C42\u5DF2\u53D1\u9001\uFF0C\u6B63\u5728\u7B49\u5F85\u54CD\u5E94... ${res.status}${res.statusText ? ` ${res.statusText}` : ""}
|
|
2306
|
+
`;
|
|
2307
|
+
if (flags.serverResponse) stderr += formatResponseHead(res);
|
|
2308
|
+
stderr += `\u957F\u5EA6\uFF1A${length} [${type}]
|
|
2309
|
+
`;
|
|
2310
|
+
if (!toStdout) stderr += `\u6B63\u5728\u4FDD\u5B58\u5230: \u201C${fileName}\u201D
|
|
2311
|
+
`;
|
|
2312
|
+
}
|
|
2313
|
+
if (!res.ok) {
|
|
2314
|
+
return { code: 1, stdout: "", stderr: `${stderr}wget: \u9519\u8BEF\uFF1A\u670D\u52A1\u5668\u8FD4\u56DE HTTP ${res.status}
|
|
2315
|
+
` };
|
|
2316
|
+
}
|
|
2317
|
+
if (toStdout) return { code: 0, stdout: res.body, stderr };
|
|
2318
|
+
this.ctx.vfs.writeFile(this.resolve(fileName), res.body);
|
|
2319
|
+
if (!flags.quiet) stderr += `${fileName} \u5DF2\u4FDD\u5B58 [${res.body.length} \u5B57\u8282]
|
|
2320
|
+
`;
|
|
2321
|
+
return { code: 0, stdout: "", stderr };
|
|
2322
|
+
}
|
|
2323
|
+
/**
|
|
2324
|
+
* `vite [子命令|目录]`:
|
|
2325
|
+
* - `vite` / `vite dev [dir]`:启动浏览器内 Vite 兼容 dev server(`dir` 为 root,缺省 `/`;
|
|
2326
|
+
* `--port` 等旗标接受但无效果——端口是虚拟语义值);
|
|
2327
|
+
* - `vite stop`:停止并释放;
|
|
2328
|
+
* - `vite build`:首版明确不支持(dev 语义优先),如实报错。
|
|
2329
|
+
* dev server 常驻在容器层(`serverready` 事件 + HMR),命令本身立即返回——与真实
|
|
2330
|
+
* vite 的前台常驻进程形态不同,属仿真终端的取舍(见 vite-dev.ts 文件头)。
|
|
2331
|
+
*/
|
|
2332
|
+
async runVite(args) {
|
|
2333
|
+
const positional = args.filter((a) => !a.startsWith("-"));
|
|
2334
|
+
const sub = positional[0] ?? "";
|
|
2335
|
+
if (sub === "stop") {
|
|
2336
|
+
const outcome2 = await this.ctx.vite.stop();
|
|
2337
|
+
return outcome2.code === 0 ? { ...outcome2, stderr: "" } : { code: outcome2.code, stdout: "", stderr: outcome2.stdout };
|
|
2338
|
+
}
|
|
2339
|
+
if (sub === "build")
|
|
2340
|
+
return {
|
|
2341
|
+
code: 1,
|
|
2342
|
+
stdout: "",
|
|
2343
|
+
stderr: "vite build: \u6682\u4E0D\u652F\u6301\uFF08\u6D4F\u89C8\u5668\u5185 vite \u9996\u7248\u53EA\u63D0\u4F9B dev server\uFF1B\u53D1\u5E03\u8D70\u524D\u7AEF\u53D1\u5E03\u7BA1\u7EBF\uFF09\n"
|
|
2344
|
+
};
|
|
2345
|
+
const rootArgs = sub === "" || sub === "dev" || sub === "serve" ? positional.slice(1) : [sub];
|
|
2346
|
+
const outcome = await this.ctx.vite.start(rootArgs);
|
|
2347
|
+
return outcome.code === 0 ? { ...outcome, stderr: "" } : { code: outcome.code, stdout: "", stderr: outcome.stdout };
|
|
2348
|
+
}
|
|
2349
|
+
/** 经 net 端口发请求;端口缺失 / 网络失败归一为 NetCommandError(curl/wget 各自映射退出码与文案)。 */
|
|
2350
|
+
async netRequest(url, init) {
|
|
2351
|
+
if (this.ctx.net === void 0) {
|
|
2352
|
+
throw new NetCommandError(
|
|
2353
|
+
"no-net",
|
|
2354
|
+
"\u672A\u6CE8\u5165\u7F51\u7EDC\u7AEF\u53E3\uFF08\u6D4F\u89C8\u5668\u7AEF\u7ECF\u5E73\u53F0 HTTP \u4E2D\u7EE7\u8BBF\u95EE\uFF1Bbootstrap \u9700\u63D0\u4F9B httpRelayBaseUrl\uFF09"
|
|
2355
|
+
);
|
|
2356
|
+
}
|
|
2357
|
+
try {
|
|
2358
|
+
const res = await this.ctx.net(url, init);
|
|
2359
|
+
return {
|
|
2360
|
+
ok: res.ok,
|
|
2361
|
+
status: res.status,
|
|
2362
|
+
statusText: res.statusText,
|
|
2363
|
+
headers: res.headers,
|
|
2364
|
+
body: await res.text()
|
|
2365
|
+
};
|
|
2366
|
+
} catch (error) {
|
|
2367
|
+
if (error instanceof NetCommandError) throw error;
|
|
2368
|
+
throw new NetCommandError("network", error instanceof Error ? error.message : String(error));
|
|
2369
|
+
}
|
|
2370
|
+
}
|
|
746
2371
|
/**
|
|
747
2372
|
* 对当前输入行求 Tab 补全候选(FE-022):命令位补内建命令名,参数位补 VFS 路径。
|
|
748
2373
|
* 无候选不抛错(Tab 不该让终端报错),由调用方决定「不响应」还是「列出可能项」。
|
|
@@ -756,261 +2381,290 @@ var Shell = class {
|
|
|
756
2381
|
return join(this.ctx.getCwd(), target);
|
|
757
2382
|
}
|
|
758
2383
|
};
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
function readString(bytes, start, length) {
|
|
765
|
-
let end = start;
|
|
766
|
-
while (end < start + length && bytes[end] !== 0) end++;
|
|
767
|
-
return new TextDecoder("ascii").decode(bytes.subarray(start, end)).replace(/\0+$/g, "").trim();
|
|
768
|
-
}
|
|
769
|
-
function parseOctalSize(bytes, start, length) {
|
|
770
|
-
const raw = bytes.subarray(start, start + length);
|
|
771
|
-
if ((raw[0] & 128) !== 0) {
|
|
772
|
-
let value = 0;
|
|
773
|
-
for (const byte of raw.subarray(1)) value = value * 256 + byte;
|
|
774
|
-
return value;
|
|
775
|
-
}
|
|
776
|
-
const text = readString(bytes, start, length);
|
|
777
|
-
if (text === "") return 0;
|
|
778
|
-
if (!/^[0-7]+$/.test(text)) throw new Error(`tar \u5934\u90E8 size \u5B57\u6BB5\u975E\u6CD5\uFF1A"${text}"`);
|
|
779
|
-
return Number.parseInt(text, 8);
|
|
780
|
-
}
|
|
781
|
-
function assertSafePath(path) {
|
|
782
|
-
if (path === "" || path.startsWith("/") || path.startsWith("\\"))
|
|
783
|
-
throw new Error(`tar \u6761\u76EE\u8DEF\u5F84\u975E\u6CD5\uFF08\u7EDD\u5BF9\u8DEF\u5F84\u6216\u4E3A\u7A7A\uFF09\uFF1A"${path}"`);
|
|
784
|
-
for (const segment of path.split("/")) {
|
|
785
|
-
if (segment === "..") throw new Error(`tar \u6761\u76EE\u8DEF\u5F84\u542B ".." \u7A7F\u8D8A\uFF0C\u5DF2\u62D2\u7EDD\uFF1A"${path}"`);
|
|
2384
|
+
var NetCommandError = class extends Error {
|
|
2385
|
+
constructor(kind, message) {
|
|
2386
|
+
super(message);
|
|
2387
|
+
this.kind = kind;
|
|
2388
|
+
this.name = "NetCommandError";
|
|
786
2389
|
}
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
2390
|
+
};
|
|
2391
|
+
var CURL_BOOLEAN_NOOP = /* @__PURE__ */ new Set([
|
|
2392
|
+
"-s",
|
|
2393
|
+
"--silent",
|
|
2394
|
+
"-S",
|
|
2395
|
+
"--show-error",
|
|
2396
|
+
"-L",
|
|
2397
|
+
"--location",
|
|
2398
|
+
"-k",
|
|
2399
|
+
"--insecure",
|
|
2400
|
+
"-g",
|
|
2401
|
+
"--globoff",
|
|
2402
|
+
"-N",
|
|
2403
|
+
"--no-buffer",
|
|
2404
|
+
"--compressed",
|
|
2405
|
+
"-#",
|
|
2406
|
+
"--progress-bar",
|
|
2407
|
+
"-v",
|
|
2408
|
+
"--verbose",
|
|
2409
|
+
"-G",
|
|
2410
|
+
"--get"
|
|
2411
|
+
]);
|
|
2412
|
+
var WGET_BOOLEAN_NOOP = /* @__PURE__ */ new Set([
|
|
2413
|
+
"--no-check-certificate",
|
|
2414
|
+
"-nc",
|
|
2415
|
+
"--no-clobber",
|
|
2416
|
+
"-c",
|
|
2417
|
+
"--continue",
|
|
2418
|
+
"-nv",
|
|
2419
|
+
"--no-verbose",
|
|
2420
|
+
"--content-on-error"
|
|
2421
|
+
]);
|
|
2422
|
+
function matchValueFlag(arg, name, longNames, consume) {
|
|
2423
|
+
if (arg === name || longNames.includes(arg)) return { matched: true, value: consume() };
|
|
2424
|
+
for (const long of longNames) {
|
|
2425
|
+
if (arg.startsWith(`${long}=`)) return { matched: true, value: arg.slice(long.length + 1) };
|
|
791
2426
|
}
|
|
792
|
-
|
|
793
|
-
const buffer = await new Response(stream).arrayBuffer();
|
|
794
|
-
return new Uint8Array(buffer);
|
|
2427
|
+
return { matched: false, value: null };
|
|
795
2428
|
}
|
|
796
|
-
function
|
|
797
|
-
const
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
if (header.every((byte) => byte === 0)) break;
|
|
803
|
-
const typeflag = String.fromCharCode(header[156]);
|
|
804
|
-
const size = parseOctalSize(header, 124, 12);
|
|
805
|
-
let name = readString(bytes, offset, 100);
|
|
806
|
-
const prefix = readString(bytes, offset + 345, 155);
|
|
807
|
-
if (prefix !== "") name = `${prefix}/${name}`;
|
|
808
|
-
const dataStart = offset + BLOCK;
|
|
809
|
-
const data = bytes.subarray(dataStart, dataStart + size);
|
|
810
|
-
offset = dataStart + Math.ceil(size / BLOCK) * BLOCK;
|
|
811
|
-
if (typeflag === "L") {
|
|
812
|
-
longName = new TextDecoder("utf-8").decode(data).replace(/\0+$/g, "").trim();
|
|
2429
|
+
function expandShortArgs(args, valueFlags) {
|
|
2430
|
+
const out = [];
|
|
2431
|
+
for (const arg of args) {
|
|
2432
|
+
const cluster = /^-([A-Za-z]+)$/.exec(arg);
|
|
2433
|
+
if (cluster === null) {
|
|
2434
|
+
out.push(arg);
|
|
813
2435
|
continue;
|
|
814
2436
|
}
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
assertSafePath(name);
|
|
825
|
-
entries.push({ path: name, contents: new Uint8Array(data) });
|
|
826
|
-
}
|
|
827
|
-
return entries;
|
|
828
|
-
}
|
|
829
|
-
async function unpackNpmTarball(bytes) {
|
|
830
|
-
const tar = unpackTar(await gunzip(bytes));
|
|
831
|
-
if (tar.length === 0) throw new Error("tarball \u89E3\u5305\u540E\u4E3A\u7A7A\uFF0C\u6CA1\u6709\u4EFB\u4F55\u6587\u4EF6\u6761\u76EE");
|
|
832
|
-
const out = [];
|
|
833
|
-
for (const entry of tar) {
|
|
834
|
-
if (!entry.path.startsWith("package/")) {
|
|
835
|
-
throw new Error(
|
|
836
|
-
`tarball \u9876\u5C42\u76EE\u5F55\u5F02\u5E38\uFF08npm registry \u5F52\u6863\u5E94\u5168\u90E8\u4F4D\u4E8E "package/" \u4E0B\uFF09\uFF1A"${entry.path}"`
|
|
837
|
-
);
|
|
2437
|
+
const chars = cluster[1];
|
|
2438
|
+
for (let j = 0; j < chars.length; j++) {
|
|
2439
|
+
const c = chars[j];
|
|
2440
|
+
out.push(`-${c}`);
|
|
2441
|
+
if (valueFlags.has(c)) {
|
|
2442
|
+
const rest = chars.slice(j + 1);
|
|
2443
|
+
if (rest !== "") out.push(rest);
|
|
2444
|
+
break;
|
|
2445
|
+
}
|
|
838
2446
|
}
|
|
839
|
-
out.push({ path: entry.path.slice("package/".length), contents: entry.contents });
|
|
840
|
-
}
|
|
841
|
-
if (!out.some((entry) => entry.path === "package.json")) {
|
|
842
|
-
throw new Error("tarball \u7F3A\u5C11 package.json\uFF0C\u62D2\u7EDD\u89E3\u5305");
|
|
843
2447
|
}
|
|
844
2448
|
return out;
|
|
845
2449
|
}
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
if (typeof rel !== "string" || rel === "") {
|
|
870
|
-
throw new ResolveError(`${context}\uFF1A\u5165\u53E3\u8DEF\u5F84\u5FC5\u987B\u662F\u5B57\u7B26\u4E32`);
|
|
871
|
-
}
|
|
872
|
-
if (!rel.startsWith("./") && !rel.startsWith("../") && rel.startsWith("/")) {
|
|
873
|
-
throw new ResolveError(`${context}\uFF1A\u5165\u53E3\u8DEF\u5F84 "${rel}" \u5FC5\u987B\u662F\u5305\u5185\u76F8\u5BF9\u8DEF\u5F84\uFF08\u4EE5 ./ \u5F00\u5934\uFF09`);
|
|
874
|
-
}
|
|
875
|
-
const abs = join(pkgDirAbs, rel.startsWith(".") ? rel : `./${rel}`);
|
|
876
|
-
if (!abs.startsWith(`${pkgDirAbs}/`)) {
|
|
877
|
-
throw new ResolveError(`${context}\uFF1A\u5165\u53E3\u8DEF\u5F84 "${rel}" \u8D8A\u51FA\u5305\u76EE\u5F55\uFF0C\u5DF2\u62D2\u7EDD`);
|
|
878
|
-
}
|
|
879
|
-
return abs;
|
|
880
|
-
}
|
|
881
|
-
function resolvePackageEntry(meta, pkgDirAbs, condition) {
|
|
882
|
-
const context = `\u5305 ${meta.name ?? meta.version ?? pkgDirAbs} \u7684 exports/main \u58F0\u660E`;
|
|
883
|
-
const exp = meta.exports;
|
|
884
|
-
if (exp !== void 0 && exp !== null) {
|
|
885
|
-
if (typeof exp === "string") return withinPackage(pkgDirAbs, exp, context);
|
|
886
|
-
if (typeof exp !== "object" || Array.isArray(exp)) {
|
|
887
|
-
throw new ResolveError(`${context} \u5F62\u6001\u4E0D\u652F\u6301\uFF08exports \u53EA\u80FD\u662F\u5B57\u7B26\u4E32\u6216\u5BF9\u8C61\uFF09`);
|
|
2450
|
+
function parseCurlArgs(rawArgs) {
|
|
2451
|
+
const args = expandShortArgs(rawArgs, /* @__PURE__ */ new Set(["X", "H", "d", "o", "A", "u", "m"]));
|
|
2452
|
+
const flags = {
|
|
2453
|
+
url: null,
|
|
2454
|
+
method: null,
|
|
2455
|
+
headers: {},
|
|
2456
|
+
data: [],
|
|
2457
|
+
include: false,
|
|
2458
|
+
head: false,
|
|
2459
|
+
output: null,
|
|
2460
|
+
fail: false
|
|
2461
|
+
};
|
|
2462
|
+
for (let i = 0; i < args.length; i++) {
|
|
2463
|
+
const arg = args[i];
|
|
2464
|
+
const consume = () => {
|
|
2465
|
+
const v = args[i + 1];
|
|
2466
|
+
if (v !== void 0) i++;
|
|
2467
|
+
return v ?? "";
|
|
2468
|
+
};
|
|
2469
|
+
let m = matchValueFlag(arg, "-X", ["--request"], consume);
|
|
2470
|
+
if (m.matched) {
|
|
2471
|
+
flags.method = (m.value ?? "").toUpperCase();
|
|
2472
|
+
continue;
|
|
888
2473
|
}
|
|
889
|
-
|
|
890
|
-
if (
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
)
|
|
2474
|
+
m = matchValueFlag(arg, "-H", ["--header"], consume);
|
|
2475
|
+
if (m.matched) {
|
|
2476
|
+
const raw = m.value ?? "";
|
|
2477
|
+
const colon = raw.indexOf(":");
|
|
2478
|
+
if (colon > 0)
|
|
2479
|
+
flags.headers[raw.slice(0, colon).trim().toLowerCase()] = raw.slice(colon + 1).trim();
|
|
2480
|
+
continue;
|
|
894
2481
|
}
|
|
895
|
-
|
|
896
|
-
if (
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
throw new ResolveError(`${context}\uFF1Aexports["."] \u5F62\u6001\u4E0D\u652F\u6301\uFF08\u4EC5\u63A5\u53D7\u5B57\u7B26\u4E32\u6216\u6761\u4EF6\u5BF9\u8C61\uFF09`);
|
|
2482
|
+
m = matchValueFlag(arg, "-A", ["--user-agent"], consume);
|
|
2483
|
+
if (m.matched) {
|
|
2484
|
+
flags.headers["user-agent"] = m.value ?? "";
|
|
2485
|
+
continue;
|
|
900
2486
|
}
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
if (typeof value === "string") return withinPackage(pkgDirAbs, value, context);
|
|
906
|
-
if (value !== void 0 && (typeof value !== "object" || value === null)) {
|
|
907
|
-
throw new ResolveError(
|
|
908
|
-
`${context}\uFF1Aexports["."]["${key}"] \u5F62\u6001\u4E0D\u652F\u6301\uFF08\u4EC5\u63A5\u53D7\u5B57\u7B26\u4E32\u6216 null\uFF09`
|
|
909
|
-
);
|
|
910
|
-
}
|
|
2487
|
+
m = matchValueFlag(arg, "-u", ["--user"], consume);
|
|
2488
|
+
if (m.matched) {
|
|
2489
|
+
flags.headers["authorization"] = `Basic ${btoa(m.value ?? "")}`;
|
|
2490
|
+
continue;
|
|
911
2491
|
}
|
|
912
|
-
|
|
913
|
-
|
|
2492
|
+
m = matchValueFlag(
|
|
2493
|
+
arg,
|
|
2494
|
+
"-d",
|
|
2495
|
+
["--data", "--data-raw", "--data-binary", "--data-urlencode"],
|
|
2496
|
+
consume
|
|
914
2497
|
);
|
|
2498
|
+
if (m.matched) {
|
|
2499
|
+
flags.data.push(m.value ?? "");
|
|
2500
|
+
continue;
|
|
2501
|
+
}
|
|
2502
|
+
m = matchValueFlag(arg, "-o", ["--output"], consume);
|
|
2503
|
+
if (m.matched) {
|
|
2504
|
+
flags.output = m.value;
|
|
2505
|
+
continue;
|
|
2506
|
+
}
|
|
2507
|
+
m = matchValueFlag(arg, "-m", ["--max-time", "--connect-timeout"], consume);
|
|
2508
|
+
if (m.matched) continue;
|
|
2509
|
+
if (arg === "-i" || arg === "--include") {
|
|
2510
|
+
flags.include = true;
|
|
2511
|
+
continue;
|
|
2512
|
+
}
|
|
2513
|
+
if (arg === "-I" || arg === "--head") {
|
|
2514
|
+
flags.head = true;
|
|
2515
|
+
continue;
|
|
2516
|
+
}
|
|
2517
|
+
if (arg === "-f" || arg === "--fail") {
|
|
2518
|
+
flags.fail = true;
|
|
2519
|
+
continue;
|
|
2520
|
+
}
|
|
2521
|
+
if (CURL_BOOLEAN_NOOP.has(arg) || arg.startsWith("-")) continue;
|
|
2522
|
+
if (flags.url === null) flags.url = arg;
|
|
915
2523
|
}
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
2524
|
+
return flags;
|
|
2525
|
+
}
|
|
2526
|
+
function parseWgetArgs(rawArgs) {
|
|
2527
|
+
const args = expandShortArgs(rawArgs, /* @__PURE__ */ new Set(["O"]));
|
|
2528
|
+
const flags = { url: null, output: null, quiet: false, serverResponse: false };
|
|
2529
|
+
for (let i = 0; i < args.length; i++) {
|
|
2530
|
+
const arg = args[i];
|
|
2531
|
+
const consume = () => {
|
|
2532
|
+
const v = args[i + 1];
|
|
2533
|
+
if (v !== void 0) i++;
|
|
2534
|
+
return v ?? "";
|
|
2535
|
+
};
|
|
2536
|
+
const m = matchValueFlag(arg, "-O", ["--output-document"], consume);
|
|
2537
|
+
if (m.matched) {
|
|
2538
|
+
flags.output = m.value;
|
|
2539
|
+
continue;
|
|
919
2540
|
}
|
|
920
|
-
|
|
2541
|
+
if (arg === "-q" || arg === "--quiet") {
|
|
2542
|
+
flags.quiet = true;
|
|
2543
|
+
continue;
|
|
2544
|
+
}
|
|
2545
|
+
if (arg === "-S" || arg === "--server-response") {
|
|
2546
|
+
flags.serverResponse = true;
|
|
2547
|
+
continue;
|
|
2548
|
+
}
|
|
2549
|
+
if (WGET_BOOLEAN_NOOP.has(arg) || arg.startsWith("-")) continue;
|
|
2550
|
+
if (flags.url === null) flags.url = arg;
|
|
921
2551
|
}
|
|
922
|
-
return
|
|
2552
|
+
return flags;
|
|
923
2553
|
}
|
|
924
|
-
function
|
|
925
|
-
if (
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
2554
|
+
function normalizeUrl(raw) {
|
|
2555
|
+
if (raw.startsWith("/")) return null;
|
|
2556
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(raw)) return raw;
|
|
2557
|
+
return `http://${raw}`;
|
|
2558
|
+
}
|
|
2559
|
+
function formatResponseHead(res) {
|
|
2560
|
+
const reason = res.statusText !== "" ? ` ${res.statusText}` : "";
|
|
2561
|
+
let head = `HTTP/1.1 ${res.status}${reason}\r
|
|
2562
|
+
`;
|
|
2563
|
+
for (const [key, value] of Object.entries(res.headers)) head += `${key}: ${value}\r
|
|
2564
|
+
`;
|
|
2565
|
+
return `${head}\r
|
|
2566
|
+
`;
|
|
2567
|
+
}
|
|
2568
|
+
function wgetFileName(url) {
|
|
2569
|
+
const segments = url.pathname.split("/").filter((s) => s !== "");
|
|
2570
|
+
const last = segments.length === 0 ? void 0 : segments[segments.length - 1];
|
|
2571
|
+
if (last === void 0) return "index.html";
|
|
2572
|
+
try {
|
|
2573
|
+
return decodeURIComponent(last);
|
|
2574
|
+
} catch {
|
|
2575
|
+
return last;
|
|
931
2576
|
}
|
|
932
|
-
const slash = spec.indexOf("/");
|
|
933
|
-
if (slash === -1) return { name: spec, subpath: "" };
|
|
934
|
-
return { name: spec.slice(0, slash), subpath: spec.slice(slash + 1) };
|
|
935
2577
|
}
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
2578
|
+
|
|
2579
|
+
// src/tar.ts
|
|
2580
|
+
var BLOCK = 512;
|
|
2581
|
+
var GZIP_MAGIC_0 = 31;
|
|
2582
|
+
var GZIP_MAGIC_1 = 139;
|
|
2583
|
+
function readString(bytes, start, length) {
|
|
2584
|
+
let end = start;
|
|
2585
|
+
while (end < start + length && bytes[end] !== 0) end++;
|
|
2586
|
+
return new TextDecoder("ascii").decode(bytes.subarray(start, end)).replace(/\0+$/g, "").trim();
|
|
2587
|
+
}
|
|
2588
|
+
function parseOctalSize(bytes, start, length) {
|
|
2589
|
+
const raw = bytes.subarray(start, start + length);
|
|
2590
|
+
if ((raw[0] & 128) !== 0) {
|
|
2591
|
+
let value = 0;
|
|
2592
|
+
for (const byte of raw.subarray(1)) value = value * 256 + byte;
|
|
2593
|
+
return value;
|
|
941
2594
|
}
|
|
942
|
-
|
|
2595
|
+
const text = readString(bytes, start, length);
|
|
2596
|
+
if (text === "") return 0;
|
|
2597
|
+
if (!/^[0-7]+$/.test(text)) throw new Error(`tar \u5934\u90E8 size \u5B57\u6BB5\u975E\u6CD5\uFF1A"${text}"`);
|
|
2598
|
+
return Number.parseInt(text, 8);
|
|
943
2599
|
}
|
|
944
|
-
function
|
|
945
|
-
if (
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
const entry = resolvePackageEntry(meta, dirAbs, "require");
|
|
950
|
-
const hit = asFile(fs, entry);
|
|
951
|
-
if (hit !== null) return hit;
|
|
2600
|
+
function assertSafePath(path) {
|
|
2601
|
+
if (path === "" || path.startsWith("/") || path.startsWith("\\"))
|
|
2602
|
+
throw new Error(`tar \u6761\u76EE\u8DEF\u5F84\u975E\u6CD5\uFF08\u7EDD\u5BF9\u8DEF\u5F84\u6216\u4E3A\u7A7A\uFF09\uFF1A"${path}"`);
|
|
2603
|
+
for (const segment of path.split("/")) {
|
|
2604
|
+
if (segment === "..") throw new Error(`tar \u6761\u76EE\u8DEF\u5F84\u542B ".." \u7A7F\u8D8A\uFF0C\u5DF2\u62D2\u7EDD\uFF1A"${path}"`);
|
|
952
2605
|
}
|
|
953
|
-
return asFile(fs, join(dirAbs, "index"));
|
|
954
2606
|
}
|
|
955
|
-
function
|
|
956
|
-
|
|
957
|
-
|
|
2607
|
+
async function gunzip(bytes) {
|
|
2608
|
+
if (bytes.length < 2 || bytes[0] !== GZIP_MAGIC_0 || bytes[1] !== GZIP_MAGIC_1) {
|
|
2609
|
+
throw new Error("tarball \u4E0D\u662F gzip \u6570\u636E\uFF08\u7F3A\u5C11 1f 8b \u9B54\u6570\uFF09\uFF0C\u65E0\u6CD5\u89E3\u5305");
|
|
2610
|
+
}
|
|
2611
|
+
const stream = new Blob([bytes]).stream().pipeThrough(new DecompressionStream("gzip"));
|
|
2612
|
+
const buffer = await new Response(stream).arrayBuffer();
|
|
2613
|
+
return new Uint8Array(buffer);
|
|
958
2614
|
}
|
|
959
|
-
function
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
2615
|
+
function unpackTar(bytes) {
|
|
2616
|
+
const entries = [];
|
|
2617
|
+
let offset = 0;
|
|
2618
|
+
let longName = null;
|
|
2619
|
+
while (offset + BLOCK <= bytes.length) {
|
|
2620
|
+
const header = bytes.subarray(offset, offset + BLOCK);
|
|
2621
|
+
if (header.every((byte) => byte === 0)) break;
|
|
2622
|
+
const typeflag = String.fromCharCode(header[156]);
|
|
2623
|
+
const size = parseOctalSize(header, 124, 12);
|
|
2624
|
+
let name = readString(bytes, offset, 100);
|
|
2625
|
+
const prefix = readString(bytes, offset + 345, 155);
|
|
2626
|
+
if (prefix !== "") name = `${prefix}/${name}`;
|
|
2627
|
+
const dataStart = offset + BLOCK;
|
|
2628
|
+
const data = bytes.subarray(dataStart, dataStart + size);
|
|
2629
|
+
offset = dataStart + Math.ceil(size / BLOCK) * BLOCK;
|
|
2630
|
+
if (typeflag === "L") {
|
|
2631
|
+
longName = new TextDecoder("utf-8").decode(data).replace(/\0+$/g, "").trim();
|
|
2632
|
+
continue;
|
|
968
2633
|
}
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
2634
|
+
if (typeflag === "x" || typeflag === "g") continue;
|
|
2635
|
+
if (typeflag === "5") continue;
|
|
2636
|
+
if (typeflag !== "0" && typeflag !== "\0") {
|
|
2637
|
+
throw new Error(`tar \u6761\u76EE\u7C7B\u578B "${typeflag}" \u4E0D\u652F\u6301\uFF08\u53EA\u63A5\u53D7\u666E\u901A\u6587\u4EF6\u4E0E\u76EE\u5F55\uFF09\uFF1A"${name}"`);
|
|
2638
|
+
}
|
|
2639
|
+
if (longName !== null) {
|
|
2640
|
+
name = longName;
|
|
2641
|
+
longName = null;
|
|
2642
|
+
}
|
|
2643
|
+
assertSafePath(name);
|
|
2644
|
+
entries.push({ path: name, contents: new Uint8Array(data) });
|
|
979
2645
|
}
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
if (hit !== null) return hit;
|
|
991
|
-
} else {
|
|
992
|
-
const hit = asFile(fs, join(pkgDir, "index")) ?? asDirectory(fs, pkgDir);
|
|
993
|
-
if (hit !== null) return hit;
|
|
994
|
-
}
|
|
995
|
-
} else {
|
|
996
|
-
const base = join(pkgDir, subpath);
|
|
997
|
-
const hit = asFile(fs, base) ?? asDirectory(fs, base);
|
|
998
|
-
if (hit !== null) return hit;
|
|
999
|
-
}
|
|
1000
|
-
throw new ResolveError(
|
|
1001
|
-
`\u5728 ${pkgDir} \u627E\u5230\u4E86\u5305 "${name}"\uFF0C\u4F46\u89E3\u6790\u8BF4\u660E\u7B26 "${specifier}" \u5931\u8D25\uFF08\u5B50\u8DEF\u5F84\u4E0D\u5B58\u5728\uFF0C\u6216 exports/main \u6307\u5411\u7684\u5165\u53E3\u6587\u4EF6\u7F3A\u5931\uFF09`
|
|
2646
|
+
return entries;
|
|
2647
|
+
}
|
|
2648
|
+
async function unpackNpmTarball(bytes) {
|
|
2649
|
+
const tar = unpackTar(await gunzip(bytes));
|
|
2650
|
+
if (tar.length === 0) throw new Error("tarball \u89E3\u5305\u540E\u4E3A\u7A7A\uFF0C\u6CA1\u6709\u4EFB\u4F55\u6587\u4EF6\u6761\u76EE");
|
|
2651
|
+
const out = [];
|
|
2652
|
+
for (const entry of tar) {
|
|
2653
|
+
if (!entry.path.startsWith("package/")) {
|
|
2654
|
+
throw new Error(
|
|
2655
|
+
`tarball \u9876\u5C42\u76EE\u5F55\u5F02\u5E38\uFF08npm registry \u5F52\u6863\u5E94\u5168\u90E8\u4F4D\u4E8E "package/" \u4E0B\uFF09\uFF1A"${entry.path}"`
|
|
1002
2656
|
);
|
|
1003
2657
|
}
|
|
1004
|
-
|
|
1005
|
-
if (parent === dir) break;
|
|
1006
|
-
dir = parent;
|
|
2658
|
+
out.push({ path: entry.path.slice("package/".length), contents: entry.contents });
|
|
1007
2659
|
}
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
2660
|
+
if (!out.some((entry) => entry.path === "package.json")) {
|
|
2661
|
+
throw new Error("tarball \u7F3A\u5C11 package.json\uFF0C\u62D2\u7EDD\u89E3\u5305");
|
|
2662
|
+
}
|
|
2663
|
+
return out;
|
|
1011
2664
|
}
|
|
1012
2665
|
|
|
1013
|
-
//
|
|
2666
|
+
// src/npm-client.ts
|
|
2667
|
+
init_node_resolve();
|
|
1014
2668
|
var DEFAULT_MAX_INSTALL_DEPTH = 5;
|
|
1015
2669
|
var defaultFetch = (url, init) => globalThis.fetch(url, init).then((res) => res);
|
|
1016
2670
|
function parseSpec(spec) {
|
|
@@ -1161,7 +2815,58 @@ function createNpmClient(options) {
|
|
|
1161
2815
|
};
|
|
1162
2816
|
}
|
|
1163
2817
|
|
|
1164
|
-
//
|
|
2818
|
+
// src/relay-fetch.ts
|
|
2819
|
+
var defaultFetch2 = (url, init) => globalThis.fetch(url, init).then((res) => res);
|
|
2820
|
+
function createRelayFetch(options) {
|
|
2821
|
+
const fetchImpl = options.fetchImpl ?? defaultFetch2;
|
|
2822
|
+
const base = options.baseUrl.replace(/\/+$/, "");
|
|
2823
|
+
return async (url, init) => {
|
|
2824
|
+
let res;
|
|
2825
|
+
try {
|
|
2826
|
+
res = await fetchImpl(`${base}/fetch`, {
|
|
2827
|
+
method: "POST",
|
|
2828
|
+
headers: { "content-type": "application/json" },
|
|
2829
|
+
body: JSON.stringify({
|
|
2830
|
+
url,
|
|
2831
|
+
method: init?.method ?? "GET",
|
|
2832
|
+
...init?.headers === void 0 ? {} : { headers: init.headers },
|
|
2833
|
+
...init?.body === void 0 ? {} : { body: init.body }
|
|
2834
|
+
})
|
|
2835
|
+
});
|
|
2836
|
+
} catch (error) {
|
|
2837
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
2838
|
+
throw new Error(`HTTP \u4E2D\u7EE7\u4E0D\u53EF\u8FBE\uFF08${base}/fetch\uFF0C${reason}\uFF09`, { cause: error });
|
|
2839
|
+
}
|
|
2840
|
+
if (!res.ok) {
|
|
2841
|
+
let message = `HTTP \u4E2D\u7EE7\u8FD4\u56DE ${res.status}`;
|
|
2842
|
+
try {
|
|
2843
|
+
const data = await res.json();
|
|
2844
|
+
if (data.error?.message) message = data.error.message;
|
|
2845
|
+
} catch {
|
|
2846
|
+
}
|
|
2847
|
+
throw new Error(message);
|
|
2848
|
+
}
|
|
2849
|
+
const env = await res.json();
|
|
2850
|
+
return {
|
|
2851
|
+
ok: env.ok,
|
|
2852
|
+
status: env.status,
|
|
2853
|
+
statusText: env.statusText,
|
|
2854
|
+
headers: env.headers ?? {},
|
|
2855
|
+
text: async () => env.body,
|
|
2856
|
+
json: async () => JSON.parse(env.body),
|
|
2857
|
+
arrayBuffer: async () => {
|
|
2858
|
+
const bytes = new TextEncoder().encode(env.body);
|
|
2859
|
+
return bytes.buffer.slice(
|
|
2860
|
+
bytes.byteOffset,
|
|
2861
|
+
bytes.byteOffset + bytes.byteLength
|
|
2862
|
+
);
|
|
2863
|
+
}
|
|
2864
|
+
};
|
|
2865
|
+
};
|
|
2866
|
+
}
|
|
2867
|
+
|
|
2868
|
+
// src/module-loader.ts
|
|
2869
|
+
init_node_resolve();
|
|
1165
2870
|
function interopDefault(value) {
|
|
1166
2871
|
if (value !== null && typeof value === "object" && "__esModule" in value) {
|
|
1167
2872
|
return value.default;
|
|
@@ -1269,7 +2974,7 @@ function composeRequire(vfs, entryFileAbs, cache) {
|
|
|
1269
2974
|
};
|
|
1270
2975
|
}
|
|
1271
2976
|
|
|
1272
|
-
//
|
|
2977
|
+
// src/worker-runtime.ts
|
|
1273
2978
|
async function evaluateSource(source, filename, args, extraGlobals) {
|
|
1274
2979
|
const output = [];
|
|
1275
2980
|
const fakeConsole = {
|
|
@@ -1367,616 +3072,10 @@ function createWorkerRuntime(vfs, options = {}) {
|
|
|
1367
3072
|
};
|
|
1368
3073
|
}
|
|
1369
3074
|
|
|
1370
|
-
//
|
|
1371
|
-
|
|
1372
|
-
constructor(construct, line) {
|
|
1373
|
-
super(`\u4E0D\u53D7\u652F\u6301\u7684 TypeScript \u8BED\u6CD5\uFF1A${construct}\uFF08\u7B2C ${line} \u884C\uFF09\u2014\u2014\u6D4F\u89C8\u5668\u6D4B\u8BD5\u6C99\u7BB1\u53EA\u64E6\u9664\u7C7B\u578B\u6807\u6CE8`);
|
|
1374
|
-
this.construct = construct;
|
|
1375
|
-
this.line = line;
|
|
1376
|
-
this.name = "UnsupportedTsSyntaxError";
|
|
1377
|
-
}
|
|
1378
|
-
};
|
|
1379
|
-
var IDENT_START = /[A-Za-z_$]/;
|
|
1380
|
-
var IDENT_PART = /[A-Za-z0-9_$]/;
|
|
1381
|
-
var DECL_KEYWORDS = /* @__PURE__ */ new Set(["const", "let", "var"]);
|
|
1382
|
-
var TS_MODIFIERS = /* @__PURE__ */ new Set([
|
|
1383
|
-
"private",
|
|
1384
|
-
"protected",
|
|
1385
|
-
"public",
|
|
1386
|
-
"readonly",
|
|
1387
|
-
"abstract",
|
|
1388
|
-
"override",
|
|
1389
|
-
"declare"
|
|
1390
|
-
]);
|
|
1391
|
-
var ANNOTATION_LEADERS = /* @__PURE__ */ new Set(["(", ",", "{", ";", "erased", "modifier"]);
|
|
1392
|
-
function isAnnotationLeader(token) {
|
|
1393
|
-
return ANNOTATION_LEADERS.has(token) || TS_MODIFIERS.has(token);
|
|
1394
|
-
}
|
|
1395
|
-
var Eraser = class {
|
|
1396
|
-
constructor(src) {
|
|
1397
|
-
this.src = src;
|
|
1398
|
-
this.chars = [...src];
|
|
1399
|
-
}
|
|
1400
|
-
chars;
|
|
1401
|
-
tokens = [];
|
|
1402
|
-
frames = [];
|
|
1403
|
-
/** 刚消费 `class` 头:下一个 `{` 是类体起始。 */
|
|
1404
|
-
pendingClassBody = false;
|
|
1405
|
-
/** 刚消费 `=>`:其后的 `(` 是箭头函数参数列表(声明位)。 */
|
|
1406
|
-
afterArrow = false;
|
|
1407
|
-
/** 刚消费 `function` 关键字:其后的标识符是函数名,再后是参数列表。 */
|
|
1408
|
-
afterFunctionKeyword = false;
|
|
1409
|
-
/** 最近一次 `)` 闭合的是不是参数列表(返回值标注判据)。 */
|
|
1410
|
-
justClosedDeclareParen = false;
|
|
1411
|
-
i = 0;
|
|
1412
|
-
run() {
|
|
1413
|
-
while (this.i < this.src.length) {
|
|
1414
|
-
if (/\s/.test(this.ch())) {
|
|
1415
|
-
this.i++;
|
|
1416
|
-
continue;
|
|
1417
|
-
}
|
|
1418
|
-
if (this.copyTrivia()) continue;
|
|
1419
|
-
if (this.copyLiteral()) continue;
|
|
1420
|
-
if (this.copyRegexIfNeeded()) continue;
|
|
1421
|
-
if (this.readNumber()) continue;
|
|
1422
|
-
const word = this.readWord();
|
|
1423
|
-
if (word !== null) this.handleWord(word);
|
|
1424
|
-
else this.handlePunctuation();
|
|
1425
|
-
}
|
|
1426
|
-
return this.chars.join("");
|
|
1427
|
-
}
|
|
1428
|
-
/* ———————————————————— 游标原语 ———————————————————— */
|
|
1429
|
-
ch(offset = 0) {
|
|
1430
|
-
return this.src[this.i + offset] ?? "";
|
|
1431
|
-
}
|
|
1432
|
-
/** 回看第 `back` 个显著 token(1 = 最近一个)。 */
|
|
1433
|
-
tokenAt(back) {
|
|
1434
|
-
return this.tokens[this.tokens.length - back] ?? "";
|
|
1435
|
-
}
|
|
1436
|
-
/** 擦除结果里 `from` 往前的最后一个非空白字符(看的是已擦除文本,不是原文)。 */
|
|
1437
|
-
prevSignificantChar(from) {
|
|
1438
|
-
for (let j = from; j >= 0; j--) {
|
|
1439
|
-
const c = this.chars[j];
|
|
1440
|
-
if (c !== " " && c !== " " && c !== "\n" && c !== "\r") return c;
|
|
1441
|
-
}
|
|
1442
|
-
return "";
|
|
1443
|
-
}
|
|
1444
|
-
/** 擦除 `[from, to)`:填等长空格,换行照抄——擦完行号与原文件一致。 */
|
|
1445
|
-
erase(from, to) {
|
|
1446
|
-
for (let j = Math.max(from, 0); j < to; j++) this.chars[j] = this.src[j] === "\n" ? "\n" : " ";
|
|
1447
|
-
}
|
|
1448
|
-
pushToken(token) {
|
|
1449
|
-
this.afterArrow = token === "=>";
|
|
1450
|
-
this.afterFunctionKeyword = token === "function";
|
|
1451
|
-
this.tokens.push(token);
|
|
1452
|
-
}
|
|
1453
|
-
/** 数字面量整段消费(否则 `1.5` 会被拆成 `1` `.` `5`,`.` 参与判据时误判)。 */
|
|
1454
|
-
readNumber() {
|
|
1455
|
-
if (!/[0-9]/.test(this.ch())) return false;
|
|
1456
|
-
let end = this.i;
|
|
1457
|
-
while (end < this.src.length && /[0-9a-fA-FxXoObB._]/.test(this.src[end])) end++;
|
|
1458
|
-
this.i = end;
|
|
1459
|
-
this.pushToken("number");
|
|
1460
|
-
return true;
|
|
1461
|
-
}
|
|
1462
|
-
readWord() {
|
|
1463
|
-
if (!IDENT_START.test(this.ch())) return null;
|
|
1464
|
-
let end = this.i;
|
|
1465
|
-
while (end < this.src.length && IDENT_PART.test(this.src[end])) end++;
|
|
1466
|
-
const word = this.src.slice(this.i, end);
|
|
1467
|
-
this.i = end;
|
|
1468
|
-
this.pushToken(word);
|
|
1469
|
-
return word;
|
|
1470
|
-
}
|
|
1471
|
-
/* —————————————— 字面量与注释:整段跳过(内容永不参与判定) —————————————— */
|
|
1472
|
-
copyTrivia() {
|
|
1473
|
-
const head = this.ch();
|
|
1474
|
-
const next = this.ch(1);
|
|
1475
|
-
if (head === "/" && next === "/") {
|
|
1476
|
-
while (this.i < this.src.length && this.ch() !== "\n") this.i++;
|
|
1477
|
-
return true;
|
|
1478
|
-
}
|
|
1479
|
-
if (head === "/" && next === "*") {
|
|
1480
|
-
const end = this.src.indexOf("*/", this.i + 2);
|
|
1481
|
-
this.i = end === -1 ? this.src.length : end + 2;
|
|
1482
|
-
return true;
|
|
1483
|
-
}
|
|
1484
|
-
return false;
|
|
1485
|
-
}
|
|
1486
|
-
copyLiteral() {
|
|
1487
|
-
const head = this.ch();
|
|
1488
|
-
if (head !== "'" && head !== '"' && head !== "`") return false;
|
|
1489
|
-
this.i = head === "`" ? this.scanTemplate(this.i) : this.scanQuoted(this.i);
|
|
1490
|
-
this.pushToken(head === "`" ? "template" : "string");
|
|
1491
|
-
return true;
|
|
1492
|
-
}
|
|
1493
|
-
/** 引号串结束下标(未闭合则到行尾,把真实的语法错误留给求值期)。 */
|
|
1494
|
-
scanQuoted(start) {
|
|
1495
|
-
const quote = this.src[start];
|
|
1496
|
-
let j = start + 1;
|
|
1497
|
-
while (j < this.src.length) {
|
|
1498
|
-
const c = this.src[j];
|
|
1499
|
-
if (c === "\\") j += 2;
|
|
1500
|
-
else if (c === quote) return j + 1;
|
|
1501
|
-
else if (c === "\n") return j;
|
|
1502
|
-
else j++;
|
|
1503
|
-
}
|
|
1504
|
-
return this.src.length;
|
|
1505
|
-
}
|
|
1506
|
-
/** 模板串结束下标(`${}` 内按大括号配平,内层字符串 / 模板一并识别)。 */
|
|
1507
|
-
scanTemplate(start) {
|
|
1508
|
-
let j = start + 1;
|
|
1509
|
-
while (j < this.src.length) {
|
|
1510
|
-
const c = this.src[j];
|
|
1511
|
-
if (c === "\\") j += 2;
|
|
1512
|
-
else if (c === "`") return j + 1;
|
|
1513
|
-
else if (c === "$" && this.src[j + 1] === "{") j = this.scanInterpolation(j + 2);
|
|
1514
|
-
else j++;
|
|
1515
|
-
}
|
|
1516
|
-
return this.src.length;
|
|
1517
|
-
}
|
|
1518
|
-
scanInterpolation(start) {
|
|
1519
|
-
let depth = 1;
|
|
1520
|
-
let j = start;
|
|
1521
|
-
while (j < this.src.length) {
|
|
1522
|
-
const c = this.src[j];
|
|
1523
|
-
if (c === "\\") j += 2;
|
|
1524
|
-
else if (c === "'" || c === '"') j = this.scanQuoted(j);
|
|
1525
|
-
else if (c === "`") j = this.scanTemplate(j);
|
|
1526
|
-
else if (c === "{") {
|
|
1527
|
-
depth++;
|
|
1528
|
-
j++;
|
|
1529
|
-
} else if (c === "}") {
|
|
1530
|
-
depth--;
|
|
1531
|
-
j++;
|
|
1532
|
-
if (depth === 0) return j;
|
|
1533
|
-
} else j++;
|
|
1534
|
-
}
|
|
1535
|
-
return this.src.length;
|
|
1536
|
-
}
|
|
1537
|
-
/**
|
|
1538
|
-
* 正则字面量:`/` 只在「运算符位」才是正则起始(前一个显著字符不是值结尾)。
|
|
1539
|
-
* 判不准时按除号处理——除号后面不会有需要擦除的类型标注,最坏只是漏擦。
|
|
1540
|
-
* 有了这条,`const url = /https:\/\//` 里的 `:` 与 `//` 才不会被人当语法(恒等用例之一)。
|
|
1541
|
-
*/
|
|
1542
|
-
copyRegexIfNeeded() {
|
|
1543
|
-
if (this.ch() !== "/") return false;
|
|
1544
|
-
const prev = this.prevSignificantChar(this.i - 1);
|
|
1545
|
-
const valueEnding = prev !== "" && (IDENT_PART.test(prev) || prev === ")" || prev === "]" || prev === "}" || prev === '"' || prev === "'" || prev === "`");
|
|
1546
|
-
if (valueEnding) return false;
|
|
1547
|
-
const end = this.scanRegex(this.i);
|
|
1548
|
-
if (end === null) return false;
|
|
1549
|
-
this.i = end;
|
|
1550
|
-
this.pushToken("regex");
|
|
1551
|
-
return true;
|
|
1552
|
-
}
|
|
1553
|
-
scanRegex(start) {
|
|
1554
|
-
let j = start + 1;
|
|
1555
|
-
let inClass = false;
|
|
1556
|
-
while (j < this.src.length) {
|
|
1557
|
-
const c = this.src[j];
|
|
1558
|
-
if (c === "\\") {
|
|
1559
|
-
j += 2;
|
|
1560
|
-
continue;
|
|
1561
|
-
}
|
|
1562
|
-
if (c === "\n") return null;
|
|
1563
|
-
if (c === "[") inClass = true;
|
|
1564
|
-
else if (c === "]") inClass = false;
|
|
1565
|
-
else if (c === "/" && !inClass) {
|
|
1566
|
-
j++;
|
|
1567
|
-
while (j < this.src.length && /[a-z]/i.test(this.src[j])) j++;
|
|
1568
|
-
return j;
|
|
1569
|
-
}
|
|
1570
|
-
j++;
|
|
1571
|
-
}
|
|
1572
|
-
return null;
|
|
1573
|
-
}
|
|
1574
|
-
/* ———————————————— 关键字与声明结构 ———————————————— */
|
|
1575
|
-
handleWord(word) {
|
|
1576
|
-
const start = this.i - word.length;
|
|
1577
|
-
if (this.tokenAt(2) === ".") {
|
|
1578
|
-
this.consumeGenericsAfterName();
|
|
1579
|
-
return;
|
|
1580
|
-
}
|
|
1581
|
-
switch (word) {
|
|
1582
|
-
case "interface": {
|
|
1583
|
-
const after = this.skipWhitespaceFrom(this.skipNameAndParams(start + word.length));
|
|
1584
|
-
if (after === null || this.src[after] !== "{") return;
|
|
1585
|
-
const end = this.scanBalanced(after, "{", "}");
|
|
1586
|
-
this.erase(start, end);
|
|
1587
|
-
this.i = end;
|
|
1588
|
-
this.pushToken("erased");
|
|
1589
|
-
return;
|
|
1590
|
-
}
|
|
1591
|
-
case "type": {
|
|
1592
|
-
const after = this.skipWhitespaceFrom(this.skipNameAndParams(this.i));
|
|
1593
|
-
if (after === null || this.src[after] !== "=") return;
|
|
1594
|
-
const end = this.scanTypeExpression(after + 1, true);
|
|
1595
|
-
this.erase(start, end);
|
|
1596
|
-
this.i = end;
|
|
1597
|
-
this.pushToken("erased");
|
|
1598
|
-
return;
|
|
1599
|
-
}
|
|
1600
|
-
case "import":
|
|
1601
|
-
case "export": {
|
|
1602
|
-
if (!/^\s+type\b/.test(this.src.slice(this.i))) return;
|
|
1603
|
-
const after = this.skipNameAndParams(this.i);
|
|
1604
|
-
if (after === null) return;
|
|
1605
|
-
const end = this.scanStatementEnd(after);
|
|
1606
|
-
this.erase(start, end);
|
|
1607
|
-
this.i = end;
|
|
1608
|
-
this.pushToken("erased");
|
|
1609
|
-
return;
|
|
1610
|
-
}
|
|
1611
|
-
case "enum":
|
|
1612
|
-
case "namespace":
|
|
1613
|
-
throw new UnsupportedTsSyntaxError(word, this.lineAt(start));
|
|
1614
|
-
case "implements": {
|
|
1615
|
-
let j = this.i;
|
|
1616
|
-
while (j < this.src.length && this.src[j] !== "{" && this.src[j] !== ";") j++;
|
|
1617
|
-
this.erase(start, j);
|
|
1618
|
-
this.i = j;
|
|
1619
|
-
this.pushToken("erased");
|
|
1620
|
-
return;
|
|
1621
|
-
}
|
|
1622
|
-
case "class":
|
|
1623
|
-
this.pendingClassBody = true;
|
|
1624
|
-
return;
|
|
1625
|
-
case "as":
|
|
1626
|
-
case "satisfies": {
|
|
1627
|
-
if (this.tokenAt(2) === "*" || this.tokenAt(2) === "type") return;
|
|
1628
|
-
const nextChar = this.skipWhitespaceFrom(this.i);
|
|
1629
|
-
if (nextChar === null || "=,:]})".includes(this.src[nextChar])) return;
|
|
1630
|
-
const end = this.scanTypeExpression(this.i, false);
|
|
1631
|
-
this.erase(start, end);
|
|
1632
|
-
this.i = end;
|
|
1633
|
-
return;
|
|
1634
|
-
}
|
|
1635
|
-
default: {
|
|
1636
|
-
if (TS_MODIFIERS.has(word) && this.inClassBody()) {
|
|
1637
|
-
const next = this.peekWord();
|
|
1638
|
-
if (next === "" || /[{(=]/.test(next[0])) return;
|
|
1639
|
-
this.erase(start, this.i);
|
|
1640
|
-
this.pushToken("modifier");
|
|
1641
|
-
return;
|
|
1642
|
-
}
|
|
1643
|
-
this.consumeGenericsAfterName();
|
|
1644
|
-
}
|
|
1645
|
-
}
|
|
1646
|
-
}
|
|
1647
|
-
peekWord() {
|
|
1648
|
-
let j = this.i;
|
|
1649
|
-
while (j < this.src.length && /\s/.test(this.src[j])) j++;
|
|
1650
|
-
let end = j;
|
|
1651
|
-
while (end < this.src.length && IDENT_PART.test(this.src[end])) end++;
|
|
1652
|
-
return this.src.slice(j, end);
|
|
1653
|
-
}
|
|
1654
|
-
lineAt(index) {
|
|
1655
|
-
let line = 1;
|
|
1656
|
-
for (let j = 0; j < index; j++) if (this.src[j] === "\n") line++;
|
|
1657
|
-
return line;
|
|
1658
|
-
}
|
|
1659
|
-
/** `:` 之前紧邻的是不是 `)`(返回值标注位;用于决定是否允许顶层 `=>` 续读)。 */
|
|
1660
|
-
lastSignificantCharIsCloseParen(at) {
|
|
1661
|
-
return this.prevSignificantChar(at - 1) === ")";
|
|
1662
|
-
}
|
|
1663
|
-
inClassBody() {
|
|
1664
|
-
return this.frames[this.frames.length - 1]?.classBody === true;
|
|
1665
|
-
}
|
|
1666
|
-
/** 当前帧是不是声明位(参数列表 / 类体)——只有这里才允许把 `:` 判成标注。 */
|
|
1667
|
-
inDeclarePosition() {
|
|
1668
|
-
const frame = this.frames[this.frames.length - 1];
|
|
1669
|
-
return frame !== void 0 && (frame.declare || frame.classBody);
|
|
1670
|
-
}
|
|
1671
|
-
/* ———————————————— 标点与括号帧 ———————————————— */
|
|
1672
|
-
handlePunctuation() {
|
|
1673
|
-
const c = this.ch();
|
|
1674
|
-
const start = this.i;
|
|
1675
|
-
if (c === "(" || c === "{" || c === "[") {
|
|
1676
|
-
const frame = {
|
|
1677
|
-
kind: c === "(" ? "paren" : c === "{" ? "brace" : "bracket",
|
|
1678
|
-
declare: false,
|
|
1679
|
-
classBody: false
|
|
1680
|
-
};
|
|
1681
|
-
if (c === "{" && this.pendingClassBody) {
|
|
1682
|
-
frame.classBody = true;
|
|
1683
|
-
this.pendingClassBody = false;
|
|
1684
|
-
}
|
|
1685
|
-
if (c === "(" && this.isDeclareParen(start)) frame.declare = true;
|
|
1686
|
-
this.frames.push(frame);
|
|
1687
|
-
this.i++;
|
|
1688
|
-
this.pushToken(c);
|
|
1689
|
-
return;
|
|
1690
|
-
}
|
|
1691
|
-
if (c === ")" || c === "}" || c === "]") {
|
|
1692
|
-
const closed = this.frames.pop();
|
|
1693
|
-
if (closed?.kind === "paren") this.justClosedDeclareParen = closed.declare;
|
|
1694
|
-
this.i++;
|
|
1695
|
-
this.pushToken(c);
|
|
1696
|
-
return;
|
|
1697
|
-
}
|
|
1698
|
-
if (c === ":") {
|
|
1699
|
-
if (this.ch(1) === ":") {
|
|
1700
|
-
this.i++;
|
|
1701
|
-
this.pushToken(":");
|
|
1702
|
-
return;
|
|
1703
|
-
}
|
|
1704
|
-
const returnType = this.isAnnotationColon(start) && this.lastSignificantCharIsCloseParen(start);
|
|
1705
|
-
if (returnType || this.isAnnotationColon(start)) {
|
|
1706
|
-
const end = this.scanTypeExpression(start + 1, false, returnType);
|
|
1707
|
-
this.erase(start, end);
|
|
1708
|
-
this.i = end;
|
|
1709
|
-
this.pushToken("erased");
|
|
1710
|
-
return;
|
|
1711
|
-
}
|
|
1712
|
-
this.i++;
|
|
1713
|
-
this.pushToken(":");
|
|
1714
|
-
return;
|
|
1715
|
-
}
|
|
1716
|
-
if (c === "?" && this.ch(1) !== "?" && this.ch(1) !== ".") {
|
|
1717
|
-
let j = start + 1;
|
|
1718
|
-
while (j < this.src.length && (this.src[j] === " " || this.src[j] === " ")) j++;
|
|
1719
|
-
if (this.src[j] === ":" && this.inDeclarePosition() && IDENT_PART.test(this.prevSignificantChar(start - 1))) {
|
|
1720
|
-
const end = this.scanTypeExpression(j + 1, false);
|
|
1721
|
-
this.erase(start, end);
|
|
1722
|
-
this.i = end;
|
|
1723
|
-
this.pushToken("erased");
|
|
1724
|
-
return;
|
|
1725
|
-
}
|
|
1726
|
-
this.i++;
|
|
1727
|
-
this.pushToken("?");
|
|
1728
|
-
return;
|
|
1729
|
-
}
|
|
1730
|
-
if (c === "!" && this.ch(1) !== "=" && this.isPostfixBang(start)) {
|
|
1731
|
-
this.erase(start, start + 1);
|
|
1732
|
-
this.i++;
|
|
1733
|
-
return;
|
|
1734
|
-
}
|
|
1735
|
-
if (c === "=" && this.ch(1) === ">") {
|
|
1736
|
-
this.i += 2;
|
|
1737
|
-
this.pushToken("=>");
|
|
1738
|
-
return;
|
|
1739
|
-
}
|
|
1740
|
-
if (c === "<" && this.consumeLeadingGenerics(start)) return;
|
|
1741
|
-
this.i++;
|
|
1742
|
-
this.pushToken(c);
|
|
1743
|
-
}
|
|
1744
|
-
/** 空白跳跃(供声明结构找下一个非空白字符;不改游标,纯函数)。 */
|
|
1745
|
-
skipWhitespaceFrom(index) {
|
|
1746
|
-
if (index === null) return null;
|
|
1747
|
-
let j = index;
|
|
1748
|
-
while (j < this.src.length && /\s/.test(this.src[j])) j++;
|
|
1749
|
-
return j;
|
|
1750
|
-
}
|
|
1751
|
-
/**
|
|
1752
|
-
* 运算符位上的泛型参数列表(`const f = <T, >(x: T) => x`、`arr.map<T2>(...)` 之外的箭头形态):
|
|
1753
|
-
* 前一个显著字符不是值结尾(否则是 `a < b` 比较),且 `>` 之后紧跟 `(` 才擦。
|
|
1754
|
-
*/
|
|
1755
|
-
consumeLeadingGenerics(at) {
|
|
1756
|
-
if (!"([{,=;:>!&|?".includes(this.prevSignificantChar(at - 1))) return false;
|
|
1757
|
-
const end = this.scanTypeParams(at);
|
|
1758
|
-
if (end === null) return false;
|
|
1759
|
-
const after = this.skipWhitespaceFrom(end);
|
|
1760
|
-
if (after === null || this.src[after] !== "(") return false;
|
|
1761
|
-
this.erase(at, end);
|
|
1762
|
-
this.i = end;
|
|
1763
|
-
return true;
|
|
1764
|
-
}
|
|
1765
|
-
/**
|
|
1766
|
-
* `(` 是否为参数列表(声明位):`function f(`、类体里的方法 `m(`、箭头 `=> (`,
|
|
1767
|
-
* 以及出现在 `(` `,` `=` `>` `:` `[` `{` `}` `;` 之后(如 `map((v: T) => v)` 的内层括号)。
|
|
1768
|
-
* 调用位 `foo(` 与控制流 `if (` `for (` 都不算。
|
|
1769
|
-
* 误判成声明位也不足以擦掉什么:`:` 还要过 `isAnnotationColon` 的「名字前驱必须是
|
|
1770
|
-
* `(` `,` `{` `;` 修饰符」这一关,而三元表达式的中间段前面必然是 `?`。
|
|
1771
|
-
*/
|
|
1772
|
-
isDeclareParen(at) {
|
|
1773
|
-
if (this.afterFunctionKeyword) return true;
|
|
1774
|
-
if (this.tokenAt(2) === "function" && IDENT_START.test(this.tokenAt(1))) return true;
|
|
1775
|
-
if (this.afterArrow) return true;
|
|
1776
|
-
if (this.inClassBody() && IDENT_PART.test(this.prevSignificantChar(at - 1))) return true;
|
|
1777
|
-
const prev = this.prevSignificantChar(at - 1);
|
|
1778
|
-
return prev === "" || "(,=>:[{};".includes(prev);
|
|
1779
|
-
}
|
|
1780
|
-
/** `:` 是类型标注吗:看名字前驱 token 与所在帧(对象键、三元、`case` 一律排除)。 */
|
|
1781
|
-
isAnnotationColon(at) {
|
|
1782
|
-
const prev = this.prevSignificantChar(at - 1);
|
|
1783
|
-
if (prev === ")") return this.justClosedDeclareParen;
|
|
1784
|
-
if (prev === "}" || prev === "]") {
|
|
1785
|
-
const frame = this.frames[this.frames.length - 1];
|
|
1786
|
-
return frame?.kind === "paren" && frame.declare;
|
|
1787
|
-
}
|
|
1788
|
-
if (!IDENT_PART.test(prev)) return false;
|
|
1789
|
-
if (this.tokenAt(1) === "number" || this.tokenAt(1) === "string") return false;
|
|
1790
|
-
if (this.inDeclarePosition() && isAnnotationLeader(this.tokenAt(2))) return true;
|
|
1791
|
-
return DECL_KEYWORDS.has(this.tokenAt(2));
|
|
1792
|
-
}
|
|
1793
|
-
/** 非空断言 `x!`:前一个显著字符是值结尾,且后一个非空白字符不是操作数起始。 */
|
|
1794
|
-
isPostfixBang(at) {
|
|
1795
|
-
const prev = this.prevSignificantChar(at - 1);
|
|
1796
|
-
if (!(IDENT_PART.test(prev) || prev === ")" || prev === "]")) return false;
|
|
1797
|
-
let j = at + 1;
|
|
1798
|
-
while (j < this.src.length && (this.src[j] === " " || this.src[j] === " ")) j++;
|
|
1799
|
-
const next = this.src[j] ?? "";
|
|
1800
|
-
if (next === "") return true;
|
|
1801
|
-
return !(IDENT_START.test(next) || /[0-9]/.test(next) || "(![~".includes(next));
|
|
1802
|
-
}
|
|
1803
|
-
/* ———————————————— 区间扫描(全部字符串 / 注释安全) ———————————————— */
|
|
1804
|
-
/** 跳过「名字 + 泛型参数」;没有合法名字时返回 null(`{ type: 1 }` 因此落空)。 */
|
|
1805
|
-
skipNameAndParams(from) {
|
|
1806
|
-
let j = from;
|
|
1807
|
-
while (j < this.src.length && /\s/.test(this.src[j])) j++;
|
|
1808
|
-
const nameStart = j;
|
|
1809
|
-
while (j < this.src.length && IDENT_PART.test(this.src[j])) j++;
|
|
1810
|
-
if (j === nameStart) return null;
|
|
1811
|
-
const generic = this.scanTypeParams(j);
|
|
1812
|
-
return generic === null ? j : generic;
|
|
1813
|
-
}
|
|
1814
|
-
/** `<T, U extends X>`:返回 `>` 之后的下标;跨到 `)` `]` `;` 换行前配不平则 null。 */
|
|
1815
|
-
scanTypeParams(from) {
|
|
1816
|
-
if (this.src[from] !== "<") return null;
|
|
1817
|
-
let depth = 0;
|
|
1818
|
-
let j = from;
|
|
1819
|
-
for (; j < this.src.length; j++) {
|
|
1820
|
-
const c = this.src[j];
|
|
1821
|
-
if (c === "<") depth++;
|
|
1822
|
-
else if (c === ">") {
|
|
1823
|
-
depth--;
|
|
1824
|
-
if (depth === 0) return j + 1;
|
|
1825
|
-
} else if (c === ")" || c === "]" || c === ";" || c === "\n") return null;
|
|
1826
|
-
else if (c === "'" || c === '"') j = this.scanQuoted(j) - 1;
|
|
1827
|
-
else if (c === "`") j = this.scanTemplate(j) - 1;
|
|
1828
|
-
}
|
|
1829
|
-
return null;
|
|
1830
|
-
}
|
|
1831
|
-
/**
|
|
1832
|
-
* 消费「刚读出的标识符之后」的泛型实参 / 类型参数(`function f<T>(`、方法 `m<T>(`、`f<T>(x)`)。
|
|
1833
|
-
* 只有 `>` 之后紧跟 `(` / `{` / `implements` 才算——`if (a < b) {` 里 `>` 之后是 `)`,故安全。
|
|
1834
|
-
*/
|
|
1835
|
-
consumeGenericsAfterName() {
|
|
1836
|
-
if (this.ch() !== "<") return;
|
|
1837
|
-
const end = this.scanTypeParams(this.i);
|
|
1838
|
-
if (end === null) return;
|
|
1839
|
-
let j = end;
|
|
1840
|
-
while (j < this.src.length && (this.src[j] === " " || this.src[j] === " ")) j++;
|
|
1841
|
-
const after = this.src.slice(j);
|
|
1842
|
-
if (!/^[({]/.test(after) && !/^implements\b/.test(after)) return;
|
|
1843
|
-
this.erase(this.i, end);
|
|
1844
|
-
this.i = end;
|
|
1845
|
-
}
|
|
1846
|
-
/** 括号配平:返回闭括号之后的下标(未配平则到文件尾)。 */
|
|
1847
|
-
scanBalanced(from, open, close) {
|
|
1848
|
-
let depth = 0;
|
|
1849
|
-
let j = from;
|
|
1850
|
-
while (j < this.src.length) {
|
|
1851
|
-
const c = this.src[j];
|
|
1852
|
-
if (c === "'" || c === '"') j = this.scanQuoted(j);
|
|
1853
|
-
else if (c === "`") j = this.scanTemplate(j);
|
|
1854
|
-
else if (c === "/" && (this.src[j + 1] === "/" || this.src[j + 1] === "*"))
|
|
1855
|
-
j = this.skipComment(j);
|
|
1856
|
-
else if (c === open) {
|
|
1857
|
-
depth++;
|
|
1858
|
-
j++;
|
|
1859
|
-
} else if (c === close) {
|
|
1860
|
-
depth--;
|
|
1861
|
-
j++;
|
|
1862
|
-
if (depth === 0) return j;
|
|
1863
|
-
} else j++;
|
|
1864
|
-
}
|
|
1865
|
-
return this.src.length;
|
|
1866
|
-
}
|
|
1867
|
-
skipComment(from) {
|
|
1868
|
-
if (this.src[from + 1] === "/") {
|
|
1869
|
-
const end2 = this.src.indexOf("\n", from);
|
|
1870
|
-
return end2 === -1 ? this.src.length : end2;
|
|
1871
|
-
}
|
|
1872
|
-
const end = this.src.indexOf("*/", from + 2);
|
|
1873
|
-
return end === -1 ? this.src.length : end + 2;
|
|
1874
|
-
}
|
|
1875
|
-
/** 语句结束(`;` 或括号已配平时的行尾),用于 `import type …`。 */
|
|
1876
|
-
scanStatementEnd(from) {
|
|
1877
|
-
let depth = 0;
|
|
1878
|
-
let j = from;
|
|
1879
|
-
while (j < this.src.length) {
|
|
1880
|
-
const c = this.src[j];
|
|
1881
|
-
if (c === "'" || c === '"') j = this.scanQuoted(j);
|
|
1882
|
-
else if (c === "`") j = this.scanTemplate(j);
|
|
1883
|
-
else if (c === "{" || c === "(" || c === "[") {
|
|
1884
|
-
depth++;
|
|
1885
|
-
j++;
|
|
1886
|
-
} else if (c === "}" || c === ")" || c === "]") {
|
|
1887
|
-
depth--;
|
|
1888
|
-
j++;
|
|
1889
|
-
} else if (c === ";" || c === "\n" && depth <= 0) break;
|
|
1890
|
-
else j++;
|
|
1891
|
-
}
|
|
1892
|
-
return j;
|
|
1893
|
-
}
|
|
1894
|
-
/**
|
|
1895
|
-
* 类型表达式结束下标:消费标识符 / 字符串字面量类型 / 泛型实参 / 元组 / 对象类型 /
|
|
1896
|
-
* 函数类型(含 `=>` 返回值)/ 联合与交叉;在深度 0 的 `= , ; ) ]` 与「已结束的行」处停下。
|
|
1897
|
-
*
|
|
1898
|
-
* 两处歧义的处理:
|
|
1899
|
-
* - `function f(): { a: number } { … }`——还没消费过类型原子时遇到的 `{` 属于类型,
|
|
1900
|
-
* 之后再遇 `{` 才是函数体;`type X = A` 的初始值花括号整体算类型(`leadingBraceIsType`)。
|
|
1901
|
-
* - `const x: Foo = { … }`——在 `=` 处停,不会把右侧对象字面量吞掉。
|
|
1902
|
-
*/
|
|
1903
|
-
scanTypeExpression(from, leadingBraceIsType, topLevelArrowEnds = false) {
|
|
1904
|
-
let depth = 0;
|
|
1905
|
-
let j = from;
|
|
1906
|
-
let sawAtom = false;
|
|
1907
|
-
while (j < this.src.length) {
|
|
1908
|
-
const c = this.src[j];
|
|
1909
|
-
if (c === "'" || c === '"') {
|
|
1910
|
-
j = this.scanQuoted(j);
|
|
1911
|
-
sawAtom = true;
|
|
1912
|
-
} else if (c === "`") {
|
|
1913
|
-
j = this.scanTemplate(j);
|
|
1914
|
-
sawAtom = true;
|
|
1915
|
-
} else if (c === "/" && (this.src[j + 1] === "/" || this.src[j + 1] === "*")) {
|
|
1916
|
-
j = this.skipComment(j);
|
|
1917
|
-
} else if (c === "(" || c === "[") {
|
|
1918
|
-
depth++;
|
|
1919
|
-
sawAtom = true;
|
|
1920
|
-
j++;
|
|
1921
|
-
} else if (c === ")" || c === "]") {
|
|
1922
|
-
if (depth === 0) break;
|
|
1923
|
-
depth--;
|
|
1924
|
-
j++;
|
|
1925
|
-
} else if (c === "{") {
|
|
1926
|
-
if (depth === 0 && sawAtom && !leadingBraceIsType) break;
|
|
1927
|
-
j = this.scanBalanced(j, "{", "}");
|
|
1928
|
-
sawAtom = true;
|
|
1929
|
-
} else if (c === "}") {
|
|
1930
|
-
if (depth === 0) break;
|
|
1931
|
-
depth--;
|
|
1932
|
-
j++;
|
|
1933
|
-
} else if (depth === 0 && (c === "," || c === ";")) break;
|
|
1934
|
-
else if (depth === 0 && c === "=") {
|
|
1935
|
-
if (this.src[j + 1] === ">" && !topLevelArrowEnds) {
|
|
1936
|
-
j += 2;
|
|
1937
|
-
continue;
|
|
1938
|
-
}
|
|
1939
|
-
break;
|
|
1940
|
-
} else if (depth === 0 && c === "\n") {
|
|
1941
|
-
if (!this.typeContinuesAt(j)) break;
|
|
1942
|
-
j++;
|
|
1943
|
-
} else if (c === "<") {
|
|
1944
|
-
const generic = this.scanTypeParams(j);
|
|
1945
|
-
if (generic === null) break;
|
|
1946
|
-
j = generic;
|
|
1947
|
-
sawAtom = true;
|
|
1948
|
-
} else {
|
|
1949
|
-
if (IDENT_PART.test(c)) sawAtom = true;
|
|
1950
|
-
j++;
|
|
1951
|
-
}
|
|
1952
|
-
}
|
|
1953
|
-
return j;
|
|
1954
|
-
}
|
|
1955
|
-
/**
|
|
1956
|
-
* 行尾处类型是否仍在继续:`type Id = string\n | number` 要接得上,
|
|
1957
|
-
* 而 `const x: Foo\nconst y = 1` 必须停。判据是两侧的非空邻字——
|
|
1958
|
-
* 前一个是连接符(`| & < > = , . ? : (`)或后一个是连接符 / 闭合符(`| & , . ? : ) ] } >`)。
|
|
1959
|
-
*/
|
|
1960
|
-
typeContinuesAt(index) {
|
|
1961
|
-
for (let j = index - 1; j >= 0; j--) {
|
|
1962
|
-
const c = this.src[j];
|
|
1963
|
-
if (/\s/.test(c)) continue;
|
|
1964
|
-
if ("|&<>=,.?:(".includes(c)) return true;
|
|
1965
|
-
break;
|
|
1966
|
-
}
|
|
1967
|
-
for (let j = index + 1; j < this.src.length; j++) {
|
|
1968
|
-
const c = this.src[j];
|
|
1969
|
-
if (/\s/.test(c)) continue;
|
|
1970
|
-
return "|&,.?:)]}>".includes(c);
|
|
1971
|
-
}
|
|
1972
|
-
return false;
|
|
1973
|
-
}
|
|
1974
|
-
};
|
|
1975
|
-
function stripTypes(source) {
|
|
1976
|
-
return new Eraser(source).run();
|
|
1977
|
-
}
|
|
3075
|
+
// src/test-runner.ts
|
|
3076
|
+
init_strip_types();
|
|
1978
3077
|
|
|
1979
|
-
//
|
|
3078
|
+
// src/test-framework.ts
|
|
1980
3079
|
var TestAssertionError = class extends Error {
|
|
1981
3080
|
constructor(message, expected, received) {
|
|
1982
3081
|
super(message);
|
|
@@ -2239,7 +3338,7 @@ function stringify(value, seen) {
|
|
|
2239
3338
|
return `{ ${entries.join(", ")} }`;
|
|
2240
3339
|
}
|
|
2241
3340
|
|
|
2242
|
-
//
|
|
3341
|
+
// src/test-runner.ts
|
|
2243
3342
|
var TEST_FILE_PATTERN = /\.(?:test|spec)\.[cm]?[jt]s$/;
|
|
2244
3343
|
var IGNORED_SEGMENTS = /* @__PURE__ */ new Set(["node_modules", ".git", ".adep"]);
|
|
2245
3344
|
function createTestRunner(options) {
|
|
@@ -2408,7 +3507,18 @@ function exampleTestSource(name = "example") {
|
|
|
2408
3507
|
].join("\n");
|
|
2409
3508
|
}
|
|
2410
3509
|
|
|
2411
|
-
//
|
|
3510
|
+
// src/container.ts
|
|
3511
|
+
function viteBanner(server, fresh) {
|
|
3512
|
+
return [
|
|
3513
|
+
fresh ? ` VITE \u6D4F\u89C8\u5668\u5185 dev server \u5DF2\u5C31\u7EEA\uFF08root: ${server.root}\uFF09
|
|
3514
|
+
` : ` vite dev server \u5DF2\u5728\u8FD0\u884C\uFF08root: ${server.root}\uFF09
|
|
3515
|
+
`,
|
|
3516
|
+
"\n",
|
|
3517
|
+
" \u279C Local: http://localhost:5173/\n",
|
|
3518
|
+
" \u279C \u9884\u89C8\uFF1A\u7EC8\u7AEF / IDE \u7684\u9884\u89C8\u9762\u677F\u5DF2\u5C31\u7EEA\uFF0C\u7F16\u8F91\u4FDD\u5B58\u540E\u81EA\u52A8\u5237\u65B0\uFF08HMR\uFF09\n",
|
|
3519
|
+
" \u279C \u505C\u6B62\uFF1Avite stop\uFF08\u6362\u76EE\u5F55\uFF1Avite <dir>\uFF09\n"
|
|
3520
|
+
].join("");
|
|
3521
|
+
}
|
|
2412
3522
|
function bootstrap(options) {
|
|
2413
3523
|
const vfs = options.vfs ?? new VirtualFileSystem();
|
|
2414
3524
|
if (options.initialTree !== void 0) vfs.mount(options.initialTree);
|
|
@@ -2427,12 +3537,46 @@ function bootstrap(options) {
|
|
|
2427
3537
|
return { code: outcome.code, stdout: outcome.stdout, stderr: outcome.stderr };
|
|
2428
3538
|
}
|
|
2429
3539
|
};
|
|
3540
|
+
const net = options.net ?? createRelayFetch({
|
|
3541
|
+
baseUrl: options.httpRelayBaseUrl ?? "/api/v1/http-relay",
|
|
3542
|
+
...options.fetchImpl === void 0 ? {} : { fetchImpl: options.fetchImpl }
|
|
3543
|
+
});
|
|
3544
|
+
let viteServer = null;
|
|
3545
|
+
const vite = {
|
|
3546
|
+
async start(args) {
|
|
3547
|
+
const target = args[0] ?? "/";
|
|
3548
|
+
const root = target.startsWith("/") ? normalize(target) : join(cwd, target);
|
|
3549
|
+
if (viteServer !== null && viteServer.root === root)
|
|
3550
|
+
return { code: 0, stdout: viteBanner(viteServer, false) };
|
|
3551
|
+
await viteServer?.stop();
|
|
3552
|
+
viteServer = null;
|
|
3553
|
+
const { createViteServer: createViteServer2 } = await Promise.resolve().then(() => (init_vite_dev(), vite_dev_exports));
|
|
3554
|
+
const server = createViteServer2(vfs, {
|
|
3555
|
+
root,
|
|
3556
|
+
...options.viteCompiler === void 0 ? {} : { sfcCompiler: options.viteCompiler }
|
|
3557
|
+
});
|
|
3558
|
+
await server.ready;
|
|
3559
|
+
viteServer = server;
|
|
3560
|
+
emit("serverready", { port: server.port, url: server.url, kind: "vite", root: server.root });
|
|
3561
|
+
return { code: 0, stdout: viteBanner(server, true) };
|
|
3562
|
+
},
|
|
3563
|
+
async stop() {
|
|
3564
|
+
if (viteServer === null) return { code: 0, stdout: "vite: dev server \u672A\u5728\u8FD0\u884C\n" };
|
|
3565
|
+
const root = viteServer.root;
|
|
3566
|
+
await viteServer.stop();
|
|
3567
|
+
viteServer = null;
|
|
3568
|
+
return { code: 0, stdout: ` vite dev server \u5DF2\u505C\u6B62\uFF08root: ${root}\uFF09
|
|
3569
|
+
` };
|
|
3570
|
+
}
|
|
3571
|
+
};
|
|
2430
3572
|
const shell = new Shell({
|
|
2431
3573
|
vfs,
|
|
2432
3574
|
getCwd: () => cwd,
|
|
2433
3575
|
setCwd,
|
|
2434
3576
|
...runtime === void 0 ? {} : { runtime },
|
|
2435
|
-
test
|
|
3577
|
+
test,
|
|
3578
|
+
net,
|
|
3579
|
+
vite
|
|
2436
3580
|
});
|
|
2437
3581
|
const npmClient = createNpmClient({
|
|
2438
3582
|
baseUrl: options.npmRelayBaseUrl,
|
|
@@ -2552,9 +3696,9 @@ function bootstrap(options) {
|
|
|
2552
3696
|
},
|
|
2553
3697
|
async preview(previewOptions) {
|
|
2554
3698
|
if (preview === null) {
|
|
2555
|
-
const { createPreviewServer: createPreviewServer2 } = await Promise.resolve().then(() => (
|
|
3699
|
+
const { createPreviewServer: createPreviewServer2 } = await Promise.resolve().then(() => (init_preview_server(), preview_server_exports));
|
|
2556
3700
|
preview = createPreviewServer2(vfs, previewOptions);
|
|
2557
|
-
emit("serverready", { port: preview.port, url: preview.url });
|
|
3701
|
+
emit("serverready", { port: preview.port, url: preview.url, kind: "preview" });
|
|
2558
3702
|
}
|
|
2559
3703
|
return { url: preview.url, port: preview.port };
|
|
2560
3704
|
},
|
|
@@ -2563,14 +3707,18 @@ function bootstrap(options) {
|
|
|
2563
3707
|
defaultRuntime?.close?.();
|
|
2564
3708
|
await preview?.stop();
|
|
2565
3709
|
preview = null;
|
|
3710
|
+
await viteServer?.stop();
|
|
3711
|
+
viteServer = null;
|
|
2566
3712
|
}
|
|
2567
3713
|
};
|
|
2568
3714
|
}
|
|
2569
3715
|
|
|
2570
|
-
//
|
|
3716
|
+
// src/index.ts
|
|
3717
|
+
init_node_resolve();
|
|
3718
|
+
init_vite_dev();
|
|
2571
3719
|
init_path();
|
|
2572
3720
|
|
|
2573
|
-
//
|
|
3721
|
+
// src/persistence.ts
|
|
2574
3722
|
function cloneEntries(entries) {
|
|
2575
3723
|
return entries.map(
|
|
2576
3724
|
(entry) => entry.kind === "file" ? { path: entry.path, kind: "file", contents: entry.contents ?? "" } : { path: entry.path, kind: "dir" }
|
|
@@ -2783,7 +3931,10 @@ function createCommandHistory(options) {
|
|
|
2783
3931
|
};
|
|
2784
3932
|
}
|
|
2785
3933
|
|
|
2786
|
-
//
|
|
3934
|
+
// src/index.ts
|
|
3935
|
+
init_strip_types();
|
|
3936
|
+
|
|
3937
|
+
// src/offline/net.ts
|
|
2787
3938
|
var HEALTH_CHECK_INTERVAL_MS = 3e4;
|
|
2788
3939
|
var HEALTH_CHECK_TIMEOUT_MS = 5e3;
|
|
2789
3940
|
var HEALTH_ENDPOINT = "/api/v1/health";
|
|
@@ -2903,7 +4054,7 @@ function onNetworkChange(listener) {
|
|
|
2903
4054
|
return networkMonitor.subscribe(listener);
|
|
2904
4055
|
}
|
|
2905
4056
|
|
|
2906
|
-
//
|
|
4057
|
+
// src/offline/queue.ts
|
|
2907
4058
|
var STORAGE_KEY = "adep:offline:publish-queue";
|
|
2908
4059
|
function simpleHash(input) {
|
|
2909
4060
|
let hash = 0;
|
|
@@ -3044,9 +4195,8 @@ var PublishQueue = class {
|
|
|
3044
4195
|
};
|
|
3045
4196
|
var publishQueue = new PublishQueue();
|
|
3046
4197
|
|
|
3047
|
-
//
|
|
4198
|
+
// src/offline/workspace.ts
|
|
3048
4199
|
var DB_NAME = "adep-offline-workspace";
|
|
3049
|
-
var DB_VERSION = 1;
|
|
3050
4200
|
function requestError(request) {
|
|
3051
4201
|
return request.error ?? new Error("IndexedDB operation failed");
|
|
3052
4202
|
}
|
|
@@ -3061,7 +4211,7 @@ var IndexedDBBackend = class {
|
|
|
3061
4211
|
reject(new Error("IndexedDB not available"));
|
|
3062
4212
|
return;
|
|
3063
4213
|
}
|
|
3064
|
-
const request = indexedDB.open(DB_NAME
|
|
4214
|
+
const request = indexedDB.open(DB_NAME);
|
|
3065
4215
|
request.onupgradeneeded = () => {
|
|
3066
4216
|
};
|
|
3067
4217
|
request.onsuccess = () => {
|
|
@@ -3088,10 +4238,12 @@ var IndexedDBBackend = class {
|
|
|
3088
4238
|
};
|
|
3089
4239
|
request.onsuccess = () => {
|
|
3090
4240
|
this.db = request.result;
|
|
4241
|
+
this.initPromise = Promise.resolve(request.result);
|
|
3091
4242
|
const tx2 = this.db.transaction(storeName, "readwrite");
|
|
3092
4243
|
resolve(tx2.objectStore(storeName));
|
|
3093
4244
|
};
|
|
3094
4245
|
request.onerror = () => reject(requestError(request));
|
|
4246
|
+
request.onblocked = () => reject(new Error("IndexedDB \u5347\u7EA7\u88AB\u5176\u4ED6\u6807\u7B7E\u9875\u963B\u585E\uFF08adep-offline-workspace\uFF09"));
|
|
3095
4247
|
});
|
|
3096
4248
|
}
|
|
3097
4249
|
const tx = db.transaction(storeName, "readwrite");
|
|
@@ -3235,7 +4387,7 @@ var OfflineWorkspace = class {
|
|
|
3235
4387
|
};
|
|
3236
4388
|
var offlineWorkspace = new OfflineWorkspace();
|
|
3237
4389
|
|
|
3238
|
-
//
|
|
4390
|
+
// src/sim/kv.ts
|
|
3239
4391
|
function createMemorySimKv() {
|
|
3240
4392
|
const map = /* @__PURE__ */ new Map();
|
|
3241
4393
|
return {
|
|
@@ -3301,7 +4453,7 @@ function pickDefaultSimKv() {
|
|
|
3301
4453
|
return typeof indexedDB !== "undefined" ? createIdbSimKv() : createMemorySimKv();
|
|
3302
4454
|
}
|
|
3303
4455
|
|
|
3304
|
-
//
|
|
4456
|
+
// ../runtime/src/shared/capability-keys.ts
|
|
3305
4457
|
var RPC_CAPABILITY_KEY = "__adepRpc";
|
|
3306
4458
|
var CHAIN_CAPABILITY_KEY = "__adepChain";
|
|
3307
4459
|
var DB_RPC = {
|
|
@@ -3333,7 +4485,7 @@ var REALTIME_CAPABILITY_METHODS = [
|
|
|
3333
4485
|
"unsubscribe"
|
|
3334
4486
|
];
|
|
3335
4487
|
|
|
3336
|
-
//
|
|
4488
|
+
// ../runtime/src/database/builder/dialect.ts
|
|
3337
4489
|
var sqliteDialect = {
|
|
3338
4490
|
name: "sqlite",
|
|
3339
4491
|
placeholder: () => "?",
|
|
@@ -3352,7 +4504,7 @@ function dialectFor(driver) {
|
|
|
3352
4504
|
return driver.engine === "pg" ? postgresDialect : sqliteDialect;
|
|
3353
4505
|
}
|
|
3354
4506
|
|
|
3355
|
-
//
|
|
4507
|
+
// ../runtime/src/database/provision/identifier.ts
|
|
3356
4508
|
var DbError = class extends Error {
|
|
3357
4509
|
constructor(status, code, message) {
|
|
3358
4510
|
super(message);
|
|
@@ -3376,7 +4528,7 @@ function quoteIdentifier(name) {
|
|
|
3376
4528
|
return `"${name.replace(/"/g, '""')}"`;
|
|
3377
4529
|
}
|
|
3378
4530
|
|
|
3379
|
-
//
|
|
4531
|
+
// ../runtime/src/database/builder/guards.ts
|
|
3380
4532
|
function unsafeOperation(message) {
|
|
3381
4533
|
return new DbError(400, "DB_UNSAFE_OP", message);
|
|
3382
4534
|
}
|
|
@@ -3400,7 +4552,7 @@ function assertReadOnlyQuery(sql) {
|
|
|
3400
4552
|
}
|
|
3401
4553
|
}
|
|
3402
4554
|
|
|
3403
|
-
//
|
|
4555
|
+
// ../runtime/src/database/builder/owned.ts
|
|
3404
4556
|
var OWNED_PK = "id";
|
|
3405
4557
|
var OWNED_OWNER_KEY = "__owner_key";
|
|
3406
4558
|
var CHANGE_LOG_PREFIX = "_adep_changes_";
|
|
@@ -3513,7 +4665,7 @@ async function readChanges(driver, table, query = {}) {
|
|
|
3513
4665
|
return rows.map(parseChange);
|
|
3514
4666
|
}
|
|
3515
4667
|
|
|
3516
|
-
//
|
|
4668
|
+
// ../runtime/src/database/builder/ulid.ts
|
|
3517
4669
|
var ENCODING = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
3518
4670
|
var TIME_LEN = 10;
|
|
3519
4671
|
var RANDOM_LEN = 16;
|
|
@@ -3579,7 +4731,7 @@ function ulid(now = Date.now()) {
|
|
|
3579
4731
|
return time + random;
|
|
3580
4732
|
}
|
|
3581
4733
|
|
|
3582
|
-
//
|
|
4734
|
+
// ../runtime/src/database/builder/table.ts
|
|
3583
4735
|
var OPERATORS = /* @__PURE__ */ new Set([
|
|
3584
4736
|
"=",
|
|
3585
4737
|
"!=",
|
|
@@ -3898,7 +5050,7 @@ function createTableBuilder(table, driver, dialect, options = {}) {
|
|
|
3898
5050
|
return build(initialState);
|
|
3899
5051
|
}
|
|
3900
5052
|
|
|
3901
|
-
//
|
|
5053
|
+
// ../runtime/src/database/builder/index.ts
|
|
3902
5054
|
var TX_STATES = /* @__PURE__ */ new WeakMap();
|
|
3903
5055
|
function createCloudDb(driver, options = {}) {
|
|
3904
5056
|
const dialect = options.dialect ?? dialectFor(driver);
|
|
@@ -3952,7 +5104,7 @@ function createCloudDb(driver, options = {}) {
|
|
|
3952
5104
|
return makeHandle({ allowWriteDuringTx: false });
|
|
3953
5105
|
}
|
|
3954
5106
|
|
|
3955
|
-
//
|
|
5107
|
+
// ../runtime/src/database/sdk/cloud.ts
|
|
3956
5108
|
var CLOUD_DB_SPEC = {
|
|
3957
5109
|
kind: "cloud-db",
|
|
3958
5110
|
rootMethod: "table",
|
|
@@ -4056,7 +5208,7 @@ function createDbCapability(driver, options = {}) {
|
|
|
4056
5208
|
};
|
|
4057
5209
|
}
|
|
4058
5210
|
|
|
4059
|
-
//
|
|
5211
|
+
// ../runtime/src/sim/sql-engine.ts
|
|
4060
5212
|
var SimDbError = class extends Error {
|
|
4061
5213
|
constructor(code, message) {
|
|
4062
5214
|
super(message);
|
|
@@ -4474,7 +5626,7 @@ function project(rows, selectRaw) {
|
|
|
4474
5626
|
});
|
|
4475
5627
|
}
|
|
4476
5628
|
|
|
4477
|
-
//
|
|
5629
|
+
// src/sim/db.ts
|
|
4478
5630
|
function simDbKey(projectId) {
|
|
4479
5631
|
return `db:${projectId}`;
|
|
4480
5632
|
}
|
|
@@ -4502,7 +5654,7 @@ function createBrowserSimDb(options) {
|
|
|
4502
5654
|
return { bundle, engine, driver };
|
|
4503
5655
|
}
|
|
4504
5656
|
|
|
4505
|
-
//
|
|
5657
|
+
// ../runtime/src/storage/driver.ts
|
|
4506
5658
|
var PROJECT_QUOTA_BYTES = 1024 * 1024 * 1024;
|
|
4507
5659
|
var StorageError = class extends Error {
|
|
4508
5660
|
constructor(status, code, message) {
|
|
@@ -4543,7 +5695,7 @@ function assertSafeStoragePath(path) {
|
|
|
4543
5695
|
return path;
|
|
4544
5696
|
}
|
|
4545
5697
|
|
|
4546
|
-
//
|
|
5698
|
+
// ../runtime/src/storage/hmac-sha256.ts
|
|
4547
5699
|
var K = new Uint32Array([
|
|
4548
5700
|
1116352408,
|
|
4549
5701
|
1899447441,
|
|
@@ -4704,7 +5856,7 @@ function hmacSha256Hex(key, message) {
|
|
|
4704
5856
|
return [...hmacSha256(key, message)].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
4705
5857
|
}
|
|
4706
5858
|
|
|
4707
|
-
//
|
|
5859
|
+
// ../runtime/src/storage/signature.ts
|
|
4708
5860
|
var DEFAULT_SIGN_TTL_SECONDS = 15 * 60;
|
|
4709
5861
|
var SIGN_EXPIRES_KEY = "x-expires";
|
|
4710
5862
|
var SIGN_SIGNATURE_KEY = "x-signature";
|
|
@@ -4719,7 +5871,7 @@ function signDownloadUrl(config, projectId, path, ttlSeconds = DEFAULT_SIGN_TTL_
|
|
|
4719
5871
|
return `${base}?${SIGN_EXPIRES_KEY}=${expires}&${SIGN_SIGNATURE_KEY}=${signature}`;
|
|
4720
5872
|
}
|
|
4721
5873
|
|
|
4722
|
-
//
|
|
5874
|
+
// ../runtime/src/storage/cloud.ts
|
|
4723
5875
|
var DEFAULT_VISIBILITY = "private";
|
|
4724
5876
|
function asBytes(data) {
|
|
4725
5877
|
if (typeof data === "string") return new TextEncoder().encode(data);
|
|
@@ -4778,7 +5930,7 @@ function createStorageCapability(driver, signer, projectId) {
|
|
|
4778
5930
|
};
|
|
4779
5931
|
}
|
|
4780
5932
|
|
|
4781
|
-
//
|
|
5933
|
+
// src/sim/storage.ts
|
|
4782
5934
|
function toMeta(object) {
|
|
4783
5935
|
return {
|
|
4784
5936
|
path: object.path,
|
|
@@ -4891,7 +6043,7 @@ function createBrowserSimStorage(options) {
|
|
|
4891
6043
|
return { bundle, driver };
|
|
4892
6044
|
}
|
|
4893
6045
|
|
|
4894
|
-
//
|
|
6046
|
+
// ../runtime/src/sim/realtime.ts
|
|
4895
6047
|
var SimRealtime = class {
|
|
4896
6048
|
seq = 0;
|
|
4897
6049
|
subscribers = /* @__PURE__ */ new Map();
|
|
@@ -4933,7 +6085,7 @@ var SimRealtime = class {
|
|
|
4933
6085
|
};
|
|
4934
6086
|
var REALTIME_CODES = REALTIME_CAPABILITY_CODES;
|
|
4935
6087
|
|
|
4936
|
-
//
|
|
6088
|
+
// src/sim/realtime.ts
|
|
4937
6089
|
function defaultTicketProvider(projectId) {
|
|
4938
6090
|
return async () => {
|
|
4939
6091
|
const res = await fetch("/api/v1/realtime/ticket", {
|
|
@@ -5103,7 +6255,7 @@ function createBrowserRealtime(options) {
|
|
|
5103
6255
|
};
|
|
5104
6256
|
}
|
|
5105
6257
|
|
|
5106
|
-
//
|
|
6258
|
+
// ../runtime/src/functions/runtime/chain.ts
|
|
5107
6259
|
function isChainCapability(value) {
|
|
5108
6260
|
if (typeof value !== "object" || value === null) return false;
|
|
5109
6261
|
const spec = value[CHAIN_CAPABILITY_KEY];
|
|
@@ -5174,7 +6326,7 @@ function createChainClient(capability, port, pending, nextId, spec) {
|
|
|
5174
6326
|
return makeRoot(void 0);
|
|
5175
6327
|
}
|
|
5176
6328
|
|
|
5177
|
-
//
|
|
6329
|
+
// ../runtime/src/functions/runtime/cloud-container.ts
|
|
5178
6330
|
var KNOWN_CAPABILITY_HINTS = {
|
|
5179
6331
|
db: {
|
|
5180
6332
|
code: "DB_NOT_PROVISIONED",
|
|
@@ -5250,7 +6402,7 @@ function createCloudContainer() {
|
|
|
5250
6402
|
};
|
|
5251
6403
|
}
|
|
5252
6404
|
|
|
5253
|
-
//
|
|
6405
|
+
// src/sim/ctx.ts
|
|
5254
6406
|
function createLocalPort(handler) {
|
|
5255
6407
|
const listeners = [];
|
|
5256
6408
|
const dispatch = (message) => {
|
|
@@ -5330,7 +6482,7 @@ function buildCloudFromBundle(bundle) {
|
|
|
5330
6482
|
return container.cloud;
|
|
5331
6483
|
}
|
|
5332
6484
|
|
|
5333
|
-
//
|
|
6485
|
+
// ../runtime/src/functions/runtime/exports.ts
|
|
5334
6486
|
function transformModule(code) {
|
|
5335
6487
|
let out = code;
|
|
5336
6488
|
const named = [];
|
|
@@ -5372,7 +6524,8 @@ function transformModule(code) {
|
|
|
5372
6524
|
return out;
|
|
5373
6525
|
}
|
|
5374
6526
|
|
|
5375
|
-
//
|
|
6527
|
+
// src/sim/runner.ts
|
|
6528
|
+
init_strip_types();
|
|
5376
6529
|
function safeStringify2(value) {
|
|
5377
6530
|
if (typeof value === "string") return value;
|
|
5378
6531
|
try {
|
|
@@ -5502,7 +6655,7 @@ function createOfflineFunctionRunner(options = {}) {
|
|
|
5502
6655
|
return { run };
|
|
5503
6656
|
}
|
|
5504
6657
|
|
|
5505
|
-
//
|
|
6658
|
+
// src/sim/runtime.ts
|
|
5506
6659
|
function mergeBundles(bundles) {
|
|
5507
6660
|
const capabilities = [];
|
|
5508
6661
|
const byName = /* @__PURE__ */ new Map();
|
|
@@ -5548,10 +6701,14 @@ async function createBrowserSimRuntime(options) {
|
|
|
5548
6701
|
dispose: async () => void 0
|
|
5549
6702
|
};
|
|
5550
6703
|
}
|
|
6704
|
+
|
|
6705
|
+
// src/index.ts
|
|
6706
|
+
init_function_fetch_proxy();
|
|
5551
6707
|
export {
|
|
5552
6708
|
COMPLETABLE_COMMANDS,
|
|
5553
6709
|
DEFAULT_MAX_INSTALL_DEPTH,
|
|
5554
6710
|
EXTRA_COMMANDS,
|
|
6711
|
+
FN_FETCH_INTERCEPTOR_SCRIPT,
|
|
5555
6712
|
RESOLVE_EXTENSIONS,
|
|
5556
6713
|
ResolveError,
|
|
5557
6714
|
SHELL_COMMANDS,
|
|
@@ -5560,6 +6717,7 @@ export {
|
|
|
5560
6717
|
TestAssertionError,
|
|
5561
6718
|
UnsupportedTsSyntaxError,
|
|
5562
6719
|
VirtualFileSystem,
|
|
6720
|
+
ViteDevError,
|
|
5563
6721
|
applyCompletion,
|
|
5564
6722
|
bootstrap,
|
|
5565
6723
|
buildCloudFromBundle,
|
|
@@ -5575,6 +6733,7 @@ export {
|
|
|
5575
6733
|
createBrowserStorageDriver,
|
|
5576
6734
|
createCommandHistory,
|
|
5577
6735
|
createFsPersistence,
|
|
6736
|
+
createFunctionFetchHandler,
|
|
5578
6737
|
createIdbSimKv,
|
|
5579
6738
|
createIndexedDbFsBackend,
|
|
5580
6739
|
createLocalPort,
|
|
@@ -5583,10 +6742,12 @@ export {
|
|
|
5583
6742
|
createMemorySimKv,
|
|
5584
6743
|
createNpmClient,
|
|
5585
6744
|
createOfflineFunctionRunner,
|
|
6745
|
+
createRelayFetch,
|
|
5586
6746
|
createRequire,
|
|
5587
6747
|
createSandboxModuleGlobals,
|
|
5588
6748
|
createTestRunner,
|
|
5589
6749
|
createTestScope,
|
|
6750
|
+
createViteServer,
|
|
5590
6751
|
createWorkerRuntime,
|
|
5591
6752
|
deepEqual,
|
|
5592
6753
|
displayPath,
|
|
@@ -5615,6 +6776,7 @@ export {
|
|
|
5615
6776
|
resolvePackageEntry,
|
|
5616
6777
|
restoreVfs,
|
|
5617
6778
|
runCollectedTests,
|
|
6779
|
+
scanEsmSpecifiers,
|
|
5618
6780
|
simDbKey,
|
|
5619
6781
|
simStorageKey,
|
|
5620
6782
|
snapshotVfs,
|