@vesk/vesk-cli 0.2.40 → 0.2.42

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 (2) hide show
  1. package/dist/cli.js +171 -39
  2. package/package.json +5 -5
package/dist/cli.js CHANGED
@@ -2283,6 +2283,16 @@ function runInHydrateBlock(fn) {
2283
2283
  throw error;
2284
2284
  }
2285
2285
  }
2286
+ function recordClaim(root3, record) {
2287
+ claimRegistry.set(record.el, record);
2288
+ if (!root3) return;
2289
+ const records = walkerClaimRecords.get(root3);
2290
+ if (records) records.push(record);
2291
+ else walkerClaimRecords.set(root3, [record]);
2292
+ }
2293
+ function isAdopted(el) {
2294
+ return claimRegistry.get(el)?.state === "adopted";
2295
+ }
2286
2296
  function reportHydrationIssue(issue) {
2287
2297
  if (!__mismatchHandler) return;
2288
2298
  try {
@@ -2304,13 +2314,15 @@ function reportMiss(kind, detail, loud) {
2304
2314
  }
2305
2315
  if (loud) devWarn(`${kind}: ${detail}`);
2306
2316
  }
2307
- function stampClaimed(el) {
2308
- if (__hydrateDevMode) {
2309
- try {
2310
- el.setAttribute(CLAIMED_ATTR, "");
2311
- } catch {
2312
- }
2313
- }
2317
+ function recordAdopted(root3, el, expectedTag) {
2318
+ recordClaim(root3, {
2319
+ state: "adopted",
2320
+ el,
2321
+ owner: root3,
2322
+ expectedTag,
2323
+ actualTag: el.tagName.toLowerCase(),
2324
+ detached: false
2325
+ });
2314
2326
  }
2315
2327
  function stripDirectTextNodes(el) {
2316
2328
  let text = "";
@@ -2361,8 +2373,8 @@ function scanTwins(container) {
2361
2373
  let a = false;
2362
2374
  let b = false;
2363
2375
  try {
2364
- a = el.hasAttribute(CLAIMED_ATTR);
2365
- b = prev.hasAttribute(CLAIMED_ATTR);
2376
+ a = isAdopted(el);
2377
+ b = isAdopted(prev);
2366
2378
  } catch {
2367
2379
  continue;
2368
2380
  }
@@ -2405,6 +2417,30 @@ function auditHydration(container) {
2405
2417
  }
2406
2418
  if (!sample && container.outerHTML) sample = String(container.outerHTML).slice(0, 800);
2407
2419
  }
2420
+ const structuralRecords = [];
2421
+ const seenRecords = /* @__PURE__ */ new Set();
2422
+ const collectWalkerRecords = (el) => {
2423
+ const records = walkerClaimRecords.get(el);
2424
+ if (records) {
2425
+ for (const record of records) {
2426
+ if (!seenRecords.has(record)) {
2427
+ seenRecords.add(record);
2428
+ structuralRecords.push(record);
2429
+ }
2430
+ }
2431
+ }
2432
+ for (let i = 0; i < el.children.length; i++) collectWalkerRecords(el.children[i]);
2433
+ };
2434
+ collectWalkerRecords(container);
2435
+ const structuralGhosts = structuralRecords.filter((record) => record.state === "ghost");
2436
+ if (structuralGhosts.length > 0) {
2437
+ unclaimed += structuralGhosts.length;
2438
+ if (orphanNames.length < 5) {
2439
+ orphanNames.push(
2440
+ `${structuralGhosts.length} structural hydration ghost(s) (${structuralGhosts.slice(0, 3).map((record) => `<${record.actualTag}> expected <${record.expectedTag}>`).join(", ")})`
2441
+ );
2442
+ }
2443
+ }
2408
2444
  const twinRes = scanTwins(container);
2409
2445
  const issues = [];
2410
2446
  if (unclaimed > 0) {
@@ -2432,6 +2468,14 @@ function auditHydration(container) {
2432
2468
  } catch {
2433
2469
  }
2434
2470
  }
2471
+ for (const record of structuralGhosts) {
2472
+ try {
2473
+ if (record.detached || !record.owner || record.el.parentNode !== record.owner) continue;
2474
+ record.el.remove();
2475
+ record.detached = true;
2476
+ } catch {
2477
+ }
2478
+ }
2435
2479
  }
2436
2480
  if (__mismatchHandler) {
2437
2481
  for (const issue of issues) {
@@ -2462,7 +2506,7 @@ ${report.sample}`
2462
2506
  }
2463
2507
  function underClaimedAncestor(el, container) {
2464
2508
  for (let n = el; n && n !== container; n = n.parentElement) {
2465
- if (n.nodeType === 1 && n.hasAttribute(CLAIMED_ATTR)) return true;
2509
+ if (n.nodeType === 1 && isAdopted(n)) return true;
2466
2510
  }
2467
2511
  return false;
2468
2512
  }
@@ -2523,7 +2567,7 @@ function checkMarkerIdentity(marker, el) {
2523
2567
  }
2524
2568
  }
2525
2569
  }
2526
- function adoptElement(marker, tag) {
2570
+ function adoptElement(root3, marker, tag) {
2527
2571
  const el = marker.nextElementSibling;
2528
2572
  if (!el) {
2529
2573
  marker.remove();
@@ -2534,10 +2578,10 @@ function adoptElement(marker, tag) {
2534
2578
  marker.remove();
2535
2579
  stripDirectTextNodes(el);
2536
2580
  captureSsrElementChildren(el);
2537
- stampClaimed(el);
2581
+ recordAdopted(root3, el, tag || el.tagName.toLowerCase());
2538
2582
  return el;
2539
2583
  }
2540
- function adoptElementRaw(marker, tag) {
2584
+ function adoptElementRaw(root3, marker, tag) {
2541
2585
  const el = marker.nextElementSibling;
2542
2586
  if (!el) {
2543
2587
  marker.remove();
@@ -2547,7 +2591,7 @@ function adoptElementRaw(marker, tag) {
2547
2591
  checkMarkerIdentity(marker, el);
2548
2592
  marker.remove();
2549
2593
  captureSsrElementChildren(el);
2550
- stampClaimed(el);
2594
+ recordAdopted(root3, el, tag || el.tagName.toLowerCase());
2551
2595
  return el;
2552
2596
  }
2553
2597
  function createHydrateWalker(container, markerList) {
@@ -2752,14 +2796,15 @@ function hydrateOnInteraction(container, componentFn, props, options = {}) {
2752
2796
  }
2753
2797
  };
2754
2798
  }
2755
- var __hydrateDevMode, CLAIMED_ATTR, __hydrateStrict, __mismatchHandler, _SHOW_COMMENT, _FILTER_ACCEPT, _FILTER_SKIP, WalkerEngine, StructuralWalker;
2799
+ var __hydrateDevMode, claimRegistry, walkerClaimRecords, __hydrateStrict, __mismatchHandler, _SHOW_COMMENT, _FILTER_ACCEPT, _FILTER_SKIP, WalkerEngine, StructuralWalker;
2756
2800
  var init_hydrate = __esm({
2757
2801
  "../runtime/src/hydrate.ts"() {
2758
2802
  "use strict";
2759
2803
  init_ripple_runtime();
2760
2804
  init_ripple_blocks();
2761
2805
  __hydrateDevMode = true;
2762
- CLAIMED_ATTR = "data-vsk-claimed";
2806
+ claimRegistry = /* @__PURE__ */ new WeakMap();
2807
+ walkerClaimRecords = /* @__PURE__ */ new WeakMap();
2763
2808
  __hydrateStrict = false;
2764
2809
  __mismatchHandler = null;
2765
2810
  _SHOW_COMMENT = 128;
@@ -2844,7 +2889,7 @@ var init_hydrate = __esm({
2844
2889
  break;
2845
2890
  }
2846
2891
  this.idx++;
2847
- const adopted = adoptElement(tm.comment, tag);
2892
+ const adopted = adoptElement(this.root, tm.comment, tag);
2848
2893
  if (adopted === null) {
2849
2894
  tm.state = "claimed";
2850
2895
  break;
@@ -2896,7 +2941,7 @@ ${String(this.root.outerHTML).slice(0, 800)}` : ""),
2896
2941
  break;
2897
2942
  }
2898
2943
  this.idx++;
2899
- const adopted = adoptElementRaw(tm.comment, tag);
2944
+ const adopted = adoptElementRaw(this.root, tm.comment, tag);
2900
2945
  if (adopted === null) {
2901
2946
  tm.state = "claimed";
2902
2947
  break;
@@ -2986,7 +3031,7 @@ ${String(this.root.outerHTML).slice(0, 800)}` : ""),
2986
3031
  tm.comment.remove();
2987
3032
  stripDirectTextNodes(el);
2988
3033
  captureSsrElementChildren(el);
2989
- stampClaimed(el);
3034
+ recordAdopted(this.root, el, el.tagName.toLowerCase());
2990
3035
  return { el };
2991
3036
  }
2992
3037
  claimByKey(key, options) {
@@ -3160,12 +3205,37 @@ ${String(this.root.outerHTML).slice(0, 800)}` : ""),
3160
3205
  const el = this.els[target];
3161
3206
  if (tag && el.tagName.toLowerCase() !== tag) {
3162
3207
  this.idx = this.advancePastSkippable(target + 1);
3208
+ const ghost = {
3209
+ state: "ghost",
3210
+ el,
3211
+ owner: this.root,
3212
+ expectedTag: tag || "div",
3213
+ actualTag: el.tagName.toLowerCase(),
3214
+ detached: false
3215
+ };
3216
+ recordClaim(this.root, ghost);
3217
+ if (this.root && el.parentNode === this.root) {
3218
+ try {
3219
+ el.remove();
3220
+ ghost.detached = true;
3221
+ } catch {
3222
+ }
3223
+ }
3224
+ const fresh2 = document.createElement(tag || "div");
3225
+ recordClaim(this.root, {
3226
+ state: "fresh",
3227
+ el: fresh2,
3228
+ owner: this.root,
3229
+ expectedTag: tag || "div",
3230
+ actualTag: fresh2.tagName.toLowerCase(),
3231
+ detached: false
3232
+ });
3163
3233
  reportMiss(
3164
3234
  "tag-mismatch",
3165
- `descriptor wanted <${tag}> but the SSR slot holds <${el.tagName.toLowerCase()}>; consuming one slot and rendering fresh.`,
3235
+ `descriptor wanted <${tag}> but the SSR slot holds <${el.tagName.toLowerCase()}>; consumed one slot, detached the local ghost, and rendered fresh.`,
3166
3236
  false
3167
3237
  );
3168
- return document.createElement(tag || "div");
3238
+ return fresh2;
3169
3239
  }
3170
3240
  if (this.adopted.has(el)) {
3171
3241
  target = this.advancePastSkippable(target + 1);
@@ -3175,10 +3245,19 @@ ${String(this.root.outerHTML).slice(0, 800)}` : ""),
3175
3245
  this.adopted.add(el);
3176
3246
  if (!raw2) stripDirectTextNodes(el);
3177
3247
  captureSsrElementChildren(el);
3178
- stampClaimed(el);
3248
+ recordAdopted(this.root, el, tag || el.tagName.toLowerCase());
3179
3249
  return el;
3180
3250
  }
3181
3251
  this.idx = this.els.length;
3252
+ const fresh = document.createElement(tag || "div");
3253
+ recordClaim(this.root, {
3254
+ state: "fresh",
3255
+ el: fresh,
3256
+ owner: this.root,
3257
+ expectedTag: tag || "div",
3258
+ actualTag: fresh.tagName.toLowerCase(),
3259
+ detached: false
3260
+ });
3182
3261
  reportMiss(
3183
3262
  "exhausted",
3184
3263
  `hydration claim missed <${tag || "element"}>; the client rendered more or different content than SSR.`,
@@ -3187,7 +3266,7 @@ ${String(this.root.outerHTML).slice(0, 800)}` : ""),
3187
3266
  // nodes) — telemetry-only, mirroring the marker engine.
3188
3267
  false
3189
3268
  );
3190
- return document.createElement(tag || "div");
3269
+ return fresh;
3191
3270
  }
