@joinezco/codeblock 0.0.8 → 0.0.10

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 (73) hide show
  1. package/dist/editor.d.ts +30 -3
  2. package/dist/editor.js +416 -43
  3. package/dist/index.d.ts +5 -1
  4. package/dist/index.js +5 -1
  5. package/dist/lsps/index.d.ts +5 -0
  6. package/dist/lsps/index.js +9 -2
  7. package/dist/lsps/typescript.d.ts +3 -1
  8. package/dist/lsps/typescript.js +8 -17
  9. package/dist/panels/settings.d.ts +22 -0
  10. package/dist/panels/settings.js +267 -0
  11. package/dist/panels/terminal.d.ts +3 -0
  12. package/dist/panels/terminal.js +76 -0
  13. package/dist/panels/toolbar.d.ts +53 -3
  14. package/dist/panels/toolbar.js +1336 -164
  15. package/dist/panels/toolbar.test.js +20 -14
  16. package/dist/rpc/transport.d.ts +2 -11
  17. package/dist/rpc/transport.js +19 -35
  18. package/dist/themes/index.js +226 -13
  19. package/dist/themes/vscode.js +3 -2
  20. package/dist/types.d.ts +5 -0
  21. package/dist/utils/fs.d.ts +22 -3
  22. package/dist/utils/fs.js +126 -21
  23. package/dist/utils/lsp.d.ts +26 -15
  24. package/dist/utils/lsp.js +79 -44
  25. package/dist/utils/search.d.ts +2 -0
  26. package/dist/utils/search.js +13 -4
  27. package/dist/utils/typescript-defaults.d.ts +57 -0
  28. package/dist/utils/typescript-defaults.js +208 -0
  29. package/dist/utils/typescript-defaults.test.d.ts +1 -0
  30. package/dist/utils/typescript-defaults.test.js +197 -0
  31. package/dist/workers/fs.worker.d.ts +4 -8
  32. package/dist/workers/fs.worker.js +30 -60
  33. package/dist/workers/javascript.worker.js +11 -9
  34. package/package.json +8 -4
  35. package/dist/assets/clike-C8IJ2oj_.js +0 -1
  36. package/dist/assets/cmake-BQqOBYOt.js +0 -1
  37. package/dist/assets/dockerfile-C_y-rIpk.js +0 -1
  38. package/dist/assets/fs.worker-BwEqZcql.ts +0 -109
  39. package/dist/assets/go-CTD25R5P.js +0 -1
  40. package/dist/assets/haskell-BWDZoCOh.js +0 -1
  41. package/dist/assets/index-9HdhmM_Y.js +0 -1
  42. package/dist/assets/index-C-QhPFHP.js +0 -3
  43. package/dist/assets/index-C3BnE2cG.js +0 -222
  44. package/dist/assets/index-CGx5MZO7.js +0 -6
  45. package/dist/assets/index-CIuq3uTk.js +0 -1
  46. package/dist/assets/index-CXFONXS8.js +0 -1
  47. package/dist/assets/index-D5Z27j1C.js +0 -1
  48. package/dist/assets/index-DWOBdRjn.js +0 -1
  49. package/dist/assets/index-Dvu-FFzd.js +0 -1
  50. package/dist/assets/index-Dx_VuNNd.js +0 -1
  51. package/dist/assets/index-I0dlv-r3.js +0 -1
  52. package/dist/assets/index-MGle_v2x.js +0 -1
  53. package/dist/assets/index-N-GE7HTU.js +0 -1
  54. package/dist/assets/index-aEsF5o-7.js +0 -2
  55. package/dist/assets/index-as7ELo0J.js +0 -1
  56. package/dist/assets/index-gUUzXNuP.js +0 -1
  57. package/dist/assets/index-pGm0qkrJ.js +0 -13
  58. package/dist/assets/javascript.worker-C1zGArKk.js +0 -527
  59. package/dist/assets/lua-BgMRiT3U.js +0 -1
  60. package/dist/assets/perl-CdXCOZ3F.js +0 -1
  61. package/dist/assets/process-Dw9K5EnD.js +0 -1357
  62. package/dist/assets/properties-C78fOPTZ.js +0 -1
  63. package/dist/assets/ruby-B2Rjki9n.js +0 -1
  64. package/dist/assets/shell-CjFT_Tl9.js +0 -1
  65. package/dist/assets/swift-BzpIVaGY.js +0 -1
  66. package/dist/assets/toml-BXUEaScT.js +0 -1
  67. package/dist/assets/vb-CmGdzxic.js +0 -1
  68. package/dist/e2e/example.spec.d.ts +0 -5
  69. package/dist/e2e/example.spec.js +0 -44
  70. package/dist/index.html +0 -16
  71. package/dist/resources/config.json +0 -13
  72. package/dist/snapshot.bin +0 -0
  73. package/dist/styles.css +0 -7
