11import { LanguagePlugin } from '@volar/language-core' ;
2+ import * as path from 'node:path' ;
3+ import { fileURLToPath } from 'node:url' ;
24import type ts from 'typescript' ;
35import { URI } from 'vscode-uri' ;
46import { GlintConfig } from '../index.js' ;
57import { VirtualGtsCode } from './gts-virtual-code.js' ;
68export type TS = typeof ts ;
79
8- /**
9- * Create a [Volar](https://volarjs.dev) language plugin to support
10- *
11- * - .gts/.gjs files (the `ember-template-imports` environment)
12- */
13- export function createEmberLanguagePlugin < T extends URI | string > (
14- glintConfig : GlintConfig ,
15- { clientId } : { clientId ?: string } = { } ,
10+ type EmberLanguagePluginOptions = {
11+ clientId ?: string ;
12+ getCurrentDirectory ?: ( ) => string ;
13+ allowJs ?: boolean ;
14+ throwOnUnexpectedLanguageId ?: boolean ;
15+ } ;
16+
17+ function getLanguageIdForFileName ( fileName : string ) : 'glimmer-ts' | 'glimmer-js' | undefined {
18+ if ( fileName . endsWith ( '.gts' ) ) {
19+ return 'glimmer-ts' ;
20+ }
21+ if ( fileName . endsWith ( '.gjs' ) ) {
22+ return 'glimmer-js' ;
23+ }
24+ }
25+
26+ function isGlimmerLanguageId (
27+ languageId : string | undefined ,
28+ ) : languageId is 'glimmer-ts' | 'glimmer-js' | 'typescript.glimmer' | 'javascript.glimmer' {
29+ return (
30+ languageId === 'glimmer-ts' ||
31+ languageId === 'glimmer-js' ||
32+ languageId === 'typescript.glimmer' ||
33+ languageId === 'javascript.glimmer'
34+ ) ;
35+ }
36+
37+ function normalizeFileName ( scriptIdStr : string , getCurrentDirectory ?: ( ) => string ) : string {
38+ let fileName = scriptIdStr ;
39+ if ( scriptIdStr . startsWith ( 'file://' ) ) {
40+ try {
41+ fileName = fileURLToPath ( scriptIdStr ) ;
42+ } catch {
43+ try {
44+ fileName = decodeURIComponent ( new URL ( scriptIdStr ) . pathname ) ;
45+ } catch {
46+ fileName = scriptIdStr ;
47+ }
48+ }
49+ }
50+
51+ if ( ! path . isAbsolute ( fileName ) ) {
52+ const baseDir = getCurrentDirectory ?.( ) ;
53+ if ( baseDir ) {
54+ fileName = path . resolve ( baseDir , fileName ) ;
55+ }
56+ }
57+
58+ return fileName ;
59+ }
60+
61+ function createEmberLanguagePluginInternal < T extends URI | string > (
62+ getGlintConfig : ( fileName : string ) => GlintConfig | null ,
63+ {
64+ clientId,
65+ getCurrentDirectory,
66+ allowJs = false ,
67+ throwOnUnexpectedLanguageId = false ,
68+ } : EmberLanguagePluginOptions = { } ,
1669) : LanguagePlugin < T > {
1770 return {
1871 /**
@@ -34,17 +87,21 @@ export function createEmberLanguagePlugin<T extends URI | string>(
3487 }
3588 } ,
3689
37- createVirtualCode ( scriptId : URI | string , languageId , snapshot , codegenContext ) {
90+ createVirtualCode ( scriptId : URI | string , languageId , snapshot ) {
3891 const scriptIdStr = String ( scriptId ) ;
92+ const fileName = normalizeFileName ( scriptIdStr , getCurrentDirectory ) ;
93+ const inferredLanguageId = languageId ?? getLanguageIdForFileName ( fileName ) ;
3994
40- if (
41- languageId === 'glimmer-ts' ||
42- languageId === 'glimmer-js' ||
43- languageId === 'typescript.glimmer' ||
44- languageId === 'javascript.glimmer'
45- ) {
46- return new VirtualGtsCode ( glintConfig , snapshot , languageId , clientId ) ;
95+ if ( ! isGlimmerLanguageId ( inferredLanguageId ) ) {
96+ return ;
4797 }
98+
99+ const glintConfig = getGlintConfig ( fileName ) ;
100+ if ( ! glintConfig ) {
101+ return ;
102+ }
103+
104+ return new VirtualGtsCode ( glintConfig , snapshot , inferredLanguageId , clientId ) ;
48105 } ,
49106
50107 typescript : {
@@ -89,20 +146,52 @@ export function createEmberLanguagePlugin<T extends URI | string>(
89146 scriptKind : 1 satisfies ts . ScriptKind . JS ,
90147 } ;
91148 default :
92- throw new Error ( `getScript: Unexpected languageId: ${ rootVirtualCode . languageId } ` ) ;
149+ if ( throwOnUnexpectedLanguageId ) {
150+ throw new Error ( `getScript: Unexpected languageId: ${ rootVirtualCode . languageId } ` ) ;
151+ }
93152 }
94153 } ,
95154
96- resolveLanguageServiceHost ( host ) {
97- return {
98- ...host ,
99- getCompilationSettings : ( ) => ( {
100- ...host . getCompilationSettings ( ) ,
101- // Always allow JS for type checking.
102- allowJs : true ,
103- } ) ,
104- } ;
105- } ,
155+ ...( allowJs
156+ ? {
157+ resolveLanguageServiceHost ( host ) {
158+ return {
159+ ...host ,
160+ getCompilationSettings : ( ) => ( {
161+ ...host . getCompilationSettings ( ) ,
162+ // Always allow JS for type checking.
163+ allowJs : true ,
164+ } ) ,
165+ } ;
166+ } ,
167+ }
168+ : { } ) ,
106169 } ,
107170 } ;
108171}
172+
173+ /**
174+ * Create a [Volar](https://volarjs.dev) language plugin to support
175+ *
176+ * - .gts/.gjs files (the `ember-template-imports` environment)
177+ */
178+ export function createEmberLanguagePlugin < T extends URI | string > (
179+ glintConfig : GlintConfig ,
180+ { clientId } : { clientId ?: string } = { } ,
181+ ) : LanguagePlugin < T > {
182+ return createEmberLanguagePluginInternal ( ( ) => glintConfig , {
183+ clientId,
184+ allowJs : true ,
185+ throwOnUnexpectedLanguageId : true ,
186+ } ) ;
187+ }
188+
189+ export function createDynamicEmberLanguagePlugin < T extends URI | string > (
190+ findConfig : ( from : string ) => GlintConfig | null ,
191+ { clientId, getCurrentDirectory } : { clientId ?: string ; getCurrentDirectory : ( ) => string } ,
192+ ) : LanguagePlugin < T > {
193+ return createEmberLanguagePluginInternal ( ( fileName ) => findConfig ( path . dirname ( fileName ) ) , {
194+ clientId,
195+ getCurrentDirectory,
196+ } ) ;
197+ }
0 commit comments