tutuca 0.9.109 → 0.9.111
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/tutuca-cli.js +150 -79
- package/dist/tutuca-components.js +6 -6
- package/dist/tutuca-dev.ext.js +15 -11
- package/dist/tutuca-dev.js +15 -11
- package/dist/tutuca-dev.min.js +2 -2
- package/dist/tutuca-extra.ext.js +8 -4
- package/dist/tutuca-extra.js +8 -4
- package/dist/tutuca-extra.min.js +2 -2
- package/dist/tutuca.ext.js +8 -4
- package/dist/tutuca.js +8 -4
- package/dist/tutuca.min.js +2 -2
- package/package.json +1 -1
- package/skill/tutuca/cli.md +13 -1
- package/skill/tutuca/core.md +10 -6
- package/skill/tutuca/iteration.md +15 -9
- package/skill/tutuca/patterns/README.md +1 -1
- package/skill/tutuca-source/tutuca.ext.js +8 -4
package/dist/tutuca-cli.js
CHANGED
|
@@ -6210,9 +6210,12 @@ function parseRenderEach(px, value, as, attrs) {
|
|
|
6210
6210
|
maybeDeprecateBareXDirective(px, "render-each", "when");
|
|
6211
6211
|
attrParser._parseWhen(when.value);
|
|
6212
6212
|
}
|
|
6213
|
-
const lWith = attrs.getNamedItem("loop-with");
|
|
6214
|
-
if (lWith)
|
|
6213
|
+
const lWith = attrs.getNamedItem("@loop-with") ?? attrs.getNamedItem("loop-with");
|
|
6214
|
+
if (lWith) {
|
|
6215
|
+
if (lWith.name.charCodeAt(0) !== 64)
|
|
6216
|
+
maybeDeprecateBareXDirective(px, "render-each", "loop-with");
|
|
6215
6217
|
attrParser._parseLoopWith(lWith.value);
|
|
6218
|
+
}
|
|
6216
6219
|
const each = px.addNodeIf(EachNode, seqVal);
|
|
6217
6220
|
each.iterInfo.whenVal = eachAttr.whenVal ?? null;
|
|
6218
6221
|
each.iterInfo.loopWithVal = eachAttr.loopWithVal ?? null;
|
|
@@ -6726,7 +6729,7 @@ var init_anode = __esm(() => {
|
|
|
6726
6729
|
text: xOp([], { wrappable: true, ignoresChildren: true }),
|
|
6727
6730
|
render: xOp(["as"], { wrappable: true, ignoresChildren: true }),
|
|
6728
6731
|
"render-it": xOp(["as"], { wrappable: true, ignoresChildren: true }),
|
|
6729
|
-
"render-each": xOp(["as", "when", "loop-with", "@when"], {
|
|
6732
|
+
"render-each": xOp(["as", "when", "loop-with", "@when", "@loop-with"], {
|
|
6730
6733
|
wrappable: true,
|
|
6731
6734
|
ignoresChildren: true
|
|
6732
6735
|
}),
|
|
@@ -9473,11 +9476,11 @@ function checkConsistentAttrs(lx, Comp, referencedAlters, referencedDynamics) {
|
|
|
9473
9476
|
const iter = node.iterInfo;
|
|
9474
9477
|
if (iter.whenVal)
|
|
9475
9478
|
checkConsistentAttrVal(lx, iter.whenVal, env, false, {
|
|
9476
|
-
originAttr: "<x render-each when>"
|
|
9479
|
+
originAttr: "<x render-each @when>"
|
|
9477
9480
|
});
|
|
9478
9481
|
if (iter.loopWithVal)
|
|
9479
9482
|
checkConsistentAttrVal(lx, iter.loopWithVal, env, false, {
|
|
9480
|
-
originAttr: "<x render-each loop-with>"
|
|
9483
|
+
originAttr: "<x render-each @loop-with>"
|
|
9481
9484
|
});
|
|
9482
9485
|
}
|
|
9483
9486
|
}
|
|
@@ -9741,7 +9744,7 @@ var init_lint_check = __esm(() => {
|
|
|
9741
9744
|
};
|
|
9742
9745
|
BINDING_MEMBER_TOO_DEEP_RE = /^@[a-zA-Z]\w*\??(\.[a-zA-Z]\w*\??){2,}$/;
|
|
9743
9746
|
UNSUPPORTED_EXPR_GUIDANCE = {
|
|
9744
|
-
ternary: "Ternary expressions aren't supported in dynamic attributes. Define a method or computed
|
|
9747
|
+
ternary: "Ternary expressions aren't supported in dynamic attributes. Define a method or computed value on the component that returns the value, then reference it as '$methodName'.",
|
|
9745
9748
|
comparison: "Comparisons aren't supported in dynamic attributes. Define a method like 'isFooSelected' that returns the boolean, then reference it as '$isFooSelected'.",
|
|
9746
9749
|
logical: "Logical operators aren't supported in dynamic attributes. Combine the conditions in a method on the component and reference it as '$methodName'.",
|
|
9747
9750
|
"call-with-args": "Method calls with arguments aren't supported here. Reference a no-arg method ('$methodName') and read what you need from component state, or split into per-case methods.",
|
|
@@ -10076,7 +10079,7 @@ var init_lint_rules = __esm(() => {
|
|
|
10076
10079
|
code: DEPRECATED_BARE_X_DIRECTIVE,
|
|
10077
10080
|
level: "warn",
|
|
10078
10081
|
group: "Templates / events",
|
|
10079
|
-
summary: "Bare `show`/`hide`/`when` on an `<x>` op is deprecated — use the `@`-prefixed directive (`@show`/`@hide`/`@when`)."
|
|
10082
|
+
summary: "Bare `show`/`hide`/`when`/`loop-with` on an `<x>` op is deprecated — use the `@`-prefixed directive (`@show`/`@hide`/`@when`/`@loop-with`)."
|
|
10080
10083
|
},
|
|
10081
10084
|
{
|
|
10082
10085
|
code: BAD_VALUE,
|
|
@@ -10212,7 +10215,7 @@ function badValueMessage(info) {
|
|
|
10212
10215
|
case "handler-arg":
|
|
10213
10216
|
return `Cannot parse handler argument ${v}`;
|
|
10214
10217
|
case "macro-var":
|
|
10215
|
-
return `Macro
|
|
10218
|
+
return `Macro parameter '^${info.name}' is not defined`;
|
|
10216
10219
|
default:
|
|
10217
10220
|
return `Cannot parse value ${v}`;
|
|
10218
10221
|
}
|
|
@@ -10292,7 +10295,7 @@ function lintIdToMessage(id, info) {
|
|
|
10292
10295
|
case "ALT_HANDLER_NOT_REFERENCED":
|
|
10293
10296
|
return `Alter handler '${info.name}' is defined but never used — remove it or reference it from @when, @enrich-with, or @loop-with`;
|
|
10294
10297
|
case "DYN_VAL_NOT_DEFINED":
|
|
10295
|
-
return `Dynamic
|
|
10298
|
+
return `Dynamic binding '*${info.name}' is not defined${fmtOriginSuffix(info)}`;
|
|
10296
10299
|
case "DYN_ALIAS_NOT_REFERENCED":
|
|
10297
10300
|
return `Lookup '${info.name}' is defined but never used — remove it or reference it as '*${info.name}' in a view`;
|
|
10298
10301
|
case "PROVIDE_NOT_ADDRESSABLE":
|
|
@@ -10330,9 +10333,9 @@ function lintIdToMessage(id, info) {
|
|
|
10330
10333
|
return `Enrich handler '${info.name}' only copies members of the loop value — read them directly${reads ? ` (${reads})` : ""} and remove the '@enrich-with'`;
|
|
10331
10334
|
}
|
|
10332
10335
|
case "REDUNDANT_TEMPLATE_STRING":
|
|
10333
|
-
return `Redundant template
|
|
10336
|
+
return `Redundant string template — '{${info.simpler}}' should be just '${info.simpler}'${fmtOriginSuffix(info)}`;
|
|
10334
10337
|
case "PLACEHOLDERLESS_TEMPLATE_STRING":
|
|
10335
|
-
return `
|
|
10338
|
+
return `String template has no dynamic parts — use the string literal ${info.literal} instead${fmtOriginSuffix(info)}`;
|
|
10336
10339
|
case "CONSTANT_CONDITION":
|
|
10337
10340
|
return `Condition is the constant literal ${info.literal} — it never changes; reference a field ('.name') or a method ('$name') instead${fmtOriginSuffix(info)}`;
|
|
10338
10341
|
case "UNKNOWN_COMPONENT_SPEC_KEY":
|
|
@@ -14453,16 +14456,23 @@ class Example {
|
|
|
14453
14456
|
value,
|
|
14454
14457
|
view = "main",
|
|
14455
14458
|
componentName = null,
|
|
14456
|
-
|
|
14459
|
+
requestHandlers = null,
|
|
14460
|
+
requestHandlerNames = [],
|
|
14461
|
+
on = null
|
|
14457
14462
|
}) {
|
|
14458
14463
|
this.title = title;
|
|
14459
14464
|
this.description = description;
|
|
14460
14465
|
this.value = value;
|
|
14461
14466
|
this.view = view;
|
|
14462
14467
|
this.componentName = componentName;
|
|
14468
|
+
this.requestHandlers = requestHandlers;
|
|
14463
14469
|
this.requestHandlerNames = requestHandlerNames;
|
|
14470
|
+
this.on = on;
|
|
14464
14471
|
}
|
|
14465
14472
|
}
|
|
14473
|
+
function isPlainObject2(v) {
|
|
14474
|
+
return v != null && typeof v === "object" && !Array.isArray(v);
|
|
14475
|
+
}
|
|
14466
14476
|
function resolveComponentName(value, components) {
|
|
14467
14477
|
for (const comp of components) {
|
|
14468
14478
|
if (value instanceof comp.Class)
|
|
@@ -14509,13 +14519,29 @@ function parseExample(raw, index, components, parentPath) {
|
|
|
14509
14519
|
if (rh != null && (typeof rh !== "object" || Array.isArray(rh))) {
|
|
14510
14520
|
throw shapeError(`example at ${where} "requestHandlers" must be an object of functions`, where);
|
|
14511
14521
|
}
|
|
14522
|
+
const on = raw.on;
|
|
14523
|
+
if (on != null) {
|
|
14524
|
+
if (!isPlainObject2(on)) {
|
|
14525
|
+
throw shapeError(`example at ${where} "on" must be an object of lifecycle phases (${PHASE_NAMES.join(", ")})`, where);
|
|
14526
|
+
}
|
|
14527
|
+
for (const key in on) {
|
|
14528
|
+
if (!PHASE_NAMES.includes(key)) {
|
|
14529
|
+
throw shapeError(`example at ${where} has unknown lifecycle phase "on.${key}"; expected one of ${PHASE_NAMES.join(", ")}`, where);
|
|
14530
|
+
}
|
|
14531
|
+
if (!isPlainObject2(on[key])) {
|
|
14532
|
+
throw shapeError(`example at ${where} "on.${key}" must be an object of actions (send, bubble, request, input, do)`, where);
|
|
14533
|
+
}
|
|
14534
|
+
}
|
|
14535
|
+
}
|
|
14512
14536
|
return new Example({
|
|
14513
14537
|
title: raw.title ?? `Example ${index + 1}`,
|
|
14514
14538
|
description: raw.description ?? null,
|
|
14515
14539
|
value: raw.value,
|
|
14516
14540
|
view: raw.view ?? "main",
|
|
14517
14541
|
componentName: resolveComponentName(raw.value, components),
|
|
14518
|
-
|
|
14542
|
+
requestHandlers: rh ?? null,
|
|
14543
|
+
requestHandlerNames: rh ? Object.keys(rh) : [],
|
|
14544
|
+
on: on ?? null
|
|
14519
14545
|
});
|
|
14520
14546
|
}
|
|
14521
14547
|
function parseSection(raw, components, where) {
|
|
@@ -14609,7 +14635,10 @@ function findComponentNameConflicts(entries) {
|
|
|
14609
14635
|
}
|
|
14610
14636
|
return conflicts.sort((a, b) => a.name.localeCompare(b.name));
|
|
14611
14637
|
}
|
|
14612
|
-
var EXAMPLES_SHAPE_MISMATCH = "EXAMPLES_SHAPE_MISMATCH";
|
|
14638
|
+
var EXAMPLES_SHAPE_MISMATCH = "EXAMPLES_SHAPE_MISMATCH", PHASE_NAMES;
|
|
14639
|
+
var init_module = __esm(() => {
|
|
14640
|
+
PHASE_NAMES = ["init", "resume", "suspend"];
|
|
14641
|
+
});
|
|
14613
14642
|
|
|
14614
14643
|
// tools/core/results.js
|
|
14615
14644
|
class ModuleInfo {
|
|
@@ -14781,7 +14810,9 @@ function describeModule(mod, { path = null } = {}) {
|
|
|
14781
14810
|
}
|
|
14782
14811
|
return new ModuleInfo({ path, present, counts, warnings });
|
|
14783
14812
|
}
|
|
14784
|
-
var init_describe = () => {
|
|
14813
|
+
var init_describe = __esm(() => {
|
|
14814
|
+
init_module();
|
|
14815
|
+
});
|
|
14785
14816
|
|
|
14786
14817
|
// tools/core/docs.js
|
|
14787
14818
|
function getSignature(name, fn) {
|
|
@@ -15819,6 +15850,7 @@ class App {
|
|
|
15819
15850
|
this._compiled = false;
|
|
15820
15851
|
this._renderOpts = { document: rootNode.ownerDocument };
|
|
15821
15852
|
this._renderState = null;
|
|
15853
|
+
this.rootViewName = null;
|
|
15822
15854
|
}
|
|
15823
15855
|
get state() {
|
|
15824
15856
|
return this.transactor.state;
|
|
@@ -15930,7 +15962,7 @@ class App {
|
|
|
15930
15962
|
const root = this.state.val;
|
|
15931
15963
|
const stack = this.makeStack(root);
|
|
15932
15964
|
const { renderer, rootNode, _renderOpts, _renderState } = this;
|
|
15933
|
-
const newState = render(renderer.renderRoot(stack, root), rootNode, _renderOpts, _renderState);
|
|
15965
|
+
const newState = render(renderer.renderRoot(stack, root, this.rootViewName), rootNode, _renderOpts, _renderState);
|
|
15934
15966
|
this._renderState = newState;
|
|
15935
15967
|
return newState.dom;
|
|
15936
15968
|
}
|
|
@@ -16069,12 +16101,71 @@ var init_app = __esm(() => {
|
|
|
16069
16101
|
_evs = "dragstart dragover dragend touchstart touchmove touchend touchcancel".split(" ");
|
|
16070
16102
|
});
|
|
16071
16103
|
|
|
16104
|
+
// src/on.js
|
|
16105
|
+
function phaseOps(phase) {
|
|
16106
|
+
const ops = [];
|
|
16107
|
+
for (const type3 of OP_KINDS)
|
|
16108
|
+
for (const a of phase[type3] ?? [])
|
|
16109
|
+
ops.push({ type: type3, ...a });
|
|
16110
|
+
for (const a of phase.do ?? [])
|
|
16111
|
+
ops.push(a);
|
|
16112
|
+
return ops;
|
|
16113
|
+
}
|
|
16114
|
+
function resolveArgs(args, self) {
|
|
16115
|
+
return typeof args === "function" ? args(self) ?? [] : args ?? [];
|
|
16116
|
+
}
|
|
16117
|
+
function phaseHasBubble(phase) {
|
|
16118
|
+
if (!phase)
|
|
16119
|
+
return false;
|
|
16120
|
+
if (phase.bubble?.length)
|
|
16121
|
+
return true;
|
|
16122
|
+
return (phase.do ?? []).some((op) => op.type === "bubble");
|
|
16123
|
+
}
|
|
16124
|
+
function dispatchPhase(dispatcher, targetPath, phase, self) {
|
|
16125
|
+
if (!phase)
|
|
16126
|
+
return;
|
|
16127
|
+
for (const op of phaseOps(phase)) {
|
|
16128
|
+
const args = resolveArgs(op.args, self);
|
|
16129
|
+
switch (op.type) {
|
|
16130
|
+
case "send":
|
|
16131
|
+
dispatcher.sendAtPath(targetPath, op.name, args, op.opts);
|
|
16132
|
+
break;
|
|
16133
|
+
case "bubble":
|
|
16134
|
+
dispatcher.sendAtPath(targetPath, op.name, args, {
|
|
16135
|
+
skipSelf: true,
|
|
16136
|
+
bubbles: true,
|
|
16137
|
+
...op.opts
|
|
16138
|
+
});
|
|
16139
|
+
break;
|
|
16140
|
+
case "request":
|
|
16141
|
+
dispatcher.requestAtPath(targetPath, op.name, args, op.opts);
|
|
16142
|
+
break;
|
|
16143
|
+
case "input":
|
|
16144
|
+
dispatcher.inputAtPath(targetPath, op.name, args, op.opts);
|
|
16145
|
+
break;
|
|
16146
|
+
}
|
|
16147
|
+
}
|
|
16148
|
+
}
|
|
16149
|
+
var OP_KINDS;
|
|
16150
|
+
var init_on = __esm(() => {
|
|
16151
|
+
OP_KINDS = ["send", "bubble", "request", "input"];
|
|
16152
|
+
});
|
|
16153
|
+
|
|
16072
16154
|
// src/util/render.js
|
|
16073
16155
|
function reindexComponents(comps) {
|
|
16074
16156
|
for (let i = 0;i < comps.length; i++) {
|
|
16075
16157
|
comps[i].id = i;
|
|
16076
16158
|
}
|
|
16077
16159
|
}
|
|
16160
|
+
function serializeContainer(container) {
|
|
16161
|
+
for (const input of container.querySelectorAll("input")) {
|
|
16162
|
+
if (input.value)
|
|
16163
|
+
input.setAttribute("value", input.value);
|
|
16164
|
+
if (input.checked)
|
|
16165
|
+
input.setAttribute("checked", "");
|
|
16166
|
+
}
|
|
16167
|
+
return container.innerHTML;
|
|
16168
|
+
}
|
|
16078
16169
|
function renderToHTMLNode(document2, components, macros, rootState, ParseContext2, opts = { noCache: true }) {
|
|
16079
16170
|
const container = document2.createElement("div");
|
|
16080
16171
|
document2.body.appendChild(container);
|
|
@@ -16085,14 +16176,11 @@ function renderToHTMLNode(document2, components, macros, rootState, ParseContext
|
|
|
16085
16176
|
const scope = app.registerComponents(components);
|
|
16086
16177
|
if (macros)
|
|
16087
16178
|
scope.registerMacros(macros);
|
|
16179
|
+
if (opts.requestHandlers)
|
|
16180
|
+
scope.registerRequestHandlers(opts.requestHandlers);
|
|
16181
|
+
app.rootViewName = opts.view ?? null;
|
|
16088
16182
|
app.transactor.state.set(rootState);
|
|
16089
16183
|
app.start(opts);
|
|
16090
|
-
for (const input of container.querySelectorAll("input")) {
|
|
16091
|
-
if (input.value)
|
|
16092
|
-
input.setAttribute("value", input.value);
|
|
16093
|
-
if (input.checked)
|
|
16094
|
-
input.setAttribute("checked", "");
|
|
16095
|
-
}
|
|
16096
16184
|
return {
|
|
16097
16185
|
container,
|
|
16098
16186
|
app,
|
|
@@ -16102,20 +16190,44 @@ function renderToHTMLNode(document2, components, macros, rootState, ParseContext
|
|
|
16102
16190
|
}
|
|
16103
16191
|
};
|
|
16104
16192
|
}
|
|
16105
|
-
function
|
|
16106
|
-
const { container, cleanup } = renderToHTMLNode(document2, components, macros, rootState, ParseContext2);
|
|
16107
|
-
|
|
16108
|
-
|
|
16109
|
-
|
|
16193
|
+
async function renderToHTMLDriven(document2, components, macros, rootState, ParseContext2, { phase = null, requestHandlers = null, view = null, warn = console.warn } = {}) {
|
|
16194
|
+
const { container, app, cleanup } = renderToHTMLNode(document2, components, macros, rootState, ParseContext2, { noCache: true, requestHandlers, view });
|
|
16195
|
+
try {
|
|
16196
|
+
if (phase) {
|
|
16197
|
+
if (phaseHasBubble(phase))
|
|
16198
|
+
warn("render: a `bubble` action is a no-op here — the example's value is the render root, so there is no ancestor to receive it. Use send/request/input to drive a preset state.");
|
|
16199
|
+
dispatchPhase(rootDispatcher(app.transactor), new Path([]), phase, app.state.val);
|
|
16200
|
+
await app.transactor.settle();
|
|
16201
|
+
}
|
|
16202
|
+
return serializeContainer(container);
|
|
16203
|
+
} finally {
|
|
16204
|
+
cleanup();
|
|
16205
|
+
}
|
|
16110
16206
|
}
|
|
16111
16207
|
var init_render = __esm(() => {
|
|
16112
16208
|
init_app();
|
|
16113
16209
|
init_components();
|
|
16210
|
+
init_on();
|
|
16211
|
+
init_path();
|
|
16114
16212
|
init_renderer();
|
|
16213
|
+
init_transactor();
|
|
16115
16214
|
});
|
|
16116
16215
|
|
|
16117
16216
|
// tools/core/render.js
|
|
16118
|
-
function
|
|
16217
|
+
function checkView2(value, components, viewName) {
|
|
16218
|
+
if (viewName === "main")
|
|
16219
|
+
return null;
|
|
16220
|
+
const comp = components.find((c) => value instanceof c.Class);
|
|
16221
|
+
if (!comp || comp.views[viewName])
|
|
16222
|
+
return null;
|
|
16223
|
+
const known = Object.keys(comp.views).sort().join(", ");
|
|
16224
|
+
return new Error(`view "${viewName}" is not defined on ${comp.name} (has: ${known})`);
|
|
16225
|
+
}
|
|
16226
|
+
function handlersFor(normalized, example) {
|
|
16227
|
+
const merged = { ...normalized.requestHandlers ?? {}, ...example.requestHandlers ?? {} };
|
|
16228
|
+
return Object.keys(merged).length > 0 ? merged : null;
|
|
16229
|
+
}
|
|
16230
|
+
async function renderExamples(normalized, env, { name = null, title = null, view = null } = {}) {
|
|
16119
16231
|
const sections = [];
|
|
16120
16232
|
for (const section of normalized.sections) {
|
|
16121
16233
|
const items = [];
|
|
@@ -16129,7 +16241,14 @@ function renderExamples(normalized, env, { name = null, title = null, view = nul
|
|
|
16129
16241
|
let html = "";
|
|
16130
16242
|
let error = null;
|
|
16131
16243
|
try {
|
|
16132
|
-
|
|
16244
|
+
const bad = checkView2(example.value, normalized.components, viewName);
|
|
16245
|
+
if (bad)
|
|
16246
|
+
throw bad;
|
|
16247
|
+
html = await renderToHTMLDriven(env.document, normalized.components, normalized.macros, example.value, env.ParseContext, {
|
|
16248
|
+
phase: example.on?.init ?? null,
|
|
16249
|
+
requestHandlers: handlersFor(normalized, example),
|
|
16250
|
+
view: viewName
|
|
16251
|
+
});
|
|
16133
16252
|
} catch (e) {
|
|
16134
16253
|
error = { message: e.message, stack: e.stack };
|
|
16135
16254
|
}
|
|
@@ -16156,56 +16275,6 @@ var init_render2 = __esm(() => {
|
|
|
16156
16275
|
init_render();
|
|
16157
16276
|
});
|
|
16158
16277
|
|
|
16159
|
-
// src/on.js
|
|
16160
|
-
function phaseOps(phase) {
|
|
16161
|
-
const ops = [];
|
|
16162
|
-
for (const type3 of OP_KINDS)
|
|
16163
|
-
for (const a of phase[type3] ?? [])
|
|
16164
|
-
ops.push({ type: type3, ...a });
|
|
16165
|
-
for (const a of phase.do ?? [])
|
|
16166
|
-
ops.push(a);
|
|
16167
|
-
return ops;
|
|
16168
|
-
}
|
|
16169
|
-
function resolveArgs(args, self) {
|
|
16170
|
-
return typeof args === "function" ? args(self) ?? [] : args ?? [];
|
|
16171
|
-
}
|
|
16172
|
-
function phaseHasBubble(phase) {
|
|
16173
|
-
if (!phase)
|
|
16174
|
-
return false;
|
|
16175
|
-
if (phase.bubble?.length)
|
|
16176
|
-
return true;
|
|
16177
|
-
return (phase.do ?? []).some((op) => op.type === "bubble");
|
|
16178
|
-
}
|
|
16179
|
-
function dispatchPhase(dispatcher, targetPath, phase, self) {
|
|
16180
|
-
if (!phase)
|
|
16181
|
-
return;
|
|
16182
|
-
for (const op of phaseOps(phase)) {
|
|
16183
|
-
const args = resolveArgs(op.args, self);
|
|
16184
|
-
switch (op.type) {
|
|
16185
|
-
case "send":
|
|
16186
|
-
dispatcher.sendAtPath(targetPath, op.name, args, op.opts);
|
|
16187
|
-
break;
|
|
16188
|
-
case "bubble":
|
|
16189
|
-
dispatcher.sendAtPath(targetPath, op.name, args, {
|
|
16190
|
-
skipSelf: true,
|
|
16191
|
-
bubbles: true,
|
|
16192
|
-
...op.opts
|
|
16193
|
-
});
|
|
16194
|
-
break;
|
|
16195
|
-
case "request":
|
|
16196
|
-
dispatcher.requestAtPath(targetPath, op.name, args, op.opts);
|
|
16197
|
-
break;
|
|
16198
|
-
case "input":
|
|
16199
|
-
dispatcher.inputAtPath(targetPath, op.name, args, op.opts);
|
|
16200
|
-
break;
|
|
16201
|
-
}
|
|
16202
|
-
}
|
|
16203
|
-
}
|
|
16204
|
-
var OP_KINDS;
|
|
16205
|
-
var init_on = __esm(() => {
|
|
16206
|
-
OP_KINDS = ["send", "bubble", "request", "input"];
|
|
16207
|
-
});
|
|
16208
|
-
|
|
16209
16278
|
// tools/core/tests.js
|
|
16210
16279
|
class Describe {
|
|
16211
16280
|
constructor({ title, componentName = null, parent = null }) {
|
|
@@ -17686,6 +17755,7 @@ async function run5(argv, opts = {}) {
|
|
|
17686
17755
|
var describe5 = "Serve a storybook for the project's co-located *.dev.js modules (auto-discovered).", BOOTSTRAP_URL = "/__tutuca_storybook__.js", DIST_PREFIX = "/__tutuca__/", MARGAUI_PREFIX = "/__margaui__/", MIME, MARGAUI_CDN = "https://cdn.jsdelivr.net/npm/margaui/+esm", MARGAUI_THEME = "https://marianoguerra.github.io/margaui/themes/theme.css";
|
|
17687
17756
|
var init_storybook = __esm(() => {
|
|
17688
17757
|
init_chai2();
|
|
17758
|
+
init_module();
|
|
17689
17759
|
init_test();
|
|
17690
17760
|
init_env2();
|
|
17691
17761
|
init_errors();
|
|
@@ -17864,6 +17934,7 @@ import { statSync as statSync2 } from "node:fs";
|
|
|
17864
17934
|
import { parseArgs as parseArgs4 } from "node:util";
|
|
17865
17935
|
|
|
17866
17936
|
// tools/cli/load.js
|
|
17937
|
+
init_module();
|
|
17867
17938
|
import { existsSync as existsSync4 } from "node:fs";
|
|
17868
17939
|
import { resolve as resolve6 } from "node:path";
|
|
17869
17940
|
async function loadAndNormalize(modulePath) {
|
|
@@ -151,7 +151,7 @@ function makeCompositeView({
|
|
|
151
151
|
@show=".isExpanded"
|
|
152
152
|
class="ml-1 flex flex-col gap-0.5 border-l ${borderClass} pl-2 mt-0.5"
|
|
153
153
|
>
|
|
154
|
-
<x render-each=".items" loop-with="getPageRange"></x>
|
|
154
|
+
<x render-each=".items" @loop-with="getPageRange"></x>
|
|
155
155
|
</div>
|
|
156
156
|
</span>`;
|
|
157
157
|
}
|
|
@@ -786,13 +786,13 @@ var GRID_BODY = html4`<div
|
|
|
786
786
|
class="ml-1 grid gap-x-2 gap-y-0.5 items-baseline border-l border-base-content/10 pl-2 mt-0.5"
|
|
787
787
|
style="grid-template-columns: auto 1fr"
|
|
788
788
|
>
|
|
789
|
-
<x render-each=".items" loop-with="getPageRange"></x>
|
|
789
|
+
<x render-each=".items" @loop-with="getPageRange"></x>
|
|
790
790
|
</div>`;
|
|
791
791
|
var LIST_BODY = html4`<div
|
|
792
792
|
@show=".isExpanded"
|
|
793
793
|
class="ml-1 flex flex-col gap-0.5 border-l border-base-content/10 pl-2 mt-0.5"
|
|
794
794
|
>
|
|
795
|
-
<x render-each=".items" loop-with="getPageRange"></x>
|
|
795
|
+
<x render-each=".items" @loop-with="getPageRange"></x>
|
|
796
796
|
</div>`;
|
|
797
797
|
function makeSchemaView(accent, body = GRID_BODY) {
|
|
798
798
|
return html4`<span class="font-mono text-sm leading-tight inline-block">
|
|
@@ -1640,7 +1640,7 @@ function lintMessage(id, info = {}) {
|
|
|
1640
1640
|
case "ALT_HANDLER_NOT_DEFINED":
|
|
1641
1641
|
return `Alter handler '${info.name}' is not defined${originSuffix(info)}`;
|
|
1642
1642
|
case "DYN_VAL_NOT_DEFINED":
|
|
1643
|
-
return `Dynamic
|
|
1643
|
+
return `Dynamic binding '*${info.name}' is not defined${originSuffix(info)}`;
|
|
1644
1644
|
case "UNKNOWN_COMPONENT_NAME":
|
|
1645
1645
|
return `Unknown component '${info.name}'${originSuffix(info)}`;
|
|
1646
1646
|
case "UNKNOWN_DIRECTIVE":
|
|
@@ -1648,9 +1648,9 @@ function lintMessage(id, info = {}) {
|
|
|
1648
1648
|
case "IF_NO_BRANCH_SET":
|
|
1649
1649
|
return `'@if.${info.attr}' has no '@then' or '@else' branch${tagSuffix(info)}`;
|
|
1650
1650
|
case "REDUNDANT_TEMPLATE_STRING":
|
|
1651
|
-
return `Redundant template
|
|
1651
|
+
return `Redundant string template — '{${info.simpler}}' should be just '${info.simpler}'${originSuffix(info)}`;
|
|
1652
1652
|
case "PLACEHOLDERLESS_TEMPLATE_STRING":
|
|
1653
|
-
return `
|
|
1653
|
+
return `String template has no dynamic parts — use the literal ${info.literal} instead${originSuffix(info)}`;
|
|
1654
1654
|
case "CONSTANT_CONDITION":
|
|
1655
1655
|
return `Constant condition ${info.literal} — reference a field ('.name') or method ('$name')${originSuffix(info)}`;
|
|
1656
1656
|
case "ASYNC_HANDLER":
|
package/dist/tutuca-dev.ext.js
CHANGED
|
@@ -2375,9 +2375,12 @@ function parseRenderEach(px, value, as, attrs) {
|
|
|
2375
2375
|
maybeDeprecateBareXDirective(px, "render-each", "when");
|
|
2376
2376
|
attrParser._parseWhen(when.value);
|
|
2377
2377
|
}
|
|
2378
|
-
const lWith = attrs.getNamedItem("loop-with");
|
|
2379
|
-
if (lWith)
|
|
2378
|
+
const lWith = attrs.getNamedItem("@loop-with") ?? attrs.getNamedItem("loop-with");
|
|
2379
|
+
if (lWith) {
|
|
2380
|
+
if (lWith.name.charCodeAt(0) !== 64)
|
|
2381
|
+
maybeDeprecateBareXDirective(px, "render-each", "loop-with");
|
|
2380
2382
|
attrParser._parseLoopWith(lWith.value);
|
|
2383
|
+
}
|
|
2381
2384
|
const each = px.addNodeIf(EachNode, seqVal);
|
|
2382
2385
|
each.iterInfo.whenVal = eachAttr.whenVal ?? null;
|
|
2383
2386
|
each.iterInfo.loopWithVal = eachAttr.loopWithVal ?? null;
|
|
@@ -2513,7 +2516,7 @@ var X_OPS = {
|
|
|
2513
2516
|
text: xOp([], { wrappable: true, ignoresChildren: true }),
|
|
2514
2517
|
render: xOp(["as"], { wrappable: true, ignoresChildren: true }),
|
|
2515
2518
|
"render-it": xOp(["as"], { wrappable: true, ignoresChildren: true }),
|
|
2516
|
-
"render-each": xOp(["as", "when", "loop-with", "@when"], {
|
|
2519
|
+
"render-each": xOp(["as", "when", "loop-with", "@when", "@loop-with"], {
|
|
2517
2520
|
wrappable: true,
|
|
2518
2521
|
ignoresChildren: true
|
|
2519
2522
|
}),
|
|
@@ -5200,7 +5203,7 @@ function classifyBadValue(value) {
|
|
|
5200
5203
|
}
|
|
5201
5204
|
var BINDING_MEMBER_TOO_DEEP_RE = /^@[a-zA-Z]\w*\??(\.[a-zA-Z]\w*\??){2,}$/;
|
|
5202
5205
|
var UNSUPPORTED_EXPR_GUIDANCE = {
|
|
5203
|
-
ternary: "Ternary expressions aren't supported in dynamic attributes. Define a method or computed
|
|
5206
|
+
ternary: "Ternary expressions aren't supported in dynamic attributes. Define a method or computed value on the component that returns the value, then reference it as '$methodName'.",
|
|
5204
5207
|
comparison: "Comparisons aren't supported in dynamic attributes. Define a method like 'isFooSelected' that returns the boolean, then reference it as '$isFooSelected'.",
|
|
5205
5208
|
logical: "Logical operators aren't supported in dynamic attributes. Combine the conditions in a method on the component and reference it as '$methodName'.",
|
|
5206
5209
|
"call-with-args": "Method calls with arguments aren't supported here. Reference a no-arg method ('$methodName') and read what you need from component state, or split into per-case methods.",
|
|
@@ -5728,11 +5731,11 @@ function checkConsistentAttrs(lx, Comp, referencedAlters, referencedDynamics) {
|
|
|
5728
5731
|
const iter = node.iterInfo;
|
|
5729
5732
|
if (iter.whenVal)
|
|
5730
5733
|
checkConsistentAttrVal(lx, iter.whenVal, env, false, {
|
|
5731
|
-
originAttr: "<x render-each when>"
|
|
5734
|
+
originAttr: "<x render-each @when>"
|
|
5732
5735
|
});
|
|
5733
5736
|
if (iter.loopWithVal)
|
|
5734
5737
|
checkConsistentAttrVal(lx, iter.loopWithVal, env, false, {
|
|
5735
|
-
originAttr: "<x render-each loop-with>"
|
|
5738
|
+
originAttr: "<x render-each @loop-with>"
|
|
5736
5739
|
});
|
|
5737
5740
|
}
|
|
5738
5741
|
}
|
|
@@ -7399,7 +7402,7 @@ function badValueMessage(info) {
|
|
|
7399
7402
|
case "handler-arg":
|
|
7400
7403
|
return `Cannot parse handler argument ${v}`;
|
|
7401
7404
|
case "macro-var":
|
|
7402
|
-
return `Macro
|
|
7405
|
+
return `Macro parameter '^${info.name}' is not defined`;
|
|
7403
7406
|
default:
|
|
7404
7407
|
return `Cannot parse value ${v}`;
|
|
7405
7408
|
}
|
|
@@ -7479,7 +7482,7 @@ function lintIdToMessage(id, info) {
|
|
|
7479
7482
|
case "ALT_HANDLER_NOT_REFERENCED":
|
|
7480
7483
|
return `Alter handler '${info.name}' is defined but never used — remove it or reference it from @when, @enrich-with, or @loop-with`;
|
|
7481
7484
|
case "DYN_VAL_NOT_DEFINED":
|
|
7482
|
-
return `Dynamic
|
|
7485
|
+
return `Dynamic binding '*${info.name}' is not defined${fmtOriginSuffix(info)}`;
|
|
7483
7486
|
case "DYN_ALIAS_NOT_REFERENCED":
|
|
7484
7487
|
return `Lookup '${info.name}' is defined but never used — remove it or reference it as '*${info.name}' in a view`;
|
|
7485
7488
|
case "PROVIDE_NOT_ADDRESSABLE":
|
|
@@ -7517,9 +7520,9 @@ function lintIdToMessage(id, info) {
|
|
|
7517
7520
|
return `Enrich handler '${info.name}' only copies members of the loop value — read them directly${reads ? ` (${reads})` : ""} and remove the '@enrich-with'`;
|
|
7518
7521
|
}
|
|
7519
7522
|
case "REDUNDANT_TEMPLATE_STRING":
|
|
7520
|
-
return `Redundant template
|
|
7523
|
+
return `Redundant string template — '{${info.simpler}}' should be just '${info.simpler}'${fmtOriginSuffix(info)}`;
|
|
7521
7524
|
case "PLACEHOLDERLESS_TEMPLATE_STRING":
|
|
7522
|
-
return `
|
|
7525
|
+
return `String template has no dynamic parts — use the string literal ${info.literal} instead${fmtOriginSuffix(info)}`;
|
|
7523
7526
|
case "CONSTANT_CONDITION":
|
|
7524
7527
|
return `Condition is the constant literal ${info.literal} — it never changes; reference a field ('.name') or a method ('$name') instead${fmtOriginSuffix(info)}`;
|
|
7525
7528
|
case "UNKNOWN_COMPONENT_SPEC_KEY":
|
|
@@ -7642,6 +7645,7 @@ class App {
|
|
|
7642
7645
|
this._compiled = false;
|
|
7643
7646
|
this._renderOpts = { document: rootNode.ownerDocument };
|
|
7644
7647
|
this._renderState = null;
|
|
7648
|
+
this.rootViewName = null;
|
|
7645
7649
|
}
|
|
7646
7650
|
get state() {
|
|
7647
7651
|
return this.transactor.state;
|
|
@@ -7753,7 +7757,7 @@ class App {
|
|
|
7753
7757
|
const root = this.state.val;
|
|
7754
7758
|
const stack = this.makeStack(root);
|
|
7755
7759
|
const { renderer, rootNode, _renderOpts, _renderState } = this;
|
|
7756
|
-
const newState = render(renderer.renderRoot(stack, root), rootNode, _renderOpts, _renderState);
|
|
7760
|
+
const newState = render(renderer.renderRoot(stack, root, this.rootViewName), rootNode, _renderOpts, _renderState);
|
|
7757
7761
|
this._renderState = newState;
|
|
7758
7762
|
return newState.dom;
|
|
7759
7763
|
}
|
package/dist/tutuca-dev.js
CHANGED
|
@@ -10024,9 +10024,12 @@ function parseRenderEach(px, value, as, attrs) {
|
|
|
10024
10024
|
maybeDeprecateBareXDirective(px, "render-each", "when");
|
|
10025
10025
|
attrParser._parseWhen(when.value);
|
|
10026
10026
|
}
|
|
10027
|
-
const lWith = attrs.getNamedItem("loop-with");
|
|
10028
|
-
if (lWith)
|
|
10027
|
+
const lWith = attrs.getNamedItem("@loop-with") ?? attrs.getNamedItem("loop-with");
|
|
10028
|
+
if (lWith) {
|
|
10029
|
+
if (lWith.name.charCodeAt(0) !== 64)
|
|
10030
|
+
maybeDeprecateBareXDirective(px, "render-each", "loop-with");
|
|
10029
10031
|
attrParser._parseLoopWith(lWith.value);
|
|
10032
|
+
}
|
|
10030
10033
|
const each = px.addNodeIf(EachNode, seqVal);
|
|
10031
10034
|
each.iterInfo.whenVal = eachAttr.whenVal ?? null;
|
|
10032
10035
|
each.iterInfo.loopWithVal = eachAttr.loopWithVal ?? null;
|
|
@@ -10162,7 +10165,7 @@ var X_OPS = {
|
|
|
10162
10165
|
text: xOp([], { wrappable: true, ignoresChildren: true }),
|
|
10163
10166
|
render: xOp(["as"], { wrappable: true, ignoresChildren: true }),
|
|
10164
10167
|
"render-it": xOp(["as"], { wrappable: true, ignoresChildren: true }),
|
|
10165
|
-
"render-each": xOp(["as", "when", "loop-with", "@when"], {
|
|
10168
|
+
"render-each": xOp(["as", "when", "loop-with", "@when", "@loop-with"], {
|
|
10166
10169
|
wrappable: true,
|
|
10167
10170
|
ignoresChildren: true
|
|
10168
10171
|
}),
|
|
@@ -12849,7 +12852,7 @@ function classifyBadValue(value) {
|
|
|
12849
12852
|
}
|
|
12850
12853
|
var BINDING_MEMBER_TOO_DEEP_RE = /^@[a-zA-Z]\w*\??(\.[a-zA-Z]\w*\??){2,}$/;
|
|
12851
12854
|
var UNSUPPORTED_EXPR_GUIDANCE = {
|
|
12852
|
-
ternary: "Ternary expressions aren't supported in dynamic attributes. Define a method or computed
|
|
12855
|
+
ternary: "Ternary expressions aren't supported in dynamic attributes. Define a method or computed value on the component that returns the value, then reference it as '$methodName'.",
|
|
12853
12856
|
comparison: "Comparisons aren't supported in dynamic attributes. Define a method like 'isFooSelected' that returns the boolean, then reference it as '$isFooSelected'.",
|
|
12854
12857
|
logical: "Logical operators aren't supported in dynamic attributes. Combine the conditions in a method on the component and reference it as '$methodName'.",
|
|
12855
12858
|
"call-with-args": "Method calls with arguments aren't supported here. Reference a no-arg method ('$methodName') and read what you need from component state, or split into per-case methods.",
|
|
@@ -13377,11 +13380,11 @@ function checkConsistentAttrs(lx, Comp, referencedAlters, referencedDynamics) {
|
|
|
13377
13380
|
const iter = node.iterInfo;
|
|
13378
13381
|
if (iter.whenVal)
|
|
13379
13382
|
checkConsistentAttrVal(lx, iter.whenVal, env, false, {
|
|
13380
|
-
originAttr: "<x render-each when>"
|
|
13383
|
+
originAttr: "<x render-each @when>"
|
|
13381
13384
|
});
|
|
13382
13385
|
if (iter.loopWithVal)
|
|
13383
13386
|
checkConsistentAttrVal(lx, iter.loopWithVal, env, false, {
|
|
13384
|
-
originAttr: "<x render-each loop-with>"
|
|
13387
|
+
originAttr: "<x render-each @loop-with>"
|
|
13385
13388
|
});
|
|
13386
13389
|
}
|
|
13387
13390
|
}
|
|
@@ -15048,7 +15051,7 @@ function badValueMessage(info) {
|
|
|
15048
15051
|
case "handler-arg":
|
|
15049
15052
|
return `Cannot parse handler argument ${v}`;
|
|
15050
15053
|
case "macro-var":
|
|
15051
|
-
return `Macro
|
|
15054
|
+
return `Macro parameter '^${info.name}' is not defined`;
|
|
15052
15055
|
default:
|
|
15053
15056
|
return `Cannot parse value ${v}`;
|
|
15054
15057
|
}
|
|
@@ -15128,7 +15131,7 @@ function lintIdToMessage(id, info) {
|
|
|
15128
15131
|
case "ALT_HANDLER_NOT_REFERENCED":
|
|
15129
15132
|
return `Alter handler '${info.name}' is defined but never used — remove it or reference it from @when, @enrich-with, or @loop-with`;
|
|
15130
15133
|
case "DYN_VAL_NOT_DEFINED":
|
|
15131
|
-
return `Dynamic
|
|
15134
|
+
return `Dynamic binding '*${info.name}' is not defined${fmtOriginSuffix(info)}`;
|
|
15132
15135
|
case "DYN_ALIAS_NOT_REFERENCED":
|
|
15133
15136
|
return `Lookup '${info.name}' is defined but never used — remove it or reference it as '*${info.name}' in a view`;
|
|
15134
15137
|
case "PROVIDE_NOT_ADDRESSABLE":
|
|
@@ -15166,9 +15169,9 @@ function lintIdToMessage(id, info) {
|
|
|
15166
15169
|
return `Enrich handler '${info.name}' only copies members of the loop value — read them directly${reads ? ` (${reads})` : ""} and remove the '@enrich-with'`;
|
|
15167
15170
|
}
|
|
15168
15171
|
case "REDUNDANT_TEMPLATE_STRING":
|
|
15169
|
-
return `Redundant template
|
|
15172
|
+
return `Redundant string template — '{${info.simpler}}' should be just '${info.simpler}'${fmtOriginSuffix(info)}`;
|
|
15170
15173
|
case "PLACEHOLDERLESS_TEMPLATE_STRING":
|
|
15171
|
-
return `
|
|
15174
|
+
return `String template has no dynamic parts — use the string literal ${info.literal} instead${fmtOriginSuffix(info)}`;
|
|
15172
15175
|
case "CONSTANT_CONDITION":
|
|
15173
15176
|
return `Condition is the constant literal ${info.literal} — it never changes; reference a field ('.name') or a method ('$name') instead${fmtOriginSuffix(info)}`;
|
|
15174
15177
|
case "UNKNOWN_COMPONENT_SPEC_KEY":
|
|
@@ -15291,6 +15294,7 @@ class App {
|
|
|
15291
15294
|
this._compiled = false;
|
|
15292
15295
|
this._renderOpts = { document: rootNode.ownerDocument };
|
|
15293
15296
|
this._renderState = null;
|
|
15297
|
+
this.rootViewName = null;
|
|
15294
15298
|
}
|
|
15295
15299
|
get state() {
|
|
15296
15300
|
return this.transactor.state;
|
|
@@ -15402,7 +15406,7 @@ class App {
|
|
|
15402
15406
|
const root = this.state.val;
|
|
15403
15407
|
const stack = this.makeStack(root);
|
|
15404
15408
|
const { renderer, rootNode, _renderOpts, _renderState } = this;
|
|
15405
|
-
const newState = render(renderer.renderRoot(stack, root), rootNode, _renderOpts, _renderState);
|
|
15409
|
+
const newState = render(renderer.renderRoot(stack, root, this.rootViewName), rootNode, _renderOpts, _renderState);
|
|
15406
15410
|
this._renderState = newState;
|
|
15407
15411
|
return newState.dom;
|
|
15408
15412
|
}
|