@glint/ember-tsc 1.0.1-unstable.845d5fa

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/README.md +46 -0
  2. package/bin/glint-language-server.js +2 -0
  3. package/bin/glint.js +4 -0
  4. package/lib/cli/run-volar-tsc.d.ts +2 -0
  5. package/lib/cli/run-volar-tsc.d.ts.map +1 -0
  6. package/lib/cli/run-volar-tsc.js +30 -0
  7. package/lib/cli/run-volar-tsc.js.map +1 -0
  8. package/lib/config/config.d.ts +15 -0
  9. package/lib/config/config.d.ts.map +1 -0
  10. package/lib/config/config.js +21 -0
  11. package/lib/config/config.js.map +1 -0
  12. package/lib/config/environment.d.ts +26 -0
  13. package/lib/config/environment.d.ts.map +1 -0
  14. package/lib/config/environment.js +96 -0
  15. package/lib/config/environment.js.map +1 -0
  16. package/lib/config/index.d.ts +17 -0
  17. package/lib/config/index.d.ts.map +1 -0
  18. package/lib/config/index.js +26 -0
  19. package/lib/config/index.js.map +1 -0
  20. package/lib/config/loader.d.ts +25 -0
  21. package/lib/config/loader.d.ts.map +1 -0
  22. package/lib/config/loader.js +110 -0
  23. package/lib/config/loader.js.map +1 -0
  24. package/lib/config/types.cjs +3 -0
  25. package/lib/config/types.cjs.map +1 -0
  26. package/lib/config/types.d.cts +60 -0
  27. package/lib/config/types.d.cts.map +1 -0
  28. package/lib/environment-ember-template-imports/-private/environment/common.d.ts +13 -0
  29. package/lib/environment-ember-template-imports/-private/environment/common.d.ts.map +1 -0
  30. package/lib/environment-ember-template-imports/-private/environment/common.js +2 -0
  31. package/lib/environment-ember-template-imports/-private/environment/common.js.map +1 -0
  32. package/lib/environment-ember-template-imports/-private/environment/index.d.ts +3 -0
  33. package/lib/environment-ember-template-imports/-private/environment/index.d.ts.map +1 -0
  34. package/lib/environment-ember-template-imports/-private/environment/index.js +76 -0
  35. package/lib/environment-ember-template-imports/-private/environment/index.js.map +1 -0
  36. package/lib/environment-ember-template-imports/-private/environment/preprocess.d.ts +4 -0
  37. package/lib/environment-ember-template-imports/-private/environment/preprocess.d.ts.map +1 -0
  38. package/lib/environment-ember-template-imports/-private/environment/preprocess.js +73 -0
  39. package/lib/environment-ember-template-imports/-private/environment/preprocess.js.map +1 -0
  40. package/lib/environment-ember-template-imports/-private/environment/transform.d.ts +4 -0
  41. package/lib/environment-ember-template-imports/-private/environment/transform.d.ts.map +1 -0
  42. package/lib/environment-ember-template-imports/-private/environment/transform.js +134 -0
  43. package/lib/environment-ember-template-imports/-private/environment/transform.js.map +1 -0
  44. package/lib/index.d.ts +7 -0
  45. package/lib/index.d.ts.map +1 -0
  46. package/lib/index.js +6 -0
  47. package/lib/index.js.map +1 -0
  48. package/lib/plugins/g-compiler-errors.d.ts +12 -0
  49. package/lib/plugins/g-compiler-errors.d.ts.map +1 -0
  50. package/lib/plugins/g-compiler-errors.js +58 -0
  51. package/lib/plugins/g-compiler-errors.js.map +1 -0
  52. package/lib/plugins/g-template-tag-symbols.d.ts +11 -0
  53. package/lib/plugins/g-template-tag-symbols.d.ts.map +1 -0
  54. package/lib/plugins/g-template-tag-symbols.js +48 -0
  55. package/lib/plugins/g-template-tag-symbols.js.map +1 -0
  56. package/lib/plugins/utils.d.ts +25 -0
  57. package/lib/plugins/utils.d.ts.map +1 -0
  58. package/lib/plugins/utils.js +63 -0
  59. package/lib/plugins/utils.js.map +1 -0
  60. package/lib/transform/diagnostics/augmentation.d.ts +4 -0
  61. package/lib/transform/diagnostics/augmentation.d.ts.map +1 -0
  62. package/lib/transform/diagnostics/augmentation.js +223 -0
  63. package/lib/transform/diagnostics/augmentation.js.map +1 -0
  64. package/lib/transform/diagnostics/index.d.ts +5 -0
  65. package/lib/transform/diagnostics/index.d.ts.map +1 -0
  66. package/lib/transform/diagnostics/index.js +2 -0
  67. package/lib/transform/diagnostics/index.js.map +1 -0
  68. package/lib/transform/index.d.ts +4 -0
  69. package/lib/transform/index.d.ts.map +1 -0
  70. package/lib/transform/index.js +2 -0
  71. package/lib/transform/index.js.map +1 -0
  72. package/lib/transform/template/code-features.d.ts +30 -0
  73. package/lib/transform/template/code-features.d.ts.map +1 -0
  74. package/lib/transform/template/code-features.js +26 -0
  75. package/lib/transform/template/code-features.js.map +1 -0
  76. package/lib/transform/template/glimmer-ast-mapping-tree.d.ts +80 -0
  77. package/lib/transform/template/glimmer-ast-mapping-tree.d.ts.map +1 -0
  78. package/lib/transform/template/glimmer-ast-mapping-tree.js +132 -0
  79. package/lib/transform/template/glimmer-ast-mapping-tree.js.map +1 -0
  80. package/lib/transform/template/inlining/index.d.ts +16 -0
  81. package/lib/transform/template/inlining/index.d.ts.map +1 -0
  82. package/lib/transform/template/inlining/index.js +21 -0
  83. package/lib/transform/template/inlining/index.js.map +1 -0
  84. package/lib/transform/template/inlining/tagged-strings.d.ts +8 -0
  85. package/lib/transform/template/inlining/tagged-strings.d.ts.map +1 -0
  86. package/lib/transform/template/inlining/tagged-strings.js +140 -0
  87. package/lib/transform/template/inlining/tagged-strings.js.map +1 -0
  88. package/lib/transform/template/map-template-contents.d.ts +121 -0
  89. package/lib/transform/template/map-template-contents.d.ts.map +1 -0
  90. package/lib/transform/template/map-template-contents.js +287 -0
  91. package/lib/transform/template/map-template-contents.js.map +1 -0
  92. package/lib/transform/template/rewrite-module.d.ts +22 -0
  93. package/lib/transform/template/rewrite-module.d.ts.map +1 -0
  94. package/lib/transform/template/rewrite-module.js +265 -0
  95. package/lib/transform/template/rewrite-module.js.map +1 -0
  96. package/lib/transform/template/scope-stack.d.ts +13 -0
  97. package/lib/transform/template/scope-stack.d.ts.map +1 -0
  98. package/lib/transform/template/scope-stack.js +28 -0
  99. package/lib/transform/template/scope-stack.js.map +1 -0
  100. package/lib/transform/template/template-to-typescript.d.ts +19 -0
  101. package/lib/transform/template/template-to-typescript.d.ts.map +1 -0
  102. package/lib/transform/template/template-to-typescript.js +1095 -0
  103. package/lib/transform/template/template-to-typescript.js.map +1 -0
  104. package/lib/transform/template/transformed-module.d.ts +111 -0
  105. package/lib/transform/template/transformed-module.d.ts.map +1 -0
  106. package/lib/transform/template/transformed-module.js +287 -0
  107. package/lib/transform/template/transformed-module.js.map +1 -0
  108. package/lib/transform/util.d.ts +7 -0
  109. package/lib/transform/util.d.ts.map +1 -0
  110. package/lib/transform/util.js +15 -0
  111. package/lib/transform/util.js.map +1 -0
  112. package/lib/volar/ember-language-plugin.d.ts +14 -0
  113. package/lib/volar/ember-language-plugin.d.ts.map +1 -0
  114. package/lib/volar/ember-language-plugin.js +91 -0
  115. package/lib/volar/ember-language-plugin.js.map +1 -0
  116. package/lib/volar/gts-virtual-code.d.ts +83 -0
  117. package/lib/volar/gts-virtual-code.d.ts.map +1 -0
  118. package/lib/volar/gts-virtual-code.js +210 -0
  119. package/lib/volar/gts-virtual-code.js.map +1 -0
  120. package/lib/volar/language-server.d.ts +2 -0
  121. package/lib/volar/language-server.d.ts.map +1 -0
  122. package/lib/volar/language-server.js +214 -0
  123. package/lib/volar/language-server.js.map +1 -0
  124. package/lib/volar/script-snapshot.d.ts +17 -0
  125. package/lib/volar/script-snapshot.d.ts.map +1 -0
  126. package/lib/volar/script-snapshot.js +24 -0
  127. package/lib/volar/script-snapshot.js.map +1 -0
  128. package/package.json +114 -0
  129. package/src/cli/run-volar-tsc.ts +36 -0
  130. package/src/config/config.ts +33 -0
  131. package/src/config/environment.ts +128 -0
  132. package/src/config/index.ts +30 -0
  133. package/src/config/loader.ts +143 -0
  134. package/src/config/types.cts +85 -0
  135. package/src/environment-ember-template-imports/-private/environment/common.ts +14 -0
  136. package/src/environment-ember-template-imports/-private/environment/index.ts +83 -0
  137. package/src/environment-ember-template-imports/-private/environment/preprocess.ts +90 -0
  138. package/src/environment-ember-template-imports/-private/environment/transform.ts +202 -0
  139. package/src/index.ts +9 -0
  140. package/src/plugins/g-compiler-errors.ts +67 -0
  141. package/src/plugins/g-template-tag-symbols.ts +54 -0
  142. package/src/plugins/utils.ts +86 -0
  143. package/src/transform/diagnostics/augmentation.ts +333 -0
  144. package/src/transform/diagnostics/index.ts +5 -0
  145. package/src/transform/index.ts +4 -0
  146. package/src/transform/template/code-features.ts +30 -0
  147. package/src/transform/template/glimmer-ast-mapping-tree.ts +173 -0
  148. package/src/transform/template/inlining/index.ts +33 -0
  149. package/src/transform/template/inlining/tagged-strings.ts +187 -0
  150. package/src/transform/template/map-template-contents.ts +501 -0
  151. package/src/transform/template/rewrite-module.ts +372 -0
  152. package/src/transform/template/scope-stack.ts +34 -0
  153. package/src/transform/template/template-to-typescript.ts +1476 -0
  154. package/src/transform/template/transformed-module.ts +431 -0
  155. package/src/transform/util.ts +24 -0
  156. package/src/volar/ember-language-plugin.ts +108 -0
  157. package/src/volar/gts-virtual-code.ts +249 -0
  158. package/src/volar/language-server.ts +250 -0
  159. package/src/volar/script-snapshot.ts +27 -0
  160. package/types/-private/dsl/globals.d.ts +204 -0
  161. package/types/-private/dsl/index.d.ts +50 -0
  162. package/types/-private/dsl/integration-declarations.d.ts +143 -0
  163. package/types/-private/intrinsics/action.d.ts +45 -0
  164. package/types/-private/intrinsics/concat.d.ts +6 -0
  165. package/types/-private/intrinsics/each-in.d.ts +24 -0
  166. package/types/-private/intrinsics/each.d.ts +17 -0
  167. package/types/-private/intrinsics/fn.d.ts +44 -0
  168. package/types/-private/intrinsics/get.d.ts +31 -0
  169. package/types/-private/intrinsics/input.d.ts +24 -0
  170. package/types/-private/intrinsics/link-to.d.ts +31 -0
  171. package/types/-private/intrinsics/log.d.ts +6 -0
  172. package/types/-private/intrinsics/mount.d.ts +9 -0
  173. package/types/-private/intrinsics/mut.d.ts +14 -0
  174. package/types/-private/intrinsics/on.d.ts +21 -0
  175. package/types/-private/intrinsics/outlet.d.ts +8 -0
  176. package/types/-private/intrinsics/textarea.d.ts +16 -0
  177. package/types/-private/intrinsics/unbound.d.ts +10 -0
  178. package/types/-private/intrinsics/unique-id.d.ts +5 -0
  179. package/types/globals/index.d.ts +3 -0
  180. package/types/silent-error.d.ts +4 -0
