@webround/editor 0.3.13 → 0.3.14

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.
@@ -225,7 +225,7 @@ function fromJsx(el, config, file, options) {
225
225
  const children = el.isKind(SyntaxKind.JsxElement) ? nodesUnder(el, config, file, options) : [];
226
226
  // Plain components we know nothing about (`<Card>`) are only transparent: what they hold is what counts. A shared one is entered: what it renders is part of the page
227
227
  if (!wrKind && /^[A-Z]/.test(name))
228
- return [...sharedNodesOf(el, name, options), ...children];
228
+ return [...sharedNodesOf(el, name, options), ...children].map(node => ({ ...node, viaComponent: true }));
229
229
  const attrs = {};
230
230
  const alt = inLang(json.alt, options) ?? str(attrOf(el, 'alt'));
231
231
  if (alt !== undefined)
@@ -49,6 +49,35 @@ export const RULES = [
49
49
  report(null, `${problem.message} (${problem.file}:${problem.line})`);
50
50
  },
51
51
  },
52
+ {
53
+ name: 'html-structure',
54
+ severity: 'warning',
55
+ run(page, report) {
56
+ // What a tag may hold directly, and what it must sit in: the HTML content model, for the tags a WrSection can be. A node under a component
57
+ // we do not read (`viaComponent`) is not judged: that component may draw the list around it.
58
+ const OWN_CHILDREN = { ul: ['li'], ol: ['li'], menu: ['li'] };
59
+ const MUST_SIT_IN = { li: ['ul', 'ol', 'menu'], dt: ['dl', 'div'], dd: ['dl', 'div'], figcaption: ['figure'], summary: ['details'] };
60
+ const INTERACTIVE = new Set(['a', 'button']);
61
+ const visit = (node, parent, interactive) => {
62
+ if (!node.viaComponent) {
63
+ const allowed = parent ? OWN_CHILDREN[parent.tag] : undefined;
64
+ if (parent && allowed && !allowed.includes(node.tag))
65
+ report(node, `A <${node.tag}> inside a <${parent.tag}>: a list holds only <${allowed.join('>, <')}>.`);
66
+ const sits = MUST_SIT_IN[node.tag];
67
+ if (parent && sits && !sits.includes(parent.tag) && !parent.children.some(c => c.viaComponent))
68
+ report(node, `A <${node.tag}> must be inside ${sits.map(t => `a <${t}>`).join(' or ')}, not a <${parent.tag}>.`);
69
+ }
70
+ // Links and buttons cannot hold each other: a browser repairs it in a way that breaks the page
71
+ if (INTERACTIVE.has(node.tag) && interactive && !node.attrs['{spread}'])
72
+ report(node, `A <${node.tag}> inside a <${interactive.tag}>: an interactive element cannot hold another.`);
73
+ const next = INTERACTIVE.has(node.tag) ? node : interactive;
74
+ for (const child of node.children)
75
+ visit(child, node, next);
76
+ };
77
+ for (const node of page.nodes)
78
+ visit(node, null, null);
79
+ },
80
+ },
52
81
  {
53
82
  name: 'img-alt',
54
83
  severity: 'warning',
@@ -26,6 +26,8 @@ export interface CheckNode {
26
26
  key: string;
27
27
  side: string;
28
28
  }[];
29
+ /** Only the code producer says it: the node sits under a component we do not read (`<Card>`), so what is around it may be drawn by that component; the nesting rules do not judge it. */
30
+ viaComponent?: true;
29
31
  /** Shown as hidden in the JSON: the checks do not count it (it does not render), an outline says so. */
30
32
  hidden?: true;
31
33
  /** What the element says in each language, for the fields that are translated (`content`, `alt`, `ariaLabel`): field → language key → value. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webround/editor",
3
- "version": "0.3.13",
3
+ "version": "0.3.14",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -75,7 +75,7 @@
75
75
  "@lexical/selection": "^0.51.0",
76
76
  "@lexical/utils": "^0.51.0",
77
77
  "@monaco-editor/react": "^4.7.0",
78
- "@webround/core": "^0.3.13",
78
+ "@webround/core": "^0.3.14",
79
79
  "highlight.js": "^11.12.0",
80
80
  "hls.js": "^1.5.0",
81
81
  "lexical": "^0.51.0",