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

Use jiti for sync loading from TS Plugin #803

Merged
merged 2 commits into from
Mar 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/typescript-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"prepack": "yarn build"
},
"dependencies": {
"jiti": "~2.4.2",
"@glint/core": "^1.4.0",
"@volar/typescript": "2.4.11"
},
Expand Down
78 changes: 29 additions & 49 deletions packages/typescript-plugin/src/typescript-server-plugin.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,37 @@
import type ts from 'typescript';
const { createJiti } = require('jiti');
const jiti = createJiti(__filename);

// Top level "imports" need to be CJS `require`s because TS Plugins must be CJS;
// we dynamically import() the ESM modules we need below within the async fn
// to cross the gap between CJS and ESM.
const {
createAsyncLanguageServicePlugin,
} = require('@volar/typescript/lib/quickstart/createAsyncLanguageServicePlugin.js');
createLanguageServicePlugin,
} = require('@volar/typescript/lib/quickstart/createLanguageServicePlugin.js');

/**
* Volar provides two variants of functions for initializing a TS Plugin:
* - createLanguageServicePlugin
* - createAsyncLanguageServicePlugin
*
* The only difference is whether their setup callback is an async function or not.
* The reason we use the async variant is because of our use of `await import`, which
* we need in order to import the ESM glint package into our CJS VSCode extension.
*
* Unfortunately this singular tick of async appears to be causing a few race conditions,
* in particular that when freshly booting VSCode on a .gts file, there might not be
* any diagnostic messages until something "kicks" the TS Plugin to run, e.g.
* by editing the file.
*/
const plugin = createAsyncLanguageServicePlugin(
['.gts', '.gjs', '.hbs'],
(fileName: string) => {
if (fileName.endsWith('.gts')) {
return 3 satisfies ts.ScriptKind.TS;
} else if (fileName.endsWith('.gjs')) {
return 1 satisfies ts.ScriptKind.JS;
}
return 3 satisfies ts.ScriptKind.TS;
},
async (_ts: any, info: any) => {
// The diagnostics race condition mentioned above appears to happen or at least
// be exacerbated by the fact that we use `await import` here.
const glintCore = await import('@glint/core');
const plugin = createLanguageServicePlugin((_ts: typeof import('typescript'), info: any) => {
/**
* we use the jiti (https://github.com/unjs/jiti) runtime to make it possible to
* synchronously load the ESM glint libaries from the current CommonJS context. It is a requirement
* that TypeScript plugins are written in CommonJS, which poses issues with
* having Glint be authored in ESM due to the requirement that typically `await import`
* is required to load ESM modules from CJS. But with jiti we can synchronously load the ESM
* modules from CJS which lets us avoid a ton of hacks and complexity we (or Volar)
* would otherwise have to write to bridge the sync/async APIs.
*/
const glintCore = jiti('@glint/core');

const { findConfig, createEmberLanguagePlugin } = glintCore;
const { findConfig, createEmberLanguagePlugin } = glintCore;

const cwd = info.languageServiceHost.getCurrentDirectory();
const glintConfig = findConfig(cwd);
const cwd = info.languageServiceHost.getCurrentDirectory();
const glintConfig = findConfig(cwd);

if (glintConfig && glintConfig.enableTsPlugin) {
const gtsLanguagePlugin = createEmberLanguagePlugin(glintConfig);
return {
languagePlugins: [gtsLanguagePlugin],
};
} else {
return {
languagePlugins: [],
};
}
},
);
if (glintConfig && glintConfig.enableTsPlugin) {
const gtsLanguagePlugin = createEmberLanguagePlugin(glintConfig);
return {
languagePlugins: [gtsLanguagePlugin],
};
} else {
return {
languagePlugins: [],
};
}
});

export = plugin;
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9686,6 +9686,11 @@ jest-worker@^27.4.5:
merge-stream "^2.0.0"
supports-color "^8.0.0"

jiti@~2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/jiti/-/jiti-2.4.2.tgz#d19b7732ebb6116b06e2038da74a55366faef560"
integrity sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==

js-string-escape@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef"
Expand Down
Loading