3192
3271
  nextElement(tag, skipK = 0) {
3193
3272
  return this.claimAt(tag, skipK, false);
@@ -3236,7 +3315,7 @@ ${String(this.root.outerHTML).slice(0, 800)}` : ""),
3236
3315
  this.adopted.add(el);
3237
3316
  stripDirectTextNodes(el);
3238
3317
  captureSsrElementChildren(el);
3239
- stampClaimed(el);
3318
+ recordAdopted(this.root, el, el.tagName.toLowerCase());
3240
3319
  return { el };
3241
3320
  }
3242
3321
  this.idx = this.els.length;
@@ -23367,7 +23446,8 @@ function Form(props, registry, hydrate) {
23367
23446
  return `<form ${attrStr}>${children || ""}</form>`;
23368
23447
  }
23369
23448
  if (hydrate && hydrate.nextElement) {
23370
- let form2 = hydrate.nextElement("form");
23449
+ const skipK = typeof hydrate.takeSkipK === "function" ? hydrate.takeSkipK() : 0;
23450
+ let form2 = hydrate.nextElement("form", skipK);
23371
23451
  if (form2 && !form2.parentNode && hydrate.root) {
23372
23452
  const existing = hydrate.root.querySelector("form");
23373
23453
  if (existing) form2 = existing;
@@ -29213,7 +29293,7 @@ function emitComponentCall(ctx2, node, tracked2, effectsVar, parentVar, compPref
29213
29293
  const frag = ctx2.n();
29214
29294
  const wipeMount = ctx2.linkNames.has(node.componentName);
29215
29295
  const savedHydrate = ctx2.hydrate;
29216
- if (wipeMount) ctx2.hydrate = false;
29296
+ if (wipeMount || ctx2.selfClaimNames.has(node.componentName)) ctx2.hydrate = false;
29217
29297
  ctx2.push(`const ${frag} = (() => { const $f = document.createDocumentFragment();`);
29218
29298
  const savedEffects = ctx2.effects;
29219
29299
  ctx2.effects = [];
@@ -32694,6 +32774,7 @@ __export(image_pipeline_exports, {
32694
32774
  optimizeImages: () => optimizeImages
32695
32775
  });
32696
32776
  import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync9, readFileSync as readFileSync18, existsSync as existsSync18, readdirSync as readdirSync4, statSync as statSync9 } from "node:fs";
32777
+ import { spawnSync } from "node:child_process";
32697
32778
  import { resolve as resolve16, extname as extname5, dirname as dirname12 } from "node:path";
32698
32779
  async function processImage(srcPath, outDir, baseName) {
32699
32780
  const image = sharpFn ? sharpFn(srcPath) : null;
@@ -32808,8 +32889,17 @@ var init_image_pipeline = __esm({
32808
32889
  FORMATS = ["webp", "avif"];
32809
32890
  sharpFn = null;
32810
32891
  try {
32811
- const mod = await import("sharp");
32812
- sharpFn = mod.default;
32892
+ const probe = spawnSync(process.execPath, [
32893
+ "--input-type=module",
32894
+ "-e",
32895
+ "import sharp from 'sharp'; await sharp({ create: { width: 2, height: 2, channels: 3, background: 'red' } }).png().toBuffer();"
32896
+ ], { stdio: "ignore" });
32897
+ if (probe.status === 0) {
32898
+ const mod = await import("sharp");
32899
+ sharpFn = mod.default;
32900
+ } else {
32901
+ console.warn("vesk images: sharp native probe failed; using copy-only image pipeline");
32902
+ }
32813
32903
  } catch {
32814
32904
  }
32815
32905
  }
@@ -37917,9 +38007,6 @@ function compileVskCodegen(source, opts = {}) {
37917
38007
  }
37918
38008
  return { code, mappings: g2.mappings, styleRegions: g2.styleRegions, errors: [] };
37919
38009
  }
37920
- function vskToTsx(source) {
37921
- return compileVskCodegen(source).code;
37922
- }
37923
38010
  function isTypeDecl(stmt) {
37924
38011
  return stmt.type === "TSInterfaceDeclaration" || stmt.type === "TSTypeAliasDeclaration" || stmt.type === "ExportNamedDeclaration" && (stmt.declaration?.type === "TSInterfaceDeclaration" || stmt.declaration?.type === "TSTypeAliasDeclaration");
37925
38012
  }
@@ -38120,7 +38207,7 @@ function createTypecheckHost(projectRoot, appDir, vskFiles) {
38120
38207
  } else if (file.endsWith(".css.d.ts")) {
38121
38208
  content = "";
38122
38209
  } else if (vskFiles.has(file)) {
38123
- content = vskToTsx(vskFiles.get(file));
38210
+ content = compileVskCodegen(vskFiles.get(file)).code;
38124
38211
  } else if (ts8.sys.fileExists(file)) {
38125
38212
  content = ts8.sys.readFile(file);
38126
38213
  }
@@ -38161,7 +38248,7 @@ function createTypecheckHost(projectRoot, appDir, vskFiles) {
38161
38248
  if (file.endsWith(".css.d.ts"))
38162
38249
  return "";
38163
38250
  if (vskFiles.has(file))
38164
- return vskToTsx(vskFiles.get(file));
38251
+ return compileVskCodegen(vskFiles.get(file)).code;
38165
38252
  return ts8.sys.readFile(file);
38166
38253
  },
38167
38254
  writeFile: () => {
@@ -38211,6 +38298,35 @@ function createTypecheckHost(projectRoot, appDir, vskFiles) {
38211
38298
  };
38212
38299
  return host;
38213
38300
  }
38301
+ function mapGeneratedOffset(mappings, generatedOffset) {
38302
+ let nearest = null;
38303
+ for (const mapping of mappings) {
38304
+ for (let i = 0; i < mapping.generatedOffsets.length; i++) {
38305
+ const generated = mapping.generatedOffsets[i];
38306
+ const length = mapping.lengths[i] ?? 0;
38307
+ const source = mapping.sourceOffsets[i];
38308
+ if (generated <= generatedOffset && generated + length >= generatedOffset) {
38309
+ return source + (generatedOffset - generated);
38310
+ }
38311
+ if (generated <= generatedOffset && (!nearest || generated > nearest.generated)) {
38312
+ nearest = { source, generated, length };
38313
+ }
38314
+ }
38315
+ }
38316
+ return nearest ? nearest.source + Math.min(nearest.length, Math.max(0, generatedOffset - nearest.generated)) : null;
38317
+ }
38318
+ function sourcePosition(source, offset2) {
38319
+ const bounded = Math.max(0, Math.min(offset2, source.length));
38320
+ let line = 1;
38321
+ let lineStart = 0;
38322
+ for (let i = 0; i < bounded; i++) {
38323
+ if (source.charCodeAt(i) === 10) {
38324
+ line++;
38325
+ lineStart = i + 1;
38326
+ }
38327
+ }
38328
+ return { line, character: bounded - lineStart + 1 };
38329
+ }
38214
38330
  function typecheckProject(projectRoot, opts = {}) {
38215
38331
  const appDir = opts.appDir ?? join25(projectRoot, "app");
38216
38332
  const vskFiles = /* @__PURE__ */ new Map();
@@ -38291,12 +38407,28 @@ function typecheckProject(projectRoot, opts = {}) {
38291
38407
  }
38292
38408
  if (!rootSet.has(filePath) && !vskSet.has(filePath))
38293
38409
  continue;
38294
- const pos = file.getLineAndCharacterOfPosition(diag.start ?? 0);
38410
+ let sourceOffset = diag.start ?? 0;
38411
+ let line;
38412
+ let column;
38413
+ if (vskFiles.has(filePath)) {
38414
+ const source = vskFiles.get(filePath);
38415
+ const generated = compileVskCodegen(source);
38416
+ const mapped = mapGeneratedOffset(generated.mappings, sourceOffset);
38417
+ if (mapped !== null)
38418
+ sourceOffset = mapped;
38419
+ const pos = sourcePosition(source, sourceOffset);
38420
+ line = pos.line;
38421
+ column = pos.character;
38422
+ } else {
38423
+ const pos = file.getLineAndCharacterOfPosition(diag.start ?? 0);
38424
+ line = pos.line + 1;
38425
+ column = pos.character + 1;
38426
+ }
38295
38427
  const code = typeof diag.code === "number" ? `TS${diag.code}` : String(diag.code);
38296
38428
  errors.push({
38297
38429
  file: relative8(projectRoot, filePath),
38298
- line: pos.line + 1,
38299
- column: pos.character + 1,
38430
+ line,
38431
+ column,
38300
38432
  code,
38301
38433
  message: ts8.flattenDiagnosticMessageText(diag.messageText, "\n")
38302
38434
  });
@@ -43708,7 +43840,7 @@ function emitComponentCall2(ctx2, node, tracked2, effectsVar, parentVar, compPre
43708
43840
  const frag = ctx2.n();
43709
43841
  const wipeMount = ctx2.linkNames.has(node.componentName);
43710
43842
  const savedHydrate = ctx2.hydrate;
43711
- if (wipeMount)
43843
+ if (wipeMount || ctx2.selfClaimNames.has(node.componentName))
43712
43844
  ctx2.hydrate = false;
43713
43845
  ctx2.push(`const ${frag} = (() => { const $f = document.createDocumentFragment();`);
43714
43846
  const savedEffects = ctx2.effects;
@@ -52839,7 +52971,7 @@ function renderDevErrorPage(payload, opts) {
52839
52971
  }
52840
52972
 
52841
52973
  // src/build-packages.ts
52842
- import { spawnSync } from "node:child_process";
52974
+ import { spawnSync as spawnSync2 } from "node:child_process";
52843
52975
  import { cpSync as cpSync2, existsSync as existsSync40, mkdirSync as mkdirSync18, readFileSync as readFileSync36, readdirSync as readdirSync11, statSync as statSync20, writeFileSync as writeFileSync20 } from "node:fs";
52844
52976
  import { createRequire as createRequire6 } from "node:module";
52845
52977
  import { dirname as dirname25, join as join23, resolve as resolve36 } from "node:path";
@@ -52914,7 +53046,7 @@ function buildPackages(force = false) {
52914
53046
  if (!force && !distStale(pkgDir, cfg.entry)) continue;
52915
53047
  console.log(`[build] ${cfg.name} -> tsc`);
52916
53048
  const tscBin = require2.resolve("typescript/bin/tsc");
52917
- const result = spawnSync(process.execPath, [tscBin, "-p", join23(pkgDir, "tsconfig.build.json")], {
53049
+ const result = spawnSync2(process.execPath, [tscBin, "-p", join23(pkgDir, "tsconfig.build.json")], {
52918
53050
  stdio: "inherit"
52919
53051
  });
52920
53052
  if (result.status !== 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vesk/vesk-cli",
3
- "version": "0.2.40",
3
+ "version": "0.2.42",
4
4
  "type": "module",
5
5
  "description": "Vesk — compiler-first framework for the post-VDOM web. vesk dev, vesk build.",
6
6
  "bin": {
@@ -8,10 +8,10 @@
8
8
  },
9
9
  "main": "./dist/cli.js",
10
10
  "dependencies": {
11
- "@vesk/adapter": "^0.2.40",
12
- "@vesk/compiler": "^0.2.40",
13
- "@vesk/plugin-tailwind": "^0.2.40",
14
- "@vesk/runtime": "^0.2.40",
11
+ "@vesk/adapter": "^0.2.42",
12
+ "@vesk/compiler": "^0.2.42",
13
+ "@vesk/plugin-tailwind": "^0.2.42",
14
+ "@vesk/runtime": "^0.2.42",
15
15
  "esrap": "^2.3.0",
16
16
  "typescript": "^5.8.0",
17
17
  "ws": "^8.18.0",