sandlot 0.1.4 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/dist/browser/bundler.d.ts +68 -0
  2. package/dist/browser/bundler.d.ts.map +1 -0
  3. package/dist/browser/executor.d.ts +46 -0
  4. package/dist/browser/executor.d.ts.map +1 -0
  5. package/dist/browser/index.d.ts +9 -0
  6. package/dist/browser/index.d.ts.map +1 -0
  7. package/dist/browser/index.js +2690 -0
  8. package/dist/browser/preset.d.ts +63 -0
  9. package/dist/browser/preset.d.ts.map +1 -0
  10. package/dist/commands/index.d.ts +20 -11
  11. package/dist/commands/index.d.ts.map +1 -1
  12. package/dist/commands/types.d.ts +37 -130
  13. package/dist/commands/types.d.ts.map +1 -1
  14. package/dist/core/bundler-utils.d.ts +142 -0
  15. package/dist/core/bundler-utils.d.ts.map +1 -0
  16. package/dist/core/esm-types-resolver.d.ts +125 -0
  17. package/dist/core/esm-types-resolver.d.ts.map +1 -0
  18. package/dist/core/executor.d.ts +35 -0
  19. package/dist/core/executor.d.ts.map +1 -0
  20. package/dist/{fs.d.ts → core/fs.d.ts} +27 -29
  21. package/dist/core/fs.d.ts.map +1 -0
  22. package/dist/core/sandbox.d.ts +30 -0
  23. package/dist/core/sandbox.d.ts.map +1 -0
  24. package/dist/core/sandlot.d.ts +30 -0
  25. package/dist/core/sandlot.d.ts.map +1 -0
  26. package/dist/core/shared-module-registry.d.ts +46 -0
  27. package/dist/core/shared-module-registry.d.ts.map +1 -0
  28. package/dist/core/typechecker.d.ts +60 -0
  29. package/dist/core/typechecker.d.ts.map +1 -0
  30. package/dist/index.d.ts +11 -16
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +1398 -2010
  33. package/dist/node/bundler.d.ts +48 -0
  34. package/dist/node/bundler.d.ts.map +1 -0
  35. package/dist/node/executor.d.ts +48 -0
  36. package/dist/node/executor.d.ts.map +1 -0
  37. package/dist/node/index.d.ts +9 -0
  38. package/dist/node/index.d.ts.map +1 -0
  39. package/dist/node/index.js +2644 -0
  40. package/dist/node/preset.d.ts +62 -0
  41. package/dist/node/preset.d.ts.map +1 -0
  42. package/dist/types.d.ts +528 -0
  43. package/dist/types.d.ts.map +1 -0
  44. package/package.json +16 -6
  45. package/src/browser/bundler.ts +294 -0
  46. package/src/browser/executor.ts +71 -0
  47. package/src/browser/index.ts +57 -0
  48. package/src/browser/preset.ts +179 -0
  49. package/src/commands/index.ts +498 -37
  50. package/src/commands/types.ts +117 -145
  51. package/src/core/bundler-utils.ts +630 -0
  52. package/src/core/esm-types-resolver.ts +432 -0
  53. package/src/core/executor.ts +161 -0
  54. package/src/{fs.ts → core/fs.ts} +59 -37
  55. package/src/core/sandbox.ts +624 -0
  56. package/src/core/sandlot.ts +77 -0
  57. package/src/core/shared-module-registry.ts +138 -0
  58. package/src/core/typechecker.ts +609 -0
  59. package/src/index.ts +106 -139
  60. package/src/node/bundler.ts +194 -0
  61. package/src/node/executor.ts +87 -0
  62. package/src/node/index.ts +39 -0
  63. package/src/node/preset.ts +178 -0
  64. package/src/types.ts +672 -0
  65. package/README.md +0 -243
  66. package/dist/build-emitter.d.ts +0 -47
  67. package/dist/build-emitter.d.ts.map +0 -1
  68. package/dist/builder.d.ts +0 -370
  69. package/dist/builder.d.ts.map +0 -1
  70. package/dist/bundler.d.ts +0 -152
  71. package/dist/bundler.d.ts.map +0 -1
  72. package/dist/commands/compile.d.ts +0 -13
  73. package/dist/commands/compile.d.ts.map +0 -1
  74. package/dist/commands/packages.d.ts +0 -17
  75. package/dist/commands/packages.d.ts.map +0 -1
  76. package/dist/commands/run.d.ts +0 -40
  77. package/dist/commands/run.d.ts.map +0 -1
  78. package/dist/commands.d.ts +0 -179
  79. package/dist/commands.d.ts.map +0 -1
  80. package/dist/fs.d.ts.map +0 -1
  81. package/dist/internal.d.ts +0 -79
  82. package/dist/internal.d.ts.map +0 -1
  83. package/dist/internal.js +0 -1942
  84. package/dist/loader.d.ts +0 -164
  85. package/dist/loader.d.ts.map +0 -1
  86. package/dist/packages.d.ts +0 -199
  87. package/dist/packages.d.ts.map +0 -1
  88. package/dist/runner.d.ts +0 -314
  89. package/dist/runner.d.ts.map +0 -1
  90. package/dist/sandbox-manager.d.ts +0 -261
  91. package/dist/sandbox-manager.d.ts.map +0 -1
  92. package/dist/sandbox.d.ts +0 -267
  93. package/dist/sandbox.d.ts.map +0 -1
  94. package/dist/shared-modules.d.ts +0 -148
  95. package/dist/shared-modules.d.ts.map +0 -1
  96. package/dist/shared-resources.d.ts +0 -102
  97. package/dist/shared-resources.d.ts.map +0 -1
  98. package/dist/ts-libs.d.ts +0 -85
  99. package/dist/ts-libs.d.ts.map +0 -1
  100. package/dist/typechecker.d.ts +0 -127
  101. package/dist/typechecker.d.ts.map +0 -1
  102. package/src/build-emitter.ts +0 -64
  103. package/src/builder.ts +0 -498
  104. package/src/bundler.ts +0 -575
  105. package/src/commands/compile.ts +0 -236
  106. package/src/commands/packages.ts +0 -154
  107. package/src/commands/run.ts +0 -245
  108. package/src/internal.ts +0 -119
  109. package/src/loader.ts +0 -229
  110. package/src/packages.ts +0 -936
  111. package/src/sandbox.ts +0 -398
  112. package/src/shared-modules.ts +0 -280
  113. package/src/shared-resources.ts +0 -166
  114. package/src/ts-libs.ts +0 -218
  115. package/src/typechecker.ts +0 -635
