@statorjs/language-server 0.1.15 → 0.1.17

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 +146 -38
  2. package/package.json +2 -2
package/dist/server.cjs CHANGED
@@ -44220,7 +44220,7 @@ function locAt(source, offset, file) {
44220
44220
  }
44221
44221
 
44222
44222
  // ../stator/src/compiler/client-emit.ts
44223
- var PRIMITIVES = "import { StatorElement, defineElement, use, machine, bind, effect, dispatch } from '@statorjs/stator/client'";
44223
+ var PRIMITIVES = "import { StatorElement, defineElement, use, machine, bind, bindSlot, effect, dispatch } from '@statorjs/stator/client'";
44224
44224
  function emitClientModule(input) {
44225
44225
  const { script, element, directives, members } = input;
44226
44226
  const impl = `__${element.className}Impl`;
@@ -44233,9 +44233,16 @@ function emitClientModule(input) {
44233
44233
  const lines = [];
44234
44234
  let i = 0;
44235
44235
  for (const [marker, group] of byMarker) {
44236
+ if (group[0]?.kind === "slot") {
44237
+ for (const d of group) {
44238
+ const thunk = `() => (${rewriteMembers(d.expr, members)})`;
44239
+ const deps = `[${d.deps.map((dep) => `this.${dep}`).join(", ")}]`;
44240
+ lines.push(` this.track(bindSlot(this, ${JSON.stringify(marker)}, ${deps}, ${thunk}))`);
44241
+ }
44242
+ continue;
44243
+ }
44236
44244
  const node = `n${i++}`;
44237
- lines.push(` const ${node} = this.querySelector('[data-b="${marker}"]')`);
44238
- lines.push(` if (${node}) {`);
44245
+ lines.push(` for (const ${node} of this.querySelectorAll('[data-b="${marker}"]')) {`);
44239
44246
  for (const d of group) lines.push(` ${wireDirective(node, d, members)}`);
44240
44247
  lines.push(" }");
44241
44248
  }
@@ -44472,10 +44479,27 @@ function scopeHash(input) {
44472
44479
 
44473
44480
  // ../stator/src/compiler/lower.ts
44474
44481
  var import_typescript3 = __toESM(require("typescript"), 1);
44482
+ function cleanJsxText(raw) {
44483
+ const lines = raw.split(/\r\n|\n|\r/);
44484
+ let lastNonEmpty = 0;
44485
+ for (let i = 0; i < lines.length; i++) {
44486
+ if (/[^ \t]/.test(lines[i])) lastNonEmpty = i;
44487
+ }
44488
+ let out = "";
44489
+ for (let i = 0; i < lines.length; i++) {
44490
+ let line = lines[i].replace(/\t/g, " ");
44491
+ if (i !== 0) line = line.replace(/^ +/, "");
44492
+ if (i !== lines.length - 1) line = line.replace(/ +$/, "");
44493
+ if (!line) continue;
44494
+ if (i !== lastNonEmpty) line += " ";
44495
+ out += line;
44496
+ }
44497
+ return out;
44498
+ }
44475
44499
  var WRAP_PREFIX_LEN = "const __t = (<>".length;
44476
44500
  var INLINE_SCRIPT_RE = /<script\b([^>]*\bis:inline\b[^>]*)>([\s\S]*?)<\/script>/gi;
44477
44501
  var inlineMarker = (i) => `\0statorInlineScript${i}\0`;
44478
- var INLINE_MARKER_RE = /statorInlineScript(\d+)/g;
44502
+ var INLINE_MARKER_RE = /\0statorInlineScript(\d+)\0/g;
44479
44503
  function eachItemParamsFor(jsx) {
44480
44504
  let node = jsx.parent;
44481
44505
  while (node && import_typescript3.default.isParenthesizedExpression(node)) node = node.parent;
@@ -44517,6 +44541,24 @@ function lowerTemplate(template, opts = {}) {
44517
44541
  let eachParams = null;
44518
44542
  const enableItemBind = !opts.client;
44519
44543
  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;
44544
+ 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);
44545
+ const clientReadExpr = (call) => `(${call.arguments[1].getText(sf)})(${call.arguments[0].text})`;
44546
+ const allocSlotMarker = () => {
44547
+ const client = opts.client;
44548
+ if (!client) throw new CompileError("stator: internal \u2014 slot marker outside client mode");
44549
+ return `s${client.directives.filter((d) => d.kind === "slot").length}`;
44550
+ };
44551
+ const assertNoClientRead = (n) => {
44552
+ if (!opts.client) return;
44553
+ if (import_typescript3.default.isJsxElement(n) || import_typescript3.default.isJsxSelfClosingElement(n) || import_typescript3.default.isJsxFragment(n)) return;
44554
+ if (isClientRead(n)) {
44555
+ throw new CompileError(
44556
+ `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.`,
44557
+ loc2(n)
44558
+ );
44559
+ }
44560
+ import_typescript3.default.forEachChild(n, assertNoClientRead);
44561
+ };
44520
44562
  const loc2 = (node) => {
44521
44563
  if (opts.source == null || opts.templateOffset == null) return void 0;
44522
44564
  const orig = opts.templateOffset + doctypeLen + (node.getStart(sf) - WRAP_PREFIX_LEN);
@@ -44622,9 +44664,20 @@ function lowerTemplate(template, opts = {}) {
44622
44664
  return out;
44623
44665
  };
44624
44666
  const contentOfChild = (node) => {
44625
- if (import_typescript3.default.isJsxText(node)) return escapeText(node.getText(sf));
44667
+ if (import_typescript3.default.isJsxText(node)) return escapeText(cleanJsxText(node.getFullText(sf)));
44626
44668
  if (import_typescript3.default.isJsxExpression(node)) {
44627
44669
  if (!node.expression) return "";
44670
+ if (opts.client && isClientRead(node.expression)) {
44671
+ const marker = allocSlotMarker();
44672
+ const expr = clientReadExpr(node.expression);
44673
+ opts.client.directives.push({
44674
+ marker,
44675
+ kind: "slot",
44676
+ expr,
44677
+ deps: inferDeps(expr, opts.client.useFields)
44678
+ });
44679
+ return `<!--${marker}-->`;
44680
+ }
44628
44681
  return `\${${lowerExprText(node.expression)}}`;
44629
44682
  }
44630
44683
  if (import_typescript3.default.isJsxElement(node)) {
@@ -44658,6 +44711,7 @@ function lowerTemplate(template, opts = {}) {
44658
44711
  if (import_typescript3.default.isJsxElement(expr) || import_typescript3.default.isJsxSelfClosingElement(expr) || import_typescript3.default.isJsxFragment(expr)) {
44659
44712
  return `html\`${contentOfChild(expr)}\``;
44660
44713
  }
44714
+ assertNoClientRead(expr);
44661
44715
  if (isItemRead(expr)) {
44662
44716
  return `itemBind(${lowerExprText(expr.arguments[1])})`;
44663
44717
  }
@@ -44703,56 +44757,79 @@ function lowerTemplate(template, opts = {}) {
44703
44757
  );
44704
44758
  }
44705
44759
  }
44706
- const clientMarker = opts.client ? collectClientDirectives(attrs) : void 0;
44760
+ const collected = opts.client ? collectClientDirectives(attrs) : void 0;
44707
44761
  let out = "";
44708
44762
  for (const attr of attrs.properties) {
44709
44763
  if (import_typescript3.default.isJsxSpreadAttribute(attr)) {
44710
- throw new CompileError("stator: spread attributes ({...x}) are not supported", loc2(attr));
44711
- }
44712
- if (import_typescript3.default.isJsxAttribute(attr) && !import_typescript3.default.isJsxNamespacedName(attr.name) && attr.name.getText(sf) === "child") {
44764
+ out += ` \${spreadAttrs(${lowerExprText(attr.expression)})}`;
44713
44765
  continue;
44714
44766
  }
44715
- if (opts.client && import_typescript3.default.isJsxAttribute(attr) && import_typescript3.default.isJsxNamespacedName(attr.name) && (attr.name.namespace.text === "on" || attr.name.namespace.text === "bind")) {
44767
+ if (import_typescript3.default.isJsxAttribute(attr) && !import_typescript3.default.isJsxNamespacedName(attr.name) && attr.name.getText(sf) === "child") {
44716
44768
  continue;
44717
44769
  }
44770
+ if (collected?.consumed.has(attr)) continue;
44718
44771
  const lowered = lowerAttribute(attr);
44719
44772
  if (lowered) out += ` ${lowered}`;
44720
44773
  }
44721
- if (clientMarker) out += ` data-b="${clientMarker}"`;
44774
+ if (collected?.marker) out += ` data-b="${collected.marker}"`;
44722
44775
  return out;
44723
44776
  };
44724
44777
  const collectClientDirectives = (attrs) => {
44778
+ const consumed = /* @__PURE__ */ new Set();
44725
44779
  const client = opts.client;
44726
- if (!client) return void 0;
44780
+ if (!client) return { consumed };
44727
44781
  const pending = [];
44728
44782
  for (const attr of attrs.properties) {
44729
- if (!import_typescript3.default.isJsxAttribute(attr) || !import_typescript3.default.isJsxNamespacedName(attr.name)) continue;
44730
- const ns = attr.name.namespace.text;
44731
- const name = attr.name.name.text;
44732
- if (ns !== "on" && ns !== "bind") continue;
44733
- const expr = attrExpr(attr);
44734
- if (!expr) {
44735
- throw new CompileError(`stator: ${ns}:${name} requires a value ({...})`, loc2(attr));
44783
+ if (!import_typescript3.default.isJsxAttribute(attr)) continue;
44784
+ if (import_typescript3.default.isJsxNamespacedName(attr.name)) {
44785
+ const ns = attr.name.namespace.text;
44786
+ const name = attr.name.name.text;
44787
+ if (ns !== "on" && ns !== "bind") continue;
44788
+ const expr = attrExpr(attr);
44789
+ if (!expr) {
44790
+ throw new CompileError(`stator: ${ns}:${name} requires a value ({...})`, loc2(attr));
44791
+ }
44792
+ if (ns === "on") {
44793
+ pending.push({ marker: "", kind: "on", event: name, expr, deps: [] });
44794
+ } else {
44795
+ pending.push({
44796
+ marker: "",
44797
+ kind: "bind",
44798
+ target: name,
44799
+ expr,
44800
+ deps: inferDeps(expr, client.useFields)
44801
+ });
44802
+ }
44803
+ consumed.add(attr);
44804
+ continue;
44736
44805
  }
44737
- if (ns === "on") {
44738
- pending.push({ marker: "", kind: "on", event: name, expr, deps: [] });
44739
- } else {
44806
+ const init = attr.initializer;
44807
+ if (init && import_typescript3.default.isJsxExpression(init) && init.expression && isClientRead(init.expression)) {
44808
+ const target = attr.name.getText(sf);
44809
+ if (target === "value" || target === "checked") {
44810
+ throw new CompileError(
44811
+ `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.`,
44812
+ loc2(attr)
44813
+ );
44814
+ }
44815
+ const expr = clientReadExpr(init.expression);
44740
44816
  pending.push({
44741
44817
  marker: "",
44742
44818
  kind: "bind",
44743
- target: name,
44819
+ target,
44744
44820
  expr,
44745
44821
  deps: inferDeps(expr, client.useFields)
44746
44822
  });
44823
+ consumed.add(attr);
44747
44824
  }
44748
44825
  }
44749
- if (pending.length === 0) return void 0;
44750
- const marker = `b${new Set(client.directives.map((d) => d.marker)).size}`;
44826
+ if (pending.length === 0) return { consumed };
44827
+ const marker = `b${new Set(client.directives.filter((d) => d.kind !== "slot").map((d) => d.marker)).size}`;
44751
44828
  for (const d of pending) {
44752
44829
  d.marker = marker;
44753
44830
  client.directives.push(d);
44754
44831
  }
44755
- return marker;
44832
+ return { marker, consumed };
44756
44833
  };
44757
44834
  const lowerChildrenPlaceholder = (attrs) => {
44758
44835
  if (meta) meta.usesChildren = true;
@@ -44827,18 +44904,30 @@ function lowerTemplate(template, opts = {}) {
44827
44904
  };
44828
44905
  const lowerComponent = (tag, attrs, children) => {
44829
44906
  const entries = [];
44907
+ const forwarded = [];
44830
44908
  for (const attr of attrs.properties) {
44831
44909
  if (import_typescript3.default.isJsxSpreadAttribute(attr)) {
44832
- throw new CompileError(
44833
- `stator: spread props ({...x}) on <${tag}/> are not supported`,
44834
- loc2(attr)
44835
- );
44910
+ entries.push(`...${lowerExprText(attr.expression)}`);
44911
+ continue;
44836
44912
  }
44837
44913
  if (import_typescript3.default.isJsxNamespacedName(attr.name)) {
44838
- throw new CompileError(
44839
- `stator: directive "${attr.name.namespace.text}:${attr.name.name.text}" is not valid on component <${tag}/> \u2014 directives apply to HTML elements, not components`,
44840
- loc2(attr)
44841
- );
44914
+ const ns = attr.name.namespace.text;
44915
+ const dirName = attr.name.name.text;
44916
+ if (ns !== "on") {
44917
+ throw new CompileError(
44918
+ `stator: forwarding "${ns}:${dirName}" to a component isn't supported yet \u2014 only on:* event directives forward. Apply bind:/ref: on an element directly.`,
44919
+ loc2(attr)
44920
+ );
44921
+ }
44922
+ const value = attrExpr(attr);
44923
+ if (!value) {
44924
+ throw new CompileError(
44925
+ `stator: on:${dirName} on <${tag}/> requires a handler ({...})`,
44926
+ loc2(attr)
44927
+ );
44928
+ }
44929
+ forwarded.push(`${JSON.stringify(`on:${dirName}`)}: ${value}`);
44930
+ continue;
44842
44931
  }
44843
44932
  const name = attr.name.getText(sf);
44844
44933
  if (!attr.initializer) {
@@ -44875,6 +44964,7 @@ function lowerTemplate(template, opts = {}) {
44875
44964
  }
44876
44965
  if (bag.length > 0) entries.push(`children: { ${bag.join(", ")} }`);
44877
44966
  }
44967
+ if (forwarded.length > 0) entries.push(`$directives: { ${forwarded.join(", ")} }`);
44878
44968
  return `${tag}({ ${entries.join(", ")} })`;
44879
44969
  };
44880
44970
  const body = `html\`${doctype}${contentOfChildren(fragment.children)}\``;
@@ -45128,7 +45218,7 @@ function isSubjectGlobal(sel) {
45128
45218
  }
45129
45219
 
45130
45220
  // ../stator/src/compiler/compile.ts
45131
- var PRIMITIVES_IMPORT = "import { html, read, each, itemBind, when, match, defer, on, classList, styleList } from '@statorjs/stator/template'";
45221
+ var PRIMITIVES_IMPORT = "import { html, read, each, itemBind, when, match, defer, on, classList, styleList, spreadAttrs } from '@statorjs/stator/template'";
45132
45222
  function compile(source, opts = {}) {
45133
45223
  const { frontmatter, template, styles, scripts, scriptOffsets, templateOffset } = splitStator(source);
45134
45224
  const kind = opts.kind ?? "component";
@@ -45205,7 +45295,7 @@ function compileClient(template, script, ctx) {
45205
45295
  const attrDecl = `{ ${[...cls.staticAttrs].map(([k, v]) => `${k}: ${JSON.stringify(v)}`).join(", ")} }`;
45206
45296
  const rootScope = ctx.scopeAttr ? ` data-s-${ctx.hash}` : "";
45207
45297
  const serverCode = [
45208
- "import { html, read, each, when, match, defer, on, classList, styleList, createHtmlFragment, clientShellAttrs } from '@statorjs/stator/template'",
45298
+ "import { html, read, each, when, match, defer, on, classList, styleList, spreadAttrs, createHtmlFragment, clientShellAttrs } from '@statorjs/stator/template'",
45209
45299
  "",
45210
45300
  `export default function (props = {}) {`,
45211
45301
  ` const __inner = ${innerExpr}`,
@@ -45334,6 +45424,23 @@ function processFrontmatter(fm, kind, source, file) {
45334
45424
  repls.push([n.getStart(sf), n.getEnd(), bound]);
45335
45425
  return;
45336
45426
  }
45427
+ if (import_typescript4.default.isCallExpression(n) && isStatorCall(n, "forwarded")) {
45428
+ if (kind === "route") {
45429
+ throw new CompileError(
45430
+ "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.",
45431
+ locInFm(n)
45432
+ );
45433
+ }
45434
+ const arg = n.arguments[0];
45435
+ if (!arg) {
45436
+ throw new CompileError(
45437
+ "stator: Stator.forwarded(name) takes a directive name, e.g. Stator.forwarded('on:click')",
45438
+ locInFm(n)
45439
+ );
45440
+ }
45441
+ repls.push([n.getStart(sf), n.getEnd(), `props.$directives?.[${arg.getText(sf)}]`]);
45442
+ return;
45443
+ }
45337
45444
  if (import_typescript4.default.isPropertyAccessExpression(n) && isStatorMember(n, "request")) {
45338
45445
  requireRoute("Stator.request", kind, locInFm(n));
45339
45446
  repls.push([n.getStart(sf), n.getEnd(), "__req"]);
@@ -45495,16 +45602,17 @@ function injectImports(globals, modulePath, userCode) {
45495
45602
  return missing.length > 0 ? `import { ${missing.join(", ")} } from '${modulePath}';
45496
45603
  ` : "";
45497
45604
  }
45498
- var AMBIENT_TYPE_IMPORTS = "import type { AnyMachineDef as __SMachineDef } from '@statorjs/stator/machine';\nimport type { InstanceOf as __SInstanceOf } from '@statorjs/stator/template';\n";
45605
+ 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";
45499
45606
  var STATOR_AMBIENT = `declare const Stator: {
45500
45607
  props<P>(): P;
45501
45608
  reads<const T extends readonly __SMachineDef[]>(defs: T): { -readonly [K in keyof T]: __SInstanceOf<T[K]> };
45609
+ forwarded(name: \`on:\${string}\`): (() => unknown) | undefined;
45502
45610
  request: any;
45503
45611
  response: { status: number; headers: Record<string, string>; cookies: any };
45504
45612
  };
45505
45613
  declare global {
45506
45614
  namespace JSX {
45507
- interface IntrinsicElements extends Record<string, any> {}
45615
+ interface IntrinsicElements extends __SIntrinsics {}
45508
45616
  }
45509
45617
  }
45510
45618
  `;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statorjs/language-server",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
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.7.1"
36
+ "@statorjs/stator": "1.9.0"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "typescript": "^5.6.0"