Skip to content

Commit bce4f3f

Browse files
fix: strip typescript assertions before analysis (#10329)
* strip typescript assertions before analysis * add test * changeset * move ts handling from transform to before analysis * format * types * remove unwrap_ts_expression --------- Co-authored-by: Simon Holthausen <[email protected]>
1 parent 2a31987 commit bce4f3f

File tree

16 files changed

+102
-101
lines changed

16 files changed

+102
-101
lines changed

.changeset/sixty-items-crash.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte": patch
3+
---
4+
5+
fix: bindings with typescript assertions

packages/svelte/src/compiler/index.js

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
import { parse as _parse } from './phases/1-parse/index.js';
1+
import { getLocator } from 'locate-character';
2+
import { walk } from 'zimmerframe';
3+
import { CompileError } from './errors.js';
4+
import { convert } from './legacy.js';
25
import { parse as parse_acorn } from './phases/1-parse/acorn.js';
6+
import { parse as _parse } from './phases/1-parse/index.js';
7+
import { remove_typescript_nodes } from './phases/1-parse/remove_typescript_nodes.js';
38
import { analyze_component, analyze_module } from './phases/2-analyze/index.js';
49
import { transform_component, transform_module } from './phases/3-transform/index.js';
5-
import { getLocator } from 'locate-character';
6-
import { walk } from 'zimmerframe';
710
import { validate_component_options, validate_module_options } from './validate-options.js';
8-
import { convert } from './legacy.js';
9-
import { CompileError } from './errors.js';
1011
export { default as preprocess } from './preprocess/index.js';
1112

1213
/**
@@ -20,14 +21,24 @@ export { default as preprocess } from './preprocess/index.js';
2021
export function compile(source, options) {
2122
try {
2223
const validated = validate_component_options(options, '');
23-
const parsed = _parse(source);
24+
let parsed = _parse(source);
2425

2526
const combined_options = /** @type {import('#compiler').ValidatedCompileOptions} */ ({
2627
...validated,
2728
...parsed.options
2829
});
2930

31+
if (parsed.metadata.ts) {
32+
parsed = {
33+
...parsed,
34+
fragment: parsed.fragment && remove_typescript_nodes(parsed.fragment),
35+
instance: parsed.instance && remove_typescript_nodes(parsed.instance),
36+
module: parsed.module && remove_typescript_nodes(parsed.module)
37+
};
38+
}
39+
3040
const analysis = analyze_component(parsed, combined_options);
41+
3142
const result = transform_component(analysis, source, combined_options);
3243
return result;
3344
} catch (e) {

packages/svelte/src/compiler/phases/1-parse/index.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,10 @@ export class Parser {
6767
end: null,
6868
type: 'Root',
6969
fragment: create_fragment(),
70-
options: null
70+
options: null,
71+
metadata: {
72+
ts: this.ts
73+
}
7174
};
7275

7376
this.stack.push(this.root);

packages/svelte/src/compiler/phases/3-transform/typescript.js renamed to packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import { walk } from 'zimmerframe';
12
import * as b from '../../utils/builders.js';
23

3-
/** @type {import('zimmerframe').Visitors<any, any>} */
4-
export const remove_types = {
4+
/** @type {import('zimmerframe').Visitors<any, null>} */
5+
const visitors = {
56
ImportDeclaration(node) {
67
if (node.importKind === 'type') return b.empty;
78

@@ -54,3 +55,12 @@ export const remove_types = {
5455
return b.empty;
5556
}
5657
};
58+
59+
/**
60+
* @template T
61+
* @param {T} ast
62+
* @returns {T}
63+
*/
64+
export function remove_typescript_nodes(ast) {
65+
return walk(ast, null, visitors);
66+
}

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ import {
77
extract_paths,
88
is_event_attribute,
99
is_text_attribute,
10-
object,
11-
unwrap_ts_expression
10+
object
1211
} from '../../utils/ast.js';
1312
import * as b from '../../utils/builders.js';
1413
import { ReservedKeywords, Runes, SVGElements } from '../constants.js';
@@ -706,7 +705,7 @@ const runes_scope_tweaker = {
706705
}
707706
},
708707
VariableDeclarator(node, { state }) {
709-
const init = unwrap_ts_expression(node.init);
708+
const init = node.init;
710709
if (!init || init.type !== 'CallExpression') return;
711710
const rune = get_rune(init, state.scope);
712711
if (rune === null) return;

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

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
import { error } from '../../errors.js';
2-
import {
3-
extract_identifiers,
4-
get_parent,
5-
is_text_attribute,
6-
unwrap_ts_expression
7-
} from '../../utils/ast.js';
2+
import { extract_identifiers, get_parent, is_text_attribute } from '../../utils/ast.js';
83
import { warn } from '../../warnings.js';
94
import fuzzymatch from '../1-parse/utils/fuzzymatch.js';
105
import { disallowed_parapgraph_contents, interactive_elements } from '../1-parse/utils/html.js';
@@ -338,11 +333,11 @@ const validation = {
338333
BindDirective(node, context) {
339334
validate_no_const_assignment(node, node.expression, context.state.scope, true);
340335

341-
const assignee = unwrap_ts_expression(node.expression);
336+
const assignee = node.expression;
342337
let left = assignee;
343338

344339
while (left.type === 'MemberExpression') {
345-
left = unwrap_ts_expression(/** @type {import('estree').MemberExpression} */ (left.object));
340+
left = /** @type {import('estree').MemberExpression} */ (left.object);
346341
}
347342

348343
if (left.type !== 'Identifier') {
@@ -950,7 +945,7 @@ export const validation_runes = merge(validation, a11y_validators, {
950945
next({ ...state });
951946
},
952947
VariableDeclarator(node, { state, path }) {
953-
const init = unwrap_ts_expression(node.init);
948+
const init = node.init;
954949
const rune = get_rune(init, state.scope);
955950

956951
if (rune === null) return;

packages/svelte/src/compiler/phases/3-transform/client/transform-client.js

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { javascript_visitors } from './visitors/javascript.js';
88
import { javascript_visitors_runes } from './visitors/javascript-runes.js';
99
import { javascript_visitors_legacy } from './visitors/javascript-legacy.js';
1010
import { is_state_source, serialize_get_binding } from './utils.js';
11-
import { remove_types } from '../typescript.js';
1211

1312
/**
1413
* This function ensures visitor sets don't accidentally clobber each other
@@ -102,7 +101,6 @@ export function client_component(source, analysis, options) {
102101
state,
103102
combine_visitors(
104103
set_scope(analysis.module.scopes),
105-
remove_types,
106104
global_visitors,
107105
// @ts-expect-error TODO
108106
javascript_visitors,
@@ -118,21 +116,18 @@ export function client_component(source, analysis, options) {
118116
instance_state,
119117
combine_visitors(
120118
set_scope(analysis.instance.scopes),
121-
{ ...remove_types, ImportDeclaration: undefined, ExportNamedDeclaration: undefined },
122119
global_visitors,
123120
// @ts-expect-error TODO
124121
javascript_visitors,
125122
analysis.runes ? javascript_visitors_runes : javascript_visitors_legacy,
126123
{
127-
ImportDeclaration(node, context) {
128-
// @ts-expect-error
129-
state.hoisted.push(remove_types.ImportDeclaration(node, context));
124+
ImportDeclaration(node) {
125+
state.hoisted.push(node);
130126
return b.empty;
131127
},
132128
ExportNamedDeclaration(node, context) {
133129
if (node.declaration) {
134-
// @ts-expect-error
135-
return remove_types.ExportNamedDeclaration(context.visit(node.declaration), context);
130+
return context.visit(node.declaration);
136131
}
137132

138133
return b.empty;
@@ -148,7 +143,6 @@ export function client_component(source, analysis, options) {
148143
{ ...state, scope: analysis.instance.scope },
149144
combine_visitors(
150145
set_scope(analysis.template.scopes),
151-
remove_types,
152146
global_visitors,
153147
// @ts-expect-error TODO
154148
template_visitors

packages/svelte/src/compiler/phases/3-transform/client/utils.js

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
import * as b from '../../../utils/builders.js';
2-
import {
3-
extract_paths,
4-
is_simple_expression,
5-
object,
6-
unwrap_ts_expression
7-
} from '../../../utils/ast.js';
2+
import { extract_paths, is_simple_expression, object } from '../../../utils/ast.js';
83
import { error } from '../../../errors.js';
94
import {
105
PROPS_IS_LAZY_INITIAL,
@@ -228,7 +223,7 @@ function is_expression_async(expression) {
228223
export function serialize_set_binding(node, context, fallback, options) {
229224
const { state, visit } = context;
230225

231-
const assignee = unwrap_ts_expression(node.left);
226+
const assignee = node.left;
232227
if (
233228
assignee.type === 'ArrayPattern' ||
234229
assignee.type === 'ObjectPattern' ||

packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { is_hoistable_function, transform_inspect_rune } from '../../utils.js';
33
import * as b from '../../../../utils/builders.js';
44
import * as assert from '../../../../utils/assert.js';
55
import { get_prop_source, is_state_source, should_proxy_or_freeze } from '../utils.js';
6-
import { extract_paths, unwrap_ts_expression } from '../../../../utils/ast.js';
6+
import { extract_paths } from '../../../../utils/ast.js';
77

88
/** @type {import('../types.js').ComponentVisitors} */
99
export const javascript_visitors_runes = {
@@ -174,7 +174,7 @@ export const javascript_visitors_runes = {
174174
const declarations = [];
175175

176176
for (const declarator of node.declarations) {
177-
const init = unwrap_ts_expression(declarator.init);
177+
const init = declarator.init;
178178
const rune = get_rune(init, state.scope);
179179
if (!rune || rune === '$effect.active' || rune === '$effect.root' || rune === '$inspect') {
180180
if (init != null && is_hoistable_function(init)) {

packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ import {
33
extract_paths,
44
is_event_attribute,
55
is_text_attribute,
6-
object,
7-
unwrap_ts_expression
6+
object
87
} from '../../../../utils/ast.js';
98
import { binding_properties } from '../../../bindings.js';
109
import {
@@ -2575,7 +2574,7 @@ export const template_visitors = {
25752574
},
25762575
BindDirective(node, context) {
25772576
const { state, path, visit } = context;
2578-
const expression = unwrap_ts_expression(node.expression);
2577+
const expression = node.expression;
25792578
const getter = b.thunk(/** @type {import('estree').Expression} */ (visit(expression)));
25802579
const assignment = b.assignment('=', expression, b.id('$$value'));
25812580
const setter = b.arrow(

0 commit comments

Comments
 (0)