@vesk/vesk-cli 0.2.54 → 0.2.56

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 +96 -3
  2. package/package.json +5 -5
package/dist/cli.js CHANGED
@@ -3991,6 +3991,10 @@ function isRouteHref(href) {
3991
3991
  if (!routerMode()) return !href.startsWith("#");
3992
3992
  return !href.startsWith("#") || href.startsWith("#/");
3993
3993
  }
3994
+ function addActiveClass(el, activeClass) {
3995
+ const tokens = String(activeClass || "active").split(/\s+/);
3996
+ for (const t of tokens) if (t) el.classList.add(t);
3997
+ }
3994
3998
  function setIsHydrating(v) {
3995
3999
  __isHydrating = v;
3996
4000
  }
@@ -4264,7 +4268,7 @@ function NavLink(props, registry, hydrate) {
4264
4268
  const routeHref2 = routeHrefOf(props.href);
4265
4269
  const isActive2 = routeHref2 === path2 || routeHref2 !== "/" && path2.startsWith(routeHref2) && (path2.length === routeHref2.length || path2[routeHref2.length] === "/" || path2[routeHref2.length] === "?");
4266
4270
  if (isActive2) {
4267
- a2.classList.add(props.activeClass || "active");
4271
+ addActiveClass(a2, props.activeClass);
4268
4272
  if (props.ariaCurrent !== false) a2.setAttribute("aria-current", "page");
4269
4273
  }
4270
4274
  return claimed ? document.createDocumentFragment() : a2;
@@ -4294,7 +4298,7 @@ function NavLink(props, registry, hydrate) {
4294
4298
  const routeHref2 = routeHrefOf(props.href);
4295
4299
  const isActive2 = routeHref2 === path2 || routeHref2 !== "/" && path2.startsWith(routeHref2) && (path2.length === routeHref2.length || path2[routeHref2.length] === "/" || path2[routeHref2.length] === "?");
4296
4300
  if (isActive2) {
4297
- q.classList.add(props.activeClass || "active");
4301
+ addActiveClass(q, props.activeClass);
4298
4302
  if (props.ariaCurrent !== false) q.setAttribute("aria-current", "page");
4299
4303
  }
4300
4304
  const claimed = q.parentNode !== null;
@@ -4306,7 +4310,7 @@ function NavLink(props, registry, hydrate) {
4306
4310
  const routeHref = routeHrefOf(props.href);
4307
4311
  const isActive = routeHref === path || routeHref !== "/" && path.startsWith(routeHref) && (path.length === routeHref.length || path[routeHref.length] === "/" || path[routeHref.length] === "?");
4308
4312
  if (isActive) {
4309
- a.classList.add(props.activeClass || "active");
4313
+ addActiveClass(a, props.activeClass);
4310
4314
  if (props.ariaCurrent !== false) a.setAttribute("aria-current", "page");
4311
4315
  }
4312
4316
  return a;
@@ -21870,6 +21874,94 @@ function idxOfQuote(s, from) {
21870
21874
  }
21871
21875
  return -1;
21872
21876
  }
21877
+ function warnStaleConstBindings(comp, file) {
21878
+ const reactive = /* @__PURE__ */ new Set();
21879
+ const eachNode = (nodes, fn) => {
21880
+ for (const n of nodes) {
21881
+ fn(n);
21882
+ if (n instanceof ComponentCall) eachNode(n.children, fn);
21883
+ if (n instanceof OpaqueDynamicRegion) {
21884
+ eachNode(n.consequentNodes, fn);
21885
+ eachNode(n.alternateNodes, fn);
21886
+ }
21887
+ if (n instanceof MapRegion) eachNode(n.bodyTemplate, fn);
21888
+ if (n instanceof ForLoop) eachNode(n.bodyTemplate, fn);
21889
+ if (n instanceof WhileLoop) eachNode(n.bodyTemplate, fn);
21890
+ if (n instanceof SwitchBlock) for (const c of n.cases) eachNode(c.body, fn);
21891
+ if (n instanceof TryCatch) {
21892
+ eachNode(n.bodyTemplate, fn);
21893
+ eachNode(n.catchBody, fn);
21894
+ }
21895
+ if (n instanceof StaticNode) eachNode(n.children, fn);
21896
+ }
21897
+ };
21898
+ eachNode(comp.body, (n) => {
21899
+ if (!(n instanceof TrackDecl)) return;
21900
+ reactive.add(n.name);
21901
+ if (n.rawName) reactive.add(n.rawName);
21902
+ });
21903
+ if (reactive.size === 0) return;
21904
+ const stale = /* @__PURE__ */ new Map();
21905
+ eachNode(comp.body, (n) => {
21906
+ if (!(n instanceof RuntimeStatement)) return;
21907
+ const st = n.ast;
21908
+ if (st?.type !== "VariableDeclaration") return;
21909
+ for (const d of st.declarations || []) {
21910
+ if (d.id?.type !== "Identifier" || !d.init) continue;
21911
+ if (reactive.has(d.id.name)) continue;
21912
+ if (d.init.type === "Identifier" && reactive.has(d.init.name)) continue;
21913
+ const seen = /* @__PURE__ */ new Set();
21914
+ collectIdentifiers(d.init, seen);
21915
+ for (const name of seen) {
21916
+ if (!reactive.has(name)) continue;
21917
+ stale.set(d.id.name, name);
21918
+ break;
21919
+ }
21920
+ }
21921
+ });
21922
+ if (stale.size === 0) return;
21923
+ const reported2 = /* @__PURE__ */ new Set();
21924
+ const report = (name) => {
21925
+ if (reported2.has(name)) return;
21926
+ const src2 = stale.get(name);
21927
+ if (!src2) return;
21928
+ reported2.add(name);
21929
+ console.warn(
21930
+ `[vesk] ${comp.name}: \`const ${name} = ...\` is computed once and reads the tracked value \`${src2}\`, so bindings that use \`${name}\` never update. Inline the expression in the binding, or wrap the value in derived().`
21931
+ );
21932
+ };
21933
+ const isEventAttr = (target) => !!target && /^on./.test(target);
21934
+ eachNode(comp.body, (n) => {
21935
+ if (n instanceof DynamicBinding) {
21936
+ if (!isEventAttr(n.target)) {
21937
+ const seen = /* @__PURE__ */ new Set();
21938
+ collectIdentifiers(n.expression.ast, seen);
21939
+ for (const name of seen) if (stale.has(name)) report(name);
21940
+ }
21941
+ return;
21942
+ }
21943
+ if (n instanceof ComponentCall) {
21944
+ for (const pr of n.props) {
21945
+ const seen = /* @__PURE__ */ new Set();
21946
+ collectIdentifiers(pr.value.ast, seen);
21947
+ for (const name of seen) if (stale.has(name)) report(name);
21948
+ }
21949
+ }
21950
+ });
21951
+ }
21952
+ function collectIdentifiers(node, into) {
21953
+ if (!node || typeof node !== "object") return;
21954
+ if (Array.isArray(node)) {
21955
+ for (const child of node) collectIdentifiers(child, into);
21956
+ return;
21957
+ }
21958
+ if (typeof node.type !== "string") return;
21959
+ if (node.type === "Identifier" && typeof node.name === "string") into.add(node.name);
21960
+ for (const key of Object.keys(node)) {
21961
+ if (key === "loc" || key === "range" || key === "start" || key === "end") continue;
21962
+ collectIdentifiers(node[key], into);
21963
+ }
21964
+ }
21873
21965
  function generateIR(ast, source, filename) {
21874
21966
  __vskAnnotations = ast.__vskAnnotations ?? [];
21875
21967
  __slotProps = null;
@@ -22157,6 +22249,7 @@ function generateIR(ast, source, filename) {
22157
22249
  imports.push(`import { ${missing.join(", ")} } from '@vesk/runtime';`);
22158
22250
  }
22159
22251
  }
22252
+ for (const comp of components) warnStaleConstBindings(comp, file);
22160
22253
  const irRoot = new IRRoot(components, imports, importedNames, staticProps, loadFn, topLevelCode, exportAliases, reexportSources);
22161
22254
  irRoot.metadataSource = metadataSource;
22162
22255
  return irRoot;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vesk/vesk-cli",
3
- "version": "0.2.54",
3
+ "version": "0.2.56",
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.54",
12
- "@vesk/compiler": "^0.2.54",
13
- "@vesk/plugin-tailwind": "^0.2.54",
14
- "@vesk/runtime": "^0.2.54",
11
+ "@vesk/adapter": "^0.2.56",
12
+ "@vesk/compiler": "^0.2.56",
13
+ "@vesk/plugin-tailwind": "^0.2.56",
14
+ "@vesk/runtime": "^0.2.56",
15
15
  "esrap": "^2.3.0",
16
16
  "typescript": "^5.8.0",
17
17
  "ws": "^8.18.0",