Repository navigation
[api] Expose API client modules from VS Code extension - #64647
Andrew Branch (andrewbranch) wants to merge 9 commits into
Conversation
| } | ||
|
|
||
| /** The selected TypeScript installation, bound to one language server initialization. */ | ||
| export interface TypeScriptSDK { |
There was a problem hiding this comment.
Type names also valid for bikeshedding
| export interface ExtensionAPI { | ||
| onLanguageServerInitialized: Event<void>; | ||
| export interface APIModules { | ||
| [exportPath: string]: unknown; |
There was a problem hiding this comment.
I opted to have a fallback here so you could use a newer import path than your types expose, but I'm not sure if there's much reason not to just install the latest types as soon as you want to use the latest features. Would it be better to remove this?
| } | ||
| const require = createRequire(manifestPath); | ||
| const modulePath = require.resolve(`${manifest.name}/${exportPath.slice("typescript/".length)}`); | ||
| return import(pathToFileURL(modulePath).href); |
There was a problem hiding this comment.
I assume we're stuck not using import.meta.resolve? Or someone's shim for it? Seems odd to pull out require just for this
| name: "vscode-typescript:pack", | ||
| hiddenFromTaskList: true, | ||
| dependencies: options.forRelease || usePublishedPlatformPackagesForVsix ? undefined : [buildNativePreviewPackages, cleanSignTempDirectory], | ||
| dependencies: options.forRelease || usePublishedPlatformPackagesForVsix ? undefined : [packNativePreviewPackages], |
There was a problem hiding this comment.
Where's the clean go?
| return { path: exe, version: "(local)", isLocal: true }; | ||
| return { | ||
| path: exe, | ||
| version: "(local)", |
There was a problem hiding this comment.
This is preexisting but now that we don't say tsgo (local), this just says (local) which always seemed wrong to me in the UI 😄
| packageJson.dependencies = { | ||
| typescript: embeddedTypeScriptPackageJson.name === "typescript" | ||
| ? embeddedTypeScriptPackageJson.version | ||
| : `npm:${embeddedTypeScriptPackageJson.name}@${embeddedTypeScriptPackageJson.version}`, | ||
| }; |
There was a problem hiding this comment.
Why do we have to set this? Just so we trick vsce?



Our VS Code extension already exposes a way for third-party extensions to connect a TypeScript API client to the user's currently running TypeScript LSP server. But since the API client and server need to be version-matched, and users can choose between a workspace version of TypeScript or one of potentially multiple built-in versions for their LSP server, the VS Code extension really needs to give those third-party extensions a way to resolve the correct version of the API client to use.
Previously, third-party extensions would do something like this:
With this PR, that will change to:
Of course, this means that your API client types are effectively a devDependency representing a peerDependency whose versions could be mismatched at runtime. Extensions should type check and test against multiple versions, and use runtime probes to conditionally access newer client features:
More guidance on version compatibility will be documented soon.
Doing the same thing from a third-party LSP server
If you're launching an LSP server that connects to the TypeScript API, the method above won't help you, since you can't access the extension API from another process. For convenience, the same typed module loader is exported from
"typescript/vscode". If you're bundling your extension, you can import from that module without bundling in the actual API client:VSIX structure
This changes VSIX packaging to include
node_modulescontainingtypescriptand@typescript/typescript-${os}-${arch}instead of the bare tsc executable inlib/.Manually tested the LSP + an API connection with the packed VSIX, that plus the nightly VSIX, and local dev, but extra eyes on the output are appreciated.