package/dist/utils/fs.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as Comlink from "comlink";
2
2
  import { watchOptionsTransferHandler, asyncGeneratorTransferHandler } from "../rpc/serde";
3
3
  import { FileType } from '@volar/language-service';
4
+ import { TopLevelFs } from "@joinezco/jswasi/filesystem";
4
5
  import { constants } from "@joinezco/jswasi";
5
6
  Comlink.transferHandlers.set("asyncGenerator", asyncGeneratorTransferHandler);
6
7
  Comlink.transferHandlers.set("watchOptions", watchOptionsTransferHandler);
@@ -58,7 +59,6 @@ export var Vfs;
58
59
  let cur = "/";
59
60
  for (const part of parts) {
60
61
  cur = cur === "/" ? `/${part}` : `${cur}/${part}`;
61
- console.log('creating', { cur, abs });
62
62
  const exists = await this.exists(cur);
63
63
  if (exists)
64
64
  continue;
@@ -115,13 +115,25 @@ export var Vfs;
115
115
  type: toVolarFileType(filestat.filetype),
116
116
  };
117
117
  },
118
+ async unlink(path) {
119
+ const abs = ensureAbs(path);
120
+ const res = await jswasiFs.removeEntry(abs, false);
121
+ if (res !== WASI_ESUCCESS) {
122
+ throw new Error(`unlink failed (${res}) for ${abs}`);
123
+ }
124
+ },
118
125
  };
119
126
  };
120
- // TODO: this is incorrect, fs is a Comlink proxy
121
127
  Vfs.fromMemfs = (fs) => {
122
128
  return {
123
129
  async readFile(path) {
124
- return fs.promises.readFile(path, { encoding: "utf-8" });
130
+ const result = await fs.promises.readFile(path, { encoding: "utf-8" });
131
+ // memfs may return a Buffer — ensure we return a string
132
+ if (typeof result === 'string')
133
+ return result;
134
+ if (result && typeof result.toString === 'function')
135
+ return result.toString('utf-8');
136
+ return String(result ?? '');
125
137
  },
126
138
  async writeFile(path, data) {
127
139
  await fs.promises.writeFile(path, data);
@@ -178,7 +190,10 @@ export var Vfs;
178
190
  catch (err) {
179
191
  return null;
180
192
  }
181
- }
193
+ },
194
+ async unlink(path) {
195
+ await fs.promises.unlink(path);
196
+ },
182
197
  };
183
198
  };
184
199
  Vfs.fromNodelike = (fs) => {
@@ -246,9 +261,25 @@ export var Vfs;
246
261
  catch (err) {
247
262
  return null;
248
263
  }
249
- }
264
+ },
265
+ async unlink(path) {
266
+ await fs.unlink(path);
267
+ },
250
268
  };
251
269
  };
270
+ /**
271
+ * Create an FSA (File System Access / OPFS) backed filesystem.
272
+ * Data persists across page reloads via the browser's Origin Private File System.
273
+ *
274
+ * @param name - Unique name for the FSA storage bucket (default: 'codeblock')
275
+ */
276
+ Vfs.fsa = async (name = 'codeblock') => {
277
+ const topFs = new TopLevelFs();
278
+ await topFs.addMount(
279
+ // @ts-ignore - TopLevelFs.addMount typing
280
+ undefined, "", undefined, "/", "fsa", 0n, { name, keepMetadata: "true", create: "true" });
281
+ return Vfs.fromJswasiFs(topFs);
282
+ };
252
283
  /**
253
284
  * Create a filesystem worker with optional snapshot data.
254
285
  *
@@ -257,27 +288,22 @@ export var Vfs;
257
288
  * for better performance with large files.
258
289
  */
259
290
  Vfs.worker = async (bufferOrUrl) => {
260
- const url = new URL('../workers/fs.worker.js', import.meta.url);
261
- const worker = new SharedWorker(url, { type: 'module' });
291
+ // TODO: fix this for non-Vite consumers
292
+ const worker = new SharedWorker(new URL('../workers/fs.worker.js', import.meta.url), { type: 'module' });
262
293
  worker.port.start();
263
294
  const proxy = Comlink.wrap(worker.port);
264
- let fs;
295
+ let vfs;
265
296
  if (!bufferOrUrl) {
266
- // No buffer or URL provided - create empty filesystem
267
- ({ fs } = await proxy.mount({ mountPoint: '/' }));
297
+ vfs = await proxy.mount({ mountPoint: '/' });
268
298
  }
269
299
  else if (typeof bufferOrUrl === 'string') {
270
- // URL provided - use optimized mountFromUrl for better performance
271
- ({ fs } = await proxy.mountFromUrl({
272
- url: bufferOrUrl,
273
- mountPoint: '/'
274
- }));
300
+ vfs = await proxy.mountFromUrl({ url: bufferOrUrl, mountPoint: '/' });
275
301
  }
276
302
  else {
277
- // Buffer provided - use traditional mount method
278
- ({ fs } = await proxy.mount(Comlink.transfer({ buffer: bufferOrUrl, mountPoint: "/" }, [bufferOrUrl])));
303
+ vfs = await proxy.mount(Comlink.transfer({ buffer: bufferOrUrl, mountPoint: "/" }, [bufferOrUrl]));
279
304
  }
280
- return Comlink.proxy(Vfs.fromMemfs(fs));
305
+ console.debug('Filesystem worker mounted');
306
+ return vfs;
281
307
  };
282
308
  async function* walk(fs, path) {
283
309
  const files = await fs.readDir(path);
@@ -295,16 +321,95 @@ export var Vfs;
295
321
  })(Vfs || (Vfs = {}));
296
322
  export class VolarFs {
297
323
  #fs;
324
+ #fileCache = new Map();
325
+ #statCache = new Map();
326
+ #dirCache = new Map();
298
327
  constructor(fs) {
299
328
  this.#fs = fs;
300
329
  }
301
- async stat(uri) {
330
+ /**
331
+ * Synchronously populate the cache from a pre-resolved map of path → content.
332
+ * This bypasses async VFS reads entirely, ensuring TypeScript gets lib files
333
+ * immediately on first program creation.
334
+ */
335
+ preloadFromMap(files) {
336
+ const now = Date.now();
337
+ for (const [path, content] of Object.entries(files)) {
338
+ this.#fileCache.set(path, content);
339
+ this.#statCache.set(path, {
340
+ type: FileType.File,
341
+ ctime: now,
342
+ mtime: now,
343
+ size: content.length,
344
+ });
345
+ }
346
+ // Build directory tree from file paths
347
+ const dirChildren = new Map();
348
+ for (const path of Object.keys(files)) {
349
+ let dir = path;
350
+ let child = '';
351
+ while (true) {
352
+ const lastSlash = dir.lastIndexOf('/');
353
+ if (lastSlash < 0)
354
+ break;
355
+ child = dir.substring(lastSlash + 1);
356
+ dir = dir.substring(0, lastSlash) || '/';
357
+ if (!dirChildren.has(dir)) {
358
+ dirChildren.set(dir, new Map());
359
+ }
360
+ const children = dirChildren.get(dir);
361
+ // First encounter of this child — it's the file itself
362
+ if (!children.has(child)) {
363
+ // If we've already seen this as a parent dir, it's a Directory
364
+ children.set(child, dirChildren.has(dir === '/' ? `/${child}` : `${dir}/${child}`) ? FileType.Directory : FileType.File);
365
+ }
366
+ if (dir === '/')
367
+ break;
368
+ }
369
+ }
370
+ // Update directory type for children that are actually directories
371
+ for (const [dirPath, children] of dirChildren) {
372
+ for (const [name] of children) {
373
+ const fullPath = dirPath === '/' ? `/${name}` : `${dirPath}/${name}`;
374
+ if (dirChildren.has(fullPath)) {
375
+ children.set(name, FileType.Directory);
376
+ }
377
+ }
378
+ }
379
+ // Cache directory listings and stats
380
+ for (const [dirPath, children] of dirChildren) {
381
+ this.#dirCache.set(dirPath, [...children.entries()]);
382
+ this.#statCache.set(dirPath, {
383
+ type: FileType.Directory,
384
+ ctime: now,
385
+ mtime: now,
386
+ size: 0,
387
+ });
388
+ }
389
+ }
390
+ stat(uri) {
391
+ const cached = this.#statCache.get(uri.path);
392
+ if (cached)
393
+ return cached;
302
394
  return this.#fs.stat(uri.path);
303
395
  }
