-
-
Notifications
You must be signed in to change notification settings - Fork 205
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initially support - rename (doesn't work for prop renames yet) - diagnostics - find references - go to definition - go to implementation(s) This makes all files TSX hardcoded for now, it seems the TS language server is okay with importing tsx into js #580 #550 #342 #110
- Loading branch information
1 parent
c676a77
commit 505d7dc
Showing
20 changed files
with
1,220 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,3 @@ | ||
src/**/*.js | ||
src/**/*.d.ts | ||
src/tsconfig.tsbuildinfo | ||
dist | ||
node_modules | ||
tsconfig.tsbuildinfo |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
/node_modules | ||
/src | ||
tsconfig.json | ||
.gitignore | ||
internal.md |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# A TypeScript plugin for Svelte intellisense | ||
|
||
This plugin provides intellisense for interacting with Svelte files. It is in a very early stage, so expect bugs. So far the plugin supports | ||
|
||
- Rename | ||
- Find Usages | ||
- Go To Definition | ||
- Diagnostics | ||
|
||
Note that these features are only available within TS/JS files. Intellisense within Svelte files is provided by the [svelte-language-server](https://www.npmjs.com/package/svelte-language-server). | ||
|
||
## Usage | ||
|
||
The plugin comes packaged with the [Svelte for VS Code extension](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode). If you are using that one, you don't need to add it manually. | ||
|
||
Adding it manually: | ||
|
||
`npm install --save-dev typescript-svelte-plugin` | ||
|
||
Then add it to your `tsconfig.json` or `jsconfig.json`: | ||
|
||
``` | ||
{ | ||
"compilerOptions": { | ||
... | ||
"plugins": [{ | ||
"name": "typescript-svelte-plugin" | ||
}] | ||
} | ||
} | ||
``` | ||
|
||
## Limitations | ||
|
||
Changes to Svelte files are only recognized after they are saved to disk. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Notes on how this works internally | ||
|
||
To get a general understanding on how to write a TypeScript plugin, read [this how-to](https://github.com/microsoft/TypeScript/wiki/Writing-a-Language-Service-Plugin). | ||
|
||
However, for getting Svelte support inside TS/JS files, we need to do more than what's shown in the how-to. We need to | ||
|
||
- make TypeScript aware that Svelte files exist and can be loaded | ||
- present Svelte files to TypeScript in a way TypeScript understands | ||
- enhance the language service (the part that's shown in the how-to) | ||
|
||
To make TypeScript aware of Svelte files, we need to patch its module resolution algorithm. `.svelte` is not a valid file ending for TypeScript, so it searches for files like `.svelte.ts`. This logic is decorated in `src/module-loader` to also resolve Svelte files. They are resolved to file-type TSX/JSX, which leads us to the next obstacle: to present Svelte files to TypeScript in a way it understands. | ||
|
||
We achieve that by utilizing `svelte2tsx`, which transforms Svelte code into TSX/JSX code. We do that transformation by patching `readFile` of TypeScript's project service in `src/svelte-snapshots`: If a Svelte file is read, transform the code before returning it. During that we also patch the ScriptInfo that TypeScript uses to interact with files. We patch the methods that transform positions to offsets and vice versa and either do transforms on the generated or original code, depending on the situation. | ||
|
||
The last step is to enhance the language service. For that, we patch the desired methods and apply custom logic. Most of that is transforming the generated code positions to the original code positions. | ||
|
||
Along the way, we need to patch some internal methods, which is brittly and hacky, but to our knowledge there currently is no other way. | ||
|
||
## Limitations | ||
|
||
Currently, changes to Svelte files are only recognized after they are saved to disk. That could be changed by adding `"languages": ["svelte"]` to the plugin provide options. The huge disadvantage is that diagnostics, rename etc within Svelte files no longer stay in the control of the language-server, instead TS/JS starts interacting with Svelte files on a much deeper level, which would mean patching many more undocumented/private methods, and having less control of the situation overall. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
70 changes: 70 additions & 0 deletions
70
packages/typescript-plugin/src/language-service/completions.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import type ts from 'typescript/lib/tsserverlibrary'; | ||
import { Logger } from '../logger'; | ||
import { isSvelteFilePath, replaceDeep } from '../utils'; | ||
|
||
const componentPostfix = '__SvelteComponent_'; | ||
|
||
export function decorateCompletions(ls: ts.LanguageService, logger: Logger): void { | ||
const getCompletionsAtPosition = ls.getCompletionsAtPosition; | ||
ls.getCompletionsAtPosition = (fileName, position, options) => { | ||
const completions = getCompletionsAtPosition(fileName, position, options); | ||
if (!completions) { | ||
return completions; | ||
} | ||
return { | ||
...completions, | ||
entries: completions.entries.map((entry) => { | ||
if ( | ||
!isSvelteFilePath(entry.source || '') || | ||
!entry.name.endsWith(componentPostfix) | ||
) { | ||
return entry; | ||
} | ||
return { | ||
...entry, | ||
name: entry.name.slice(0, -componentPostfix.length) | ||
}; | ||
}) | ||
}; | ||
}; | ||
|
||
const getCompletionEntryDetails = ls.getCompletionEntryDetails; | ||
ls.getCompletionEntryDetails = ( | ||
fileName, | ||
position, | ||
entryName, | ||
formatOptions, | ||
source, | ||
preferences | ||
) => { | ||
const details = getCompletionEntryDetails( | ||
fileName, | ||
position, | ||
entryName, | ||
formatOptions, | ||
source, | ||
preferences | ||
); | ||
if (details || !isSvelteFilePath(source || '')) { | ||
return details; | ||
} | ||
|
||
// In the completion list we removed the component postfix. Internally, | ||
// the language service saved the list with the postfix, so details | ||
// won't match anything. Therefore add it back and remove it afterwards again. | ||
const svelteDetails = getCompletionEntryDetails( | ||
fileName, | ||
position, | ||
entryName + componentPostfix, | ||
formatOptions, | ||
source, | ||
preferences | ||
); | ||
if (!svelteDetails) { | ||
return undefined; | ||
} | ||
logger.debug('Found Svelte Component import completion details'); | ||
|
||
return replaceDeep(svelteDetails, componentPostfix, ''); | ||
}; | ||
} |
44 changes: 44 additions & 0 deletions
44
packages/typescript-plugin/src/language-service/definition.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import type ts from 'typescript/lib/tsserverlibrary'; | ||
import { Logger } from '../logger'; | ||
import { SvelteSnapshotManager } from '../svelte-snapshots'; | ||
import { isNotNullOrUndefined, isSvelteFilePath } from '../utils'; | ||
|
||
export function decorateGetDefinition( | ||
ls: ts.LanguageService, | ||
snapshotManager: SvelteSnapshotManager, | ||
logger: Logger | ||
): void { | ||
const getDefinitionAndBoundSpan = ls.getDefinitionAndBoundSpan; | ||
ls.getDefinitionAndBoundSpan = (fileName, position) => { | ||
const definition = getDefinitionAndBoundSpan(fileName, position); | ||
if (!definition?.definitions) { | ||
return definition; | ||
} | ||
|
||
return { | ||
...definition, | ||
definitions: definition.definitions | ||
.map((def) => { | ||
if (!isSvelteFilePath(def.fileName)) { | ||
return def; | ||
} | ||
|
||
const textSpan = snapshotManager | ||
.get(def.fileName) | ||
?.getOriginalTextSpan(def.textSpan); | ||
if (!textSpan) { | ||
return undefined; | ||
} | ||
return { | ||
...def, | ||
textSpan, | ||
// Spare the work for now | ||
originalTextSpan: undefined, | ||
contextSpan: undefined, | ||
originalContextSpan: undefined | ||
}; | ||
}) | ||
.filter(isNotNullOrUndefined) | ||
}; | ||
}; | ||
} |
45 changes: 45 additions & 0 deletions
45
packages/typescript-plugin/src/language-service/diagnostics.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import type ts from 'typescript/lib/tsserverlibrary'; | ||
import { Logger } from '../logger'; | ||
import { isSvelteFilePath } from '../utils'; | ||
|
||
export function decorateDiagnostics(ls: ts.LanguageService, logger: Logger): void { | ||
decorateSyntacticDiagnostics(ls); | ||
decorateSemanticDiagnostics(ls); | ||
decorateSuggestionDiagnostics(ls); | ||
} | ||
|
||
function decorateSyntacticDiagnostics(ls: ts.LanguageService): void { | ||
const getSyntacticDiagnostics = ls.getSyntacticDiagnostics; | ||
ls.getSyntacticDiagnostics = (fileName: string) => { | ||
// Diagnostics inside Svelte files are done | ||
// by the svelte-language-server / Svelte for VS Code extension | ||
if (isSvelteFilePath(fileName)) { | ||
return []; | ||
} | ||
return getSyntacticDiagnostics(fileName); | ||
}; | ||
} | ||
|
||
function decorateSemanticDiagnostics(ls: ts.LanguageService): void { | ||
const getSemanticDiagnostics = ls.getSemanticDiagnostics; | ||
ls.getSemanticDiagnostics = (fileName: string) => { | ||
// Diagnostics inside Svelte files are done | ||
// by the svelte-language-server / Svelte for VS Code extension | ||
if (isSvelteFilePath(fileName)) { | ||
return []; | ||
} | ||
return getSemanticDiagnostics(fileName); | ||
}; | ||
} | ||
|
||
function decorateSuggestionDiagnostics(ls: ts.LanguageService): void { | ||
const getSuggestionDiagnostics = ls.getSuggestionDiagnostics; | ||
ls.getSuggestionDiagnostics = (fileName: string) => { | ||
// Diagnostics inside Svelte files are done | ||
// by the svelte-language-server / Svelte for VS Code extension | ||
if (isSvelteFilePath(fileName)) { | ||
return []; | ||
} | ||
return getSuggestionDiagnostics(fileName); | ||
}; | ||
} |
Oops, something went wrong.