@vesk/compiler 0.2.45 → 0.2.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { parse, createBaseParser } from '@vesk/compiler/src/parser';
1
+ export { parse, createBaseParser, parseGeneratedJs } from '@vesk/compiler/src/parser';
2
2
  export { VeskParserPlugin } from '@vesk/compiler/src/vesk-plugin';
3
3
  export { generateIR } from '@vesk/compiler/src/ir-generator';
4
4
  export { render } from '@vesk/compiler/src/server-codegen';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAC7D,OAAO,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AAC3D,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EACL,MAAM,EACN,WAAW,EACX,UAAU,EACV,QAAQ,EACR,cAAc,EACd,mBAAmB,EACnB,SAAS,EACT,aAAa,EACb,UAAU,GACX,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAEpD,YAAY,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,YAAY,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,YAAY,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,YAAY,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,YAAY,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AACtH,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACjG,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,YAAY,EACV,QAAQ,EACR,WAAW,EACX,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACtF,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAC7D,OAAO,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AAC3D,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EACL,MAAM,EACN,WAAW,EACX,UAAU,EACV,QAAQ,EACR,cAAc,EACd,mBAAmB,EACnB,SAAS,EACT,aAAa,EACb,UAAU,GACX,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAEpD,YAAY,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,YAAY,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,YAAY,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,YAAY,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,YAAY,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AACtH,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACjG,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,YAAY,EACV,QAAQ,EACR,WAAW,EACX,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,4BAA4B,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { parse, createBaseParser } from '@vesk/compiler/src/parser';
1
+ export { parse, createBaseParser, parseGeneratedJs } from '@vesk/compiler/src/parser';
2
2
  export { VeskParserPlugin } from '@vesk/compiler/src/vesk-plugin';
3
3
  export { generateIR } from '@vesk/compiler/src/ir-generator';
4
4
  export { render } from '@vesk/compiler/src/server-codegen';
