Skip to content

Commit 8d7dfd6

Browse files
author
Vatroslav Vrbanic
authored
(feat) Add accessors autocomplete (#893)
If the component option `accessors` is present / present and set to `true`, add getters and setters to the generated Svelte2TsxComponent class for each exported `let` prop. This way we get autocomplete / intellisense for accessors. #882
1 parent f33173f commit 8d7dfd6

File tree

124 files changed

+272
-124
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

124 files changed

+272
-124
lines changed

packages/svelte2tsx/src/svelte2tsx/index.ts

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { ComponentEvents } from './nodes/ComponentEvents';
99
import { EventHandler } from './nodes/event-handler';
1010
import { ExportedNames } from './nodes/ExportedNames';
1111
import { createClassGetters, createRenderFunctionGetterStr } from './nodes/exportgetters';
12+
import { createClassSetters, createRenderFunctionSetterStr } from './nodes/exportsetters';
1213
import {
1314
handleScopeAndResolveForSlot,
1415
handleScopeAndResolveLetVarForSlot
@@ -45,6 +46,7 @@ interface AddComponentExportPara {
4546
strictEvents: boolean;
4647
isTsFile: boolean;
4748
getters: Set<string>;
49+
setters: Set<string>;
4850
fileName?: string;
4951
componentDocumentation: ComponentDocumentation;
5052
}
@@ -60,6 +62,7 @@ type TemplateProcessResult = {
6062
componentDocumentation: ComponentDocumentation;
6163
events: ComponentEvents;
6264
resolvedStores: string[];
65+
usesAccessors: boolean;
6366
};
6467

6568
/**
@@ -77,6 +80,7 @@ function processSvelteTemplate(
7780
let uses$$props = false;
7881
let uses$$restProps = false;
7982
let uses$$slots = false;
83+
let usesAccessors = false;
8084

8185
const componentDocumentation = new ComponentDocumentation();
8286

@@ -90,6 +94,25 @@ function processSvelteTemplate(
9094
const stores = new Stores(scopeStack, str, isDeclaration);
9195
const scripts = new Scripts(htmlxAst);
9296

97+
const handleSvelteOptions = (node: Node) => {
98+
for (let i = 0; i < node.attributes.length; i++) {
99+
const optionName = node.attributes[i].name;
100+
const optionValue = node.attributes[i].value;
101+
102+
switch (optionName) {
103+
case 'accessors':
104+
if (Array.isArray(optionValue)) {
105+
if (optionValue[0].type === 'MustacheTag') {
106+
usesAccessors = optionValue[0].expression.value;
107+
}
108+
} else {
109+
usesAccessors = true;
110+
}
111+
break;
112+
}
113+
}
114+
};
115+
93116
const handleIdentifier = (node: Node) => {
94117
if (node.name === '$$props') {
95118
uses$$props = true;
@@ -177,6 +200,9 @@ function processSvelteTemplate(
177200
case 'Comment':
178201
componentDocumentation.handleComment(node);
179202
break;
203+
case 'Options':
204+
handleSvelteOptions(node);
205+
break;
180206
case 'Identifier':
181207
handleIdentifier(node);
182208
stores.handleIdentifier(node, parent, prop);
@@ -278,7 +304,8 @@ function processSvelteTemplate(
278304
uses$$restProps,
279305
uses$$slots,
280306
componentDocumentation,
281-
resolvedStores
307+
resolvedStores,
308+
usesAccessors
282309
};
283310
}
284311

@@ -289,6 +316,7 @@ function addComponentExport({
289316
strictEvents,
290317
isTsFile,
291318
getters,
319+
setters,
292320
fileName,
293321
componentDocumentation
294322
}: AddComponentExportPara) {
@@ -301,19 +329,16 @@ function addComponentExport({
301329
? uses$$propsOr$$restProps
302330
? `__sveltets_with_any(${eventsDef})`
303331
: eventsDef
304-
: `__sveltets_partial${isTsFile ? '_ts' : ''}${
305-
uses$$propsOr$$restProps ? '_with_any' : ''
306-
}(${eventsDef})`;
332+
: `__sveltets_partial${isTsFile ? '_ts' : ''}${uses$$propsOr$$restProps ? '_with_any' : ''
333+
}(${eventsDef})`;
307334

308335
const doc = componentDocumentation.getFormatted();
309336
const className = fileName && classNameFromFilename(fileName);
310337

311338
const statement =
312-
`\n\n${doc}export default class${
313-
className ? ` ${className}` : ''
339+
`\n\n${doc}export default class${className ? ` ${className}` : ''
314340
} extends createSvelte2TsxComponent(${propDef}) {` +
315-
createClassGetters(getters) +
316-
'\n}';
341+
createClassGetters(getters) + createClassSetters(setters) + '\n}';
317342

318343
str.append(statement);
319344
}
@@ -353,6 +378,7 @@ function createRenderFunction({
353378
scriptDestination,
354379
slots,
355380
getters,
381+
setters,
356382
events,
357383
exportedNames,
358384
isTsFile,
@@ -410,6 +436,7 @@ function createRenderFunction({
410436
`\nreturn { props: ${exportedNames.createPropsStr(
411437
isTsFile
412438
)}, slots: ${slotsAsDef}, getters: ${createRenderFunctionGetterStr(getters)}` +
439+
`, setters: ${createRenderFunctionSetterStr(setters)}` +
413440
`, events: ${events.toDefString()} }}`;
414441

415442
// wrap template with callback
@@ -440,7 +467,8 @@ export function svelte2tsx(
440467
uses$$restProps,
441468
events,
442469
componentDocumentation,
443-
resolvedStores
470+
resolvedStores,
471+
usesAccessors
444472
} = processSvelteTemplate(str, options);
445473

446474
/* Rearrange the script tags so that module is first, and instance second followed finally by the template
@@ -467,17 +495,18 @@ export function svelte2tsx(
467495
//move the instance script and process the content
468496
let exportedNames = new ExportedNames();
469497
let getters = new Set<string>();
498+
let setters = new Set<string>();
470499
if (scriptTag) {
471500
//ensure it is between the module script and the rest of the template (the variables need to be declared before the jsx template)
472501
if (scriptTag.start != instanceScriptTarget) {
473502
str.move(scriptTag.start, scriptTag.end, instanceScriptTarget);
474503
}
475-
const res = processInstanceScriptContent(str, scriptTag, events, implicitStoreValues);
504+
const res = processInstanceScriptContent(str, scriptTag, events, implicitStoreValues, usesAccessors);
476505
uses$$props = uses$$props || res.uses$$props;
477506
uses$$restProps = uses$$restProps || res.uses$$restProps;
478507
uses$$slots = uses$$slots || res.uses$$slots;
479508

480-
({ exportedNames, events, getters } = res);
509+
({ exportedNames, events, getters, setters } = res);
481510
}
482511

483512
//wrap the script tag and template content in a function returning the slot and exports
@@ -488,6 +517,7 @@ export function svelte2tsx(
488517
slots,
489518
events,
490519
getters,
520+
setters,
491521
exportedNames,
492522
isTsFile: options?.isTsFile,
493523
uses$$props,
@@ -511,6 +541,7 @@ export function svelte2tsx(
511541
strictEvents: events.hasInterface(),
512542
isTsFile: options?.isTsFile,
513543
getters,
544+
setters,
514545
fileName: options?.filename,
515546
componentDocumentation
516547
});
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export const createClassSetter = (name: string) =>
2+
`\n
3+
/**accessor*/\n${' '.repeat(4)}set ${name}(${name}) {}`;
4+
5+
export const createClassSetters = (names: Set<string>) => {
6+
return Array.from(names).map(createClassSetter).join('');
7+
};
8+
9+
export function createRenderFunctionSetterStr(setters: Set<string>) {
10+
const properties = Array.from(setters).map((name) => `${name}: ${name}`);
11+
return `{${properties.join(', ')}}`;
12+
}

packages/svelte2tsx/src/svelte2tsx/processInstanceScriptContent.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export interface InstanceScriptProcessResult {
2121
uses$$restProps: boolean;
2222
uses$$slots: boolean;
2323
getters: Set<string>;
24+
setters: Set<string>;
2425
}
2526

2627
interface PendingStoreResolution {
@@ -33,7 +34,8 @@ export function processInstanceScriptContent(
3334
str: MagicString,
3435
script: Node,
3536
events: ComponentEvents,
36-
implicitStoreValues: ImplicitStoreValues
37+
implicitStoreValues: ImplicitStoreValues,
38+
usesAccessors: boolean
3739
): InstanceScriptProcessResult {
3840
const htmlx = str.original;
3941
const scriptContent = htmlx.substring(script.content.start, script.content.end);
@@ -47,6 +49,7 @@ export function processInstanceScriptContent(
4749
const astOffset = script.content.start;
4850
const exportedNames = new ExportedNames();
4951
const getters = new Set<string>();
52+
const setters = new Set<string>();
5053

5154
const implicitTopLevelNames = new ImplicitTopLevelNames();
5255
let uses$$props = false;
@@ -101,6 +104,11 @@ export function processInstanceScriptContent(
101104
const name = identifier.getText();
102105
const end = declaration.end + astOffset;
103106

107+
if (usesAccessors) {
108+
setters.add(name);
109+
getters.add(name);
110+
}
111+
104112
str.appendLeft(end, `;${name} = __sveltets_any(${name});`);
105113
};
106114

@@ -499,6 +507,7 @@ export function processInstanceScriptContent(
499507
uses$$props,
500508
uses$$restProps,
501509
uses$$slots,
502-
getters
510+
getters,
511+
setters
503512
};
504513
}

packages/svelte2tsx/test/sourcemaps/event-binding.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
1==== 2==================
55
<button onclick={(__sveltets_store_get(check), $check) ? method1 : method2} >Bla</button></>
66
3==== 4==================
7-
return { props: {}, slots: {}, getters: {}, events: {} }}
7+
return { props: {}, slots: {}, getters: {}, setters: {}, events: {} }}
88

99
export default class extends createSvelte2TsxComponent(__sveltets_partial(__sveltets_with_any_event(render))) {
1010
}

packages/svelte2tsx/test/sourcemaps/let.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
;
66
() => (<>
77
</>);
8-
return { props: {}, slots: {}, getters: {}, events: {} }}
8+
return { props: {}, slots: {}, getters: {}, setters: {}, events: {} }}
99

1010
export default class extends createSvelte2TsxComponent(__sveltets_partial(__sveltets_with_any_event(render))) {
1111
}

packages/svelte2tsx/test/sourcemaps/repl.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@
178178
</> : <></>}
179179
</div>
180180
</>);
181-
return { props: {slug: slug , chapter: chapter}, slots: {}, getters: {}, events: {} }}
181+
return { props: {slug: slug , chapter: chapter}, slots: {}, getters: {}, setters: {}, events: {} }}
182182

183183
export default class extends createSvelte2TsxComponent(__sveltets_partial(__sveltets_with_any_event(render))) {
184184
}

packages/svelte2tsx/test/svelte2tsx/samples/$store-assign/expected.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
(__sveltets_store_get(store), $store).b = false;
2424
;
2525
() => (<></>);
26-
return { props: {}, slots: {}, getters: {}, events: {} }}
26+
return { props: {}, slots: {}, getters: {}, setters: {}, events: {} }}
2727

2828
export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(__sveltets_with_any_event(render))) {
2929
}

packages/svelte2tsx/test/svelte2tsx/samples/$store-index/expected.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<>{someRecordOrArr[(__sveltets_store_get(store), $store)]}
44
{someObject['$store']}
55
{someObject.$store}</>
6-
return { props: {}, slots: {}, getters: {}, events: {} }}
6+
return { props: {}, slots: {}, getters: {}, setters: {}, events: {} }}
77

88
export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(__sveltets_with_any_event(render))) {
99
}

packages/svelte2tsx/test/svelte2tsx/samples/array-binding-export/expected.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
let [a,b,c] = [1,2,3];
55
;
66
() => (<></>);
7-
return { props: {a: a , b: b , c: c}, slots: {}, getters: {}, events: {} }}
7+
return { props: {a: a , b: b , c: c}, slots: {}, getters: {}, setters: {}, events: {} }}
88

99
export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(__sveltets_with_any_event(render))) {
1010
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<></>;function render() {
33
;(__sveltets_store_get(var), $var);
44
() => (<></>);
5-
return { props: {}, slots: {}, getters: {}, events: {} }}
5+
return { props: {}, slots: {}, getters: {}, setters: {}, events: {} }}
66

77
export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(__sveltets_with_any_event(render))) {
88
}

0 commit comments

Comments
 (0)