Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(feat) auto-add types to exports when $$Props used #1212

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 31 additions & 3 deletions packages/svelte2tsx/src/svelte2tsx/nodes/ExportedNames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export function is$$PropsDeclaration(

interface ExportedName {
isLet: boolean;
identifierEnd: number;
type?: string;
identifierText?: string;
required?: boolean;
Expand All @@ -36,9 +37,17 @@ export class ExportedNames {
const isLet = node.declarationList.flags === ts.NodeFlags.Let;
const isConst = node.declarationList.flags === ts.NodeFlags.Const;

this.handleExportedVariableDeclarationList(node.declarationList, (_, ...args) =>
this.addExport(...args)
);
this.handleExportedVariableDeclarationList(node.declarationList, (_, ...args) => {
this.addExport(...args);
ts.forEachChild(_, (node) => {
if (
ts.isVariableDeclaration(node) &&
ts.isIdentifier(node.name) &&
!node.type
) {
}
});
});
if (isLet) {
this.propTypeAssertToUserDefined(node.declarationList);
} else if (isConst) {
Expand Down Expand Up @@ -233,6 +242,7 @@ export class ExportedNames {
if (target && ts.isIdentifier(target)) {
this.possibleExports.set(name.text, {
declaration,
identifierEnd: name.getEnd() + this.astOffset,
isLet,
type: type?.getText(),
identifierText: (target as ts.Identifier).text,
Expand All @@ -241,6 +251,7 @@ export class ExportedNames {
});
} else {
this.possibleExports.set(name.text, {
identifierEnd: name.getEnd() + this.astOffset,
declaration,
isLet
});
Expand Down Expand Up @@ -269,6 +280,7 @@ export class ExportedNames {
if (target) {
this.exports.set(name.text, {
isLet: isLet || existingDeclaration?.isLet,
identifierEnd: name.getEnd() + this.astOffset,
type: type?.getText() || existingDeclaration?.type,
identifierText: (target as ts.Identifier).text,
required: required || existingDeclaration?.required,
Expand All @@ -277,6 +289,7 @@ export class ExportedNames {
} else {
this.exports.set(name.text, {
isLet: isLet || existingDeclaration?.isLet,
identifierEnd: name.getEnd() + this.astOffset,
type: existingDeclaration?.type,
required: existingDeclaration?.required,
doc: existingDeclaration?.doc
Expand Down Expand Up @@ -307,6 +320,21 @@ export class ExportedNames {
return doc;
}

addTypesToExportsIf$$PropsUsed(): void {
if (!this.uses$$Props) {
return;
}

for (const [name, { identifierEnd, type, isLet }] of this.exports.entries()) {
if (isLet && !type) {
// TODO: this makes rename behave buggy
// - you can't enter rename when the cursor is at the last char: export let foo|
// - renaming itself produces buggy results for export let: rename foo to fo1o --> fofo11o.
this.str.prependRight(identifierEnd, `: $$Props['${name}']`);
}
}
}

/**
* Creates a string from the collected props
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -357,22 +357,25 @@ export function processInstanceScriptContent(
handleTypeAssertion(str, node, astOffset);
}

//to save a bunch of condition checks on each node, we recurse into processChild which skips all the checks for top level items
// to save a bunch of condition checks on each node, we recurse into processChild which skips all the checks for top level items
ts.forEachChild(node, (n) => walk(n, node));
//fire off the on leave callbacks
// fire off the on leave callbacks
onLeaveCallbacks.map((c) => c());
};

//walk the ast and convert to tsx as we go
// walk the ast and convert to tsx as we go
tsAst.forEachChild((n) => walk(n, tsAst));

//resolve stores
// resolve stores
pendingStoreResolutions.map(resolveStore);

// declare implicit reactive variables we found in the script
implicitTopLevelNames.modifyCode(rootScope.declared);
implicitStoreValues.modifyCode(astOffset, str);

// add types from $$Props to props that don't have a type defined
exportedNames.addTypesToExportsIf$$PropsUsed();

const firstImport = tsAst.statements
.filter(ts.isImportDeclaration)
.sort((a, b) => a.end - b.end)[0];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
///<reference types="svelte" />
<></>;function render() {


interface $$Props {
exported1: string;
exported2?: boolean;
exported3: number;
exported4?: 'foo';
exported5?: '1';
exported6?: '2';
}

let exported1: $$Props['exported1'];
let exported2: $$Props['exported2'] = true;exported2 = __sveltets_1_any(exported2);;
let exported3: number;
let exported4: 'foo' = 'foo';exported4 = __sveltets_1_any(exported4);;
let exported5: '1' = '1';exported5 = __sveltets_1_any(exported5);;let exported6: $$Props['exported6'] = '2';
;
() => (<></>);
return { props: {...__sveltets_1_ensureRightProps<{exported1: typeof exported1,exported2?: typeof exported2,exported3: number,exported4?: 'foo',exported5?: '1',exported6?: typeof exported6}>(__sveltets_1_any("") as $$Props), ...__sveltets_1_ensureRightProps<Partial<$$Props>>({exported1: exported1,exported2: exported2,exported3: exported3,exported4: exported4,exported5: exported5,exported6: exported6}), ...{} as unknown as $$Props, ...{} as {}}, slots: {}, getters: {}, events: {} }}

export default class Input__SvelteComponent_ extends __sveltets_1_createSvelte2TsxComponent(__sveltets_1_with_any_event(render())) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<script lang="ts">

interface $$Props {
exported1: string;
exported2?: boolean;
exported3: number;
exported4?: 'foo';
exported5?: '1';
exported6?: '2';
}

export let exported1;
export let exported2 = true;
export let exported3: number;
export let exported4: 'foo' = 'foo';
export let exported5: '1' = '1', exported6 = '2';
</script>