@statorjs/language-server 0.1.20 → 0.1.21

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 +128 -20
  2. package/package.json +2 -2
package/dist/server.cjs CHANGED
@@ -44183,8 +44183,38 @@ var elementMarker = (i) => `b${i}`;
44183
44183
  var slotMarker = (i) => `s${i}`;
44184
44184
  var elementMarkerSelector = (marker) => `[${ISLAND_MARKER_ATTR}="${marker}"]`;
44185
44185
 
44186
+ // ../stator/src/compiler/emit-names.ts
44187
+ var TEMPLATE_AUTHOR_GLOBALS = [
44188
+ "read",
44189
+ "each",
44190
+ "when",
44191
+ "match",
44192
+ "defer",
44193
+ "on",
44194
+ "classList",
44195
+ "styleList"
44196
+ ];
44197
+ var TEMPLATE_LOWERING_TARGETS = ["html", "itemBind", "spreadAttrs"];
44198
+ var ISLAND_SHELL_EXTRAS = ["createHtmlFragment", "clientShellAttrs"];
44199
+ var CLIENT_AUTHOR_GLOBALS = [
44200
+ "StatorElement",
44201
+ "use",
44202
+ "machine",
44203
+ "defineElement",
44204
+ "bind",
44205
+ "effect",
44206
+ "dispatch"
44207
+ ];
44208
+ var CLIENT_LOWERING_TARGETS = ["bindSlot", "attrValue", "setAttr"];
44209
+ function importLine(names, from) {
44210
+ return `import { ${names.join(", ")} } from '${from}'`;
44211
+ }
44212
+
44186
44213
  // ../stator/src/compiler/client-emit.ts
