@statorjs/language-server 0.1.6 → 0.1.8

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 -11
  2. package/package.json +2 -2
package/dist/server.cjs CHANGED
@@ -44476,6 +44476,17 @@ var WRAP_PREFIX_LEN = "const __t = (<>".length;
44476
44476
  var INLINE_SCRIPT_RE = /<script\b([^>]*\bis:inline\b[^>]*)>([\s\S]*?)<\/script>/gi;
44477
44477
  var inlineMarker = (i) => `\0statorInlineScript${i}\0`;
44478
44478
  var INLINE_MARKER_RE = /statorInlineScript(\d+)/g;
44479
+ function eachItemParamsFor(jsx) {
44480
+ let node = jsx.parent;
44481
+ while (node && import_typescript3.default.isParenthesizedExpression(node)) node = node.parent;
44482
+ if (!node || !import_typescript3.default.isArrowFunction(node)) return null;
44483
+ const call = node.parent;
44484
+ if (!call || !import_typescript3.default.isCallExpression(call) || !import_typescript3.default.isIdentifier(call.expression)) return null;
44485
+ if (call.expression.text !== "each" || call.arguments[1] !== node) return null;
44486
+ const p0 = node.parameters[0];
44487
+ if (!p0 || !import_typescript3.default.isIdentifier(p0.name)) return null;
44488
+ return { item: p0.name.text };
44489
+ }
44479
44490
  function lowerTemplate(template, opts = {}) {
44480
44491
  let doctype = "";
44481
44492
  let doctypeLen = 0;
@@ -44503,6 +44514,9 @@ function lowerTemplate(template, opts = {}) {
44503
44514
  );
44504
44515
  const scopeSuffix = opts.scopeAttr ? ` ${opts.scopeAttr}` : "";
44505
44516
  const meta = opts.meta;
44517
+ let eachParams = null;
44518
+ const enableItemBind = !opts.client;
44519
+ 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;
44506
44520
  const loc2 = (node) => {
44507
44521
  if (opts.source == null || opts.templateOffset == null) return void 0;
44508
44522
  const orig = opts.templateOffset + doctypeLen + (node.getStart(sf) - WRAP_PREFIX_LEN);
@@ -44518,6 +44532,90 @@ function lowerTemplate(template, opts = {}) {
44518
44532
  };
44519
44533
  find(sf);
44520
44534
  if (!fragment) throw new CompileError("stator: could not parse template body as JSX");
44535
+ const checkNoReadInDefer = (node, insideDefer) => {
44536
+ let nowInsideDefer = insideDefer;
44537
+ if (import_typescript3.default.isCallExpression(node) && import_typescript3.default.isIdentifier(node.expression)) {
44538
+ const callee = node.expression.text;
44539
+ if (callee === "defer") {
44540
+ nowInsideDefer = true;
44541
+ } else if (callee === "read" && insideDefer) {
44542
+ throw new CompileError(
44543
+ `stator: read() cannot appear inside a defer() arm \u2014 a defer slot is one-shot and static, so the value would never update. For a live value, use a machine and place the read in a sibling slot outside the defer.`,
44544
+ loc2(node)
44545
+ );
44546
+ }
44547
+ }
44548
+ import_typescript3.default.forEachChild(node, (child) => checkNoReadInDefer(child, nowInsideDefer));
44549
+ };
44550
+ checkNoReadInDefer(fragment, false);
44551
+ const checkItemReadPlacement = (node, frames) => {
44552
+ if (import_typescript3.default.isCallExpression(node) && import_typescript3.default.isIdentifier(node.expression)) {
44553
+ const callee = node.expression.text;
44554
+ const arg0 = node.arguments[0];
44555
+ if (callee === "read" && node.arguments.length >= 2 && arg0 && import_typescript3.default.isIdentifier(arg0)) {
44556
+ const param = arg0.text;
44557
+ for (let i = frames.length - 1; i >= 0; i--) {
44558
+ const f = frames[i];
44559
+ if (f.kind !== "each" || f.param !== param) continue;
44560
+ const between = frames.slice(i + 1);
44561
+ const branch = between.find((b) => b.kind === "branch");
44562
+ if (branch && branch.kind === "branch") {
44563
+ throw new CompileError(
44564
+ branch.position === "head" ? `stator: read(${param}, \u2026) cannot drive a ${branch.callee}() \u2014 a branch binding needs a machine source to re-diff against. Derive the condition in a machine selector and read that instead.` : `stator: read(${param}, \u2026) cannot appear inside a ${branch.callee}() arm \u2014 an item read is owned by its each() row, and an arm re-renders on its own schedule without the row. Inside the arm, read the field from the machine (read(machine, (m) => m.items.find(\u2026))), or restructure so the arm doesn't split the row (render both states and toggle with a class binding + CSS).`,
44565
+ loc2(node)
44566
+ );
44567
+ }
44568
+ const outer = between.find((b) => b.kind === "each");
44569
+ if (outer) {
44570
+ throw new CompileError(
44571
+ `stator: read(${param}, \u2026) reads an outer each()'s item from inside a nested each() row \u2014 an item read is owned by the row that binds its item, and the inner row would evaluate it against the wrong item. Derive the field onto the inner item, or use a machine read.`,
44572
+ loc2(node)
44573
+ );
44574
+ }
44575
+ const listAttr = between.find((b) => b.kind === "list-attr");
44576
+ if (listAttr && listAttr.kind === "list-attr") {
44577
+ throw new CompileError(
44578
+ `stator: read(${param}, \u2026) is not supported inside a ${listAttr.attr} spec \u2014 the compound directive recomposes per machine, not per row. Give the whole attribute a single item read (style={read(${param}, \u2026)}), or use a machine read inside the spec.`,
44579
+ loc2(node)
44580
+ );
44581
+ }
44582
+ break;
44583
+ }
44584
+ }
44585
+ if (callee === "when" || callee === "match" || callee === "defer") {
44586
+ node.arguments.forEach((arg, idx) => {
44587
+ checkItemReadPlacement(arg, [
44588
+ ...frames,
44589
+ { kind: "branch", callee, position: idx === 0 ? "head" : "arm" }
44590
+ ]);
44591
+ });
44592
+ return;
44593
+ }
44594
+ if (callee === "each") {
44595
+ const renderer = node.arguments[1];
44596
+ for (const arg of node.arguments) {
44597
+ if (arg === renderer && import_typescript3.default.isArrowFunction(arg)) {
44598
+ const p0 = arg.parameters[0];
44599
+ const inner = p0 && import_typescript3.default.isIdentifier(p0.name) ? [...frames, { kind: "each", param: p0.name.text }] : frames;
44600
+ checkItemReadPlacement(arg.body, inner);
44601
+ } else {
44602
+ checkItemReadPlacement(arg, frames);
44603
+ }
44604
+ }
44605
+ return;
44606
+ }
44607
+ }
44608
+ if (import_typescript3.default.isJsxAttribute(node) && import_typescript3.default.isJsxNamespacedName(node.name) && node.name.name.text === "list" && (node.name.namespace.text === "class" || node.name.namespace.text === "style")) {
44609
+ const attr = `${node.name.namespace.text}:list`;
44610
+ import_typescript3.default.forEachChild(
44611
+ node,
44612
+ (c) => checkItemReadPlacement(c, [...frames, { kind: "list-attr", attr }])
44613
+ );
44614
+ return;
44615
+ }
44616
+ import_typescript3.default.forEachChild(node, (c) => checkItemReadPlacement(c, frames));
44617
+ };
44618
+ if (enableItemBind) checkItemReadPlacement(fragment, []);
44521
44619
  const contentOfChildren = (children) => {
44522
44620
  let out = "";
44523
44621
  for (const child of children) out += contentOfChild(child);
@@ -44560,16 +44658,24 @@ function lowerTemplate(template, opts = {}) {
44560
44658
  if (import_typescript3.default.isJsxElement(expr) || import_typescript3.default.isJsxSelfClosingElement(expr) || import_typescript3.default.isJsxFragment(expr)) {
44561
44659
  return `html\`${contentOfChild(expr)}\``;
44562
44660
  }
44661
+ if (isItemRead(expr)) {
44662
+ return `itemBind(${lowerExprText(expr.arguments[1])})`;
44663
+ }
44563
44664
  const exprStart = expr.getStart(sf);
44564
44665
  let text = expr.getText(sf);
44565
44666
  const repls = [];
44566
44667
  const visit = (n) => {
44567
44668
  if (n !== expr && (import_typescript3.default.isJsxElement(n) || import_typescript3.default.isJsxSelfClosingElement(n) || import_typescript3.default.isJsxFragment(n))) {
44568
- repls.push([
44569
- n.getStart(sf),
44570
- n.getEnd(),
44571
- `html\`${contentOfChild(n)}\``
44572
- ]);
44669
+ const prevEach = eachParams;
44670
+ const params = enableItemBind ? eachItemParamsFor(n) : null;
44671
+ if (params) eachParams = params;
44672
+ const lowered = `html\`${contentOfChild(n)}\``;
44673
+ eachParams = prevEach;
44674
+ repls.push([n.getStart(sf), n.getEnd(), lowered]);
44675
+ return;
44676
+ }
44677
+ if (n !== expr && isItemRead(n)) {
44678
+ repls.push([n.getStart(sf), n.getEnd(), `itemBind(${n.arguments[1].getText(sf)})`]);
44573
44679
  return;
44574
44680
  }
44575
44681
  import_typescript3.default.forEachChild(n, visit);
@@ -44582,6 +44688,21 @@ function lowerTemplate(template, opts = {}) {
44582
44688
  return text;
44583
44689
  };
44584
44690
  const lowerAttributes = (attrs) => {
44691
+ for (const ns of ["class", "style"]) {
44692
+ const hasStatic = attrs.properties.some(
44693
+ (a) => import_typescript3.default.isJsxAttribute(a) && !import_typescript3.default.isJsxNamespacedName(a.name) && a.name.getText(sf) === ns
44694
+ );
44695
+ const listAttr = attrs.properties.find(
44696
+ (a) => import_typescript3.default.isJsxAttribute(a) && import_typescript3.default.isJsxNamespacedName(a.name) && a.name.namespace.text === ns && a.name.name.text === "list"
44697
+ );
44698
+ if (hasStatic && listAttr) {
44699
+ const example = ns === "class" ? "class:list={['place-tab', { active }]}" : "style:list={['color: red', { ... }]}";
44700
+ throw new CompileError(
44701
+ `stator: an element has both a static \`${ns}\` attribute and \`${ns}:list\` \u2014 they emit two \`${ns}\` attributes and the browser silently keeps only one. Move the static ${ns} into \`${ns}:list\`, which accepts a static string alongside the dynamic parts: ${example}.`,
44702
+ loc2(listAttr)
44703
+ );
44704
+ }
44705
+ }
44585
44706
  const clientMarker = opts.client ? collectClientDirectives(attrs) : void 0;
44586
44707
  let out = "";
44587
44708
  for (const attr of attrs.properties) {
@@ -45007,7 +45128,7 @@ function isSubjectGlobal(sel) {
45007
45128
  }
45008
45129
 
45009
45130
  // ../stator/src/compiler/compile.ts
45010
- var PRIMITIVES_IMPORT = "import { html, read, each, when, match, on, classList, styleList } from '@statorjs/stator/template'";
45131
+ var PRIMITIVES_IMPORT = "import { html, read, each, itemBind, when, match, defer, on, classList, styleList } from '@statorjs/stator/template'";
45011
45132
  function compile(source, opts = {}) {
45012
45133
  const { frontmatter, template, styles, scripts, scriptOffsets, templateOffset } = splitStator(source);
45013
45134
  const kind = opts.kind ?? "component";
@@ -45084,11 +45205,11 @@ function compileClient(template, script, ctx) {
45084
45205
  const attrDecl = `{ ${[...cls.staticAttrs].map(([k, v]) => `${k}: ${JSON.stringify(v)}`).join(", ")} }`;
45085
45206
  const rootScope = ctx.scopeAttr ? ` data-s-${ctx.hash}` : "";
45086
45207
  const serverCode = [
45087
- "import { html, read, each, when, match, on, classList, styleList, createHtmlFragment, clientShellAttrs } from '@statorjs/stator/template'",
45208
+ "import { html, read, each, when, match, defer, on, classList, styleList, createHtmlFragment, clientShellAttrs } from '@statorjs/stator/template'",
45088
45209
  "",
45089
45210
  `export default function (props = {}) {`,
45090
45211
  ` const __inner = ${innerExpr}`,
45091
- ` const __attrs = clientShellAttrs(props, ${attrDecl})`,
45212
+ ` const __attrs = clientShellAttrs(props, ${attrDecl}, ${JSON.stringify(root2.rootAttrs)})`,
45092
45213
  ` return createHtmlFragment(\`<${root2.tag}\${__attrs}${rootScope}>\` + __inner.html + \`</${root2.tag}>\`)`,
45093
45214
  "}",
45094
45215
  ""
@@ -45109,6 +45230,20 @@ function compileClient(template, script, ctx) {
45109
45230
  clientTag: root2.tag
45110
45231
  };
45111
45232
  }
45233
+ function staticRootAttrs(attrs, sf) {
45234
+ const out = {};
45235
+ for (const attr of attrs.properties) {
45236
+ if (!import_typescript4.default.isJsxAttribute(attr) || import_typescript4.default.isJsxNamespacedName(attr.name)) continue;
45237
+ const name = attr.name.getText(sf);
45238
+ const init = attr.initializer;
45239
+ if (init === void 0) {
45240
+ out[name] = true;
45241
+ } else if (import_typescript4.default.isStringLiteral(init)) {
45242
+ out[name] = init.text;
45243
+ }
45244
+ }
45245
+ return out;
45246
+ }
45112
45247
  function extractClientRoot(template, file) {
45113
45248
  const wrapped = `const __t = (<>${template}</>);`;
45114
45249
  const sf = import_typescript4.default.createSourceFile("t.tsx", wrapped, import_typescript4.default.ScriptTarget.Latest, true, import_typescript4.default.ScriptKind.TSX);
@@ -45134,13 +45269,13 @@ function extractClientRoot(template, file) {
45134
45269
  if (import_typescript4.default.isJsxSelfClosingElement(rootEl)) {
45135
45270
  const tag2 = rootEl.tagName.getText(sf);
45136
45271
  requireCustomRoot(tag2, file);
45137
- return { tag: tag2, inner: "" };
45272
+ return { tag: tag2, inner: "", rootAttrs: staticRootAttrs(rootEl.attributes, sf) };
45138
45273
  }
45139
45274
  const el = rootEl;
45140
45275
  const tag = el.openingElement.tagName.getText(sf);
45141
45276
  requireCustomRoot(tag, file);
45142
45277
  const inner = wrapped.slice(el.openingElement.getEnd(), el.closingElement.getStart());
45143
- return { tag, inner };
45278
+ return { tag, inner, rootAttrs: staticRootAttrs(el.openingElement.attributes, sf) };
45144
45279
  }
45145
45280
  function requireCustomRoot(tag, file) {
45146
45281
  if (!isCustomElementTag(tag)) {
@@ -45329,7 +45464,7 @@ var import_typescript6 = __toESM(require("typescript"), 1);
45329
45464
 
45330
45465
  // ../stator/src/compiler/virtual-code.ts
45331
45466
  var import_typescript7 = __toESM(require("typescript"), 1);
45332
- var TEMPLATE_GLOBALS = ["read", "each", "when", "match", "on", "classList", "styleList"];
45467
+ var TEMPLATE_GLOBALS = ["read", "each", "when", "match", "defer", "on", "classList", "styleList"];
45333
45468
  var CLIENT_GLOBALS = [
45334
45469
  "StatorElement",
45335
45470
  "use",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statorjs/language-server",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
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.3.0"
36
+ "@statorjs/stator": "1.4.1"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "typescript": "^5.6.0"