Skip to content

Commit 7f89480

Browse files
authored
(svelte2tsx) export class, const and function (#328)
* export const and class are not props * add getters for exported to exported class
1 parent 5cd71dc commit 7f89480

File tree

71 files changed

+206
-131
lines changed

Some content is hidden

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

71 files changed

+206
-131
lines changed

packages/language-server/src/plugins/typescript/features/RenameProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ export class RenameProviderImpl implements RenameProvider {
152152
// First find out if it's really the "rename prop inside component with that prop" case
153153
// Use original document for that because only there the `export` is present.
154154
const regex = new RegExp(
155-
`export\\s+(const|let)\\s+${this.getVariableAtPosition(
155+
`export\\s+let\\s+${this.getVariableAtPosition(
156156
tsDoc,
157157
fragment,
158158
lang,

packages/svelte2tsx/.editorconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
[test/**/*.{tsx,jsx,html}]
22
trim_trailing_whitespace = false
3+
[test/**/*.html]
4+
insert_final_newline = false
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import MagicString from 'magic-string';
2+
import { Node } from 'estree-walker';
3+
4+
export type ExportedNames = Map<
5+
string,
6+
{
7+
type?: string;
8+
identifierText?: string;
9+
required?: boolean;
10+
}
11+
>;
12+
13+
export interface InstanceScriptProcessResult {
14+
exportedNames: ExportedNames;
15+
uses$$props: boolean;
16+
uses$$restProps: boolean;
17+
getters: Set<string>;
18+
}
19+
20+
export interface CreateRenderFunctionPara extends InstanceScriptProcessResult {
21+
str: MagicString;
22+
scriptTag: Node;
23+
scriptDestination: number;
24+
slots: Map<string, Map<string, string>>;
25+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export const createClassGetter = (name: string) =>
2+
`\n${' '.repeat(4)}get ${name}() { return render().getters.${name} }`;
3+
export const createClassGetters = (names: Set<string>) => {
4+
return Array.from(names).map(createClassGetter).join('');
5+
};
6+
export function createRenderFunctionGetterStr(getters: Set<string>) {
7+
const properties = Array.from(getters).map((name) => `${name}: ${name}`);
8+
return `{${properties.join(', ')}}`;
9+
}

packages/svelte2tsx/src/svelte2tsx.ts

Lines changed: 61 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import { parseHtmlx } from './htmlxparser';
66
import { convertHtmlxToJsx } from './htmlxtojsx';
77
import { Node } from 'estree-walker';
88
import * as ts from 'typescript';
9+
import { findExortKeyword } from './utils/tsAst';
10+
import { ExportedNames, InstanceScriptProcessResult, CreateRenderFunctionPara } from './interfaces';
11+
import { createRenderFunctionGetterStr, createClassGetters } from './nodes/exportgetters';
912

1013
function AttributeValueAsJsExpression(htmlx: string, attr: Node): string {
1114
if (attr.value.length == 0) return "''"; //wut?
@@ -364,20 +367,6 @@ function processSvelteTemplate(str: MagicString): TemplateProcessResult {
364367
};
365368
}
366369

367-
type ExportedNames = Map<
368-
string,
369-
{
370-
type?: string;
371-
identifierText?: string;
372-
required?: boolean;
373-
}
374-
>;
375-
376-
type InstanceScriptProcessResult = {
377-
exportedNames: ExportedNames;
378-
uses$$props: boolean;
379-
uses$$restProps: boolean;
380-
};
381370

382371
function processInstanceScriptContent(str: MagicString, script: Node): InstanceScriptProcessResult {
383372
const htmlx = str.original;
@@ -391,6 +380,7 @@ function processInstanceScriptContent(str: MagicString, script: Node): InstanceS
391380
);
392381
const astOffset = script.content.start;
393382
const exportedNames: ExportedNames = new Map();
383+
const getters = new Set<string>();
394384

395385
const implicitTopLevelNames: Map<string, number> = new Map();
396386
let uses$$props = false;
@@ -431,6 +421,12 @@ function processInstanceScriptContent(str: MagicString, script: Node): InstanceS
431421
exportedNames.set(name.text, {});
432422
}
433423
};
424+
const addGetter = (node: ts.Identifier) => {
425+
if (!node) {
426+
return;
427+
}
428+
getters.add(node.text);
429+
};
434430

435431
const removeExport = (start: number, end: number) => {
436432
const exportStart = str.original.indexOf('export', start + astOffset);
@@ -439,10 +435,6 @@ function processInstanceScriptContent(str: MagicString, script: Node): InstanceS
439435
};
440436

441437
const propTypeAssertToUserDefined = (node: ts.VariableDeclarationList) => {
442-
if (node.flags !== ts.NodeFlags.Let) {
443-
return;
444-
}
445-
446438
const hasInitializers = node.declarations.filter((declaration) => declaration.initializer);
447439
const handleTypeAssertion = (declaration: ts.VariableDeclaration) => {
448440
const identifier = declaration.name;
@@ -669,31 +661,46 @@ function processInstanceScriptContent(str: MagicString, script: Node): InstanceS
669661
const onLeaveCallbacks: onLeaveCallback[] = [];
670662

671663
if (ts.isVariableStatement(node)) {
672-
const exportModifier = node.modifiers
673-
? node.modifiers.find((x) => x.kind == ts.SyntaxKind.ExportKeyword)
674-
: null;
664+
const exportModifier = findExortKeyword(node);
675665
if (exportModifier) {
676-
handleExportedVariableDeclarationList(node.declarationList);
677-
propTypeAssertToUserDefined(node.declarationList);
666+
const isLet = node.declarationList.flags === ts.NodeFlags.Let;
667+
const isConst = node.declarationList.flags === ts.NodeFlags.Const;
668+
669+
if (isLet) {
670+
handleExportedVariableDeclarationList(node.declarationList);
671+
propTypeAssertToUserDefined(node.declarationList);
672+
} else if (isConst) {
673+
node.declarationList.forEachChild((n) => {
674+
if (ts.isVariableDeclaration(n) && ts.isIdentifier(n.name)) {
675+
addGetter(n.name);
676+
}
677+
});
678+
}
678679
removeExport(exportModifier.getStart(), exportModifier.end);
679680
}
680681
}
681682

682683
if (ts.isFunctionDeclaration(node)) {
683684
if (node.modifiers) {
684-
const exportModifier = node.modifiers.find(
685-
(x) => x.kind == ts.SyntaxKind.ExportKeyword,
686-
);
685+
const exportModifier = findExortKeyword(node);
687686
if (exportModifier) {
688-
addExport(node.name);
689687
removeExport(exportModifier.getStart(), exportModifier.end);
688+
addGetter(node.name);
690689
}
691690
}
692691

693692
pushScope();
694693
onLeaveCallbacks.push(() => popScope());
695694
}
696695

696+
if (ts.isClassDeclaration(node)) {
697+
const exportModifier = findExortKeyword(node);
698+
if (exportModifier) {
699+
removeExport(exportModifier.getStart(), exportModifier.end);
700+
addGetter(node.name);
701+
}
702+
}
703+
697704
if (ts.isBlock(node)) {
698705
pushScope();
699706
onLeaveCallbacks.push(() => popScope());
@@ -803,6 +810,7 @@ function processInstanceScriptContent(str: MagicString, script: Node): InstanceS
803810
exportedNames,
804811
uses$$props,
805812
uses$$restProps,
813+
getters,
806814
};
807815
}
808816

@@ -825,6 +833,7 @@ function addComponentExport(
825833
uses$$propsOr$$restProps: boolean,
826834
strictMode: boolean,
827835
isTsFile: boolean,
836+
getters: Set<string>,
828837
/** A named export allows for TSDoc-compatible docstrings */
829838
className?: string,
830839
componentDocumentation?: string | null,
@@ -841,10 +850,12 @@ function addComponentExport(
841850

842851
const doc = formatComponentDocumentation(componentDocumentation);
843852

844-
// eslint-disable-next-line max-len
845-
const statement = `\n\n${doc}export default class ${
846-
className ? `${className} ` : ''
847-
}{\n $$prop_def = ${propDef}\n $$slot_def = render().slots\n}`;
853+
const statement =
854+
`\n\n${doc}export default class ${
855+
className ? `${className} ` : ''
856+
}{\n $$prop_def = ${propDef}\n $$slot_def = render().slots` +
857+
createClassGetters(getters) +
858+
'\n}';
848859

849860
str.append(statement);
850861
}
@@ -899,15 +910,16 @@ function processModuleScriptTag(str: MagicString, script: Node) {
899910
str.overwrite(scriptEndTagStart, script.end, ';<>');
900911
}
901912

902-
function createRenderFunction(
903-
str: MagicString,
904-
scriptTag: Node,
905-
scriptDestination: number,
906-
slots: Map<string, Map<string, string>>,
907-
exportedNames: ExportedNames,
908-
uses$$props: boolean,
909-
uses$$restProps: boolean,
910-
) {
913+
function createRenderFunction({
914+
str,
915+
scriptTag,
916+
scriptDestination,
917+
slots,
918+
getters,
919+
exportedNames,
920+
uses$$props,
921+
uses$$restProps
922+
}: CreateRenderFunctionPara) {
911923
const htmlx = str.original;
912924
let propsDecl = '';
913925

@@ -946,7 +958,7 @@ function createRenderFunction(
946958

947959
const returnString = `\nreturn { props: ${createPropsStr(
948960
exportedNames,
949-
)}, slots: ${slotsAsDef} }}`;
961+
)}, slots: ${slotsAsDef}, getters: ${createRenderFunctionGetterStr(getters)} }}`;
950962
str.append(returnString);
951963
}
952964

@@ -1008,27 +1020,30 @@ export function svelte2tsx(svelte: string, options?: { filename?: string; strict
10081020

10091021
//move the instance script and process the content
10101022
let exportedNames: ExportedNames = new Map();
1023+
let getters = new Set<string>();
10111024
if (scriptTag) {
10121025
//ensure it is between the module script and the rest of the template (the variables need to be declared before the jsx template)
10131026
if (scriptTag.start != instanceScriptTarget) {
10141027
str.move(scriptTag.start, scriptTag.end, instanceScriptTarget);
10151028
}
10161029
const res = processInstanceScriptContent(str, scriptTag);
1017-
exportedNames = res.exportedNames;
10181030
uses$$props = uses$$props || res.uses$$props;
10191031
uses$$restProps = uses$$restProps || res.uses$$restProps;
1032+
1033+
({ exportedNames, getters } = res);
10201034
}
10211035

10221036
//wrap the script tag and template content in a function returning the slot and exports
1023-
createRenderFunction(
1037+
createRenderFunction({
10241038
str,
10251039
scriptTag,
1026-
instanceScriptTarget,
1040+
scriptDestination: instanceScriptTarget,
10271041
slots,
1042+
getters,
10281043
exportedNames,
10291044
uses$$props,
10301045
uses$$restProps,
1031-
);
1046+
});
10321047

10331048
// we need to process the module script after the instance script has moved otherwise we get warnings about moving edited items
10341049
if (moduleScriptTag) {
@@ -1042,6 +1057,7 @@ export function svelte2tsx(svelte: string, options?: { filename?: string; strict
10421057
uses$$props || uses$$restProps,
10431058
!!options?.strictMode,
10441059
isTsFile(scriptTag, moduleScriptTag),
1060+
getters,
10451061
className,
10461062
componentDocumentation,
10471063
);
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import ts from 'typescript';
2+
3+
export function findExortKeyword(node: ts.Node) {
4+
return node.modifiers?.find((x) => x.kind == ts.SyntaxKind.ExportKeyword);
5+
}

packages/svelte2tsx/test/sourcemaps/.editorconfig

Lines changed: 0 additions & 2 deletions
This file was deleted.

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

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

88
export default class {
99
$$prop_def = __sveltets_partial(render().props)

packages/svelte2tsx/test/sourcemaps/let.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
;
55
<>
66
</>
7-
return { props: {}, slots: {} }}
7+
return { props: {}, slots: {}, getters: {} }}
88

99
export default class {
1010
$$prop_def = __sveltets_partial(render().props)

packages/svelte2tsx/test/sourcemaps/repl.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@
177177
</>}}}
178178
</div>
179179
</>
180-
return { props: {slug: slug , chapter: chapter}, slots: {} }}
180+
return { props: {slug: slug , chapter: chapter}, slots: {}, getters: {} }}
181181

182182
export default class {
183183
$$prop_def = __sveltets_partial(render().props)

0 commit comments

Comments
 (0)