Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API migration help for TypeScript 5.0 #52847

Closed
5 tasks done
mrmckeb opened this issue Feb 19, 2023 · 12 comments
Closed
5 tasks done

API migration help for TypeScript 5.0 #52847

mrmckeb opened this issue Feb 19, 2023 · 12 comments
Labels
Question An issue which isn't directly actionable in code

Comments

@mrmckeb
Copy link

mrmckeb commented Feb 19, 2023

Suggestion

🔍 Search Terms

  • createLanguageServiceSourceFile
  • Language server
  • Language service plugin
  • TypeScript 5.0 API

✅ Viability Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

⭐ Suggestion

Background

I'm currently trying to get my TypeScript plugin (typescript-plugin-css-modules) ready for TypeScript 5.0.

This plugin adds autocompletion and go to definition support for CSS by generating virtual DTS files.

When running in TypeScript 5.0, I'm hitting this issue:

Info 27   [13:59:50.911] Plugin activation failed: TypeError: Cannot set property createLanguageServiceSourceFile of #<Object> which has only a getter

As I understand it, this is because of this TS 5.0 breaking change:

The exported API is no longer defined as a "configurable" object

Source: https://github.com/microsoft/TypeScript/wiki/API-Breaking-Changes#typescript-50

What would help

I understand that the original design of this plugin may not be ideal, but I'd like to get this working in TypeScript 5.0.

I can see that proxying the LanguageService will help with most functions, but some of the methods I've been wrapping are not available on the LanguageService - namely createLanguageServiceSourceFile and updateLanguageServiceSourceFile.

The offending lines are here:

As someone that isn't an expert in TS plugins, I'd love a migration guide or an example of how to migrate implementations like this.

📃 Motivating Example

N/A

💻 Use Cases

N/A

@jakebailey
Copy link
Member

jakebailey commented Feb 19, 2023

TS 5.0 also adds support for d.ts files for arbitrary extensions: https://devblogs.microsoft.com/typescript/announcing-typescript-5-0-beta/#allowarbitraryextensions

