Skip to content

Commit

Permalink
feat: support generics attribute for JSDoc
Browse files Browse the repository at this point in the history
  • Loading branch information
dummdidumm committed Dec 9, 2024
1 parent 02db54d commit 883a282
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 19 deletions.
24 changes: 18 additions & 6 deletions packages/svelte2tsx/src/svelte2tsx/addComponentExport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ function addGenericsComponentExport({
fileName,
mode,
usesAccessors,
isTsFile,
str,
generics,
usesSlots,
Expand All @@ -65,6 +66,8 @@ function addGenericsComponentExport({
return `ReturnType<__sveltets_Render${genericsRef}['${forPart}']>`;
}

// TODO once Svelte 4 compatibility is dropped, we can simplify this, because since TS 4.7 it is possible to use generics
// like this: `typeof render<T>` - which wasn't possibly before, hence the class + methods workaround.
let statement = `
class __sveltets_Render${genericsDef} {
props() {
Expand All @@ -76,14 +79,23 @@ class __sveltets_Render${genericsDef} {
slots() {
return render${genericsRef}().slots;
}
${
isSvelte5
`;

// For Svelte 5+ we assume TS > 4.7
if (isSvelte5 && !isTsFile && exportedNames.usesRunes()) {
statement = `
class __sveltets_Render${genericsDef} {
props(): ReturnType<typeof render${genericsRef}>['props'] { return null as any; }
events(): ReturnType<typeof render${genericsRef}>['events'] { return null as any; }
slots(): ReturnType<typeof render${genericsRef}>['slots'] { return null as any; }
`;
}

statement += isSvelte5
? ` bindings() { return ${exportedNames.createBindingsStr()}; }
exports() { return ${exportedNames.hasExports() ? `render${genericsRef}().exports` : '{}'}; }
}`
: '}'
}
`;
}\n`
: '}\n';

const svelteComponentClass = noSvelteComponentTyped
? 'SvelteComponent'
Expand Down
16 changes: 13 additions & 3 deletions packages/svelte2tsx/src/svelte2tsx/createRenderFunction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import MagicString from 'magic-string';
import { Node } from 'estree-walker';
import { ComponentEvents } from './nodes/ComponentEvents';
import { InstanceScriptProcessResult } from './processInstanceScriptContent';
import { surroundWithIgnoreComments } from '../utils/ignore';
import {
IGNORE_END_COMMENT,
IGNORE_START_COMMENT,
surroundWithIgnoreComments
} from '../utils/ignore';

export interface CreateRenderFunctionPara extends InstanceScriptProcessResult {
str: MagicString;
Expand All @@ -12,6 +16,7 @@ export interface CreateRenderFunctionPara extends InstanceScriptProcessResult {
events: ComponentEvents;
uses$$SlotsInterface: boolean;
svelte5Plus: boolean;
isTsFile: boolean;
mode?: 'ts' | 'dts';
}

Expand All @@ -27,6 +32,7 @@ export function createRenderFunction({
uses$$slots,
uses$$SlotsInterface,
generics,
isTsFile,
mode
}: CreateRenderFunctionPara) {
const htmlx = str.original;
Expand Down Expand Up @@ -70,8 +76,12 @@ export function createRenderFunction({
end--;
}
str.overwrite(scriptTag.start + 1, start - 1, `function render`);
str.overwrite(start - 1, start, `<`); // if the generics are unused, only this char is colored opaque
str.overwrite(end, scriptTagEnd, `>() {${propsDecl}\n`);
str.overwrite(start - 1, start, isTsFile ? '<' : `<${IGNORE_START_COMMENT}`); // if the generics are unused, only this char is colored opaque
str.overwrite(
end,
scriptTagEnd,
`>${isTsFile ? '' : IGNORE_END_COMMENT}() {${propsDecl}\n`
);
} else {
str.overwrite(
scriptTag.start + 1,
Expand Down
15 changes: 5 additions & 10 deletions packages/svelte2tsx/src/svelte2tsx/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export function svelte2tsx(
const str = new MagicString(svelte);
const basename = path.basename(options.filename || '');
const svelte5Plus = Number(options.version![0]) > 4;
const isTsFile = options?.isTsFile;

// process the htmlx as a svelte template
let {
Expand Down Expand Up @@ -95,14 +96,7 @@ export function svelte2tsx(
: instanceScriptTarget;
const implicitStoreValues = new ImplicitStoreValues(resolvedStores, renderFunctionStart);
//move the instance script and process the content
let exportedNames = new ExportedNames(
str,
0,
basename,
options?.isTsFile,
svelte5Plus,
isRunes
);
let exportedNames = new ExportedNames(str, 0, basename, isTsFile, svelte5Plus, isRunes);
let generics = new Generics(str, 0, { attributes: [] } as any);
let uses$$SlotsInterface = false;
if (scriptTag) {
Expand All @@ -117,7 +111,7 @@ export function svelte2tsx(
implicitStoreValues,
options.mode,
moduleAst,
options?.isTsFile,
isTsFile,
basename,
svelte5Plus,
isRunes
Expand Down Expand Up @@ -148,6 +142,7 @@ export function svelte2tsx(
uses$$SlotsInterface,
generics,
svelte5Plus,
isTsFile,
mode: options.mode
});

Expand Down Expand Up @@ -195,7 +190,7 @@ export function svelte2tsx(
str,
canHaveAnyProp: !exportedNames.uses$$Props && (uses$$props || uses$$restProps),
events,
isTsFile: options?.isTsFile,
isTsFile,
exportedNames,
usesAccessors,
usesSlots: slots.size > 0,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
///<reference types="svelte" />
;function render</*Ωignore_startΩ*/A, B extends keyof A, C extends boolean>/*Ωignore_endΩ*/() {

/** @typedef {{ a: A; b: B; c: C }} $$ComponentProps *//** @type {$$ComponentProps} */
const { a, b, c } = $props();

function getA() {
return a;
}
;
async () => {};
return { props: /** @type {$$ComponentProps} */({}), exports: /** @type {{getA: typeof getA}} */ ({}), bindings: __sveltets_$$bindings(''), slots: {}, events: {} }}
class __sveltets_Render<A,B extends keyof A,C extends boolean> {
props(): ReturnType<typeof render<A,B,C>>['props'] { return null as any; }
events(): ReturnType<typeof render<A,B,C>>['events'] { return null as any; }
slots(): ReturnType<typeof render<A,B,C>>['slots'] { return null as any; }
bindings() { return __sveltets_$$bindings(''); }
exports() { return render<A,B,C>().exports; }
}

interface $$IsomorphicComponent {
new <A,B extends keyof A,C extends boolean>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<A,B,C>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<A,B,C>['props']>, ReturnType<__sveltets_Render<A,B,C>['events']>, ReturnType<__sveltets_Render<A,B,C>['slots']>> & { $$bindings?: ReturnType<__sveltets_Render<A,B,C>['bindings']> } & ReturnType<__sveltets_Render<A,B,C>['exports']>;
<A,B extends keyof A,C extends boolean>(internal: unknown, props: ReturnType<__sveltets_Render<A,B,C>['props']> & {}): ReturnType<__sveltets_Render<A,B,C>['exports']>;
z_$$bindings?: ReturnType<__sveltets_Render<any,any,any>['bindings']>;
}
const Input__SvelteComponent_: $$IsomorphicComponent = null as any;
/*Ωignore_startΩ*/type Input__SvelteComponent_<A,B extends keyof A,C extends boolean> = InstanceType<typeof Input__SvelteComponent_<A,B,C>>;
/*Ωignore_endΩ*/export default Input__SvelteComponent_;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<script generics="A, B extends keyof A, C extends boolean">
/** @type {{ a: A; b: B; c: C }} */
const { a, b, c } = $props();
export function getA() {
return a;
}
</script>

0 comments on commit 883a282

Please sign in to comment.