Skip to content

Instantly share code, notes, and snippets.

@orta
Last active August 7, 2020 22:03
Show Gist options
  • Save orta/ce05553de3fa9ba847ab410f29f874a4 to your computer and use it in GitHub Desktop.
Save orta/ce05553de3fa9ba847ab410f29f874a4 to your computer and use it in GitHub Desktop.
TypeScript and JS tooling in Svelte

Goal: TypeScript-style tooling in Svelte

Getting TS in Svelte might be a blocker for some, but I think the underlaying advantage comes in that the TS tools are just a stricter version of the JS tools. Everyone using Svelte gets improvements in tooling from this, not just folks with TS.

Three main steps:

  1. Migrate svelte-preprocess into core (and/or take enough functionality that it's not needed)
  2. Create "svelte/svelte-language-tools" repo in main org
    1. Add svelte-language-server
    2. Add svelte-vscode
    3. Create a new svelte check tool (or build this into the main CLI) which uses the LSP to validate - this makes LSP calls against all .svelte files to run TS/JS tooling for errors
  3. Do some work on svelte-langauge-server

Rationale

  1. This is to allow the editor support to work out of the box, and for a user to be able to safely make assumptions that if they wrote <script lang="typescript"> (and they have the dep) that it will work.

  2. Adding TypeScript support to Svelte can take a lot of tips from prior art done on Vue. Vetur acts as the higher level tooling framework for both providing the JS and TS tooling support in scripts. This pattern keeps most of the work in the LSP level, which means all editors can use it, the vscode extension would just be one client of many. If folks have other popular IDE integrations, they could live in this repo too.

  3. Adds a way to validate on CI, and for folks without the extension.

Work on Svelte Lanaguge Server

Luckily, both vscode-svelte and svelte-langauge-server have already been built and worked on by UnwrittenFun and he's interested in moving those to the org. They both are solid foundations to build upon, from looking through the code.

I think one of the first blockers on making it something recommended by default, is finding a way to make sure that reactive designators are handled in both JS and TS.

Because the $: syntax does more work than TypeScript can know about $: x = count + 1 would probably need transforming into something like const x = () => { count + 1 } as an in-memory transform, so that the variables exist in the "TS representation" of the LSP (but not on disk)

HTMLx

The quick route:

I think you can get autocompletion, but I'm not sure how feasible typechecking would be here in a cheap way.

For inside {} the LSP could make API requests to the known script tags above and request all the variables at the top level scope (basically the equivilient of adding a newline in that script and then checking what's available.)

In theory you could type check each one by making a unique TS document for all of them, with exposed globals from the main script - but that could be quite slow, and it's hard to say how well that could scale.

The long route:

Start work on a real LSP for htmlx.

@activescott
Copy link

Although not VSCode specific, and somewhat dated, I never had much of a problem getting Svelte and TypeScript to work together. It was also a very good experience in general to have the benefits of TypeScript along with svelte. A brief pointer on how to get it all working is at https://stackoverflow.com/a/48145824/51061

@orta
Copy link
Author

orta commented Mar 5, 2020

Interesting, I think this might need to be special cased in some way inside the LSP. It would need to be something unique anyway because of the custom this for this.fetch and this.error etc.

@mikedsharp
Copy link

Thanks for putting effort into this! One thing that I haven't seen mentioned much in the various discussion around Svelte TS support is proper handling of <script context="module">. The existing Svelte typescript preprocessing solutions break when variables are defined in module context, and also used in the component-level script tag, because Typescript doesn't know about the module-level variable definitions.

This comes up most often in Sapper code, e.g.

<script context="module" lang="typescript">
import processData from './processData';
async function getData() {
  let result = await this.fetch('/data').then((r) => r.json());
  return processData(result);
}

export async function preload() {
   return { data: await getData() };
}
</script>

<script lang="typescript">
  export let data;
  async function refresh() {
     // TS error because it doesn't know about getData
     data = await getData();
  }
</script>

Anyway, just wanted to bring this up in case it hasn't been a part of the design you're considering. Thanks again!

I'm running into this issue right now, are there any decent workarounds?

@orta
Copy link
Author

orta commented Aug 7, 2020

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment