Skip to content

Latest commit

 

History

History
118 lines (82 loc) · 6.82 KB

overview.md

File metadata and controls

118 lines (82 loc) · 6.82 KB

Overview of the language-tools and how things work together

The language-tools repository is a monorepo containing several packages which are closely related to each other.

  • svelte2tsx - transforms Svelte code into JSX/TSX code
  • language-server - a language server adhering to the LSP
  • svelte-check - a command line tool to get diagnostics for Svelte code
  • svelte-vscode - the Svelte for VSCode extension

This is how they are related:

svelte-vscode |
              |-> language-server -> svelte2tsx
svelte-check  |

language-server overview

As briefly touched already, this is a language-server adhering to the Language Server Protocol (LSP). The protocol defines the communication between an editor or IDE and a language server that provides language features like auto complete, go to definition, find all references etc.

Our language-server can roughly be split into four areas:

  • CSS: Provides IntelliSense for the things inside <style>. Internally it mostly forwards stuff to the vscode-css-languageservice.
  • HTML: Provides IntelliSense for basic HTML tags like div, a etc. Internally it mostly forwards stuff to the vscode-html-languageservice. Svelte-specific template syntax is NOT handled here.
  • Svelte: Provides the diagnostics of the Svelte compiler. If you use preprocessors, those are invoked beforehand - that's why we need the svelte.config.js to know how to preprocess your files. It also does the formatting through prettier-plugin-svelte and other cross-cutting concerns like the "Extract Component" refactoring.
  • TypeScript/JavaScript: Provides IntelliSense for all JS/TS related stuff. This not only includes code inside <script> but also inside the template the moment you use any of the Svelte specifics like bindings or template syntax. svelte2tsx is used in here, and only here.

The last area, TS/JS, is where most of the work is done. Svelte is a mix of JavaScript/TypeScript inside script and more of it within Svelte's template syntax. To get a holistic view of the file, we need to account for both. This is also the reason why preprocessors such as svelte-preprocess cannot do type checking because it produces wrong diagnostics. To preprocess the script content, it only gets the content from the script. Think of this situation:

<script lang="ts">
    let a: number = 1;
</script>
{a}

svelte-preprocess only gets let a = 1 and the diagnostics would output that a is never used, which is wrong.

To get that holistic view, we need svelte2tsx.

svelte2tsx

To get a holistic view of Svelte files, we have two options:

  1. Write a language service ourselves which is capable of doing things like auto complete, go to definition, rename etc.
  2. Convert the Svelte code to something an existing language service can process which then does auto complete, go to definition, rename etc for us.

We chose the second option because TypeScript provides a language service which can do all the heavy lifting for us: We give it some files and then invoke methods like getQuickInfoAtPosition for hover info. These files need to be in a format the language service can understand: A form of JavaScript or TypeScript. svelte2tsx is the package which does this transformation: Pass in Svelte code and get back JS or TS code, depending on whether or not you use TS in Svelte.

svelte2tsx uses some ambient definitions which are loaded by the language server to do some of the transformations. It also provides type definitions which are recognized by the TypeScript compiler and define intrinsic elements, attributes and events - so if you ever get an error that a DOM attribute is not assignable to a DOM element, it's likely a missing declaration in there.

The code generated by svelte2tsx is not runnable in any way and does not actually exist at runtime when you run your app, it purely exists for the IntelliSense.

The code also returns source mappings so we know which position in the original code corresponds to which generated position.

The package is called svelte2tsx because originally it transformed the code to jsx/tsx - but that's no longer the case.

Integration of svelte2tsx into the language-server

This example shows how our language-server uses svelte2tsx:

Svelte file changes:

  1. Svelte file comes in
<script>
    export let world = 'name';
</script>

<h1>hello {world}</h1>
  1. Transform Svelte to TS/JS
<></>;
function render() {
    // -- the transformed script:
    let world = 'name';
    // -- the transformed template
    async () => {
        {
            svelteHTML.createElement('h1', {});
            world;
        }
    };
    // -- obtained props, slots and events,
    //    to get intellisense for this component in other components
    return { props: { world }, slots: {}, events: {} };
}
// -- generate a class
export default class _World_ extends createSvelte2TsxComponent(__sveltets_2_partial(render)) {}
  1. Pass that result to the TypeScript language service

User wants hover info:

  1. User hovers over a component property in the template
  2. Get generated file from original file
  3. Transform the position (line, character) where the user hovers from the original to the generated position
  4. Invoke TypeScript language service's method for hover info with the generated position
  5. Hand back results to user

As you can see, the language-server is mostly doing position transformations and then handing the main work off to the TypeScript language service. Sometimes we also need to do post processing of the results and work around quirks.

svelte-check

As mentioned above, preprocessors cannot do type checking. That's why we added svelte-check, a command line tool to get diagnostics for Svelte code. The tool is using the language-server to do this work. Inside the language-server we added an entry point for svelte-check which spins up the language server and just executes diagnostics.

svelte-vscode

This is the Svelte for VSCode extension most of you probably use. Syntax highlighting is provided through a JSON file which tells VSCode how to tokenize the input. To get all the intellisense capabilities, the extension uses the the language-server. VSCode implements the LSP and also provides a node library to do most of the wiring for us. Some things like special refactoring commands ("Extract Component") need some extra glue code, but that's all.