Repository: sveltejs/language-tools
Pull request #1620 (2022-09-04 15:27:56)

(feat) create sveltekit route files
- [x] create a single page/layout/error/endpoint
- [x] create multiple files at once
- [x] enter url path for the file location
- [x] use `WorkspaceEdit` instead of `fs`
- [ ] ~~find out if this is really a SvelteKit app (check package.json)~~
- [ ] ask if a [matcher](https://kit.svelte.dev/docs/advanced-routing#matching) should be created if found in the path and does not exist
- [ ] only show the explorer menu in the routes folder (don´t know if this is possible, or needed)
- [ ] read svelte.config.js because people can customize the routes folder (?) (maybe later, not sure how feasible this is since we need to read a JS file)


dummdidumm: From reading the bullet point list it sounds like we only need to do the `package.json` check, or maybe not even that because it's a dedicated command someone actively needs to invoke, and that someone likely doesn't do that in a non-Kit app. I'm ok with doing the other stuff later. Thoughts?
david-plugge: I totally agree, it just adds unnecessary complexity.
david-plugge: I also added some snippets for `RequestHandler` and `Load` functions.
Is it fine to include them in this PR?
Or do you think we should not add any kit related snippets?
dummdidumm: Apart from the snippet thing (prefix was omitted deliberatly) this seems like it's ready to review?

Patch:
diff --git a/packages/svelte-vscode/package.json b/packages/svelte-vscode/package.json
index d5e98a75..1025a456 100644
--- a/packages/svelte-vscode/package.json
+++ b/packages/svelte-vscode/package.json
@@ -538,6 +538,42 @@
             {
                 "command": "svelte.typescript.findComponentReferences",
                 "title": "Svelte: Find Component References"
+            },
+            {
+                "command": "svelte.kit.generateMultipleFiles",
+                "title": "SvelteKit: Generate files"
+            },
+            {
+                "command": "svelte.kit.generatePage",
+                "title": "SvelteKit: Generate Page"
+            },
+            {
+                "command": "svelte.kit.generatePageLoad",
+                "title": "SvelteKit: Generate Page load"
+            },
+            {
+                "command": "svelte.kit.generatePageServerLoad",
+                "title": "SvelteKit: Generate Page server load"
+            },
+            {
+                "command": "svelte.kit.generateLayout",
+                "title": "SvelteKit: Generate Layout"
+            },
+            {
+                "command": "svelte.kit.generateLayoutLoad",
+                "title": "SvelteKit: Generate Layout load"
+            },
+            {
+                "command": "svelte.kit.generateLayoutServerLoad",
+                "title": "SvelteKit: Generate Layout server load"
+            },
+            {
+                "command": "svelte.kit.generateServer",
+                "title": "SvelteKit: Generate Endpoint"
+            },
+            {
+                "command": "svelte.kit.generateError",
+                "title": "SvelteKit: Generate Error"
             }
         ],
         "menus": {
@@ -599,9 +635,58 @@
                     "command": "svelte.typescript.findComponentReferences",
                     "when": "resourceLangId == svelte",
                     "group": "4_search"
+                },
+                {
+                    "when": "explorerResourceIsFolder",
+                    "submenu": "sveltekit2files",
+                    "group": "1_SvelteKit2files"
+                }
+            ],
+            "sveltekit2files": [
+                {
+                    "command": "svelte.kit.generateMultipleFiles",
+                    "group": "0_SvelteKit_Multiple"
+                },
+                {
+                    "command": "svelte.kit.generatePage",
+                    "group": "1_SvelteKit_Page"
+                },
+                {
+                    "command": "svelte.kit.generatePageLoad",
+                    "group": "1_SvelteKit_Page"
+                },
+                {
+                    "command": "svelte.kit.generatePageServerLoad",
+                    "group": "1_SvelteKit_Page"
+                },
+                {
+                    "command": "svelte.kit.generateLayout",
+                    "group": "3_SvelteKit_Layout"
+                },
+                {
+                    "command": "svelte.kit.generateLayoutLoad",
+                    "group": "3_SvelteKit_Layout"
+                },
+                {
+                    "command": "svelte.kit.generateLayoutServerLoad",
+                    "group": "3_SvelteKit_Layout"
+                },
+                {
+                    "command": "svelte.kit.generateServer",
+                    "group": "2_SvelteKit_Server"
+                },
+                {
+                    "command": "svelte.kit.generateError",
+                    "group": "4_SvelteKit_Error"
                 }
             ]
         },
+        "submenus": [
+            {
+                "id": "sveltekit2files",
+                "label": "SvelteKit Files"
+            }
+        ],
         "breakpoints": [
             {
                 "language": "svelte"
@@ -611,6 +696,14 @@
             {
                 "language": "svelte",
                 "path": "./snippets/svelte.json"
+            },
+            {
+                "language": "javascript",
+                "path": "./snippets/javascript.json"
+            },
+            {
+                "language": "typescript",
+                "path": "./snippets/typescript.json"
             }
         ]
     },