44187
- var PRIMITIVES = "import { StatorElement, defineElement, use, machine, bind, bindSlot, effect, dispatch, attrValue, setAttr } from '@statorjs/stator/client'";
44214
+ var PRIMITIVES = importLine(
44215
+ [...CLIENT_AUTHOR_GLOBALS, ...CLIENT_LOWERING_TARGETS],
44216
+ "@statorjs/stator/client"
44217
+ );
44188
44218
  function emitClientModule(input) {
44189
44219
  const { script, element, directives, members } = input;
44190
44220
  const impl = `__${element.className}Impl`;
@@ -45199,7 +45229,18 @@ function isSubjectGlobal(sel) {
45199
45229
  }
45200
45230
 
45201
45231
  // ../stator/src/compiler/compile.ts
45202
- var PRIMITIVES_IMPORT = "import { html, read, each, itemBind, when, match, defer, on, classList, styleList, spreadAttrs } from '@statorjs/stator/template'";
45232
+ var PRIMITIVES_IMPORT = importLine(
45233
+ [...TEMPLATE_AUTHOR_GLOBALS, ...TEMPLATE_LOWERING_TARGETS],
45234
+ "@statorjs/stator/template"
45235
+ );
45236
+ var SHELL_PRIMITIVES_IMPORT = importLine(
45237
+ [
45238
+ ...TEMPLATE_AUTHOR_GLOBALS,
45239
+ ...TEMPLATE_LOWERING_TARGETS.filter((n) => n !== "itemBind"),
45240
+ ...ISLAND_SHELL_EXTRAS
45241
+ ],
45242
+ "@statorjs/stator/template"
45243
+ );
45203
45244
  function compile(source, opts = {}) {
45204
45245
  const { frontmatter, template, styles, scripts, scriptOffsets, templateOffset } = splitStator(source);
45205
45246
  const kind = opts.kind ?? "component";
@@ -45209,17 +45250,14 @@ function compile(source, opts = {}) {
45209
45250
  const script = scripts.join("\n");
45210
45251
  if (script.trim()) {
45211
45252
  if (analyzeScriptClasses(script).length > 0) {
45212
- if (frontmatter.trim()) {
45213
- throw new CompileError(
45214
- `stator: an island file (a template with a StatorElement <script>) can't have frontmatter \u2014 the fence would never run: the island's shell renders from props. Move the server work into the route or component that renders this island and pass the results as props.`,
45215
- locAt(source, 0, opts.id)
45216
- );
45217
- }
45253
+ const fm2 = processFrontmatter(frontmatter, "island", source, opts.id);
45218
45254
  return compileClient(template, script, {
45219
45255
  hash,
45220
45256
  scopeAttr,
45221
45257
  styles,
45222
- file: opts.id
45258
+ file: opts.id,
45259
+ fm: fm2,
45260
+ source
45223
45261
  });
45224
45262
  }
45225
45263
  throw new CompileError(
@@ -45260,6 +45298,17 @@ function compileClient(template, script, ctx) {
45260
45298
  const tags = /* @__PURE__ */ new Set([root2.tag]);
45261
45299
  analyzeClient(script, tags, { file: ctx.file });
45262
45300
  const cls = classes.find((c) => pascalToKebab(c.name) === root2.tag);
45301
+ if (ctx.fm && cls) {
45302
+ const fenceNames = fenceBindingNames(ctx.fm);
45303
+ for (const field of cls.useFields.keys()) {
45304
+ if (fenceNames.has(field)) {
45305
+ throw new CompileError(
45306
+ `stator: "${field}" is both a fence binding and a use() field \u2014 the template couldn't tell the server value from the client machine. Rename one.`,
45307
+ ctx.source ? locAt(ctx.source, 0, ctx.file) : void 0
45308
+ );
45309
+ }
45310
+ }
45311
+ }
45263
45312
  if (!cls) {
45264
45313
  throw new CompileError(
45265
45314
  `stator: internal \u2014 no exported class matches root <${root2.tag}> after name validation`
@@ -45282,9 +45331,13 @@ function compileClient(template, script, ctx) {
45282
45331
  const attrDecl = `{ ${[...cls.staticAttrs].map(([k, v]) => `${k}: ${JSON.stringify(v)}`).join(", ")} }`;
45283
45332
  const rootScope = ctx.scopeAttr ? ` data-s-${ctx.hash}` : "";
45284
45333
  const serverCode = [
45285
- "import { html, read, each, when, match, defer, on, classList, styleList, spreadAttrs, createHtmlFragment, clientShellAttrs } from '@statorjs/stator/template'",
45334
+ SHELL_PRIMITIVES_IMPORT,
45335
+ ...ctx.fm?.hoisted ? [ctx.fm.hoisted] : [],
45286
45336
  "",
45287
45337
  `export default function (props = {}) {`,
45338
+ // The fence body runs here — per shell render, server only, ahead of
45339
+ // the template so its bindings are in scope for the interpolations.
45340
+ ...ctx.fm?.body ? [ctx.fm.body] : [],
45288
45341
  ` const __inner = ${innerExpr}`,
45289
45342
  ` const __attrs = clientShellAttrs(props, ${attrDecl}, ${JSON.stringify(root2.rootAttrs)})`,
45290
45343
  ` return createHtmlFragment(\`<${root2.tag}\${__attrs}${rootScope}>\` + __inner.html + \`</${root2.tag}>\`)`,
@@ -45382,6 +45435,12 @@ function processFrontmatter(fm, kind, source, file) {
45382
45435
  const repls = [];
45383
45436
  const visit = (n) => {
45384
45437
  if (import_typescript4.default.isCallExpression(n) && isStatorCall(n, "props")) {
45438
+ if (kind === "island") {
45439
+ throw new CompileError(
45440
+ "stator: Stator.props() is not available in an island's fence \u2014 island props are declared by `static attrs` on the element class, and richer values arrive through the hydrate pattern (the shell renders with the props of its use site).",
45441
+ locInFm(n)
45442
+ );
45443
+ }
45385
45444
  if (kind === "route") {
45386
45445
  throw new CompileError(
45387
45446
  "stator: Stator.props() is not available in a route page (a route has no parent props). Use Stator.reads([...]) to access machines.",
@@ -45412,6 +45471,12 @@ function processFrontmatter(fm, kind, source, file) {
45412
45471
  return;
45413
45472
  }
45414
45473
  if (import_typescript4.default.isCallExpression(n) && isStatorCall(n, "forwarded")) {
45474
+ if (kind === "island") {
45475
+ throw new CompileError(
45476
+ "stator: Stator.forwarded() is not available in an island's fence.",
45477
+ locInFm(n)
45478
+ );
45479
+ }
45415
45480
  if (kind === "route") {
45416
45481
  throw new CompileError(
45417
45482
  "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.",
@@ -45455,6 +45520,38 @@ function processFrontmatter(fm, kind, source, file) {
45455
45520
  pragmas
45456
45521
  };
45457
45522
  }
45523
+ function fenceBindingNames(fm) {
45524
+ const names = /* @__PURE__ */ new Set();
45525
+ const sf = import_typescript4.default.createSourceFile(
45526
+ "fence.ts",
45527
+ `${fm.hoisted}
45528
+ ${fm.body}`,
45529
+ import_typescript4.default.ScriptTarget.Latest,
45530
+ true,
45531
+ import_typescript4.default.ScriptKind.TS
45532
+ );
45533
+ const bind = (n) => {
45534
+ if (import_typescript4.default.isIdentifier(n)) names.add(n.text);
45535
+ else for (const el of n.elements) if (import_typescript4.default.isBindingElement(el)) bind(el.name);
45536
+ };
45537
+ for (const stmt of sf.statements) {
45538
+ if (import_typescript4.default.isImportDeclaration(stmt) && stmt.importClause) {
45539
+ const c = stmt.importClause;
45540
+ if (c.name) names.add(c.name.text);
45541
+ if (c.namedBindings) {
45542
+ if (import_typescript4.default.isNamespaceImport(c.namedBindings)) names.add(c.namedBindings.name.text);
45543
+ else for (const s of c.namedBindings.elements) names.add(s.name.text);
45544
+ }
45545
+ } else if (import_typescript4.default.isVariableStatement(stmt)) {
45546
+ for (const d of stmt.declarationList.declarations) bind(d.name);
45547
+ } else if (import_typescript4.default.isFunctionDeclaration(stmt) && stmt.name) {
45548
+ names.add(stmt.name.text);
45549
+ } else if (import_typescript4.default.isClassDeclaration(stmt) && stmt.name) {
45550
+ names.add(stmt.name.text);
45551
+ }
45552
+ }
45553
+ return names;
45554
+ }
45458
45555
  function emitComponent(fm, htmlExpr, meta) {
45459
45556
  let param = "";
45460
45557
  if (fm.propsType && meta.usesChildren) param = `props: ${fm.propsType} & { children?: any }`;
@@ -45578,16 +45675,8 @@ var import_typescript6 = __toESM(require("typescript"), 1);
45578
45675
 
45579
45676
  // ../stator/src/compiler/virtual-code.ts
45580
45677
  var import_typescript7 = __toESM(require("typescript"), 1);
45581
- var TEMPLATE_GLOBALS = ["read", "each", "when", "match", "defer", "on", "classList", "styleList"];
45582
- var CLIENT_GLOBALS = [
45583
- "StatorElement",
45584
- "use",
45585
- "machine",
45586
- "defineElement",
45587
- "bind",
45588
- "effect",
45589
- "dispatch"
45590
- ];
45678
+ var TEMPLATE_GLOBALS = TEMPLATE_AUTHOR_GLOBALS;
45679
+ var CLIENT_GLOBALS = CLIENT_AUTHOR_GLOBALS;
45591
45680
  function userImportedLocals(code, modulePath) {
45592
45681
  const locals = /* @__PURE__ */ new Set();
45593
45682
  const re = new RegExp(
@@ -45789,6 +45878,25 @@ function buildClientTsx(regions) {
45789
45878
  const mappings = [];
45790
45879
  const userScript = regions.scripts.map((s) => s.content).join("\n");
45791
45880
  let code = injectImports(TEMPLATE_GLOBALS, "@statorjs/stator/template", userScript) + injectImports(CLIENT_GLOBALS, "@statorjs/stator/client", userScript) + AMBIENT_TYPE_IMPORTS + STATOR_AMBIENT;
45881
+ const fm = regions.frontmatter?.content ?? "";
45882
+ if (fm.trim()) {
45883
+ const fmOffset2 = regions.frontmatter?.contentOffset ?? 0;
45884
+ const { hoisted, body } = splitFrontmatter(fm, fmOffset2);
45885
+ for (const seg of hoisted) {
45886
+ if (userScript.includes(seg.text.trim())) continue;
45887
+ push(mappings, seg.sourceOffset, code.length, seg.text.length);
45888
+ code += `${seg.text}
45889
+ `;
45890
+ }
45891
+ code += "function __statorFence(props: Record<string, unknown>) {\n";
45892
+ for (const seg of body) {
45893
+ code += " ";
45894
+ push(mappings, seg.sourceOffset, code.length, seg.text.length);
45895
+ code += `${seg.text}
45896
+ `;
45897
+ }
45898
+ code += "}\nvoid __statorFence\n";
45899
+ }
45792
45900
  for (const script of regions.scripts) {
45793
45901
  push(mappings, script.contentOffset, code.length, script.content.length);
45794
45902
  code += `${script.content}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statorjs/language-server",
3
- "version": "0.1.20",
3
+ "version": "0.1.21",
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": "2.0.2"
36
+ "@statorjs/stator": "2.1.0"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "typescript": "^5.6.0"