tutuca 0.9.100 → 0.9.102

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.
@@ -3447,9 +3447,10 @@ function resolveComponentName(value, components) {
3447
3447
  }
3448
3448
 
3449
3449
  class ExampleSection {
3450
- constructor({ title, description = null, items = [] }) {
3450
+ constructor({ title, description = null, group = "", items = [] }) {
3451
3451
  this.title = title;
3452
3452
  this.description = description;
3453
+ this.group = group;
3453
3454
  this.items = items;
3454
3455
  }
3455
3456
  }
@@ -3500,9 +3501,13 @@ function parseSection(raw, components, where) {
3500
3501
  if (items.length === 0) {
3501
3502
  throw shapeError(`section at ${where} has no items`, where);
3502
3503
  }
3504
+ if (raw.group != null && typeof raw.group !== "string") {
3505
+ throw shapeError(`section at ${where} has a non-string "group" (got ${typeof raw.group}); ` + `group must be a string naming the sidebar group, or be omitted`, where);
3506
+ }
3503
3507
  return new ExampleSection({
3504
3508
  title: raw.title ?? "Examples",
3505
3509
  description: raw.description ?? null,
3510
+ group: raw.group ?? "",
3506
3511
  items
3507
3512
  });
3508
3513
  }
@@ -3547,6 +3552,34 @@ function normalizeModule(mod, { path = null } = {}) {
3547
3552
  present
3548
3553
  };
3549
3554
  }
3555
+ function findComponentNameConflicts(entries) {
3556
+ const byName = new Map;
3557
+ for (const { path, components } of entries) {
3558
+ for (const comp of components ?? []) {
3559
+ if (!comp || typeof comp.name !== "string")
3560
+ continue;
3561
+ let objs = byName.get(comp.name);
3562
+ if (!objs)
3563
+ byName.set(comp.name, objs = new Map);
3564
+ let paths = objs.get(comp);
3565
+ if (!paths)
3566
+ objs.set(comp, paths = new Set);
3567
+ if (path != null)
3568
+ paths.add(path);
3569
+ }
3570
+ }
3571
+ const conflicts = [];
3572
+ for (const [name, objs] of byName) {
3573
+ if (objs.size < 2)
3574
+ continue;
3575
+ const paths = new Set;
3576
+ for (const set2 of objs.values())
3577
+ for (const p of set2)
3578
+ paths.add(p);
3579
+ conflicts.push({ name, paths: [...paths].sort() });
3580
+ }
3581
+ return conflicts.sort((a, b) => a.name.localeCompare(b.name));
3582
+ }
3550
3583
  var EXAMPLES_SHAPE_MISMATCH = "EXAMPLES_SHAPE_MISMATCH";
3551
3584
 
3552
3585
  // tools/core/results.js
@@ -9783,13 +9816,16 @@ class Renderer {
9783
9816
  if (cachedNode)
9784
9817
  return cachedNode;
9785
9818
  const view = viewName ? comp.getView(viewName) : stack.lookupBestView(comp.views, "main");
9819
+ const body = this.renderView(view, stack);
9820
+ if (body == null)
9821
+ return null;
9786
9822
  const meta = this._renderMetadata({
9787
9823
  $: "Comp",
9788
9824
  nid: node?.nodeId ?? null,
9789
9825
  cid: comp.id,
9790
9826
  vid: view.name
9791
9827
  });
9792
- const dom = new VFragment([meta, this.renderView(view, stack)]);
9828
+ const dom = new VFragment([meta, body]);
9793
9829
  this.cache.set(cachePath, cacheKey, dom);
9794
9830
  return dom;
9795
9831
  }