@@ -0,0 +1,609 @@
1
+ /**
2
+ * Typechecker - TypeScript type checking.
3
+ *
4
+ * Uses TypeScript's compiler API
5
+ * Fetches TypeScript lib files (lib.dom.d.ts, etc.) from CDN and caches them.
6
+ */
7
+
8
+ import ts from "typescript";
9
+ import type { Filesystem } from "./fs";
10
+ import type {
11
+ ITypechecker,
12
+ TypecheckOptions,
13
+ TypecheckResult,
14
+ Diagnostic,
15
+ } from "../types";
16
+
17
+ // =============================================================================
18
+ // Configuration
19
+ // =============================================================================
20
+
21
+ /** TypeScript version to fetch libs for - should match package.json */
22
+ const TS_VERSION = "5.9.3";
23
+
24
+ /** CDN base URL for TypeScript lib files */
25
+ const DEFAULT_CDN_BASE = `https://cdn.jsdelivr.net/npm/typescript@${TS_VERSION}/lib`;
26
+
27
+ /** Default libs for environment */
28
+ const DEFAULT_LIBS = ["es2020", "dom", "dom.iterable"];
29
+
30
+ /** Virtual path where lib files are "located" for TypeScript */
31
+ const LIB_PATH_PREFIX = "/node_modules/typescript/lib/";
32
+
33
+ // =============================================================================
34
+ // Types
35
+ // =============================================================================
36
+
37
+ export interface TypecheckerOptions {
38
+ /**
39
+ * TypeScript lib names to include (e.g., "dom", "es2020").
40
+ * If not provided, uses sensible defaults: ["es2020", "dom", "dom.iterable"]
41
+ */
42
+ libs?: string[];
43
+
44
+ /**
45
+ * Base URL to fetch TypeScript lib files from.
46
+ * Defaults to jsDelivr CDN.
47
+ */
48
+ libsBaseUrl?: string;
49
+ }
50
+
51
+ // =============================================================================
52
+ // Lib File Fetching
53
+ // =============================================================================
54
+
55
+ /**
56
+ * Parse `/// <reference lib="..." />` directives from a lib file.
57
+ */
58
+ function parseLibReferences(content: string): string[] {
59
+ const refs: string[] = [];
60
+ const regex = /\/\/\/\s*<reference\s+lib="([^"]+)"\s*\/>/g;
61
+ let match;
62
+
63
+ while ((match = regex.exec(content)) !== null) {
64
+ if (match[1]) {
65
+ refs.push(match[1]);
66
+ }
67
+ }
68
+
69
+ return refs;
70
+ }
71
+
72
+ /**
73
+ * Convert a lib name to its filename.
74
+ * e.g., "es2020" -> "lib.es2020.d.ts"
75
+ */
76
+ function libNameToFileName(name: string): string {
77
+ return `lib.${name}.d.ts`;
78
+ }
79
+
80
+ /**
81
+ * Fetch a single lib file from CDN.
82
+ */
83
+ async function fetchLibFile(name: string, baseUrl: string): Promise<string> {
84
+ const fileName = libNameToFileName(name);
85
+ const url = `${baseUrl}/${fileName}`;
86
+
87
+ const response = await fetch(url);
88
+ if (!response.ok) {
89
+ throw new Error(`Failed to fetch ${url}: ${response.status} ${response.statusText}`);
90
+ }
91
+
92
+ return response.text();
93
+ }
94
+
95
+ /**
96
+ * Fetch all lib files, following reference directives.
97
+ */
98
+ async function fetchAllLibs(
99
+ libs: string[],
100
+ baseUrl: string
101
+ ): Promise<Map<string, string>> {
102
+ const result = new Map<string, string>();
103
+ const pending = new Set<string>(libs);
104
+ const fetched = new Set<string>();
105
+
106
+ while (pending.size > 0) {
107
+ const batch = Array.from(pending);
108
+ pending.clear();
109
+
110
+ const results = await Promise.all(
111
+ batch.map(async (name) => {
112
+ if (fetched.has(name)) {
113
+ return { name, content: null };
114
+ }
115
+ fetched.add(name);
116
+
117
+ try {
118
+ const content = await fetchLibFile(name, baseUrl);
119
+ return { name, content };
120
+ } catch (err) {
121
+ console.warn(`[typechecker] Failed to fetch lib.${name}.d.ts:`, err);
122
+ return { name, content: null };
123
+ }
124
+ })
125
+ );
126
+
127
+ for (const { name, content } of results) {
128
+ if (content === null) continue;
129
+
130
+ result.set(name, content);
131
+
132
+ // Parse references and queue unfetched ones
133
+ const refs = parseLibReferences(content);
134
+ for (const ref of refs) {
135
+ if (!fetched.has(ref) && !pending.has(ref)) {
136
+ pending.add(ref);
137
+ }
138
+ }
139
+ }
140
+ }
141
+
142
+ return result;
143
+ }
144
+
145
+ // =============================================================================
146
+ // Compiler Host
147
+ // =============================================================================
148
+
149
+ /**
150
+ * Normalize a path to absolute form.
151
+ */
152
+ function normalizePath(path: string): string {
153
+ if (!path.startsWith("/")) {
154
+ return "/" + path;
155
+ }
156
+ return path;
157
+ }
158
+
159
+ /**
160
+ * Get lib content from the lib cache.
161
+ * Handles various path formats TypeScript might request.
162
+ */
163
+ function getLibContent(
164
+ fileName: string,
165
+ libFiles: Map<string, string>
166
+ ): string | undefined {
167
+ // Extract lib name from path: "lib.dom.d.ts" or "/node_modules/typescript/lib/lib.dom.d.ts"
168
+ const match = fileName.match(/lib\.([^/]+)\.d\.ts$/);
169
+ if (match?.[1]) {
170
+ return libFiles.get(match[1]);
171
+ }
172
+ return undefined;
173
+ }
174
+
175
+ /**
176
+ * Create a TypeScript compiler host that reads from our filesystem.
177
+ */
178
+ function createCompilerHost(
179
+ fs: Filesystem,
180
+ libFiles: Map<string, string>,
181
+ options: ts.CompilerOptions
182
+ ): ts.CompilerHost {
183
+ return {
184
+ getSourceFile(
185
+ fileName: string,
186
+ languageVersion: ts.ScriptTarget,
187
+ onError?: (message: string) => void
188
+ ): ts.SourceFile | undefined {
189
+ // Try filesystem first (user files + node_modules with package types)
190
+ const normalizedPath = normalizePath(fileName);
191
+
192
+ try {
193
+ if (fs.exists(normalizedPath)) {
194
+ const stat = fs.stat(normalizedPath);
195
+ if (stat.isFile) {
196
+ const content = fs.readFile(normalizedPath);
197
+ return ts.createSourceFile(normalizedPath, content, languageVersion, true);
198
+ }
199
+ }
200
+ } catch {
201
+ // Not found in filesystem, continue to lib files
202
+ }
203
+
204
+ // Try without leading slash
205
+ try {
206
+ if (fs.exists(fileName)) {
207
+ const stat = fs.stat(fileName);
208
+ if (stat.isFile) {
209
+ const content = fs.readFile(fileName);
210
+ return ts.createSourceFile(fileName, content, languageVersion, true);
211
+ }
212
+ }
213
+ } catch {
214
+ // Not found, continue
215
+ }
216
+
217
+ // Try lib files
218
+ const libContent = getLibContent(fileName, libFiles);
219
+ if (libContent !== undefined) {
220
+ return ts.createSourceFile(fileName, libContent, languageVersion, true);
221
+ }
222
+
223
+ if (onError) {
224
+ onError(`File not found: ${fileName}`);
225
+ }
226
+ return undefined;
227
+ },
228
+
229
+ getDefaultLibFileName(opts: ts.CompilerOptions): string {
230
+ return LIB_PATH_PREFIX + ts.getDefaultLibFileName(opts);
231
+ },
232
+
233
+ writeFile(): void {
234
+ // No-op: we don't emit files
235
+ },
236
+
237
+ getCurrentDirectory(): string {
238
+ return "/";
239
+ },
240
+
241
+ getCanonicalFileName(fileName: string): string {
242
+ return fileName;
243
+ },
244
+
245
+ useCaseSensitiveFileNames(): boolean {
246
+ return true;
247
+ },
248
+
249
+ getNewLine(): string {
250
+ return "\n";
251
+ },
252
+
253
+ fileExists(fileName: string): boolean {
254
+ const normalizedPath = normalizePath(fileName);
255
+
256
+ // Check filesystem
257
+ try {
258
+ if (fs.exists(normalizedPath)) {
259
+ return fs.stat(normalizedPath).isFile;
260
+ }
261
+ } catch {
262
+ // Not found
263
+ }
264
+
265
+ // Check lib files
266
+ return getLibContent(fileName, libFiles) !== undefined;
267
+ },
268
+
269
+ readFile(fileName: string): string | undefined {
270
+ const normalizedPath = normalizePath(fileName);
271
+
272
+ // Try filesystem
273
+ try {
274
+ if (fs.exists(normalizedPath)) {
275
+ return fs.readFile(normalizedPath);
276
+ }
277
+ } catch {
278
+ // Not found
279
+ }
280
+
281
+ // Try lib files
282
+ return getLibContent(fileName, libFiles);
283
+ },
284
+
285
+ directoryExists(directoryName: string): boolean {
286
+ const normalizedDir = normalizePath(directoryName);
287
+
288
+ // Check filesystem directly - it tracks directories!
289
+ try {
290
+ if (fs.exists(normalizedDir)) {
291
+ return fs.stat(normalizedDir).isDirectory;
292
+ }
293
+ } catch {
294
+ // Not found
295
+ }
296
+
297
+ // Virtual directories for lib files
298
+ if (
299
+ normalizedDir === "/node_modules/typescript/lib" ||
300
+ normalizedDir === "/node_modules/typescript" ||
301
+ normalizedDir === "/node_modules"
302
+ ) {
303
+ return libFiles.size > 0;
304
+ }
305
+
306
+ return false;
307
+ },
308
+
309
+ getDirectories(path: string): string[] {
310
+ const normalizedPath = normalizePath(path);
311
+
312
+ try {
313
+ if (!fs.exists(normalizedPath)) {
314
+ return [];
315
+ }
316
+
317
+ const stat = fs.stat(normalizedPath);
318
+ if (!stat.isDirectory) {
319
+ return [];
320
+ }
321
+
322
+ // Use readdir and filter to directories
323
+ const entries = fs.readdir(normalizedPath);
324
+ const dirs: string[] = [];
325
+
326
+ for (const name of entries) {
327
+ const childPath =
328
+ normalizedPath === "/" ? `/${name}` : `${normalizedPath}/${name}`;
329
+ try {
330
+ if (fs.stat(childPath).isDirectory) {
331
+ dirs.push(name);
332
+ }
333
+ } catch {
334
+ // Skip if can't stat
335
+ }
336
+ }
337
+
338
+ return dirs;
339
+ } catch {
340
+ return [];
341
+ }
342
+ },
343
+
344
+ realpath(path: string): string {
345
+ return path;
346
+ },
347
+
348
+ getEnvironmentVariable(): string | undefined {
349
+ return undefined;
350
+ },
351
+ };
352
+ }
353
+
354
+ // =============================================================================
355
+ // tsconfig Parsing
356
+ // =============================================================================
357
+
358
+ /**
359
+ * Get default compiler options for TypeScript.
360
+ */
361
+ function getDefaultCompilerOptions(): ts.CompilerOptions {
362
+ return {
363
+ target: ts.ScriptTarget.ES2020,
364
+ module: ts.ModuleKind.ESNext,
365
+ moduleResolution: ts.ModuleResolutionKind.Bundler,
366
+ esModuleInterop: true,
367
+ strict: true,
368
+ skipLibCheck: true,
369
+ noEmit: true,
370
+ jsx: ts.JsxEmit.ReactJSX,
371
+ allowJs: true,
372
+ resolveJsonModule: true,
373
+ lib: ["lib.es2020.d.ts", "lib.dom.d.ts", "lib.dom.iterable.d.ts"],
374
+ };
375
+ }
376
+
377
+ /**
378
+ * Parse tsconfig.json content into compiler options.
379
+ *
380
+ * Uses TypeScript's built-in parsing instead of manual enum mapping.
381
+ */
382
+ function parseTsConfig(
383
+ fs: Filesystem,
384
+ configPath: string
385
+ ): ts.CompilerOptions {
386
+ try {
387
+ if (!fs.exists(configPath)) {
388
+ return getDefaultCompilerOptions();
389
+ }
390
+
391
+ const configText = fs.readFile(configPath);
392
+ const { config, error } = ts.parseConfigFileTextToJson(configPath, configText);
393
+
394
+ if (error) {
395
+ console.warn("[typechecker] Error parsing tsconfig:", error.messageText);
396
+ return getDefaultCompilerOptions();
397
+ }
398
+
399
+ // Create a minimal parse host for config parsing
400
+ const parseHost: ts.ParseConfigHost = {
401
+ useCaseSensitiveFileNames: true,
402
+ readDirectory: () => [],
403
+ fileExists: (path) => fs.exists(normalizePath(path)),
404
+ readFile: (path) => {
405
+ try {
406
+ return fs.readFile(normalizePath(path));
407
+ } catch {
408
+ return undefined;
409
+ }
410
+ },
411
+ };
412
+
413
+ const parsed = ts.parseJsonConfigFileContent(
414
+ config,
415
+ parseHost,
416
+ "/", // base path
417
+ undefined, // existing options
418
+ configPath
419
+ );
420
+
421
+ // Filter out "no inputs found" error (TS18003) - we pass entry points explicitly
422
+ const relevantErrors = parsed.errors.filter((e) => e.code !== 18003);
423
+ if (relevantErrors.length > 0) {
424
+ console.warn(
425
+ "[typechecker] tsconfig parse errors:",
426
+ relevantErrors.map((e) => e.messageText)
427
+ );
428
+ }
429
+
430
+ // Ensure noEmit is always true for type checking
431
+ return {
432
+ ...parsed.options,
433
+ noEmit: true,
434
+ };
435
+ } catch (err) {
436
+ console.warn("[typechecker] Error reading tsconfig:", err);
437
+ return getDefaultCompilerOptions();
438
+ }
439
+ }
440
+
441
+ // =============================================================================
442
+ // Diagnostic Conversion
443
+ // =============================================================================
444
+
445
+ /**
446
+ * Convert TypeScript diagnostic category to our severity.
447
+ */
448
+ function categoryToSeverity(
449
+ category: ts.DiagnosticCategory
450
+ ): "error" | "warning" | "info" {
451
+ switch (category) {
452
+ case ts.DiagnosticCategory.Error:
453
+ return "error";
454
+ case ts.DiagnosticCategory.Warning:
455
+ return "warning";
456
+ default:
457
+ return "info";
458
+ }
459
+ }
460
+
461
+ /**
462
+ * Convert TypeScript diagnostic to our format.
463
+ */
464
+ function convertDiagnostic(diag: ts.Diagnostic): Diagnostic {
465
+ let file: string | undefined;
466
+ let line: number | undefined;
467
+ let column: number | undefined;
468
+
469
+ if (diag.file && diag.start !== undefined) {
470
+ file = diag.file.fileName;
471
+ const pos = diag.file.getLineAndCharacterOfPosition(diag.start);
472
+ line = pos.line + 1; // Convert to 1-based
473
+ column = pos.character + 1;
474
+ }
475
+
476
+ return {
477
+ file,
478
+ line,
479
+ column,
480
+ message: ts.flattenDiagnosticMessageText(diag.messageText, "\n"),
481
+ severity: categoryToSeverity(diag.category),
482
+ };
483
+ }
484
+
485
+ // =============================================================================
486
+ // Typechecker
487
+ // =============================================================================
488
+
489
+ /**
490
+ * Typechecker using TypeScript compiler API.
491
+ *
492
+ * Fetches TypeScript lib files from CDN and caches them.
493
+ * Uses filesystem access for efficient type checking.
494
+ *
495
+ * @example
496
+ * ```ts
497
+ * const typechecker = new Typechecker();
498
+ *
499
+ * const result = await typechecker.typecheck({
500
+ * fs: myFilesystem,
501
+ * entryPoint: "/src/index.ts",
502
+ * });
503
+ *
504
+ * if (!result.success) {
505
+ * console.log("Type errors:", result.diagnostics);
506
+ * }
507
+ * ```
508
+ */
509
+ export class Typechecker implements ITypechecker {
510
+ private options: TypecheckerOptions;
511
+ private libCache: Map<string, string> = new Map();
512
+ private initPromise: Promise<void> | null = null;
513
+
514
+ constructor(options: TypecheckerOptions = {}) {
515
+ this.options = options;
516
+ }
517
+
518
+ /**
519
+ * Pre-fetch TypeScript lib files.
520
+ * Called automatically on first typecheck() if not already done.
521
+ */
522
+ async initialize(): Promise<void> {
523
+ if (this.initPromise) {
524
+ await this.initPromise;
525
+ return;
526
+ }
527
+
528
+ if (this.libCache.size > 0) {
529
+ return; // Already initialized
530
+ }
531
+
532
+ this.initPromise = this.fetchLibs();
533
+ await this.initPromise;
534
+ }
535
+
536
+ private async fetchLibs(): Promise<void> {
537
+ const libs = this.options.libs ?? DEFAULT_LIBS;
538
+ const baseUrl = this.options.libsBaseUrl ?? DEFAULT_CDN_BASE;
539
+
540
+ console.log(`[typechecker] Fetching TypeScript libs: ${libs.join(", ")}...`);
541
+ const fetched = await fetchAllLibs(libs, baseUrl);
542
+ console.log(`[typechecker] Fetched ${fetched.size} lib files`);
543
+
544
+ this.libCache = fetched;
545
+ }
546
+
547
+ /**
548
+ * Type check files in a filesystem.
549
+ */
550
+ async typecheck(options: TypecheckOptions): Promise<TypecheckResult> {
551
+ await this.initialize();
552
+
553
+ const { fs, entryPoint, tsconfigPath = "/tsconfig.json" } = options;
554
+ const normalizedEntry = normalizePath(entryPoint);
555
+
556
+ // Verify entry point exists
557
+ if (!fs.exists(normalizedEntry)) {
558
+ return {
559
+ success: false,
560
+ diagnostics: [
561
+ {
562
+ file: normalizedEntry,
563
+ message: `Entry point not found: ${normalizedEntry}`,
564
+ severity: "error",
565
+ },
566
+ ],
567
+ };
568
+ }
569
+
570
+ // Parse tsconfig
571
+ const compilerOptions = parseTsConfig(fs, tsconfigPath);
572
+
573
+ // Create compiler host
574
+ const host = createCompilerHost(fs, this.libCache, compilerOptions);
575
+
576
+ // Create program and collect diagnostics
577
+ const program = ts.createProgram([normalizedEntry], compilerOptions, host);
578
+
579
+ const allDiagnostics = [
580
+ ...program.getSyntacticDiagnostics(),
581
+ ...program.getSemanticDiagnostics(),
582
+ ...program.getDeclarationDiagnostics(),
583
+ ];
584
+
585
+ // Convert diagnostics
586
+ const diagnostics = allDiagnostics.map(convertDiagnostic);
587
+
588
+ // Check for errors
589
+ const success = !diagnostics.some((d) => d.severity === "error");
590
+
591
+ return {
592
+ success,
593
+ diagnostics,
594
+ };
595
+ }
596
+ }
597
+
598
+ // =============================================================================
599
+ // Factory Function
600
+ // =============================================================================
601
+
602
+ /**
603
+ * Create a typechecker instance.
604
+ */
605
+ export function createTypechecker(
606
+ options?: TypecheckerOptions
607
+ ): ITypechecker {
608
+ return new Typechecker(options);
609
+ }