From 61f1b9045bfb66f60e8bf1dcdfca0ad38cfa4198 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Mon, 13 Mar 2023 17:36:45 +0100 Subject: [PATCH] (fix) kit auto type fixes - recognize export const snapshot - add types at correct position when initializer given - recognize +page/layout@ files --- packages/svelte2tsx/index.d.ts | 2 +- packages/svelte2tsx/src/helpers/sveltekit.ts | 11 +++-- packages/svelte2tsx/src/helpers/typescript.ts | 9 +++- .../src/svelte2tsx/nodes/ExportedNames.ts | 42 +++++++++++++++---- packages/svelte2tsx/test/helpers/index.ts | 16 +++++++ .../samples/sveltekit-autotypes/+page.svelte | 1 + .../samples/sveltekit-autotypes/expectedv2.ts | 5 ++- 7 files changed, 67 insertions(+), 19 deletions(-) diff --git a/packages/svelte2tsx/index.d.ts b/packages/svelte2tsx/index.d.ts index 0b577aab4..879fc610d 100644 --- a/packages/svelte2tsx/index.d.ts +++ b/packages/svelte2tsx/index.d.ts @@ -115,7 +115,7 @@ export const internalHelpers: { fileName: string, options: InternalHelpers.KitFilesSettings ) => boolean; - isKitRouteFile: (fileName: string, basename: string) =>boolean, + isKitRouteFile: (basename: string) => boolean, isClientHooksFile: ( fileName: string, basename: string, diff --git a/packages/svelte2tsx/src/helpers/sveltekit.ts b/packages/svelte2tsx/src/helpers/sveltekit.ts index 7e498e92d..ad49f35fa 100644 --- a/packages/svelte2tsx/src/helpers/sveltekit.ts +++ b/packages/svelte2tsx/src/helpers/sveltekit.ts @@ -26,7 +26,7 @@ const kitPageFiles = new Set(['+page', '+layout', '+page.server', '+layout.serve export function isKitFile(fileName: string, options: KitFilesSettings): boolean { const basename = path.basename(fileName); return ( - isKitRouteFile(fileName, basename) || + isKitRouteFile(basename) || isServerHooksFile(fileName, basename, options.serverHooksPath) || isClientHooksFile(fileName, basename, options.clientHooksPath) || isParamsFile(fileName, basename, options.paramsPath) @@ -36,12 +36,12 @@ export function isKitFile(fileName: string, options: KitFilesSettings): boolean /** * Determines whether or not a given file is a SvelteKit-specific route file */ -export function isKitRouteFile(fileName: string, basename: string): boolean { +export function isKitRouteFile(basename: string): boolean { if (basename.includes('@')) { // +page@foo -> +page basename = basename.split('@')[0]; } else { - basename = basename.slice(0, -path.extname(fileName).length); + basename = basename.slice(0, -path.extname(basename).length); } return kitPageFiles.has(basename); @@ -97,7 +97,7 @@ export function upsertKitFile( ): { text: string; addedCode: AddedCode[] } { let basename = path.basename(fileName); const result = - upserKitRouteFile(ts, fileName, basename, getSource, surround) ?? + upserKitRouteFile(ts, basename, getSource, surround) ?? upserKitServerHooksFile( ts, fileName, @@ -141,12 +141,11 @@ export function upsertKitFile( function upserKitRouteFile( ts: _ts, - fileName: string, basename: string, getSource: () => ts.SourceFile | undefined, surround: (text: string) => string ) { - if (!isKitRouteFile(fileName, basename)) return; + if (!isKitRouteFile(basename)) return; const source = getSource(); if (!source) return; diff --git a/packages/svelte2tsx/src/helpers/typescript.ts b/packages/svelte2tsx/src/helpers/typescript.ts index 8f104fbe5..7ebd88e8c 100644 --- a/packages/svelte2tsx/src/helpers/typescript.ts +++ b/packages/svelte2tsx/src/helpers/typescript.ts @@ -52,11 +52,18 @@ export function findExports(ts: _ts, source: ts.SourceFile, isTsFile: boolean) { (ts.isSatisfiesExpression(declaration.initializer) && ts.isParenthesizedExpression(declaration.initializer.expression) && (ts.isFunctionExpression(declaration.initializer.expression.expression) || - ts.isArrowFunction(declaration.initializer.expression.expression)))) + ts.isArrowFunction(declaration.initializer.expression.expression))) || + (ts.isParenthesizedExpression(declaration.initializer) && + (ts.isFunctionExpression(declaration.initializer.expression) || + ts.isArrowFunction(declaration.initializer.expression)))) ) { const node = ts.isSatisfiesExpression(declaration.initializer) ? ((declaration.initializer.expression as ts.ParenthesizedExpression) .expression as ts.FunctionExpression | ts.ArrowFunction) + : ts.isParenthesizedExpression(declaration.initializer) + ? (declaration.initializer.expression as + | ts.FunctionExpression + | ts.ArrowFunction) : declaration.initializer; exports.set(declaration.name.getText(), { type: 'function', diff --git a/packages/svelte2tsx/src/svelte2tsx/nodes/ExportedNames.ts b/packages/svelte2tsx/src/svelte2tsx/nodes/ExportedNames.ts index c9f3729c4..fc9c811ee 100644 --- a/packages/svelte2tsx/src/svelte2tsx/nodes/ExportedNames.ts +++ b/packages/svelte2tsx/src/svelte2tsx/nodes/ExportedNames.ts @@ -1,5 +1,6 @@ import MagicString from 'magic-string'; import ts from 'typescript'; +import { internalHelpers } from '../../helpers'; import { surroundWithIgnoreComments } from '../../utils/ignore'; import { preprendStr, overwriteStr } from '../../utils/magic-string'; import { findExportKeyword, getLastLeadingDoc, isInterfaceOrTypeDeclaration } from '../utils/tsAst'; @@ -18,8 +19,6 @@ interface ExportedName { doc?: string; } -const kitPageFiles = new Set(['+page.svelte', '+layout.svelte']); - export class ExportedNames { /** * Uses the $$Props type @@ -52,6 +51,17 @@ export class ExportedNames { node.declarationList.forEachChild((n) => { if (ts.isVariableDeclaration(n) && ts.isIdentifier(n.name)) { this.addGetter(n.name); + + const type = n.type || ts.getJSDocType(n); + const isKitExport = + internalHelpers.isKitRouteFile(this.basename) && + n.name.getText() === 'snapshot'; + // TS types are not allowed in JS files, but TS will still pick it up and the ignore comment will filter out the error + const kitType = isKitExport && !type ? `: import('./$types').Snapshot` : ''; + const nameEnd = n.name.end + this.astOffset; + if (kitType) { + preprendStr(this.str, nameEnd, surroundWithIgnoreComments(kitType)); + } } }); } @@ -117,7 +127,7 @@ export class ExportedNames { const type = tsType || jsDocType; const name = identifier.getText(); const isKitExport = - kitPageFiles.has(this.basename) && + internalHelpers.isKitRouteFile(this.basename) && (name === 'data' || name === 'form' || name === 'snapshot'); // TS types are not allowed in JS files, but TS will still pick it up and the ignore comment will filter out the error const kitType = @@ -132,6 +142,7 @@ export class ExportedNames { : 'Snapshot' }` : ''; + const nameEnd = identifier.end + this.astOffset; const end = declaration.end + this.astOffset; if ( @@ -150,13 +161,26 @@ export class ExportedNames { ) { const name = identifier.getText(); - preprendStr( - this.str, - end, - surroundWithIgnoreComments(`${kitType};${name} = __sveltets_2_any(${name});`) - ); + if (nameEnd === end) { + preprendStr( + this.str, + end, + surroundWithIgnoreComments( + `${kitType};${name} = __sveltets_2_any(${name});` + ) + ); + } else { + if (kitType) { + preprendStr(this.str, nameEnd, surroundWithIgnoreComments(kitType)); + } + preprendStr( + this.str, + end, + surroundWithIgnoreComments(`;${name} = __sveltets_2_any(${name});`) + ); + } } else if (kitType) { - preprendStr(this.str, end, surroundWithIgnoreComments(`${kitType}`)); + preprendStr(this.str, nameEnd, surroundWithIgnoreComments(kitType)); } }; diff --git a/packages/svelte2tsx/test/helpers/index.ts b/packages/svelte2tsx/test/helpers/index.ts index 855617234..694ce7fcc 100644 --- a/packages/svelte2tsx/test/helpers/index.ts +++ b/packages/svelte2tsx/test/helpers/index.ts @@ -41,4 +41,20 @@ describe('Internal Helpers - upsertKitFile', () => { `export async function GET(e: import('./$types').RequestEvent) : Response | Promise {}` ); }); + + it('upserts load const with paranthesis', () => { + upsert( + '+page.ts', + `export const load = (async (e) => {});`, + `export const load = (async (e: import('./$types').PageLoadEvent) => {});` + ); + }); + + it('recognizes page@', () => { + upsert('+page@.ts', `export const ssr = true;`, `export const ssr : boolean = true;`); + }); + + it('recognizes layout@foo', () => { + upsert('+layout@foo.ts', `export const ssr = true;`, `export const ssr : boolean = true;`); + }); }); diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/sveltekit-autotypes/+page.svelte b/packages/svelte2tsx/test/svelte2tsx/samples/sveltekit-autotypes/+page.svelte index eecc3cc85..373454fdb 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/sveltekit-autotypes/+page.svelte +++ b/packages/svelte2tsx/test/svelte2tsx/samples/sveltekit-autotypes/+page.svelte @@ -4,5 +4,6 @@ export const snapshot = {}; export let nope; + export let form = {} export let data: number; diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/sveltekit-autotypes/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/sveltekit-autotypes/expectedv2.ts index 3fd5b569e..9a08d45fd 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/sveltekit-autotypes/expectedv2.ts +++ b/packages/svelte2tsx/test/svelte2tsx/samples/sveltekit-autotypes/expectedv2.ts @@ -3,14 +3,15 @@ let data/*Ωignore_startΩ*/: import('./$types').PageData;data = __sveltets_2_any(data);/*Ωignore_endΩ*/; let form/*Ωignore_startΩ*/: import('./$types').ActionData;form = __sveltets_2_any(form);/*Ωignore_endΩ*/; - const snapshot = {}; + const snapshot/*Ωignore_startΩ*/: import('./$types').Snapshot/*Ωignore_endΩ*/ = {}; let nope/*Ωignore_startΩ*/;nope = __sveltets_2_any(nope);/*Ωignore_endΩ*/; + let form/*Ωignore_startΩ*/: import('./$types').ActionData/*Ωignore_endΩ*/ = {} let data: number/*Ωignore_startΩ*/;data = __sveltets_2_any(data);/*Ωignore_endΩ*/; ; async () => {}; return { props: {data: data , form: form , snapshot: snapshot , nope: nope}, slots: {}, events: {} }} -export default class Page__SvelteComponent_ extends __sveltets_2_createSvelte2TsxComponent(__sveltets_2_partial(['snapshot'], __sveltets_2_with_any_event(render()))) { +export default class Page__SvelteComponent_ extends __sveltets_2_createSvelte2TsxComponent(__sveltets_2_partial(['form','snapshot'], __sveltets_2_with_any_event(render()))) { get snapshot() { return __sveltets_2_nonNullable(this.$$prop_def.snapshot) } } \ No newline at end of file