Skip to content

Commit 22a069a

Browse files
committed
chore: better ignore code handling
Instead of hacking an ignores array onto each node (and possibly degrading perf a bit because the object shape is mutated) we keep track of ignores in a stack. The new approach also avoids the indirection the old one had to do because the new approach looks upwards (checking if parent is a fragment) instead of iterating the children (checking for comments in them). Also fixes #11482 because text nodes of all shapes are ok
1 parent ac7709f commit 22a069a

File tree

7 files changed

+103
-110
lines changed

7 files changed

+103
-110
lines changed

packages/svelte/scripts/process-messages/templates/compile-warnings.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { filename, locator, warnings } from './state.js';
1+
import { filename, locator, warnings, ignore_stack } from './state.js';
22

33
/** @typedef {{ start?: number, end?: number }} NodeLike */
44

@@ -8,8 +8,7 @@ import { filename, locator, warnings } from './state.js';
88
* @param {string} message
99
*/
1010
function w(node, code, message) {
11-
// @ts-expect-error
12-
if (node?.ignores?.has(code)) return;
11+
if (ignore_stack.at(-1)?.has(code)) return;
1312

1413
warnings.push({
1514
code,

packages/svelte/src/compiler/phases/2-analyze/a11y.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -727,9 +727,6 @@ function check_element(node, state) {
727727
for (const attribute of node.attributes) {
728728
if (attribute.type !== 'Attribute') continue;
729729

730-
// @ts-expect-error gross hack
731-
attribute.ignores = node.ignores;
732-
733730
const name = attribute.name.toLowerCase();
734731
// aria-props
735732
if (name.startsWith('aria-')) {

packages/svelte/src/compiler/phases/2-analyze/index.js

Lines changed: 41 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { prune } from './css/css-prune.js';
2525
import { hash } from './utils.js';
2626
import { warn_unused } from './css/css-warn.js';
2727
import { extract_svelte_ignore } from '../../utils/extract_svelte_ignore.js';
28+
import { pop_ignore, push_ignore } from '../../state.js';
2829

2930
/**
3031
* @param {import('#compiler').Script | null} script
@@ -439,8 +440,7 @@ export function analyze_component(root, source, options) {
439440
component_slots: new Set(),
440441
expression: null,
441442
private_derived_state: [],
442-
function_depth: scope.function_depth,
443-
ignores: new Set()
443+
function_depth: scope.function_depth
444444
};
445445

446446
walk(
@@ -511,8 +511,7 @@ export function analyze_component(root, source, options) {
511511
component_slots: new Set(),
512512
expression: null,
513513
private_derived_state: [],
514-
function_depth: scope.function_depth,
515-
ignores: new Set()
514+
function_depth: scope.function_depth
516515
};
517516

518517
walk(
@@ -1100,67 +1099,51 @@ function is_safe_identifier(expression, scope) {
11001099

11011100
/** @type {import('./types').Visitors} */
11021101
const common_visitors = {
1103-
_(node, context) {
1104-
// @ts-expect-error
1105-
const comments = /** @type {import('estree').Comment[]} */ (node.leadingComments);
1106-
1107-
if (comments) {
1102+
_(node, { state, next, path }) {
1103+
const parent = path.at(-1);
1104+
if (parent?.type === 'Fragment' && node.type !== 'Comment' && node.type !== 'Text') {
1105+
const idx = parent.nodes.indexOf(/** @type {any} */ (node));
11081106
/** @type {string[]} */
11091107
const ignores = [];
1110-
1111-
for (const comment of comments) {
1112-
const start = /** @type {any} */ (comment).start + 2;
1113-
ignores.push(...extract_svelte_ignore(start, comment.value, context.state.analysis.runes));
1108+
for (let i = idx - 1; i >= 0; i--) {
1109+
const prev = parent.nodes[i];
1110+
if (prev.type === 'Comment') {
1111+
ignores.push(
1112+
...extract_svelte_ignore(
1113+
prev.start + 2 /* '//'.length */,
1114+
prev.data,
1115+
state.analysis.runes
1116+
)
1117+
);
1118+
} else if (prev.type !== 'Text') {
1119+
break;
1120+
}
11141121
}
11151122

11161123
if (ignores.length > 0) {
1117-
// @ts-expect-error see below
1118-
node.ignores = new Set([...context.state.ignores, ...ignores]);
1124+
push_ignore(ignores);
1125+
next();
1126+
pop_ignore();
11191127
}
1120-
}
1121-
1122-
// @ts-expect-error
1123-
if (node.ignores) {
1124-
context.next({
1125-
...context.state,
1126-
// @ts-expect-error see below
1127-
ignores: node.ignores
1128-
});
1129-
} else if (context.state.ignores.size > 0) {
1130-
// @ts-expect-error
1131-
node.ignores = context.state.ignores;
1132-
}
1133-
},
1134-
Fragment(node, context) {
1135-
/** @type {string[]} */
1136-
let ignores = [];
1137-
1138-
for (const child of node.nodes) {
1139-
if (child.type === 'Text' && child.data.trim() === '') {
1140-
continue;
1141-
}
1142-
1143-
if (child.type === 'Comment') {
1144-
const start =
1145-
child.start +
1146-
(context.state.analysis.source.slice(child.start, child.start + 4) === '<!--' ? 4 : 2);
1147-
1148-
ignores.push(...extract_svelte_ignore(start, child.data, context.state.analysis.runes));
1149-
} else {
1150-
const combined_ignores = new Set(context.state.ignores);
1151-
for (const ignore of ignores) combined_ignores.add(ignore);
1152-
1153-
if (combined_ignores.size > 0) {
1154-
// TODO this is a grotesque hack that's made necessary by the fact that
1155-
// we can't call `context.visit(...)` here, because we do the convoluted
1156-
// visitor merging thing. I'm increasingly of the view that we should
1157-
// rearchitect this stuff and have a single visitor per node. It'd be
1158-
// more efficient and much simpler.
1159-
// @ts-expect-error
1160-
child.ignores = combined_ignores;
1128+
} else {
1129+
const comments = /** @type {any} */ (node).leadingComments;
1130+
if (comments) {
1131+
/** @type {string[]} */
1132+
const ignores = [];
1133+
for (const comment of comments) {
1134+
ignores.push(
1135+
...extract_svelte_ignore(
1136+
comment.start + 4 /* '<!--'.length */,
1137+
comment.value,
1138+
state.analysis.runes
1139+
)
1140+
);
1141+
}
1142+
if (ignores.length > 0) {
1143+
push_ignore(ignores);
1144+
next();
1145+
pop_ignore();
11611146
}
1162-
1163-
ignores = [];
11641147
}
11651148
}
11661149
},

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ export interface AnalysisState {
2222
expression: ExpressionTag | ClassDirective | SpreadAttribute | null;
2323
private_derived_state: string[];
2424
function_depth: number;
25-
ignores: Set<string>;
2625
}
2726

2827
export interface LegacyAnalysisState extends AnalysisState {

packages/svelte/src/compiler/state.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,21 @@ export let filename;
1010

1111
export let locator = getLocator('', { offsetLine: 1 });
1212

13+
/** @type {Set<string>[]} */
14+
export let ignore_stack = [];
15+
16+
/**
17+
* @param {string[]} ignores
18+
*/
19+
export function push_ignore(ignores) {
20+
const next = new Set([...(ignore_stack.at(-1) || []), ...ignores]);
21+
ignore_stack.push(next);
22+
}
23+
24+
export function pop_ignore() {
25+
ignore_stack.pop();
26+
}
27+
1328
/**
1429
* @param {{
1530
* source: string;
@@ -20,4 +35,5 @@ export function reset(options) {
2035
filename = options.filename;
2136
locator = getLocator(options.source, { offsetLine: 1 });
2237
warnings = [];
38+
ignore_stack = [];
2339
}

packages/svelte/src/compiler/warnings.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* This file is generated by scripts/process-messages/index.js. Do not edit! */
22

3-
import { filename, locator, warnings } from './state.js';
3+
import { filename, locator, warnings, ignore_stack } from './state.js';
44

55
/** @typedef {{ start?: number, end?: number }} NodeLike */
66
/**
@@ -9,8 +9,7 @@ import { filename, locator, warnings } from './state.js';
99
* @param {string} message
1010
*/
1111
function w(node, code, message) {
12-
// @ts-expect-error
13-
if (node?.ignores?.has(code)) return;
12+
if (ignore_stack.at(-1)?.has(code)) return;
1413

1514
warnings.push({
1615
code,
Lines changed: 42 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,74 @@
11
[
22
{
33
"code": "legacy_code",
4-
"end": {
5-
"column": 41,
6-
"line": 3
7-
},
84
"message": "`a11y-missing-attribute` is no longer valid — please use `a11y_missing_attribute` instead",
95
"start": {
10-
"column": 19,
11-
"line": 3
6+
"line": 3,
7+
"column": 17
8+
},
9+
"end": {
10+
"line": 3,
11+
"column": 39
1212
}
1313
},
1414
{
15-
"code": "unknown_code",
16-
"end": {
17-
"column": 41,
18-
"line": 8
19-
},
20-
"message": "`ally_missing_attribute` is not a recognised code (did you mean `a11y_missing_attribute`?)",
15+
"code": "a11y_missing_attribute",
16+
"message": "`<img>` element should have an alt attribute",
2117
"start": {
22-
"column": 19,
23-
"line": 8
18+
"line": 5,
19+
"column": 1
20+
},
21+
"end": {
22+
"line": 5,
23+
"column": 29
2424
}
2525
},
2626
{
27-
"code": "legacy_code",
28-
"end": {
29-
"column": 39,
30-
"line": 13
31-
},
32-
"message": "`a11y-misplaced-scope` is no longer valid — please use `a11y_misplaced_scope` instead",
27+
"code": "unknown_code",
28+
"message": "`ally_missing_attribute` is not a recognised code (did you mean `a11y_missing_attribute`?)",
3329
"start": {
34-
"column": 19,
35-
"line": 13
30+
"line": 8,
31+
"column": 17
32+
},
33+
"end": {
34+
"line": 8,
35+
"column": 39
3636
}
3737
},
3838
{
3939
"code": "a11y_missing_attribute",
40-
"end": {
41-
"column": 29,
42-
"line": 5
43-
},
4440
"message": "`<img>` element should have an alt attribute",
4541
"start": {
46-
"column": 1,
47-
"line": 5
42+
"line": 10,
43+
"column": 1
44+
},
45+
"end": {
46+
"line": 10,
47+
"column": 29
4848
}
4949
},
5050
{
51-
"code": "a11y_missing_attribute",
52-
"end": {
53-
"column": 29,
54-
"line": 10
55-
},
56-
"message": "`<img>` element should have an alt attribute",
51+
"code": "legacy_code",
52+
"message": "`a11y-misplaced-scope` is no longer valid — please use `a11y_misplaced_scope` instead",
5753
"start": {
58-
"column": 1,
59-
"line": 10
54+
"line": 13,
55+
"column": 17
56+
},
57+
"end": {
58+
"line": 13,
59+
"column": 37
6060
}
6161
},
6262
{
6363
"code": "a11y_misplaced_scope",
64-
"end": {
65-
"column": 10,
66-
"line": 14
67-
},
6864
"message": "The scope attribute should only be used with `<th>` elements",
6965
"start": {
70-
"column": 5,
71-
"line": 14
66+
"line": 14,
67+
"column": 5
68+
},
69+
"end": {
70+
"line": 14,
71+
"column": 10
7272
}
7373
}
7474
]

0 commit comments

Comments
 (0)