@@ -0,0 +1,249 @@
1
+ import { CodeInformation, CodeMapping, VirtualCode } from '@volar/language-core';
2
+ import type ts from 'typescript';
3
+ import { IScriptSnapshot } from 'typescript';
4
+ import { GlintConfig } from '../index.js';
5
+ import { Directive, rewriteModule } from '../transform/index.js';
6
+ import { ScriptSnapshot } from './script-snapshot.js';
7
+ export type TS = typeof ts;
8
+
9
+ interface EmbeddedCodeWithDirectives extends VirtualCode {
10
+ directives: readonly Directive[];
11
+ }
12
+
13
+ /**
14
+ * A Volar VirtualCode representing .gts/.gjs files, which includes 0+ embedded
15
+ * Handlebars templates within <template> tags.
16
+ *
17
+ * ## Virtual Code Overview (and how we use it)
18
+ *
19
+ * VirtualCodes are one of the most core/central primitives provided by Volar.
20
+ * They are:
21
+ *
22
+ * - Used both in (classic) Language Server mode and in newer TS Plugin mode
23
+ * - Responsible for parsing and transforming source code of a particular language (e.g. GTS)
24
+ * into embedded codes (e.g. a valid type-checkable TS file wherein all `<template>` tags
25
+ * have been replaced with valid TS code)
26
+ *
27
+ * ## Embedded Codes
28
+ *
29
+ * The VirtualCode interface has an `embeddedCodes` array that (typically) contains
30
+ * generated code based on the root virtual code file type (in our case, GTS). The generated
31
+ * embedded codes will typically have a different file type ID (e.g. `typescript`); Volar
32
+ * in turn can perform language processing on these specific files (e.g. generate diagnostics),
33
+ * and then propagate these diagnostics upwards to the root virtual code file. In order to do this,
34
+ * we need to provide source code mapping information so that Volar can correctly map the diagnostics
35
+ * back to the original source code.
36
+ *
37
+ * Note that "embedded code" can be somewhat of a confusing concept based on how familiar
38
+ * languages like HTML allow other languages (JS in script tags, CSS in style tags, etc.)
39
+ * to be embedded within a parent file (e.g. HTML). If you were building a VirtualCode for HTML,
40
+ * you would have an `embeddedCodes` array that contains VirtualCodes for the embedded languages,
41
+ * where each VirtualCode had the extracted HTML, CSS, JS, etc contents.
42
+ *
43
+ * But for GTS, the embedded code that we generate does not actually exist in the same form in
44
+ * the parent document (the .gts file); rather, the embedded code is a valid TS file that has
45
+ * been generated from the .gts by replacing all of the embedded `<template>`s with TS. Whereas
46
+ * for HTML, embedded codes are simple extracted strings of HTML, CSS, JS, etc, for GTS, the
47
+ * embedded code is a transformed file that's most likely much larger than the original .gts file.
48
+ *
49
+ * Additionally:
50
+ *
51
+ * - We always generate an embedded code of TypeScript even when the .gts file has no embedded templates
52
+ * (meaning it is already valid TS). This keeps both our code and the structure of our VirtualCode
53
+ * simple and consistent. In cases where there are no embedded templates, the embedded code contains
54
+ * the same contents as the root virtual code file.
55
+ *
56
+ * - Even when there are multiple <template> tags, we only generate 1 embedded TS code representing
57
+ * the TS code for all of the entire .gts file.
58
+ *
59
+ * - The Root VirtualCode we provide is simple the untransformed .gts file itself. This is a Volar
60
+ * convention and also allows for the possible of writing language service plugins that operate
61
+ * directly on .gts (i.e. `glimmer-ts`) files directly (rather than on an embedded code).
62
+ *
63
+ * In summary, `embeddedCodes` is a more general/abstract concept of ANY kind of language that
64
+ * is embedded or generated from the root virtual code file for the purposes of performing
65
+ * language service processing for the particular file type of the embedded code.
66
+ */
67
+ export class VirtualGtsCode implements VirtualCode {
68
+ /**
69
+ * The virtual files embedded in the GTS file. (such as <template>)
70
+ */
71
+ embeddedCodes: EmbeddedCodeWithDirectives[] = [];
72
+
73
+ /**
74
+ * The id is a unique (within the VirtualCode and its embedded files) id for Volar to identify it. It could be any string.
75
+ */
76
+ id = 'gts';
77
+
78
+ mappings: CodeMapping[] = [];
79
+
80
+ transformedModule: ReturnType<typeof rewriteModule> | null = null;
81
+
82
+ constructor(
83
+ private glintConfig: GlintConfig,
84
+ public snapshot: IScriptSnapshot,
85
+ public languageId: 'glimmer-ts' | 'glimmer-js' | 'typescript.glimmer' | 'javascript.glimmer',
86
+ public clientId?: string,
87
+ ) {
88
+ this.update(snapshot);
89
+ }
90
+
91
+ // This gets called by the constructor and whenever the language server receives a file change event,
92
+ // i.e. the user saved the file.
93
+ update(snapshot: IScriptSnapshot): void {
94
+ this.snapshot = snapshot;
95
+ const length = snapshot.getLength();
96
+
97
+ // The root virtual code is always the untransformed .gts file itself. (See docs/explanation
98
+ // at top of file for more details.) Therefore we declare a mapping that maps the entire
99
+ // contents of the source .gts to the identical "generated" .gts file (which is literally
100
+ // the same contents).
101
+ this.mappings[0] = {
102
+ // Map the entire length of the file from source to "generated"
103
+ sourceOffsets: [0],
104
+ generatedOffsets: [0],
105
+ lengths: [length],
106
+
107
+ // The `CodeInformation` specifies the capabilities / language feature enablements for the span
108
+ // of code covered by the mapping. Because this mapping covers the whole file, we are
109
+ // essentially controlling which language features are enabled for the entire .gts file,
110
+ // which ultimately determines whether Volar will pass the contents of this file to
111
+ // any number of the various language feature plugins supported by Volar.
112
+ data: {
113
+ // I have not personally vetted whether these should be disabled; we should revisit
114
+ // what specifically we actually need.
115
+ completion: true,
116
+ format: true,
117
+ navigation: true,
118
+ semantic: true,
119
+ structure: true,
120
+
121
+ // `verification: true` tells Volar to evaluate the root untransformed .gts file
122
+ // for diagnostics. Note that this only applies to diagnostics generated by the
123
+ // _Language Server_, and not the TS Plugin. The TS Plugin only performs
124
+ // type-checking a specific TypeScript embedded code, i.e. the TS code
125
+ // containing the transformed representation of our .gts file. Search `getServiceScript`
126
+ // for where this embedded code selection occurs.
127
+ //
128
+ // @GLINT_FEATURE_DIAGNOSTICS
129
+ // @GLINT_FEATURE_DIAGNOSTICS_LANGUAGE_SERVER
130
+ // @GLINT_FEATURE_DIAGNOSTICS_LANGUAGE_SERVER_GTS_COMPILER_ERRORS
131
+ verification: true,
132
+ } satisfies CodeInformation,
133
+ };
134
+
135
+ const contents = snapshot.getText(0, length);
136
+ const isJavascript =
137
+ this.languageId === 'glimmer-js' || this.languageId === 'javascript.glimmer';
138
+ const embeddedLanguageId = isJavascript ? 'javascript' : 'typescript';
139
+ const filename = isJavascript ? 'root.gjs' : 'root.gts';
140
+ let script = { filename, contents };
141
+
142
+ const transformedModule = rewriteModule(
143
+ this.glintConfig.ts,
144
+ { script },
145
+ this.glintConfig.environment,
146
+ this.clientId,
147
+ );
148
+
149
+ this.transformedModule = transformedModule;
150
+
151
+ if (transformedModule) {
152
+ // .gts file has embedded templates, so lets generate a new embedded code
153
+ // that contains the transformed TS code.
154
+ const mappings = transformedModule.toVolarMappings();
155
+ const embeddedCodes = [
156
+ {
157
+ embeddedCodes: [],
158
+ id: 'template_ts',
159
+ languageId: embeddedLanguageId,
160
+ mappings,
161
+ snapshot: new ScriptSnapshot(transformedModule.transformedContents),
162
+ directives: transformedModule.directives,
163
+ },
164
+ ];
165
+
166
+ // Add an embedded code for each <template> tag in the .gts file
167
+ // so that the HTML Language Service can kick in and provide symbols
168
+ // and other functionality.
169
+ transformedModule.correlatedSpans.forEach((span, index) => {
170
+ if (!span.glimmerAstMapping) {
171
+ return;
172
+ }
173
+
174
+ const openTemplateTagLength = 10; // "<template>"
175
+ const closeTemplateTagLength = 11; // "</template>"
176
+
177
+ embeddedCodes.push({
178
+ embeddedCodes: [],
179
+ id: `template_html_${index}`,
180
+ languageId: 'html', // technically this is 'handlebars' but 'html' causes the HTML Language Service to kick in
181
+ mappings: [
182
+ {
183
+ sourceOffsets: [span.originalStart + openTemplateTagLength],
184
+ generatedOffsets: [0],
185
+ lengths: [span.originalLength - openTemplateTagLength - closeTemplateTagLength],
186
+
187
+ data: {
188
+ completion: true,
189
+ format: true,
190
+ navigation: true,
191
+ semantic: true,
192
+ structure: true,
193
+ verification: false,
194
+ } satisfies CodeInformation,
195
+ },
196
+ ],
197
+ snapshot: new ScriptSnapshot(
198
+ contents.slice(
199
+ span.originalStart + openTemplateTagLength,
200
+ span.originalStart + span.originalLength - closeTemplateTagLength,
201
+ ),
202
+ ),
203
+ directives: [],
204
+ });
205
+ });
206
+
207
+ this.embeddedCodes = embeddedCodes;
208
+ } else {
209
+ // Null transformed module means there's no embedded HBS templates,
210
+ // so just return a full "no-op" mapping from source to transformed.
211
+ //
212
+ // Note that this does mean that both the root virtual code and this embedded
213
+ // code we generate have the same contents, but we will configure the
214
+ // CodeInformation below differently than we do above to enable type-checking
215
+ // on the embedded code. While this might seem a bit wasteful/confusing to have
216
+ // root and embedded code essentially point to the same document, it keeps the
217
+ // structure clean, simple, and consistent, rather than, say, changing the structure
218
+ // of our VirtualCode only in the case of .gts files without embedded templates.
219
+ this.embeddedCodes = [
220
+ {
221
+ embeddedCodes: [],
222
+ id: 'template_ts',
223
+ languageId: embeddedLanguageId,
224
+ mappings: [
225
+ {
226
+ // Hacked hardwired values for now.
227
+ sourceOffsets: [0],
228
+ generatedOffsets: [0],
229
+ lengths: [length],
230
+
231
+ data: {
232
+ completion: true,
233
+ format: false,
234
+ navigation: true,
235
+ semantic: true,
236
+ structure: true,
237
+
238
+ // Unlike the root virtual code, we enable verification (which enables TS type-checking).
239
+ verification: true,
240
+ } satisfies CodeInformation,
241
+ },
242
+ ],
243
+ snapshot: new ScriptSnapshot(contents),
244
+ directives: [],
245
+ },
246
+ ];
247
+ }
248
+ }
249
+ }
@@ -0,0 +1,250 @@
1
+ import { createLanguage } from '@volar/language-core';
2
+ import type { LanguagePlugin, LanguageServer } from '@volar/language-server';
3
+ import { createLanguageServiceEnvironment } from '@volar/language-server/lib/project/simpleProject.js';
4
+ import { createConnection, createServer } from '@volar/language-server/node.js';
5
+ import type { LanguageServiceContext, LanguageServicePlugin } from '@volar/language-service';
6
+ import { createLanguageService, createUriMap, LanguageService } from '@volar/language-service';
7
+ import * as ts from 'typescript';
8
+ import { create as createHtmlSyntacticPlugin } from 'volar-service-html';
9
+ import { create as createTypeScriptSyntacticPlugin } from 'volar-service-typescript/lib/plugins/syntactic.js';
10
+ import { URI } from 'vscode-uri';
11
+ import { ConfigLoader } from '../config/loader.js';
12
+ import { create as createCompilerErrorsPlugin } from '../plugins/g-compiler-errors.js';
13
+ import { create as createTemplateTagSymbolsPlugin } from '../plugins/g-template-tag-symbols.js';
14
+ import { createEmberLanguagePlugin } from './ember-language-plugin.js';
15
+
16
+ const connection = createConnection();
17
+ const server = createServer(connection);
18
+ const tsserverRequestHandlers = new Map<number, (res: any) => void>();
19
+
20
+ let tsserverRequestId = 0;
21
+
22
+ connection.listen();
23
+
24
+ connection.onNotification('tsserver/response', ([id, res]) => {
25
+ tsserverRequestHandlers.get(id)?.(res);
26
+ tsserverRequestHandlers.delete(id);
27
+ });
28
+
29
+ /**
30
+ * Handle the `initialize` request from the client. This is the first request sent by the client to
31
+ * the server. It includes the set of capabilities supported by the client as well as
32
+ * other initialization params needed by the server.
33
+ */
34
+ connection.onInitialize((params) => {
35
+ const tsconfigProjects = createUriMap<LanguageService>();
36
+
37
+ server.fileWatcher.onDidChangeWatchedFiles((obj: any) => {
38
+ for (const change of obj.changes) {
39
+ const changeUri = URI.parse(change.uri);
40
+ if (tsconfigProjects.has(changeUri)) {
41
+ tsconfigProjects.get(changeUri)!.dispose();
42
+ tsconfigProjects.delete(changeUri);
43
+ }
44
+ }
45
+ });
46
+
47
+ let simpleLs: LanguageService | undefined;
48
+
49
+ return server.initialize(
50
+ params,
51
+ {
52
+ setup() {},
53
+ async getLanguageService(uri) {
54
+ if (uri.scheme === 'file') {
55
+ // Use tsserver to find the tsconfig governing this file.
56
+ const fileName = uri.fsPath.replace(/\\/g, '/');
57
+ const projectInfo = await sendTsServerRequest<ts.server.protocol.ProjectInfo>(
58
+ '_glint:' + ts.server.protocol.CommandTypes.ProjectInfo,
59
+ {
60
+ file: fileName,
61
+ needFileNameList: false,
62
+ } satisfies ts.server.protocol.ProjectInfoRequestArgs,
63
+ );
64
+ if (projectInfo) {
65
+ const { configFileName } = projectInfo;
66
+ let ls = tsconfigProjects.get(URI.file(configFileName));
67
+ if (!ls) {
68
+ ls = createLanguageServiceHelper(server, configFileName);
69
+ tsconfigProjects.set(URI.file(configFileName), ls);
70
+ }
71
+ return ls;
72
+ }
73
+ }
74
+ // TODO: this branch is hit when running Volar Labs and currently breaks. Figure out
75
+ // how to reinstate a "simple" LS without a tsconfig.
76
+ return (simpleLs ??= createLanguageServiceHelper(server, undefined));
77
+ },
78
+ getExistingLanguageServices() {
79
+ return Promise.all([...tsconfigProjects.values(), simpleLs].filter((promise) => !!promise));
80
+ },
81
+ reload() {
82
+ for (const ls of [...tsconfigProjects.values(), simpleLs]) {
83
+ ls?.dispose();
84
+ }
85
+ tsconfigProjects.clear();
86
+ simpleLs = undefined;
87
+ },
88
+ },
89
+ getHybridModeLanguageServicePluginsForLanguageServer({
90
+ // TODO: Implement Glint-specific tsserver requests similar to Vue
91
+ // For now, keeping the basic structure ready for future implementation
92
+ // getQuickInfoAtPosition(...args) {
93
+ // return sendTsServerRequest('_glint:quickinfo', args);
94
+ // },
95
+ // getDocumentHighlights(...args) {
96
+ // return sendTsServerRequest('_glint:documentHighlights-full', args);
97
+ // },
98
+ // getEncodedSemanticClassifications(...args) {
99
+ // return sendTsServerRequest('_glint:encodedSemanticClassifications-full', args);
100
+ // },
101
+ }),
102
+ );
103
+
104
+ async function sendTsServerRequest<T>(command: string, args: any): Promise<T | null> {
105
+ return await new Promise<T | null>((resolve) => {
106
+ const requestId = ++tsserverRequestId;
107
+ tsserverRequestHandlers.set(requestId, resolve);
108
+ connection.sendNotification('tsserver/request', [requestId, command, args]);
109
+ });
110
+ }
111
+
112
+ function createLanguageServiceHelper(
113
+ server: LanguageServer,
114
+ tsconfigFileName: string | undefined,
115
+ ): LanguageService {
116
+ const languagePlugins: LanguagePlugin<URI>[] = [
117
+ {
118
+ getLanguageId: (uri) => server.documents.get(uri)?.languageId,
119
+ },
120
+ ];
121
+
122
+ if (tsconfigFileName) {
123
+ const configLoader = new ConfigLoader();
124
+ const glintConfig = configLoader.configForFile(tsconfigFileName);
125
+ if (glintConfig) {
126
+ const emberLanguagePlugin = createEmberLanguagePlugin(glintConfig);
127
+ languagePlugins.push(emberLanguagePlugin);
128
+ }
129
+ }
130
+
131
+ const language = createLanguage<URI>(languagePlugins, createUriMap(), (uri) => {
132
+ const document = server.documents.get(uri);
133
+ if (document) {
134
+ language.scripts.set(uri, document.getSnapshot(), document.languageId);
135
+ } else {
136
+ language.scripts.delete(uri);
137
+ }
138
+ });
139
+ return createLanguageService(
140
+ language,
141
+ server.languageServicePlugins,
142
+ createLanguageServiceEnvironment(server, [...server.workspaceFolders.all]),
143
+ {},
144
+ );
145
+ }
146
+ });
147
+
148
+ connection.onInitialized(server.initialized);
149
+
150
+ connection.onShutdown(server.shutdown);
151
+
152
+ function getHybridModeLanguageServicePluginsForLanguageServer(
153
+ tsPluginClient: any = {}, // Glint's equivalent to Vue's tsPluginClient
154
+ ): LanguageServicePlugin<any>[] {
155
+ const plugins = [
156
+ // Lightweight syntax-only TS Language Service. Provides Symbols (e.g. Outline view) and other features.
157
+ createTypeScriptSyntacticPlugin(ts),
158
+ createHtmlSyntacticPlugin(),
159
+ ...getCommonLanguageServicePluginsForLanguageServer(() => tsPluginClient),
160
+ ];
161
+ for (const plugin of plugins) {
162
+ // avoid affecting TS plugin
163
+ delete plugin.capabilities.semanticTokensProvider;
164
+ }
165
+ return plugins;
166
+ }
167
+
168
+ function getCommonLanguageServicePluginsForLanguageServer(
169
+ getTsPluginClient: (context: LanguageServiceContext) => any,
170
+ // ) => import('@glint/tsserver/lib/requests').Requests | undefined,
171
+ ): LanguageServicePlugin[] {
172
+ return [
173
+ createTemplateTagSymbolsPlugin(),
174
+ createCompilerErrorsPlugin(),
175
+ // createTypeScriptTwoslashQueriesPlugin(ts),
176
+ // createCssPlugin(),
177
+ // createPugFormatPlugin(),
178
+ // createJsonPlugin(),
179
+ // createVueTemplatePlugin('html', getTsPluginClient),
180
+ // createVueTemplatePlugin('pug', getTsPluginClient),
181
+ // createVueMissingPropsHintsPlugin(getTsPluginClient),
182
+ // createVueCompilerDomErrorsPlugin(),
183
+ // createVueSfcPlugin(),
184
+ // createVueTwoslashQueriesPlugin(getTsPluginClient),
185
+ // createVueDocumentLinksPlugin(),
186
+ // createVueDocumentDropPlugin(ts, getTsPluginClient),
187
+ // createVueCompleteDefineAssignmentPlugin(),
188
+ // createVueAutoDotValuePlugin(ts, getTsPluginClient),
189
+ // createVueAutoAddSpacePlugin(),
190
+ // createVueInlayHintsPlugin(ts),
191
+ // createVueDirectiveCommentsPlugin(),
192
+ // createVueExtractFilePlugin(ts, getTsPluginClient),
193
+ // createEmmetPlugin({
194
+ // mappedLanguages: {
195
+ // 'vue-root-tags': 'html',
196
+ // 'postcss': 'scss',
197
+ // },
198
+ // }),
199
+ // {
200
+ // name: 'vue-parse-sfc',
201
+ // capabilities: {
202
+ // executeCommandProvider: {
203
+ // commands: [commands.parseSfc],
204
+ // },
205
+ // },
206
+ // create() {
207
+ // return {
208
+ // executeCommand(_command, [source]) {
209
+ // return parse(source);
210
+ // },
211
+ // };
212
+ // },
213
+ // },
214
+ // {
215
+ // name: 'vue-name-casing',
216
+ // capabilities: {
217
+ // executeCommandProvider: {
218
+ // commands: [
219
+ // commands.detectNameCasing,
220
+ // commands.convertTagsToKebabCase,
221
+ // commands.convertTagsToPascalCase,
222
+ // commands.convertPropsToKebabCase,
223
+ // commands.convertPropsToCamelCase,
224
+ // ],
225
+ // }
226
+ // },
227
+ // create(context) {
228
+ // return {
229
+ // executeCommand(command, [uri]) {
230
+ // if (command === commands.detectNameCasing) {
231
+ // return detect(context, URI.parse(uri));
232
+ // }
233
+ // else if (command === commands.convertTagsToKebabCase) {
234
+ // return convertTagName(context, URI.parse(uri), TagNameCasing.Kebab, getTsPluginClient(context));
235
+ // }
236
+ // else if (command === commands.convertTagsToPascalCase) {
237
+ // return convertTagName(context, URI.parse(uri), TagNameCasing.Pascal, getTsPluginClient(context));
238
+ // }
239
+ // else if (command === commands.convertPropsToKebabCase) {
240
+ // return convertAttrName(context, URI.parse(uri), AttrNameCasing.Kebab, getTsPluginClient(context));
241
+ // }
242
+ // else if (command === commands.convertPropsToCamelCase) {
243
+ // return convertAttrName(context, URI.parse(uri), AttrNameCasing.Camel, getTsPluginClient(context));
244
+ // }
245
+ // },
246
+ // };
247
+ // },
248
+ // }
249
+ ];
250
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @typedef {import('typescript').IScriptSnapshot} IScriptSnapshot
3
+ */
4
+
5
+ import { IScriptSnapshot, TextChangeRange } from 'typescript';
6
+
7
+ /**
8
+ * A TypeScript compatible script snapshot that wraps a string of text.
9
+ *
10
+ * @implements {IScriptSnapshot}
11
+ */
12
+ export class ScriptSnapshot implements IScriptSnapshot {
13
+ constructor(public text: string) {}
14
+
15
+ // Not Implemented
16
+ getChangeRange(_oldSnapshot: IScriptSnapshot): TextChangeRange | undefined {
17
+ return undefined;
18
+ }
19
+
20
+ getLength(): number {
21
+ return this.text.length;
22
+ }
23
+
24
+ getText(start: number, end: number): string {
25
+ return this.text.slice(start, end);
26
+ }
27
+ }