This seems pretty similar to what you're trying to do in your plugin, in that one can define a styles.d.css.ts file and provide types. It's possible that you could implement it by providing a d.ts and d.ts.map file via a plugin (though, I'm less familiar with how much can be proxied there). That or, use some sort of codegen that puts the d.ts files next to the css files, that way they can be used outside of the LS too.

@mrmckeb
Copy link
Author

mrmckeb commented Feb 20, 2023

Thanks @jakebailey, I missed that allowArbitraryExtensions was coming and that looks really interesting.

Do you know who might be able to help with information around proxying other methods needed to read/process non-TS files? Perhaps a better alternative to using the now immutable createLanguageServiceSourceFile and updateLanguageServiceSourceFile methods?

For context, this plugin has around 200k+ weekly downloads, which I think (from a quick search) makes it one of the most popular language service plugins. It allows users to have type information from CSS (including Less, Sass, etc) without needing to watch and generate DTS files for those files in a separate process.

@jakebailey
Copy link
Member

jakebailey commented Feb 21, 2023

@rbuckton has suggested that you could provide styles.d.css.ts via the host, but @sheetalkamat may also have ideas.

I'm planning on trying to go through as many plugins and patchers as possible to get a handle on which APIs people are trying to patch. I was hoping that the lack of feedback regarding this meant that it wasn't so widespread (this change happened circa November 2022), but, it seems like people are coming out of the woodwork now that the RC is approaching (which is unfortunate given a "fix" for this on the TS side is likely to be nontrivial).

@jakebailey
Copy link
Member

jakebailey commented Feb 22, 2023

I've been working on that collected "ts patchers" list and stumbled upon Vue's LS plugin; they appear to proxy the host to provide similar functionality, so I believe that it's possible to do something like you already do within the current API:

@sheetalkamat
Copy link
Member

Sorry i missed this notification yesterday.
You would want to proxy following methods on languageServiceHost thats passed in through plugin create info.

getScriptKind?(fileName: string): ScriptKind`
getScriptVersion(fileName: string): string;
getScriptSnapshot(fileName: string): IScriptSnapshot | undefined;

@mrmckeb
Copy link
Author

mrmckeb commented Feb 24, 2023

Thanks so much for all of the detail @jakebailey, and for the additional info @sheetalkamat.

This is an unsponsored project, so I'll take a look at migrating to these APIs over the weekend!

@RyanCavanaugh RyanCavanaugh added the Question An issue which isn't directly actionable in code label Feb 24, 2023
@typescript-bot
Copy link
Collaborator

This issue has been marked as 'Question' and has seen no recent activity. It has been automatically closed for house-keeping purposes. If you're still waiting on a response, questions are usually better suited to stackoverflow or the TypeScript Discord community.

@mrmckeb
Copy link
Author

mrmckeb commented Mar 11, 2023

Thanks again for the help on this issue. I managed to get it working this weekend, and feeling very relieved!

mrmckeb/typescript-plugin-css-modules#211

@jakebailey
Copy link
Member

Glad you got it working. I left some comments on the PR which you may want to look at; in general, plugins should not import typescript, only ever using the ts value passed into init, otherwise they will be different objects entirely and may cause oddities.

@yoyo930021
Copy link

yoyo930021 commented Apr 17, 2023

Sorry for the disturbance, I am the current primary maintainer of Vetur. Vetur now relies on createLanguageServiceSourceFile to inject the export default object wrapped with Vue.extends into each Vue SFC. This layer is added directly to the AST and is set to not exist in the document through a hack.

If we want to change to the method mentioned above, we need to switch to a text-based approach combined with sourcemaps, which would be equivalent to rewriting the entire project. I would like to confirm if there are any similar hackable methods available? Otherwise, Vetur might have to give up on continuing support for TypeScript 5 and beyond.

@jakebailey
Copy link
Member

jakebailey commented Apr 17, 2023

I am assuming you're referring to: https://github.com/vuejs/vetur/blob/96aaa707f8ca629f0883c57a47adb0e58995936d/server/src/services/typescriptService/preprocess.ts

If I'm reading this correctly, you're calling the original createLanguageServiceSourceFile, then using its AST, modifying it, reprinting it, and then reparsing it, correct?

In which case, can you just do the same thing but skip the final re-parse step and return source in getScriptSnapshot? The only other bit I can see is that it automagically adds imports, which I think can be done in other ways, either also by source modification, or by using a global d.ts file which declares these for you. I know that volar does some of this.

Though, I'm a bit confused; I thought vetur was deprecated in favor of volar, which I think is working with TS 5.0?

@yoyo930021
Copy link

yoyo930021 commented Apr 18, 2023

I am assuming you're referring to: https://github.com/vuejs/vetur/blob/96aaa707f8ca629f0883c57a47adb0e58995936d/server/src/services/typescriptService/preprocess.ts

If I'm reading this correctly, you're calling the original createLanguageServiceSourceFile, then using its AST, modifying it, reprinting it, and then reparsing it, correct?

In which case, can you just do the same thing but skip the final re-parse step and return source in getScriptSnapshot? The only other bit I can see is that it automagically adds imports, which I think can be done in other ways, either also by source modification, or by using a global d.ts file which declares these for you. I know that volar does some of this.

Though, I'm a bit confused; I thought vetur was deprecated in favor of volar, which I think is working with TS 5.0?

The main logic is in this line:
https://github.com/vuejs/vetur/blob/96aaa707f8ca629f0883c57a47adb0e58995936d/server/src/services/typescriptService/preprocess.ts#L184
It inserts additional AST and sets the AST's position to 0, so that TS will not generate messages at incorrect positions. If changed to use the getScriptSnapshot method, an extra sourcemap would be required to map the code position information back, which would be equivalent to rewriting the entire project.
The rest of the logic is actually not that important.

Volar is the new official Vue extension, but it mainly focuses on Vue 3 projects; Vetur now focuses on supporting older Vue 2 projects, with limited support for Vue 3 and only providing basic maintenance, without adding new features.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Question An issue which isn't directly actionable in code
Projects
None yet
Development

No branches or pull requests

6 participants