Skip to content

Commit 57137e6

Browse files
Revert "fix: improve custom element checks in html validation"
This reverts commit 72fab2b.
1 parent 72fab2b commit 57137e6

File tree

8 files changed

+39
-115
lines changed

8 files changed

+39
-115
lines changed

packages/svelte/src/compiler/phases/2-analyze/types.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ export interface AnalysisState {
1010
options: ValidatedCompileOptions;
1111
ast_type: 'instance' | 'template' | 'module';
1212
/**
13-
* The parent element. `null` if the parent is `svelte:element`, `#snippet`, a component or the root.
13+
* Tag name of the parent element. `null` if the parent is `svelte:element`, `#snippet`, a component or the root.
1414
* Parent doesn't necessarily mean direct path predecessor because there could be `#each`, `#if` etc in-between.
1515
*/
16-
parent_element: AST.RegularElement | null;
16+
parent_element: string | null;
1717
has_props_rune: boolean;
1818
/** Which slots the current parent component has */
1919
component_slots: Set<string>;

packages/svelte/src/compiler/phases/2-analyze/visitors/ExpressionTag.js

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
/** @import { Context } from '../types' */
33
import { is_tag_valid_with_parent } from '../../../../html-tree-validation.js';
44
import * as e from '../../../errors.js';
5-
import { is_custom_element_node } from '../../nodes.js';
65
import { mark_subtree_dynamic } from './shared/fragment.js';
76

87
/**
@@ -13,16 +12,7 @@ export function ExpressionTag(node, context) {
1312
const in_template = context.path.at(-1)?.type === 'Fragment';
1413

1514
if (in_template && context.state.parent_element) {
16-
const message = is_tag_valid_with_parent(
17-
{
18-
tag: '#text',
19-
custom_element: false
20-
},
21-
{
22-
tag: context.state.parent_element.name,
23-
custom_element: is_custom_element_node(context.state.parent_element)
24-
}
25-
);
15+
const message = is_tag_valid_with_parent('#text', context.state.parent_element);
2616
if (message) {
2717
e.node_invalid_placement(node, message);
2818
}

packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js

Lines changed: 7 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,7 @@ export function RegularElement(node, context) {
116116
if (context.state.parent_element) {
117117
let past_parent = false;
118118
let only_warn = false;
119-
const ancestors = [
120-
{
121-
tag: context.state.parent_element.name,
122-
custom_element: is_custom_element_node(context.state.parent_element)
123-
}
124-
];
119+
const ancestors = [context.state.parent_element];
125120

126121
for (let i = context.path.length - 1; i >= 0; i--) {
127122
const ancestor = context.path[i];
@@ -137,20 +132,8 @@ export function RegularElement(node, context) {
137132
}
138133

139134
if (!past_parent) {
140-
if (
141-
ancestor.type === 'RegularElement' &&
142-
ancestor.name === context.state.parent_element.name
143-
) {
144-
const message = is_tag_valid_with_parent(
145-
{
146-
tag: node.name,
147-
custom_element: is_custom_element_node(node)
148-
},
149-
{
150-
tag: context.state.parent_element.name,
151-
custom_element: is_custom_element_node(context.state.parent_element)
152-
}
153-
);
135+
if (ancestor.type === 'RegularElement' && ancestor.name === context.state.parent_element) {
136+
const message = is_tag_valid_with_parent(node.name, context.state.parent_element);
154137
if (message) {
155138
if (only_warn) {
156139
w.node_invalid_placement_ssr(node, message);
@@ -162,18 +145,9 @@ export function RegularElement(node, context) {
162145
past_parent = true;
163146
}
164147
} else if (ancestor.type === 'RegularElement') {
165-
ancestors.push({
166-
tag: ancestor.name,
167-
custom_element: is_custom_element_node(ancestor)
168-
});
169-
170-
const message = is_tag_valid_with_ancestor(
171-
{
172-
tag: node.name,
173-
custom_element: is_custom_element_node(node)
174-
},
175-
ancestors
176-
);
148+
ancestors.push(ancestor.name);
149+
150+
const message = is_tag_valid_with_ancestor(node.name, ancestors);
177151
if (message) {
178152
if (only_warn) {
179153
w.node_invalid_placement_ssr(node, message);
@@ -204,7 +178,7 @@ export function RegularElement(node, context) {
204178
w.element_invalid_self_closing_tag(node, node.name);
205179
}
206180

207-
context.next({ ...context.state, parent_element: node });
181+
context.next({ ...context.state, parent_element: node.name });
208182

209183
// Special case: <a> tags are valid in both the SVG and HTML namespace.
210184
// If there's no parent, look downwards to see if it's the parent of a SVG or HTML element.

packages/svelte/src/compiler/phases/2-analyze/visitors/Text.js

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import { is_tag_valid_with_parent } from '../../../../html-tree-validation.js';
44
import { regex_not_whitespace } from '../../patterns.js';
55
import * as e from '../../../errors.js';
6-
import { is_custom_element_node } from '../../nodes.js';
76

87
/**
98
* @param {AST.Text} node
@@ -13,16 +12,7 @@ export function Text(node, context) {
1312
const in_template = context.path.at(-1)?.type === 'Fragment';
1413

1514
if (in_template && context.state.parent_element && regex_not_whitespace.test(node.data)) {
16-
const message = is_tag_valid_with_parent(
17-
{
18-
tag: '#text',
19-
custom_element: false
20-
},
21-
{
22-
tag: context.state.parent_element.name,
23-
custom_element: is_custom_element_node(context.state.parent_element)
24-
}
25-
);
15+
const message = is_tag_valid_with_parent('#text', context.state.parent_element);
2616
if (message) {
2717
e.node_invalid_placement(node, message);
2818
}

packages/svelte/src/compiler/phases/3-transform/server/visitors/RegularElement.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import { is_void } from '../../../../../utils.js';
66
import { dev, locator } from '../../../../state.js';
77
import * as b from '../../../../utils/builders.js';
8-
import { is_custom_element_node } from '../../../nodes.js';
98
import { clean_nodes, determine_namespace_for_children } from '../../utils.js';
109
import { build_element_attributes } from './shared/element.js';
1110
import { process_children, build_template } from './shared/utils.js';
@@ -63,7 +62,6 @@ export function RegularElement(node, context) {
6362
'$.push_element',
6463
b.id('$$payload'),
6564
b.literal(node.name),
66-
b.literal(is_custom_element_node(node)),
6765
b.literal(location.line),
6866
b.literal(location.column)
6967
)

packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteElement.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ export function SvelteElement(node, context) {
4747
'$.push_element',
4848
b.id('$$payload'),
4949
tag,
50-
b.literal(false),
5150
b.literal(location.line),
5251
b.literal(location.column)
5352
)

packages/svelte/src/html-tree-validation.js

Lines changed: 22 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,3 @@
1-
/**
2-
* @typedef {{
3-
* tag: string;
4-
* custom_element: boolean;
5-
* }} Element
6-
*/
7-
81
/**
92
* Map of elements that have certain elements that are not allowed inside them, in the sense that they will auto-close the parent/ancestor element.
103
* Theoretically one could take advantage of it but most of the time it will just result in confusing behavior and break when SSR'd.
@@ -144,33 +137,33 @@ const disallowed_children = {
144137
/**
145138
* Returns an error message if the tag is not allowed inside the ancestor tag (which is grandparent and above) such that it will result
146139
* in the browser repairing the HTML, which will likely result in an error during hydration.
147-
* @param {Element} child_node
148-
* @param {Element[]} ancestors All nodes starting with the parent, up until the ancestor, which means two entries minimum
140+
* @param {string} child_tag
141+
* @param {string[]} ancestors All nodes starting with the parent, up until the ancestor, which means two entries minimum
149142
* @param {string} [child_loc]
150143
* @param {string} [ancestor_loc]
151144
* @returns {string | null}
152145
*/
153-
export function is_tag_valid_with_ancestor(child_node, ancestors, child_loc, ancestor_loc) {
154-
if (child_node.custom_element) return null; // custom elements can be anything
146+
export function is_tag_valid_with_ancestor(child_tag, ancestors, child_loc, ancestor_loc) {
147+
if (child_tag.includes('-')) return null; // custom elements can be anything
155148

156-
const ancestor_tag = ancestors[ancestors.length - 1].tag;
149+
const ancestor_tag = ancestors[ancestors.length - 1];
157150
const disallowed = disallowed_children[ancestor_tag];
158151
if (!disallowed) return null;
159152

160153
if ('reset_by' in disallowed && disallowed.reset_by) {
161154
for (let i = ancestors.length - 2; i >= 0; i--) {
162155
const ancestor = ancestors[i];
163-
if (ancestor.custom_element) return null; // custom elements can be anything
156+
if (ancestor.includes('-')) return null; // custom elements can be anything
164157

165158
// A reset means that forbidden descendants are allowed again
166-
if (disallowed.reset_by.includes(ancestors[i].tag)) {
159+
if (disallowed.reset_by.includes(ancestors[i])) {
167160
return null;
168161
}
169162
}
170163
}
171164

172-
if ('descendant' in disallowed && disallowed.descendant.includes(child_node.tag)) {
173-
const child = child_loc ? `\`<${child_node.tag}>\` (${child_loc})` : `\`<${child_node.tag}>\``;
165+
if ('descendant' in disallowed && disallowed.descendant.includes(child_tag)) {
166+
const child = child_loc ? `\`<${child_tag}>\` (${child_loc})` : `\`<${child_tag}>\``;
174167
const ancestor = ancestor_loc
175168
? `\`<${ancestor_tag}>\` (${ancestor_loc})`
176169
: `\`<${ancestor_tag}>\``;
@@ -184,38 +177,36 @@ export function is_tag_valid_with_ancestor(child_node, ancestors, child_loc, anc
184177
/**
185178
* Returns an error message if the tag is not allowed inside the parent tag such that it will result
186179
* in the browser repairing the HTML, which will likely result in an error during hydration.
187-
* @param {Element} child_node
188-
* @param {Element} parent_node
180+
* @param {string} child_tag
181+
* @param {string} parent_tag
189182
* @param {string} [child_loc]
190183
* @param {string} [parent_loc]
191184
* @returns {string | null}
192185
*/
193-
export function is_tag_valid_with_parent(child_node, parent_node, child_loc, parent_loc) {
194-
if (child_node.custom_element || parent_node?.custom_element) return null; // custom elements can be anything
186+
export function is_tag_valid_with_parent(child_tag, parent_tag, child_loc, parent_loc) {
187+
if (child_tag.includes('-') || parent_tag?.includes('-')) return null; // custom elements can be anything
195188

196-
if (parent_node.tag === 'template') return null; // no errors or warning should be thrown in immediate children of template tags
189+
if (parent_tag === 'template') return null; // no errors or warning should be thrown in immediate children of template tags
197190

198-
const disallowed = disallowed_children[parent_node.tag];
191+
const disallowed = disallowed_children[parent_tag];
199192

200-
const child = child_loc ? `\`<${child_node.tag}>\` (${child_loc})` : `\`<${child_node.tag}>\``;
201-
const parent = parent_loc
202-
? `\`<${parent_node.tag}>\` (${parent_loc})`
203-
: `\`<${parent_node.tag}>\``;
193+
const child = child_loc ? `\`<${child_tag}>\` (${child_loc})` : `\`<${child_tag}>\``;
194+
const parent = parent_loc ? `\`<${parent_tag}>\` (${parent_loc})` : `\`<${parent_tag}>\``;
204195

205196
if (disallowed) {
206-
if ('direct' in disallowed && disallowed.direct.includes(child_node.tag)) {
197+
if ('direct' in disallowed && disallowed.direct.includes(child_tag)) {
207198
return `${child} cannot be a direct child of ${parent}`;
208199
}
209200

210-
if ('descendant' in disallowed && disallowed.descendant.includes(child_node.tag)) {
201+
if ('descendant' in disallowed && disallowed.descendant.includes(child_tag)) {
211202
return `${child} cannot be a child of ${parent}`;
212203
}
213204

214205
if ('only' in disallowed && disallowed.only) {
215-
if (disallowed.only.includes(child_node.tag)) {
206+
if (disallowed.only.includes(child_tag)) {
216207
return null;
217208
} else {
218-
return `${child} cannot be a child of ${parent}. \`<${parent_node.tag}>\` only allows these children: ${disallowed.only.map((d) => `\`<${d}>\``).join(', ')}`;
209+
return `${child} cannot be a child of ${parent}. \`<${parent_tag}>\` only allows these children: ${disallowed.only.map((d) => `\`<${d}>\``).join(', ')}`;
219210
}
220211
}
221212
}
@@ -224,7 +215,7 @@ export function is_tag_valid_with_parent(child_node, parent_node, child_loc, par
224215
// parsing rules - if we're down here, then none of those matched and
225216
// so we allow it only if we don't know what the parent is, as all other
226217
// cases are invalid (and we only get into this function if we know the parent).
227-
switch (child_node.tag) {
218+
switch (child_tag) {
228219
case 'body':
229220
case 'caption':
230221
case 'col':

packages/svelte/src/internal/server/dev.js

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { current_component } from './context.js';
99
/**
1010
* @typedef {{
1111
* tag: string;
12-
* custom_element: boolean;
1312
* parent: null | Element;
1413
* filename: null | string;
1514
* line: number;
@@ -61,49 +60,32 @@ export function reset_elements() {
6160
/**
6261
* @param {Payload} payload
6362
* @param {string} tag
64-
* @param {boolean} custom_element
6563
* @param {number} line
6664
* @param {number} column
6765
*/
68-
export function push_element(payload, tag, custom_element, line, column) {
66+
export function push_element(payload, tag, line, column) {
6967
var filename = /** @type {Component} */ (current_component).function[FILENAME];
70-
var child = { tag, custom_element, parent, filename, line, column };
68+
var child = { tag, parent, filename, line, column };
7169

7270
if (parent !== null) {
7371
var ancestor = parent.parent;
74-
var ancestors = [parent];
72+
var ancestors = [parent.tag];
7573

7674
const child_loc = filename ? `${filename}:${line}:${column}` : undefined;
7775
const parent_loc = parent.filename
7876
? `${parent.filename}:${parent.line}:${parent.column}`
7977
: undefined;
8078

81-
const message = is_tag_valid_with_parent(
82-
{
83-
tag,
84-
custom_element
85-
},
86-
parent,
87-
child_loc,
88-
parent_loc
89-
);
79+
const message = is_tag_valid_with_parent(tag, parent.tag, child_loc, parent_loc);
9080
if (message) print_error(payload, message);
9181

9282
while (ancestor != null) {
93-
ancestors.push(ancestor);
83+
ancestors.push(ancestor.tag);
9484
const ancestor_loc = ancestor.filename
9585
? `${ancestor.filename}:${ancestor.line}:${ancestor.column}`
9686
: undefined;
9787

98-
const message = is_tag_valid_with_ancestor(
99-
{
100-
tag,
101-
custom_element
102-
},
103-
ancestors,
104-
child_loc,
105-
ancestor_loc
106-
);
88+
const message = is_tag_valid_with_ancestor(tag, ancestors, child_loc, ancestor_loc);
10789
if (message) print_error(payload, message);
10890

10991
ancestor = ancestor.parent;

0 commit comments

Comments
 (0)