Skip to content

Commit d29dba0

Browse files
authored
(fix) store access first in expression statement (#860)
The store transformation now is prepended with a semicolon if it's the first thing in an expression statement because the statement above it could end without a semicolon, making JavaScript think that this is a function invocation #859
1 parent 9740a5c commit d29dba0

File tree

8 files changed

+85
-21
lines changed

8 files changed

+85
-21
lines changed

packages/svelte2tsx/src/svelte2tsx/processInstanceScriptContent.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as ts from 'typescript';
44
import {
55
findExportKeyword,
66
getBinaryAssignmentExpr,
7+
isFirstInAnExpressionStatement,
78
isNotPropertyNameOfImport
89
} from './utils/tsAst';
910
import { ExportedNames } from './nodes/ExportedNames';
@@ -22,11 +23,11 @@ export interface InstanceScriptProcessResult {
2223
getters: Set<string>;
2324
}
2425

25-
type PendingStoreResolution<T> = {
26-
node: T;
27-
parent: T;
26+
interface PendingStoreResolution {
27+
node: ts.Identifier;
28+
parent: ts.Node;
2829
scope: Scope;
29-
};
30+
}
3031

3132
export function processInstanceScriptContent(
3233
str: MagicString,
@@ -57,7 +58,7 @@ export function processInstanceScriptContent(
5758

5859
//track $store variables since we are only supposed to give top level scopes special treatment, and users can declare $blah variables at higher scopes
5960
//which prevents us just changing all instances of Identity that start with $
60-
const pendingStoreResolutions: Array<PendingStoreResolution<ts.Node>> = [];
61+
const pendingStoreResolutions: PendingStoreResolution[] = [];
6162

6263
let scope = new Scope();
6364
const rootScope = scope;
@@ -140,7 +141,7 @@ export function processInstanceScriptContent(
140141
}
141142
};
142143

143-
const handleStore = (ident: ts.Node, parent: ts.Node) => {
144+
const handleStore = (ident: ts.Identifier, parent: ts.Node) => {
144145
const storename = ident.getText().slice(1); // drop the $
145146
// handle assign to
146147
// eslint-disable-next-line max-len
@@ -229,11 +230,12 @@ export function processInstanceScriptContent(
229230
// - in order to get ts errors if store is not assignable to SvelteStore
230231
// - use $store variable defined above to get ts flow control
231232
const dollar = str.original.indexOf('$', ident.getStart() + astOffset);
232-
str.overwrite(dollar, dollar + 1, '(__sveltets_store_get(');
233+
const getPrefix = isFirstInAnExpressionStatement(ident) ? ';' : '';
234+
str.overwrite(dollar, dollar + 1, getPrefix + '(__sveltets_store_get(');
233235
str.prependLeft(ident.end + astOffset, `), $${storename})`);
234236
};
235237

236-
const resolveStore = (pending: PendingStoreResolution<ts.Node>) => {
238+
const resolveStore = (pending: PendingStoreResolution) => {
237239
let { node, parent, scope } = pending;
238240
const name = (node as ts.Identifier).text;
239241
while (scope) {

packages/svelte2tsx/src/svelte2tsx/utils/tsAst.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,14 @@ export function getNamesFromLabeledStatement(node: ts.LabeledStatement): string[
174174
.filter((name) => !name.startsWith('$'))
175175
);
176176
}
177+
178+
export function isFirstInAnExpressionStatement(node: ts.Identifier): boolean {
179+
let parent = node.parent;
180+
while (parent && !ts.isExpressionStatement(parent)) {
181+
parent = parent.parent;
182+
}
183+
if (!parent) {
184+
return false;
185+
}
186+
return parent.getStart() === node.getStart();
187+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
///<reference types="svelte" />
2+
<></>;function render() {
3+
4+
const store = writable([]);let $store = __sveltets_store_get(store);;
5+
6+
;(__sveltets_store_get(store), $store)[1] = true;
7+
;(__sveltets_store_get(store), $store).foo = true;
8+
9+
;(__sveltets_store_get(store), $store)[1] = true
10+
;(__sveltets_store_get(store), $store).foo = true
11+
12+
store.set( true)
13+
store.set( true);
14+
15+
hello[(__sveltets_store_get(store), $store)] = true;
16+
17+
store.set( true),
18+
store.set( false),
19+
(__sveltets_store_get(store), $store),
20+
(__sveltets_store_get(store), $store).a = true
21+
22+
;(__sveltets_store_get(store), $store).a = true,
23+
(__sveltets_store_get(store), $store).b = false;
24+
;
25+
() => (<></>);
26+
return { props: {}, slots: {}, getters: {}, events: {} }}
27+
28+
export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(__sveltets_with_any_event(render))) {
29+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<script lang="ts">
2+
const store = writable([]);
3+
4+
$store[1] = true;
5+
$store.foo = true;
6+
7+
$store[1] = true
8+
$store.foo = true
9+
10+
$store = true
11+
$store = true;
12+
13+
hello[$store] = true;
14+
15+
$store = true,
16+
$store = false,
17+
$store,
18+
$store.a = true
19+
20+
$store.a = true,
21+
$store.b = false;
22+
</script>

packages/svelte2tsx/test/svelte2tsx/samples/ast-offset-none/expected.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
///<reference types="svelte" />
22
<></>;function render() {
3-
(__sveltets_store_get(var), $var);
3+
;(__sveltets_store_get(var), $var);
44
() => (<></>);
55
return { props: {}, slots: {}, getters: {}, events: {} }}
66

packages/svelte2tsx/test/svelte2tsx/samples/ast-offset-some/expected.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
///<reference types="svelte" />
22
<></>;function render() {
3-
(__sveltets_store_get(var), $var);
3+
;(__sveltets_store_get(var), $var);
44
() => (<></>);
55
return { props: {}, slots: {}, getters: {}, events: {} }}
66

packages/svelte2tsx/test/svelte2tsx/samples/store-from-module/expected.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
const store4 = writable('');let $store4 = __sveltets_store_get(store4);;
66
;<></>;function render() {
77

8-
(__sveltets_store_get(store1), $store1);
9-
(__sveltets_store_get(store3), $store3);
8+
;(__sveltets_store_get(store1), $store1);
9+
;(__sveltets_store_get(store3), $store3);
1010
;
1111
() => (<>
1212

packages/svelte2tsx/test/svelte2tsx/samples/store-property-access/expected.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
<></>;function render() {
33

44
const store = someStore();let $store = __sveltets_store_get(store);;
5-
(__sveltets_store_get(store), $store);
6-
(__sveltets_store_get(store), $store).prop;
7-
(__sveltets_store_get(store), $store)['prop'];
8-
(__sveltets_store_get(store), $store).prop.anotherProp;
9-
(__sveltets_store_get(store), $store)['prop'].anotherProp;
10-
(__sveltets_store_get(store), $store).prop['anotherProp'];
11-
(__sveltets_store_get(store), $store)['prop']['anotherProp'];
12-
(__sveltets_store_get(store), $store)?.prop.anotherProp;
13-
(__sveltets_store_get(store), $store)?.prop?.anotherProp;
5+
;(__sveltets_store_get(store), $store);
6+
;(__sveltets_store_get(store), $store).prop;
7+
;(__sveltets_store_get(store), $store)['prop'];
8+
;(__sveltets_store_get(store), $store).prop.anotherProp;
9+
;(__sveltets_store_get(store), $store)['prop'].anotherProp;
10+
;(__sveltets_store_get(store), $store).prop['anotherProp'];
11+
;(__sveltets_store_get(store), $store)['prop']['anotherProp'];
12+
;(__sveltets_store_get(store), $store)?.prop.anotherProp;
13+
;(__sveltets_store_get(store), $store)?.prop?.anotherProp;
1414
;
1515
() => (<>
1616
<p>{(__sveltets_store_get(store), $store)}</p>

0 commit comments

Comments
 (0)