@litsx/typescript 0.6.0
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.
- package/README.md +85 -0
- package/dist/authored-semantics.cjs +18567 -0
- package/dist/authored-semantics.cjs.map +1 -0
- package/dist/editor-session.cjs +1345 -0
- package/dist/editor-session.cjs.map +1 -0
- package/dist/index.cjs +841 -0
- package/dist/index.cjs.map +1 -0
- package/dist/litsx-tsc.js +18025 -0
- package/dist/typecheck.cjs +931 -0
- package/dist/typecheck.cjs.map +1 -0
- package/dist/virtualization.cjs +113 -0
- package/dist/virtualization.cjs.map +1 -0
- package/package.json +76 -0
- package/src/authored-semantics.js +1543 -0
- package/src/editor-session.js +1360 -0
- package/src/index.js +844 -0
- package/src/litsx-tsc.js +4 -0
- package/src/typecheck.js +535 -0
- package/src/virtualization.js +125 -0
- package/tsserver-plugin.cjs +11 -0
package/src/litsx-tsc.js
ADDED
package/src/typecheck.js
ADDED
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
import { fileURLToPath } from "url";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import ts from "typescript";
|
|
4
|
+
import { getOrCreateProjectTsSession } from "@litsx/typescript-session";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
createToolingVirtualLitsxSource,
|
|
8
|
+
looksLikeLitsxJsx,
|
|
9
|
+
remapToolingTextSpanToOriginal,
|
|
10
|
+
remapVirtualText,
|
|
11
|
+
} from "./virtualization.js";
|
|
12
|
+
|
|
13
|
+
const PROFILE_ENABLED = process.env.LITSX_PROFILE === "1";
|
|
14
|
+
const PARSED_COMMAND_LINE_CACHE = new Map();
|
|
15
|
+
const TYPECHECK_SESSION_BY_PROJECT = new Map();
|
|
16
|
+
const TYPECHECK_SESSION_CACHE_LIMIT = 20;
|
|
17
|
+
const FORMAT_HOST = createFormatHost();
|
|
18
|
+
const LITSX_EXTRA_FILE_EXTENSIONS = [
|
|
19
|
+
{
|
|
20
|
+
extension: ".litsx",
|
|
21
|
+
isMixedContent: false,
|
|
22
|
+
scriptKind: ts.ScriptKind.TSX,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
extension: ".litsx.jsx",
|
|
26
|
+
isMixedContent: false,
|
|
27
|
+
scriptKind: ts.ScriptKind.JSX,
|
|
28
|
+
},
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
function profilePhase(namespace, name, callback) {
|
|
32
|
+
if (!PROFILE_ENABLED) {
|
|
33
|
+
return callback();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const start = performance.now();
|
|
37
|
+
try {
|
|
38
|
+
return callback();
|
|
39
|
+
} finally {
|
|
40
|
+
globalThis.__litsxProfileEvents ??= [];
|
|
41
|
+
globalThis.__litsxProfileEvents.push({
|
|
42
|
+
namespace,
|
|
43
|
+
name,
|
|
44
|
+
durationMs: performance.now() - start,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function isRelevantFile(fileName) {
|
|
50
|
+
return /\.(jsx|tsx|litsx)$/.test(fileName) || fileName.endsWith(".litsx.jsx");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function getPluginsForFile(fileName) {
|
|
54
|
+
return (fileName.endsWith(".tsx") || fileName.endsWith(".litsx")) ? ["typescript"] : [];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function getAdditionalLitsxFileNames(tsconfigPath, configJson, basePath) {
|
|
58
|
+
const configuredFiles = Array.isArray(configJson?.files)
|
|
59
|
+
? configJson.files
|
|
60
|
+
.filter((file) => file.endsWith(".litsx") || file.endsWith(".litsx.jsx"))
|
|
61
|
+
.map((file) => path.resolve(basePath, file))
|
|
62
|
+
: [];
|
|
63
|
+
|
|
64
|
+
const explicitlyIncludedFiles = Array.isArray(configJson?.include)
|
|
65
|
+
? configJson.include
|
|
66
|
+
.filter((pattern) => pattern.endsWith(".litsx") || pattern.endsWith(".litsx.jsx"))
|
|
67
|
+
.map((pattern) => path.resolve(basePath, pattern))
|
|
68
|
+
.filter((fileName) => ts.sys.fileExists(fileName))
|
|
69
|
+
: [];
|
|
70
|
+
|
|
71
|
+
const includedFiles = ts.sys.readDirectory(
|
|
72
|
+
basePath,
|
|
73
|
+
[".litsx", ".litsx.jsx"],
|
|
74
|
+
configJson?.exclude,
|
|
75
|
+
configJson?.include ?? ["**/*"],
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
return [...new Set([...configuredFiles, ...explicitlyIncludedFiles, ...includedFiles])]
|
|
79
|
+
.filter((fileName) => fileName !== tsconfigPath)
|
|
80
|
+
.sort();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function trimCacheToLimit(cache, limit) {
|
|
84
|
+
while (cache.size > limit) {
|
|
85
|
+
const oldestKey = cache.keys().next().value;
|
|
86
|
+
if (oldestKey == null) break;
|
|
87
|
+
cache.delete(oldestKey);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function getFileVersion(filePath) {
|
|
92
|
+
try {
|
|
93
|
+
const stats = ts.sys.getModifiedTime?.(filePath);
|
|
94
|
+
if (stats instanceof Date) {
|
|
95
|
+
return String(stats.getTime());
|
|
96
|
+
}
|
|
97
|
+
} catch {}
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
const content = ts.sys.readFile(filePath);
|
|
101
|
+
return typeof content === "string" ? String(content.length) : null;
|
|
102
|
+
} catch {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function createNormalizedArgKey(rawArgs) {
|
|
108
|
+
return rawArgs.join("\0");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function createSessionKey(projectPath, rawArgs) {
|
|
112
|
+
return `${projectPath || "<no-project>"}:${createNormalizedArgKey(rawArgs)}`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function createVirtualizationState() {
|
|
116
|
+
const cache = new Map();
|
|
117
|
+
const sourceFileCache = new Map();
|
|
118
|
+
|
|
119
|
+
function normalizeFileName(fileName) {
|
|
120
|
+
return path.resolve(fileName);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function get(fileName) {
|
|
124
|
+
return cache.get(normalizeFileName(fileName)) ?? null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function clear(fileName) {
|
|
128
|
+
const normalizedFileName = normalizeFileName(fileName);
|
|
129
|
+
cache.delete(normalizedFileName);
|
|
130
|
+
sourceFileCache.delete(normalizedFileName);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function buildVirtualizationRecord(fileName, sourceText) {
|
|
134
|
+
const normalizedFileName = normalizeFileName(fileName);
|
|
135
|
+
const cachedRecord = cache.get(normalizedFileName) ?? null;
|
|
136
|
+
|
|
137
|
+
if (cachedRecord?.sourceText === sourceText) {
|
|
138
|
+
return cachedRecord;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (!isRelevantFile(fileName) || !looksLikeLitsxJsx(sourceText)) {
|
|
142
|
+
clear(fileName);
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const parserPlugins = getPluginsForFile(fileName);
|
|
147
|
+
const virtualization = profilePhase(
|
|
148
|
+
"typescript-typecheck",
|
|
149
|
+
"tooling-virtualization",
|
|
150
|
+
() => createToolingVirtualLitsxSource(sourceText, {
|
|
151
|
+
plugins: parserPlugins,
|
|
152
|
+
}),
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
if (virtualization.code === sourceText) {
|
|
156
|
+
clear(fileName);
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const record = {
|
|
161
|
+
...virtualization,
|
|
162
|
+
sourceText,
|
|
163
|
+
parserPlugins,
|
|
164
|
+
virtualizedText: virtualization.code,
|
|
165
|
+
};
|
|
166
|
+
cache.set(normalizedFileName, record);
|
|
167
|
+
return record;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
get,
|
|
172
|
+
getOrBuild(fileName, sourceText) {
|
|
173
|
+
return buildVirtualizationRecord(fileName, sourceText);
|
|
174
|
+
},
|
|
175
|
+
getVirtualizedText(fileName, sourceText) {
|
|
176
|
+
const virtualization = buildVirtualizationRecord(fileName, sourceText);
|
|
177
|
+
return virtualization?.virtualizedText ?? sourceText;
|
|
178
|
+
},
|
|
179
|
+
getOrCreateSourceFile(fileName, sourceText, languageVersion, scriptKind) {
|
|
180
|
+
const normalizedFileName = normalizeFileName(fileName);
|
|
181
|
+
const virtualizedText = this.getVirtualizedText(fileName, sourceText);
|
|
182
|
+
const cacheKey = `${String(languageVersion)}:${String(scriptKind ?? "")}:${sourceText}`;
|
|
183
|
+
let fileCache = sourceFileCache.get(normalizedFileName);
|
|
184
|
+
if (!fileCache) {
|
|
185
|
+
fileCache = new Map();
|
|
186
|
+
sourceFileCache.set(normalizedFileName, fileCache);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const cachedSourceFile = fileCache.get(cacheKey);
|
|
190
|
+
if (cachedSourceFile) {
|
|
191
|
+
return cachedSourceFile;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const sourceFile = ts.createSourceFile(
|
|
195
|
+
fileName,
|
|
196
|
+
virtualizedText,
|
|
197
|
+
languageVersion,
|
|
198
|
+
true,
|
|
199
|
+
scriptKind,
|
|
200
|
+
);
|
|
201
|
+
fileCache.set(cacheKey, sourceFile);
|
|
202
|
+
return sourceFile;
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function remapMessageText(messageText) {
|
|
208
|
+
if (typeof messageText === "string") {
|
|
209
|
+
return remapVirtualText(messageText);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (!messageText || typeof messageText !== "object") {
|
|
213
|
+
return messageText;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
...messageText,
|
|
218
|
+
messageText: remapMessageText(messageText.messageText),
|
|
219
|
+
next: messageText.next?.map(remapMessageText),
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function remapDiagnostic(diagnostic, virtualizationState) {
|
|
224
|
+
const fileName = diagnostic.file?.fileName;
|
|
225
|
+
const virtualization = fileName ? virtualizationState.get(fileName) : null;
|
|
226
|
+
|
|
227
|
+
if (!virtualization || typeof diagnostic.start !== "number") {
|
|
228
|
+
return {
|
|
229
|
+
...diagnostic,
|
|
230
|
+
messageText: remapMessageText(diagnostic.messageText),
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const remappedSpan = remapToolingTextSpanToOriginal(
|
|
235
|
+
{
|
|
236
|
+
start: diagnostic.start,
|
|
237
|
+
length: diagnostic.length ?? 0,
|
|
238
|
+
},
|
|
239
|
+
virtualization,
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
return {
|
|
243
|
+
...diagnostic,
|
|
244
|
+
start: remappedSpan.start,
|
|
245
|
+
length: remappedSpan.length,
|
|
246
|
+
messageText: remapMessageText(diagnostic.messageText),
|
|
247
|
+
relatedInformation: diagnostic.relatedInformation?.map((info) => {
|
|
248
|
+
if (!info.file || typeof info.start !== "number") {
|
|
249
|
+
return {
|
|
250
|
+
...info,
|
|
251
|
+
messageText: remapMessageText(info.messageText),
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const infoVirtualization = virtualizationState.get(info.file.fileName);
|
|
256
|
+
if (!infoVirtualization) {
|
|
257
|
+
return {
|
|
258
|
+
...info,
|
|
259
|
+
messageText: remapMessageText(info.messageText),
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const infoSpan = remapToolingTextSpanToOriginal(
|
|
264
|
+
{
|
|
265
|
+
start: info.start,
|
|
266
|
+
length: info.length ?? 0,
|
|
267
|
+
},
|
|
268
|
+
infoVirtualization,
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
return {
|
|
272
|
+
...info,
|
|
273
|
+
start: infoSpan.start,
|
|
274
|
+
length: infoSpan.length,
|
|
275
|
+
messageText: remapMessageText(info.messageText),
|
|
276
|
+
};
|
|
277
|
+
}),
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function createFormatHost() {
|
|
282
|
+
return {
|
|
283
|
+
getCanonicalFileName(fileName) {
|
|
284
|
+
return ts.sys.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
|
|
285
|
+
},
|
|
286
|
+
getCurrentDirectory() {
|
|
287
|
+
return ts.sys.getCurrentDirectory();
|
|
288
|
+
},
|
|
289
|
+
getNewLine() {
|
|
290
|
+
return ts.sys.newLine;
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function resolveParsedCommandLine(rawArgs) {
|
|
296
|
+
const parsedCommandLine = ts.parseCommandLine(rawArgs);
|
|
297
|
+
|
|
298
|
+
if (parsedCommandLine.errors.length > 0) {
|
|
299
|
+
return parsedCommandLine;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const projectPath = parsedCommandLine.options.project
|
|
303
|
+
? path.resolve(parsedCommandLine.options.project)
|
|
304
|
+
: ts.findConfigFile(process.cwd(), ts.sys.fileExists, "tsconfig.json");
|
|
305
|
+
|
|
306
|
+
if (!projectPath) {
|
|
307
|
+
return {
|
|
308
|
+
...parsedCommandLine,
|
|
309
|
+
errors: [
|
|
310
|
+
{
|
|
311
|
+
category: ts.DiagnosticCategory.Error,
|
|
312
|
+
code: 5083,
|
|
313
|
+
file: undefined,
|
|
314
|
+
start: undefined,
|
|
315
|
+
length: undefined,
|
|
316
|
+
messageText: "Cannot find a tsconfig.json file.",
|
|
317
|
+
},
|
|
318
|
+
],
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const configFile = ts.readConfigFile(projectPath, ts.sys.readFile);
|
|
323
|
+
if (configFile.error) {
|
|
324
|
+
return {
|
|
325
|
+
...parsedCommandLine,
|
|
326
|
+
errors: [configFile.error],
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const configHost = {
|
|
331
|
+
...ts.sys,
|
|
332
|
+
onUnRecoverableConfigFileDiagnostic() {},
|
|
333
|
+
};
|
|
334
|
+
const projectVersion = getFileVersion(projectPath) ?? "unknown";
|
|
335
|
+
const cacheKey = `${projectPath}:${projectVersion}:${createNormalizedArgKey(rawArgs)}`;
|
|
336
|
+
const cached = PARSED_COMMAND_LINE_CACHE.get(cacheKey);
|
|
337
|
+
if (cached) {
|
|
338
|
+
PARSED_COMMAND_LINE_CACHE.delete(cacheKey);
|
|
339
|
+
PARSED_COMMAND_LINE_CACHE.set(cacheKey, cached);
|
|
340
|
+
return cached;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const resolved = {
|
|
344
|
+
...ts.parseJsonConfigFileContent(
|
|
345
|
+
configFile.config,
|
|
346
|
+
configHost,
|
|
347
|
+
path.dirname(projectPath),
|
|
348
|
+
parsedCommandLine.options,
|
|
349
|
+
projectPath,
|
|
350
|
+
undefined,
|
|
351
|
+
LITSX_EXTRA_FILE_EXTENSIONS,
|
|
352
|
+
),
|
|
353
|
+
projectPath,
|
|
354
|
+
projectVersion,
|
|
355
|
+
__litsxSessionKey: createSessionKey(projectPath, rawArgs),
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
if (!Object.hasOwn(configFile.config?.compilerOptions || {}, "types")) {
|
|
359
|
+
resolved.options = {
|
|
360
|
+
...resolved.options,
|
|
361
|
+
// Preserve TypeScript 5.x ambient @types loading for litsx-tsc when
|
|
362
|
+
// projects haven't opted into an explicit `types` list yet.
|
|
363
|
+
types: ["*"],
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
resolved.fileNames = [
|
|
368
|
+
...new Set([
|
|
369
|
+
...resolved.fileNames,
|
|
370
|
+
...getAdditionalLitsxFileNames(projectPath, configFile.config, path.dirname(projectPath)),
|
|
371
|
+
]),
|
|
372
|
+
].sort();
|
|
373
|
+
|
|
374
|
+
if (resolved.fileNames.some((fileName) => fileName.endsWith(".litsx") || fileName.endsWith(".litsx.jsx"))) {
|
|
375
|
+
resolved.options = {
|
|
376
|
+
...resolved.options,
|
|
377
|
+
allowNonTsExtensions: true,
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (resolved.fileNames.length > 0 && Array.isArray(resolved.errors)) {
|
|
382
|
+
resolved.errors = resolved.errors.filter((error) => error?.code !== 18003);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
PARSED_COMMAND_LINE_CACHE.set(cacheKey, resolved);
|
|
386
|
+
trimCacheToLimit(PARSED_COMMAND_LINE_CACHE, TYPECHECK_SESSION_CACHE_LIMIT);
|
|
387
|
+
return resolved;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function createTypecheckSession(parsedCommandLine, projectSession = null) {
|
|
391
|
+
const virtualizationState = createVirtualizationState();
|
|
392
|
+
return {
|
|
393
|
+
parsedCommandLine,
|
|
394
|
+
sessionKey: parsedCommandLine.__litsxSessionKey,
|
|
395
|
+
virtualizationState,
|
|
396
|
+
diagnosticsCacheKey: null,
|
|
397
|
+
diagnosticsCacheResult: null,
|
|
398
|
+
projectSession: projectSession || getOrCreateProjectTsSession(parsedCommandLine.__litsxSessionKey, {
|
|
399
|
+
typescript: ts,
|
|
400
|
+
parsedCommandLine,
|
|
401
|
+
}),
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function getTypecheckSession(parsedCommandLine, projectSession = null) {
|
|
406
|
+
const sessionKey =
|
|
407
|
+
parsedCommandLine.__litsxSessionKey ||
|
|
408
|
+
createSessionKey(parsedCommandLine.projectPath, []);
|
|
409
|
+
const cached = TYPECHECK_SESSION_BY_PROJECT.get(sessionKey);
|
|
410
|
+
if (cached) {
|
|
411
|
+
cached.parsedCommandLine = parsedCommandLine;
|
|
412
|
+
if (projectSession) {
|
|
413
|
+
cached.projectSession = projectSession;
|
|
414
|
+
cached.diagnosticsCacheKey = null;
|
|
415
|
+
cached.diagnosticsCacheResult = null;
|
|
416
|
+
}
|
|
417
|
+
cached.projectSession.refresh({
|
|
418
|
+
parsedCommandLine,
|
|
419
|
+
});
|
|
420
|
+
TYPECHECK_SESSION_BY_PROJECT.delete(sessionKey);
|
|
421
|
+
TYPECHECK_SESSION_BY_PROJECT.set(sessionKey, cached);
|
|
422
|
+
return cached;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const session = createTypecheckSession(parsedCommandLine, projectSession);
|
|
426
|
+
TYPECHECK_SESSION_BY_PROJECT.set(sessionKey, session);
|
|
427
|
+
trimCacheToLimit(TYPECHECK_SESSION_BY_PROJECT, TYPECHECK_SESSION_CACHE_LIMIT);
|
|
428
|
+
return session;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
export function createLitsxTypecheckSession(rawArgs = process.argv.slice(2), options = {}) {
|
|
432
|
+
const parsedCommandLine = resolveParsedCommandLine(rawArgs);
|
|
433
|
+
return getTypecheckSession(parsedCommandLine, options.projectSession);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function createDiagnosticsCacheKey(session) {
|
|
437
|
+
const parsedCommandLine = session.parsedCommandLine;
|
|
438
|
+
const fileVersions = parsedCommandLine.fileNames.map((fileName) => (
|
|
439
|
+
`${fileName}:${getFileVersion(fileName) ?? "missing"}`
|
|
440
|
+
));
|
|
441
|
+
return `${parsedCommandLine.projectVersion || "unknown"}\0${fileVersions.join("\0")}`;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function runTypecheckSession(session) {
|
|
445
|
+
const parsedCommandLine = session.parsedCommandLine;
|
|
446
|
+
|
|
447
|
+
if (parsedCommandLine.errors?.length) {
|
|
448
|
+
const output = ts.formatDiagnosticsWithColorAndContext(parsedCommandLine.errors, FORMAT_HOST);
|
|
449
|
+
if (output) {
|
|
450
|
+
process.stderr.write(output);
|
|
451
|
+
}
|
|
452
|
+
return 1;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const virtualizationState = session.virtualizationState;
|
|
456
|
+
session.projectSession.refresh({
|
|
457
|
+
parsedCommandLine,
|
|
458
|
+
});
|
|
459
|
+
for (const fileName of parsedCommandLine.fileNames) {
|
|
460
|
+
const sourceText = session.projectSession.readFile?.(fileName) ?? ts.sys.readFile(fileName);
|
|
461
|
+
if (typeof sourceText !== "string") {
|
|
462
|
+
session.projectSession.clearOverlayFile(fileName);
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const virtualizedText = virtualizationState.getVirtualizedText(fileName, sourceText);
|
|
467
|
+
if (virtualizedText === sourceText) {
|
|
468
|
+
session.projectSession.clearOverlayFile(fileName);
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
session.projectSession.setOverlayFile(fileName, virtualizedText);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
const program = profilePhase(
|
|
476
|
+
"typescript-typecheck",
|
|
477
|
+
"create-program",
|
|
478
|
+
() => session.projectSession.getProgram(),
|
|
479
|
+
);
|
|
480
|
+
|
|
481
|
+
const diagnosticsCacheKey = profilePhase(
|
|
482
|
+
"typescript-typecheck",
|
|
483
|
+
"diagnostic-cache-key",
|
|
484
|
+
() => createDiagnosticsCacheKey(session),
|
|
485
|
+
);
|
|
486
|
+
|
|
487
|
+
if (
|
|
488
|
+
session.diagnosticsCacheKey === diagnosticsCacheKey &&
|
|
489
|
+
session.diagnosticsCacheResult
|
|
490
|
+
) {
|
|
491
|
+
const cachedResult = session.diagnosticsCacheResult;
|
|
492
|
+
if (cachedResult.output) {
|
|
493
|
+
process.stderr.write(cachedResult.output);
|
|
494
|
+
}
|
|
495
|
+
return cachedResult.exitCode;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const diagnostics = profilePhase(
|
|
499
|
+
"typescript-typecheck",
|
|
500
|
+
"diagnostics",
|
|
501
|
+
() => ts.getPreEmitDiagnostics(program),
|
|
502
|
+
)
|
|
503
|
+
.map((diagnostic) => remapDiagnostic(diagnostic, virtualizationState));
|
|
504
|
+
|
|
505
|
+
if (diagnostics.length > 0) {
|
|
506
|
+
const output = ts.formatDiagnosticsWithColorAndContext(diagnostics, FORMAT_HOST);
|
|
507
|
+
if (output) {
|
|
508
|
+
process.stderr.write(output);
|
|
509
|
+
}
|
|
510
|
+
session.diagnosticsCacheKey = diagnosticsCacheKey;
|
|
511
|
+
session.diagnosticsCacheResult = {
|
|
512
|
+
exitCode: 1,
|
|
513
|
+
output,
|
|
514
|
+
};
|
|
515
|
+
return 1;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
session.diagnosticsCacheKey = diagnosticsCacheKey;
|
|
519
|
+
session.diagnosticsCacheResult = {
|
|
520
|
+
exitCode: 0,
|
|
521
|
+
output: "",
|
|
522
|
+
};
|
|
523
|
+
return 0;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
export function runLitsxTypecheck(rawArgs = process.argv.slice(2)) {
|
|
527
|
+
const session = Array.isArray(rawArgs)
|
|
528
|
+
? createLitsxTypecheckSession(rawArgs)
|
|
529
|
+
: rawArgs;
|
|
530
|
+
return runTypecheckSession(session);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
if (process.argv[1] && fileURLToPath(import.meta.url) === path.resolve(process.argv[1])) {
|
|
534
|
+
process.exitCode = runLitsxTypecheck(process.argv.slice(2));
|
|
535
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import {
|
|
2
|
+
collectLitsxAuthoredIssues,
|
|
3
|
+
createVirtualLitsxJsxSource,
|
|
4
|
+
decodeVirtualAttributeName,
|
|
5
|
+
inferLitsxStaticHoistInfoAtPosition,
|
|
6
|
+
getLitsxAttributeCompletionNames,
|
|
7
|
+
inferLitsxComponentPropNames,
|
|
8
|
+
inferLitsxComponentEventNames,
|
|
9
|
+
inferLitsxAttributeCompletionContext,
|
|
10
|
+
inferLitsxAttributeInfoAtPosition,
|
|
11
|
+
inferLitsxMarkupCompletionContext,
|
|
12
|
+
getLitsxMarkupCompletionNames,
|
|
13
|
+
looksLikeLitsxJsx,
|
|
14
|
+
mapOriginalPositionToVirtual,
|
|
15
|
+
remapTextSpanToOriginal,
|
|
16
|
+
remapVirtualText,
|
|
17
|
+
STATIC_HOIST_CALL_RE,
|
|
18
|
+
} from "./authored-semantics.js";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* TypeScript-facing virtualization helpers for LitSX authored syntax.
|
|
22
|
+
*
|
|
23
|
+
* The tsserver plugin, CLI typechecker, editor sessions, and lint tooling use
|
|
24
|
+
* this module to translate LitSX-only forms into TypeScript-safe source text,
|
|
25
|
+
* then remap diagnostics, hovers, and completions back to authored positions.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
export function createToolingVirtualLitsxSource(sourceText, options = {}) {
|
|
29
|
+
const virtualization = createVirtualLitsxJsxSource(sourceText, options);
|
|
30
|
+
const hoistNames = new Set();
|
|
31
|
+
const usesTypeScriptSyntax = options.plugins?.includes("typescript");
|
|
32
|
+
|
|
33
|
+
for (const match of virtualization.code.matchAll(STATIC_HOIST_CALL_RE)) {
|
|
34
|
+
hoistNames.add(match[1]);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (hoistNames.size === 0) {
|
|
38
|
+
return {
|
|
39
|
+
...virtualization,
|
|
40
|
+
toolingPreamble: "",
|
|
41
|
+
toolingPreambleLength: 0,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const toolingDeclarations = [];
|
|
46
|
+
|
|
47
|
+
toolingDeclarations.push(
|
|
48
|
+
...Array.from(hoistNames)
|
|
49
|
+
.sort()
|
|
50
|
+
.map((name) => (
|
|
51
|
+
usesTypeScriptSyntax
|
|
52
|
+
? (
|
|
53
|
+
name === "__litsx_static_lightDom"
|
|
54
|
+
? "declare function __litsx_static_lightDom(): void;\n"
|
|
55
|
+
: `declare function ${name}<T = unknown>(value: T): T;\n`
|
|
56
|
+
)
|
|
57
|
+
: (
|
|
58
|
+
name === "__litsx_static_lightDom"
|
|
59
|
+
? "function __litsx_static_lightDom() {}\n"
|
|
60
|
+
: `/** @template T @param {T} value @returns {T} */\nfunction ${name}(value) { return value; }\n`
|
|
61
|
+
)
|
|
62
|
+
))
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const toolingPreamble = toolingDeclarations.join("");
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
...virtualization,
|
|
69
|
+
code: `${toolingPreamble}${virtualization.code}`,
|
|
70
|
+
toolingPreamble,
|
|
71
|
+
toolingPreambleLength: toolingPreamble.length,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function mapOriginalPositionToToolingVirtual(position, virtualization) {
|
|
76
|
+
return mapOriginalPositionToVirtual(position, virtualization.replacements) + (virtualization.toolingPreambleLength ?? 0);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function remapToolingTextSpanToOriginal(span, virtualization) {
|
|
80
|
+
if (!span) {
|
|
81
|
+
return span;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const preambleLength = virtualization.toolingPreambleLength ?? 0;
|
|
85
|
+
const start = Math.max(0, (span.start ?? 0) - preambleLength);
|
|
86
|
+
|
|
87
|
+
return remapTextSpanToOriginal(
|
|
88
|
+
{
|
|
89
|
+
start,
|
|
90
|
+
length: span.length ?? 0,
|
|
91
|
+
},
|
|
92
|
+
virtualization.replacements,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function collectLitsxAuthoredDiagnostics(sourceText, ts, options = {}) {
|
|
97
|
+
return collectLitsxAuthoredIssues(sourceText, options).map((issue) => ({
|
|
98
|
+
start: issue.start,
|
|
99
|
+
length: issue.length,
|
|
100
|
+
category: issue.severity === "warning"
|
|
101
|
+
? (ts?.DiagnosticCategory?.Warning ?? 0)
|
|
102
|
+
: (ts?.DiagnosticCategory?.Error ?? 1),
|
|
103
|
+
code: issue.code,
|
|
104
|
+
source: "@litsx/typescript",
|
|
105
|
+
messageText: issue.message,
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export {
|
|
110
|
+
collectLitsxAuthoredIssues,
|
|
111
|
+
createVirtualLitsxJsxSource,
|
|
112
|
+
decodeVirtualAttributeName,
|
|
113
|
+
inferLitsxStaticHoistInfoAtPosition,
|
|
114
|
+
getLitsxAttributeCompletionNames,
|
|
115
|
+
inferLitsxComponentPropNames,
|
|
116
|
+
inferLitsxComponentEventNames,
|
|
117
|
+
inferLitsxAttributeCompletionContext,
|
|
118
|
+
inferLitsxAttributeInfoAtPosition,
|
|
119
|
+
inferLitsxMarkupCompletionContext,
|
|
120
|
+
getLitsxMarkupCompletionNames,
|
|
121
|
+
looksLikeLitsxJsx,
|
|
122
|
+
mapOriginalPositionToVirtual,
|
|
123
|
+
remapTextSpanToOriginal,
|
|
124
|
+
remapVirtualText,
|
|
125
|
+
};
|