sandlot 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +616 -0
- package/dist/bundler.d.ts +148 -0
- package/dist/bundler.d.ts.map +1 -0
- package/dist/commands.d.ts +179 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/fs.d.ts +125 -0
- package/dist/fs.d.ts.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2920 -0
- package/dist/internal.d.ts +74 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +1897 -0
- package/dist/loader.d.ts +164 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/packages.d.ts +199 -0
- package/dist/packages.d.ts.map +1 -0
- package/dist/react.d.ts +159 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +149 -0
- package/dist/sandbox-manager.d.ts +249 -0
- package/dist/sandbox-manager.d.ts.map +1 -0
- package/dist/sandbox.d.ts +193 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/shared-modules.d.ts +129 -0
- package/dist/shared-modules.d.ts.map +1 -0
- package/dist/shared-resources.d.ts +105 -0
- package/dist/shared-resources.d.ts.map +1 -0
- package/dist/ts-libs.d.ts +98 -0
- package/dist/ts-libs.d.ts.map +1 -0
- package/dist/typechecker.d.ts +127 -0
- package/dist/typechecker.d.ts.map +1 -0
- package/package.json +64 -0
- package/src/bundler.ts +513 -0
- package/src/commands.ts +733 -0
- package/src/fs.ts +935 -0
- package/src/index.ts +149 -0
- package/src/internal.ts +116 -0
- package/src/loader.ts +229 -0
- package/src/packages.ts +936 -0
- package/src/react.tsx +331 -0
- package/src/sandbox-manager.ts +490 -0
- package/src/sandbox.ts +402 -0
- package/src/shared-modules.ts +210 -0
- package/src/shared-resources.ts +169 -0
- package/src/ts-libs.ts +320 -0
- package/src/typechecker.ts +635 -0
package/dist/internal.js
ADDED
|
@@ -0,0 +1,1897 @@
|
|
|
1
|
+
// src/loader.ts
|
|
2
|
+
class ModuleLoadError extends Error {
|
|
3
|
+
constructor(message, cause) {
|
|
4
|
+
super(message, { cause });
|
|
5
|
+
this.name = "ModuleLoadError";
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
class ExportNotFoundError extends Error {
|
|
10
|
+
exportName;
|
|
11
|
+
availableExports;
|
|
12
|
+
constructor(exportName, availableExports) {
|
|
13
|
+
super(`Export "${exportName}" not found. Available exports: ${availableExports.length > 0 ? availableExports.join(", ") : "(none)"}`);
|
|
14
|
+
this.exportName = exportName;
|
|
15
|
+
this.availableExports = availableExports;
|
|
16
|
+
this.name = "ExportNotFoundError";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function createModuleUrl(result) {
|
|
20
|
+
const blob = new Blob([result.code], { type: "application/javascript" });
|
|
21
|
+
return URL.createObjectURL(blob);
|
|
22
|
+
}
|
|
23
|
+
function revokeModuleUrl(url) {
|
|
24
|
+
URL.revokeObjectURL(url);
|
|
25
|
+
}
|
|
26
|
+
async function loadModule(result) {
|
|
27
|
+
const url = createModuleUrl(result);
|
|
28
|
+
try {
|
|
29
|
+
const module = await import(url);
|
|
30
|
+
return module;
|
|
31
|
+
} catch (err) {
|
|
32
|
+
throw new ModuleLoadError(`Failed to load module: ${err instanceof Error ? err.message : String(err)}`, err);
|
|
33
|
+
} finally {
|
|
34
|
+
revokeModuleUrl(url);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async function loadExport(result, exportName = "default") {
|
|
38
|
+
const module = await loadModule(result);
|
|
39
|
+
if (!(exportName in module)) {
|
|
40
|
+
const availableExports = Object.keys(module).filter((key) => !key.startsWith("__"));
|
|
41
|
+
throw new ExportNotFoundError(exportName, availableExports);
|
|
42
|
+
}
|
|
43
|
+
return module[exportName];
|
|
44
|
+
}
|
|
45
|
+
async function loadDefault(result) {
|
|
46
|
+
return loadExport(result, "default");
|
|
47
|
+
}
|
|
48
|
+
async function getExportNames(result) {
|
|
49
|
+
const module = await loadModule(result);
|
|
50
|
+
return Object.keys(module).filter((key) => !key.startsWith("__"));
|
|
51
|
+
}
|
|
52
|
+
async function hasExport(result, exportName) {
|
|
53
|
+
const module = await loadModule(result);
|
|
54
|
+
return exportName in module;
|
|
55
|
+
}
|
|
56
|
+
// src/packages.ts
|
|
57
|
+
var ESM_CDN_BASE = "https://esm.sh";
|
|
58
|
+
var KNOWN_SUBPATHS = {
|
|
59
|
+
react: ["jsx-runtime", "jsx-dev-runtime"],
|
|
60
|
+
"react-dom": ["client", "server"]
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
class InMemoryTypesCache {
|
|
64
|
+
cache = new Map;
|
|
65
|
+
key(name, version) {
|
|
66
|
+
return `${name}@${version}`;
|
|
67
|
+
}
|
|
68
|
+
get(name, version) {
|
|
69
|
+
return this.cache.get(this.key(name, version)) ?? null;
|
|
70
|
+
}
|
|
71
|
+
set(name, version, types) {
|
|
72
|
+
this.cache.set(this.key(name, version), types);
|
|
73
|
+
}
|
|
74
|
+
has(name, version) {
|
|
75
|
+
return this.cache.has(this.key(name, version));
|
|
76
|
+
}
|
|
77
|
+
delete(name, version) {
|
|
78
|
+
return this.cache.delete(this.key(name, version));
|
|
79
|
+
}
|
|
80
|
+
clear() {
|
|
81
|
+
this.cache.clear();
|
|
82
|
+
}
|
|
83
|
+
get size() {
|
|
84
|
+
return this.cache.size;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
var PACKAGE_JSON_PATH = "/package.json";
|
|
88
|
+
function parsePackageSpec(spec) {
|
|
89
|
+
if (spec.startsWith("@")) {
|
|
90
|
+
const slashIndex = spec.indexOf("/");
|
|
91
|
+
if (slashIndex === -1) {
|
|
92
|
+
return { name: spec };
|
|
93
|
+
}
|
|
94
|
+
const afterSlash = spec.slice(slashIndex + 1);
|
|
95
|
+
const atIndex2 = afterSlash.indexOf("@");
|
|
96
|
+
if (atIndex2 === -1) {
|
|
97
|
+
return { name: spec };
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
name: spec.slice(0, slashIndex + 1 + atIndex2),
|
|
101
|
+
version: afterSlash.slice(atIndex2 + 1)
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
const atIndex = spec.indexOf("@");
|
|
105
|
+
if (atIndex === -1) {
|
|
106
|
+
return { name: spec };
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
name: spec.slice(0, atIndex),
|
|
110
|
+
version: spec.slice(atIndex + 1)
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function isTypesPackage(name) {
|
|
114
|
+
return name.startsWith("@types/");
|
|
115
|
+
}
|
|
116
|
+
function extractVersionFromUrl(url, packageName) {
|
|
117
|
+
const exactRegex = new RegExp(`${escapeRegExp(packageName)}@([^/]+)`);
|
|
118
|
+
const exactMatch = url.match(exactRegex);
|
|
119
|
+
if (exactMatch?.[1]) {
|
|
120
|
+
return exactMatch[1];
|
|
121
|
+
}
|
|
122
|
+
if (packageName.startsWith("@")) {
|
|
123
|
+
const scopedParts = packageName.split("/");
|
|
124
|
+
if (scopedParts.length === 2 && scopedParts[1]) {
|
|
125
|
+
const partialRegex = new RegExp(`${escapeRegExp(scopedParts[1])}@([^/]+)`);
|
|
126
|
+
const partialMatch = url.match(partialRegex);
|
|
127
|
+
if (partialMatch?.[1]) {
|
|
128
|
+
return partialMatch[1];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const genericMatch = url.match(/@(\d+\.\d+\.\d+[^/]*)/);
|
|
133
|
+
if (genericMatch?.[1]) {
|
|
134
|
+
return genericMatch[1];
|
|
135
|
+
}
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
async function fetchVersionFromNpm(name) {
|
|
139
|
+
const registryUrl = `https://registry.npmjs.org/${name}/latest`;
|
|
140
|
+
const response = await fetch(registryUrl);
|
|
141
|
+
if (!response.ok) {
|
|
142
|
+
throw new Error(`Failed to fetch version from npm: ${response.status}`);
|
|
143
|
+
}
|
|
144
|
+
const data = await response.json();
|
|
145
|
+
return data.version;
|
|
146
|
+
}
|
|
147
|
+
function extractVersionFromHeaders(headers, packageName) {
|
|
148
|
+
const esmId = headers.get("x-esm-id");
|
|
149
|
+
if (esmId) {
|
|
150
|
+
const version = extractVersionFromUrl(esmId, packageName);
|
|
151
|
+
if (version)
|
|
152
|
+
return version;
|
|
153
|
+
}
|
|
154
|
+
const typesHeader = headers.get("X-TypeScript-Types");
|
|
155
|
+
if (typesHeader) {
|
|
156
|
+
const versionMatch = typesHeader.match(/@(\d+\.\d+\.\d+[^/]*)/);
|
|
157
|
+
if (versionMatch?.[1]) {
|
|
158
|
+
return versionMatch[1];
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
async function fetchPackageInfo(name, version, subpath) {
|
|
164
|
+
let url = version ? `${ESM_CDN_BASE}/${name}@${version}` : `${ESM_CDN_BASE}/${name}`;
|
|
165
|
+
if (subpath) {
|
|
166
|
+
url += `/${subpath}`;
|
|
167
|
+
}
|
|
168
|
+
const response = await fetch(url, { method: "HEAD" });
|
|
169
|
+
if (!response.ok) {
|
|
170
|
+
throw new Error(`Package not found: ${name}${version ? `@${version}` : ""}${subpath ? `/${subpath}` : ""}`);
|
|
171
|
+
}
|
|
172
|
+
const resolvedUrl = response.url;
|
|
173
|
+
let resolvedVersion = extractVersionFromUrl(resolvedUrl, name);
|
|
174
|
+
if (!resolvedVersion) {
|
|
175
|
+
resolvedVersion = extractVersionFromHeaders(response.headers, name);
|
|
176
|
+
}
|
|
177
|
+
if (!resolvedVersion && version && version !== "latest") {
|
|
178
|
+
resolvedVersion = version;
|
|
179
|
+
}
|
|
180
|
+
if (!resolvedVersion) {
|
|
181
|
+
try {
|
|
182
|
+
resolvedVersion = await fetchVersionFromNpm(name);
|
|
183
|
+
} catch (err) {
|
|
184
|
+
console.warn(`Could not resolve version for ${name}:`, err);
|
|
185
|
+
resolvedVersion = "latest";
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const typesUrl = response.headers.get("X-TypeScript-Types") ?? undefined;
|
|
189
|
+
return {
|
|
190
|
+
version: resolvedVersion,
|
|
191
|
+
typesUrl: typesUrl ? new URL(typesUrl, resolvedUrl).href : undefined
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
function escapeRegExp(string) {
|
|
195
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
196
|
+
}
|
|
197
|
+
async function fetchTypeDefinitions(typesUrl, packageName) {
|
|
198
|
+
const types = new Map;
|
|
199
|
+
try {
|
|
200
|
+
const response = await fetch(typesUrl);
|
|
201
|
+
if (!response.ok) {
|
|
202
|
+
throw new Error(`Failed to fetch types: ${response.status}`);
|
|
203
|
+
}
|
|
204
|
+
const content = await response.text();
|
|
205
|
+
const typePath = `/node_modules/${packageName}/index.d.ts`;
|
|
206
|
+
types.set(typePath, content);
|
|
207
|
+
const refs = parseTypeReferences(content);
|
|
208
|
+
await fetchReferencedTypes(refs, typesUrl, packageName, types);
|
|
209
|
+
} catch (err) {
|
|
210
|
+
console.warn(`Failed to fetch types for ${packageName}:`, err);
|
|
211
|
+
throw err;
|
|
212
|
+
}
|
|
213
|
+
return types;
|
|
214
|
+
}
|
|
215
|
+
function parseTypeReferences(content) {
|
|
216
|
+
const paths = [];
|
|
217
|
+
const types = [];
|
|
218
|
+
const pathRegex = /\/\/\/\s*<reference\s+path="([^"]+)"\s*\/>/g;
|
|
219
|
+
let match;
|
|
220
|
+
while ((match = pathRegex.exec(content)) !== null) {
|
|
221
|
+
if (match[1])
|
|
222
|
+
paths.push(match[1]);
|
|
223
|
+
}
|
|
224
|
+
const typesRegex = /\/\/\/\s*<reference\s+types="([^"]+)"\s*\/>/g;
|
|
225
|
+
while ((match = typesRegex.exec(content)) !== null) {
|
|
226
|
+
if (match[1])
|
|
227
|
+
types.push(match[1]);
|
|
228
|
+
}
|
|
229
|
+
return { paths, types };
|
|
230
|
+
}
|
|
231
|
+
async function fetchReferencedTypes(refs, baseUrl, packageName, collected, visited = new Set) {
|
|
232
|
+
for (const pathRef of refs.paths) {
|
|
233
|
+
const refUrl = new URL(pathRef, baseUrl).href;
|
|
234
|
+
if (visited.has(refUrl))
|
|
235
|
+
continue;
|
|
236
|
+
visited.add(refUrl);
|
|
237
|
+
try {
|
|
238
|
+
const response = await fetch(refUrl);
|
|
239
|
+
if (!response.ok)
|
|
240
|
+
continue;
|
|
241
|
+
const content = await response.text();
|
|
242
|
+
const fileName = pathRef.split("/").pop() ?? "types.d.ts";
|
|
243
|
+
const typePath = `/node_modules/${packageName}/${fileName}`;
|
|
244
|
+
collected.set(typePath, content);
|
|
245
|
+
const nestedRefs = parseTypeReferences(content);
|
|
246
|
+
await fetchReferencedTypes(nestedRefs, refUrl, packageName, collected, visited);
|
|
247
|
+
} catch {}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
async function fetchSubpathTypes(packageName, subpath, version) {
|
|
251
|
+
const types = new Map;
|
|
252
|
+
try {
|
|
253
|
+
const info = await fetchPackageInfo(packageName, version, subpath);
|
|
254
|
+
if (!info.typesUrl) {
|
|
255
|
+
return types;
|
|
256
|
+
}
|
|
257
|
+
const response = await fetch(info.typesUrl);
|
|
258
|
+
if (!response.ok) {
|
|
259
|
+
return types;
|
|
260
|
+
}
|
|
261
|
+
const content = await response.text();
|
|
262
|
+
const typePath = `/node_modules/${packageName}/${subpath}.d.ts`;
|
|
263
|
+
types.set(typePath, content);
|
|
264
|
+
const indexTypePath = `/node_modules/${packageName}/${subpath}/index.d.ts`;
|
|
265
|
+
types.set(indexTypePath, content);
|
|
266
|
+
const refs = parseTypeReferences(content);
|
|
267
|
+
await fetchReferencedTypes(refs, info.typesUrl, packageName, types);
|
|
268
|
+
} catch (err) {
|
|
269
|
+
console.warn(`Failed to fetch types for ${packageName}/${subpath}:`, err);
|
|
270
|
+
}
|
|
271
|
+
return types;
|
|
272
|
+
}
|
|
273
|
+
async function fetchTypesPackageContent(name, version) {
|
|
274
|
+
const url = version ? `${ESM_CDN_BASE}/${name}@${version}` : `${ESM_CDN_BASE}/${name}`;
|
|
275
|
+
const headResponse = await fetch(url, { method: "HEAD" });
|
|
276
|
+
if (!headResponse.ok) {
|
|
277
|
+
throw new Error(`Package not found: ${name}${version ? `@${version}` : ""}`);
|
|
278
|
+
}
|
|
279
|
+
const resolvedVersion = extractVersionFromUrl(headResponse.url, name) ?? version ?? "latest";
|
|
280
|
+
const indexUrl = `${ESM_CDN_BASE}/${name}@${resolvedVersion}/index.d.ts`;
|
|
281
|
+
const response = await fetch(indexUrl);
|
|
282
|
+
if (!response.ok) {
|
|
283
|
+
throw new Error(`Failed to fetch types from ${name}: ${response.status}`);
|
|
284
|
+
}
|
|
285
|
+
const content = await response.text();
|
|
286
|
+
const types = new Map;
|
|
287
|
+
const typePath = `/node_modules/${name}/index.d.ts`;
|
|
288
|
+
types.set(typePath, content);
|
|
289
|
+
const refs = parseTypeReferences(content);
|
|
290
|
+
for (const pathRef of refs.paths) {
|
|
291
|
+
try {
|
|
292
|
+
const refUrl = new URL(pathRef, indexUrl).href;
|
|
293
|
+
const refResponse = await fetch(refUrl);
|
|
294
|
+
if (refResponse.ok) {
|
|
295
|
+
const refContent = await refResponse.text();
|
|
296
|
+
const fileName = pathRef.startsWith("./") ? pathRef.slice(2) : pathRef;
|
|
297
|
+
const refTypePath = `/node_modules/${name}/${fileName}`;
|
|
298
|
+
types.set(refTypePath, refContent);
|
|
299
|
+
}
|
|
300
|
+
} catch {}
|
|
301
|
+
}
|
|
302
|
+
return { version: resolvedVersion, types };
|
|
303
|
+
}
|
|
304
|
+
async function getPackageManifest(fs) {
|
|
305
|
+
try {
|
|
306
|
+
if (await fs.exists(PACKAGE_JSON_PATH)) {
|
|
307
|
+
const content = await fs.readFile(PACKAGE_JSON_PATH);
|
|
308
|
+
const parsed = JSON.parse(content);
|
|
309
|
+
return {
|
|
310
|
+
dependencies: parsed.dependencies ?? {}
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
} catch {}
|
|
314
|
+
return { dependencies: {} };
|
|
315
|
+
}
|
|
316
|
+
async function savePackageManifest(fs, manifest) {
|
|
317
|
+
let existing = {};
|
|
318
|
+
try {
|
|
319
|
+
if (await fs.exists(PACKAGE_JSON_PATH)) {
|
|
320
|
+
const content = await fs.readFile(PACKAGE_JSON_PATH);
|
|
321
|
+
existing = JSON.parse(content);
|
|
322
|
+
}
|
|
323
|
+
} catch {}
|
|
324
|
+
const updated = {
|
|
325
|
+
...existing,
|
|
326
|
+
dependencies: manifest.dependencies
|
|
327
|
+
};
|
|
328
|
+
await fs.writeFile(PACKAGE_JSON_PATH, JSON.stringify(updated, null, 2));
|
|
329
|
+
}
|
|
330
|
+
async function installPackage(fs, packageSpec, options) {
|
|
331
|
+
const { name, version } = parsePackageSpec(packageSpec);
|
|
332
|
+
const { cache } = options ?? {};
|
|
333
|
+
if (isTypesPackage(name)) {
|
|
334
|
+
return installTypesPackage(fs, name, version, cache);
|
|
335
|
+
}
|
|
336
|
+
const info = await fetchPackageInfo(name, version);
|
|
337
|
+
const packageDir = `/node_modules/${name}`;
|
|
338
|
+
await ensureDir(fs, packageDir);
|
|
339
|
+
const packageJsonPath = `${packageDir}/package.json`;
|
|
340
|
+
const packageJson = {
|
|
341
|
+
name,
|
|
342
|
+
version: info.version,
|
|
343
|
+
types: "./index.d.ts",
|
|
344
|
+
main: "./index.js"
|
|
345
|
+
};
|
|
346
|
+
await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
347
|
+
let typeFiles = null;
|
|
348
|
+
let fromCache = false;
|
|
349
|
+
if (cache) {
|
|
350
|
+
typeFiles = cache.get(name, info.version);
|
|
351
|
+
if (typeFiles) {
|
|
352
|
+
fromCache = true;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
let typesError;
|
|
356
|
+
if (!typeFiles) {
|
|
357
|
+
typeFiles = new Map;
|
|
358
|
+
if (info.typesUrl) {
|
|
359
|
+
try {
|
|
360
|
+
const mainTypes = await fetchTypeDefinitions(info.typesUrl, name);
|
|
361
|
+
for (const [path, content] of mainTypes) {
|
|
362
|
+
typeFiles.set(path, content);
|
|
363
|
+
}
|
|
364
|
+
} catch (err) {
|
|
365
|
+
typesError = err instanceof Error ? err.message : String(err);
|
|
366
|
+
}
|
|
367
|
+
} else {
|
|
368
|
+
typesError = "No TypeScript types available from esm.sh";
|
|
369
|
+
}
|
|
370
|
+
const knownSubpaths = KNOWN_SUBPATHS[name];
|
|
371
|
+
if (knownSubpaths && knownSubpaths.length > 0) {
|
|
372
|
+
const subpathResults = await Promise.allSettled(knownSubpaths.map((subpath) => fetchSubpathTypes(name, subpath, info.version)));
|
|
373
|
+
for (const result of subpathResults) {
|
|
374
|
+
if (result.status === "fulfilled") {
|
|
375
|
+
for (const [path, content] of result.value) {
|
|
376
|
+
typeFiles.set(path, content);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
if (cache && typeFiles.size > 0) {
|
|
382
|
+
cache.set(name, info.version, typeFiles);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
for (const [path, content] of typeFiles) {
|
|
386
|
+
const dir = path.substring(0, path.lastIndexOf("/"));
|
|
387
|
+
await ensureDir(fs, dir);
|
|
388
|
+
await fs.writeFile(path, content);
|
|
389
|
+
}
|
|
390
|
+
const manifest = await getPackageManifest(fs);
|
|
391
|
+
manifest.dependencies[name] = info.version;
|
|
392
|
+
await savePackageManifest(fs, manifest);
|
|
393
|
+
return {
|
|
394
|
+
name,
|
|
395
|
+
version: info.version,
|
|
396
|
+
typesInstalled: typeFiles.size > 0,
|
|
397
|
+
typeFilesCount: typeFiles.size,
|
|
398
|
+
typesError,
|
|
399
|
+
fromCache
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
async function installTypesPackage(fs, name, version, cache) {
|
|
403
|
+
const url = version ? `${ESM_CDN_BASE}/${name}@${version}` : `${ESM_CDN_BASE}/${name}`;
|
|
404
|
+
const headResponse = await fetch(url, { method: "HEAD" });
|
|
405
|
+
if (!headResponse.ok) {
|
|
406
|
+
throw new Error(`Package not found: ${name}${version ? `@${version}` : ""}`);
|
|
407
|
+
}
|
|
408
|
+
const resolvedVersion = extractVersionFromUrl(headResponse.url, name) ?? version ?? "latest";
|
|
409
|
+
let types = null;
|
|
410
|
+
let fromCache = false;
|
|
411
|
+
if (cache) {
|
|
412
|
+
types = cache.get(name, resolvedVersion);
|
|
413
|
+
if (types) {
|
|
414
|
+
fromCache = true;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
if (!types) {
|
|
418
|
+
const result = await fetchTypesPackageContent(name, version);
|
|
419
|
+
types = result.types;
|
|
420
|
+
if (cache && types.size > 0) {
|
|
421
|
+
cache.set(name, resolvedVersion, types);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
const packageDir = `/node_modules/${name}`;
|
|
425
|
+
await ensureDir(fs, packageDir);
|
|
426
|
+
const packageJsonPath = `${packageDir}/package.json`;
|
|
427
|
+
const packageJson = {
|
|
428
|
+
name,
|
|
429
|
+
version: resolvedVersion,
|
|
430
|
+
types: "./index.d.ts"
|
|
431
|
+
};
|
|
432
|
+
await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
433
|
+
for (const [path, content] of types) {
|
|
434
|
+
const dir = path.substring(0, path.lastIndexOf("/"));
|
|
435
|
+
await ensureDir(fs, dir);
|
|
436
|
+
await fs.writeFile(path, content);
|
|
437
|
+
}
|
|
438
|
+
const manifest = await getPackageManifest(fs);
|
|
439
|
+
manifest.dependencies[name] = resolvedVersion;
|
|
440
|
+
await savePackageManifest(fs, manifest);
|
|
441
|
+
return {
|
|
442
|
+
name,
|
|
443
|
+
version: resolvedVersion,
|
|
444
|
+
typesInstalled: types.size > 0,
|
|
445
|
+
typeFilesCount: types.size,
|
|
446
|
+
fromCache
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
async function ensureDir(fs, path) {
|
|
450
|
+
if (path === "/" || path === "")
|
|
451
|
+
return;
|
|
452
|
+
if (await fs.exists(path)) {
|
|
453
|
+
const stat = await fs.stat(path);
|
|
454
|
+
if (stat.isDirectory)
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
const parent = path.substring(0, path.lastIndexOf("/")) || "/";
|
|
458
|
+
await ensureDir(fs, parent);
|
|
459
|
+
await fs.mkdir(path);
|
|
460
|
+
}
|
|
461
|
+
async function uninstallPackage(fs, packageName) {
|
|
462
|
+
const manifest = await getPackageManifest(fs);
|
|
463
|
+
if (!(packageName in manifest.dependencies)) {
|
|
464
|
+
return false;
|
|
465
|
+
}
|
|
466
|
+
delete manifest.dependencies[packageName];
|
|
467
|
+
await savePackageManifest(fs, manifest);
|
|
468
|
+
const typesPath = `/node_modules/${packageName}`;
|
|
469
|
+
if (await fs.exists(typesPath)) {
|
|
470
|
+
await removePath(fs, typesPath);
|
|
471
|
+
}
|
|
472
|
+
return true;
|
|
473
|
+
}
|
|
474
|
+
async function removePath(fs, path) {
|
|
475
|
+
if (!await fs.exists(path))
|
|
476
|
+
return;
|
|
477
|
+
await fs.rm(path, { recursive: true, force: true });
|
|
478
|
+
}
|
|
479
|
+
function resolveToEsmUrl(importPath, installedPackages) {
|
|
480
|
+
const { packageName, subpath } = parseImportPath(importPath);
|
|
481
|
+
const version = installedPackages[packageName];
|
|
482
|
+
if (!version) {
|
|
483
|
+
return null;
|
|
484
|
+
}
|
|
485
|
+
const baseUrl = `${ESM_CDN_BASE}/${packageName}@${version}`;
|
|
486
|
+
return subpath ? `${baseUrl}/${subpath}` : baseUrl;
|
|
487
|
+
}
|
|
488
|
+
function parseImportPath(importPath) {
|
|
489
|
+
if (importPath.startsWith("@")) {
|
|
490
|
+
const parts = importPath.split("/");
|
|
491
|
+
if (parts.length >= 2) {
|
|
492
|
+
const packageName = `${parts[0]}/${parts[1]}`;
|
|
493
|
+
const subpath = parts.slice(2).join("/") || undefined;
|
|
494
|
+
return { packageName, subpath };
|
|
495
|
+
}
|
|
496
|
+
return { packageName: importPath };
|
|
497
|
+
}
|
|
498
|
+
const slashIndex = importPath.indexOf("/");
|
|
499
|
+
if (slashIndex === -1) {
|
|
500
|
+
return { packageName: importPath };
|
|
501
|
+
}
|
|
502
|
+
return {
|
|
503
|
+
packageName: importPath.slice(0, slashIndex),
|
|
504
|
+
subpath: importPath.slice(slashIndex + 1)
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
async function listPackages(fs) {
|
|
508
|
+
const manifest = await getPackageManifest(fs);
|
|
509
|
+
return Object.entries(manifest.dependencies).map(([name, version]) => ({
|
|
510
|
+
name,
|
|
511
|
+
version
|
|
512
|
+
}));
|
|
513
|
+
}
|
|
514
|
+
// src/ts-libs.ts
|
|
515
|
+
var TS_VERSION = "5.9.3";
|
|
516
|
+
var CDN_BASE = `https://cdn.jsdelivr.net/npm/typescript@${TS_VERSION}/lib`;
|
|
517
|
+
var DB_NAME = "ts-lib-cache";
|
|
518
|
+
var DB_VERSION = 1;
|
|
519
|
+
var STORE_NAME = "libs";
|
|
520
|
+
function getDefaultBrowserLibs() {
|
|
521
|
+
return ["es2020", "dom", "dom.iterable"];
|
|
522
|
+
}
|
|
523
|
+
function parseLibReferences(content) {
|
|
524
|
+
const refs = [];
|
|
525
|
+
const regex = /\/\/\/\s*<reference\s+lib="([^"]+)"\s*\/>/g;
|
|
526
|
+
let match;
|
|
527
|
+
while ((match = regex.exec(content)) !== null) {
|
|
528
|
+
if (match[1]) {
|
|
529
|
+
refs.push(match[1]);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
return refs;
|
|
533
|
+
}
|
|
534
|
+
function libNameToFileName(name) {
|
|
535
|
+
return `lib.${name}.d.ts`;
|
|
536
|
+
}
|
|
537
|
+
function extractLibName(filePath) {
|
|
538
|
+
const match = filePath.match(/lib\.([^/]+)\.d\.ts$/);
|
|
539
|
+
return match?.[1] ?? null;
|
|
540
|
+
}
|
|
541
|
+
async function fetchLibFile(name) {
|
|
542
|
+
const fileName = libNameToFileName(name);
|
|
543
|
+
const url = `${CDN_BASE}/${fileName}`;
|
|
544
|
+
const response = await fetch(url);
|
|
545
|
+
if (!response.ok) {
|
|
546
|
+
throw new Error(`Failed to fetch ${url}: ${response.status} ${response.statusText}`);
|
|
547
|
+
}
|
|
548
|
+
return response.text();
|
|
549
|
+
}
|
|
550
|
+
async function fetchAllLibs(libs) {
|
|
551
|
+
const result = new Map;
|
|
552
|
+
const pending = new Set(libs);
|
|
553
|
+
const fetched = new Set;
|
|
554
|
+
while (pending.size > 0) {
|
|
555
|
+
const batch = Array.from(pending);
|
|
556
|
+
pending.clear();
|
|
557
|
+
const results = await Promise.all(batch.map(async (name) => {
|
|
558
|
+
if (fetched.has(name)) {
|
|
559
|
+
return { name, content: null };
|
|
560
|
+
}
|
|
561
|
+
fetched.add(name);
|
|
562
|
+
try {
|
|
563
|
+
const content = await fetchLibFile(name);
|
|
564
|
+
return { name, content };
|
|
565
|
+
} catch (err) {
|
|
566
|
+
console.warn(`Failed to fetch lib.${name}.d.ts:`, err);
|
|
567
|
+
return { name, content: null };
|
|
568
|
+
}
|
|
569
|
+
}));
|
|
570
|
+
for (const { name, content } of results) {
|
|
571
|
+
if (content === null)
|
|
572
|
+
continue;
|
|
573
|
+
result.set(name, content);
|
|
574
|
+
const refs = parseLibReferences(content);
|
|
575
|
+
for (const ref of refs) {
|
|
576
|
+
if (!fetched.has(ref) && !pending.has(ref)) {
|
|
577
|
+
pending.add(ref);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
return result;
|
|
583
|
+
}
|
|
584
|
+
async function openDatabase() {
|
|
585
|
+
return new Promise((resolve, reject) => {
|
|
586
|
+
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
|
587
|
+
request.onerror = () => reject(request.error);
|
|
588
|
+
request.onsuccess = () => resolve(request.result);
|
|
589
|
+
request.onupgradeneeded = (event) => {
|
|
590
|
+
const db = event.target.result;
|
|
591
|
+
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
|
592
|
+
db.createObjectStore(STORE_NAME);
|
|
593
|
+
}
|
|
594
|
+
};
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
function promisifyRequest(request) {
|
|
598
|
+
return new Promise((resolve, reject) => {
|
|
599
|
+
request.onsuccess = () => resolve(request.result);
|
|
600
|
+
request.onerror = () => reject(request.error);
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
function getCacheKey() {
|
|
604
|
+
return `libs-${TS_VERSION}`;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
class LibCache {
|
|
608
|
+
db;
|
|
609
|
+
constructor(db) {
|
|
610
|
+
this.db = db;
|
|
611
|
+
}
|
|
612
|
+
static async create() {
|
|
613
|
+
const db = await openDatabase();
|
|
614
|
+
return new LibCache(db);
|
|
615
|
+
}
|
|
616
|
+
async getOrFetch(libs) {
|
|
617
|
+
const cached = await this.get();
|
|
618
|
+
if (cached) {
|
|
619
|
+
const missing = libs.filter((lib) => !cached.has(lib));
|
|
620
|
+
if (missing.length === 0) {
|
|
621
|
+
return cached;
|
|
622
|
+
}
|
|
623
|
+
console.log(`Cache missing libs: ${missing.join(", ")}, fetching all...`);
|
|
624
|
+
}
|
|
625
|
+
console.log(`Fetching TypeScript libs from CDN: ${libs.join(", ")}...`);
|
|
626
|
+
const fetched = await fetchAllLibs(libs);
|
|
627
|
+
console.log(`Fetched ${fetched.size} lib files`);
|
|
628
|
+
await this.set(fetched);
|
|
629
|
+
return fetched;
|
|
630
|
+
}
|
|
631
|
+
async get() {
|
|
632
|
+
const tx = this.db.transaction(STORE_NAME, "readonly");
|
|
633
|
+
const store = tx.objectStore(STORE_NAME);
|
|
634
|
+
const key = getCacheKey();
|
|
635
|
+
const cached = await promisifyRequest(store.get(key));
|
|
636
|
+
if (!cached) {
|
|
637
|
+
return null;
|
|
638
|
+
}
|
|
639
|
+
if (cached.version !== TS_VERSION) {
|
|
640
|
+
console.log(`Cache version mismatch: ${cached.version} vs ${TS_VERSION}`);
|
|
641
|
+
return null;
|
|
642
|
+
}
|
|
643
|
+
return new Map(Object.entries(cached.libs));
|
|
644
|
+
}
|
|
645
|
+
async set(libs) {
|
|
646
|
+
const tx = this.db.transaction(STORE_NAME, "readwrite");
|
|
647
|
+
const store = tx.objectStore(STORE_NAME);
|
|
648
|
+
const key = getCacheKey();
|
|
649
|
+
const cached = {
|
|
650
|
+
version: TS_VERSION,
|
|
651
|
+
timestamp: Date.now(),
|
|
652
|
+
libs: Object.fromEntries(libs)
|
|
653
|
+
};
|
|
654
|
+
await promisifyRequest(store.put(cached, key));
|
|
655
|
+
}
|
|
656
|
+
async clear() {
|
|
657
|
+
const tx = this.db.transaction(STORE_NAME, "readwrite");
|
|
658
|
+
const store = tx.objectStore(STORE_NAME);
|
|
659
|
+
await promisifyRequest(store.clear());
|
|
660
|
+
}
|
|
661
|
+
close() {
|
|
662
|
+
this.db.close();
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
async function fetchAndCacheLibs(libs = getDefaultBrowserLibs()) {
|
|
666
|
+
const cache = await LibCache.create();
|
|
667
|
+
try {
|
|
668
|
+
return await cache.getOrFetch(libs);
|
|
669
|
+
} finally {
|
|
670
|
+
cache.close();
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
// src/shared-modules.ts
|
|
674
|
+
var GLOBAL_KEY = "__sandlot_shared_modules__";
|
|
675
|
+
|
|
676
|
+
class SharedModuleRegistry {
|
|
677
|
+
modules = new Map;
|
|
678
|
+
constructor() {
|
|
679
|
+
globalThis[GLOBAL_KEY] = this;
|
|
680
|
+
}
|
|
681
|
+
register(moduleId, module) {
|
|
682
|
+
this.modules.set(moduleId, module);
|
|
683
|
+
return this;
|
|
684
|
+
}
|
|
685
|
+
registerAll(modules) {
|
|
686
|
+
for (const [id, mod] of Object.entries(modules)) {
|
|
687
|
+
this.modules.set(id, mod);
|
|
688
|
+
}
|
|
689
|
+
return this;
|
|
690
|
+
}
|
|
691
|
+
unregister(moduleId) {
|
|
692
|
+
return this.modules.delete(moduleId);
|
|
693
|
+
}
|
|
694
|
+
get(moduleId) {
|
|
695
|
+
const mod = this.modules.get(moduleId);
|
|
696
|
+
if (mod === undefined && !this.modules.has(moduleId)) {
|
|
697
|
+
const available = this.list();
|
|
698
|
+
throw new Error(`Shared module "${moduleId}" not registered. ` + `Available: ${available.length > 0 ? available.join(", ") : "(none)"}. ` + `Call registerSharedModules({ '${moduleId}': ... }) in your host application.`);
|
|
699
|
+
}
|
|
700
|
+
return mod;
|
|
701
|
+
}
|
|
702
|
+
has(moduleId) {
|
|
703
|
+
return this.modules.has(moduleId);
|
|
704
|
+
}
|
|
705
|
+
list() {
|
|
706
|
+
return [...this.modules.keys()];
|
|
707
|
+
}
|
|
708
|
+
clear() {
|
|
709
|
+
this.modules.clear();
|
|
710
|
+
}
|
|
711
|
+
get size() {
|
|
712
|
+
return this.modules.size;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
var defaultRegistry = null;
|
|
716
|
+
function getSharedModuleRegistry() {
|
|
717
|
+
if (!defaultRegistry) {
|
|
718
|
+
defaultRegistry = new SharedModuleRegistry;
|
|
719
|
+
}
|
|
720
|
+
return defaultRegistry;
|
|
721
|
+
}
|
|
722
|
+
function hasSharedModuleRegistry() {
|
|
723
|
+
return GLOBAL_KEY in globalThis;
|
|
724
|
+
}
|
|
725
|
+
function registerSharedModules(modules) {
|
|
726
|
+
getSharedModuleRegistry().registerAll(modules);
|
|
727
|
+
}
|
|
728
|
+
function unregisterSharedModule(moduleId) {
|
|
729
|
+
return getSharedModuleRegistry().unregister(moduleId);
|
|
730
|
+
}
|
|
731
|
+
function clearSharedModules() {
|
|
732
|
+
getSharedModuleRegistry().clear();
|
|
733
|
+
}
|
|
734
|
+
function getSharedModuleRuntimeCode(moduleId) {
|
|
735
|
+
return `
|
|
736
|
+
(function() {
|
|
737
|
+
var registry = globalThis["${GLOBAL_KEY}"];
|
|
738
|
+
if (!registry) {
|
|
739
|
+
throw new Error(
|
|
740
|
+
'Sandlot SharedModuleRegistry not found. ' +
|
|
741
|
+
'Call registerSharedModules() in your host application before loading dynamic modules.'
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
return registry.get(${JSON.stringify(moduleId)});
|
|
745
|
+
})()
|
|
746
|
+
`.trim();
|
|
747
|
+
}
|
|
748
|
+
// src/commands.ts
|
|
749
|
+
import { defineCommand } from "just-bash/browser";
|
|
750
|
+
|
|
751
|
+
// src/typechecker.ts
|
|
752
|
+
import ts from "typescript";
|
|
753
|
+
function categoryToString(category) {
|
|
754
|
+
switch (category) {
|
|
755
|
+
case ts.DiagnosticCategory.Error:
|
|
756
|
+
return "error";
|
|
757
|
+
case ts.DiagnosticCategory.Warning:
|
|
758
|
+
return "warning";
|
|
759
|
+
case ts.DiagnosticCategory.Suggestion:
|
|
760
|
+
return "suggestion";
|
|
761
|
+
case ts.DiagnosticCategory.Message:
|
|
762
|
+
return "message";
|
|
763
|
+
default:
|
|
764
|
+
return "error";
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
function convertDiagnostic(diag) {
|
|
768
|
+
let file = null;
|
|
769
|
+
let line = null;
|
|
770
|
+
let column = null;
|
|
771
|
+
if (diag.file && diag.start !== undefined) {
|
|
772
|
+
file = diag.file.fileName;
|
|
773
|
+
const pos = diag.file.getLineAndCharacterOfPosition(diag.start);
|
|
774
|
+
line = pos.line + 1;
|
|
775
|
+
column = pos.character + 1;
|
|
776
|
+
}
|
|
777
|
+
return {
|
|
778
|
+
file,
|
|
779
|
+
line,
|
|
780
|
+
column,
|
|
781
|
+
code: diag.code,
|
|
782
|
+
category: categoryToString(diag.category),
|
|
783
|
+
message: ts.flattenDiagnosticMessageText(diag.messageText, `
|
|
784
|
+
`)
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
function normalizePath(path) {
|
|
788
|
+
if (!path.startsWith("/")) {
|
|
789
|
+
return "/" + path;
|
|
790
|
+
}
|
|
791
|
+
return path;
|
|
792
|
+
}
|
|
793
|
+
async function preloadFiles(fs) {
|
|
794
|
+
const cache = new Map;
|
|
795
|
+
const allPaths = fs.getAllPaths();
|
|
796
|
+
for (const path of allPaths) {
|
|
797
|
+
if (path.endsWith(".ts") || path.endsWith(".tsx") || path.endsWith(".js") || path.endsWith(".jsx") || path.endsWith(".json") || path.endsWith(".d.ts")) {
|
|
798
|
+
try {
|
|
799
|
+
const stat = await fs.stat(path);
|
|
800
|
+
if (stat.isFile) {
|
|
801
|
+
const content = await fs.readFile(path);
|
|
802
|
+
cache.set(path, content);
|
|
803
|
+
}
|
|
804
|
+
} catch {}
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
return cache;
|
|
808
|
+
}
|
|
809
|
+
function parseTsConfig(configText, _configPath) {
|
|
810
|
+
try {
|
|
811
|
+
const config = JSON.parse(configText);
|
|
812
|
+
const compilerOptions = config.compilerOptions || {};
|
|
813
|
+
const options = {
|
|
814
|
+
...getDefaultCompilerOptions()
|
|
815
|
+
};
|
|
816
|
+
if (compilerOptions.target) {
|
|
817
|
+
const targetMap = {
|
|
818
|
+
es5: ts.ScriptTarget.ES5,
|
|
819
|
+
es6: ts.ScriptTarget.ES2015,
|
|
820
|
+
es2015: ts.ScriptTarget.ES2015,
|
|
821
|
+
es2016: ts.ScriptTarget.ES2016,
|
|
822
|
+
es2017: ts.ScriptTarget.ES2017,
|
|
823
|
+
es2018: ts.ScriptTarget.ES2018,
|
|
824
|
+
es2019: ts.ScriptTarget.ES2019,
|
|
825
|
+
es2020: ts.ScriptTarget.ES2020,
|
|
826
|
+
es2021: ts.ScriptTarget.ES2021,
|
|
827
|
+
es2022: ts.ScriptTarget.ES2022,
|
|
828
|
+
esnext: ts.ScriptTarget.ESNext
|
|
829
|
+
};
|
|
830
|
+
options.target = targetMap[compilerOptions.target.toLowerCase()] ?? ts.ScriptTarget.ES2020;
|
|
831
|
+
}
|
|
832
|
+
if (compilerOptions.module) {
|
|
833
|
+
const moduleMap = {
|
|
834
|
+
commonjs: ts.ModuleKind.CommonJS,
|
|
835
|
+
amd: ts.ModuleKind.AMD,
|
|
836
|
+
umd: ts.ModuleKind.UMD,
|
|
837
|
+
system: ts.ModuleKind.System,
|
|
838
|
+
es6: ts.ModuleKind.ES2015,
|
|
839
|
+
es2015: ts.ModuleKind.ES2015,
|
|
840
|
+
es2020: ts.ModuleKind.ES2020,
|
|
841
|
+
es2022: ts.ModuleKind.ES2022,
|
|
842
|
+
esnext: ts.ModuleKind.ESNext,
|
|
843
|
+
node16: ts.ModuleKind.Node16,
|
|
844
|
+
nodenext: ts.ModuleKind.NodeNext
|
|
845
|
+
};
|
|
846
|
+
options.module = moduleMap[compilerOptions.module.toLowerCase()] ?? ts.ModuleKind.ESNext;
|
|
847
|
+
}
|
|
848
|
+
if (compilerOptions.moduleResolution) {
|
|
849
|
+
const resolutionMap = {
|
|
850
|
+
classic: ts.ModuleResolutionKind.Classic,
|
|
851
|
+
node: ts.ModuleResolutionKind.NodeJs,
|
|
852
|
+
node10: ts.ModuleResolutionKind.NodeJs,
|
|
853
|
+
node16: ts.ModuleResolutionKind.Node16,
|
|
854
|
+
nodenext: ts.ModuleResolutionKind.NodeNext,
|
|
855
|
+
bundler: ts.ModuleResolutionKind.Bundler
|
|
856
|
+
};
|
|
857
|
+
options.moduleResolution = resolutionMap[compilerOptions.moduleResolution.toLowerCase()] ?? ts.ModuleResolutionKind.Bundler;
|
|
858
|
+
}
|
|
859
|
+
if (compilerOptions.jsx) {
|
|
860
|
+
const jsxMap = {
|
|
861
|
+
preserve: ts.JsxEmit.Preserve,
|
|
862
|
+
react: ts.JsxEmit.React,
|
|
863
|
+
"react-jsx": ts.JsxEmit.ReactJSX,
|
|
864
|
+
"react-jsxdev": ts.JsxEmit.ReactJSXDev,
|
|
865
|
+
"react-native": ts.JsxEmit.ReactNative
|
|
866
|
+
};
|
|
867
|
+
options.jsx = jsxMap[compilerOptions.jsx.toLowerCase()] ?? ts.JsxEmit.ReactJSX;
|
|
868
|
+
}
|
|
869
|
+
if (compilerOptions.strict !== undefined)
|
|
870
|
+
options.strict = compilerOptions.strict;
|
|
871
|
+
if (compilerOptions.esModuleInterop !== undefined)
|
|
872
|
+
options.esModuleInterop = compilerOptions.esModuleInterop;
|
|
873
|
+
if (compilerOptions.skipLibCheck !== undefined)
|
|
874
|
+
options.skipLibCheck = compilerOptions.skipLibCheck;
|
|
875
|
+
if (compilerOptions.allowJs !== undefined)
|
|
876
|
+
options.allowJs = compilerOptions.allowJs;
|
|
877
|
+
if (compilerOptions.resolveJsonModule !== undefined)
|
|
878
|
+
options.resolveJsonModule = compilerOptions.resolveJsonModule;
|
|
879
|
+
if (compilerOptions.noImplicitAny !== undefined)
|
|
880
|
+
options.noImplicitAny = compilerOptions.noImplicitAny;
|
|
881
|
+
if (compilerOptions.strictNullChecks !== undefined)
|
|
882
|
+
options.strictNullChecks = compilerOptions.strictNullChecks;
|
|
883
|
+
if (Array.isArray(compilerOptions.lib)) {
|
|
884
|
+
options.lib = compilerOptions.lib.map((lib) => lib.toLowerCase().startsWith("lib.") ? lib : `lib.${lib.toLowerCase()}.d.ts`);
|
|
885
|
+
}
|
|
886
|
+
options.noEmit = true;
|
|
887
|
+
return options;
|
|
888
|
+
} catch (err) {
|
|
889
|
+
console.warn("Error parsing tsconfig.json:", err);
|
|
890
|
+
return getDefaultCompilerOptions();
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
function getDefaultCompilerOptions() {
|
|
894
|
+
return {
|
|
895
|
+
target: ts.ScriptTarget.ES2020,
|
|
896
|
+
module: ts.ModuleKind.ESNext,
|
|
897
|
+
moduleResolution: ts.ModuleResolutionKind.Bundler,
|
|
898
|
+
esModuleInterop: true,
|
|
899
|
+
strict: true,
|
|
900
|
+
skipLibCheck: true,
|
|
901
|
+
noEmit: true,
|
|
902
|
+
jsx: ts.JsxEmit.ReactJSX,
|
|
903
|
+
allowJs: true,
|
|
904
|
+
resolveJsonModule: true,
|
|
905
|
+
noLib: false,
|
|
906
|
+
lib: [
|
|
907
|
+
"lib.es2020.d.ts",
|
|
908
|
+
"lib.dom.d.ts",
|
|
909
|
+
"lib.dom.iterable.d.ts"
|
|
910
|
+
]
|
|
911
|
+
};
|
|
912
|
+
}
|
|
913
|
+
var LIB_PATH_PREFIX = "/node_modules/typescript/lib/";
|
|
914
|
+
function createVfsCompilerHost(fileCache, libFiles, _options) {
|
|
915
|
+
function getLibContent(fileName) {
|
|
916
|
+
const libMatch = fileName.match(/lib\.([^/]+)\.d\.ts$/);
|
|
917
|
+
if (libMatch && libMatch[1]) {
|
|
918
|
+
return libFiles.get(libMatch[1]);
|
|
919
|
+
}
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
return {
|
|
923
|
+
getSourceFile(fileName, languageVersion, onError) {
|
|
924
|
+
const normalizedPath = normalizePath(fileName);
|
|
925
|
+
const content = fileCache.get(normalizedPath);
|
|
926
|
+
if (content !== undefined) {
|
|
927
|
+
return ts.createSourceFile(normalizedPath, content, languageVersion, true);
|
|
928
|
+
}
|
|
929
|
+
const altContent = fileCache.get(fileName);
|
|
930
|
+
if (altContent !== undefined) {
|
|
931
|
+
return ts.createSourceFile(fileName, altContent, languageVersion, true);
|
|
932
|
+
}
|
|
933
|
+
const libContent = getLibContent(fileName);
|
|
934
|
+
if (libContent !== undefined) {
|
|
935
|
+
return ts.createSourceFile(fileName, libContent, languageVersion, true);
|
|
936
|
+
}
|
|
937
|
+
if (onError) {
|
|
938
|
+
onError(`File not found: ${fileName}`);
|
|
939
|
+
}
|
|
940
|
+
return;
|
|
941
|
+
},
|
|
942
|
+
getDefaultLibFileName(options) {
|
|
943
|
+
return LIB_PATH_PREFIX + ts.getDefaultLibFileName(options);
|
|
944
|
+
},
|
|
945
|
+
writeFile() {},
|
|
946
|
+
getCurrentDirectory() {
|
|
947
|
+
return "/";
|
|
948
|
+
},
|
|
949
|
+
getCanonicalFileName(fileName) {
|
|
950
|
+
return fileName;
|
|
951
|
+
},
|
|
952
|
+
useCaseSensitiveFileNames() {
|
|
953
|
+
return true;
|
|
954
|
+
},
|
|
955
|
+
getNewLine() {
|
|
956
|
+
return `
|
|
957
|
+
`;
|
|
958
|
+
},
|
|
959
|
+
fileExists(fileName) {
|
|
960
|
+
const normalizedPath = normalizePath(fileName);
|
|
961
|
+
if (fileCache.has(normalizedPath) || fileCache.has(fileName)) {
|
|
962
|
+
return true;
|
|
963
|
+
}
|
|
964
|
+
return getLibContent(fileName) !== undefined;
|
|
965
|
+
},
|
|
966
|
+
readFile(fileName) {
|
|
967
|
+
const normalizedPath = normalizePath(fileName);
|
|
968
|
+
const content = fileCache.get(normalizedPath) ?? fileCache.get(fileName);
|
|
969
|
+
if (content !== undefined) {
|
|
970
|
+
return content;
|
|
971
|
+
}
|
|
972
|
+
return getLibContent(fileName);
|
|
973
|
+
},
|
|
974
|
+
directoryExists(directoryName) {
|
|
975
|
+
const normalizedDir = normalizePath(directoryName);
|
|
976
|
+
for (const path of fileCache.keys()) {
|
|
977
|
+
if (path.startsWith(normalizedDir + "/")) {
|
|
978
|
+
return true;
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
if (normalizedDir === "/node_modules/typescript/lib" || normalizedDir === "/node_modules/typescript") {
|
|
982
|
+
return libFiles.size > 0;
|
|
983
|
+
}
|
|
984
|
+
if (normalizedDir === "/node_modules") {
|
|
985
|
+
return libFiles.size > 0 || Array.from(fileCache.keys()).some((p) => p.startsWith("/node_modules/"));
|
|
986
|
+
}
|
|
987
|
+
if (normalizedDir === "/node_modules/@types") {
|
|
988
|
+
return Array.from(fileCache.keys()).some((p) => p.startsWith("/node_modules/@types/"));
|
|
989
|
+
}
|
|
990
|
+
return false;
|
|
991
|
+
},
|
|
992
|
+
getDirectories(path) {
|
|
993
|
+
const normalizedPath = normalizePath(path);
|
|
994
|
+
const prefix = normalizedPath === "/" ? "/" : normalizedPath + "/";
|
|
995
|
+
const dirs = new Set;
|
|
996
|
+
for (const filePath of fileCache.keys()) {
|
|
997
|
+
if (filePath.startsWith(prefix)) {
|
|
998
|
+
const relative = filePath.slice(prefix.length);
|
|
999
|
+
const firstSlash = relative.indexOf("/");
|
|
1000
|
+
if (firstSlash > 0) {
|
|
1001
|
+
dirs.add(relative.slice(0, firstSlash));
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
if (normalizedPath === "/") {
|
|
1006
|
+
const hasNodeModules = libFiles.size > 0 || Array.from(fileCache.keys()).some((p) => p.startsWith("/node_modules/"));
|
|
1007
|
+
if (hasNodeModules) {
|
|
1008
|
+
dirs.add("node_modules");
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
return Array.from(dirs);
|
|
1012
|
+
},
|
|
1013
|
+
realpath(path) {
|
|
1014
|
+
return path;
|
|
1015
|
+
},
|
|
1016
|
+
getEnvironmentVariable() {
|
|
1017
|
+
return;
|
|
1018
|
+
}
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
async function typecheck(options) {
|
|
1022
|
+
const {
|
|
1023
|
+
fs,
|
|
1024
|
+
entryPoint,
|
|
1025
|
+
tsconfigPath = "/tsconfig.json",
|
|
1026
|
+
libFiles = new Map
|
|
1027
|
+
} = options;
|
|
1028
|
+
const normalizedEntry = normalizePath(entryPoint);
|
|
1029
|
+
if (!await fs.exists(normalizedEntry)) {
|
|
1030
|
+
throw new Error(`Entry point not found: ${normalizedEntry}`);
|
|
1031
|
+
}
|
|
1032
|
+
const fileCache = await preloadFiles(fs);
|
|
1033
|
+
let compilerOptions = getDefaultCompilerOptions();
|
|
1034
|
+
const tsconfigContent = fileCache.get(normalizePath(tsconfigPath));
|
|
1035
|
+
if (tsconfigContent) {
|
|
1036
|
+
compilerOptions = {
|
|
1037
|
+
...parseTsConfig(tsconfigContent, tsconfigPath),
|
|
1038
|
+
noEmit: true
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
const host = createVfsCompilerHost(fileCache, libFiles, compilerOptions);
|
|
1042
|
+
const program = ts.createProgram([normalizedEntry], compilerOptions, host);
|
|
1043
|
+
const allDiagnostics = [
|
|
1044
|
+
...program.getSyntacticDiagnostics(),
|
|
1045
|
+
...program.getSemanticDiagnostics(),
|
|
1046
|
+
...program.getDeclarationDiagnostics()
|
|
1047
|
+
];
|
|
1048
|
+
const diagnostics = allDiagnostics.map(convertDiagnostic);
|
|
1049
|
+
const checkedFiles = program.getSourceFiles().map((sf) => sf.fileName).filter((f) => !f.includes("node_modules/typescript/lib"));
|
|
1050
|
+
const hasErrors = diagnostics.some((d) => d.category === "error");
|
|
1051
|
+
return {
|
|
1052
|
+
diagnostics,
|
|
1053
|
+
hasErrors,
|
|
1054
|
+
checkedFiles
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
function formatDiagnostics(diagnostics) {
|
|
1058
|
+
return diagnostics.map((d) => {
|
|
1059
|
+
const location = d.file ? `${d.file}${d.line ? `:${d.line}` : ""}${d.column ? `:${d.column}` : ""}` : "(global)";
|
|
1060
|
+
return `${location} - ${d.category} TS${d.code}: ${d.message}`;
|
|
1061
|
+
}).join(`
|
|
1062
|
+
`);
|
|
1063
|
+
}
|
|
1064
|
+
function formatDiagnosticsForAgent(diagnostics) {
|
|
1065
|
+
if (diagnostics.length === 0) {
|
|
1066
|
+
return "No type errors found.";
|
|
1067
|
+
}
|
|
1068
|
+
const errorCount = diagnostics.filter((d) => d.category === "error").length;
|
|
1069
|
+
const warningCount = diagnostics.filter((d) => d.category === "warning").length;
|
|
1070
|
+
const summary = errorCount > 0 || warningCount > 0 ? `Found ${errorCount} error(s) and ${warningCount} warning(s):
|
|
1071
|
+
|
|
1072
|
+
` : "";
|
|
1073
|
+
const formatted = diagnostics.map((d) => {
|
|
1074
|
+
const location = d.file ? `${d.file}${d.line ? ` at line ${d.line}` : ""}` : "Global";
|
|
1075
|
+
const prefix = d.category === "error" ? "Error" : d.category === "warning" ? "Warning" : "Info";
|
|
1076
|
+
return `${prefix} in ${location}: ${d.message} (TS${d.code})`;
|
|
1077
|
+
}).join(`
|
|
1078
|
+
|
|
1079
|
+
`);
|
|
1080
|
+
return summary + formatted;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
// src/bundler.ts
|
|
1084
|
+
import * as esbuild from "esbuild-wasm";
|
|
1085
|
+
var initialized = false;
|
|
1086
|
+
var initPromise = null;
|
|
1087
|
+
function getWasmUrl() {
|
|
1088
|
+
const version = "0.27.2";
|
|
1089
|
+
return `https://unpkg.com/esbuild-wasm@${version}/esbuild.wasm`;
|
|
1090
|
+
}
|
|
1091
|
+
async function initBundler() {
|
|
1092
|
+
if (initialized)
|
|
1093
|
+
return;
|
|
1094
|
+
if (initPromise) {
|
|
1095
|
+
await initPromise;
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
initPromise = esbuild.initialize({
|
|
1099
|
+
wasmURL: getWasmUrl()
|
|
1100
|
+
});
|
|
1101
|
+
await initPromise;
|
|
1102
|
+
initialized = true;
|
|
1103
|
+
}
|
|
1104
|
+
function isBareImport(path) {
|
|
1105
|
+
return !path.startsWith(".") && !path.startsWith("/");
|
|
1106
|
+
}
|
|
1107
|
+
function getLoader(path) {
|
|
1108
|
+
const ext = path.split(".").pop()?.toLowerCase();
|
|
1109
|
+
switch (ext) {
|
|
1110
|
+
case "ts":
|
|
1111
|
+
return "ts";
|
|
1112
|
+
case "tsx":
|
|
1113
|
+
return "tsx";
|
|
1114
|
+
case "jsx":
|
|
1115
|
+
return "jsx";
|
|
1116
|
+
case "js":
|
|
1117
|
+
case "mjs":
|
|
1118
|
+
return "js";
|
|
1119
|
+
case "json":
|
|
1120
|
+
return "json";
|
|
1121
|
+
case "css":
|
|
1122
|
+
return "css";
|
|
1123
|
+
case "txt":
|
|
1124
|
+
return "text";
|
|
1125
|
+
default:
|
|
1126
|
+
return "js";
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
function matchesSharedModule(importPath, sharedModuleIds) {
|
|
1130
|
+
if (sharedModuleIds.has(importPath)) {
|
|
1131
|
+
return importPath;
|
|
1132
|
+
}
|
|
1133
|
+
for (const moduleId of sharedModuleIds) {
|
|
1134
|
+
if (importPath === moduleId || importPath.startsWith(moduleId + "/")) {
|
|
1135
|
+
return importPath;
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
return null;
|
|
1139
|
+
}
|
|
1140
|
+
function createVfsPlugin(options) {
|
|
1141
|
+
const {
|
|
1142
|
+
fs,
|
|
1143
|
+
entryPoint,
|
|
1144
|
+
npmImports,
|
|
1145
|
+
installedPackages,
|
|
1146
|
+
includedFiles,
|
|
1147
|
+
sharedModuleIds
|
|
1148
|
+
} = options;
|
|
1149
|
+
return {
|
|
1150
|
+
name: "virtual-fs",
|
|
1151
|
+
setup(build2) {
|
|
1152
|
+
build2.onResolve({ filter: /.*/ }, async (args) => {
|
|
1153
|
+
if (args.kind === "entry-point") {
|
|
1154
|
+
return { path: entryPoint, namespace: "vfs" };
|
|
1155
|
+
}
|
|
1156
|
+
if (isBareImport(args.path)) {
|
|
1157
|
+
const sharedMatch = matchesSharedModule(args.path, sharedModuleIds);
|
|
1158
|
+
if (sharedMatch) {
|
|
1159
|
+
return {
|
|
1160
|
+
path: sharedMatch,
|
|
1161
|
+
namespace: "sandlot-shared"
|
|
1162
|
+
};
|
|
1163
|
+
}
|
|
1164
|
+
switch (npmImports) {
|
|
1165
|
+
case "cdn": {
|
|
1166
|
+
const esmUrl = resolveToEsmUrl(args.path, installedPackages);
|
|
1167
|
+
if (esmUrl) {
|
|
1168
|
+
return { path: esmUrl, external: true };
|
|
1169
|
+
}
|
|
1170
|
+
return { path: args.path, external: true };
|
|
1171
|
+
}
|
|
1172
|
+
case "external":
|
|
1173
|
+
return { path: args.path, external: true };
|
|
1174
|
+
case "bundle": {
|
|
1175
|
+
const resolved2 = fs.resolvePath(args.resolveDir, `node_modules/${args.path}`);
|
|
1176
|
+
const exists = await fs.exists(resolved2);
|
|
1177
|
+
if (exists) {
|
|
1178
|
+
return { path: resolved2, namespace: "vfs" };
|
|
1179
|
+
}
|
|
1180
|
+
return { path: args.path, external: true };
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
const resolved = fs.resolvePath(args.resolveDir, args.path);
|
|
1185
|
+
const extensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".json"];
|
|
1186
|
+
const hasExtension = extensions.some((ext) => resolved.endsWith(ext));
|
|
1187
|
+
if (hasExtension) {
|
|
1188
|
+
const exists = await fs.exists(resolved);
|
|
1189
|
+
if (exists) {
|
|
1190
|
+
return { path: resolved, namespace: "vfs" };
|
|
1191
|
+
}
|
|
1192
|
+
return { errors: [{ text: `File not found: ${resolved}` }] };
|
|
1193
|
+
}
|
|
1194
|
+
for (const ext of extensions) {
|
|
1195
|
+
const withExt = resolved + ext;
|
|
1196
|
+
if (await fs.exists(withExt)) {
|
|
1197
|
+
return { path: withExt, namespace: "vfs" };
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
for (const ext of extensions) {
|
|
1201
|
+
const indexPath = `${resolved}/index${ext}`;
|
|
1202
|
+
if (await fs.exists(indexPath)) {
|
|
1203
|
+
return { path: indexPath, namespace: "vfs" };
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
return { errors: [{ text: `Cannot resolve: ${args.path} from ${args.resolveDir}` }] };
|
|
1207
|
+
});
|
|
1208
|
+
build2.onLoad({ filter: /.*/, namespace: "sandlot-shared" }, (args) => {
|
|
1209
|
+
const contents = `export default ${getSharedModuleRuntimeCode(args.path)};
|
|
1210
|
+
export * from ${JSON.stringify(args.path)};
|
|
1211
|
+
// Re-export all named exports by importing from registry
|
|
1212
|
+
const __mod__ = ${getSharedModuleRuntimeCode(args.path)};
|
|
1213
|
+
for (const __k__ in __mod__) {
|
|
1214
|
+
if (__k__ !== 'default') Object.defineProperty(exports, __k__, {
|
|
1215
|
+
enumerable: true,
|
|
1216
|
+
get: function() { return __mod__[__k__]; }
|
|
1217
|
+
});
|
|
1218
|
+
}`;
|
|
1219
|
+
const esmContents = `
|
|
1220
|
+
const __sandlot_mod__ = ${getSharedModuleRuntimeCode(args.path)};
|
|
1221
|
+
export default __sandlot_mod__.default ?? __sandlot_mod__;
|
|
1222
|
+
${generateNamedExports(args.path)}
|
|
1223
|
+
`;
|
|
1224
|
+
return {
|
|
1225
|
+
contents: esmContents.trim(),
|
|
1226
|
+
loader: "js"
|
|
1227
|
+
};
|
|
1228
|
+
});
|
|
1229
|
+
build2.onLoad({ filter: /.*/, namespace: "vfs" }, async (args) => {
|
|
1230
|
+
try {
|
|
1231
|
+
const contents = await fs.readFile(args.path);
|
|
1232
|
+
includedFiles.add(args.path);
|
|
1233
|
+
return {
|
|
1234
|
+
contents,
|
|
1235
|
+
loader: getLoader(args.path),
|
|
1236
|
+
resolveDir: args.path.substring(0, args.path.lastIndexOf("/"))
|
|
1237
|
+
};
|
|
1238
|
+
} catch (err) {
|
|
1239
|
+
return {
|
|
1240
|
+
errors: [{ text: `Failed to read ${args.path}: ${err}` }]
|
|
1241
|
+
};
|
|
1242
|
+
}
|
|
1243
|
+
});
|
|
1244
|
+
}
|
|
1245
|
+
};
|
|
1246
|
+
}
|
|
1247
|
+
function generateNamedExports(moduleId) {
|
|
1248
|
+
const knownExports = {
|
|
1249
|
+
react: [
|
|
1250
|
+
"useState",
|
|
1251
|
+
"useEffect",
|
|
1252
|
+
"useContext",
|
|
1253
|
+
"useReducer",
|
|
1254
|
+
"useCallback",
|
|
1255
|
+
"useMemo",
|
|
1256
|
+
"useRef",
|
|
1257
|
+
"useImperativeHandle",
|
|
1258
|
+
"useLayoutEffect",
|
|
1259
|
+
"useDebugValue",
|
|
1260
|
+
"useDeferredValue",
|
|
1261
|
+
"useTransition",
|
|
1262
|
+
"useId",
|
|
1263
|
+
"useSyncExternalStore",
|
|
1264
|
+
"useInsertionEffect",
|
|
1265
|
+
"useOptimistic",
|
|
1266
|
+
"useActionState",
|
|
1267
|
+
"createElement",
|
|
1268
|
+
"cloneElement",
|
|
1269
|
+
"createContext",
|
|
1270
|
+
"forwardRef",
|
|
1271
|
+
"lazy",
|
|
1272
|
+
"memo",
|
|
1273
|
+
"startTransition",
|
|
1274
|
+
"Children",
|
|
1275
|
+
"Component",
|
|
1276
|
+
"PureComponent",
|
|
1277
|
+
"Fragment",
|
|
1278
|
+
"Profiler",
|
|
1279
|
+
"StrictMode",
|
|
1280
|
+
"Suspense",
|
|
1281
|
+
"version",
|
|
1282
|
+
"isValidElement"
|
|
1283
|
+
],
|
|
1284
|
+
"react-dom": ["createPortal", "flushSync", "version"],
|
|
1285
|
+
"react-dom/client": ["createRoot", "hydrateRoot"],
|
|
1286
|
+
"react-dom/server": ["renderToString", "renderToStaticMarkup", "renderToPipeableStream"]
|
|
1287
|
+
};
|
|
1288
|
+
const exports = knownExports[moduleId];
|
|
1289
|
+
if (!exports) {
|
|
1290
|
+
return `
|
|
1291
|
+
// Dynamic re-export for unknown module
|
|
1292
|
+
export const __moduleProxy__ = __sandlot_mod__;
|
|
1293
|
+
`;
|
|
1294
|
+
}
|
|
1295
|
+
return exports.map((name) => `export const ${name} = __sandlot_mod__.${name};`).join(`
|
|
1296
|
+
`);
|
|
1297
|
+
}
|
|
1298
|
+
async function bundle(options) {
|
|
1299
|
+
await initBundler();
|
|
1300
|
+
const {
|
|
1301
|
+
fs,
|
|
1302
|
+
entryPoint,
|
|
1303
|
+
external = [],
|
|
1304
|
+
npmImports = "cdn",
|
|
1305
|
+
sharedModules = [],
|
|
1306
|
+
format = "esm",
|
|
1307
|
+
minify = false,
|
|
1308
|
+
sourcemap = false,
|
|
1309
|
+
globalName,
|
|
1310
|
+
target = ["es2020"]
|
|
1311
|
+
} = options;
|
|
1312
|
+
const normalizedEntry = entryPoint.startsWith("/") ? entryPoint : `/${entryPoint}`;
|
|
1313
|
+
if (!await fs.exists(normalizedEntry)) {
|
|
1314
|
+
throw new Error(`Entry point not found: ${normalizedEntry}`);
|
|
1315
|
+
}
|
|
1316
|
+
const manifest = await getPackageManifest(fs);
|
|
1317
|
+
const installedPackages = manifest.dependencies;
|
|
1318
|
+
const sharedModuleIds = new Set(sharedModules);
|
|
1319
|
+
const includedFiles = new Set;
|
|
1320
|
+
const plugin = createVfsPlugin({
|
|
1321
|
+
fs,
|
|
1322
|
+
entryPoint: normalizedEntry,
|
|
1323
|
+
npmImports,
|
|
1324
|
+
installedPackages,
|
|
1325
|
+
includedFiles,
|
|
1326
|
+
sharedModuleIds
|
|
1327
|
+
});
|
|
1328
|
+
const result = await esbuild.build({
|
|
1329
|
+
entryPoints: [normalizedEntry],
|
|
1330
|
+
bundle: true,
|
|
1331
|
+
write: false,
|
|
1332
|
+
format,
|
|
1333
|
+
minify,
|
|
1334
|
+
sourcemap: sourcemap ? "inline" : false,
|
|
1335
|
+
globalName,
|
|
1336
|
+
target,
|
|
1337
|
+
external,
|
|
1338
|
+
plugins: [plugin]
|
|
1339
|
+
});
|
|
1340
|
+
const code = result.outputFiles?.[0]?.text ?? "";
|
|
1341
|
+
return {
|
|
1342
|
+
code,
|
|
1343
|
+
warnings: result.warnings,
|
|
1344
|
+
includedFiles: Array.from(includedFiles)
|
|
1345
|
+
};
|
|
1346
|
+
}
|
|
1347
|
+
async function bundleToUrl(options) {
|
|
1348
|
+
const result = await bundle(options);
|
|
1349
|
+
const blob = new Blob([result.code], { type: "application/javascript" });
|
|
1350
|
+
return URL.createObjectURL(blob);
|
|
1351
|
+
}
|
|
1352
|
+
async function bundleAndImport(options) {
|
|
1353
|
+
const url = await bundleToUrl(options);
|
|
1354
|
+
try {
|
|
1355
|
+
return await import(url);
|
|
1356
|
+
} finally {
|
|
1357
|
+
URL.revokeObjectURL(url);
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
// src/commands.ts
|
|
1362
|
+
function formatEsbuildMessages(messages) {
|
|
1363
|
+
if (messages.length === 0)
|
|
1364
|
+
return "";
|
|
1365
|
+
return messages.map((msg) => {
|
|
1366
|
+
if (msg.location) {
|
|
1367
|
+
const { file, line, column } = msg.location;
|
|
1368
|
+
const loc = file ? `${file}${line ? `:${line}` : ""}${column ? `:${column}` : ""}` : "";
|
|
1369
|
+
return loc ? `${loc}: ${msg.text}` : msg.text;
|
|
1370
|
+
}
|
|
1371
|
+
return msg.text;
|
|
1372
|
+
}).join(`
|
|
1373
|
+
`);
|
|
1374
|
+
}
|
|
1375
|
+
function createTscCommand(deps) {
|
|
1376
|
+
const { fs, libFiles, tsconfigPath } = deps;
|
|
1377
|
+
return defineCommand("tsc", async (args, _ctx) => {
|
|
1378
|
+
const entryPoint = args[0];
|
|
1379
|
+
if (!entryPoint) {
|
|
1380
|
+
return {
|
|
1381
|
+
stdout: "",
|
|
1382
|
+
stderr: `Usage: tsc <entry-point>
|
|
1383
|
+
|
|
1384
|
+
Example: tsc /src/index.ts
|
|
1385
|
+
`,
|
|
1386
|
+
exitCode: 1
|
|
1387
|
+
};
|
|
1388
|
+
}
|
|
1389
|
+
try {
|
|
1390
|
+
if (!await fs.exists(entryPoint)) {
|
|
1391
|
+
return {
|
|
1392
|
+
stdout: "",
|
|
1393
|
+
stderr: `Error: Entry point not found: ${entryPoint}
|
|
1394
|
+
`,
|
|
1395
|
+
exitCode: 1
|
|
1396
|
+
};
|
|
1397
|
+
}
|
|
1398
|
+
const result = await typecheck({
|
|
1399
|
+
fs,
|
|
1400
|
+
entryPoint,
|
|
1401
|
+
tsconfigPath,
|
|
1402
|
+
libFiles
|
|
1403
|
+
});
|
|
1404
|
+
if (result.hasErrors) {
|
|
1405
|
+
const formatted = formatDiagnosticsForAgent(result.diagnostics);
|
|
1406
|
+
return {
|
|
1407
|
+
stdout: "",
|
|
1408
|
+
stderr: formatted + `
|
|
1409
|
+
`,
|
|
1410
|
+
exitCode: 1
|
|
1411
|
+
};
|
|
1412
|
+
}
|
|
1413
|
+
const checkedCount = result.checkedFiles.length;
|
|
1414
|
+
const warningCount = result.diagnostics.filter((d) => d.category === "warning").length;
|
|
1415
|
+
let output = `Type check passed. Checked ${checkedCount} file(s).
|
|
1416
|
+
`;
|
|
1417
|
+
if (warningCount > 0) {
|
|
1418
|
+
output += `
|
|
1419
|
+
Warnings:
|
|
1420
|
+
${formatDiagnosticsForAgent(result.diagnostics.filter((d) => d.category === "warning"))}
|
|
1421
|
+
`;
|
|
1422
|
+
}
|
|
1423
|
+
return {
|
|
1424
|
+
stdout: output,
|
|
1425
|
+
stderr: "",
|
|
1426
|
+
exitCode: 0
|
|
1427
|
+
};
|
|
1428
|
+
} catch (err) {
|
|
1429
|
+
return {
|
|
1430
|
+
stdout: "",
|
|
1431
|
+
stderr: `Type check failed: ${err instanceof Error ? err.message : String(err)}
|
|
1432
|
+
`,
|
|
1433
|
+
exitCode: 1
|
|
1434
|
+
};
|
|
1435
|
+
}
|
|
1436
|
+
});
|
|
1437
|
+
}
|
|
1438
|
+
function createBuildCommand(deps) {
|
|
1439
|
+
const { fs, libFiles, tsconfigPath, onBuild, sharedModules } = deps;
|
|
1440
|
+
return defineCommand("build", async (args, _ctx) => {
|
|
1441
|
+
let entryPoint = null;
|
|
1442
|
+
let skipTypecheck = false;
|
|
1443
|
+
let minify = false;
|
|
1444
|
+
let format = "esm";
|
|
1445
|
+
for (let i = 0;i < args.length; i++) {
|
|
1446
|
+
const arg = args[i];
|
|
1447
|
+
if (arg === "--skip-typecheck" || arg === "-s") {
|
|
1448
|
+
skipTypecheck = true;
|
|
1449
|
+
} else if (arg === "--minify" || arg === "-m") {
|
|
1450
|
+
minify = true;
|
|
1451
|
+
} else if ((arg === "--format" || arg === "-f") && args[i + 1]) {
|
|
1452
|
+
const f = args[++i].toLowerCase();
|
|
1453
|
+
if (f === "esm" || f === "iife" || f === "cjs") {
|
|
1454
|
+
format = f;
|
|
1455
|
+
}
|
|
1456
|
+
} else if (!arg.startsWith("-")) {
|
|
1457
|
+
entryPoint = arg;
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
if (!entryPoint) {
|
|
1461
|
+
return {
|
|
1462
|
+
stdout: "",
|
|
1463
|
+
stderr: `Usage: build <entry-point> [options]
|
|
1464
|
+
|
|
1465
|
+
Options:
|
|
1466
|
+
--skip-typecheck, -s Skip type checking
|
|
1467
|
+
--minify, -m Minify output
|
|
1468
|
+
--format, -f <fmt> Output format (esm|iife|cjs)
|
|
1469
|
+
|
|
1470
|
+
Example: build /src/index.ts
|
|
1471
|
+
`,
|
|
1472
|
+
exitCode: 1
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
try {
|
|
1476
|
+
if (!await fs.exists(entryPoint)) {
|
|
1477
|
+
return {
|
|
1478
|
+
stdout: "",
|
|
1479
|
+
stderr: `Error: Entry point not found: ${entryPoint}
|
|
1480
|
+
`,
|
|
1481
|
+
exitCode: 1
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1484
|
+
let typecheckResult = null;
|
|
1485
|
+
if (!skipTypecheck) {
|
|
1486
|
+
typecheckResult = await typecheck({
|
|
1487
|
+
fs,
|
|
1488
|
+
entryPoint,
|
|
1489
|
+
tsconfigPath,
|
|
1490
|
+
libFiles
|
|
1491
|
+
});
|
|
1492
|
+
if (typecheckResult.hasErrors) {
|
|
1493
|
+
const formatted = formatDiagnosticsForAgent(typecheckResult.diagnostics);
|
|
1494
|
+
return {
|
|
1495
|
+
stdout: "",
|
|
1496
|
+
stderr: `Build failed: Type errors found.
|
|
1497
|
+
|
|
1498
|
+
${formatted}
|
|
1499
|
+
`,
|
|
1500
|
+
exitCode: 1
|
|
1501
|
+
};
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
const bundleResult = await bundle({
|
|
1505
|
+
fs,
|
|
1506
|
+
entryPoint,
|
|
1507
|
+
format,
|
|
1508
|
+
minify,
|
|
1509
|
+
sharedModules
|
|
1510
|
+
});
|
|
1511
|
+
if (onBuild) {
|
|
1512
|
+
await onBuild(bundleResult);
|
|
1513
|
+
}
|
|
1514
|
+
let output = `Build successful!
|
|
1515
|
+
`;
|
|
1516
|
+
output += `Entry: ${entryPoint}
|
|
1517
|
+
`;
|
|
1518
|
+
output += `Format: ${format}
|
|
1519
|
+
`;
|
|
1520
|
+
output += `Size: ${(bundleResult.code.length / 1024).toFixed(2)} KB
|
|
1521
|
+
`;
|
|
1522
|
+
if (typecheckResult) {
|
|
1523
|
+
output += `Type checked: ${typecheckResult.checkedFiles.length} file(s)
|
|
1524
|
+
`;
|
|
1525
|
+
}
|
|
1526
|
+
output += `Bundled: ${bundleResult.includedFiles.length} file(s)
|
|
1527
|
+
`;
|
|
1528
|
+
if (bundleResult.warnings.length > 0) {
|
|
1529
|
+
output += `
|
|
1530
|
+
Build warnings:
|
|
1531
|
+
${formatEsbuildMessages(bundleResult.warnings)}
|
|
1532
|
+
`;
|
|
1533
|
+
}
|
|
1534
|
+
if (typecheckResult) {
|
|
1535
|
+
const warnings = typecheckResult.diagnostics.filter((d) => d.category === "warning");
|
|
1536
|
+
if (warnings.length > 0) {
|
|
1537
|
+
output += `
|
|
1538
|
+
Type warnings:
|
|
1539
|
+
${formatDiagnosticsForAgent(warnings)}
|
|
1540
|
+
`;
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
return {
|
|
1544
|
+
stdout: output,
|
|
1545
|
+
stderr: "",
|
|
1546
|
+
exitCode: 0
|
|
1547
|
+
};
|
|
1548
|
+
} catch (err) {
|
|
1549
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
1550
|
+
return {
|
|
1551
|
+
stdout: "",
|
|
1552
|
+
stderr: `Build failed: ${errorMessage}
|
|
1553
|
+
`,
|
|
1554
|
+
exitCode: 1
|
|
1555
|
+
};
|
|
1556
|
+
}
|
|
1557
|
+
});
|
|
1558
|
+
}
|
|
1559
|
+
function createInstallCommand(deps) {
|
|
1560
|
+
const { fs, typesCache } = deps;
|
|
1561
|
+
return defineCommand("install", async (args, _ctx) => {
|
|
1562
|
+
if (args.length === 0) {
|
|
1563
|
+
return {
|
|
1564
|
+
stdout: "",
|
|
1565
|
+
stderr: `Usage: install <package>[@version] [...packages]
|
|
1566
|
+
|
|
1567
|
+
Examples:
|
|
1568
|
+
install react
|
|
1569
|
+
install lodash@4.17.21
|
|
1570
|
+
install @tanstack/react-query@5
|
|
1571
|
+
`,
|
|
1572
|
+
exitCode: 1
|
|
1573
|
+
};
|
|
1574
|
+
}
|
|
1575
|
+
const results = [];
|
|
1576
|
+
let hasError = false;
|
|
1577
|
+
for (const packageSpec of args) {
|
|
1578
|
+
try {
|
|
1579
|
+
const result = await installPackage(fs, packageSpec, { cache: typesCache });
|
|
1580
|
+
let status = `+ ${result.name}@${result.version}`;
|
|
1581
|
+
if (result.typesInstalled) {
|
|
1582
|
+
status += ` (${result.typeFilesCount} type file${result.typeFilesCount !== 1 ? "s" : ""})`;
|
|
1583
|
+
if (result.fromCache) {
|
|
1584
|
+
status += " [cached]";
|
|
1585
|
+
}
|
|
1586
|
+
} else if (result.typesError) {
|
|
1587
|
+
status += ` (no types: ${result.typesError})`;
|
|
1588
|
+
}
|
|
1589
|
+
results.push(status);
|
|
1590
|
+
} catch (err) {
|
|
1591
|
+
hasError = true;
|
|
1592
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1593
|
+
results.push(`x ${packageSpec}: ${message}`);
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
const output = results.join(`
|
|
1597
|
+
`) + `
|
|
1598
|
+
`;
|
|
1599
|
+
if (hasError) {
|
|
1600
|
+
return {
|
|
1601
|
+
stdout: "",
|
|
1602
|
+
stderr: output,
|
|
1603
|
+
exitCode: 1
|
|
1604
|
+
};
|
|
1605
|
+
}
|
|
1606
|
+
return {
|
|
1607
|
+
stdout: output,
|
|
1608
|
+
stderr: "",
|
|
1609
|
+
exitCode: 0
|
|
1610
|
+
};
|
|
1611
|
+
});
|
|
1612
|
+
}
|
|
1613
|
+
function createUninstallCommand(deps) {
|
|
1614
|
+
const { fs } = deps;
|
|
1615
|
+
return defineCommand("uninstall", async (args, _ctx) => {
|
|
1616
|
+
if (args.length === 0) {
|
|
1617
|
+
return {
|
|
1618
|
+
stdout: "",
|
|
1619
|
+
stderr: `Usage: uninstall <package> [...packages]
|
|
1620
|
+
`,
|
|
1621
|
+
exitCode: 1
|
|
1622
|
+
};
|
|
1623
|
+
}
|
|
1624
|
+
const results = [];
|
|
1625
|
+
let hasError = false;
|
|
1626
|
+
for (const packageName of args) {
|
|
1627
|
+
try {
|
|
1628
|
+
const removed = await uninstallPackage(fs, packageName);
|
|
1629
|
+
if (removed) {
|
|
1630
|
+
results.push(`- ${packageName}`);
|
|
1631
|
+
} else {
|
|
1632
|
+
results.push(`x ${packageName}: not installed`);
|
|
1633
|
+
hasError = true;
|
|
1634
|
+
}
|
|
1635
|
+
} catch (err) {
|
|
1636
|
+
hasError = true;
|
|
1637
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1638
|
+
results.push(`x ${packageName}: ${message}`);
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
const output = results.join(`
|
|
1642
|
+
`) + `
|
|
1643
|
+
`;
|
|
1644
|
+
if (hasError) {
|
|
1645
|
+
return {
|
|
1646
|
+
stdout: "",
|
|
1647
|
+
stderr: output,
|
|
1648
|
+
exitCode: 1
|
|
1649
|
+
};
|
|
1650
|
+
}
|
|
1651
|
+
return {
|
|
1652
|
+
stdout: output,
|
|
1653
|
+
stderr: "",
|
|
1654
|
+
exitCode: 0
|
|
1655
|
+
};
|
|
1656
|
+
});
|
|
1657
|
+
}
|
|
1658
|
+
function createListCommand(deps) {
|
|
1659
|
+
const { fs } = deps;
|
|
1660
|
+
return defineCommand("list", async (_args, _ctx) => {
|
|
1661
|
+
try {
|
|
1662
|
+
const packages = await listPackages(fs);
|
|
1663
|
+
if (packages.length === 0) {
|
|
1664
|
+
return {
|
|
1665
|
+
stdout: `No packages installed.
|
|
1666
|
+
`,
|
|
1667
|
+
stderr: "",
|
|
1668
|
+
exitCode: 0
|
|
1669
|
+
};
|
|
1670
|
+
}
|
|
1671
|
+
const output = packages.map((pkg) => `${pkg.name}@${pkg.version}`).join(`
|
|
1672
|
+
`) + `
|
|
1673
|
+
`;
|
|
1674
|
+
return {
|
|
1675
|
+
stdout: output,
|
|
1676
|
+
stderr: "",
|
|
1677
|
+
exitCode: 0
|
|
1678
|
+
};
|
|
1679
|
+
} catch (err) {
|
|
1680
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1681
|
+
return {
|
|
1682
|
+
stdout: "",
|
|
1683
|
+
stderr: `Failed to list packages: ${message}
|
|
1684
|
+
`,
|
|
1685
|
+
exitCode: 1
|
|
1686
|
+
};
|
|
1687
|
+
}
|
|
1688
|
+
});
|
|
1689
|
+
}
|
|
1690
|
+
function createRunCommand(deps) {
|
|
1691
|
+
const { fs, libFiles, tsconfigPath, runOptions = {}, sharedModules } = deps;
|
|
1692
|
+
return defineCommand("run", async (args, _ctx) => {
|
|
1693
|
+
let entryPoint = null;
|
|
1694
|
+
let skipTypecheck = runOptions.skipTypecheck ?? false;
|
|
1695
|
+
let timeout = runOptions.timeout ?? 30000;
|
|
1696
|
+
const scriptArgs = [];
|
|
1697
|
+
let collectingArgs = false;
|
|
1698
|
+
for (let i = 0;i < args.length; i++) {
|
|
1699
|
+
const arg = args[i];
|
|
1700
|
+
if (collectingArgs) {
|
|
1701
|
+
scriptArgs.push(arg);
|
|
1702
|
+
continue;
|
|
1703
|
+
}
|
|
1704
|
+
if (arg === "--") {
|
|
1705
|
+
collectingArgs = true;
|
|
1706
|
+
} else if (arg === "--skip-typecheck" || arg === "-s") {
|
|
1707
|
+
skipTypecheck = true;
|
|
1708
|
+
} else if ((arg === "--timeout" || arg === "-t") && args[i + 1]) {
|
|
1709
|
+
timeout = parseInt(args[++i], 10);
|
|
1710
|
+
if (isNaN(timeout))
|
|
1711
|
+
timeout = 30000;
|
|
1712
|
+
} else if (!arg.startsWith("-")) {
|
|
1713
|
+
entryPoint = arg;
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
if (!entryPoint) {
|
|
1717
|
+
return {
|
|
1718
|
+
stdout: "",
|
|
1719
|
+
stderr: `Usage: run <entry-point> [options] [-- args...]
|
|
1720
|
+
|
|
1721
|
+
Options:
|
|
1722
|
+
--skip-typecheck, -s Skip type checking
|
|
1723
|
+
--timeout, -t <ms> Execution timeout (default: 30000)
|
|
1724
|
+
|
|
1725
|
+
Example: run /src/index.ts
|
|
1726
|
+
`,
|
|
1727
|
+
exitCode: 1
|
|
1728
|
+
};
|
|
1729
|
+
}
|
|
1730
|
+
const logs = [];
|
|
1731
|
+
const originalConsole = {
|
|
1732
|
+
log: console.log,
|
|
1733
|
+
warn: console.warn,
|
|
1734
|
+
error: console.error,
|
|
1735
|
+
info: console.info,
|
|
1736
|
+
debug: console.debug
|
|
1737
|
+
};
|
|
1738
|
+
const formatArgs = (...a) => a.map((v) => typeof v === "object" ? JSON.stringify(v) : String(v)).join(" ");
|
|
1739
|
+
const captureLog = (...a) => {
|
|
1740
|
+
logs.push(formatArgs(...a));
|
|
1741
|
+
originalConsole.log.apply(console, a);
|
|
1742
|
+
};
|
|
1743
|
+
const captureWarn = (...a) => {
|
|
1744
|
+
logs.push(`[warn] ${formatArgs(...a)}`);
|
|
1745
|
+
originalConsole.warn.apply(console, a);
|
|
1746
|
+
};
|
|
1747
|
+
const captureError = (...a) => {
|
|
1748
|
+
logs.push(`[error] ${formatArgs(...a)}`);
|
|
1749
|
+
originalConsole.error.apply(console, a);
|
|
1750
|
+
};
|
|
1751
|
+
const captureInfo = (...a) => {
|
|
1752
|
+
logs.push(`[info] ${formatArgs(...a)}`);
|
|
1753
|
+
originalConsole.info.apply(console, a);
|
|
1754
|
+
};
|
|
1755
|
+
const captureDebug = (...a) => {
|
|
1756
|
+
logs.push(`[debug] ${formatArgs(...a)}`);
|
|
1757
|
+
originalConsole.debug.apply(console, a);
|
|
1758
|
+
};
|
|
1759
|
+
const restoreConsole = () => {
|
|
1760
|
+
console.log = originalConsole.log;
|
|
1761
|
+
console.warn = originalConsole.warn;
|
|
1762
|
+
console.error = originalConsole.error;
|
|
1763
|
+
console.info = originalConsole.info;
|
|
1764
|
+
console.debug = originalConsole.debug;
|
|
1765
|
+
};
|
|
1766
|
+
try {
|
|
1767
|
+
if (!await fs.exists(entryPoint)) {
|
|
1768
|
+
return {
|
|
1769
|
+
stdout: "",
|
|
1770
|
+
stderr: `Error: Entry point not found: ${entryPoint}
|
|
1771
|
+
`,
|
|
1772
|
+
exitCode: 1
|
|
1773
|
+
};
|
|
1774
|
+
}
|
|
1775
|
+
if (!skipTypecheck) {
|
|
1776
|
+
const typecheckResult = await typecheck({
|
|
1777
|
+
fs,
|
|
1778
|
+
entryPoint,
|
|
1779
|
+
tsconfigPath,
|
|
1780
|
+
libFiles
|
|
1781
|
+
});
|
|
1782
|
+
if (typecheckResult.hasErrors) {
|
|
1783
|
+
const formatted = formatDiagnosticsForAgent(typecheckResult.diagnostics);
|
|
1784
|
+
return {
|
|
1785
|
+
stdout: "",
|
|
1786
|
+
stderr: `Type errors:
|
|
1787
|
+
${formatted}
|
|
1788
|
+
`,
|
|
1789
|
+
exitCode: 1
|
|
1790
|
+
};
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
const bundleResult = await bundle({
|
|
1794
|
+
fs,
|
|
1795
|
+
entryPoint,
|
|
1796
|
+
format: "esm",
|
|
1797
|
+
sharedModules
|
|
1798
|
+
});
|
|
1799
|
+
console.log = captureLog;
|
|
1800
|
+
console.warn = captureWarn;
|
|
1801
|
+
console.error = captureError;
|
|
1802
|
+
console.info = captureInfo;
|
|
1803
|
+
console.debug = captureDebug;
|
|
1804
|
+
const context = {
|
|
1805
|
+
fs,
|
|
1806
|
+
env: { ...runOptions.env },
|
|
1807
|
+
args: scriptArgs,
|
|
1808
|
+
log: captureLog,
|
|
1809
|
+
error: captureError
|
|
1810
|
+
};
|
|
1811
|
+
const startTime = performance.now();
|
|
1812
|
+
let returnValue;
|
|
1813
|
+
const executeCode = async () => {
|
|
1814
|
+
const module = await loadModule(bundleResult);
|
|
1815
|
+
if (typeof module.main === "function") {
|
|
1816
|
+
returnValue = await module.main(context);
|
|
1817
|
+
}
|
|
1818
|
+
};
|
|
1819
|
+
if (timeout > 0) {
|
|
1820
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
1821
|
+
setTimeout(() => reject(new Error(`Execution timed out after ${timeout}ms`)), timeout);
|
|
1822
|
+
});
|
|
1823
|
+
await Promise.race([executeCode(), timeoutPromise]);
|
|
1824
|
+
} else {
|
|
1825
|
+
await executeCode();
|
|
1826
|
+
}
|
|
1827
|
+
const executionTimeMs = performance.now() - startTime;
|
|
1828
|
+
restoreConsole();
|
|
1829
|
+
let output = "";
|
|
1830
|
+
if (logs.length > 0) {
|
|
1831
|
+
output = logs.join(`
|
|
1832
|
+
`) + `
|
|
1833
|
+
`;
|
|
1834
|
+
}
|
|
1835
|
+
if (returnValue !== undefined) {
|
|
1836
|
+
const returnStr = typeof returnValue === "object" ? JSON.stringify(returnValue, null, 2) : String(returnValue);
|
|
1837
|
+
output += `[return] ${returnStr}
|
|
1838
|
+
`;
|
|
1839
|
+
}
|
|
1840
|
+
output += `
|
|
1841
|
+
Execution completed in ${executionTimeMs.toFixed(2)}ms
|
|
1842
|
+
`;
|
|
1843
|
+
return {
|
|
1844
|
+
stdout: output,
|
|
1845
|
+
stderr: "",
|
|
1846
|
+
exitCode: 0
|
|
1847
|
+
};
|
|
1848
|
+
} catch (err) {
|
|
1849
|
+
restoreConsole();
|
|
1850
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
1851
|
+
const errorStack = err instanceof Error && err.stack ? `
|
|
1852
|
+
${err.stack}` : "";
|
|
1853
|
+
let output = "";
|
|
1854
|
+
if (logs.length > 0) {
|
|
1855
|
+
output = logs.join(`
|
|
1856
|
+
`) + `
|
|
1857
|
+
|
|
1858
|
+
`;
|
|
1859
|
+
}
|
|
1860
|
+
return {
|
|
1861
|
+
stdout: output,
|
|
1862
|
+
stderr: `Runtime error: ${errorMessage}${errorStack}
|
|
1863
|
+
`,
|
|
1864
|
+
exitCode: 1
|
|
1865
|
+
};
|
|
1866
|
+
}
|
|
1867
|
+
});
|
|
1868
|
+
}
|
|
1869
|
+
function createDefaultCommands(deps) {
|
|
1870
|
+
return [
|
|
1871
|
+
createTscCommand(deps),
|
|
1872
|
+
createBuildCommand(deps),
|
|
1873
|
+
createRunCommand(deps),
|
|
1874
|
+
createInstallCommand(deps),
|
|
1875
|
+
createUninstallCommand(deps),
|
|
1876
|
+
createListCommand(deps)
|
|
1877
|
+
];
|
|
1878
|
+
}
|
|
1879
|
+
export {
|
|
1880
|
+
revokeModuleUrl,
|
|
1881
|
+
resolveToEsmUrl,
|
|
1882
|
+
parsePackageSpec,
|
|
1883
|
+
parseLibReferences,
|
|
1884
|
+
parseImportPath,
|
|
1885
|
+
libNameToFileName,
|
|
1886
|
+
hasSharedModuleRegistry,
|
|
1887
|
+
getSharedModuleRuntimeCode,
|
|
1888
|
+
getSharedModuleRegistry,
|
|
1889
|
+
formatEsbuildMessages,
|
|
1890
|
+
fetchLibFile,
|
|
1891
|
+
fetchAllLibs,
|
|
1892
|
+
extractLibName,
|
|
1893
|
+
createModuleUrl,
|
|
1894
|
+
SharedModuleRegistry,
|
|
1895
|
+
LibCache,
|
|
1896
|
+
InMemoryTypesCache
|
|
1897
|
+
};
|