sveltejs/language-tools

TypeScript Plugin

dummdidumm opened this issue ยท 10 comments

Now implemented, please provide feedback

See this comment for more info

Original Post

The language-server has quite a few features by now. What's missing for the complete developer experience and intellisense though is crossing the boundaries of Svelte and JS/TS files. This can be achieved through a TypeScript plugin. The following features would be cool to have:

  • Rename variables in Svelte files, too, when renaming starts in a TS/JS file
  • Go to Svelte definition/file from a TS/JS file
  • Find references
  • Better diagnostics (#550)

Open Questions

  • How to connect the plugin to the running language server?
  • A TypeScript plugin has to be synchronous because all language service methods are synchronous. Our code is asynchronous because of the Source Mapping initialization. How to work around that?

Dumping some thoughts:

  • A TS plugin needs to be synchronous, meaning all methods like getDiagnostics need to be synchronous. This is not possible to do right now since we use source-map which asynchronously generates a SourceMapConsumer. So either we switch the library or we do some kind of "use latest processed if available"-logic. What we definitely cannot do is reuse the language server connection and do requestX, await response.
  • Because we cannot use the language server like the LSP protocol intends. Maybe it's possible to make the TypeScriptPlugin from the language server available for direct use while making sure it uses the same instance as the LS.

Idea for an incomplete solution

In the client inside extension.ts, add typescript/javascript as languages to deal with. Add logic to not active if it's not a Svelte project. Enhance the language server to deal with events from TypeScript and JavaScript files, too. This means things like "don't return usages from TS/JS files when find usages was triggered in a TS/JS file, as these will be shown twice then" or "route textdocument change events to DocumentManager only if it's a Svelte file". This will not solve the diagnostic errors problem because we cannot silence the TS server. It also doesn't work for rename because VS Code seems to go through the extensions one by one and stop at the first one providing rename functionality, which is the TS server - that's kind of a showstopper because this is likely the most valuable addition. Go to definition/find references/autocompletion/hover would work.

Hey I worked on a proof of concept today

2021-02-15.02-18-31_2.mp4

https://github.com/pushkine/ts-plugin-svelte/

Beside obvious bugs where everything gets stale this doesn't yet work with Components, might be a conflict with declare global module "*.svelte" or some tweaking to do with svelte2tsx

Looks good to me though, what do you think ?

This is a nice POC!

I though about this topic a little more and there are to me two ways to get to a solution:

Option 1: A "real" TS plugin (variant 1)

  1. Create performance-tests (#676)
  2. Replace async source-map with sync sourcemap-codec + own litte mapping function similar to how I did it in this ESLint plugin PR. Optionally reuse that logic in that PR to simplify+fix our mapping logic (could solve #666)
  3. Test if the changes in 2 did not hurt the performance using the tests implemented in 1. If there are no big performance hits, continue.
  4. Make all TypeScriptPlugin function synchronous - should be easily doable then because the only reason it was asynchronous was because of source-map
  5. Refactor out the functionality of autocompletion/rename etc of the TypeScriptPlugin into a separate package (which we hopefully do not have to deploy, only pull it out for internal reuse)
  6. Create a svelte-typescript-plugin. Both this plugin and the language-server use the internal package to provide TS/JS intellisense
  7. Incorporate the plugin into the VS Code extension. Add an option to enable the plugin (maybe off by default for a short beta time).

Steps 1/2 can be done in parallel, the others are best done one after another. If someone wants to help in one of these steps, please ping here so we can coordinate. Step 5 is best done by me or @jasonlyu123 because we know the code base best and this is a critical part.

Advante: A "real solution"
Disadvantage: Especially "get generated position from original position" is likely much slower. Fortunately, we don't have to do this often.

Option 2: A "fake" TS plugin

  1. Create a minimal TS plugin:
    • convert Svelte files through svelte2tsx
    • patch all messages and filter out everything related to Svelte files from the results except for rename and diagnostics. Leave diagnostics untouched (disadvantage: "X is declared here" messages/references from TS will show transformed tsx code). For rename, filter out Svelte file related changes, package them up into a command and send it to the language server for further processing.
  2. Update the language-server as outlined in my "Idea for an incomplete solution" for the other intellisense features

Advantage: We keep the language server as-is mostly
Disadvantage: Incomplete "diagnostics"-solution? Will this work for rename?

Option 3: A "real" TS plugin (variant 2)

This is a combination of option 1 and 2: Create a TS plugin which converts Svelte files through svelte2tsx and uses sourcemap-codec + custom mapping logic to get original positions. It will reimplement some of the logic inside the language server, which on one hand is some duplicated work, but it will keep the plugin clear of all additional logic/workarounds that are only needed within Svelte files.

Advantage: Separate, clean implementation
Disadvantage: Duplicated work

=========

Right now I think I prefer option 3. What do others think?

I also prefer option 3. Maybe we can export some functions from the plugin package or create another utility package to reuse some logic. For example, svelte-sys, source mapping utility, and source mapping workarounds.

First: Thanks for all the great work you are doing! You are creating a really good DX.

Now to the question:

I have created a typescript plugin that powers Twind IntelliSense for VS Code.

Is it possible or would it be possible to have the completions/diagnostics from that typescript plugin within svelte files using the same implementation?

I'm happy to contribute if there needs something to be implemented here. Or could give me some pointers/ideas on how that could be implemented in userland?

If this is the wrong issue I will move that into a separate issue.

Thanks for your time.

Thanks for the kind words! This question is unrelated to this issue. This issue is about implementing a TS plugin for Svelte. What you want is to hook into the Svelte language server. Please open a separate issue for that.

There exists a TypeScript plugin now which comes packaged with the VS Code extension and which you need to enable through the settings. It also is available standalone as a npm package if you need to use it outside of VS Code.
The one limitation is that you need to save changes of Svelte files to disk before they can be picked up inside TS/JS files.

Please test out the plugin and provide feedback. If you find bugs, please open separate issues for them.

It is still possible to compile the code with Rollup despite the Typescript errors, however this breaks the build step when using Sapper and Webpack with Typescript.

sveltejs/svelte#5817 (comment)

@dummdidumm should using this plugin resolve the 'has no exported member' issue with exported members from Svelte modules when building with webpack? Or is it only intended to help IDEs?

Only IDEs can use plugins, they are not loaded by tsc

Just following up on #1413 . I am using v105.13.0 of the extension and I do have svelte.enable-ts-plugin : true but the issue still persists on my work machine. Home machine is working fine. Anything I do/provide to help diagnose the issue?

Just FYI updating vscode from 1.64 to 1
65 fixed my concern.