304
- async readDirectory(uri) {
396
+ readDirectory(uri) {
397
+ // Only use dirCache for node_modules subtree (stable, preloaded).
398
+ // Root and user directories must go through live VFS to pick up new files.
399
+ if (uri.path.startsWith('/node_modules/')) {
400
+ const cached = this.#dirCache.get(uri.path);
401
+ if (cached)
402
+ return cached;
403
+ }
305
404
  return this.#fs.readDir(uri.path);
306
405
  }
307
- async readFile(uri) {
406
+ readFile(uri) {
407
+ const cached = this.#fileCache.get(uri.path);
408
+ if (cached !== undefined)
409
+ return cached;
308
410
  return this.#fs.readFile(uri.path);
309
411
  }
412
+ getCacheSize() {
413
+ return this.#fileCache.size;
414
+ }
310
415
  }
@@ -1,26 +1,37 @@
1
1
  import { VfsInterface } from "../types";
2
- import { LanguageServerClient } from "@marimo-team/codemirror-languageserver";
3
2
  import { Extension } from "@codemirror/state";
4
- import { LanguageServer } from "@volar/language-server";
5
- import { EditorView } from "@codemirror/view";
6
- export type LSPClientExtension = {
7
- client: LanguageServerClient;
8
- } & Extension;
3
+ export declare const FileChangeType: {
4
+ readonly Created: 1;
5
+ readonly Changed: 2;
6
+ readonly Deleted: 3;
7
+ };
8
+ export interface LspLogEntry {
9
+ timestamp: number;
10
+ level: 'error' | 'warn' | 'info' | 'log';
11
+ message: string;
12
+ }
13
+ export declare namespace LspLog {
14
+ function entries(): readonly LspLogEntry[];
15
+ function push(level: LspLogEntry['level'], message: string): void;
16
+ function clear(): void;
17
+ function subscribe(fn: () => void): () => void;
18
+ }
9
19
  export type ClientOptions = {
10
- view: EditorView;
11
20
  language: string;
12
21
  path: string;
13
22
  fs: VfsInterface;
23
+ libFiles?: Record<string, string>;
14
24
  };
15
- export declare const languageServerFactory: Map<string, (args: {
16
- fs: VfsInterface;
17
- }) => Promise<{
18
- server: LanguageServer;
19
- }>>;
20
25
  export declare const lspWorkers: Map<string, SharedWorker>;
21
26
  export declare namespace LSP {
22
- function worker(language: string, fs: VfsInterface): Promise<{
27
+ function worker(language: string, fs: VfsInterface, libFiles?: Record<string, string>): Promise<{
23
28
  worker: SharedWorker;
24
- }>;
25
- function client({ fs, language, path, view }: ClientOptions): Promise<LSPClientExtension>;
29
+ lspPort: MessagePort;
30
+ } | null>;
31
+ function client({ fs, language, path, libFiles }: ClientOptions): Promise<Extension | null>;
32
+ /**
33
+ * Notify all connected LSP clients that a file was created, changed, or deleted.
34
+ * This sends workspace/didChangeWatchedFiles so the server re-evaluates the project.
35
+ */
36
+ function notifyFileChanged(path: string, type?: number): void;
26
37
  }
package/dist/utils/lsp.js CHANGED
@@ -1,74 +1,109 @@
1
1
  import * as Comlink from 'comlink';
2
- import { LanguageServerClient, languageServerWithClient } from "@marimo-team/codemirror-languageserver";
3
- import MessagePortTransport from "../rpc/transport";
4
- import { HighlightStyle } from "@codemirror/language";
5
- import { languageSupportCompartment, renderMarkdownCode } from "../editor";
6
- import markdownit from 'markdown-it';
7
- import { vscodeLightDark } from "../themes/vscode";
2
+ import { LSPClient, languageServerExtensions } from "@codemirror/lsp-client";
3
+ import { messagePortTransport } from "../rpc/transport";
8
4
  const clients = new Map();
9
- // TODO: better fix for this reference sticking around to prevent Comlink from releasing the port
10
- export const languageServerFactory = new Map();
5
+ // FileChangeType from LSP spec
6
+ export const FileChangeType = { Created: 1, Changed: 2, Deleted: 3 };
7
+ const MAX_LOG_ENTRIES = 200;
8
+ const lspLogBuffer = [];
9
+ const lspLogListeners = new Set();
10
+ export var LspLog;
11
+ (function (LspLog) {
12
+ function entries() {
13
+ return lspLogBuffer;
14
+ }
15
+ LspLog.entries = entries;
16
+ function push(level, message) {
17
+ lspLogBuffer.push({ timestamp: Date.now(), level, message });
18
+ if (lspLogBuffer.length > MAX_LOG_ENTRIES) {
19
+ lspLogBuffer.splice(0, lspLogBuffer.length - MAX_LOG_ENTRIES);
20
+ }
21
+ for (const listener of lspLogListeners)
22
+ listener();
23
+ }
24
+ LspLog.push = push;
25
+ function clear() {
26
+ lspLogBuffer.length = 0;
27
+ for (const listener of lspLogListeners)
28
+ listener();
29
+ }
30
+ LspLog.clear = clear;
31
+ function subscribe(fn) {
32
+ lspLogListeners.add(fn);
33
+ return () => { lspLogListeners.delete(fn); };
34
+ }
35
+ LspLog.subscribe = subscribe;
36
+ })(LspLog || (LspLog = {}));
37
+ // Cached factory (Comlink-wrapped) and LSP port per language
38
+ const languageServerFactory = new Map();
39
+ const lspPorts = new Map();
11
40
  export const lspWorkers = new Map();
12
41
  export var LSP;
13
42
  (function (LSP) {
14
- async function worker(language, fs) {
15
- let factory, worker;
16
- console.debug('language', { language });
43
+ async function worker(language, fs, libFiles) {
44
+ let factory;
45
+ let worker;
17
46
  switch (language) {
18
47
  case 'javascript':
19
48
  case 'typescript':
20
49
  factory = languageServerFactory.get('javascript');
21
50
  worker = lspWorkers.get('javascript');
22
- console.debug('got worker', { worker, factory });
23
51
  if (!factory) {
24
52
  worker = new SharedWorker(new URL('../workers/javascript.worker.js', import.meta.url), { type: 'module' });
25
53
  worker.port.start();
26
54
  lspWorkers.set('javascript', worker);
27
- const { createLanguageServer } = Comlink.wrap(worker.port);
28
- factory = createLanguageServer;
55
+ const wrapped = Comlink.wrap(worker.port);
56
+ factory = wrapped.createLanguageServer;
29
57
  languageServerFactory.set('javascript', factory);
30
58
  }
31
59
  break;
60
+ default:
61
+ return null;
32
62
  }
33
- await factory?.(Comlink.proxy({ fs }));
34
- return { worker };
63
+ // fs is proxied (has methods), libFiles is plain data (structured clone)
64
+ // The factory returns a MessagePort for the LSP connection (separate from Comlink's port)
65
+ const lspPort = await factory(Comlink.proxy(fs), libFiles);
66
+ lspPort.start();
67
+ lspPorts.set(language, lspPort);
68
+ return { worker: worker, lspPort };
35
69
  }
36
70
  LSP.worker = worker;
37
- async function client({ fs, language, path, view }) {
71
+ async function client({ fs, language, path, libFiles }) {
38
72
  let client = clients.get(language);
39
- let clientExtension;
40
73
  const uri = `file:///${path}`;
41
74
  if (!client) {
42
- const { worker } = await LSP.worker(language, fs);
43
- if (!worker)
75
+ const result = await LSP.worker(language, fs, libFiles);
76
+ if (!result)
44
77
  return null;
45
- console.debug('got worker', { worker });
46
- client = new LanguageServerClient({
47
- transport: new MessagePortTransport(worker.port),
78
+ const { lspPort } = result;
79
+ client = new LSPClient({
48
80
  rootUri: 'file:///',
49
- workspaceFolders: [{ name: 'workspace', uri: 'file:///' }]
81
+ extensions: languageServerExtensions(),
82
+ notificationHandlers: {
83
+ "window/logMessage": (_client, params) => {
84
+ const level = params.type === 1 ? 'error' : params.type === 2 ? 'warn' : params.type === 3 ? 'info' : 'log';
85
+ LspLog.push(level, params.message);
86
+ return false; // fall through to default handler (console)
87
+ }
88
+ },
50
89
  });
90
+ client.connect(messagePortTransport(lspPort));
91
+ clients.set(language, client);
51
92
  }
52
- clients.set(language, client);
53
- clientExtension = { client, extension: [] };
54
- clientExtension.extension = languageServerWithClient({
55
- client: clientExtension.client,
56
- documentUri: uri,
57
- languageId: language,
58
- allowHTMLContent: true,
59
- markdownRenderer(markdown) {
60
- const support = languageSupportCompartment.get(view.state);
61
- const highlighter = vscodeLightDark[1].find(item => item.value instanceof HighlightStyle)?.value;
62
- const parser = support.language?.parser;
63
- const md = markdownit({
64
- highlight: (str) => {
65
- return renderMarkdownCode(str, parser, highlighter);
66
- }
67
- });
68
- return md.render(markdown);
69
- },
70
- });
71
- return clientExtension;
93
+ return client.plugin(uri, language);
72
94
  }
73
95
  LSP.client = client;
96
+ /**
97
+ * Notify all connected LSP clients that a file was created, changed, or deleted.
98
+ * This sends workspace/didChangeWatchedFiles so the server re-evaluates the project.
99
+ */
100
+ function notifyFileChanged(path, type = FileChangeType.Changed) {
101
+ const uri = `file:///${path}`;
102
+ for (const client of clients.values()) {
103
+ client.notification("workspace/didChangeWatchedFiles", {
104
+ changes: [{ uri, type }]
105
+ });
106
+ }
107
+ }
108
+ LSP.notifyFileChanged = notifyFileChanged;
74
109
  })(LSP || (LSP = {}));
@@ -15,7 +15,9 @@ export type HighlightedSearch = SearchResult & {
15
15
  };
16
16
  export declare class SearchIndex {
17
17
  index: MiniSearch;
18
+ savePath?: string;
18
19
  constructor(index: MiniSearch);
20
+ add(path: string): void;
19
21
  search(...params: Parameters<MiniSearch['search']>): HighlightedSearch[];
20
22
  /**
21
23
  *
@@ -7,9 +7,16 @@ export const defaultFilter = (path) => {
7
7
  };
8
8
  export class SearchIndex {
9
9
  index;
10
+ /// The VFS path this index was loaded from / saved to, if known.
11
+ savePath;
10
12
  constructor(index) {
11
13
  this.index = index;
12
14
  }
15
+ add(path) {
16
+ if (!this.index.has(path)) {
17
+ this.index.add({ path });
18
+ }
19
+ }
13
20
  search(...params) {
14
21
  const results = this.index.search(...params);
15
22
  const highlights = this.highlight(results);
@@ -48,10 +55,12 @@ export class SearchIndex {
48
55
  return new SearchIndex(index);
49
56
  }
50
57
  static async get(fs, path, fields = defaultFields) {
51
- const index = await fs.exists(path) ? await fs.readFile(path) : null;
52
- return index ?
53
- SearchIndex.from(index, fields) :
54
- SearchIndex.build(fs, { fields, idField: 'path' }).then(index => index.save(fs, path));
58
+ const data = await fs.exists(path) ? await fs.readFile(path) : null;
59
+ let index = data
60
+ ? SearchIndex.from(data, fields)
61
+ : await SearchIndex.build(fs, { fields, idField: 'path' }).then(idx => idx.save(fs, path));
62
+ index.savePath = path;
63
+ return index;
55
64
  }
56
65
  static async build(fs, { filter = defaultFilter, ...rest }) {
57
66
  const index = new MiniSearch({ ...rest });
@@ -0,0 +1,57 @@
1
+ import { VfsInterface } from "../types";
2
+ export type TypescriptDefaultsConfig = {
3
+ /** ES target, determines which lib files are needed. Default: "ES2020" */
4
+ target?: string;
5
+ /** Additional lib names to load beyond the ES target libs.
6
+ * Defaults to TypeScript's `.full` environment libs (dom, dom.iterable,
7
+ * dom.asynciterable, webworker.importscripts, scripthost).
8
+ * Pass `[]` to disable. */
9
+ additionalLibs?: string[];
10
+ /** Custom tsconfig compilerOptions merged with defaults */
11
+ compilerOptions?: Record<string, any>;
12
+ };
13
+ /**
14
+ * Returns the list of TypeScript lib file names required for a given ES target,
15
+ * plus any additional libs (DOM by default).
16
+ * Names are without the `lib.` prefix and `.d.ts` suffix (e.g. "es5", "dom").
17
+ */
18
+ export declare function getRequiredLibs(target?: string, additionalLibs?: string[]): string[];
19
+ /**
20
+ * Returns all individual lib names for the tsconfig `lib` field.
21
+ *
22
+ * Lists every individual lib file (e.g. "es5", "es2015.promise", "dom") instead of
23
+ * just the top-level entry (e.g. "ES2020"). This is critical for browser-based
24
+ * TypeScript via Volar: the virtual filesystem is async, so each
25
+ * `/// <reference lib="..." />` chain level requires a separate async round-trip.
26
+ * By listing all libs explicitly, TypeScript loads them all in a single pass.
27
+ */
28
+ export declare function getLibFieldForTarget(target?: string, additionalLibs?: string[]): string[];
29
+ /**
30
+ * Returns the cached lib file contents from the last prefill, if available.
31
+ * These are keyed by full path (e.g. "/node_modules/typescript/lib/lib.es5.d.ts").
32
+ * Includes the tsconfig.json content as well.
33
+ */
34
+ export declare function getCachedLibFiles(): Record<string, string> | undefined;
35
+ /**
36
+ * Pre-fills the virtual filesystem with TypeScript default lib definitions and tsconfig.
37
+ * Writes to `/node_modules/typescript/lib/` where Volar's TypeScript language server
38
+ * expects to find them in a browser environment.
39
+ *
40
+ * - Skips files that already exist on the filesystem
41
+ * - Only runs once per session (subsequent calls are no-ops)
42
+ * - Should be called lazily when a TypeScript file is first opened
43
+ *
44
+ * Returns a map of file paths to their contents for direct use by the LSP worker,
45
+ * bypassing the need for the worker to read through nested Comlink proxies.
46
+ *
47
+ * @param fs - Virtual filesystem to write to
48
+ * @param resolveLib - Function that resolves a lib name to its `.d.ts` content.
49
+ * Receives names like "es5", "es2015.collection".
50
+ * In Vite, use `import.meta.glob('typescript/lib/*.d.ts', { query: '?raw' })`.
51
+ * @param config - Optional target and tsconfig overrides
52
+ */
53
+ export declare function prefillTypescriptDefaults(fs: VfsInterface, resolveLib: (name: string) => Promise<string>, config?: TypescriptDefaultsConfig): Promise<Record<string, string>>;
54
+ /**
55
+ * Resets the prefilled state. Primarily useful for testing.
56
+ */
57
+ export declare function resetPrefillState(): void;