@@ -1 +1 @@
1
- {"version":3,"file":"module-imports.d.ts","sourceRoot":"","sources":["../src/module-imports.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"module-imports.d.ts","sourceRoot":"","sources":["../src/module-imports.ts"],"names":[],"mappings":"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"}
@@ -301,15 +301,70 @@ function depsFresh(cachedVal) {
301
301
  }
302
302
  return true;
303
303
  }
304
- export function readSsrSource(absPath) {
304
+ /**
305
+ * Raw file contents, keyed by path and validated by mtime.
306
+ *
307
+ * `readSsrSource` is on the hot path of every `.vsk` compile: a page's module
308
+ * bundle pulls in its `.ts`/`.js` imports, and a docs-style app where 26 pages
309
+ * all import one content module read and re-parsed that module 26 times. The
310
+ * parsed AST is deliberately NOT shared (callers mutate it — `stripTsTypes`,
311
+ * `stripped.body = ...`) and is still produced per call; only the immutable
312
+ * text is reused, and only while the file's mtime is unchanged, so an edit in
313
+ * a watch session is picked up immediately.
314
+ */
315
+ const rawSourceCache = new Map();
316
+ /**
317
+ * Bounded, insertion-ordered eviction.
318
+ *
319
+ * The cap has to clear the OLDEST entries, not the whole map: an app that
320
+ * imports an icon barrel walks 1,600+ modules, and a 512-entry cap with
321
+ * `clear()` on overflow thrashed itself into a 0% hit rate (11,235 reads of
322
+ * 1,620 distinct files on vesk-doc). Draining the front keeps the hot modules
323
+ * resident and bounds memory.
324
+ */
325
+ const BUILD_CACHE_MAX = 8192;
326
+ const BUILD_CACHE_EVICT = 2048;
327
+ function evict(cache) {
328
+ if (cache.size <= BUILD_CACHE_MAX)
329
+ return;
330
+ let dropped = 0;
331
+ for (const key of cache.keys()) {
332
+ cache.delete(key);
333
+ if (++dropped >= BUILD_CACHE_EVICT)
334
+ break;
335
+ }
336
+ }
337
+ function readSourceCached(absPath) {
338
+ let mtimeMs;
339
+ try {
340
+ mtimeMs = statSync(absPath).mtimeMs;
341
+ }
342
+ catch {
343
+ return null;
344
+ }
345
+ const hit = rawSourceCache.get(absPath);
346
+ if (hit && hit.mtimeMs === mtimeMs)
347
+ return hit.raw;
305
348
  let raw;
306
349
  try {
307
350
  raw = readFileSync(absPath, 'utf-8');
308
351
  }
309
- catch (err) {
310
- console.warn(`[vesk] SSR: failed to read ${absPath}: ${err?.message ?? String(err)}`);
352
+ catch {
353
+ return null;
354
+ }
355
+ rawSourceCache.set(absPath, { mtimeMs, raw });
356
+ evict(rawSourceCache);
357
+ return raw;
358
+ }
359
+ export function readSsrSource(absPath) {
360
+ const cachedRaw = readSourceCached(absPath);
361
+ if (cachedRaw === null) {
362
+ // Keep the original warning: a missing module is a build-time signal, not
363
+ // a cache miss to swallow.
364
+ console.warn(`[vesk] SSR: failed to read ${absPath}`);
311
365
  return null;
312
366
  }
367
+ const raw = cachedRaw;
313
368
  let ast = null;
314
369
  try {
315
370
  ast = parse(raw, { filename: absPath });
@@ -1208,18 +1263,44 @@ export function collectModuleBundled(absPath, collector) {
1208
1263
  collector.modules.push({ key, code: `module.exports = ${raw};`, dir, deps: {} });
1209
1264
  return key;
1210
1265
  }
1211
- const src = readSsrSource(absPath);
1212
- if (!src || !src.ast) {
1213
- const raw = src ? src.raw : '// unreadable during build\nmodule.exports = {};';
1214
- collector.modules.push({ key, code: raw, dir, deps: {} });
1215
- return key;
1266
+ // The transpiled code and the relative specifiers of a module do not depend
1267
+ // on which collector is asking, only on the file — and every `.vsk` compile
1268
+ // builds its own collector, so a docs-style app whose 26 pages all import one
1269
+ // content module read, parsed, stripped and re-transpiled that module 26
1270
+ // times (51s of a 95s build). Cache the result against the file's mtime and
1271
+ // skip the read AND the parse on a hit: callers mutate the AST they get
1272
+ // (`stripTsTypes`, `stripped.body = ...`), so the AST itself stays private to
1273
+ // the call that created it.
1274
+ let mtimeMs = -1;
1275
+ try {
1276
+ mtimeMs = statSync(absPath).mtimeMs;
1277
+ }
1278
+ catch { /* unreadable: fall through to the uncached path */ }
1279
+ const cached = mtimeMs >= 0 ? bundledCodeCache.get(absPath) : undefined;
1280
+ let code;
1281
+ let specifiers;
1282
+ if (cached && cached.mtimeMs === mtimeMs) {
1283
+ code = cached.code;
1284
+ specifiers = cached.specifiers;
1285
+ }
1286
+ else {
1287
+ const src = readSsrSource(absPath);
1288
+ if (!src || !src.ast) {
1289
+ const raw = src ? src.raw : '// unreadable during build\nmodule.exports = {};';
1290
+ collector.modules.push({ key, code: raw, dir, deps: {} });
1291
+ return key;
1292
+ }
1293
+ let stripped = src.ast;
1294
+ if (hasTsSyntax(src.ast))
1295
+ stripped = stripTsTypes(src.ast);
1296
+ stripped.body = (stripped.body || []).filter((n) => (n ? !isTypeOnlyStatement(n) : false));
1297
+ specifiers = [...collectModuleSpecifiers(src.ast)];
1298
+ code = esmToCjs(stripped.body);
1299
+ bundledCodeCache.set(absPath, { mtimeMs, code, specifiers });
1300
+ evict(bundledCodeCache);
1216
1301
  }
1217
- let stripped = src.ast;
1218
- if (hasTsSyntax(src.ast))
1219
- stripped = stripTsTypes(src.ast);
1220
- stripped.body = (stripped.body || []).filter((n) => (n ? !isTypeOnlyStatement(n) : false));
1221
1302
  const deps = {};
1222
- for (const spec of collectModuleSpecifiers(src.ast)) {
1303
+ for (const spec of specifiers) {
1223
1304
  if (!isRelativeSpec(spec))
1224
1305
  continue;
1225
1306
  const depPath = resolveSsrModule(spec, dir);
@@ -1227,9 +1308,10 @@ export function collectModuleBundled(absPath, collector) {
1227
1308
  continue;
1228
1309
  deps[spec] = collectModuleBundled(depPath, collector);
1229
1310
  }
1230
- collector.modules.push({ key, code: esmToCjs(stripped.body), dir, deps });
1311
+ collector.modules.push({ key, code, dir, deps });
1231
1312
  return key;
1232
1313
  }
1314
+ const bundledCodeCache = new Map();
1233
1315
  /**
1234
1316
  * Builds the module bundle + local-binding list for a set of import lines
1235
1317
  * (a `.vsk` file's `imports`). Framework-owned, `.vsk`, `.css` and markdown
package/dist/parser.d.ts CHANGED
@@ -34,5 +34,23 @@ export declare function preprocessForClauses(source: string): {
34
34
  annotations: VeskAnnotation[];
35
35
  };
36
36
  export declare function createBaseParser(): typeof acorn.Parser;
37
+ /**
38
+ * Parse GENERATED JavaScript with plain acorn — no TypeScript plugin, no
39
+ * `{#clause}` preprocessing, no comment blanking.
40
+ *
41
+ * The build parses every emitted module several times over (strip imports,
42
+ * strip exports, collect runtime names, fold chunk imports), and on a CPU
43
+ * profile of a 28-route build that parsing was two thirds of the whole build.
44
+ * None of those passes need the `.vsk` pipeline: they only read top-level
45
+ * import/export nodes out of code the code generator just produced, and acorn
46
+ * reports the same offsets either way (blankComments and friends preserve
47
+ * length). Measured on a 43 KB emitted chunk: 180 ms with the `.vsk` parser,
48
+ * 36 ms here.
49
+ *
50
+ * Returns `null` when the source is not plain ES — anything with JSX, type
51
+ * syntax or `{#clause}` markers — so callers fall back to `parse()` and keep
52
+ * exactly the behaviour they had.
53
+ */
54
+ export declare function parseGeneratedJs(source: string): Program | null;
37
55
  export declare function parse(source: string, options?: ParseOptions): Program;
38
56
  //# sourceMappingURL=parser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAOtC,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,YAAY,CAAC;IACnB,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA0BD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,cAAc,EAAE,CAAA;CAAE,CA4LpG;AAED,wBAAgB,gBAAgB,IAAI,OAAO,KAAK,CAAC,MAAM,CAEtD;AAED,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAuEzE"}
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAOtC,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,YAAY,CAAC;IACnB,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA0BD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,cAAc,EAAE,CAAA;CAAE,CA4LpG;AAED,wBAAgB,gBAAgB,IAAI,OAAO,KAAK,CAAC,MAAM,CAEtD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAM/D;AAED,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAuEzE"}
package/dist/parser.js CHANGED
@@ -256,6 +256,31 @@ export function preprocessForClauses(source) {
256
256
  export function createBaseParser() {
257
257
  return acorn.Parser.extend(tsPlugin({}), VeskParserPlugin());
258
258
  }
259
+ /**
260
+ * Parse GENERATED JavaScript with plain acorn — no TypeScript plugin, no
261
+ * `{#clause}` preprocessing, no comment blanking.
262
+ *
263
+ * The build parses every emitted module several times over (strip imports,
264
+ * strip exports, collect runtime names, fold chunk imports), and on a CPU
265
+ * profile of a 28-route build that parsing was two thirds of the whole build.
266
+ * None of those passes need the `.vsk` pipeline: they only read top-level
267
+ * import/export nodes out of code the code generator just produced, and acorn
268
+ * reports the same offsets either way (blankComments and friends preserve
269
+ * length). Measured on a 43 KB emitted chunk: 180 ms with the `.vsk` parser,
270
+ * 36 ms here.
271
+ *
272
+ * Returns `null` when the source is not plain ES — anything with JSX, type
273
+ * syntax or `{#clause}` markers — so callers fall back to `parse()` and keep
274
+ * exactly the behaviour they had.
275
+ */
276
+ export function parseGeneratedJs(source) {
277
+ try {
278
+ return acorn.parse(source, { ecmaVersion: 'latest', sourceType: 'module' });
279
+ }
280
+ catch {
281
+ return null;
282
+ }
283
+ }
259
284
  export function parse(source, options = {}) {
260
285
  const ParserClass = createBaseParser();
261
286
  const { code, annotations } = preprocessForClauses(blankComments(source));
@@ -10,4 +10,6 @@ import type { PrecompileFilePlan } from '@vesk/compiler/src/precompile-runtime';
10
10
  * access and no `.vsk` source embedded in the deployment.
11
11
  */
12
12
  export declare function precompileFile(source: string, sourcePath?: string): PrecompileFilePlan;
13
+ /** Drop the plan cache (tests, and any caller that compiles outside a build). */
14
+ export declare function clearPrecompileCache(): void;
13
15
  //# sourceMappingURL=precompile.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"precompile.d.ts","sourceRoot":"","sources":["../src/precompile.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EACV,kBAAkB,EAGnB,MAAM,uCAAuC,CAAC;AAG/C;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,kBAAkB,CAEtF"}
1
+ {"version":3,"file":"precompile.d.ts","sourceRoot":"","sources":["../src/precompile.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EACV,kBAAkB,EAGnB,MAAM,uCAAuC,CAAC;AAG/C;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,kBAAkB,CAYtF;AAeD,iFAAiF;AACjF,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C"}
@@ -20,7 +20,36 @@ import { irToJSON } from '@vesk/compiler/src/precompile-runtime';
20
20
  * access and no `.vsk` source embedded in the deployment.
21
21
  */
22
22
  export function precompileFile(source, sourcePath) {
23
- return precompileFileInternal(source, sourcePath, new Set());
23
+ if (sourcePath === undefined)
24
+ return precompileFileInternal(source, sourcePath, new Set());
25
+ const hit = precompileCache.get(sourcePath);
26
+ // Compare the source, not just the path: the dev server rebuilds in-process
27
+ // and a stale plan would silently ship yesterday's component.
28
+ if (hit !== undefined && hit.source === source)
29
+ return hit.plan;
30
+ const plan = precompileFileInternal(source, sourcePath, new Set());
31
+ // Bound it: plans are sizeable on a large app, and a watch session can
32
+ // outlive many builds.
33
+ if (precompileCache.size >= 512)
34
+ precompileCache.clear();
35
+ precompileCache.set(sourcePath, { source, plan });
36
+ return plan;
37
+ }
38
+ /**
39
+ * Compiled plans, keyed by absolute path.
40
+ *
41
+ * Every generated SSR function precompiles its own page, its layout chain, its
42
+ * error boundary and every shared component it pulls in, so a build compiled
43
+ * the same `layout.vsk` once per route and each shared `components/*.vsk` once
44
+ * per route that used it — 25x the root layout and 28x a shared counter on the
45
+ * 28-route test-app, i.e. work growing as routes x shared files. The plan is a
46
+ * pure function of (source, path) and callers only JSON.stringify it into the
47
+ * generated function, so one compile per file per build is enough.
48
+ */
49
+ const precompileCache = new Map();
50
+ /** Drop the plan cache (tests, and any caller that compiles outside a build). */
51
+ export function clearPrecompileCache() {
52
+ precompileCache.clear();
24
53
  }
25
54
  function precompileFileInternal(source, sourcePath, seenImportFiles) {
26
55
  if (sourcePath) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vesk/compiler",
3
- "version": "0.2.45",
3
+ "version": "0.2.47",
4
4
  "type": "module",
5
5
  "description": "Vesk compiler — lexer, parser, semantic analysis, IR, codegen",
6
6
  "main": "./dist/index.js",
@@ -20,7 +20,7 @@
20
20
  },
21
21
  "dependencies": {
22
22
  "@sveltejs/acorn-typescript": "^1.0.0",
23
- "@vesk/types": "^0.2.45",
23
+ "@vesk/types": "^0.2.47",
24
24
  "acorn": "^8.15.0",
25
25
  "esrap": "^2.3.0",
26
26
  "is-reference": "^3.0.0",