@@ -9802,7 +9838,8 @@ class Renderer {
9802
9838
  const { iterData, start, end, keys } = unpackLoopResult(loopWith.call(stack.it, seq, makeLoopCtx(stack, filter)), seq);
9803
9839
  const renderOne = (key, value, attrName) => {
9804
9840
  const dom = this.renderIt(stack.enter(value, { key }, true), node, key, viewName);
9805
- this.pushEachEntry(r, node.nodeId, attrName, key, dom);
9841
+ if (dom != null)
9842
+ this.pushEachEntry(r, node.nodeId, attrName, key, dom);
9806
9843
  };
9807
9844
  if (keys)
9808
9845
  imKeysIter(seq, renderOne, keys);
@@ -9829,7 +9866,8 @@ class Renderer {
9829
9866
  this.pushEachEntry(r, nid, attrName, key, cachedNode);
9830
9867
  else {
9831
9868
  const dom = this.renderView(view, stack.enter(value, binds, false));
9832
- this.pushEachEntry(r, nid, attrName, key, dom);
9869
+ if (dom != null)
9870
+ this.pushEachEntry(r, nid, attrName, key, dom);
9833
9871
  this.cache.set(cachePath, cacheKey, dom);
9834
9872
  }
9835
9873
  };
@@ -9958,7 +9996,8 @@ function parseXOp(attrs, childs, opIdx, px) {
9958
9996
  if (attrs.length <= opIdx)
9959
9997
  return maybeFragment(childs);
9960
9998
  const { name, value } = attrs[opIdx];
9961
- const as = attrs.getNamedItem("as")?.value ?? null;
9999
+ const asAttr = attrs.getNamedItem("as")?.value ?? null;
10000
+ const as = asAttr === null ? null : parseViewName(asAttr, px);
9962
10001
  let node;
9963
10002
  switch (name) {
9964
10003
  case "slot":
@@ -9998,6 +10037,9 @@ function parseXOpVal(opName, value, px, parserFn) {
9998
10037
  px.onParseIssue("bad-value", { role: "x-op", op: opName, value });
9999
10038
  return val;
10000
10039
  }
10040
+ function parseViewName(s, px) {
10041
+ return vp.parseText(s, px) ?? vp.const(s);
10042
+ }
10001
10043
  function processXExtras(node, attrs, opName, startIdx, px) {
10002
10044
  const { consumed, wrappable } = X_OPS[opName];
10003
10045
  const wrappers = [];
@@ -10427,16 +10469,19 @@ var init_anode = __esm(() => {
10427
10469
  }
10428
10470
  };
10429
10471
  RenderViewId = class RenderViewId extends ANode {
10430
- constructor(nodeId, val, viewId) {
10472
+ constructor(nodeId, val, viewVal) {
10431
10473
  super(nodeId, val);
10432
- this.viewId = viewId;
10474
+ this.viewVal = viewVal;
10475
+ }
10476
+ evalViewName(stack) {
10477
+ return this.viewVal ? this.viewVal.eval(stack) : null;
10433
10478
  }
10434
10479
  setDataAttr(_key, _val) {}
10435
10480
  };
10436
10481
  RenderNode = class RenderNode extends RenderViewId {
10437
10482
  render(stack, rx) {
10438
10483
  const newStack = stack.enter(this.val.eval(stack), {}, true);
10439
- return rx.renderIt(newStack, this, "", this.viewId);
10484
+ return rx.renderIt(newStack, this, "", this.evalViewName(stack));
10440
10485
  }
10441
10486
  toPathStep(ctx) {
10442
10487
  if (this.val instanceof DynVal)
@@ -10447,7 +10492,7 @@ var init_anode = __esm(() => {
10447
10492
  RenderItNode = class RenderItNode extends RenderViewId {
10448
10493
  render(stack, rx) {
10449
10494
  const newStack = stack.enter(stack.it, {}, true);
10450
- return rx.renderIt(newStack, this, "", this.viewId);
10495
+ return rx.renderIt(newStack, this, "", this.evalViewName(stack));
10451
10496
  }
10452
10497
  toPathStep(ctx) {
10453
10498
  const next = ctx.next();
@@ -10463,12 +10508,12 @@ var init_anode = __esm(() => {
10463
10508
  }
10464
10509
  };
10465
10510
  RenderEachNode = class RenderEachNode extends RenderViewId {
10466
- constructor(nodeId, val, viewId) {
10467
- super(nodeId, val, viewId);
10511
+ constructor(nodeId, val, viewVal) {
10512
+ super(nodeId, val, viewVal);
10468
10513
  this.iterInfo = new IterInfo(val, null, null, null);
10469
10514
  }
10470
10515
  render(stack, rx) {
10471
- return rx.renderEach(stack, this.iterInfo, this, this.viewId);
10516
+ return rx.renderEach(stack, this.iterInfo, this, this.evalViewName(stack));
10472
10517
  }
10473
10518
  toPathStep(ctx) {
10474
10519
  if (this.val instanceof DynVal)
@@ -13003,6 +13048,7 @@ function checkComponent(Comp, lx = new LintContext, { wellKnownExtras = EMPTY_SE
13003
13048
  return lx.push({ componentName: Comp.name }, () => {
13004
13049
  checkUnknownSpecKeys(lx, Comp, wellKnownExtras);
13005
13050
  checkFieldDeclarations(lx, Comp);
13051
+ checkRecordFieldNameCollisions(lx, Comp);
13006
13052
  checkProvidesAreAddressable(lx, Comp);
13007
13053
  checkLookupShapes(lx, Comp);
13008
13054
  checkHandlersNotAsync(lx, Comp);
@@ -13393,6 +13439,16 @@ function checkFieldDeclarations(lx, Comp) {
13393
13439
  }
13394
13440
  }
13395
13441
  }
13442
+ function checkRecordFieldNameCollisions(lx, Comp) {
13443
+ const fields = Comp.Class?.getMetaClass?.().fields;
13444
+ if (!fields)
13445
+ return;
13446
+ for (const name in fields) {
13447
+ if (RECORD_FIELD_NAME_COLLISIONS.has(name)) {
13448
+ lx.error(FIELD_NAME_RESERVED_BY_RECORD, { name });
13449
+ }
13450
+ }
13451
+ }
13396
13452
  function checkUnreferencedAlterHandlers(lx, Comp, referencedAlters) {
13397
13453
  for (const name in Comp.alter) {
13398
13454
  if (!referencedAlters.has(name)) {
@@ -13554,7 +13610,7 @@ class LintContext {
13554
13610
  this.reports.push({ id, info, level, context: { ...this.frame }, suggestion });
13555
13611
  }
13556
13612
  }
13557
- var KNOWN_COMPONENT_SPEC_KEYS, EMPTY_SET2, FRAMEWORK_WELL_KNOWN_EXTRAS, KNOWN_DIRECTIVE_NAMES, ALT_HANDLER_NOT_DEFINED = "ALT_HANDLER_NOT_DEFINED", ALT_HANDLER_NOT_REFERENCED = "ALT_HANDLER_NOT_REFERENCED", DYN_VAL_NOT_DEFINED = "DYN_VAL_NOT_DEFINED", DYN_ALIAS_NOT_REFERENCED = "DYN_ALIAS_NOT_REFERENCED", PROVIDE_NOT_ADDRESSABLE = "PROVIDE_NOT_ADDRESSABLE", LOOKUP_BAD_SHAPE = "LOOKUP_BAD_SHAPE", LOOKUP_TARGET_MALFORMED = "LOOKUP_TARGET_MALFORMED", RENDER_IT_OUTSIDE_OF_LOOP = "RENDER_IT_OUTSIDE_OF_LOOP", UNKNOWN_EVENT_MODIFIER = "UNKNOWN_EVENT_MODIFIER", UNKNOWN_HANDLER_ARG_NAME = "UNKNOWN_HANDLER_ARG_NAME", INPUT_HANDLER_NOT_IMPLEMENTED = "INPUT_HANDLER_NOT_IMPLEMENTED", INPUT_HANDLER_NOT_REFERENCED = "INPUT_HANDLER_NOT_REFERENCED", INPUT_HANDLER_METHOD_NOT_IMPLEMENTED = "INPUT_HANDLER_METHOD_NOT_IMPLEMENTED", INPUT_HANDLER_FOR_INPUT_HANDLER_METHOD = "INPUT_HANDLER_FOR_INPUT_HANDLER_METHOD", INPUT_HANDLER_METHOD_FOR_INPUT_HANDLER = "INPUT_HANDLER_METHOD_FOR_INPUT_HANDLER", FIELD_VAL_NOT_DEFINED = "FIELD_VAL_NOT_DEFINED", FIELD_VAL_IS_METHOD = "FIELD_VAL_IS_METHOD", METHOD_VAL_NOT_DEFINED = "METHOD_VAL_NOT_DEFINED", METHOD_VAL_IS_FIELD = "METHOD_VAL_IS_FIELD", DUPLICATE_ATTR_DEFINITION = "DUPLICATE_ATTR_DEFINITION", IF_NO_BRANCH_SET = "IF_NO_BRANCH_SET", UNKNOWN_REQUEST_NAME = "UNKNOWN_REQUEST_NAME", UNKNOWN_COMPONENT_NAME = "UNKNOWN_COMPONENT_NAME", UNKNOWN_MACRO_ARG = "UNKNOWN_MACRO_ARG", UNKNOWN_DIRECTIVE = "UNKNOWN_DIRECTIVE", UNKNOWN_X_OP = "UNKNOWN_X_OP", UNKNOWN_X_ATTR = "UNKNOWN_X_ATTR", MAYBE_DROP_AT_PREFIX = "MAYBE_DROP_AT_PREFIX", MAYBE_ADD_AT_PREFIX = "MAYBE_ADD_AT_PREFIX", BAD_VALUE = "BAD_VALUE", UNSUPPORTED_EXPR_SYNTAX = "UNSUPPORTED_EXPR_SYNTAX", REDUNDANT_TEMPLATE_STRING = "REDUNDANT_TEMPLATE_STRING", PLACEHOLDERLESS_TEMPLATE_STRING = "PLACEHOLDERLESS_TEMPLATE_STRING", UNKNOWN_COMPONENT_SPEC_KEY = "UNKNOWN_COMPONENT_SPEC_KEY", COMP_FIELD_BAD_SHAPE = "COMP_FIELD_BAD_SHAPE", ASYNC_HANDLER = "ASYNC_HANDLER", TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE = "TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE", GLOBAL_SELECTOR_IN_SCOPED_STYLE = "GLOBAL_SELECTOR_IN_SCOPED_STYLE", X_KNOWN_OP_NAMES, X_KNOWN_ATTR_NAMES, HOST_DIRECTIVE_ONLY_NAMES, LEVEL_WARN2 = "warn", LEVEL_ERROR2 = "error", LEVEL_HINT = "hint", PARSE_ISSUES, UNSUPPORTED_EXPR_GUIDANCE, HTML_LINT_OPTS, NO_WRAPPERS, KNOWN_HANDLER_NAMES, fixTo = (from, to) => ({ kind: "rewrite", from, to }), ATTR_VAL_CHECKERS, NODE_KIND_TO_CTX, HANDLER_CHANNELS, ASYNC_HANDLER_HELP, NON_NESTABLE_AT_RULE, GLOBAL_LEADING_SELECTOR, IGNORE_DIRECTIVE, blankRun = (m) => m.replace(/[^\n]/g, " "), STYLE_TO_GLOBAL_HELP, KNOWN_LOOKUP_KEYS, LintParseContext;
13613
+ var KNOWN_COMPONENT_SPEC_KEYS, EMPTY_SET2, FRAMEWORK_WELL_KNOWN_EXTRAS, KNOWN_DIRECTIVE_NAMES, ALT_HANDLER_NOT_DEFINED = "ALT_HANDLER_NOT_DEFINED", ALT_HANDLER_NOT_REFERENCED = "ALT_HANDLER_NOT_REFERENCED", DYN_VAL_NOT_DEFINED = "DYN_VAL_NOT_DEFINED", DYN_ALIAS_NOT_REFERENCED = "DYN_ALIAS_NOT_REFERENCED", PROVIDE_NOT_ADDRESSABLE = "PROVIDE_NOT_ADDRESSABLE", LOOKUP_BAD_SHAPE = "LOOKUP_BAD_SHAPE", LOOKUP_TARGET_MALFORMED = "LOOKUP_TARGET_MALFORMED", RENDER_IT_OUTSIDE_OF_LOOP = "RENDER_IT_OUTSIDE_OF_LOOP", UNKNOWN_EVENT_MODIFIER = "UNKNOWN_EVENT_MODIFIER", UNKNOWN_HANDLER_ARG_NAME = "UNKNOWN_HANDLER_ARG_NAME", INPUT_HANDLER_NOT_IMPLEMENTED = "INPUT_HANDLER_NOT_IMPLEMENTED", INPUT_HANDLER_NOT_REFERENCED = "INPUT_HANDLER_NOT_REFERENCED", INPUT_HANDLER_METHOD_NOT_IMPLEMENTED = "INPUT_HANDLER_METHOD_NOT_IMPLEMENTED", INPUT_HANDLER_FOR_INPUT_HANDLER_METHOD = "INPUT_HANDLER_FOR_INPUT_HANDLER_METHOD", INPUT_HANDLER_METHOD_FOR_INPUT_HANDLER = "INPUT_HANDLER_METHOD_FOR_INPUT_HANDLER", FIELD_VAL_NOT_DEFINED = "FIELD_VAL_NOT_DEFINED", FIELD_VAL_IS_METHOD = "FIELD_VAL_IS_METHOD", METHOD_VAL_NOT_DEFINED = "METHOD_VAL_NOT_DEFINED", METHOD_VAL_IS_FIELD = "METHOD_VAL_IS_FIELD", DUPLICATE_ATTR_DEFINITION = "DUPLICATE_ATTR_DEFINITION", IF_NO_BRANCH_SET = "IF_NO_BRANCH_SET", UNKNOWN_REQUEST_NAME = "UNKNOWN_REQUEST_NAME", UNKNOWN_COMPONENT_NAME = "UNKNOWN_COMPONENT_NAME", UNKNOWN_MACRO_ARG = "UNKNOWN_MACRO_ARG", UNKNOWN_DIRECTIVE = "UNKNOWN_DIRECTIVE", UNKNOWN_X_OP = "UNKNOWN_X_OP", UNKNOWN_X_ATTR = "UNKNOWN_X_ATTR", MAYBE_DROP_AT_PREFIX = "MAYBE_DROP_AT_PREFIX", MAYBE_ADD_AT_PREFIX = "MAYBE_ADD_AT_PREFIX", BAD_VALUE = "BAD_VALUE", UNSUPPORTED_EXPR_SYNTAX = "UNSUPPORTED_EXPR_SYNTAX", REDUNDANT_TEMPLATE_STRING = "REDUNDANT_TEMPLATE_STRING", PLACEHOLDERLESS_TEMPLATE_STRING = "PLACEHOLDERLESS_TEMPLATE_STRING", UNKNOWN_COMPONENT_SPEC_KEY = "UNKNOWN_COMPONENT_SPEC_KEY", COMP_FIELD_BAD_SHAPE = "COMP_FIELD_BAD_SHAPE", ASYNC_HANDLER = "ASYNC_HANDLER", TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE = "TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE", GLOBAL_SELECTOR_IN_SCOPED_STYLE = "GLOBAL_SELECTOR_IN_SCOPED_STYLE", FIELD_NAME_RESERVED_BY_RECORD = "FIELD_NAME_RESERVED_BY_RECORD", RECORD_FIELD_NAME_COLLISIONS, X_KNOWN_OP_NAMES, X_KNOWN_ATTR_NAMES, HOST_DIRECTIVE_ONLY_NAMES, LEVEL_WARN2 = "warn", LEVEL_ERROR2 = "error", LEVEL_HINT = "hint", PARSE_ISSUES, UNSUPPORTED_EXPR_GUIDANCE, HTML_LINT_OPTS, NO_WRAPPERS, KNOWN_HANDLER_NAMES, fixTo = (from, to) => ({ kind: "rewrite", from, to }), ATTR_VAL_CHECKERS, NODE_KIND_TO_CTX, HANDLER_CHANNELS, ASYNC_HANDLER_HELP, NON_NESTABLE_AT_RULE, GLOBAL_LEADING_SELECTOR, IGNORE_DIRECTIVE, blankRun = (m) => m.replace(/[^\n]/g, " "), STYLE_TO_GLOBAL_HELP, KNOWN_LOOKUP_KEYS, LintParseContext;
13558
13614
  var init_lint_check = __esm(() => {
13559
13615
  init_anode();
13560
13616
  init_htmllinter();
@@ -13575,6 +13631,7 @@ var init_lint_check = __esm(() => {
13575
13631
  "then",
13576
13632
  "else"
13577
13633
  ]);
13634
+ RECORD_FIELD_NAME_COLLISIONS = new Set(["entries", "hashCode"]);
13578
13635
  X_KNOWN_OP_NAMES = new Set([
13579
13636
  "slot",
13580
13637
  "text",
@@ -15408,6 +15465,12 @@ var init_lint_rules = __esm(() => {
15408
15465
  level: "error",
15409
15466
  group: "Component field declarations",
15410
15467
  summary: "`fields: { x: { component, args } }` shape is wrong: `component` must be a string and `args` must be a plain object."
15468
+ },
15469
+ {
15470
+ code: FIELD_NAME_RESERVED_BY_RECORD,
15471
+ level: "error",
15472
+ group: "Component field declarations",
15473
+ summary: "Field name matches an Immutable Record API member, so `.field` reads the API member instead of the value."
15411
15474
  }
15412
15475
  ];
15413
15476
  });
@@ -15501,6 +15564,8 @@ function lintIdToMessage(id, info) {
15501
15564
  return `'$${info.name}' is a method reference, but '${info.name}' is defined as an input handler${fmtEventSuffix(info)}`;
15502
15565
  case "INPUT_HANDLER_METHOD_FOR_INPUT_HANDLER":
15503
15566
  return `'${info.name}' is an input handler reference, but '${info.name}' is defined as a method${fmtEventSuffix(info)}`;
15567
+ case "FIELD_NAME_RESERVED_BY_RECORD":
15568
+ return `Field '${info.name}' collides with the Immutable Record API: '.${info.name}' reads the Record member, not your value — rename the field (its value is only reachable via .get('${info.name}'))`;
15504
15569
  case "FIELD_VAL_NOT_DEFINED":
15505
15570
  return `Field '.${info.name}' is not defined${fmtOriginSuffix(info)}`;
15506
15571
  case "FIELD_VAL_IS_METHOD":
@@ -16205,6 +16270,23 @@ async function runDevTests(projectDir, devModuleUrls) {
16205
16270
  }
16206
16271
  return { totalTests, failedTests, withTests, failures, importErrors };
16207
16272
  }
16273
+ async function checkComponentNameConflicts(projectDir, devModuleUrls) {
16274
+ await createNodeEnv();
16275
+ const entries = [];
16276
+ for (const url of devModuleUrls) {
16277
+ const abs = resolve5(projectDir, url.slice(1));
16278
+ try {
16279
+ const mod = await import(abs);
16280
+ const components = typeof mod.getComponents === "function" ? mod.getComponents() : [];
16281
+ entries.push({ path: url, components });
16282
+ } catch {}
16283
+ }
16284
+ return findComponentNameConflicts(entries);
16285
+ }
16286
+ function formatComponentNameConflict(c) {
16287
+ return ` ! component name conflict: "${c.name}" is defined by different component objects in ${c.paths.length} modules (${c.paths.join(", ")}). They share a name but are distinct objects, so the storybook registers one and the other renders uncompiled (throws on ` + `render). Make those modules import the same definition — e.g. all from "tutuca/components" ` + `rather than a relative path.
16288
+ `;
16289
+ }
16208
16290
  async function discoverModules(projectDir, devModuleUrls) {
16209
16291
  await createNodeEnv();
16210
16292
  const modules = [];
@@ -16346,6 +16428,7 @@ async function run4(argv, opts = {}) {
16346
16428
  const imports2 = buildImports(base2, { margaui });
16347
16429
  const modules = await discoverModules(projectDir, devModuleUrls);
16348
16430
  const tests = parsed.values["no-tests"] ? null : await runDevTests(projectDir, devModuleUrls);
16431
+ const componentNameConflicts = await checkComponentNameConflicts(projectDir, devModuleUrls);
16349
16432
  const source = tutucaSource(base2);
16350
16433
  const result = {
16351
16434
  projectDir,
@@ -16353,7 +16436,8 @@ async function run4(argv, opts = {}) {
16353
16436
  options: { margaui, check, noCache, runTests: !parsed.values["no-tests"] },
16354
16437
  imports: imports2,
16355
16438
  modules,
16356
- tests
16439
+ tests,
16440
+ componentNameConflicts
16357
16441
  };
16358
16442
  if (opts.format === "json") {
16359
16443
  process.stdout.write(`${JSON.stringify(result, null, 2)}
@@ -16396,6 +16480,9 @@ async function run4(argv, opts = {}) {
16396
16480
  process.stdout.write(` tests: skipped (--no-tests)
16397
16481
  `);
16398
16482
  }
16483
+ for (const c of componentNameConflicts) {
16484
+ process.stdout.write(formatComponentNameConflict(c));
16485
+ }
16399
16486
  return;
16400
16487
  }
16401
16488
  if (!parsed.values["no-tests"]) {
@@ -16416,6 +16503,9 @@ async function run4(argv, opts = {}) {
16416
16503
  }
16417
16504
  }
16418
16505
  }
16506
+ for (const c of await checkComponentNameConflicts(projectDir, devModuleUrls)) {
16507
+ process.stdout.write(formatComponentNameConflict(c));
16508
+ }
16419
16509
  const { base, serveDist } = resolveTutucaBase(projectDir, self, false);
16420
16510
  const imports = buildImports(base, { margaui });
16421
16511
  const indexHtml = renderIndexHtml(imports, { margaui, bootstrapUrl: BOOTSTRAP_URL });
@@ -1825,13 +1825,16 @@ class Renderer {
1825
1825
  if (cachedNode)
1826
1826
  return cachedNode;
1827
1827
  const view = viewName ? comp.getView(viewName) : stack.lookupBestView(comp.views, "main");
1828
+ const body = this.renderView(view, stack);
1829
+ if (body == null)
1830
+ return null;
1828
1831
  const meta = this._renderMetadata({
1829
1832
  $: "Comp",
1830
1833
  nid: node?.nodeId ?? null,
1831
1834
  cid: comp.id,
1832
1835
  vid: view.name
1833
1836
  });
1834
- const dom = new VFragment([meta, this.renderView(view, stack)]);
1837
+ const dom = new VFragment([meta, body]);
1835
1838
  this.cache.set(cachePath, cacheKey, dom);
1836
1839
  return dom;
1837
1840
  }
@@ -1844,7 +1847,8 @@ class Renderer {
1844
1847
  const { iterData, start, end, keys } = unpackLoopResult(loopWith.call(stack.it, seq, makeLoopCtx(stack, filter)), seq);
1845
1848
  const renderOne = (key, value, attrName) => {
1846
1849
  const dom = this.renderIt(stack.enter(value, { key }, true), node, key, viewName);
1847
- this.pushEachEntry(r, node.nodeId, attrName, key, dom);
1850
+ if (dom != null)
1851
+ this.pushEachEntry(r, node.nodeId, attrName, key, dom);
1848
1852
  };
1849
1853
  if (keys)
1850
1854
  imKeysIter(seq, renderOne, keys);
@@ -1871,7 +1875,8 @@ class Renderer {
1871
1875
  this.pushEachEntry(r, nid, attrName, key, cachedNode);
1872
1876
  else {
1873
1877
  const dom = this.renderView(view, stack.enter(value, binds, false));
1874
- this.pushEachEntry(r, nid, attrName, key, dom);
1878
+ if (dom != null)
1879
+ this.pushEachEntry(r, nid, attrName, key, dom);
1875
1880
  this.cache.set(cachePath, cacheKey, dom);
1876
1881
  }
1877
1882
  };
@@ -2156,7 +2161,8 @@ function parseXOp(attrs, childs, opIdx, px) {
2156
2161
  if (attrs.length <= opIdx)
2157
2162
  return maybeFragment(childs);
2158
2163
  const { name, value } = attrs[opIdx];
2159
- const as = attrs.getNamedItem("as")?.value ?? null;
2164
+ const asAttr = attrs.getNamedItem("as")?.value ?? null;
2165
+ const as = asAttr === null ? null : parseViewName(asAttr, px);
2160
2166
  let node;
2161
2167
  switch (name) {
2162
2168
  case "slot":
@@ -2196,6 +2202,9 @@ function parseXOpVal(opName, value, px, parserFn) {
2196
2202
  px.onParseIssue("bad-value", { role: "x-op", op: opName, value });
2197
2203
  return val;
2198
2204
  }
2205
+ function parseViewName(s, px) {
2206
+ return vp.parseText(s, px) ?? vp.const(s);
2207
+ }
2199
2208
  function processXExtras(node, attrs, opName, startIdx, px) {
2200
2209
  const { consumed, wrappable } = X_OPS[opName];
2201
2210
  const wrappers = [];
@@ -2292,9 +2301,12 @@ class Macro {
2292
2301
  }
2293
2302
 
2294
2303
  class RenderViewId extends ANode {
2295
- constructor(nodeId, val, viewId) {
2304
+ constructor(nodeId, val, viewVal) {
2296
2305
  super(nodeId, val);
2297
- this.viewId = viewId;
2306
+ this.viewVal = viewVal;
2307
+ }
2308
+ evalViewName(stack) {
2309
+ return this.viewVal ? this.viewVal.eval(stack) : null;
2298
2310
  }
2299
2311
  setDataAttr(_key, _val) {}
2300
2312
  }
@@ -2308,7 +2320,7 @@ function dynRenderStep(comp, name, key) {
2308
2320
  class RenderNode extends RenderViewId {
2309
2321
  render(stack, rx) {
2310
2322
  const newStack = stack.enter(this.val.eval(stack), {}, true);
2311
- return rx.renderIt(newStack, this, "", this.viewId);
2323
+ return rx.renderIt(newStack, this, "", this.evalViewName(stack));
2312
2324
  }
2313
2325
  toPathStep(ctx) {
2314
2326
  if (this.val instanceof DynVal)
@@ -2320,7 +2332,7 @@ class RenderNode extends RenderViewId {
2320
2332
  class RenderItNode extends RenderViewId {
2321
2333
  render(stack, rx) {
2322
2334
  const newStack = stack.enter(stack.it, {}, true);
2323
- return rx.renderIt(newStack, this, "", this.viewId);
2335
+ return rx.renderIt(newStack, this, "", this.evalViewName(stack));
2324
2336
  }
2325
2337
  toPathStep(ctx) {
2326
2338
  const next = ctx.next();
@@ -2337,12 +2349,12 @@ class RenderItNode extends RenderViewId {
2337
2349
  }
2338
2350
 
2339
2351
  class RenderEachNode extends RenderViewId {
2340
- constructor(nodeId, val, viewId) {
2341
- super(nodeId, val, viewId);
2352
+ constructor(nodeId, val, viewVal) {
2353
+ super(nodeId, val, viewVal);
2342
2354
  this.iterInfo = new IterInfo(val, null, null, null);
2343
2355
  }
2344
2356
  render(stack, rx) {
2345
- return rx.renderEach(stack, this.iterInfo, this, this.viewId);
2357
+ return rx.renderEach(stack, this.iterInfo, this, this.evalViewName(stack));
2346
2358
  }
2347
2359
  toPathStep(ctx) {
2348
2360
  if (this.val instanceof DynVal)
@@ -2628,6 +2640,9 @@ class View {
2628
2640
  this.anode = optimizeNode(this.anode);
2629
2641
  }
2630
2642
  render(stack, rx) {
2643
+ if (this.anode === null) {
2644
+ throw new Error(`tutuca: view "${this.name}" was rendered before it was compiled — its ` + `component is not registered in this app/scope. Source: ` + `${String(this.rawView).slice(0, 80).replace(/\s+/g, " ")}…`);
2645
+ }
2631
2646
  return this.anode.render(stack, rx);
2632
2647
  }
2633
2648
  }
@@ -5081,6 +5096,8 @@ var COMP_FIELD_BAD_SHAPE = "COMP_FIELD_BAD_SHAPE";
5081
5096
  var ASYNC_HANDLER = "ASYNC_HANDLER";
5082
5097
  var TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE = "TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE";
5083
5098
  var GLOBAL_SELECTOR_IN_SCOPED_STYLE = "GLOBAL_SELECTOR_IN_SCOPED_STYLE";
5099
+ var FIELD_NAME_RESERVED_BY_RECORD = "FIELD_NAME_RESERVED_BY_RECORD";
5100
+ var RECORD_FIELD_NAME_COLLISIONS = new Set(["entries", "hashCode"]);
5084
5101
  var X_KNOWN_OP_NAMES = new Set([
5085
5102
  "slot",
5086
5103
  "text",
@@ -5169,6 +5186,7 @@ function checkComponent(Comp, lx = new LintContext, { wellKnownExtras = EMPTY_SE
5169
5186
  return lx.push({ componentName: Comp.name }, () => {
5170
5187
  checkUnknownSpecKeys(lx, Comp, wellKnownExtras);
5171
5188
  checkFieldDeclarations(lx, Comp);
5189
+ checkRecordFieldNameCollisions(lx, Comp);
5172
5190
  checkProvidesAreAddressable(lx, Comp);
5173
5191
  checkLookupShapes(lx, Comp);
5174
5192
  checkHandlersNotAsync(lx, Comp);
@@ -5661,6 +5679,16 @@ function checkFieldDeclarations(lx, Comp) {
5661
5679
  }
5662
5680
  }
5663
5681
  }
5682
+ function checkRecordFieldNameCollisions(lx, Comp) {
5683
+ const fields = Comp.Class?.getMetaClass?.().fields;
5684
+ if (!fields)
5685
+ return;
5686
+ for (const name in fields) {
5687
+ if (RECORD_FIELD_NAME_COLLISIONS.has(name)) {
5688
+ lx.error(FIELD_NAME_RESERVED_BY_RECORD, { name });
5689
+ }
5690
+ }
5691
+ }
5664
5692
  function checkUnreferencedAlterHandlers(lx, Comp, referencedAlters) {
5665
5693
  for (const name in Comp.alter) {
5666
5694
  if (!referencedAlters.has(name)) {
@@ -7249,6 +7277,8 @@ function lintIdToMessage(id, info) {
7249
7277
  return `'$${info.name}' is a method reference, but '${info.name}' is defined as an input handler${fmtEventSuffix(info)}`;
7250
7278
  case "INPUT_HANDLER_METHOD_FOR_INPUT_HANDLER":
7251
7279
  return `'${info.name}' is an input handler reference, but '${info.name}' is defined as a method${fmtEventSuffix(info)}`;
7280
+ case "FIELD_NAME_RESERVED_BY_RECORD":
7281
+ return `Field '${info.name}' collides with the Immutable Record API: '.${info.name}' reads the Record member, not your value — rename the field (its value is only reachable via .get('${info.name}'))`;
7252
7282
  case "FIELD_VAL_NOT_DEFINED":
7253
7283
  return `Field '.${info.name}' is not defined${fmtOriginSuffix(info)}`;
7254
7284
  case "FIELD_VAL_IS_METHOD":
@@ -8562,6 +8592,7 @@ export {
8562
8592
  GLOBAL_SELECTOR_IN_SCOPED_STYLE,
8563
8593
  FIELD_VAL_NOT_DEFINED,
8564
8594
  FIELD_VAL_IS_METHOD,
8595
+ FIELD_NAME_RESERVED_BY_RECORD,
8565
8596
  FIELD_CLASS,
8566
8597
  ExampleIndex,
8567
8598
  DescribeResult,
@@ -9476,13 +9476,16 @@ class Renderer {
9476
9476
  if (cachedNode)
9477
9477
  return cachedNode;
9478
9478
  const view = viewName ? comp.getView(viewName) : stack.lookupBestView(comp.views, "main");
9479
+ const body = this.renderView(view, stack);
9480
+ if (body == null)
9481
+ return null;
9479
9482
  const meta = this._renderMetadata({
9480
9483
  $: "Comp",
9481
9484
  nid: node?.nodeId ?? null,
9482
9485
  cid: comp.id,
9483
9486
  vid: view.name
9484
9487
  });
9485
- const dom = new VFragment([meta, this.renderView(view, stack)]);
9488
+ const dom = new VFragment([meta, body]);
9486
9489
  this.cache.set(cachePath, cacheKey, dom);
9487
9490
  return dom;
9488
9491
  }
@@ -9495,7 +9498,8 @@ class Renderer {
9495
9498
  const { iterData, start, end, keys } = unpackLoopResult(loopWith.call(stack.it, seq, makeLoopCtx(stack, filter)), seq);
9496
9499
  const renderOne = (key, value, attrName) => {
9497
9500
  const dom = this.renderIt(stack.enter(value, { key }, true), node, key, viewName);
9498
- this.pushEachEntry(r, node.nodeId, attrName, key, dom);
9501
+ if (dom != null)
9502
+ this.pushEachEntry(r, node.nodeId, attrName, key, dom);
9499
9503
  };
9500
9504
  if (keys)
9501
9505
  imKeysIter(seq, renderOne, keys);
@@ -9522,7 +9526,8 @@ class Renderer {
9522
9526
  this.pushEachEntry(r, nid, attrName, key, cachedNode);
9523
9527
  else {
9524
9528
  const dom = this.renderView(view, stack.enter(value, binds, false));
9525
- this.pushEachEntry(r, nid, attrName, key, dom);
9529
+ if (dom != null)
9530
+ this.pushEachEntry(r, nid, attrName, key, dom);
9526
9531
  this.cache.set(cachePath, cacheKey, dom);
9527
9532
  }
9528
9533
  };
@@ -9807,7 +9812,8 @@ function parseXOp(attrs, childs, opIdx, px) {
9807
9812
  if (attrs.length <= opIdx)
9808
9813
  return maybeFragment(childs);
9809
9814
  const { name, value } = attrs[opIdx];
9810
- const as = attrs.getNamedItem("as")?.value ?? null;
9815
+ const asAttr = attrs.getNamedItem("as")?.value ?? null;
9816
+ const as = asAttr === null ? null : parseViewName(asAttr, px);
9811
9817
  let node;
9812
9818
  switch (name) {
9813
9819
  case "slot":
@@ -9847,6 +9853,9 @@ function parseXOpVal(opName, value, px, parserFn) {
9847
9853
  px.onParseIssue("bad-value", { role: "x-op", op: opName, value });
9848
9854
  return val;
9849
9855
  }
9856
+ function parseViewName(s, px) {
9857
+ return vp.parseText(s, px) ?? vp.const(s);
9858
+ }
9850
9859
  function processXExtras(node, attrs, opName, startIdx, px) {
9851
9860
  const { consumed, wrappable } = X_OPS[opName];
9852
9861
  const wrappers = [];
@@ -9943,9 +9952,12 @@ class Macro {
9943
9952
  }
9944
9953
 
9945
9954
  class RenderViewId extends ANode {
9946
- constructor(nodeId, val, viewId) {
9955
+ constructor(nodeId, val, viewVal) {
9947
9956
  super(nodeId, val);
9948
- this.viewId = viewId;
9957
+ this.viewVal = viewVal;
9958
+ }
9959
+ evalViewName(stack) {
9960
+ return this.viewVal ? this.viewVal.eval(stack) : null;
9949
9961
  }
9950
9962
  setDataAttr(_key, _val) {}
9951
9963
  }
@@ -9959,7 +9971,7 @@ function dynRenderStep(comp, name, key) {
9959
9971
  class RenderNode extends RenderViewId {
9960
9972
  render(stack, rx) {
9961
9973
  const newStack = stack.enter(this.val.eval(stack), {}, true);
9962
- return rx.renderIt(newStack, this, "", this.viewId);
9974
+ return rx.renderIt(newStack, this, "", this.evalViewName(stack));
9963
9975
  }
9964
9976
  toPathStep(ctx) {
9965
9977
  if (this.val instanceof DynVal)
@@ -9971,7 +9983,7 @@ class RenderNode extends RenderViewId {
9971
9983
  class RenderItNode extends RenderViewId {
9972
9984
  render(stack, rx) {
9973
9985
  const newStack = stack.enter(stack.it, {}, true);
9974
- return rx.renderIt(newStack, this, "", this.viewId);
9986
+ return rx.renderIt(newStack, this, "", this.evalViewName(stack));
9975
9987
  }
9976
9988
  toPathStep(ctx) {
9977
9989
  const next = ctx.next();
@@ -9988,12 +10000,12 @@ class RenderItNode extends RenderViewId {
9988
10000
  }
9989
10001
 
9990
10002
  class RenderEachNode extends RenderViewId {
9991
- constructor(nodeId, val, viewId) {
9992
- super(nodeId, val, viewId);
10003
+ constructor(nodeId, val, viewVal) {
10004
+ super(nodeId, val, viewVal);
9993
10005
  this.iterInfo = new IterInfo(val, null, null, null);
9994
10006
  }
9995
10007
  render(stack, rx) {
9996
- return rx.renderEach(stack, this.iterInfo, this, this.viewId);
10008
+ return rx.renderEach(stack, this.iterInfo, this, this.evalViewName(stack));
9997
10009
  }
9998
10010
  toPathStep(ctx) {
9999
10011
  if (this.val instanceof DynVal)
@@ -10279,6 +10291,9 @@ class View {
10279
10291
  this.anode = optimizeNode(this.anode);
10280
10292
  }
10281
10293
  render(stack, rx) {
10294
+ if (this.anode === null) {
10295
+ throw new Error(`tutuca: view "${this.name}" was rendered before it was compiled — its ` + `component is not registered in this app/scope. Source: ` + `${String(this.rawView).slice(0, 80).replace(/\s+/g, " ")}…`);
10296
+ }
10282
10297
  return this.anode.render(stack, rx);
10283
10298
  }
10284
10299
  }
@@ -12732,6 +12747,8 @@ var COMP_FIELD_BAD_SHAPE = "COMP_FIELD_BAD_SHAPE";
12732
12747
  var ASYNC_HANDLER = "ASYNC_HANDLER";
12733
12748
  var TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE = "TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE";
12734
12749
  var GLOBAL_SELECTOR_IN_SCOPED_STYLE = "GLOBAL_SELECTOR_IN_SCOPED_STYLE";
12750
+ var FIELD_NAME_RESERVED_BY_RECORD = "FIELD_NAME_RESERVED_BY_RECORD";
12751
+ var RECORD_FIELD_NAME_COLLISIONS = new Set(["entries", "hashCode"]);
12735
12752
  var X_KNOWN_OP_NAMES = new Set([
12736
12753
  "slot",
12737
12754
  "text",
@@ -12820,6 +12837,7 @@ function checkComponent(Comp, lx = new LintContext, { wellKnownExtras = EMPTY_SE
12820
12837
  return lx.push({ componentName: Comp.name }, () => {
12821
12838
  checkUnknownSpecKeys(lx, Comp, wellKnownExtras);
12822
12839
  checkFieldDeclarations(lx, Comp);
12840
+ checkRecordFieldNameCollisions(lx, Comp);
12823
12841
  checkProvidesAreAddressable(lx, Comp);
12824
12842
  checkLookupShapes(lx, Comp);
12825
12843
  checkHandlersNotAsync(lx, Comp);
@@ -13312,6 +13330,16 @@ function checkFieldDeclarations(lx, Comp) {
13312
13330
  }
13313
13331
  }
13314
13332
  }
13333
+ function checkRecordFieldNameCollisions(lx, Comp) {
13334
+ const fields = Comp.Class?.getMetaClass?.().fields;
13335
+ if (!fields)
13336
+ return;
13337
+ for (const name in fields) {
13338
+ if (RECORD_FIELD_NAME_COLLISIONS.has(name)) {
13339
+ lx.error(FIELD_NAME_RESERVED_BY_RECORD, { name });
13340
+ }
13341
+ }
13342
+ }
13315
13343
  function checkUnreferencedAlterHandlers(lx, Comp, referencedAlters) {
13316
13344
  for (const name in Comp.alter) {
13317
13345
  if (!referencedAlters.has(name)) {
@@ -14900,6 +14928,8 @@ function lintIdToMessage(id, info) {
14900
14928
  return `'$${info.name}' is a method reference, but '${info.name}' is defined as an input handler${fmtEventSuffix(info)}`;
14901
14929
  case "INPUT_HANDLER_METHOD_FOR_INPUT_HANDLER":
14902
14930
  return `'${info.name}' is an input handler reference, but '${info.name}' is defined as a method${fmtEventSuffix(info)}`;
14931
+ case "FIELD_NAME_RESERVED_BY_RECORD":
14932
+ return `Field '${info.name}' collides with the Immutable Record API: '.${info.name}' reads the Record member, not your value — rename the field (its value is only reachable via .get('${info.name}'))`;
14903
14933
  case "FIELD_VAL_NOT_DEFINED":
14904
14934
  return `Field '.${info.name}' is not defined${fmtOriginSuffix(info)}`;
14905
14935
  case "FIELD_VAL_IS_METHOD":
@@ -16156,6 +16186,7 @@ export {
16156
16186
  GLOBAL_SELECTOR_IN_SCOPED_STYLE,
16157
16187
  FIELD_VAL_NOT_DEFINED,
16158
16188
  FIELD_VAL_IS_METHOD,
16189
+ FIELD_NAME_RESERVED_BY_RECORD,
16159
16190
  FIELD_CLASS,
16160
16191
  ExampleIndex,
16161
16192
  DescribeResult,