diff --git a/packages/svelte-vscode/snippets/javascript.json b/packages/svelte-vscode/snippets/javascript.json
new file mode 100644
index 00000000..417f51cc
--- /dev/null
+++ b/packages/svelte-vscode/snippets/javascript.json
@@ -0,0 +1,43 @@
+{
+    "SvelteKit Endpoint": {
+        "prefix": "kitEndpoint",
+        "description": "SvelteKit Endpoint",
+        "body": [
+            "/** @type {import('./\\$types').RequestHandler} */",
+            "export async function ${1|GET,POST,PUT,PATCH,DELETE|}($2) {",
+            "\t$3",
+            "\treturn new Response();",
+            "}"
+        ]
+    },
+    "SvelteKit Actions": {
+        "prefix": "kitActions",
+        "description": "SvelteKit Actions",
+        "body": [
+            "/** @type {import('./\\$types').Actions} */",
+            "export const actions = {",
+            "\t$1",
+            "};"
+        ]
+    },
+    "SvelteKit Load": {
+        "prefix": "kitLoad",
+        "description": "SvelteKit Load",
+        "body": [
+            "/** @type {import('./\\$types').${1|PageLoad,PageServerLoad,LayoutLoad,LayoutServerLoad|}} */",
+            "export async function load($2) {",
+            "\t$3",
+            "}"
+        ]
+    },
+    "SvelteKit Param Matcher": {
+        "prefix": "kitParamMatcher",
+        "description": "SvelteKit Param Matcher",
+        "body": [
+            "/** @type {import('./\\$types').ParamMatcher */",
+            "export function match(param) {",
+            "\treturn $1;",
+            "}"
+        ]
+    }
+}
diff --git a/packages/svelte-vscode/snippets/typescript.json b/packages/svelte-vscode/snippets/typescript.json
new file mode 100644
index 00000000..e3fe5db5
--- /dev/null
+++ b/packages/svelte-vscode/snippets/typescript.json
@@ -0,0 +1,37 @@
+{
+    "SvelteKit Endpoint": {
+        "prefix": "kitEndpoint",
+        "description": "SvelteKit Endpoint",
+        "body": [
+            "export const ${1|GET,POST,PUT,PATCH,DELETE|}: RequestHandler = async ($2) => {",
+            "\t$3",
+            "\treturn new Response();",
+            "};"
+        ]
+    },
+    "SvelteKit Actions": {
+        "prefix": "kitActions",
+        "description": "SvelteKit Actions",
+        "body": ["export const actions: Actions = {", "\t$1", "};"]
+    },
+    "SvelteKit Load": {
+        "prefix": "kitLoad",
+        "description": "SvelteKit Load",
+        "body": [
+            "export const load: ${1|PageLoad,PageServerLoad,LayoutLoad,LayoutServerLoad|} = async ($2) => {",
+            "\t$3",
+            "};"
+        ]
+    },
+    "SvelteKit Param Matcher": {
+        "prefix": "kitParamMatcher",
+        "description": "SvelteKit Param Matcher",
+        "body": [
+            "import type { ParamMatcher } from '@sveltejs/kit';",
+            "",
+            "export const match: ParamMatcher = (param) => {",
+            "\treturn $1;",
+            "};"
+        ]
+    }
+}
diff --git a/packages/svelte-vscode/src/extension.ts b/packages/svelte-vscode/src/extension.ts
index 0e4ba25f..012fb6e5 100644
--- a/packages/svelte-vscode/src/extension.ts
+++ b/packages/svelte-vscode/src/extension.ts
@@ -31,7 +31,9 @@ import { EMPTY_ELEMENTS } from './html/htmlEmptyTagsShared';
 import { TsPlugin } from './tsplugin';
 import { addFindComponentReferencesListener } from './typescript/findComponentReferences';
 import { addFindFileReferencesListener } from './typescript/findFileReferences';
+import { setupSvelteKit } from './sveltekit';
 
