@statorjs/language-server 0.1.16 → 0.1.18

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/server.cjs +208 -109
  2. package/package.json +2 -2
package/dist/server.cjs CHANGED
@@ -44177,50 +44177,14 @@ var import_volar_service_typescript = __toESM(require_volar_service_typescript()
44177
44177
  // ../stator/src/compiler/client-emit.ts
44178
44178
  var import_typescript = __toESM(require("typescript"), 1);
44179
44179
 
44180
- // ../stator/src/compiler/diagnostics.ts
44181
- var CompileError = class extends Error {
44182
- loc;
44183
- constructor(message, loc2) {
44184
- super(message);
44185
- this.name = "CompileError";
44186
- this.loc = loc2;
44187
- }
44188
- };
44189
- function offsetToLineCol(source, offset) {
44190
- const clamped = Math.max(0, Math.min(offset, source.length));
44191
- let line = 1;
44192
- let lastNewline = -1;
44193
- for (let i = 0; i < clamped; i++) {
44194
- if (source.charCodeAt(i) === 10) {
44195
- line++;
44196
- lastNewline = i;
44197
- }
44198
- }
44199
- return { line, column: clamped - lastNewline };
44200
- }
44201
- function codeFrame(source, line, column) {
44202
- const lines = source.split("\n");
44203
- const start = Math.max(1, line - 1);
44204
- const end = Math.min(lines.length, line + 1);
44205
- const gutterWidth = String(end).length;
44206
- const out = [];
44207
- for (let n = start; n <= end; n++) {
44208
- const gutter = String(n).padStart(gutterWidth);
44209
- const marker = n === line ? ">" : " ";
44210
- out.push(`${marker} ${gutter} | ${lines[n - 1] ?? ""}`);
44211
- if (n === line) {
44212
- out.push(` ${" ".repeat(gutterWidth)} | ${" ".repeat(Math.max(0, column - 1))}^`);
44213
- }
44214
- }
44215
- return out.join("\n");
44216
- }
44217
- function locAt(source, offset, file) {
44218
- const { line, column } = offsetToLineCol(source, offset);
44219
- return { file, line, column, frame: codeFrame(source, line, column) };
44220
- }
44180
+ // ../stator/src/wire/island-markers.ts
44181
+ var ISLAND_MARKER_ATTR = "data-b";
44182
+ var elementMarker = (i) => `b${i}`;
44183
+ var slotMarker = (i) => `s${i}`;
44184
+ var elementMarkerSelector = (marker) => `[${ISLAND_MARKER_ATTR}="${marker}"]`;
44221
44185
 
44222
44186
  // ../stator/src/compiler/client-emit.ts
44223
- var PRIMITIVES = "import { StatorElement, defineElement, use, machine, bind, effect, dispatch } from '@statorjs/stator/client'";
44187
+ var PRIMITIVES = "import { StatorElement, defineElement, use, machine, bind, bindSlot, effect, dispatch, attrValue, setAttr } from '@statorjs/stator/client'";
44224
44188
  function emitClientModule(input) {
44225
44189
  const { script, element, directives, members } = input;
44226
44190
  const impl = `__${element.className}Impl`;
@@ -44233,9 +44197,18 @@ function emitClientModule(input) {
44233
44197
  const lines = [];
44234
44198
  let i = 0;
44235
44199
  for (const [marker, group] of byMarker) {
44200
+ if (group[0]?.kind === "slot") {
44201
+ for (const d of group) {
44202
+ const thunk = `() => (${rewriteMembers(d.expr, members)})`;
44203
+ const deps = `[${d.deps.map((dep) => `this.${dep}`).join(", ")}]`;
44204
+ lines.push(` this.track(bindSlot(this, ${JSON.stringify(marker)}, ${deps}, ${thunk}))`);
44205
+ }
44206
+ continue;
44207
+ }
44236
44208
  const node = `n${i++}`;
44237
- lines.push(` const ${node} = this.querySelector('[data-b="${marker}"]')`);
44238
- lines.push(` if (${node}) {`);
44209
+ lines.push(
44210
+ ` for (const ${node} of this.querySelectorAll(${JSON.stringify(elementMarkerSelector(marker))})) {`
44211
+ );
44239
44212
  for (const d of group) lines.push(` ${wireDirective(node, d, members)}`);
44240
44213
  lines.push(" }");
44241
44214
  }
@@ -44261,27 +44234,9 @@ function wireDirective(node, d, members) {
44261
44234
  const target = d.target ?? "text";
44262
44235
  const thunk = `() => (${rewriteMembers(d.expr, members)})`;
44263
44236
  const deps = `[${d.deps.map((dep) => `this.${dep}`).join(", ")}]`;
44264
- if (target === "value" || target === "checked") {
44265
- const path = parseTwoWayPath(d.expr);
44266
- if (!path) {
44267
- throw new CompileError(
44268
- `stator: bind:${target}={${d.expr}} must bind to a settable context path like \`actor.key\` (a derived value can't be two-way bound).`
44269
- );
44270
- }
44271
- const writer2 = emitWriter(node, target);
44272
- const read = target === "checked" ? `${node}.checked` : `${node}.value`;
44273
- const guard = target === "value" ? `if (e.isComposing) return; ` : "";
44274
- const setter = `${node}.addEventListener(${JSON.stringify(target === "value" ? "input" : "change")}, (e) => { ${guard}this.${path.actor}.send({ type: '@set', key: ${JSON.stringify(path.key)}, value: ${read} }) })`;
44275
- return `this.track(bind(${deps}, ${thunk}, ${writer2}));
44276
- ${setter}`;
44277
- }
44278
44237
  const writer = emitWriter(node, target);
44279
44238
  return `this.track(bind(${deps}, ${thunk}, ${writer}))`;
44280
44239
  }
44281
- function parseTwoWayPath(expr) {
44282
- const m = expr.trim().match(/^([A-Za-z_$][\w$]*)\.([A-Za-z_$][\w$]*)$/);
44283
- return m ? { actor: m[1], key: m[2] } : null;
44284
- }
44285
44240
  function emitHandler(expr, members) {
44286
44241
  const t = expr.trim();
44287
44242
  if (/^[A-Za-z_$][\w$]*$/.test(t) && members.has(t)) {
@@ -44291,18 +44246,11 @@ function emitHandler(expr, members) {
44291
44246
  }
44292
44247
  function emitWriter(node, target) {
44293
44248
  switch (target) {
44294
- case "text":
44295
- return `(v) => { ${node}.textContent = v == null ? '' : String(v) }`;
44296
- case "html":
44297
- return `(v) => { ${node}.innerHTML = v == null ? '' : String(v) }`;
44298
- case "value":
44299
- return `(v) => { const s = v == null ? '' : String(v); if (${node}.value !== s) ${node}.value = s }`;
44300
44249
  case "disabled":
44301
44250
  case "hidden":
44302
- case "checked":
44303
44251
  return `(v) => { if (${node}.${target} !== !!v) ${node}.${target} = !!v }`;
44304
44252
  default:
44305
- return `(v) => { if (v == null || v === false) ${node}.removeAttribute(${JSON.stringify(target)}); else ${node}.setAttribute(${JSON.stringify(target)}, v === true ? '' : String(v)) }`;
44253
+ return `(v) => setAttr(${node}, ${JSON.stringify(target)}, attrValue(v))`;
44306
44254
  }
44307
44255
  }
44308
44256
  function stripClientPrimitiveImports(script) {
@@ -44341,6 +44289,50 @@ function rewriteMembers(expr, members) {
44341
44289
 
44342
44290
  // ../stator/src/compiler/client-script.ts
44343
44291
  var import_typescript2 = __toESM(require("typescript"), 1);
44292
+
44293
+ // ../stator/src/compiler/diagnostics.ts
44294
+ var CompileError = class extends Error {
44295
+ loc;
44296
+ constructor(message, loc2) {
44297
+ super(message);
44298
+ this.name = "CompileError";
44299
+ this.loc = loc2;
44300
+ }
44301
+ };
44302
+ function offsetToLineCol(source, offset) {
44303
+ const clamped = Math.max(0, Math.min(offset, source.length));
44304
+ let line = 1;
44305
+ let lastNewline = -1;
44306
+ for (let i = 0; i < clamped; i++) {
44307
+ if (source.charCodeAt(i) === 10) {
44308
+ line++;
44309
+ lastNewline = i;
44310
+ }
44311
+ }
44312
+ return { line, column: clamped - lastNewline };
44313
+ }
44314
+ function codeFrame(source, line, column) {
44315
+ const lines = source.split("\n");
44316
+ const start = Math.max(1, line - 1);
44317
+ const end = Math.min(lines.length, line + 1);
44318
+ const gutterWidth = String(end).length;
44319
+ const out = [];
44320
+ for (let n = start; n <= end; n++) {
44321
+ const gutter = String(n).padStart(gutterWidth);
44322
+ const marker = n === line ? ">" : " ";
44323
+ out.push(`${marker} ${gutter} | ${lines[n - 1] ?? ""}`);
44324
+ if (n === line) {
44325
+ out.push(` ${" ".repeat(gutterWidth)} | ${" ".repeat(Math.max(0, column - 1))}^`);
44326
+ }
44327
+ }
44328
+ return out.join("\n");
44329
+ }
44330
+ function locAt(source, offset, file) {
44331
+ const { line, column } = offsetToLineCol(source, offset);
44332
+ return { file, line, column, frame: codeFrame(source, line, column) };
44333
+ }
44334
+
44335
+ // ../stator/src/compiler/client-script.ts
44344
44336
  function kebabToPascal(tag) {
44345
44337
  return tag.split("-").map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join("");
44346
44338
  }
@@ -44492,7 +44484,7 @@ function cleanJsxText(raw) {
44492
44484
  var WRAP_PREFIX_LEN = "const __t = (<>".length;
44493
44485
  var INLINE_SCRIPT_RE = /<script\b([^>]*\bis:inline\b[^>]*)>([\s\S]*?)<\/script>/gi;
44494
44486
  var inlineMarker = (i) => `\0statorInlineScript${i}\0`;
44495
- var INLINE_MARKER_RE = /statorInlineScript(\d+)/g;
44487
+ var INLINE_MARKER_RE = /\0statorInlineScript(\d+)\0/g;
44496
44488
  function eachItemParamsFor(jsx) {
44497
44489
  let node = jsx.parent;
44498
44490
  while (node && import_typescript3.default.isParenthesizedExpression(node)) node = node.parent;
@@ -44534,6 +44526,28 @@ function lowerTemplate(template, opts = {}) {
44534
44526
  let eachParams = null;
44535
44527
  const enableItemBind = !opts.client;
44536
44528
  const isItemRead = (n) => enableItemBind && eachParams !== null && import_typescript3.default.isCallExpression(n) && import_typescript3.default.isIdentifier(n.expression) && n.expression.text === "read" && n.arguments.length >= 2 && n.arguments[0] !== void 0 && import_typescript3.default.isIdentifier(n.arguments[0]) && n.arguments[0].text === eachParams.item;
44529
+ const isClientRead = (n) => opts.client !== void 0 && import_typescript3.default.isCallExpression(n) && import_typescript3.default.isIdentifier(n.expression) && n.expression.text === "read" && n.arguments.length >= 2 && n.arguments[0] !== void 0 && import_typescript3.default.isIdentifier(n.arguments[0]) && opts.client.useFields.has(n.arguments[0].text);
44530
+ const bindRemoved = (name, at) => new CompileError(
44531
+ `stator: bind:${name} was removed in 2.0. Display state with read() \u2014 \`{read(m, (s) => s.value)}\` in text position, \`attr={read(m, \u2026)}\` on an attribute \u2014 and capture input at a commit boundary with a typed event (ref:/FormData + send/dispatch). See the forms guide.`,
44532
+ at
44533
+ );
44534
+ const clientReadExpr = (call) => `(${call.arguments[1].getText(sf)})(${call.arguments[0].text})`;
44535
+ const allocSlotMarker = () => {
44536
+ const client = opts.client;
44537
+ if (!client) throw new CompileError("stator: internal \u2014 slot marker outside client mode");
44538
+ return slotMarker(client.directives.filter((d) => d.kind === "slot").length);
44539
+ };
44540
+ const assertNoClientRead = (n) => {
44541
+ if (!opts.client) return;
44542
+ if (import_typescript3.default.isJsxElement(n) || import_typescript3.default.isJsxSelfClosingElement(n) || import_typescript3.default.isJsxFragment(n)) return;
44543
+ if (isClientRead(n)) {
44544
+ throw new CompileError(
44545
+ `stator: read(${n.arguments[0].text}, \u2026) on a client machine must be the entire expression \u2014 text position (\`{read(m, s => \u2026)}\`) or attribute position (\`attr={read(m, s => \u2026)}\`). Compose derived values inside the selector.`,
44546
+ loc2(n)
44547
+ );
44548
+ }
44549
+ import_typescript3.default.forEachChild(n, assertNoClientRead);
44550
+ };
44537
44551
  const loc2 = (node) => {
44538
44552
  if (opts.source == null || opts.templateOffset == null) return void 0;
44539
44553
  const orig = opts.templateOffset + doctypeLen + (node.getStart(sf) - WRAP_PREFIX_LEN);
@@ -44642,6 +44656,17 @@ function lowerTemplate(template, opts = {}) {
44642
44656
  if (import_typescript3.default.isJsxText(node)) return escapeText(cleanJsxText(node.getFullText(sf)));
44643
44657
  if (import_typescript3.default.isJsxExpression(node)) {
44644
44658
  if (!node.expression) return "";
44659
+ if (opts.client && isClientRead(node.expression)) {
44660
+ const marker = allocSlotMarker();
44661
+ const expr = clientReadExpr(node.expression);
44662
+ opts.client.directives.push({
44663
+ marker,
44664
+ kind: "slot",
44665
+ expr,
44666
+ deps: inferDeps(expr, opts.client.useFields)
44667
+ });
44668
+ return `<!--${marker}-->`;
44669
+ }
44645
44670
  return `\${${lowerExprText(node.expression)}}`;
44646
44671
  }
44647
44672
  if (import_typescript3.default.isJsxElement(node)) {
@@ -44675,6 +44700,7 @@ function lowerTemplate(template, opts = {}) {
44675
44700
  if (import_typescript3.default.isJsxElement(expr) || import_typescript3.default.isJsxSelfClosingElement(expr) || import_typescript3.default.isJsxFragment(expr)) {
44676
44701
  return `html\`${contentOfChild(expr)}\``;
44677
44702
  }
44703
+ assertNoClientRead(expr);
44678
44704
  if (isItemRead(expr)) {
44679
44705
  return `itemBind(${lowerExprText(expr.arguments[1])})`;
44680
44706
  }
@@ -44720,56 +44746,72 @@ function lowerTemplate(template, opts = {}) {
44720
44746
  );
44721
44747
  }
44722
44748
  }
44723
- const clientMarker = opts.client ? collectClientDirectives(attrs) : void 0;
44749
+ const collected = opts.client ? collectClientDirectives(attrs) : void 0;
44724
44750
  let out = "";
44725
44751
  for (const attr of attrs.properties) {
44726
44752
  if (import_typescript3.default.isJsxSpreadAttribute(attr)) {
44727
- throw new CompileError("stator: spread attributes ({...x}) are not supported", loc2(attr));
44728
- }
44729
- if (import_typescript3.default.isJsxAttribute(attr) && !import_typescript3.default.isJsxNamespacedName(attr.name) && attr.name.getText(sf) === "child") {
44753
+ out += ` \${spreadAttrs(${lowerExprText(attr.expression)})}`;
44730
44754
  continue;
44731
44755
  }
44732
- if (opts.client && import_typescript3.default.isJsxAttribute(attr) && import_typescript3.default.isJsxNamespacedName(attr.name) && (attr.name.namespace.text === "on" || attr.name.namespace.text === "bind")) {
44756
+ if (import_typescript3.default.isJsxAttribute(attr) && !import_typescript3.default.isJsxNamespacedName(attr.name) && attr.name.getText(sf) === "child") {
44733
44757
  continue;
44734
44758
  }
44759
+ if (collected?.consumed.has(attr)) continue;
44735
44760
  const lowered = lowerAttribute(attr);
44736
44761
  if (lowered) out += ` ${lowered}`;
44737
44762
  }
44738
- if (clientMarker) out += ` data-b="${clientMarker}"`;
44763
+ if (collected?.marker) out += ` ${ISLAND_MARKER_ATTR}="${collected.marker}"`;
44739
44764
  return out;
44740
44765
  };
44741
44766
  const collectClientDirectives = (attrs) => {
44767
+ const consumed = /* @__PURE__ */ new Set();
44742
44768
  const client = opts.client;
44743
- if (!client) return void 0;
44769
+ if (!client) return { consumed };
44744
44770
  const pending = [];
44745
44771
  for (const attr of attrs.properties) {
44746
- if (!import_typescript3.default.isJsxAttribute(attr) || !import_typescript3.default.isJsxNamespacedName(attr.name)) continue;
44747
- const ns = attr.name.namespace.text;
44748
- const name = attr.name.name.text;
44749
- if (ns !== "on" && ns !== "bind") continue;
44750
- const expr = attrExpr(attr);
44751
- if (!expr) {
44752
- throw new CompileError(`stator: ${ns}:${name} requires a value ({...})`, loc2(attr));
44753
- }
44754
- if (ns === "on") {
44772
+ if (!import_typescript3.default.isJsxAttribute(attr)) continue;
44773
+ if (import_typescript3.default.isJsxNamespacedName(attr.name)) {
44774
+ const ns = attr.name.namespace.text;
44775
+ const name = attr.name.name.text;
44776
+ if (ns === "bind") throw bindRemoved(name, loc2(attr));
44777
+ if (ns !== "on") continue;
44778
+ const expr = attrExpr(attr);
44779
+ if (!expr) {
44780
+ throw new CompileError(`stator: on:${name} requires a handler ({...})`, loc2(attr));
44781
+ }
44755
44782
  pending.push({ marker: "", kind: "on", event: name, expr, deps: [] });
44756
- } else {
44783
+ consumed.add(attr);
44784
+ continue;
44785
+ }
44786
+ const init = attr.initializer;
44787
+ if (init && import_typescript3.default.isJsxExpression(init) && init.expression && isClientRead(init.expression)) {
44788
+ const target = attr.name.getText(sf);
44789
+ if (target === "value" || target === "checked") {
44790
+ throw new CompileError(
44791
+ `stator: read() can't live-drive ${target}= from a client machine \u2014 the control owns its draft. Pre-fill with a server-rendered ${target} attribute, populate/reset via ref: at safe moments, and capture input with a typed commit event. See the forms guide.`,
44792
+ loc2(attr)
44793
+ );
44794
+ }
44795
+ const expr = clientReadExpr(init.expression);
44757
44796
  pending.push({
44758
44797
  marker: "",
44759
44798
  kind: "bind",
44760
- target: name,
44799
+ target,
44761
44800
  expr,
44762
44801
  deps: inferDeps(expr, client.useFields)
44763
44802
  });
44803
+ consumed.add(attr);
44764
44804
  }
44765
44805
  }
44766
- if (pending.length === 0) return void 0;
44767
- const marker = `b${new Set(client.directives.map((d) => d.marker)).size}`;
44806
+ if (pending.length === 0) return { consumed };
44807
+ const marker = elementMarker(
44808
+ new Set(client.directives.filter((d) => d.kind !== "slot").map((d) => d.marker)).size
44809
+ );
44768
44810
  for (const d of pending) {
44769
44811
  d.marker = marker;
44770
44812
  client.directives.push(d);
44771
44813
  }
44772
- return marker;
44814
+ return { marker, consumed };
44773
44815
  };
44774
44816
  const lowerChildrenPlaceholder = (attrs) => {
44775
44817
  if (meta) meta.usesChildren = true;
@@ -44823,10 +44865,8 @@ function lowerTemplate(template, opts = {}) {
44823
44865
  if (meta) meta.refs.add(name2);
44824
44866
  return `data-ref=${JSON.stringify(name2)}`;
44825
44867
  }
44826
- throw new CompileError(
44827
- `stator: directive "${ns}:${name2}" is not supported yet (Phase 3b)`,
44828
- loc2(attr)
44829
- );
44868
+ if (ns === "bind") throw bindRemoved(name2, loc2(attr));
44869
+ throw new CompileError(`stator: directive "${ns}:${name2}" is not supported`, loc2(attr));
44830
44870
  }
44831
44871
  const name = attr.name.getText(sf);
44832
44872
  if (!attr.initializer) return name;
@@ -44844,18 +44884,31 @@ function lowerTemplate(template, opts = {}) {
44844
44884
  };
44845
44885
  const lowerComponent = (tag, attrs, children) => {
44846
44886
  const entries = [];
44887
+ const forwarded = [];
44847
44888
  for (const attr of attrs.properties) {
44848
44889
  if (import_typescript3.default.isJsxSpreadAttribute(attr)) {
44849
- throw new CompileError(
44850
- `stator: spread props ({...x}) on <${tag}/> are not supported`,
44851
- loc2(attr)
44852
- );
44890
+ entries.push(`...${lowerExprText(attr.expression)}`);
44891
+ continue;
44853
44892
  }
44854
44893
  if (import_typescript3.default.isJsxNamespacedName(attr.name)) {
44855
- throw new CompileError(
44856
- `stator: directive "${attr.name.namespace.text}:${attr.name.name.text}" is not valid on component <${tag}/> \u2014 directives apply to HTML elements, not components`,
44857
- loc2(attr)
44858
- );
44894
+ const ns = attr.name.namespace.text;
44895
+ const dirName = attr.name.name.text;
44896
+ if (ns === "bind") throw bindRemoved(dirName, loc2(attr));
44897
+ if (ns !== "on") {
44898
+ throw new CompileError(
44899
+ `stator: forwarding "${ns}:${dirName}" to a component isn't supported yet \u2014 only on:* event directives forward. Apply ref: on an element directly.`,
44900
+ loc2(attr)
44901
+ );
44902
+ }
44903
+ const value = attrExpr(attr);
44904
+ if (!value) {
44905
+ throw new CompileError(
44906
+ `stator: on:${dirName} on <${tag}/> requires a handler ({...})`,
44907
+ loc2(attr)
44908
+ );
44909
+ }
44910
+ forwarded.push(`${JSON.stringify(`on:${dirName}`)}: ${value}`);
44911
+ continue;
44859
44912
  }
44860
44913
  const name = attr.name.getText(sf);
44861
44914
  if (!attr.initializer) {
@@ -44892,6 +44945,7 @@ function lowerTemplate(template, opts = {}) {
44892
44945
  }
44893
44946
  if (bag.length > 0) entries.push(`children: { ${bag.join(", ")} }`);
44894
44947
  }
44948
+ if (forwarded.length > 0) entries.push(`$directives: { ${forwarded.join(", ")} }`);
44895
44949
  return `${tag}({ ${entries.join(", ")} })`;
44896
44950
  };
44897
44951
  const body = `html\`${doctype}${contentOfChildren(fragment.children)}\``;
@@ -45145,7 +45199,7 @@ function isSubjectGlobal(sel) {
45145
45199
  }
45146
45200
 
45147
45201
  // ../stator/src/compiler/compile.ts
45148
- var PRIMITIVES_IMPORT = "import { html, read, each, itemBind, when, match, defer, on, classList, styleList } from '@statorjs/stator/template'";
45202
+ var PRIMITIVES_IMPORT = "import { html, read, each, itemBind, when, match, defer, on, classList, styleList, spreadAttrs } from '@statorjs/stator/template'";
45149
45203
  function compile(source, opts = {}) {
45150
45204
  const { frontmatter, template, styles, scripts, scriptOffsets, templateOffset } = splitStator(source);
45151
45205
  const kind = opts.kind ?? "component";
@@ -45222,7 +45276,7 @@ function compileClient(template, script, ctx) {
45222
45276
  const attrDecl = `{ ${[...cls.staticAttrs].map(([k, v]) => `${k}: ${JSON.stringify(v)}`).join(", ")} }`;
45223
45277
  const rootScope = ctx.scopeAttr ? ` data-s-${ctx.hash}` : "";
45224
45278
  const serverCode = [
45225
- "import { html, read, each, when, match, defer, on, classList, styleList, createHtmlFragment, clientShellAttrs } from '@statorjs/stator/template'",
45279
+ "import { html, read, each, when, match, defer, on, classList, styleList, spreadAttrs, createHtmlFragment, clientShellAttrs } from '@statorjs/stator/template'",
45226
45280
  "",
45227
45281
  `export default function (props = {}) {`,
45228
45282
  ` const __inner = ${innerExpr}`,
@@ -45351,6 +45405,23 @@ function processFrontmatter(fm, kind, source, file) {
45351
45405
  repls.push([n.getStart(sf), n.getEnd(), bound]);
45352
45406
  return;
45353
45407
  }
45408
+ if (import_typescript4.default.isCallExpression(n) && isStatorCall(n, "forwarded")) {
45409
+ if (kind === "route") {
45410
+ throw new CompileError(
45411
+ "stator: Stator.forwarded() is only available in a component \u2014 it reads a directive forwarded from a parent (`<Child on:click={h}/>`), and a route has no parent.",
45412
+ locInFm(n)
45413
+ );
45414
+ }
45415
+ const arg = n.arguments[0];
45416
+ if (!arg) {
45417
+ throw new CompileError(
45418
+ "stator: Stator.forwarded(name) takes a directive name, e.g. Stator.forwarded('on:click')",
45419
+ locInFm(n)
45420
+ );
45421
+ }
45422
+ repls.push([n.getStart(sf), n.getEnd(), `props.$directives?.[${arg.getText(sf)}]`]);
45423
+ return;
45424
+ }
45354
45425
  if (import_typescript4.default.isPropertyAccessExpression(n) && isStatorMember(n, "request")) {
45355
45426
  requireRoute("Stator.request", kind, locInFm(n));
45356
45427
  repls.push([n.getStart(sf), n.getEnd(), "__req"]);
@@ -45449,6 +45520,26 @@ function isStatorMember(e, member) {
45449
45520
 
45450
45521
  // ../stator/src/compiler/dts.ts
45451
45522
  var import_typescript5 = __toESM(require("typescript"), 1);
45523
+ function statorPropsType(frontmatter, template, scripts) {
45524
+ const { propsType } = extractFrontmatterTypes(frontmatter);
45525
+ let propsT = componentPropsType(propsType, template);
45526
+ let needsReadResult = false;
45527
+ const classes = scripts.length > 0 ? analyzeScriptClasses(scripts.join("\n")) : [];
45528
+ if (!propsType && classes.length > 0) {
45529
+ const attrs = /* @__PURE__ */ new Map();
45530
+ for (const cls of classes) {
45531
+ for (const [key, kind] of cls.staticAttrs) attrs.set(key, kind);
45532
+ }
45533
+ if (attrs.size > 0) {
45534
+ needsReadResult = true;
45535
+ const fields = [...attrs].map(([key, kind]) => `${key}?: ${kind} | __SReadResult<${kind}>`).join("; ");
45536
+ propsT = `{ ${fields} } & { [prop: string]: unknown }`;
45537
+ } else {
45538
+ propsT = "{ [prop: string]: unknown }";
45539
+ }
45540
+ }
45541
+ return { propsT, needsReadResult };
45542
+ }
45452
45543
  function componentPropsType(propsType, template) {
45453
45544
  const usesChildren = /<children[\s/>]/.test(template);
45454
45545
  if (propsType) return usesChildren ? `${propsType} & { children?: any }` : propsType;
@@ -45512,16 +45603,17 @@ function injectImports(globals, modulePath, userCode) {
45512
45603
  return missing.length > 0 ? `import { ${missing.join(", ")} } from '${modulePath}';
45513
45604
  ` : "";
45514
45605
  }
45515
- var AMBIENT_TYPE_IMPORTS = "import type { AnyMachineDef as __SMachineDef } from '@statorjs/stator/machine';\nimport type { InstanceOf as __SInstanceOf } from '@statorjs/stator/template';\n";
45606
+ var AMBIENT_TYPE_IMPORTS = "import type { AnyMachineDef as __SMachineDef } from '@statorjs/stator/machine';\nimport type { InstanceOf as __SInstanceOf } from '@statorjs/stator/template';\nimport type { StatorIntrinsicElements as __SIntrinsics } from '@statorjs/stator/template';\n";
45516
45607
  var STATOR_AMBIENT = `declare const Stator: {
45517
45608
  props<P>(): P;
45518
45609
  reads<const T extends readonly __SMachineDef[]>(defs: T): { -readonly [K in keyof T]: __SInstanceOf<T[K]> };
45610
+ forwarded(name: \`on:\${string}\`): (() => unknown) | undefined;
45519
45611
  request: any;
45520
45612
  response: { status: number; headers: Record<string, string>; cookies: any };
45521
45613
  };
45522
45614
  declare global {
45523
45615
  namespace JSX {
45524
- interface IntrinsicElements extends Record<string, any> {}
45616
+ interface IntrinsicElements extends __SIntrinsics {}
45525
45617
  }
45526
45618
  }
45527
45619
  `;
@@ -45553,7 +45645,14 @@ function buildServerTsx(regions) {
45553
45645
  tplOffset += doctype[0].length;
45554
45646
  tpl = tpl.slice(doctype[0].length);
45555
45647
  }
45556
- const propsT = componentPropsType(extractFrontmatterTypes(fm).propsType, regions.template.content);
45648
+ const { propsT, needsReadResult } = statorPropsType(
45649
+ fm,
45650
+ regions.template.content,
45651
+ regions.scripts.map((s) => s.content)
45652
+ );
45653
+ if (needsReadResult) {
45654
+ code += "type __SReadResult<T> = import('@statorjs/stator/template').ReadResult<T>\n";
45655
+ }
45557
45656
  code += `export default function (_props: ${propsT}) {
45558
45657
  `;
45559
45658
  for (const seg of body) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statorjs/language-server",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "description": "Volar-based language server for .stator files.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -33,7 +33,7 @@
33
33
  "volar-service-typescript": "0.0.71",
34
34
  "vscode-css-languageservice": "^6.3.0",
35
35
  "vscode-uri": "^3.0.8",
36
- "@statorjs/stator": "1.8.0"
36
+ "@statorjs/stator": "2.0.0"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "typescript": "^5.6.0"