nodality 1.3.10 → 1.3.12
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/animator.cjs.js +1 -1
- package/dist/animator.cjs.js.LICENSE.txt +1 -1
- package/dist/animator.esm.js +1 -1
- package/dist/animator.esm.js.LICENSE.txt +1 -1
- package/dist/audionew.cjs.js +1 -1
- package/dist/audionew.cjs.js.LICENSE.txt +1 -1
- package/dist/audionew.esm.js +1 -1
- package/dist/audionew.esm.js.LICENSE.txt +1 -1
- package/dist/base.cjs.js +1 -1
- package/dist/base.cjs.js.LICENSE.txt +1 -1
- package/dist/base.esm.js +1 -1
- package/dist/base.esm.js.LICENSE.txt +1 -1
- package/dist/beta-desktop-bar.cjs.js +1 -1
- package/dist/beta-desktop-bar.cjs.js.LICENSE.txt +1 -1
- package/dist/beta-desktop-bar.esm.js +1 -1
- package/dist/beta-desktop-bar.esm.js.LICENSE.txt +1 -1
- package/dist/beta-mobile-bar.cjs.js +1 -1
- package/dist/beta-mobile-bar.cjs.js.LICENSE.txt +1 -1
- package/dist/beta-mobile-bar.esm.js +1 -1
- package/dist/beta-mobile-bar.esm.js.LICENSE.txt +1 -1
- package/dist/bundle.umd.js +1 -1
- package/dist/bundle.umd.js.LICENSE.txt +1 -1
- package/dist/button.cjs.js +1 -1
- package/dist/button.cjs.js.LICENSE.txt +1 -1
- package/dist/button.esm.js +1 -1
- package/dist/button.esm.js.LICENSE.txt +1 -1
- package/dist/card-getter.cjs.js.LICENSE.txt +1 -1
- package/dist/card-getter.esm.js.LICENSE.txt +1 -1
- package/dist/center.cjs.js +1 -1
- package/dist/center.cjs.js.LICENSE.txt +1 -1
- package/dist/center.esm.js +1 -1
- package/dist/center.esm.js.LICENSE.txt +1 -1
- package/dist/checkbox.cjs.js +1 -1
- package/dist/checkbox.cjs.js.LICENSE.txt +1 -1
- package/dist/checkbox.esm.js +1 -1
- package/dist/checkbox.esm.js.LICENSE.txt +1 -1
- package/dist/code.cjs.js +1 -1
- package/dist/code.cjs.js.LICENSE.txt +1 -1
- package/dist/code.esm.js +1 -1
- package/dist/code.esm.js.LICENSE.txt +1 -1
- package/dist/container.cjs.js +1 -1
- package/dist/container.cjs.js.LICENSE.txt +1 -1
- package/dist/container.esm.js +1 -1
- package/dist/container.esm.js.LICENSE.txt +1 -1
- package/dist/data-list.cjs.js.LICENSE.txt +1 -1
- package/dist/data-list.esm.js.LICENSE.txt +1 -1
- package/dist/designer.cjs.js +1 -1
- package/dist/designer.cjs.js.LICENSE.txt +1 -1
- package/dist/designer.esm.js +1 -1
- package/dist/designer.esm.js.LICENSE.txt +1 -1
- package/dist/element-mapper.cjs.js +1 -1
- package/dist/element-mapper.cjs.js.LICENSE.txt +1 -1
- package/dist/element-mapper.esm.js +1 -1
- package/dist/element-mapper.esm.js.LICENSE.txt +1 -1
- package/dist/finalresult.esm.js +1 -1
- package/dist/finalresult.esm.js.LICENSE.txt +1 -1
- package/dist/flex-card.cjs.js +1 -1
- package/dist/flex-card.cjs.js.LICENSE.txt +1 -1
- package/dist/flex-card.esm.js +1 -1
- package/dist/flex-card.esm.js.LICENSE.txt +1 -1
- package/dist/flex-grid.cjs.js +1 -1
- package/dist/flex-grid.cjs.js.LICENSE.txt +1 -1
- package/dist/flex-grid.esm.js +1 -1
- package/dist/flex-grid.esm.js.LICENSE.txt +1 -1
- package/dist/flex-row.cjs.js +1 -1
- package/dist/flex-row.cjs.js.LICENSE.txt +1 -1
- package/dist/flex-row.esm.js +1 -1
- package/dist/flex-row.esm.js.LICENSE.txt +1 -1
- package/dist/floating-input.cjs.js +1 -1
- package/dist/floating-input.cjs.js.LICENSE.txt +1 -1
- package/dist/floating-input.esm.js +1 -1
- package/dist/floating-input.esm.js.LICENSE.txt +1 -1
- package/dist/free.cjs.js +1 -1
- package/dist/free.cjs.js.LICENSE.txt +1 -1
- package/dist/free.esm.js +1 -1
- package/dist/free.esm.js.LICENSE.txt +1 -1
- package/dist/horizontal-scroller.cjs.js.LICENSE.txt +1 -1
- package/dist/horizontal-scroller.esm.js.LICENSE.txt +1 -1
- package/dist/image-picker.cjs.js +1 -1
- package/dist/image-picker.cjs.js.LICENSE.txt +1 -1
- package/dist/image-picker.esm.js +1 -1
- package/dist/image-picker.esm.js.LICENSE.txt +1 -1
- package/dist/image.cjs.js +1 -1
- package/dist/image.cjs.js.LICENSE.txt +1 -1
- package/dist/image.esm.js +1 -1
- package/dist/image.esm.js.LICENSE.txt +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.LICENSE.txt +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.LICENSE.txt +1 -1
- package/dist/keyframe-animation.cjs.js.LICENSE.txt +1 -1
- package/dist/keyframe-animation.esm.js.LICENSE.txt +1 -1
- package/dist/link-getter.cjs.js +1 -1
- package/dist/link-getter.cjs.js.LICENSE.txt +1 -1
- package/dist/link-getter.esm.js +1 -1
- package/dist/link-getter.esm.js.LICENSE.txt +1 -1
- package/dist/link.cjs.js +1 -1
- package/dist/link.cjs.js.LICENSE.txt +1 -1
- package/dist/link.esm.js +1 -1
- package/dist/link.esm.js.LICENSE.txt +1 -1
- package/dist/meta-adder.cjs.js +1 -1
- package/dist/meta-adder.cjs.js.LICENSE.txt +1 -1
- package/dist/meta-adder.esm.js +1 -1
- package/dist/meta-adder.esm.js.LICENSE.txt +1 -1
- package/dist/modal-2025.cjs.js +1 -1
- package/dist/modal-2025.cjs.js.LICENSE.txt +1 -1
- package/dist/modal-2025.esm.js +1 -1
- package/dist/modal-2025.esm.js.LICENSE.txt +1 -1
- package/dist/multiswitcher.cjs.js +1 -1
- package/dist/multiswitcher.cjs.js.LICENSE.txt +1 -1
- package/dist/multiswitcher.esm.js +1 -1
- package/dist/multiswitcher.esm.js.LICENSE.txt +1 -1
- package/dist/new-nav-bar.cjs.js +1 -1
- package/dist/new-nav-bar.cjs.js.LICENSE.txt +1 -1
- package/dist/new-nav-bar.esm.js +1 -1
- package/dist/new-nav-bar.esm.js.LICENSE.txt +1 -1
- package/dist/picker.cjs.js +1 -1
- package/dist/picker.cjs.js.LICENSE.txt +1 -1
- package/dist/picker.esm.js +1 -1
- package/dist/picker.esm.js.LICENSE.txt +1 -1
- package/dist/progress.cjs.js +1 -1
- package/dist/progress.cjs.js.LICENSE.txt +1 -1
- package/dist/progress.esm.js +1 -1
- package/dist/progress.esm.js.LICENSE.txt +1 -1
- package/dist/radio.cjs.js +1 -1
- package/dist/radio.cjs.js.LICENSE.txt +1 -1
- package/dist/radio.esm.js +1 -1
- package/dist/radio.esm.js.LICENSE.txt +1 -1
- package/dist/range.cjs.js.LICENSE.txt +1 -1
- package/dist/range.esm.js.LICENSE.txt +1 -1
- package/dist/scroll-video.cjs.js.LICENSE.txt +1 -1
- package/dist/scroll-video.esm.js.LICENSE.txt +1 -1
- package/dist/side-bar.cjs.js +1 -1
- package/dist/side-bar.cjs.js.LICENSE.txt +1 -1
- package/dist/side-bar.esm.js +1 -1
- package/dist/side-bar.esm.js.LICENSE.txt +1 -1
- package/dist/side-nav-bar.cjs.js +1 -1
- package/dist/side-nav-bar.cjs.js.LICENSE.txt +1 -1
- package/dist/side-nav-bar.esm.js +1 -1
- package/dist/side-nav-bar.esm.js.LICENSE.txt +1 -1
- package/dist/simple-bar.cjs.js +1 -1
- package/dist/simple-bar.cjs.js.LICENSE.txt +1 -1
- package/dist/simple-bar.esm.js +1 -1
- package/dist/simple-bar.esm.js.LICENSE.txt +1 -1
- package/dist/slider-2025.cjs.js +1 -1
- package/dist/slider-2025.cjs.js.LICENSE.txt +1 -1
- package/dist/slider-2025.esm.js +1 -1
- package/dist/slider-2025.esm.js.LICENSE.txt +1 -1
- package/dist/spacer.cjs.js +1 -1
- package/dist/spacer.cjs.js.LICENSE.txt +1 -1
- package/dist/spacer.esm.js +1 -1
- package/dist/spacer.esm.js.LICENSE.txt +1 -1
- package/dist/stack.cjs.js +1 -1
- package/dist/stack.cjs.js.LICENSE.txt +1 -1
- package/dist/stack.esm.js +1 -1
- package/dist/stack.esm.js.LICENSE.txt +1 -1
- package/dist/stacker.cjs.js.LICENSE.txt +1 -1
- package/dist/stacker.esm.js.LICENSE.txt +1 -1
- package/dist/table.cjs.js +1 -1
- package/dist/table.cjs.js.LICENSE.txt +1 -1
- package/dist/table.esm.js +1 -1
- package/dist/table.esm.js.LICENSE.txt +1 -1
- package/dist/text-field.cjs.js +1 -1
- package/dist/text-field.cjs.js.LICENSE.txt +1 -1
- package/dist/text-field.esm.js +1 -1
- package/dist/text-field.esm.js.LICENSE.txt +1 -1
- package/dist/text.cjs.js +1 -1
- package/dist/text.cjs.js.LICENSE.txt +1 -1
- package/dist/text.esm.js +1 -1
- package/dist/text.esm.js.LICENSE.txt +1 -1
- package/dist/theme.cjs.js.LICENSE.txt +1 -1
- package/dist/theme.esm.js.LICENSE.txt +1 -1
- package/dist/transform-anim.cjs.js.LICENSE.txt +1 -1
- package/dist/transform-anim.esm.js.LICENSE.txt +1 -1
- package/dist/ulist.cjs.js +1 -1
- package/dist/ulist.cjs.js.LICENSE.txt +1 -1
- package/dist/ulist.esm.js +1 -1
- package/dist/ulist.esm.js.LICENSE.txt +1 -1
- package/dist/video.cjs.js +1 -1
- package/dist/video.cjs.js.LICENSE.txt +1 -1
- package/dist/video.esm.js +1 -1
- package/dist/video.esm.js.LICENSE.txt +1 -1
- package/dist/wrap.cjs.js +1 -1
- package/dist/wrap.cjs.js.LICENSE.txt +1 -1
- package/dist/wrap.esm.js +1 -1
- package/dist/wrap.esm.js.LICENSE.txt +1 -1
- package/dist/zoom-card.cjs.js +1 -1
- package/dist/zoom-card.cjs.js.LICENSE.txt +1 -1
- package/dist/zoom-card.esm.js +1 -1
- package/dist/zoom-card.esm.js.LICENSE.txt +1 -1
- package/layout/animator.js +85 -23
- package/layout/audio.js +21 -1
- package/layout/audionew.js +1 -1
- package/layout/base.js +1 -1
- package/layout/beta-desktop-bar.js +1 -1
- package/layout/beta-mobile-bar.js +1 -1
- package/layout/button.js +4 -2
- package/layout/center.js +1 -1
- package/layout/checkbox.js +1 -1
- package/layout/circle.js +1 -1
- package/layout/code.js +2 -2
- package/layout/container.js +2 -2
- package/layout/dropdown-2025.js +1 -1
- package/layout/flex-card.js +1 -1
- package/layout/flex-grid.js +1 -1
- package/layout/flex-row.js +1 -1
- package/layout/form-components/custom.js +1 -1
- package/layout/form-components/data-list.js +1 -1
- package/layout/form-components/floating-input.js +1 -1
- package/layout/form-components/form-all.js +1 -1
- package/layout/form-components/form.js +1 -1
- package/layout/form-components/image-picker.js +1 -1
- package/layout/form-components/picker.js +1 -1
- package/layout/form-components/radio.js +6 -1
- package/layout/form-components/radiogroup.js +1 -1
- package/layout/form-components/range.js +1 -1
- package/layout/free.js +1 -1
- package/layout/grid-overlay.js +19 -19
- package/layout/grid-switcher.js +3 -1
- package/layout/grid.js +1 -1
- package/layout/horizontal-scroller.js +1 -1
- package/layout/image.js +2 -2
- package/layout/index.js +1 -1
- package/layout/link.js +1 -1
- package/layout/list.js +1 -1
- package/layout/meta-adder.js +1 -1
- package/layout/modal-2025.js +1 -1
- package/layout/morph.js +1 -1
- package/layout/multiswitcher.js +6 -2
- package/layout/nav-bar.js +1 -1
- package/layout/nav-factor/custom-div.js +1 -1
- package/layout/new-nav-bar.js +1 -1
- package/layout/polygon.js +1 -1
- package/layout/prerender-site.js +1 -1
- package/layout/prerender.js +1 -1
- package/layout/progress.js +1 -1
- package/layout/row.js +1 -1
- package/layout/scroll-video.js +1 -1
- package/layout/side-bar.js +1 -1
- package/layout/side-nav-bar.js +6 -1
- package/layout/simple-bar.js +1 -1
- package/layout/slider-2025.js +1 -1
- package/layout/spacer.js +1 -1
- package/layout/stack.js +5 -2
- package/layout/svg.js +1 -1
- package/layout/switcher.js +1 -1
- package/layout/table.js +1 -1
- package/layout/text-field.js +1 -1
- package/layout/text.js +1 -1
- package/layout/ulist.js +1 -1
- package/layout/video.js +22 -22
- package/layout/wrap.js +1 -1
- package/layout/zoom-card.js +1 -1
- package/lib/agent-surface.js +1 -1
- package/lib/card-getter.js +1 -1
- package/lib/check-page.js +1 -1
- package/lib/codegen.js +1 -1
- package/lib/data.js +1 -1
- package/lib/designer.js +1 -1
- package/lib/element-mapper.js +103 -15
- package/lib/element-params.generated.js +13 -2
- package/lib/grid-geometry.js +1 -1
- package/lib/keyframe-animation.js +1 -1
- package/lib/link-getter.js +1 -1
- package/lib/morph-node.js +1 -1
- package/lib/normalize-spec.js +1 -1
- package/lib/parse-html.js +1 -1
- package/lib/raster-inspect.js +1 -1
- package/lib/raster-ops.js +1 -1
- package/lib/raster-presets.js +1 -1
- package/lib/resolve-refs.js +1 -1
- package/lib/scroll-video.js +1 -1
- package/lib/seo.js +1 -1
- package/lib/stacker.js +1 -1
- package/lib/suggest.js +1 -1
- package/lib/theme.js +1 -1
- package/lib/transform-anim.js +1 -1
- package/lib/transition.js +1 -1
- package/lib/validate-nodes.js +146 -4
- package/lib/webmcp-adapter.js +1 -1
- package/package.json +1 -1
- package/schema.json +1391 -2263
- package/scripts/generate-schema.mjs +135 -15
- package/skills/nodality/SKILL.md +15 -0
|
@@ -84,19 +84,69 @@ const DISPATCH = {};
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
// ── 3. mapper method -> component classes it constructs ──────────────
|
|
87
|
+
// Braces are counted over CODE only: a `}` inside a string or a comment is
|
|
88
|
+
// text, not structure.
|
|
89
|
+
//
|
|
90
|
+
// The previous version counted them in `mapperLive`, which strips comments by
|
|
91
|
+
// deleting everything after `//` on a line — and that also truncates any line
|
|
92
|
+
// holding a URL, taking its closing brace with it. Four mapper bodies
|
|
93
|
+
// (`dropdown`, `protoNav`, `sideNav`, `gridItemsSource`) therefore never
|
|
94
|
+
// balanced and ran on to the end of the class, so every method BELOW them was
|
|
95
|
+
// read as part of them. The visible cost was a new mapper's parameters being
|
|
96
|
+
// credited to `cards`, `nav`, `sideNav` and `dropdown` as well as to its own
|
|
97
|
+
// type; the invisible cost was that reachability (below) could not be
|
|
98
|
+
// determined at all, because an overrunning body picks up someone else's
|
|
99
|
+
// `elOpts()` call.
|
|
87
100
|
function methodBody(name) {
|
|
88
|
-
const at =
|
|
101
|
+
const at = src.indexOf(`static ${name}(`);
|
|
89
102
|
if (at < 0) return "";
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
103
|
+
const start = src.indexOf("{", at);
|
|
104
|
+
if (start < 0) return "";
|
|
105
|
+
|
|
106
|
+
let depth = 0;
|
|
107
|
+
// "code" | "line" | "block" | "'" | '"' | "`"
|
|
108
|
+
let state = "code";
|
|
109
|
+
// Template literals nest: `${ ... `inner` ... }` returns to code inside ${}.
|
|
110
|
+
const stack = [];
|
|
111
|
+
|
|
112
|
+
for (let j = start; j < src.length; j++) {
|
|
113
|
+
const c = src[j], next = src[j + 1];
|
|
114
|
+
if (state === "line") { if (c === "\n") state = "code"; continue; }
|
|
115
|
+
if (state === "block") { if (c === "*" && next === "/") { state = "code"; j++; } continue; }
|
|
116
|
+
if (state === "'" || state === '"') {
|
|
117
|
+
if (c === "\\") { j++; continue; }
|
|
118
|
+
if (c === state) state = "code";
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (state === "`") {
|
|
122
|
+
if (c === "\\") { j++; continue; }
|
|
123
|
+
if (c === "`") { state = "code"; continue; }
|
|
124
|
+
if (c === "$" && next === "{") { stack.push("`"); state = "code"; depth++; j++; }
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
// code
|
|
128
|
+
if (c === "/" && next === "/") { state = "line"; j++; continue; }
|
|
129
|
+
if (c === "/" && next === "*") { state = "block"; j++; continue; }
|
|
130
|
+
if (c === "'" || c === '"' || c === "`") { state = c; continue; }
|
|
131
|
+
if (c === "{") { depth++; continue; }
|
|
132
|
+
if (c === "}") {
|
|
133
|
+
depth--;
|
|
134
|
+
if (stack.length && depth === stack.length) { stack.pop(); state = "`"; continue; }
|
|
135
|
+
if (!depth) return src.slice(start, j);
|
|
136
|
+
}
|
|
95
137
|
}
|
|
96
|
-
return
|
|
138
|
+
return src.slice(start);
|
|
97
139
|
}
|
|
140
|
+
// `new X(` in a mapper is not always a component: the bodies legitimately
|
|
141
|
+
// build an Error to reject bad input and a Set to de-duplicate. Reporting
|
|
142
|
+
// those as the type's components told an agent that a page uses a component
|
|
143
|
+
// called `Error`.
|
|
144
|
+
const BUILTINS = new Set(["Error", "TypeError", "RangeError", "Set", "Map",
|
|
145
|
+
"WeakMap", "WeakSet", "Date", "RegExp", "Promise", "Array", "Object",
|
|
146
|
+
"Function", "Number", "String", "Boolean", "Intl", "URL", "URLSearchParams"]);
|
|
98
147
|
const classesIn = (body) =>
|
|
99
|
-
[...new Set([...body.matchAll(/new\s+([A-Z][A-Za-z0-9]*)\s*\(/g)].map((m) => m[1]))]
|
|
148
|
+
[...new Set([...body.matchAll(/new\s+([A-Z][A-Za-z0-9]*)\s*\(/g)].map((m) => m[1]))]
|
|
149
|
+
.filter((name) => !BUILTINS.has(name));
|
|
100
150
|
|
|
101
151
|
// ── 4. class -> file, from element-mapper's own imports ──────────────
|
|
102
152
|
const IMPORTS = {};
|
|
@@ -155,6 +205,12 @@ function paramsOf(file) {
|
|
|
155
205
|
// component, decides what the slot means.
|
|
156
206
|
const DESC_BY_FILE = new Map(), DEPR_BY_FILE = new Map();
|
|
157
207
|
const DESC_BY_TYPE = {};
|
|
208
|
+
// Declared value SHAPES, written as `//@ name {unit}: text`. Without them the
|
|
209
|
+
// validator can only check that a parameter name exists — never that `mar: 7`
|
|
210
|
+
// or `columns: "four"` is a value the component can use. The raster op
|
|
211
|
+
// registry has carried units since it was written; element parameters had
|
|
212
|
+
// none, which is why a spec full of unusable values validated clean.
|
|
213
|
+
const UNIT_BY_TYPE = {}, UNIT_BY_FILE = new Map(), UNIT_GLOBAL = {};
|
|
158
214
|
// Names whose meaning depends on the element type, declared with `//@scoped
|
|
159
215
|
// <name>`. For these a description is never BORROWED from a file that does not
|
|
160
216
|
// contribute the parameter: `items` is documented once, in picker.js, and is
|
|
@@ -174,19 +230,27 @@ const DESC_GLOBAL = {}, DEPR_GLOBAL = {};
|
|
|
174
230
|
.map((rel) => join(ROOT, "lib", rel)).filter(existsSync));
|
|
175
231
|
files.add(MAPPER);
|
|
176
232
|
for (const f of files) {
|
|
177
|
-
const desc = {}, depr = {};
|
|
233
|
+
const desc = {}, depr = {}, units = {};
|
|
178
234
|
for (const line of readFileSync(f, "utf8").split("\n")) {
|
|
179
235
|
let m = line.match(/\/\/@scoped\s+([a-zA-Z][a-zA-Z0-9]*)\s*$/);
|
|
180
236
|
if (m) { SCOPED.add(m[1]); continue; }
|
|
181
237
|
m = line.match(/\/\/@deprecated\s+([a-zA-Z][a-zA-Z0-9]*)\s*:\s*(.+)$/);
|
|
182
238
|
if (m) { depr[m[1]] ??= m[2].trim(); DEPR_GLOBAL[m[1]] ??= m[2].trim(); continue; }
|
|
183
239
|
// Qualified first: `//@ cards.items: …` binds to one type only.
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
240
|
+
// An optional `{unit}` before the colon declares the value's shape.
|
|
241
|
+
m = line.match(/\/\/@\s+([a-zA-Z][a-zA-Z0-9]*)\.([a-zA-Z][a-zA-Z0-9]*)\s*(?:\{([^}]+)\})?\s*:\s*(.+)$/);
|
|
242
|
+
if (m) {
|
|
243
|
+
(DESC_BY_TYPE[m[1]] ??= {})[m[2]] ??= m[4].trim();
|
|
244
|
+
if (m[3]) (UNIT_BY_TYPE[m[1]] ??= {})[m[2]] ??= m[3].trim();
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
m = line.match(/\/\/@\s+([a-zA-Z][a-zA-Z0-9]*)\s*(?:\{([^}]+)\})?\s*:\s*(.+)$/);
|
|
248
|
+
if (m) {
|
|
249
|
+
desc[m[1]] ??= m[3].trim(); DESC_GLOBAL[m[1]] ??= m[3].trim();
|
|
250
|
+
if (m[2]) { units[m[1]] ??= m[2].trim(); UNIT_GLOBAL[m[1]] ??= m[2].trim(); }
|
|
251
|
+
}
|
|
188
252
|
}
|
|
189
|
-
DESC_BY_FILE.set(f, desc); DEPR_BY_FILE.set(f, depr);
|
|
253
|
+
DESC_BY_FILE.set(f, desc); DEPR_BY_FILE.set(f, depr); UNIT_BY_FILE.set(f, units);
|
|
190
254
|
}
|
|
191
255
|
}
|
|
192
256
|
|
|
@@ -246,6 +310,38 @@ for (const type of TYPES) {
|
|
|
246
310
|
// image.js, and an annotation living in any other file would have been
|
|
247
311
|
// dropped -- which cost `img.alt` its description on the first attempt at
|
|
248
312
|
// this fix, the one parameter the comment in paramsOf() exists to protect.
|
|
313
|
+
// Can a spec actually REACH the parameters its components read?
|
|
314
|
+
//
|
|
315
|
+
// Only if the mapper hands the element's options over — `elOpts(el)` or a
|
|
316
|
+
// spread. A mapper that builds its own options object instead (mapTable is
|
|
317
|
+
// the plain case: it hardcodes cellPadding, cellAlign, style.font and
|
|
318
|
+
// headStyle and never looks at the element) leaves every one of those names
|
|
319
|
+
// unreachable, however faithfully the component reads them. Listing them
|
|
320
|
+
// anyway is the schema's worst failure mode, because the validator then
|
|
321
|
+
// vouches for a spec that cannot work: 43% of all declared parameters were
|
|
322
|
+
// in that state when this was written.
|
|
323
|
+
//
|
|
324
|
+
// Where nothing is forwarded, the type is credited only with what the mapper
|
|
325
|
+
// itself reads off the element, plus the two every mapper handles.
|
|
326
|
+
// Three ways a mapper hands the element over: `elOpts(el)`, a spread, or
|
|
327
|
+
// taking the element object ITSELF as the options object and passing it on
|
|
328
|
+
// (`let re = obj.el; re["url"] = …; new Link().set(re)` — how mapLink
|
|
329
|
+
// works). The third forwards just as completely and looks nothing like the
|
|
330
|
+
// other two.
|
|
331
|
+
//
|
|
332
|
+
// Aliasing alone is not forwarding: almost every mapper opens with
|
|
333
|
+
// `let el = obj.el` to read from it, and mapTable does exactly that while
|
|
334
|
+
// building its own options object from literals. What distinguishes the two
|
|
335
|
+
// is whether the alias is handed to a component.
|
|
336
|
+
const aliases = [...body.matchAll(/(?:let|const|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:obj\.)?el\s*[;,]/g)]
|
|
337
|
+
.map((m) => m[1]);
|
|
338
|
+
const passesAlias = aliases.some((a) =>
|
|
339
|
+
new RegExp(`\\.set\\(\\s*${a}\\s*[,)]|new\\s+[A-Z]\\w*\\(\\s*${a}\\s*[,)]`).test(body));
|
|
340
|
+
const forwardsElement = /\belOpts\s*\(|\.\.\.el\b/.test(body) || passesAlias;
|
|
341
|
+
const mapperReads = new Set([...body.matchAll(/\bel\.([a-zA-Z][a-zA-Z0-9]*)/g)].map((m) => m[1]));
|
|
342
|
+
mapperReads.add("type"); mapperReads.add("id");
|
|
343
|
+
const reachable = (name) => forwardsElement || mapperReads.has(name);
|
|
344
|
+
|
|
249
345
|
const params = new Set();
|
|
250
346
|
const from = new Map();
|
|
251
347
|
const contributed = (name, file) => {
|
|
@@ -253,7 +349,7 @@ for (const type of TYPES) {
|
|
|
253
349
|
if (!from.has(name)) from.set(name, []);
|
|
254
350
|
if (!from.get(name).includes(file)) from.get(name).push(file);
|
|
255
351
|
};
|
|
256
|
-
for (const f of files) for (const p of paramsOf(f)) contributed(p, f);
|
|
352
|
+
for (const f of files) for (const p of paramsOf(f)) if (reachable(p)) contributed(p, f);
|
|
257
353
|
|
|
258
354
|
// The mapper's OWN `el.<name>` reads. Scanning components alone missed
|
|
259
355
|
// these, because a mapper often passes an element field as a constructor
|
|
@@ -278,13 +374,20 @@ for (const type of TYPES) {
|
|
|
278
374
|
schema.types[type] = {
|
|
279
375
|
resolved: ok,
|
|
280
376
|
mapper: method,
|
|
377
|
+
// Whether the mapper hands the element's options to its components. When
|
|
378
|
+
// false, this type's vocabulary is only what the mapper reads by name.
|
|
379
|
+
forwardsElement,
|
|
281
380
|
components: classes,
|
|
282
381
|
params: [...params].sort().map((name) => {
|
|
283
382
|
const where = from.get(name) || [];
|
|
284
383
|
const description = annotationFor(DESC_BY_FILE, name, where, type, true);
|
|
285
384
|
const deprecated = annotationFor(DEPR_BY_FILE, name, where, type, false);
|
|
385
|
+
const unit = (UNIT_BY_TYPE[type] && UNIT_BY_TYPE[type][name])
|
|
386
|
+
|| where.map((f) => (UNIT_BY_FILE.get(f) || {})[name]).find(Boolean)
|
|
387
|
+
|| UNIT_GLOBAL[name];
|
|
286
388
|
return {
|
|
287
389
|
name,
|
|
390
|
+
...(unit ? { unit } : {}),
|
|
288
391
|
...(description ? { description } : {}),
|
|
289
392
|
...(deprecated ? { deprecated } : {}),
|
|
290
393
|
};
|
|
@@ -335,11 +438,28 @@ if (args.includes("--stdout")) { process.stdout.write(json); process.exit(0); }
|
|
|
335
438
|
const union = new Set();
|
|
336
439
|
for (const t of Object.values(schema.types)) for (const p of t.params) union.add(p.name);
|
|
337
440
|
const names = [...union].sort();
|
|
441
|
+
const byType = {};
|
|
442
|
+
const units = {};
|
|
443
|
+
for (const [type, t] of Object.entries(schema.types)) {
|
|
444
|
+
byType[type] = t.params.map((p) => p.name);
|
|
445
|
+
for (const p of t.params) if (p.unit) units[`${type}.${p.name}`] = p.unit;
|
|
446
|
+
}
|
|
338
447
|
const mod = `// GENERATED by scripts/generate-schema.mjs — do not edit.
|
|
339
448
|
// Every parameter name any element type reads, recovered from source.
|
|
340
449
|
// Used for near-miss typo detection in validate-nodes.js. A drift test
|
|
341
450
|
// regenerates this and fails if it differs, so it cannot rot silently.
|
|
342
451
|
export const ELEMENT_PARAM_NAMES = ${JSON.stringify(names)};
|
|
452
|
+
|
|
453
|
+
// Per-type vocabulary. The union above answers "is this a real parameter
|
|
454
|
+
// anywhere"; this answers "does THIS type accept it", which is what a page
|
|
455
|
+
// actually needs to know — writing \`keySet\` on a \`table\` is a name the
|
|
456
|
+
// union knows and the type ignores.
|
|
457
|
+
export const ELEMENT_PARAMS_BY_TYPE = ${JSON.stringify(byType)};
|
|
458
|
+
|
|
459
|
+
// Declared value shapes, keyed "type.param". Only parameters whose source
|
|
460
|
+
// carries a \`//@ name {unit}:\` annotation appear, so this grows as the
|
|
461
|
+
// library documents itself and never claims a shape it was not told.
|
|
462
|
+
export const ELEMENT_PARAM_UNITS = ${JSON.stringify(units)};
|
|
343
463
|
`;
|
|
344
464
|
writeFileSync(join(ROOT, "lib", "element-params.generated.js"), mod);
|
|
345
465
|
}
|
package/skills/nodality/SKILL.md
CHANGED
|
@@ -68,6 +68,21 @@ Skipping step 3 is the classic failure: a misspelled op renders
|
|
|
68
68
|
*nothing* rather than erroring, so the page looks plausible and is
|
|
69
69
|
silently missing its effects.
|
|
70
70
|
|
|
71
|
+
**Read `warnings`, not just `errors`.** The report has both. `errors`
|
|
72
|
+
mean the page will not render as a page; `warnings` mean it renders and
|
|
73
|
+
does not do what you wrote:
|
|
74
|
+
|
|
75
|
+
| code | what it means |
|
|
76
|
+
|---|---|
|
|
77
|
+
| `BAD_PARAM_VALUE` | the value is not the shape the parameter takes — `width: 7` is not a CSS length, `mar: 7` is not `[{a: 7}]`. As an *error* where the value is provably inert; as a warning otherwise. |
|
|
78
|
+
| `PARAM_NOT_ON_TYPE` | a real parameter name that THIS type does not read, so it is accepted and dropped — `keySet` on a `table`. |
|
|
79
|
+
|
|
80
|
+
`get_schema <type>` carries a `unit` on every parameter whose shape the
|
|
81
|
+
source declares, which is where those checks come from, and a
|
|
82
|
+
`forwardsElement` flag: when it is false the type's vocabulary is only
|
|
83
|
+
what its mapper reads by name, because the mapper builds its own options
|
|
84
|
+
rather than passing yours on.
|
|
85
|
+
|
|
71
86
|
## HTML's names are accepted
|
|
72
87
|
|
|
73
88
|
`src` and `href` both mean `url`; `options` means `items`. Write either — the
|