+// eslint-disable-next-line @typescript-eslint/no-namespace
 namespace TagCloseRequest {
     export const type: RequestType<TextDocumentPositionParams, string, any> = new RequestType(
         'html/tag'
@@ -60,6 +62,8 @@ export function activate(context: ExtensionContext) {
         context.subscriptions.push(onTextDocumentListener);
     }
 
+    setupSvelteKit(context);
+
     // This API is considered private and only exposed for experimenting.
     // Interface may change at any time. Use at your own risk!
     return {
diff --git a/packages/svelte-vscode/src/sveltekit/generateFiles/commands.ts b/packages/svelte-vscode/src/sveltekit/generateFiles/commands.ts
new file mode 100644
index 00000000..7d3f013e
--- /dev/null
+++ b/packages/svelte-vscode/src/sveltekit/generateFiles/commands.ts
@@ -0,0 +1,12 @@
+import { CommandType, ResourceType } from './types';
+
+export const addResourceCommandMap = new Map<CommandType, ResourceType>([
+    [CommandType.PAGE, ResourceType.PAGE],
+    [CommandType.PAGE_LOAD, ResourceType.PAGE_LOAD],
+    [CommandType.PAGE_SERVER, ResourceType.PAGE_SERVER],
+    [CommandType.LAYOUT, ResourceType.LAYOUT],
+    [CommandType.LAYOUT_LOAD, ResourceType.LAYOUT_LOAD],
+    [CommandType.LAYOUT_SERVER, ResourceType.LAYOUT_SERVER],
+    [CommandType.SERVER, ResourceType.SERVER],
+    [CommandType.ERROR, ResourceType.ERROR]
+]);
diff --git a/packages/svelte-vscode/src/sveltekit/generateFiles/generate.ts b/packages/svelte-vscode/src/sveltekit/generateFiles/generate.ts
new file mode 100644
index 00000000..31a27fcf
--- /dev/null
+++ b/packages/svelte-vscode/src/sveltekit/generateFiles/generate.ts
@@ -0,0 +1,38 @@
+import { FileType, GenerateConfig } from './types';
+import { join } from 'path';
+import { Position, Uri, window, workspace, WorkspaceEdit } from 'vscode';
+
+export async function generateResources(config: GenerateConfig) {
+    workspace.fs.createDirectory(Uri.file(config.path));
+    const edit = new WorkspaceEdit();
+
+    for (const resource of config.resources) {
+        const ext = resource.type === FileType.PAGE ? config.pageExtension : config.scriptExtension;
+        const filepath = join(config.path, `${resource.filename}.${ext}`);
+
+        const uri = Uri.file(filepath);
+        edit.createFile(uri, {
+            overwrite: false,
+            ignoreIfExists: true
+        });
+
+        const data = await resource.generate(config);
+        edit.insert(uri, new Position(0, 0), data);
+    }
+
+    await workspace.applyEdit(edit);
+
+    // save documents and open the first
+    await Promise.all(
+        edit.entries().map(async ([uri], i) => {
+            const doc = workspace.textDocuments.find((t) => t.uri.path === uri.path);
+            if (doc) {
+                await doc?.save();
+                if (i === 0) {
+                    await workspace.openTextDocument(uri);
+                    await window.showTextDocument(doc);
+                }
+            }
+        })
+    );
+}
diff --git a/packages/svelte-vscode/src/sveltekit/generateFiles/index.ts b/packages/svelte-vscode/src/sveltekit/generateFiles/index.ts
new file mode 100644
index 00000000..ba8a3e55
--- /dev/null
+++ b/packages/svelte-vscode/src/sveltekit/generateFiles/index.ts
@@ -0,0 +1,149 @@
+import * as path from 'path';
+import { commands, ExtensionContext, ProgressLocation, Uri, window, workspace } from 'vscode';
+import { addResourceCommandMap } from './commands';
+import { generateResources } from './generate';
+import { resourcesMap } from './resources';
+import { FileType, ResourceType, GenerateConfig, CommandType } from './types';
+import { checkIfTypescriptProject } from '../utils';
+
+class GenerateError extends Error {}
+
+export function addGenerateKitRouteFilesCommand(context: ExtensionContext) {
+    addResourceCommandMap.forEach((value, key) => {
+        context.subscriptions.push(
+            commands.registerCommand(key, (args) => {
+                handleSingle(args, value).catch(handleError);
+            })
+        );
+    });
+    context.subscriptions.push(
+        commands.registerCommand(CommandType.MULTIPLE, async (args) => {
+            handleMultiple(args).catch(handleError);
+        })
+    );
+}
+
+async function handleError(err: unknown) {
+    if (err instanceof GenerateError) {
+        await window.showErrorMessage(err.message);
+    } else {
+        throw err;
+    }
+}
+
+async function handleSingle(uri: Uri | undefined, resourceType: ResourceType) {
+    const resource = resourcesMap.get(resourceType);
+    if (!resource) {
+        throw new GenerateError(`Resource '${resourceType}' does not exist`);
+    }
+    const resources = [resource];
+
+    const { isTs, rootPath, scriptExtension } = await getCommonConfig(uri);
+
+    const itemPath = await promptResourcePath();
+
+    if (!itemPath) {
+        return;
+    }
+
+    await generate({
+        path: path.join(rootPath, itemPath),
+        typescript: isTs,
+        pageExtension: 'svelte',
+        scriptExtension,
+        resources
+    });
+}
+
+async function handleMultiple(uri: Uri | undefined) {
+    const { isTs, rootPath, scriptExtension } = await getCommonConfig(uri);
+
+    // Add multiple files
+    const opts = [
+        ResourceType.PAGE,
+        ResourceType.PAGE_LOAD,
+        ResourceType.PAGE_SERVER,
+        ResourceType.LAYOUT,
+        ResourceType.LAYOUT_LOAD,
+        ResourceType.LAYOUT_SERVER,
+        ResourceType.ERROR
+    ].map((type) => {
+        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+        const resource = resourcesMap.get(type)!;
+        // const iconName = resource.type === FileType.PAGE ? 'svelte' : isTs ? 'typescript' : 'javascript';
+        const extension = resource.type === FileType.PAGE ? 'svelte' : scriptExtension;
+        return {
+            label: resource.title,
+            // TODO: maybe add icons (ts,js,svelte - but it doesn´t work like this)
+            // description: `$(${iconName}) ${resource.filename}.${extension}`,
+            description: `${resource.filename}.${extension}`,
+            value: resource
+        };
+    });
+    const result = await window.showQuickPick(opts, { canPickMany: true });
+    if (!result) {
+        return;
+    }
+    const resources = result.map((res) => res.value);
+
+    const itemPath = await promptResourcePath();
+
+    if (!itemPath) {
+        return;
+    }
+
+    await generate({
+        path: path.join(rootPath, itemPath),
+        typescript: isTs,
+        pageExtension: 'svelte',
+        scriptExtension,
+        resources
+    });
+}
+
+async function promptResourcePath() {
+    const itemPath = await window.showInputBox({
+        prompt: 'Enter the path of the resources, relative to current folder',
+        value: '/'
+    });
+
+    return itemPath;
+}
+
+async function generate(config: GenerateConfig) {
+    await window.withProgress(
+        { location: ProgressLocation.Window, title: 'Creating SvelteKit files...' },
+        async () => {
+            await generateResources(config);
+        }
+    );
+}
+
+async function getCommonConfig(uri: Uri | undefined) {
+    const rootPath = getRootPath(uri);
+    if (!rootPath) {
+        throw new GenerateError(
+            'Could not resolve root path. Please open a file first or use the context menu!'
+        );
+    }
+
+    const isTs = await checkIfTypescriptProject(rootPath);
+    const scriptExtension = isTs ? 'ts' : 'js';
+    return {
+        isTs,
+        scriptExtension,
+        rootPath
+    };
+}
+
+function getRootPath(uri: Uri | undefined) {
+    let rootPath!: string;
+    if (uri) {
+        rootPath = uri.fsPath;
+    } else if (window.activeTextEditor) {
+        rootPath = path.dirname(window.activeTextEditor.document.fileName);
+    } else if (workspace.workspaceFolders && workspace.workspaceFolders.length === 1) {
+        rootPath = workspace.workspaceFolders[0].uri.fsPath;
+    }
+    return rootPath;
+}
diff --git a/packages/svelte-vscode/src/sveltekit/generateFiles/resources.ts b/packages/svelte-vscode/src/sveltekit/generateFiles/resources.ts
new file mode 100644
index 00000000..eb312f37
--- /dev/null
+++ b/packages/svelte-vscode/src/sveltekit/generateFiles/resources.ts
@@ -0,0 +1,52 @@
+import { FileType, Resource, ResourceType } from './types';
+
+import page from './templates/page';
+import pageLoad from './templates/page-load';
+import pageServer from './templates/page-server';
+import layout from './templates/layout';
+import layoutLoad from './templates/layout-load';
+import layoutServer from './templates/layout-server';
+import error from './templates/error';
+import server from './templates/server';
+
+export const resourcesMap = new Map<ResourceType, Resource>([
+    [ResourceType.PAGE, { type: FileType.PAGE, filename: '+page', title: 'Page', generate: page }],
+    [
+        ResourceType.PAGE_LOAD,
+        { type: FileType.SCRIPT, filename: '+page', title: 'Page load', generate: pageLoad }
+    ],
+    [
+        ResourceType.PAGE_SERVER,
+        {
+            type: FileType.SCRIPT,
+            filename: '+page.server',
+            title: 'Page server load',
+            generate: pageServer
+        }
+    ],
+    [
+        ResourceType.LAYOUT,
+        { type: FileType.PAGE, filename: '+layout', title: 'Layout', generate: layout }
+    ],
+    [
+        ResourceType.LAYOUT_LOAD,
+        { type: FileType.SCRIPT, filename: '+layout', title: 'Layout load', generate: layoutLoad }
+    ],
+    [
+        ResourceType.LAYOUT_SERVER,
+        {
+            type: FileType.SCRIPT,
+            filename: '+layout.server',
+            title: 'Layout server load',
+            generate: layoutServer
+        }
+    ],
+    [
+        ResourceType.SERVER,
+        { type: FileType.SCRIPT, filename: '+server', title: 'Server', generate: server }
+    ],
+    [
+        ResourceType.ERROR,
+        { type: FileType.PAGE, filename: '+error', title: 'Error', generate: error }
+    ]
+]);
diff --git a/packages/svelte-vscode/src/sveltekit/generateFiles/templates/error.ts b/packages/svelte-vscode/src/sveltekit/generateFiles/templates/error.ts
new file mode 100644
index 00000000..70eff270
--- /dev/null
+++ b/packages/svelte-vscode/src/sveltekit/generateFiles/templates/error.ts
@@ -0,0 +1,21 @@
+import { GenerateConfig } from '../types';
+
+export default async function (config: GenerateConfig) {
+    const ts = `
+<script lang="ts">
+    import { page } from '$app/stores';
+</script>
+
+<h1>{$page.status}: {$page.error.message}</h1>
+    `.trim();
+
+    const js = `
+<script>
+    import { page } from '$app/stores';
+</script>
+
+<h1>{$page.status}: {$page.error.message}</h1>
+    `.trim();
+
+    return config.typescript ? ts : js;
+}
diff --git a/packages/svelte-vscode/src/sveltekit/generateFiles/templates/layout-load.ts b/packages/svelte-vscode/src/sveltekit/generateFiles/templates/layout-load.ts
new file mode 100644
index 00000000..8fd4c4d9
--- /dev/null
+++ b/packages/svelte-vscode/src/sveltekit/generateFiles/templates/layout-load.ts
@@ -0,0 +1,20 @@
+import { GenerateConfig } from '../types';
+
+export default async function (config: GenerateConfig) {
+    const ts = `
+import type { LayoutLoad } from './$types';
+
+export const load: LayoutLoad = async () => {
+    return {};
+};
+    `.trim();
+
+    const js = `
+/** @type {import('./$types').LayoutLoad} */
+export async function load() {
+    return {};
+}
+    `.trim();
+
+    return config.typescript ? ts : js;
+}
diff --git a/packages/svelte-vscode/src/sveltekit/generateFiles/templates/layout-server.ts b/packages/svelte-vscode/src/sveltekit/generateFiles/templates/layout-server.ts
new file mode 100644
index 00000000..a954664b
--- /dev/null
+++ b/packages/svelte-vscode/src/sveltekit/generateFiles/templates/layout-server.ts
@@ -0,0 +1,20 @@
+import { GenerateConfig } from '../types';
+
+export default async function (config: GenerateConfig) {
+    const ts = `
+import type { LayoutServerLoad } from './$types';
+
+export const load: LayoutServerLoad = async () => {
+    return {};
+};
+    `.trim();
+
+    const js = `
+/** @type {import('./$types').LayoutServerLoad} */
+export async function load() {
+    return {};
+}
+    `.trim();
+
+    return config.typescript ? ts : js;
+}
diff --git a/packages/svelte-vscode/src/sveltekit/generateFiles/templates/layout.ts b/packages/svelte-vscode/src/sveltekit/generateFiles/templates/layout.ts
new file mode 100644
index 00000000..6173a771
--- /dev/null
+++ b/packages/svelte-vscode/src/sveltekit/generateFiles/templates/layout.ts
@@ -0,0 +1,20 @@
+import { GenerateConfig } from '../types';
+
+export default async function (config: GenerateConfig) {
+    const ts = `
+<script lang="ts">
+    import type { LayoutData } from './$types';
+    
+    export let data: LayoutData;
+</script>
+    `.trim();
+
+    const js = `
+<script>
+    /** @type {import('./$types').LayoutData} */
+    export let data;
+</script>
+    `.trim();
+
+    return config.typescript ? ts : js;
+}
diff --git a/packages/svelte-vscode/src/sveltekit/generateFiles/templates/page-load.ts b/packages/svelte-vscode/src/sveltekit/generateFiles/templates/page-load.ts
new file mode 100644
index 00000000..29c32282
--- /dev/null
+++ b/packages/svelte-vscode/src/sveltekit/generateFiles/templates/page-load.ts
@@ -0,0 +1,20 @@
+import { GenerateConfig } from '../types';
+
+export default async function (config: GenerateConfig) {
+    const ts = `
+import type { PageLoad } from './$types';
+
+export const load: PageLoad = async () => {
+    return {};
+};
+    `.trim();
+
+    const js = `
+/** @type {import('./$types').PageLoad} */
+export async function load() {
+    return {};
+};
+    `.trim();
+
+    return config.typescript ? ts : js;
+}
diff --git a/packages/svelte-vscode/src/sveltekit/generateFiles/templates/page-server.ts b/packages/svelte-vscode/src/sveltekit/generateFiles/templates/page-server.ts
new file mode 100644
index 00000000..46dea41d
--- /dev/null
+++ b/packages/svelte-vscode/src/sveltekit/generateFiles/templates/page-server.ts
@@ -0,0 +1,20 @@
+import { GenerateConfig } from '../types';
+
+export default async function (config: GenerateConfig) {
+    const ts = `
+import type { PageServerLoad } from './$types';
+
+export const load: PageServerLoad = async () => {
+    return {};
+};
+    `.trim();
+
+    const js = `
+/** @type {import('./$types').PageServerLoad} */
+export async function load() {
+    return {};
+};
+    `.trim();
+
+    return config.typescript ? ts : js;
+}
diff --git a/packages/svelte-vscode/src/sveltekit/generateFiles/templates/page.ts b/packages/svelte-vscode/src/sveltekit/generateFiles/templates/page.ts
new file mode 100644
index 00000000..95ac3a61
--- /dev/null
+++ b/packages/svelte-vscode/src/sveltekit/generateFiles/templates/page.ts
@@ -0,0 +1,20 @@
+import { GenerateConfig } from '../types';
+
+export default async function (config: GenerateConfig) {
+    const ts = `
+<script lang="ts">
+    import type { PageData } from './$types';
+    
+    export let data: PageData;
+</script>
+    `.trim();
+
+    const js = `
+<script>
+    /** @type {import('./$types').PageData} */
+    export let data;
+</script>
+    `.trim();
+
+    return config.typescript ? ts : js;
+}
diff --git a/packages/svelte-vscode/src/sveltekit/generateFiles/templates/server.ts b/packages/svelte-vscode/src/sveltekit/generateFiles/templates/server.ts
new file mode 100644
index 00000000..5be1ef7a
--- /dev/null
+++ b/packages/svelte-vscode/src/sveltekit/generateFiles/templates/server.ts
@@ -0,0 +1,20 @@
+import { GenerateConfig } from '../types';
+
+export default async function generate(config: GenerateConfig) {
+    const ts = `
+import type { RequestHandler } from './$types';
+
+export const GET: RequestHandler = async () => {
+    return new Response();
+};
+    `.trim();
+
+    const js = `
+/** @type {import('./$types').RequestHandler} */
+export async function GET() {
+    return new Response();
+};
+    `.trim();
+
+    return config.typescript ? ts : js;
+}
diff --git a/packages/svelte-vscode/src/sveltekit/generateFiles/types.ts b/packages/svelte-vscode/src/sveltekit/generateFiles/types.ts
new file mode 100644
index 00000000..c61ec54b
--- /dev/null
+++ b/packages/svelte-vscode/src/sveltekit/generateFiles/types.ts
@@ -0,0 +1,42 @@
+export enum CommandType {
+    PAGE = 'svelte.kit.generatePage',
+    PAGE_LOAD = 'svelte.kit.generatePageLoad',
+    PAGE_SERVER = 'svelte.kit.generatePageServerLoad',
+    LAYOUT = 'svelte.kit.generateLayout',
+    LAYOUT_LOAD = 'svelte.kit.generateLayoutLoad',
+    LAYOUT_SERVER = 'svelte.kit.generateLayoutServerLoad',
+    SERVER = 'svelte.kit.generateServer',
+    ERROR = 'svelte.kit.generateError',
+    MULTIPLE = 'svelte.kit.generateMultipleFiles'
+}
+
+export enum FileType {
+    SCRIPT,
+    PAGE
+}
+
+export enum ResourceType {
+    PAGE,
+    PAGE_LOAD,
+    PAGE_SERVER,
+    LAYOUT,
+    LAYOUT_LOAD,
+    LAYOUT_SERVER,
+    SERVER,
+    ERROR
+}
+
+export type Resource = {
+    type: FileType;
+    filename: string;
+    title: string;
+    generate: (config: GenerateConfig) => Promise<string>;
+};
+
+export interface GenerateConfig {
+    path: string;
+    typescript: boolean;
+    pageExtension: string;
+    scriptExtension: string;
+    resources: Resource[];
+}
diff --git a/packages/svelte-vscode/src/sveltekit/index.ts b/packages/svelte-vscode/src/sveltekit/index.ts
new file mode 100644
index 00000000..9b176c39
--- /dev/null
+++ b/packages/svelte-vscode/src/sveltekit/index.ts
@@ -0,0 +1,6 @@
+import { ExtensionContext } from 'vscode';
+import { addGenerateKitRouteFilesCommand } from './generateFiles';
+
+export function setupSvelteKit(context: ExtensionContext) {
+    addGenerateKitRouteFilesCommand(context);
+}
diff --git a/packages/svelte-vscode/src/sveltekit/utils.ts b/packages/svelte-vscode/src/sveltekit/utils.ts
new file mode 100644
index 00000000..07ffc574
--- /dev/null
+++ b/packages/svelte-vscode/src/sveltekit/utils.ts
@@ -0,0 +1,29 @@
+import * as path from 'path';
+import { Uri, workspace } from 'vscode';
+
+export async function fileExists(file: string) {
+    try {
+        await workspace.fs.stat(Uri.file(file));
+        return true;
+    } catch (err) {
+        return false;
+    }
+}
+
+export async function findFile(searchPath: string, fileName: string) {
+    for (;;) {
+        const filePath = path.join(searchPath, fileName);
+        if (await fileExists(filePath)) {
+            return filePath;
+        }
+        const parentPath = path.dirname(searchPath);
+        if (parentPath === searchPath) {
+            return;
+        }
+        searchPath = parentPath;
+    }
+}
+
+export async function checkIfTypescriptProject(path: string) {
+    return !!(await findFile(path, 'tsconfig.json'));
+}
diff --git a/packages/svelte-vscode/syntaxes/svelte.tmLanguage.src.yaml b/packages/svelte-vscode/syntaxes/svelte.tmLanguage.src.yaml
index 62cf699a..ec56b84d 100644
--- a/packages/svelte-vscode/syntaxes/svelte.tmLanguage.src.yaml
+++ b/packages/svelte-vscode/syntaxes/svelte.tmLanguage.src.yaml
@@ -93,11 +93,11 @@ injections:
     patterns:
     # Matches the store accessor symbol.
     # i.e. the `$` in `$myStore`.
-    - match: (?<![_$[:alnum:]])\$(?=[_[:alpha:]][_$[:alnum:]]*)
+    - match: (?<![_$./'"[:alnum:]])\$(?=[_[:alpha:]][_$[:alnum:]]*)
       name: punctuation.definition.variable.svelte
     # Matches the built-in constants/keywords that Svelte supports.
     # e.g. `$$restProps`.
-    - match: (?<![_$[:alnum:]])(\$\$)(?=props|restProps|slots)
+    - match: (?<![_$./'"[:alnum:]])(\$\$)(?=props|restProps|slots)
       name: punctuation.definition.variable.svelte
 
   # - PUG -
diff --git a/packages/svelte2tsx/src/htmlxtojsx_v2/nodes/EachBlock.ts b/packages/svelte2tsx/src/htmlxtojsx_v2/nodes/EachBlock.ts
index b3bc07d0..0af1981d 100644
--- a/packages/svelte2tsx/src/htmlxtojsx_v2/nodes/EachBlock.ts
+++ b/packages/svelte2tsx/src/htmlxtojsx_v2/nodes/EachBlock.ts
@@ -34,13 +34,13 @@ export function handleEach(str: MagicString, eachBlock: BaseNode): void {
         transforms = [
             `{ const $$_each = __sveltets_2_ensureArray(${containsComma ? '(' : ''}`,
             [eachBlock.expression.start, eachBlock.expression.end],
-            `${containsComma ? ')' : ''}); for(const `,
+            `${containsComma ? ')' : ''}); for(let `,
             [eachBlock.context.start, eachBlock.context.end],
             ' of $$_each){'
         ];
     } else {
         transforms = [
-            'for(const ',
+            'for(let ',
             [eachBlock.context.start, eachBlock.context.end],
             ` of __sveltets_2_ensureArray(${containsComma ? '(' : ''}`,
             [eachBlock.expression.start, eachBlock.expression.end],


Test patch:
diff --git a/packages/language-server/test/plugins/typescript/TypescriptPlugin.test.ts b/packages/language-server/test/plugins/typescript/TypescriptPlugin.test.ts
index c7ad1ba3..ddd984fc 100644
--- a/packages/language-server/test/plugins/typescript/TypescriptPlugin.test.ts
+++ b/packages/language-server/test/plugins/typescript/TypescriptPlugin.test.ts
@@ -203,7 +203,7 @@ function test(useNewTransformation: boolean) {
                     },
                     {
                         name: 'foo',
-                        kind: 14,
+                        kind: 13,
                         location: {
                             uri: getUri('documentsymbols.svelte'),
                             range: {
diff --git a/packages/svelte-vscode/test/grammar/samples/store/input.svelte b/packages/svelte-vscode/test/grammar/samples/store/input.svelte
new file mode 100644
index 00000000..1a786267
--- /dev/null
+++ b/packages/svelte-vscode/test/grammar/samples/store/input.svelte
@@ -0,0 +1,7 @@
+<script>
+import './$types';
+import '$lib/foo';
+$bar;
+</script>
+
+{$bar}
diff --git a/packages/svelte-vscode/test/grammar/samples/store/input.svelte.snap b/packages/svelte-vscode/test/grammar/samples/store/input.svelte.snap
new file mode 100644
index 00000000..b7611c7a
--- /dev/null
+++ b/packages/svelte-vscode/test/grammar/samples/store/input.svelte.snap
@@ -0,0 +1,22 @@
+><script>
+#^ source.svelte meta.script.svelte meta.tag.start.svelte punctuation.definition.tag.begin.svelte
+# ^^^^^^ source.svelte meta.script.svelte meta.tag.start.svelte entity.name.tag.svelte
+#       ^ source.svelte meta.script.svelte meta.tag.start.svelte punctuation.definition.tag.end.svelte
+>import './$types';
+#^^^^^^^^^^^^^^^^^^^ source.svelte meta.script.svelte meta.embedded.block.svelte source.js
+>import '$lib/foo';
+#^^^^^^^^^^^^^^^^^^^ source.svelte meta.script.svelte meta.embedded.block.svelte source.js
+>$bar;
+#^ source.svelte meta.script.svelte meta.embedded.block.svelte source.js punctuation.definition.variable.svelte
+# ^^^^^ source.svelte meta.script.svelte meta.embedded.block.svelte source.js
+></script>
+#^^ source.svelte meta.script.svelte meta.tag.end.svelte punctuation.definition.tag.begin.svelte
+#  ^^^^^^ source.svelte meta.script.svelte meta.tag.end.svelte entity.name.tag.svelte
+#        ^ source.svelte meta.script.svelte meta.tag.end.svelte punctuation.definition.tag.end.svelte
+>
+>{$bar}
+#^ source.svelte punctuation.section.embedded.begin.svelte
+# ^ source.svelte meta.embedded.expression.svelte source.ts punctuation.definition.variable.svelte
+#  ^^^ source.svelte meta.embedded.expression.svelte source.ts
+#     ^ source.svelte punctuation.section.embedded.end.svelte
+>
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/blocks-without-whitespace-inbetween/expectedv2.js b/packages/svelte2tsx/test/htmlx2jsx/samples/blocks-without-whitespace-inbetween/expectedv2.js
index 13efb020..fcdd24ae 100644
--- a/packages/svelte2tsx/test/htmlx2jsx/samples/blocks-without-whitespace-inbetween/expectedv2.js
+++ b/packages/svelte2tsx/test/htmlx2jsx/samples/blocks-without-whitespace-inbetween/expectedv2.js
@@ -1,1 +1,1 @@
-if(name == "world"){ }  for(const y of __sveltets_2_ensureArray(x)){ }   { const $$_value = await (x);{ const y = $$_value;  }}if(bla){ }
\ No newline at end of file
+if(name == "world"){ }  for(let y of __sveltets_2_ensureArray(x)){ }   { const $$_value = await (x);{ const y = $$_value;  }}if(bla){ }
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/each-block-basic-sequence/expectedv2.js b/packages/svelte2tsx/test/htmlx2jsx/samples/each-block-basic-sequence/expectedv2.js
index 4dc15acf..915d7522 100644
--- a/packages/svelte2tsx/test/htmlx2jsx/samples/each-block-basic-sequence/expectedv2.js
+++ b/packages/svelte2tsx/test/htmlx2jsx/samples/each-block-basic-sequence/expectedv2.js
@@ -1,3 +1,3 @@
-  for(const item of __sveltets_2_ensureArray((true, items))){
+  for(let item of __sveltets_2_ensureArray((true, items))){
      { svelteHTML.createElement("div", {});item; }
 }
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/each-block-basic/expectedv2.js b/packages/svelte2tsx/test/htmlx2jsx/samples/each-block-basic/expectedv2.js
index cfc79a8f..fae8223c 100644
--- a/packages/svelte2tsx/test/htmlx2jsx/samples/each-block-basic/expectedv2.js
+++ b/packages/svelte2tsx/test/htmlx2jsx/samples/each-block-basic/expectedv2.js
@@ -1,3 +1,3 @@
-  for(const item of __sveltets_2_ensureArray(items)){
+  for(let item of __sveltets_2_ensureArray(items)){
      { svelteHTML.createElement("div", {});item; }
 }
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/each-block-index/expectedv2.js b/packages/svelte2tsx/test/htmlx2jsx/samples/each-block-index/expectedv2.js
index b58cfffb..bbfd903f 100644
--- a/packages/svelte2tsx/test/htmlx2jsx/samples/each-block-index/expectedv2.js
+++ b/packages/svelte2tsx/test/htmlx2jsx/samples/each-block-index/expectedv2.js
@@ -1,3 +1,3 @@
-  for(const item of __sveltets_2_ensureArray(items)){let i = 1;
+  for(let item of __sveltets_2_ensureArray(items)){let i = 1;
      { svelteHTML.createElement("div", {});item;i; }
 }
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/each-block-key-else/expectedv2.js b/packages/svelte2tsx/test/htmlx2jsx/samples/each-block-key-else/expectedv2.js
index 75e69611..0a2b43cd 100644
--- a/packages/svelte2tsx/test/htmlx2jsx/samples/each-block-key-else/expectedv2.js
+++ b/packages/svelte2tsx/test/htmlx2jsx/samples/each-block-key-else/expectedv2.js
@@ -1,4 +1,4 @@
-   for(const item of __sveltets_2_ensureArray(items)){let i = 1;item.id;
+   for(let item of __sveltets_2_ensureArray(items)){let i = 1;item.id;
      { svelteHTML.createElement("div", {});item;i; }
 }
      { svelteHTML.createElement("div", {});  }
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/each-block-key/expectedv2.js b/packages/svelte2tsx/test/htmlx2jsx/samples/each-block-key/expectedv2.js
index 04cc9851..851462a4 100644
--- a/packages/svelte2tsx/test/htmlx2jsx/samples/each-block-key/expectedv2.js
+++ b/packages/svelte2tsx/test/htmlx2jsx/samples/each-block-key/expectedv2.js
@@ -1,3 +1,3 @@
-   for(const item of __sveltets_2_ensureArray(items)){let i = 1;item.id;
+   for(let item of __sveltets_2_ensureArray(items)){let i = 1;item.id;
      { svelteHTML.createElement("div", {});item;i; }
 }
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/each-block-nullish-coalescing/expectedv2.js b/packages/svelte2tsx/test/htmlx2jsx/samples/each-block-nullish-coalescing/expectedv2.js
index 85fbdc6e..b910227b 100644
--- a/packages/svelte2tsx/test/htmlx2jsx/samples/each-block-nullish-coalescing/expectedv2.js
+++ b/packages/svelte2tsx/test/htmlx2jsx/samples/each-block-nullish-coalescing/expectedv2.js
@@ -1,3 +1,3 @@
-  for(const item of __sveltets_2_ensureArray(items ?? [])){
+  for(let item of __sveltets_2_ensureArray(items ?? [])){
      { svelteHTML.createElement("div", {});item; }
 }
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/each-block-optional-chaining/expectedv2.js b/packages/svelte2tsx/test/htmlx2jsx/samples/each-block-optional-chaining/expectedv2.js
index 4d5eed95..2627ebde 100644
--- a/packages/svelte2tsx/test/htmlx2jsx/samples/each-block-optional-chaining/expectedv2.js
+++ b/packages/svelte2tsx/test/htmlx2jsx/samples/each-block-optional-chaining/expectedv2.js
@@ -1,3 +1,3 @@
-  for(const item of __sveltets_2_ensureArray(someObject?.items)){
+  for(let item of __sveltets_2_ensureArray(someObject?.items)){
      { svelteHTML.createElement("div", {});item; }
 }
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/editing-animation/expectedv2.js b/packages/svelte2tsx/test/htmlx2jsx/samples/editing-animation/expectedv2.js
index 76b19847..b5f63b64 100644
--- a/packages/svelte2tsx/test/htmlx2jsx/samples/editing-animation/expectedv2.js
+++ b/packages/svelte2tsx/test/htmlx2jsx/samples/editing-animation/expectedv2.js
@@ -1,3 +1,3 @@
-   for(const item of __sveltets_2_ensureArray(list)){item;
+   for(let item of __sveltets_2_ensureArray(list)){item;
 	 { svelteHTML.createElement("li", {  });__sveltets_2_ensureAnimation(flip(svelteHTML.mapElementTag('li'),__sveltets_2_AnimationMove,(opt?.)));item; }
 }
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/editing-const/expectedv2.js b/packages/svelte2tsx/test/htmlx2jsx/samples/editing-const/expectedv2.js
index d2b8c0c5..06115798 100644
--- a/packages/svelte2tsx/test/htmlx2jsx/samples/editing-const/expectedv2.js
+++ b/packages/svelte2tsx/test/htmlx2jsx/samples/editing-const/expectedv2.js
@@ -1,3 +1,3 @@
-  for(const str of __sveltets_2_ensureArray([''])){
+  for(let str of __sveltets_2_ensureArray([''])){
     const lower = str.;
 }
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/if-nested-each-block-shadowed/expectedv2.js b/packages/svelte2tsx/test/htmlx2jsx/samples/if-nested-each-block-shadowed/expectedv2.js
index 58188d75..d051b7db 100644
--- a/packages/svelte2tsx/test/htmlx2jsx/samples/if-nested-each-block-shadowed/expectedv2.js
+++ b/packages/svelte2tsx/test/htmlx2jsx/samples/if-nested-each-block-shadowed/expectedv2.js
@@ -1,13 +1,13 @@
 if(hello){
-       for(const hello of __sveltets_2_ensureArray(items)){let i = 1;hello.id;
+       for(let hello of __sveltets_2_ensureArray(items)){let i = 1;hello.id;
          { svelteHTML.createElement("div", {});hello;i; }
         if(hello){
-              for(const hello of __sveltets_2_ensureArray(items)){
+              for(let hello of __sveltets_2_ensureArray(items)){
                 if(hello){
                     hello;
                 }
             }
-              for(const foo of __sveltets_2_ensureArray(items)){
+              for(let foo of __sveltets_2_ensureArray(items)){
                 const hello = foo;
                 if(hello){
                     hello;
@@ -20,7 +20,7 @@ if(hello){
         }
     
     if(hi && bye){
-          for(const bye of __sveltets_2_ensureArray(items)){
+          for(let bye of __sveltets_2_ensureArray(items)){
              { svelteHTML.createElement("div", {});bye; }
         }
             if(bye){
@@ -28,17 +28,17 @@ if(hello){
             }
         
     } else if (cool){
-          for(const item of __sveltets_2_ensureArray(items)){let cool = 1;
+          for(let item of __sveltets_2_ensureArray(items)){let cool = 1;
              { svelteHTML.createElement("div", {});item;cool; }
         }
     }else{
-          for(const hello of __sveltets_2_ensureArray(items)){
+          for(let hello of __sveltets_2_ensureArray(items)){
              { svelteHTML.createElement("div", {});hello; }
         }
     }
 }
 
-  for(const hello of __sveltets_2_ensureArray(items)){let i = 1;
+  for(let hello of __sveltets_2_ensureArray(items)){let i = 1;
     if(hello && i && bye){
         hello; i; bye;
     } else if (hello && i && bye){
diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/if-nested-each-block/expectedv2.js b/packages/svelte2tsx/test/htmlx2jsx/samples/if-nested-each-block/expectedv2.js
index fbea6081..fb12d6d4 100644
--- a/packages/svelte2tsx/test/htmlx2jsx/samples/if-nested-each-block/expectedv2.js
+++ b/packages/svelte2tsx/test/htmlx2jsx/samples/if-nested-each-block/expectedv2.js
@@ -1,19 +1,19 @@
 if(hello){
-       for(const item of __sveltets_2_ensureArray(items)){let i = 1;item.id;
+       for(let item of __sveltets_2_ensureArray(items)){let i = 1;item.id;
          { svelteHTML.createElement("div", {});item;i; }
     }
     if(hi && bye){
-          for(const item of __sveltets_2_ensureArray(items)){
+          for(let item of __sveltets_2_ensureArray(items)){
              { svelteHTML.createElement("div", {});item; }
         }
              { svelteHTML.createElement("p", {});  }
         
     } else if (cool){
-          for(const item of __sveltets_2_ensureArray(items)){let i = 1;
+          for(let item of __sveltets_2_ensureArray(items)){let i = 1;
              { svelteHTML.createElement("div", {});item;i; }
         }
     }else{
-          for(const item of __sveltets_2_ensureArray(items)){
+          for(let item of __sveltets_2_ensureArray(items)){
              { svelteHTML.createElement("div", {});item; }
         }
     }
diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/nested-if-else-if-and-each-block/expectedv2.js b/packages/svelte2tsx/test/htmlx2jsx/samples/nested-if-else-if-and-each-block/expectedv2.js
index cd383cd1..45ffe209 100644
--- a/packages/svelte2tsx/test/htmlx2jsx/samples/nested-if-else-if-and-each-block/expectedv2.js
+++ b/packages/svelte2tsx/test/htmlx2jsx/samples/nested-if-else-if-and-each-block/expectedv2.js
@@ -1,5 +1,5 @@
 if(true){
   if(true){} else if (true){}
 }else{
-    for(const _ of __sveltets_2_ensureArray([])){}
+    for(let _ of __sveltets_2_ensureArray([])){}
 }
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/circle-drawer-example/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/circle-drawer-example/expectedv2.ts
index 37f1ef04..f680890f 100644
--- a/packages/svelte2tsx/test/svelte2tsx/samples/circle-drawer-example/expectedv2.ts
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/circle-drawer-example/expectedv2.ts
@@ -70,7 +70,7 @@ async () => {
  }
 
   { svelteHTML.createElement("svg", {  "on:click":handleClick,});
-	  for(const circle of __sveltets_2_ensureArray(circles)){
+	  for(let circle of __sveltets_2_ensureArray(circles)){
 		 { svelteHTML.createElement("circle", {            "cx":circle.cx,"cy":circle.cy,"r":circle.r,"on:click":event => select(circle, event),"on:contextmenu":() => {
 				adjusting = !adjusting;
 				if (adjusting) selected = circle;
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-each/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-each/expectedv2.ts
index 83939d49..db173a02 100644
--- a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-each/expectedv2.ts
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-each/expectedv2.ts
@@ -1,10 +1,10 @@
 ///<reference types="svelte" />
 ;function render() {
 /*Ωignore_startΩ*/;const __sveltets_createSlot = __sveltets_2_createCreateSlot();/*Ωignore_endΩ*/
-async () => {  for(const item of __sveltets_2_ensureArray(items)){
+async () => {  for(let item of __sveltets_2_ensureArray(items)){
      { __sveltets_createSlot("default", { "a":item,});  }
 }
-  for(const { a } of __sveltets_2_ensureArray(items2)){
+  for(let { a } of __sveltets_2_ensureArray(items2)){
      { __sveltets_createSlot("second", {  a,});  }
 }};
 return { props: {}, slots: {'default': {a:__sveltets_1_unwrapArr(items)}, 'second': {a:(({ a }) => a)(__sveltets_1_unwrapArr(items2))}}, getters: {}, events: {} }}
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-nest-scope/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-nest-scope/expectedv2.ts
index d949f8f6..51c0c7c6 100644
--- a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-nest-scope/expectedv2.ts
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-nest-scope/expectedv2.ts
@@ -1,8 +1,8 @@
 ///<reference types="svelte" />
 ;function render() {
 /*Ωignore_startΩ*/;const __sveltets_createSlot = __sveltets_2_createCreateSlot();/*Ωignore_endΩ*/
-async () => {  for(const item of __sveltets_2_ensureArray(items)){
-      for(const { a } of __sveltets_2_ensureArray(item)){
+async () => {  for(let item of __sveltets_2_ensureArray(items)){
+      for(let { a } of __sveltets_2_ensureArray(item)){
          { __sveltets_createSlot("default", {a,});  }
     }
       { __sveltets_createSlot("second", {  a,}); }
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-object-key/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-object-key/expectedv2.ts
index 85174473..083c92d9 100644
--- a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-object-key/expectedv2.ts
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-object-key/expectedv2.ts
@@ -1,7 +1,7 @@
 ///<reference types="svelte" />
 ;function render() {
 /*Ωignore_startΩ*/;const __sveltets_createSlot = __sveltets_2_createCreateSlot();/*Ωignore_endΩ*/
-async () => {  for(const item of __sveltets_2_ensureArray(items)){
+async () => {  for(let item of __sveltets_2_ensureArray(items)){
      { __sveltets_createSlot("default", {             "a":item,"b":{ item },"c":{ item: 'abc' }.item,"d":{ item: item },"e":$item,"f":$item,...g,...item,});  }
 }};
 return { props: {}, slots: {'default': {a:__sveltets_1_unwrapArr(items), b:{ item:__sveltets_1_unwrapArr(items) }, c:{ item: 'abc' }.item, d:{ item: __sveltets_1_unwrapArr(items) }, e:$item, f:$item, ...g, ...__sveltets_1_unwrapArr(items)}}, getters: {}, events: {} }}
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-var-shadowing/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-var-shadowing/expectedv2.ts
index 23e60a6f..3e2a87ff 100644
--- a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-var-shadowing/expectedv2.ts
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-var-shadowing/expectedv2.ts
@@ -1,7 +1,7 @@
 ///<reference types="svelte" />
 ;function render() {
 /*Ωignore_startΩ*/;const __sveltets_createSlot = __sveltets_2_createCreateSlot();/*Ωignore_endΩ*/
-async () => {  { const $$_each = __sveltets_2_ensureArray(items); for(const items of $$_each){
+async () => {  { const $$_each = __sveltets_2_ensureArray(items); for(let items of $$_each){
      { __sveltets_createSlot("default", { "a":items,});  }
 }}};
 return { props: {}, slots: {'default': {a:__sveltets_1_unwrapArr(items)}}, getters: {}, events: {} }}
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/const-tag-each-destructure/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/const-tag-each-destructure/expectedv2.ts
index 6c22dbf7..683c3bcc 100644
--- a/packages/svelte2tsx/test/svelte2tsx/samples/const-tag-each-destructure/expectedv2.ts
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/const-tag-each-destructure/expectedv2.ts
@@ -14,7 +14,7 @@
 ;
 async () => {
 
-  for(const { width, height } of __sveltets_2_ensureArray(boxes)){
+  for(let { width, height } of __sveltets_2_ensureArray(boxes)){
 	const {area, volume} = calculate(width, height, constant);
 	const perimeter = (width + height) * constant;
 	const [_width, _height, sum] = [width * constant, height, width * constant + height];
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/const-tag-each/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/const-tag-each/expectedv2.ts
index 0878fcef..ef3faa97 100644
--- a/packages/svelte2tsx/test/svelte2tsx/samples/const-tag-each/expectedv2.ts
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/const-tag-each/expectedv2.ts
@@ -14,7 +14,7 @@
 ;
 async () => {
 
-  for(const box of __sveltets_2_ensureArray(boxes)){
+  for(let box of __sveltets_2_ensureArray(boxes)){
 	const {area, volume} = calculate(box.width, box.height, constant);
 	const perimeter = (box.width + box.height) * constant;
 	const [width, height, sum] = [box.width * constant, box.height, box.width * constant + box.height];