11import { ESLintUtils , TSESLint , TSESTree } from "@typescript-eslint/utils" ;
22
33import type { WonderBlocksPluginDocs } from "../types" ;
4+ import {
5+ getAttributeStringValue ,
6+ INLINE_BODY_TEXT_TAGS ,
7+ WB_BUTTON_COMPONENTS ,
8+ WB_FORM_COMPONENTS ,
9+ BLOCK_CONTAINER_TAGS ,
10+ WB_HEADING_COMPONENTS ,
11+ HTML_HEADING_ELEMENTS ,
12+ } from "./jsx-utils" ;
413
514const createRule = ESLintUtils . RuleCreator < WonderBlocksPluginDocs > (
615 ( name ) =>
@@ -16,119 +25,6 @@ type MessageIds =
1625 | "nestedInBodyText"
1726 | "nestedInHeading" ;
1827
19- /**
20- * Inline tags that make BodyText safe to nest inside inline/interactive
21- * contexts (e.g., inside a button, label, or paragraph element).
22- */
23- const INLINE_BODY_TEXT_TAGS = new Set ( [
24- "span" ,
25- "sup" ,
26- "sub" ,
27- "em" ,
28- "strong" ,
29- "a" ,
30- "abbr" ,
31- "code" ,
32- "kbd" ,
33- "mark" ,
34- "cite" ,
35- "q" ,
36- "s" ,
37- "u" ,
38- "b" ,
39- "i" ,
40- "small" ,
41- "del" ,
42- "ins" ,
43- "time" ,
44- "var" ,
45- "samp" ,
46- "dfn" ,
47- ] ) ;
48-
49- /**
50- * Block-level container tags. When an outer BodyText uses one of these, it
51- * can contain an inner BodyText that renders as <p> without creating invalid
52- * HTML nesting.
53- */
54- const BLOCK_CONTAINER_TAGS = new Set ( [
55- "div" ,
56- "section" ,
57- "article" ,
58- "aside" ,
59- "main" ,
60- "header" ,
61- "footer" ,
62- "nav" ,
63- "blockquote" ,
64- "figure" ,
65- "figcaption" ,
66- "details" ,
67- "summary" ,
68- ] ) ;
69-
70- /**
71- * Wonder Blocks form components that internally render a <label> or similar
72- * inline-context element around their content.
73- */
74- const WB_FORM_COMPONENTS = new Set ( [ "Choice" , "Checkbox" , "Radio" ] ) ;
75-
76- /**
77- * Wonder Blocks Button component names. Buttons are inline contexts and
78- * cannot contain block-level elements like <p>.
79- */
80- const WB_BUTTON_COMPONENTS = new Set ( [ "Button" , "ActivityButton" ] ) ;
81-
82- /**
83- * Wonder Blocks Heading component names. Headings are block-level but
84- * cannot contain other block-level elements like <p>.
85- */
86- const WB_HEADING_COMPONENTS = new Set ( [
87- "Heading" ,
88- "HeadingLarge" ,
89- "HeadingMedium" ,
90- "HeadingSmall" ,
91- "HeadingXSmall" ,
92- ] ) ;
93-
94- /**
95- * HTML heading element names.
96- */
97- const HTML_HEADING_ELEMENTS = new Set ( [ "h1" , "h2" , "h3" , "h4" , "h5" , "h6" ] ) ;
98-
99- /**
100- * Returns the string value of a JSX attribute if it's a simple string
101- * literal, otherwise null.
102- */
103- function getAttributeStringValue (
104- openingElement : TSESTree . JSXOpeningElement ,
105- attributeName : string ,
106- ) : string | null {
107- const attr = openingElement . attributes . find (
108- ( a ) : a is TSESTree . JSXAttribute =>
109- a . type === "JSXAttribute" &&
110- a . name . type === "JSXIdentifier" &&
111- ( a . name as TSESTree . JSXIdentifier ) . name === attributeName ,
112- ) ;
113-
114- if ( ! attr ?. value ) {
115- return null ;
116- }
117-
118- if ( attr . value . type === "Literal" ) {
119- return String ( attr . value . value ) ;
120- }
121-
122- if (
123- attr . value . type === "JSXExpressionContainer" &&
124- attr . value . expression . type === "Literal"
125- ) {
126- return String ( ( attr . value . expression as TSESTree . Literal ) . value ) ;
127- }
128-
129- return null ;
130- }
131-
13228/**
13329 * Returns true if this BodyText uses an inline `tag` prop, making it safe
13430 * to place inside any of the restricted parent elements.
0 commit comments