@statorjs/language-server 0.1.6 → 0.1.7
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.
- package/dist/server.cjs +146 -11
- 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
|
-
|
|
44569
|
-
|
|
44570
|
-
|
|
44571
|
-
|
|
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.
|
|
3
|
+
"version": "0.1.7",
|
|
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.
|
|
36
|
+
"@statorjs/stator": "1.4.0"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"typescript": "^5.6.0"
|