These are the categories of remaining API features I think we need for 7.1, with a few specific items included below. Costs are very rough estimates and hopefully skew high.
This list does not include non-feature work like bug fixes, performance improvements, or docs, which are all important too.
1. TS Server Plugin Replacements
1A. Content mappers
1B. LSP client middleware registration
TS Server plugins have the ability to directly modify LS requests/responses. Existing TS 7 LSP/API integration only allows extensions to add TS-augmented providers, and cannot filter/modify any stock responses or alter the TS 7 editor behavior in any way. Our extension can expose a command or cross-extension API allowing other extensions to register callbacks that we can invoke in middleware in vscode-languageclient. (This is not a very well documented feature of the client, but it exists.)
- Needed by: Angular (diagnostic filtering), likely others
- Cost: 1 dev-week
2. Top-level utility APIs
Currently, almost all APIs require obtaining a snapshot containing a full program build of the project/files being worked on. This is a pretty good model for working within a tsconfig-defined project on disk, especially if needing to update the project over time, but awkward for many kinds of ad-hoc analysis; it’s not a natural substitute for ts.createProgram much less ts.createSourceFile.
This list is not exhaustive, but these are the top priority ones people have asked for.
2A. createProgram(options)
This will still be backed by a snapshot, but provides the opportunity to specify rootFiles and compilerOptions instead of needing to resolve a tsconfig file from disk. On the server side, I'm not sure whether the snapshot should be derived from an existing snapshot or should be built entirely from scratch. Either way, it should be an idempotent operation (other open projects not included in the returned snapshot, new snapshot not adopted by the project session). We’ll need to expose the underlying Project somehow (program.project parent pointer seems fine) and make the returned Program disposable (disposing its snapshot, since this API will guarantee 1:1 mapping between Program and Snapshot).
Note: This dovetails with 3B and may be better suited to be folded into that.
- Needed by: likely ts-morph, many others
- Cost: 1 dev-week
2B. createSourceFile(fileName, sourceText, options), createSourceFileFromFile(fileName, options)
Quick way to get an isolated AST from text or disk. Open question as to whether this can be truly stateless, or should be server-tracked to allow for a follow-up bindSourceFile, but the latter could be added in a backward-compatible way if needed. May be better to introduce a parameter indicating whether to send binder symbols back in the same response.
- Needed by: ts-blank-space, likely ts-morph, many others
- Cost: <1 dev-week
2C. transpileModule(sourceText, options), transpileModuleFromFile(fileName, options), transpileDeclaration(sourceText, options), transpileDeclarationFromFile(fileName, options)
Quick way to transpile TypeScript code to JavaScript or declaration files without needing a full program or project context.
3. CLI-wrapper APIs
We’ve discussed high-level APIs that would make it easy (or at least possible) to create a CLI tool that performs similar functions to tsc, with modifications that can only be achieved through API usage. Some of the scenarios motivating this will be handled by content mappers (#4712) instead, but I think Angular is still interested in this.
I’m treating --watch functionality as out of scope for now, because the TS 6 public API didn’t provide watching.
3A. parseCommandLine and related functions
High priority:
parseCommandLine(commandLine: readonly string[])
readConfigFile
parseJsonConfigFileContent(json, { configDirectory } | { configFileName })
Lower priority: other non-internal exported functions from commandLineParser.ts that have equivalents in Go
- Needed by: Google
- Cost: <1 dev-week
3B. Solution builder APIs, createIncrementalProgram
Budgeting more time for this because the API is built on top of the LSP server’s project layer, which currently does not integrate with the solution builder whatsoever. It may turn out to be simple, but it's a new integration point into a complex system.
- Needed by: ts-loader, Bloomberg, Angular?
- Cost: 2 dev-weeks
3C. Custom transformers
Much of the reason to create a CLI wrapper is making emit modifications. I think we can support an API very similar to Strada here by fetching the JS emit as a SourceFile (AST), running custom transformations client-side, and sending the transformed AST back to the server for final emit (basically printNode). I think the infrastructure to do this already exists, but may be missing a lot of emit-node metadata required to do proper comment preservation and formatting.
- Needed by: Angular, Google, ts-loader
- Cost: 2 dev-weeks (less for MVP)
3D. ts.formatDiagnostics, ts.formatDiagnosticsWithColorAndContext
formatDiagnostics can likely be a pure client-side port from Strada. The complication with formatDiagnosticsWithColorAndContext is that the source file text needed to create the context may or may not already be cached on the client. Having formatDiagnosticsWithColorAndContext be a dedicated remote API call probably makes sense.
4. General infrastructure
4A. VFS and overlay improvements
The included VFS implementation was meant only for testing, and includes no caching. There should be a way for the API client to provide file system contents eagerly and prevent IPC round-trips. This is easy implementation-wise, but finding the right design (particularly around invalidation, passthrough to disk, interaction with LSP-connected API) is important.
- Needed by: Bloomberg, TypeScript playground
- Cost: 1 dev-week
4B. Generic NodeHandle<T> and type guards on node handles
NodeHandle is currently non-generic, but would often be useful to carry narrowing information. A symbol.valueDeclaration.resolve() yields a Node when it could be a Declaration instead. This is easy, but also allowing a NodeHandle<Node> to be narrowed to a more specific NodeHandle type was surprisingly hard without code duplication or hacks. The likely path forward is giving in to code duplication or hacks and just getting it done.
- Needed by: everyone
- Cost: minimal for MVP
5. Additional methods in existing categories
There are still additional methods that can be exposed with no new concepts or infrastructure. The biggest categories are language service methods and small top-level utilities. The hardest part is just identifying them.
These are the categories of remaining API features I think we need for 7.1, with a few specific items included below. Costs are very rough estimates and hopefully skew high.
This list does not include non-feature work like bug fixes, performance improvements, or docs, which are all important too.
1. TS Server Plugin Replacements
1A. Content mappers
1B. LSP client middleware registration
TS Server plugins have the ability to directly modify LS requests/responses. Existing TS 7 LSP/API integration only allows extensions to add TS-augmented providers, and cannot filter/modify any stock responses or alter the TS 7 editor behavior in any way. Our extension can expose a command or cross-extension API allowing other extensions to register callbacks that we can invoke in middleware in vscode-languageclient. (This is not a very well documented feature of the client, but it exists.)
2. Top-level utility APIs
Currently, almost all APIs require obtaining a snapshot containing a full program build of the project/files being worked on. This is a pretty good model for working within a tsconfig-defined project on disk, especially if needing to update the project over time, but awkward for many kinds of ad-hoc analysis; it’s not a natural substitute for
ts.createProgrammuch lessts.createSourceFile.This list is not exhaustive, but these are the top priority ones people have asked for.
2A.
createProgram(options)This will still be backed by a snapshot, but provides the opportunity to specify
rootFilesandcompilerOptionsinstead of needing to resolve a tsconfig file from disk. On the server side, I'm not sure whether the snapshot should be derived from an existing snapshot or should be built entirely from scratch. Either way, it should be an idempotent operation (other open projects not included in the returned snapshot, new snapshot not adopted by the project session). We’ll need to expose the underlying Project somehow (program.projectparent pointer seems fine) and make the returned Program disposable (disposing its snapshot, since this API will guarantee 1:1 mapping between Program and Snapshot).Note: This dovetails with 3B and may be better suited to be folded into that.
2B.
createSourceFile(fileName, sourceText, options),createSourceFileFromFile(fileName, options)Quick way to get an isolated AST from text or disk. Open question as to whether this can be truly stateless, or should be server-tracked to allow for a follow-up
bindSourceFile, but the latter could be added in a backward-compatible way if needed. May be better to introduce a parameter indicating whether to send binder symbols back in the same response.2C.
transpileModule(sourceText, options),transpileModuleFromFile(fileName, options),transpileDeclaration(sourceText, options),transpileDeclarationFromFile(fileName, options)Quick way to transpile TypeScript code to JavaScript or declaration files without needing a full program or project context.
3. CLI-wrapper APIs
We’ve discussed high-level APIs that would make it easy (or at least possible) to create a CLI tool that performs similar functions to
tsc, with modifications that can only be achieved through API usage. Some of the scenarios motivating this will be handled by content mappers (#4712) instead, but I think Angular is still interested in this.I’m treating
--watchfunctionality as out of scope for now, because the TS 6 public API didn’t provide watching.3A.
parseCommandLineand related functionsHigh priority:
parseCommandLine(commandLine: readonly string[])readConfigFileparseJsonConfigFileContent(json, { configDirectory } | { configFileName })Lower priority: other non-internal exported functions from commandLineParser.ts that have equivalents in Go
3B. Solution builder APIs,
createIncrementalProgramBudgeting more time for this because the API is built on top of the LSP server’s project layer, which currently does not integrate with the solution builder whatsoever. It may turn out to be simple, but it's a new integration point into a complex system.
3C. Custom transformers
Much of the reason to create a CLI wrapper is making emit modifications. I think we can support an API very similar to Strada here by fetching the JS emit as a SourceFile (AST), running custom transformations client-side, and sending the transformed AST back to the server for final emit (basically
printNode). I think the infrastructure to do this already exists, but may be missing a lot of emit-node metadata required to do proper comment preservation and formatting.3D.
ts.formatDiagnostics,ts.formatDiagnosticsWithColorAndContextformatDiagnosticscan likely be a pure client-side port from Strada. The complication withformatDiagnosticsWithColorAndContextis that the source file text needed to create the context may or may not already be cached on the client. HavingformatDiagnosticsWithColorAndContextbe a dedicated remote API call probably makes sense.4. General infrastructure
4A. VFS and overlay improvements
The included VFS implementation was meant only for testing, and includes no caching. There should be a way for the API client to provide file system contents eagerly and prevent IPC round-trips. This is easy implementation-wise, but finding the right design (particularly around invalidation, passthrough to disk, interaction with LSP-connected API) is important.
4B. Generic
NodeHandle<T>and type guards on node handlesNodeHandleis currently non-generic, but would often be useful to carry narrowing information. Asymbol.valueDeclaration.resolve()yields aNodewhen it could be aDeclarationinstead. This is easy, but also allowing aNodeHandle<Node>to be narrowed to a more specificNodeHandletype was surprisingly hard without code duplication or hacks. The likely path forward is giving in to code duplication or hacks and just getting it done.5. Additional methods in existing categories
There are still additional methods that can be exposed with no new concepts or infrastructure. The biggest categories are language service methods and small top-level utilities. The hardest part is just identifying them.