tempo-sdk 0.0.1 → 0.0.3
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/dist/index.d.ts +33 -8
- package/dist/index.js +228 -21
- package/dist/index.js.map +1 -1
- package/package.json +7 -2
- package/dist/page.d.ts +0 -27
- package/dist/page.js +0 -1
- package/dist/page.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { JSXElement } from 'oxc-parser';
|
|
2
2
|
import { FilterPattern } from '@rollup/pluginutils';
|
|
3
|
+
import { ServerResponse, IncomingMessage } from 'node:http';
|
|
3
4
|
import { Plugin } from 'vite';
|
|
4
|
-
import {
|
|
5
|
+
import { ReactElement } from 'react';
|
|
5
6
|
|
|
6
7
|
interface ElementInfo {
|
|
7
8
|
element: JSXElement;
|
|
@@ -41,26 +42,50 @@ interface StoryboardLayout {
|
|
|
41
42
|
height: number;
|
|
42
43
|
zIndex?: number;
|
|
43
44
|
}
|
|
44
|
-
interface TempoStoryboard
|
|
45
|
-
|
|
45
|
+
interface TempoStoryboard {
|
|
46
|
+
render: () => ReactElement;
|
|
46
47
|
name?: string;
|
|
47
|
-
args?: Partial<ComponentProps<C>>;
|
|
48
48
|
layout: StoryboardLayout;
|
|
49
|
-
container?: (Story: ComponentType<any>) => ReactElement;
|
|
50
49
|
}
|
|
51
50
|
interface TempoRouteStoryboard {
|
|
52
51
|
route: string;
|
|
53
52
|
name?: string;
|
|
54
53
|
layout: StoryboardLayout;
|
|
55
|
-
container?: (Story: ComponentType<any>) => ReactElement;
|
|
56
54
|
}
|
|
57
55
|
|
|
56
|
+
/** Error payload captured from Vite's HMR error events or console interception. */
|
|
57
|
+
type TempoViteError = {
|
|
58
|
+
type: "compile" | "evaluation";
|
|
59
|
+
message: string;
|
|
60
|
+
stack?: string;
|
|
61
|
+
file?: string;
|
|
62
|
+
frame?: string;
|
|
63
|
+
plugin?: string;
|
|
64
|
+
loc?: {
|
|
65
|
+
file?: string;
|
|
66
|
+
line?: number;
|
|
67
|
+
column?: number;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
58
70
|
type ComputeAnnotatedProps = (info: ElementInfo, filepath: string, modifiedtsMs: number) => Record<string, PropValue>;
|
|
59
71
|
interface TempoAnnotateOptions {
|
|
60
72
|
computeAnnotatedProps?: ComputeAnnotatedProps;
|
|
61
73
|
include?: FilterPattern;
|
|
62
74
|
exclude?: FilterPattern;
|
|
63
75
|
}
|
|
64
|
-
|
|
76
|
+
type SseClient = ServerResponse<IncomingMessage>;
|
|
77
|
+
interface TempoErrorServer {
|
|
78
|
+
/** The error currently broadcast to SSE clients (most recent across all modules). */
|
|
79
|
+
readonly currentError: TempoViteError | null;
|
|
80
|
+
clients: Set<SseClient>;
|
|
81
|
+
setError(error: TempoViteError): void;
|
|
82
|
+
/** Clear errors for modules present in the HMR update. */
|
|
83
|
+
clearErrorForModules(updatedPaths: string[]): void;
|
|
84
|
+
/** Clear all errors unconditionally (e.g. report-clear endpoint). */
|
|
85
|
+
clearAll(): void;
|
|
86
|
+
}
|
|
87
|
+
declare function tempoVitePlugin(options?: TempoAnnotateOptions): Plugin;
|
|
88
|
+
/** @deprecated Use `tempoVitePlugin` instead. */
|
|
89
|
+
declare const tempoAnnotate: typeof tempoVitePlugin;
|
|
65
90
|
|
|
66
|
-
export { type ComputeAnnotatedProps, type ElementInfo, type ElementTypeSupport, type FileTypeInfo, type PropValue, type StoryboardLayout, type TempoAnnotateOptions, type TempoPage, type TempoRouteStoryboard, type TempoStoryboard, type TypeAnalyzer, createTypeAnalyzer, tempoAnnotate };
|
|
91
|
+
export { type ComputeAnnotatedProps, type ElementInfo, type ElementTypeSupport, type FileTypeInfo, type PropValue, type StoryboardLayout, type TempoAnnotateOptions, type TempoErrorServer, type TempoPage, type TempoRouteStoryboard, type TempoStoryboard, type TempoViteError, type TypeAnalyzer, createTypeAnalyzer, tempoAnnotate, tempoVitePlugin };
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { realpathSync as realpathSync3, statSync as statSync2 } from "fs";
|
|
|
3
3
|
import { relative } from "path";
|
|
4
4
|
|
|
5
5
|
// ../../modules/annotation/annotate-file.ts
|
|
6
|
-
import { parseSync } from "oxc-parser";
|
|
6
|
+
import { parseSync as parseSync2 } from "oxc-parser";
|
|
7
7
|
|
|
8
8
|
// ../../modules/file-parser/utils.ts
|
|
9
9
|
function getTagName(name) {
|
|
@@ -42,6 +42,9 @@ function walk(node, callbacks) {
|
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
// ../../modules/file-parser/scope-analyzer/scopeAnalyzer.ts
|
|
46
|
+
import { parseSync, visitorKeys } from "oxc-parser";
|
|
47
|
+
|
|
45
48
|
// ../../modules/annotation/annotate-file.ts
|
|
46
49
|
function formatPropValue(value) {
|
|
47
50
|
if (value === void 0) return null;
|
|
@@ -57,7 +60,7 @@ function formatProps(props) {
|
|
|
57
60
|
}
|
|
58
61
|
var FRAGMENT_TAG_NAMES = /* @__PURE__ */ new Set(["Fragment", "React.Fragment"]);
|
|
59
62
|
function annotateFile(source, filename, computeAnnotatedProps) {
|
|
60
|
-
const parseResult =
|
|
63
|
+
const parseResult = parseSync2(filename, source);
|
|
61
64
|
const insertions = [];
|
|
62
65
|
walk(parseResult.program, {
|
|
63
66
|
JSXElement(node) {
|
|
@@ -145,10 +148,14 @@ function loadProject(descriptor) {
|
|
|
145
148
|
path.dirname(tsconfigPath)
|
|
146
149
|
);
|
|
147
150
|
if (parsed.errors.length > 0) {
|
|
148
|
-
const message = parsed.errors.map(
|
|
151
|
+
const message = parsed.errors.map(
|
|
152
|
+
(diagnostic) => ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n")
|
|
153
|
+
).join("; ");
|
|
149
154
|
throw new Error(`Failed to parse tsconfig at ${tsconfigPath}: ${message}`);
|
|
150
155
|
}
|
|
151
|
-
const normalizedFiles = parsed.fileNames.map(
|
|
156
|
+
const normalizedFiles = parsed.fileNames.map(
|
|
157
|
+
(fileName) => canonicalPath(fileName)
|
|
158
|
+
);
|
|
152
159
|
return {
|
|
153
160
|
id,
|
|
154
161
|
rootDir,
|
|
@@ -173,10 +180,14 @@ function getFileMtimeVersion(filePath) {
|
|
|
173
180
|
}
|
|
174
181
|
function createTsWorkspaceService(descriptor) {
|
|
175
182
|
if (descriptor.projects.length === 0) {
|
|
176
|
-
throw new Error(
|
|
183
|
+
throw new Error(
|
|
184
|
+
"TsWorkspaceDescriptor.projects must include at least one project"
|
|
185
|
+
);
|
|
177
186
|
}
|
|
178
187
|
const workspaceRoot = canonicalPath(descriptor.workspaceRoot);
|
|
179
|
-
const loadedProjects = descriptor.projects.map(
|
|
188
|
+
const loadedProjects = descriptor.projects.map(
|
|
189
|
+
(project) => loadProject(project)
|
|
190
|
+
);
|
|
180
191
|
const projectsById = /* @__PURE__ */ new Map();
|
|
181
192
|
for (const project of loadedProjects) {
|
|
182
193
|
if (projectsById.has(project.id)) {
|
|
@@ -186,7 +197,9 @@ function createTsWorkspaceService(descriptor) {
|
|
|
186
197
|
}
|
|
187
198
|
const primaryProject = (descriptor.primaryProjectId ? projectsById.get(descriptor.primaryProjectId) : null) ?? loadedProjects[0];
|
|
188
199
|
if (descriptor.primaryProjectId && !projectsById.has(descriptor.primaryProjectId)) {
|
|
189
|
-
throw new Error(
|
|
200
|
+
throw new Error(
|
|
201
|
+
`Primary project id not found: ${descriptor.primaryProjectId}`
|
|
202
|
+
);
|
|
190
203
|
}
|
|
191
204
|
const orderedProjects = [...loadedProjects].sort(compareProjectPriority);
|
|
192
205
|
const rootFileNames = /* @__PURE__ */ new Set();
|
|
@@ -272,7 +285,10 @@ function createTsWorkspaceService(descriptor) {
|
|
|
272
285
|
});
|
|
273
286
|
}
|
|
274
287
|
};
|
|
275
|
-
const languageService = ts.createLanguageService(
|
|
288
|
+
const languageService = ts.createLanguageService(
|
|
289
|
+
host,
|
|
290
|
+
ts.createDocumentRegistry()
|
|
291
|
+
);
|
|
276
292
|
return {
|
|
277
293
|
updateFile(filePath, content) {
|
|
278
294
|
const canonicalFilePath = canonicalPath(filePath);
|
|
@@ -300,7 +316,10 @@ function createTsWorkspaceService(descriptor) {
|
|
|
300
316
|
},
|
|
301
317
|
getDefinition(filePath, position) {
|
|
302
318
|
const canonicalFilePath = canonicalPath(filePath);
|
|
303
|
-
return languageService.getDefinitionAtPosition(
|
|
319
|
+
return languageService.getDefinitionAtPosition(
|
|
320
|
+
canonicalFilePath,
|
|
321
|
+
position
|
|
322
|
+
);
|
|
304
323
|
},
|
|
305
324
|
dispose() {
|
|
306
325
|
languageService.dispose();
|
|
@@ -333,7 +352,9 @@ function parseManagedTsconfigPaths(rawConfig) {
|
|
|
333
352
|
if (!Array.isArray(managedPaths)) {
|
|
334
353
|
return [];
|
|
335
354
|
}
|
|
336
|
-
return managedPaths.filter(
|
|
355
|
+
return managedPaths.filter(
|
|
356
|
+
(entry) => typeof entry === "string" && entry.trim().length > 0
|
|
357
|
+
);
|
|
337
358
|
}
|
|
338
359
|
function parseProjectReferenceTsconfigPaths(references, configDir) {
|
|
339
360
|
if (!references || references.length === 0) {
|
|
@@ -362,11 +383,17 @@ function buildWorkspaceProjects(root, configPath, parsedConfig, rawConfig) {
|
|
|
362
383
|
}
|
|
363
384
|
];
|
|
364
385
|
const seenTsconfigPaths = /* @__PURE__ */ new Set([canonicalPath2(configPath)]);
|
|
365
|
-
const referencedTsconfigPaths = parseProjectReferenceTsconfigPaths(
|
|
386
|
+
const referencedTsconfigPaths = parseProjectReferenceTsconfigPaths(
|
|
387
|
+
parsedConfig.projectReferences,
|
|
388
|
+
configDir
|
|
389
|
+
);
|
|
366
390
|
const managedTsconfigPaths = parseManagedTsconfigPaths(rawConfig).map(
|
|
367
391
|
(entry) => path2.resolve(configDir, entry)
|
|
368
392
|
);
|
|
369
|
-
const candidateTsconfigPaths = [
|
|
393
|
+
const candidateTsconfigPaths = [
|
|
394
|
+
...managedTsconfigPaths,
|
|
395
|
+
...referencedTsconfigPaths
|
|
396
|
+
];
|
|
370
397
|
for (const candidatePath of candidateTsconfigPaths) {
|
|
371
398
|
const normalizedPath = canonicalPath2(candidatePath);
|
|
372
399
|
if (seenTsconfigPaths.has(normalizedPath)) {
|
|
@@ -386,7 +413,11 @@ function buildWorkspaceProjects(root, configPath, parsedConfig, rawConfig) {
|
|
|
386
413
|
return projects;
|
|
387
414
|
}
|
|
388
415
|
function createTypeAnalyzer(root) {
|
|
389
|
-
const configPath = ts2.findConfigFile(
|
|
416
|
+
const configPath = ts2.findConfigFile(
|
|
417
|
+
root,
|
|
418
|
+
ts2.sys.fileExists,
|
|
419
|
+
"tsconfig.json"
|
|
420
|
+
);
|
|
390
421
|
if (!configPath) return null;
|
|
391
422
|
const configFile = ts2.readConfigFile(configPath, ts2.sys.readFile);
|
|
392
423
|
if (configFile.error) return null;
|
|
@@ -395,8 +426,15 @@ function createTypeAnalyzer(root) {
|
|
|
395
426
|
ts2.sys,
|
|
396
427
|
root
|
|
397
428
|
);
|
|
398
|
-
const workspaceProjects = buildWorkspaceProjects(
|
|
399
|
-
|
|
429
|
+
const workspaceProjects = buildWorkspaceProjects(
|
|
430
|
+
root,
|
|
431
|
+
configPath,
|
|
432
|
+
parsedConfig,
|
|
433
|
+
configFile.config
|
|
434
|
+
);
|
|
435
|
+
const workspaceProjectRoots = workspaceProjects.map(
|
|
436
|
+
(project) => canonicalPath2(project.rootDir)
|
|
437
|
+
);
|
|
400
438
|
const workspaceService = createTsWorkspaceService({
|
|
401
439
|
workspaceRoot: root,
|
|
402
440
|
primaryProjectId: "root",
|
|
@@ -406,17 +444,40 @@ function createTypeAnalyzer(root) {
|
|
|
406
444
|
function resolveCSSPropertiesType(checker, sourceFile) {
|
|
407
445
|
if (cssPropertiesType !== void 0) return cssPropertiesType;
|
|
408
446
|
try {
|
|
409
|
-
|
|
447
|
+
let jsxNs = checker.resolveName(
|
|
410
448
|
"JSX",
|
|
411
449
|
sourceFile,
|
|
412
450
|
ts2.SymbolFlags.Namespace,
|
|
413
451
|
false
|
|
414
452
|
);
|
|
453
|
+
if (!jsxNs) {
|
|
454
|
+
const program = workspaceService.getProgram();
|
|
455
|
+
const compilerOptions = program?.getCompilerOptions();
|
|
456
|
+
const jsxImportSource = compilerOptions?.jsxImportSource ?? "react";
|
|
457
|
+
const resolved = ts2.resolveModuleName(
|
|
458
|
+
`${jsxImportSource}/jsx-runtime`,
|
|
459
|
+
sourceFile.fileName,
|
|
460
|
+
compilerOptions,
|
|
461
|
+
ts2.sys
|
|
462
|
+
);
|
|
463
|
+
if (resolved.resolvedModule) {
|
|
464
|
+
const runtimeFile = program?.getSourceFile(
|
|
465
|
+
resolved.resolvedModule.resolvedFileName
|
|
466
|
+
);
|
|
467
|
+
if (runtimeFile) {
|
|
468
|
+
const runtimeSymbol = checker.getSymbolAtLocation(runtimeFile);
|
|
469
|
+
if (runtimeSymbol) {
|
|
470
|
+
const exports = checker.getExportsOfModule(runtimeSymbol);
|
|
471
|
+
jsxNs = exports.find((s) => s.name === "JSX");
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
415
476
|
if (!jsxNs) {
|
|
416
477
|
cssPropertiesType = null;
|
|
417
478
|
return null;
|
|
418
479
|
}
|
|
419
|
-
const intrinsicSym = checker.getExportsOfModule(jsxNs).find((s) => s.name === "IntrinsicElements");
|
|
480
|
+
const intrinsicSym = checker.getExportsOfModule(jsxNs).find((s) => s.name === "IntrinsicElements") ?? checker.getDeclaredTypeOfSymbol(jsxNs).getProperty("IntrinsicElements");
|
|
420
481
|
if (!intrinsicSym) {
|
|
421
482
|
cssPropertiesType = null;
|
|
422
483
|
return null;
|
|
@@ -523,7 +584,110 @@ function defaultComputeAnnotatedProps(info, filepath, modifiedtsMs) {
|
|
|
523
584
|
"data-tempo-modifiedts": modifiedtsMs
|
|
524
585
|
};
|
|
525
586
|
}
|
|
526
|
-
function
|
|
587
|
+
function createErrorServer() {
|
|
588
|
+
const errorsByFile = /* @__PURE__ */ new Map();
|
|
589
|
+
function getMostRecentError() {
|
|
590
|
+
if (errorsByFile.size === 0) return null;
|
|
591
|
+
let latest = null;
|
|
592
|
+
for (const err of errorsByFile.values()) latest = err;
|
|
593
|
+
return latest;
|
|
594
|
+
}
|
|
595
|
+
function broadcastCurrentState() {
|
|
596
|
+
const data = JSON.stringify(server.currentError);
|
|
597
|
+
for (const client of server.clients) {
|
|
598
|
+
client.write(`data: ${data}
|
|
599
|
+
|
|
600
|
+
`);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
const server = {
|
|
604
|
+
get currentError() {
|
|
605
|
+
return getMostRecentError();
|
|
606
|
+
},
|
|
607
|
+
clients: /* @__PURE__ */ new Set(),
|
|
608
|
+
setError(error) {
|
|
609
|
+
const key = error.file ?? error.loc?.file ?? "unknown";
|
|
610
|
+
errorsByFile.delete(key);
|
|
611
|
+
errorsByFile.set(key, error);
|
|
612
|
+
broadcastCurrentState();
|
|
613
|
+
},
|
|
614
|
+
clearErrorForModules(updatedPaths) {
|
|
615
|
+
if (errorsByFile.size === 0) return;
|
|
616
|
+
let changed = false;
|
|
617
|
+
for (const errorKey of [...errorsByFile.keys()]) {
|
|
618
|
+
if (errorKey === "unknown" || updatedPaths.some(
|
|
619
|
+
(p) => errorKey.endsWith(p) || p.endsWith(errorKey)
|
|
620
|
+
)) {
|
|
621
|
+
errorsByFile.delete(errorKey);
|
|
622
|
+
changed = true;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
if (changed) broadcastCurrentState();
|
|
626
|
+
},
|
|
627
|
+
clearAll() {
|
|
628
|
+
errorsByFile.clear();
|
|
629
|
+
broadcastCurrentState();
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
return server;
|
|
633
|
+
}
|
|
634
|
+
function readBody(req) {
|
|
635
|
+
return new Promise((resolve, reject) => {
|
|
636
|
+
const chunks = [];
|
|
637
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
638
|
+
req.on("end", () => resolve(Buffer.concat(chunks).toString()));
|
|
639
|
+
req.on("error", reject);
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
function handleErrorMiddleware(errorServer, req, res, next) {
|
|
643
|
+
const url = req.url;
|
|
644
|
+
if (!url?.startsWith("/__tempo/")) return next();
|
|
645
|
+
if (url === "/__tempo/report-error" && req.method === "POST") {
|
|
646
|
+
readBody(req).then((body) => {
|
|
647
|
+
try {
|
|
648
|
+
errorServer.setError(JSON.parse(body));
|
|
649
|
+
} catch {
|
|
650
|
+
}
|
|
651
|
+
res.statusCode = 200;
|
|
652
|
+
res.end("ok");
|
|
653
|
+
});
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
if (url === "/__tempo/report-clear" && req.method === "POST") {
|
|
657
|
+
errorServer.clearAll();
|
|
658
|
+
res.statusCode = 200;
|
|
659
|
+
res.end("ok");
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
if (url === "/__tempo/errors" && req.method === "OPTIONS") {
|
|
663
|
+
res.writeHead(204, {
|
|
664
|
+
"Access-Control-Allow-Origin": "*",
|
|
665
|
+
"Access-Control-Allow-Methods": "GET, OPTIONS",
|
|
666
|
+
"Access-Control-Allow-Headers": "*"
|
|
667
|
+
});
|
|
668
|
+
res.end();
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
if (url === "/__tempo/errors" && req.method === "GET") {
|
|
672
|
+
res.writeHead(200, {
|
|
673
|
+
"Content-Type": "text/event-stream",
|
|
674
|
+
"Cache-Control": "no-cache",
|
|
675
|
+
Connection: "keep-alive",
|
|
676
|
+
"Access-Control-Allow-Origin": "*"
|
|
677
|
+
});
|
|
678
|
+
res.write(`data: ${JSON.stringify(errorServer.currentError)}
|
|
679
|
+
|
|
680
|
+
`);
|
|
681
|
+
errorServer.clients.add(res);
|
|
682
|
+
req.on("close", () => {
|
|
683
|
+
errorServer.clients.delete(res);
|
|
684
|
+
});
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
next();
|
|
688
|
+
}
|
|
689
|
+
function tempoVitePlugin(options = {}) {
|
|
690
|
+
const enabled = !!process.env.TEMPO;
|
|
527
691
|
const include = options.include === void 0 ? DEFAULT_INCLUDE : options.include;
|
|
528
692
|
const exclude = options.exclude === void 0 ? DEFAULT_EXCLUDE : options.exclude;
|
|
529
693
|
const filter = createFilter(include, exclude);
|
|
@@ -531,18 +695,59 @@ function tempoAnnotate(options = {}) {
|
|
|
531
695
|
let canonicalRoot = process.cwd();
|
|
532
696
|
let command = "serve";
|
|
533
697
|
let typeAnalyzer = null;
|
|
698
|
+
const errorServer = createErrorServer();
|
|
534
699
|
return {
|
|
535
700
|
name: "tempo-annotate",
|
|
536
701
|
enforce: "pre",
|
|
702
|
+
// Expose error server for testing
|
|
703
|
+
_tempoErrorServer: errorServer,
|
|
537
704
|
configResolved(resolvedConfig) {
|
|
705
|
+
if (!enabled) return;
|
|
538
706
|
command = resolvedConfig.command;
|
|
539
707
|
canonicalRoot = resolveCanonicalRoot(resolvedConfig.root);
|
|
540
708
|
if (command === "serve") {
|
|
541
709
|
typeAnalyzer = createTypeAnalyzer(canonicalRoot);
|
|
542
710
|
}
|
|
543
711
|
},
|
|
712
|
+
configureServer(server) {
|
|
713
|
+
if (!enabled) return;
|
|
714
|
+
server.middlewares.use((req, res, next) => {
|
|
715
|
+
handleErrorMiddleware(errorServer, req, res, next);
|
|
716
|
+
});
|
|
717
|
+
const interceptHmrSend = (target) => {
|
|
718
|
+
const orig = target.send.bind(target);
|
|
719
|
+
target.send = ((...args) => {
|
|
720
|
+
const payload = args[0];
|
|
721
|
+
if (typeof payload === "object" && payload !== null && "type" in payload) {
|
|
722
|
+
const typed = payload;
|
|
723
|
+
if (typed.type === "error") {
|
|
724
|
+
const err = typed.err;
|
|
725
|
+
errorServer.setError({
|
|
726
|
+
type: "compile",
|
|
727
|
+
message: err?.message ?? "Unknown error",
|
|
728
|
+
stack: err?.stack,
|
|
729
|
+
file: err?.id ?? err?.loc?.file,
|
|
730
|
+
frame: err?.frame,
|
|
731
|
+
plugin: err?.plugin,
|
|
732
|
+
loc: err?.loc
|
|
733
|
+
});
|
|
734
|
+
} else if (typed.type === "update") {
|
|
735
|
+
const updates = typed.updates;
|
|
736
|
+
const paths = updates?.map((u) => u.path).filter((p) => typeof p === "string") ?? [];
|
|
737
|
+
errorServer.clearErrorForModules(paths);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
return orig(...args);
|
|
741
|
+
});
|
|
742
|
+
};
|
|
743
|
+
interceptHmrSend(server.ws);
|
|
744
|
+
const clientHot = server.environments?.client?.hot;
|
|
745
|
+
if (clientHot && clientHot !== server.ws) {
|
|
746
|
+
interceptHmrSend(clientHot);
|
|
747
|
+
}
|
|
748
|
+
},
|
|
544
749
|
transformIndexHtml(html) {
|
|
545
|
-
if (command !== "serve") {
|
|
750
|
+
if (!enabled || command !== "serve") {
|
|
546
751
|
return;
|
|
547
752
|
}
|
|
548
753
|
if (hasRootMetaTag(html)) {
|
|
@@ -560,7 +765,7 @@ function tempoAnnotate(options = {}) {
|
|
|
560
765
|
];
|
|
561
766
|
},
|
|
562
767
|
transform(source, id) {
|
|
563
|
-
if (command === "build") {
|
|
768
|
+
if (!enabled || command === "build") {
|
|
564
769
|
return null;
|
|
565
770
|
}
|
|
566
771
|
if (id.startsWith("\0")) {
|
|
@@ -599,8 +804,10 @@ function tempoAnnotate(options = {}) {
|
|
|
599
804
|
}
|
|
600
805
|
};
|
|
601
806
|
}
|
|
807
|
+
var tempoAnnotate = tempoVitePlugin;
|
|
602
808
|
export {
|
|
603
809
|
createTypeAnalyzer,
|
|
604
|
-
tempoAnnotate
|
|
810
|
+
tempoAnnotate,
|
|
811
|
+
tempoVitePlugin
|
|
605
812
|
};
|
|
606
813
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../index.ts","../../../modules/annotation/annotate-file.ts","../../../modules/file-parser/utils.ts","../../../modules/file-parser/walk.ts","../type-analyzer.ts","../../../modules/typescript/workspace-service.ts"],"sourcesContent":["import { realpathSync, statSync } from \"node:fs\";\nimport { relative } from \"node:path\";\n\nimport {\n annotateFile,\n type ElementInfo,\n type PropValue,\n} from \"../../modules/annotation/annotate-file.ts\";\nimport { createFilter, type FilterPattern } from \"@rollup/pluginutils\";\nimport type { Plugin, ResolvedConfig } from \"vite\";\nimport { createTypeAnalyzer, type TypeAnalyzer } from \"./type-analyzer.ts\";\n\nconst DEFAULT_INCLUDE: FilterPattern = [\"**/*.tsx\", \"**/*.jsx\"];\nconst DEFAULT_EXCLUDE: FilterPattern = [\"**/node_modules/**\"];\nconst ROOT_META_NAME = \"tempo-project-root\";\n\nfunction cleanModuleId(id: string): string {\n return id.replace(/[?#].*$/, \"\");\n}\n\nfunction resolveCanonicalRoot(root: string): string {\n try {\n return realpathSync.native(root);\n } catch {\n return root;\n }\n}\n\nfunction hasRootMetaTag(html: string): boolean {\n return /<meta[^>]+name=[\"']tempo-project-root[\"'][^>]*>/i.test(html);\n}\n\nfunction isIntrinsicElement(tagName: string): boolean {\n return tagName.length > 0 && tagName[0] === tagName[0]!.toLowerCase();\n}\n\nfunction defaultComputeAnnotatedProps(\n info: ElementInfo,\n filepath: string,\n modifiedtsMs: number,\n): Record<string, PropValue> {\n return {\n \"data-tempo-filepath\": filepath,\n \"data-tempo-position\": info.element.openingElement.start,\n \"data-tempo-modifiedts\": modifiedtsMs,\n };\n}\n\nexport type ComputeAnnotatedProps = (\n info: ElementInfo,\n filepath: string,\n modifiedtsMs: number,\n) => Record<string, PropValue>;\n\nexport interface TempoAnnotateOptions {\n computeAnnotatedProps?: ComputeAnnotatedProps;\n include?: FilterPattern;\n exclude?: FilterPattern;\n}\n\nexport function tempoAnnotate(options: TempoAnnotateOptions = {}): Plugin {\n const include =\n options.include === undefined ? DEFAULT_INCLUDE : options.include;\n const exclude =\n options.exclude === undefined ? DEFAULT_EXCLUDE : options.exclude;\n const filter = createFilter(include, exclude);\n\n const computeAnnotatedProps =\n options.computeAnnotatedProps ?? defaultComputeAnnotatedProps;\n\n let canonicalRoot = process.cwd();\n let command: ResolvedConfig[\"command\"] = \"serve\";\n let typeAnalyzer: TypeAnalyzer | null = null;\n\n return {\n name: \"tempo-annotate\",\n enforce: \"pre\",\n configResolved(resolvedConfig) {\n command = resolvedConfig.command;\n canonicalRoot = resolveCanonicalRoot(resolvedConfig.root);\n\n if (command === \"serve\") {\n typeAnalyzer = createTypeAnalyzer(canonicalRoot);\n }\n },\n transformIndexHtml(html) {\n if (command !== \"serve\") {\n return;\n }\n\n if (hasRootMetaTag(html)) {\n return;\n }\n\n return [\n {\n tag: \"meta\",\n attrs: {\n name: ROOT_META_NAME,\n content: canonicalRoot,\n },\n injectTo: \"head\",\n },\n ];\n },\n transform(source, id) {\n if (command === \"build\") {\n return null;\n }\n\n if (id.startsWith(\"\\0\")) {\n return null;\n }\n\n const cleanId = cleanModuleId(id);\n if (!filter(cleanId)) {\n return null;\n }\n\n try {\n const filepath = relative(canonicalRoot, cleanId);\n const modifiedtsMs = Math.trunc(statSync(cleanId).mtimeMs);\n\n const typeInfo = typeAnalyzer?.analyzeFileTypes(cleanId, source);\n\n const result = annotateFile(source, cleanId, (info) => {\n const userProps = computeAnnotatedProps(info, filepath, modifiedtsMs);\n\n // Intrinsic HTML elements always support style and className — no annotation needed\n if (isIntrinsicElement(info.tagName)) {\n return userProps;\n }\n\n // Custom components: look up type support by position, default to false\n const support = typeInfo?.get(info.element.openingElement.start);\n return {\n ...userProps,\n \"data-tempo-supports-style\": support?.supportsStyle ?? false,\n \"data-tempo-supports-classname\": support?.supportsClassName ?? false,\n };\n });\n\n if (result.elementsAnnotated === 0) {\n return null;\n }\n\n return {\n code: result.annotatedSource,\n map: null,\n };\n } catch {\n return null;\n }\n },\n };\n}\n\nexport type { ElementInfo, PropValue };\n\nexport type { ElementTypeSupport, FileTypeInfo, TypeAnalyzer } from \"./type-analyzer.ts\";\nexport { createTypeAnalyzer } from \"./type-analyzer.ts\";\n\nexport type {\n TempoPage,\n StoryboardLayout,\n TempoStoryboard,\n TempoRouteStoryboard,\n} from \"./page.ts\";\n","import { parseSync, type JSXElement, type ParseResult } from 'oxc-parser';\nimport { getTagName } from '../file-parser/utils.ts';\nimport { walk } from '../file-parser/walk.ts';\n\nexport interface ElementInfo {\n element: JSXElement;\n /** e.g. \"div\", \"Button\", \"Foo.Bar\" */\n tagName: string;\n}\n\n/** Supported prop value types. `undefined` values are skipped. */\nexport type PropValue =\n | string\n | number\n | boolean\n | null\n | undefined\n | ((...args: unknown[]) => unknown)\n | object;\n\nfunction formatPropValue(value: PropValue): string | null {\n if (value === undefined) return null;\n if (typeof value === 'function') return value.toString();\n return JSON.stringify(value);\n}\n\nfunction formatProps(props: Record<string, PropValue>): string {\n return Object.entries(props)\n .map(([key, value]) => {\n const formatted = formatPropValue(value);\n if (formatted === null) return null;\n return ` ${key}={${formatted}}`;\n })\n .filter((s): s is string => s !== null)\n .join('');\n}\n\nexport interface AnnotateFileResult {\n annotatedSource: string;\n elementsAnnotated: number;\n /** The oxc-parser result. Check `parseResult.errors` for any parse errors. */\n parseResult: ParseResult;\n}\n\n/** Tag names that represent React fragments and should not be annotated */\nconst FRAGMENT_TAG_NAMES = new Set(['Fragment', 'React.Fragment']);\n\n/**\n * Annotates JSX elements in a source file by inserting additional props.\n *\n * Visits every JSXElement in source order and calls the handler to compute\n * props to insert. Props are inserted at the end of each opening tag.\n *\n * Fragments are skipped since they don't render DOM elements:\n * - JSX shorthand fragments (`<>...</>`) are a separate AST node type\n * - `React.Fragment` and `Fragment` are skipped by tag name\n */\nexport function annotateFile(\n source: string,\n filename: string,\n computeAnnotatedProps: (info: ElementInfo) => Record<string, PropValue>\n): AnnotateFileResult {\n const parseResult = parseSync(filename, source);\n const insertions: { position: number; text: string }[] = [];\n\n // Walk the AST to find all JSXElement nodes\n // Uses our own walker instead of oxc-parser's Visitor (CJS/ESM interop issue in Electron)\n walk(parseResult.program, {\n JSXElement(node: unknown) {\n const element = node as JSXElement;\n const tagName = getTagName(element.openingElement.name);\n if (FRAGMENT_TAG_NAMES.has(tagName)) return;\n\n const props = computeAnnotatedProps({ element, tagName });\n if (Object.keys(props).length === 0) return;\n\n const openingElement = element.openingElement;\n\n // Insert before \">\" or \"/>\" at end of opening tag, skipping whitespace before \"/>\"\n let insertPos = openingElement.end - 1;\n if (openingElement.selfClosing) {\n insertPos--;\n while (insertPos > openingElement.start && /\\s/.test(source[insertPos - 1]!)) {\n insertPos--;\n }\n }\n\n insertions.push({ position: insertPos, text: formatProps(props) });\n },\n });\n\n // Build result in a single pass using chunks (O(n) instead of O(n*m))\n insertions.sort((a, b) => a.position - b.position);\n\n const chunks: string[] = [];\n let lastPos = 0;\n for (const { position, text } of insertions) {\n chunks.push(source.slice(lastPos, position));\n chunks.push(text);\n lastPos = position;\n }\n chunks.push(source.slice(lastPos));\n\n return { annotatedSource: chunks.join(''), elementsAnnotated: insertions.length, parseResult };\n}\n","\n/**\n * Annotation Utilities\n *\n * Helper functions for working with oxc-parser AST nodes.\n */\n\nimport type { JSXElementName, JSXMemberExpression } from 'oxc-parser';\n\n/**\n * Extracts the tag name string from any JSXElementName variant.\n *\n * JSXElementName is a union of three types representing different JSX tag syntaxes:\n * - JSXIdentifier: `<button>` → \"button\", `<Button>` → \"Button\"\n * - JSXNamespacedName: `<svg:rect>` → \"svg:rect\" (rare, used in SVG/XML)\n * - JSXMemberExpression: `<Foo.Bar>` → \"Foo.Bar\", `<UI.Button.Primary>` → \"UI.Button.Primary\"\n */\nexport function getTagName(name: JSXElementName): string {\n switch (name.type) {\n case 'JSXIdentifier':\n return name.name;\n case 'JSXNamespacedName':\n return `${name.namespace.name}:${name.name.name}`;\n case 'JSXMemberExpression':\n return getJSXMemberExpressionName(name);\n }\n}\n\n/**\n * Recursively builds the dotted name string from a JSXMemberExpression.\n *\n * JSXMemberExpression represents dotted component names like `<Foo.Bar.Baz>`:\n * - object: either a JSXIdentifier or another JSXMemberExpression (for chaining)\n * - property: always a JSXIdentifier (the rightmost part)\n */\nexport function getJSXMemberExpressionName(node: JSXMemberExpression): string {\n const object = node.object.type === 'JSXIdentifier' ? node.object.name : getJSXMemberExpressionName(node.object);\n return `${object}.${node.property.name}`;\n}\n","/**\n * AST Walker\n *\n * Simple recursive walker for oxc-parser ASTs. Visits every node in the tree\n * and calls type-specific callbacks. Drop-in replacement for oxc-parser's\n * Visitor class, which has CJS/ESM interop issues in Electron.\n *\n * Once oxc-parser's Visitor works in Electron, this can be swapped back.\n */\n\n/**\n * Callbacks for specific AST node types.\n * Add more node types here as needed.\n */\nexport interface WalkCallbacks {\n JSXElement?: (node: any) => void;\n}\n\n/**\n * Recursively walks an oxc-parser AST, calling callbacks for matching node types.\n * Traverses all object properties and array elements depth-first.\n */\nexport function walk(node: unknown, callbacks: WalkCallbacks): void {\n if (node === null || node === undefined || typeof node !== \"object\") return;\n\n if (Array.isArray(node)) {\n for (const child of node) {\n walk(child, callbacks);\n }\n return;\n }\n\n const record = node as Record<string, unknown>;\n const type = record.type;\n\n if (typeof type === \"string\" && type in callbacks) {\n (callbacks as Record<string, (node: unknown) => void>)[type]!(node);\n }\n\n for (const value of Object.values(record)) {\n if (value !== null && typeof value === \"object\") {\n walk(value, callbacks);\n }\n }\n}\n","/**\n * TypeScript type analyzer for JSX element prop support.\n *\n * Uses a TypeScript Language Service to determine whether each custom React\n * component supports `style` (CSSProperties-compatible) and `className`\n * (string-compatible) props. Correlation between OXC and TypeScript ASTs is\n * position-based — both use UTF-16 string indices, so element positions can\n * be compared directly.\n *\n * Intrinsic HTML elements are excluded from the analysis entirely — the\n * consumer already knows they support both from the HTML standard.\n */\nimport ts from \"typescript\";\nimport path from \"node:path\";\nimport { realpathSync } from \"node:fs\";\nimport {\n createTsWorkspaceService,\n type TsProjectDescriptor,\n type TsWorkspaceService,\n} from \"@modules/typescript\";\n\nexport interface ElementTypeSupport {\n supportsStyle: boolean;\n supportsClassName: boolean;\n}\n\n/** Position → type support for custom components only */\nexport type FileTypeInfo = Map<number, ElementTypeSupport>;\n\nexport interface TypeAnalyzer {\n analyzeFileTypes(filePath: string, source: string): FileTypeInfo | null;\n}\n\n/** Custom component if first character is uppercase or it's a member expression */\nfunction isCustomComponent(node: ts.JsxTagNameExpression): boolean {\n const text = node.getText();\n return text.length > 0 && text[0] !== text[0]!.toLowerCase();\n}\n\ninterface TempobookTsMetadata {\n managedTsconfigPaths?: unknown;\n}\n\nfunction canonicalPath(value: string): string {\n const resolved = path.resolve(value);\n try {\n return realpathSync.native(resolved);\n } catch {\n return resolved;\n }\n}\n\nfunction parseManagedTsconfigPaths(rawConfig: unknown): string[] {\n if (!rawConfig || typeof rawConfig !== \"object\" || Array.isArray(rawConfig)) {\n return [];\n }\n\n const metadata = (rawConfig as { tempobook?: TempobookTsMetadata }).tempobook;\n if (!metadata || typeof metadata !== \"object\" || Array.isArray(metadata)) {\n return [];\n }\n\n const managedPaths = metadata.managedTsconfigPaths;\n if (!Array.isArray(managedPaths)) {\n return [];\n }\n\n return managedPaths.filter((entry): entry is string => typeof entry === \"string\" && entry.trim().length > 0);\n}\n\nfunction parseProjectReferenceTsconfigPaths(\n references: readonly ts.ProjectReference[] | undefined,\n configDir: string,\n): string[] {\n if (!references || references.length === 0) {\n return [];\n }\n\n const entries: string[] = [];\n for (const reference of references) {\n const referencePath = reference.path;\n if (!referencePath) {\n continue;\n }\n\n const resolvedPath = path.resolve(configDir, referencePath);\n const tsconfigPath = resolvedPath.endsWith(\".json\")\n ? resolvedPath\n : path.join(resolvedPath, \"tsconfig.json\");\n\n entries.push(tsconfigPath);\n }\n\n return entries;\n}\n\nfunction buildWorkspaceProjects(\n root: string,\n configPath: string,\n parsedConfig: ts.ParsedCommandLine,\n rawConfig: unknown,\n): TsProjectDescriptor[] {\n const configDir = path.dirname(configPath);\n const projects: TsProjectDescriptor[] = [\n {\n id: \"root\",\n rootDir: root,\n tsconfigPath: configPath,\n priority: 0,\n },\n ];\n\n const seenTsconfigPaths = new Set<string>([canonicalPath(configPath)]);\n const referencedTsconfigPaths = parseProjectReferenceTsconfigPaths(parsedConfig.projectReferences, configDir);\n const managedTsconfigPaths = parseManagedTsconfigPaths(rawConfig).map((entry) =>\n path.resolve(configDir, entry),\n );\n\n const candidateTsconfigPaths = [...managedTsconfigPaths, ...referencedTsconfigPaths];\n for (const candidatePath of candidateTsconfigPaths) {\n const normalizedPath = canonicalPath(candidatePath);\n if (seenTsconfigPaths.has(normalizedPath)) {\n continue;\n }\n if (!ts.sys.fileExists(normalizedPath)) {\n continue;\n }\n\n seenTsconfigPaths.add(normalizedPath);\n projects.push({\n id: `external-${projects.length}`,\n rootDir: path.dirname(normalizedPath),\n tsconfigPath: normalizedPath,\n priority: 0,\n });\n }\n\n return projects;\n}\n\n/**\n * Create a type analyzer backed by a TypeScript Language Service.\n *\n * The Language Service provides incremental updates — when a file changes,\n * we bump its version and provide the new content. The service lazily\n * recomputes only what's needed.\n *\n * Returns null if no tsconfig.json is found.\n */\nexport function createTypeAnalyzer(root: string): TypeAnalyzer | null {\n const configPath = ts.findConfigFile(root, ts.sys.fileExists, \"tsconfig.json\");\n if (!configPath) return null;\n\n const configFile = ts.readConfigFile(configPath, ts.sys.readFile);\n if (configFile.error) return null;\n\n const parsedConfig = ts.parseJsonConfigFileContent(\n configFile.config,\n ts.sys,\n root,\n );\n\n const workspaceProjects = buildWorkspaceProjects(root, configPath, parsedConfig, configFile.config);\n const workspaceProjectRoots = workspaceProjects.map((project) => canonicalPath(project.rootDir));\n\n const workspaceService: TsWorkspaceService = createTsWorkspaceService({\n workspaceRoot: root,\n primaryProjectId: \"root\",\n projects: workspaceProjects,\n });\n\n // Lazily resolved CSSProperties type for style compatibility checks.\n // Resolved from JSX.IntrinsicElements['div'].style, with the undefined\n // union member stripped to get the pure CSSProperties type.\n let cssPropertiesType: ts.Type | null | undefined;\n\n function resolveCSSPropertiesType(\n checker: ts.TypeChecker,\n sourceFile: ts.SourceFile,\n ): ts.Type | null {\n if (cssPropertiesType !== undefined) return cssPropertiesType;\n\n try {\n // JSX namespace → IntrinsicElements → div → style → unwrap undefined\n const jsxNs = (checker as any).resolveName(\n \"JSX\", sourceFile, ts.SymbolFlags.Namespace, false,\n );\n if (!jsxNs) { cssPropertiesType = null; return null; }\n\n const intrinsicSym = checker.getExportsOfModule(jsxNs)\n .find((s) => s.name === \"IntrinsicElements\");\n if (!intrinsicSym) { cssPropertiesType = null; return null; }\n\n const divSym = checker.getDeclaredTypeOfSymbol(intrinsicSym)\n .getProperty(\"div\");\n if (!divSym) { cssPropertiesType = null; return null; }\n\n const styleSym = checker.getTypeOfSymbol(divSym).getProperty(\"style\");\n if (!styleSym) { cssPropertiesType = null; return null; }\n\n const styleType = checker.getTypeOfSymbol(styleSym);\n\n // styleType is CSSProperties | undefined — extract the non-undefined member\n if (styleType.isUnion()) {\n const nonUndefined = styleType.types.find(\n (t) => !(t.flags & ts.TypeFlags.Undefined),\n );\n cssPropertiesType = nonUndefined ?? null;\n } else {\n cssPropertiesType = styleType;\n }\n } catch {\n cssPropertiesType = null;\n }\n\n return cssPropertiesType;\n }\n\n return {\n analyzeFileTypes(filePath: string, source: string): FileTypeInfo | null {\n const canonicalFilePath = canonicalPath(filePath);\n const belongsToWorkspace = workspaceProjectRoots.some(\n (projectRoot) =>\n canonicalFilePath === projectRoot ||\n canonicalFilePath.startsWith(`${projectRoot}${path.sep}`),\n );\n if (!belongsToWorkspace) {\n return null;\n }\n\n // Update the workspace service with current in-memory source.\n workspaceService.updateFile(filePath, source);\n\n const program = workspaceService.getProgram();\n if (!program) return null;\n\n const sourceFile = program.getSourceFile(filePath);\n if (!sourceFile) return null;\n\n const checker = program.getTypeChecker();\n const cssProps = resolveCSSPropertiesType(checker, sourceFile);\n const result: FileTypeInfo = new Map();\n\n function visit(node: ts.Node) {\n if (\n (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) &&\n isCustomComponent(node.tagName)\n ) {\n const position = node.getStart(sourceFile);\n const attrsType = checker.getContextualType(node.attributes);\n\n let supportsStyle = false;\n let supportsClassName = false;\n\n if (attrsType) {\n // className: check that `string` is assignable to the prop's type\n const classNameProp = attrsType.getProperty(\"className\");\n if (classNameProp) {\n const propType = checker.getTypeOfSymbol(classNameProp);\n supportsClassName = (checker as any).isTypeAssignableTo(\n checker.getStringType(), propType,\n );\n }\n\n // style: check that CSSProperties is assignable to the prop's type\n const styleProp = attrsType.getProperty(\"style\");\n if (styleProp && cssProps) {\n const propType = checker.getTypeOfSymbol(styleProp);\n supportsStyle = (checker as any).isTypeAssignableTo(\n cssProps, propType,\n );\n }\n }\n\n result.set(position, { supportsStyle, supportsClassName });\n }\n\n ts.forEachChild(node, visit);\n }\n\n visit(sourceFile);\n return result;\n },\n };\n}\n","import { realpathSync, statSync } from \"node:fs\";\nimport path from \"node:path\";\n\nimport ts from \"typescript\";\n\nexport interface TsProjectDescriptor {\n id: string;\n rootDir: string;\n tsconfigPath: string;\n priority?: number;\n}\n\nexport interface TsWorkspaceDescriptor {\n workspaceRoot: string;\n projects: TsProjectDescriptor[];\n primaryProjectId?: string;\n}\n\nexport interface TsWorkspaceService {\n updateFile(filePath: string, content: string): void;\n closeFile(filePath: string): void;\n getProgram(): ts.Program | null;\n getSemanticDiagnostics(filePath: string): readonly ts.Diagnostic[];\n getDefinition(filePath: string, position: number): readonly ts.DefinitionInfo[] | undefined;\n dispose(): void;\n}\n\ninterface LoadedProject {\n id: string;\n rootDir: string;\n tsconfigPath: string;\n priority: number;\n options: ts.CompilerOptions;\n fileNames: string[];\n moduleResolutionCache: ts.ModuleResolutionCache;\n}\n\ninterface InMemoryFile {\n version: number;\n content: string;\n}\n\nfunction canonicalPath(input: string): string {\n const resolved = path.resolve(input);\n try {\n return realpathSync.native(resolved);\n } catch {\n return resolved;\n }\n}\n\nfunction compareProjectPriority(left: LoadedProject, right: LoadedProject): number {\n const depthDelta = right.rootDir.length - left.rootDir.length;\n if (depthDelta !== 0) {\n return depthDelta;\n }\n\n const priorityDelta = right.priority - left.priority;\n if (priorityDelta !== 0) {\n return priorityDelta;\n }\n\n return left.id.localeCompare(right.id);\n}\n\nfunction isWithinRoot(filePath: string, rootDir: string): boolean {\n return filePath === rootDir || filePath.startsWith(`${rootDir}${path.sep}`);\n}\n\nfunction loadProject(descriptor: TsProjectDescriptor): LoadedProject {\n const id = descriptor.id.trim();\n if (!id) {\n throw new Error(\"Project id must be a non-empty string\");\n }\n\n const tsconfigPath = canonicalPath(descriptor.tsconfigPath);\n const rootDir = canonicalPath(descriptor.rootDir);\n\n const readResult = ts.readConfigFile(tsconfigPath, ts.sys.readFile);\n if (readResult.error) {\n throw new Error(\n `Failed to read tsconfig at ${tsconfigPath}: ${ts.flattenDiagnosticMessageText(\n readResult.error.messageText,\n \"\\n\",\n )}`,\n );\n }\n\n const parsed = ts.parseJsonConfigFileContent(\n readResult.config,\n ts.sys,\n path.dirname(tsconfigPath),\n );\n\n if (parsed.errors.length > 0) {\n const message = parsed.errors\n .map((diagnostic) => ts.flattenDiagnosticMessageText(diagnostic.messageText, \"\\n\"))\n .join(\"; \");\n throw new Error(`Failed to parse tsconfig at ${tsconfigPath}: ${message}`);\n }\n\n const normalizedFiles = parsed.fileNames.map((fileName) => canonicalPath(fileName));\n return {\n id,\n rootDir,\n tsconfigPath,\n priority: descriptor.priority ?? 0,\n options: parsed.options,\n fileNames: normalizedFiles,\n moduleResolutionCache: ts.createModuleResolutionCache(\n rootDir,\n (value) => value,\n parsed.options,\n ),\n };\n}\n\nfunction getFileMtimeVersion(filePath: string): string {\n try {\n const stats = statSync(filePath);\n return String(stats.mtimeMs);\n } catch {\n return \"0\";\n }\n}\n\nexport function createTsWorkspaceService(\n descriptor: TsWorkspaceDescriptor,\n): TsWorkspaceService {\n if (descriptor.projects.length === 0) {\n throw new Error(\"TsWorkspaceDescriptor.projects must include at least one project\");\n }\n\n const workspaceRoot = canonicalPath(descriptor.workspaceRoot);\n const loadedProjects = descriptor.projects.map((project) => loadProject(project));\n\n const projectsById = new Map<string, LoadedProject>();\n for (const project of loadedProjects) {\n if (projectsById.has(project.id)) {\n throw new Error(`Duplicate project id: ${project.id}`);\n }\n projectsById.set(project.id, project);\n }\n\n const primaryProject =\n (descriptor.primaryProjectId ? projectsById.get(descriptor.primaryProjectId) : null) ??\n loadedProjects[0]!;\n if (descriptor.primaryProjectId && !projectsById.has(descriptor.primaryProjectId)) {\n throw new Error(`Primary project id not found: ${descriptor.primaryProjectId}`);\n }\n\n const orderedProjects = [...loadedProjects].sort(compareProjectPriority);\n const rootFileNames = new Set<string>();\n for (const project of loadedProjects) {\n for (const fileName of project.fileNames) {\n rootFileNames.add(fileName);\n }\n }\n\n const inMemoryFiles = new Map<string, InMemoryFile>();\n let projectVersion = 0;\n\n function getOwningProject(filePath: string): LoadedProject {\n const canonicalFilePath = canonicalPath(filePath);\n for (const project of orderedProjects) {\n if (isWithinRoot(canonicalFilePath, project.rootDir)) {\n return project;\n }\n }\n return primaryProject;\n }\n\n const moduleResolutionHost: ts.ModuleResolutionHost = {\n fileExists: ts.sys.fileExists,\n readFile: ts.sys.readFile,\n realpath: ts.sys.realpath,\n directoryExists: ts.sys.directoryExists,\n getCurrentDirectory: () => workspaceRoot,\n getDirectories: ts.sys.getDirectories,\n };\n\n const host: ts.LanguageServiceHost = {\n getScriptFileNames: () => Array.from(rootFileNames),\n getScriptVersion: (filePath) => {\n const canonicalFilePath = canonicalPath(filePath);\n const inMemory = inMemoryFiles.get(canonicalFilePath);\n if (inMemory) {\n return String(inMemory.version);\n }\n return getFileMtimeVersion(canonicalFilePath);\n },\n getScriptSnapshot: (filePath) => {\n const canonicalFilePath = canonicalPath(filePath);\n const inMemory = inMemoryFiles.get(canonicalFilePath);\n if (inMemory) {\n return ts.ScriptSnapshot.fromString(inMemory.content);\n }\n\n const diskContent = ts.sys.readFile(canonicalFilePath);\n if (diskContent === undefined) {\n return undefined;\n }\n return ts.ScriptSnapshot.fromString(diskContent);\n },\n getCurrentDirectory: () => workspaceRoot,\n getCompilationSettings: () => primaryProject.options,\n getDefaultLibFileName: (options) => ts.getDefaultLibFilePath(options),\n fileExists: ts.sys.fileExists,\n readFile: ts.sys.readFile,\n readDirectory: ts.sys.readDirectory,\n directoryExists: ts.sys.directoryExists,\n getDirectories: ts.sys.getDirectories,\n useCaseSensitiveFileNames: () => ts.sys.useCaseSensitiveFileNames,\n getProjectVersion: () => String(projectVersion),\n resolveModuleNames: (moduleNames, containingFile) => {\n const owningProject = getOwningProject(containingFile);\n\n return moduleNames.map((moduleName) => {\n const ownerResolution = ts.resolveModuleName(\n moduleName,\n containingFile,\n owningProject.options,\n moduleResolutionHost,\n owningProject.moduleResolutionCache,\n ).resolvedModule;\n\n if (ownerResolution) {\n return ownerResolution;\n }\n\n if (owningProject.id === primaryProject.id) {\n return undefined;\n }\n\n return ts.resolveModuleName(\n moduleName,\n containingFile,\n primaryProject.options,\n moduleResolutionHost,\n primaryProject.moduleResolutionCache,\n ).resolvedModule;\n });\n },\n };\n\n const languageService = ts.createLanguageService(host, ts.createDocumentRegistry());\n\n return {\n updateFile(filePath: string, content: string): void {\n const canonicalFilePath = canonicalPath(filePath);\n const currentVersion = inMemoryFiles.get(canonicalFilePath)?.version ?? 0;\n inMemoryFiles.set(canonicalFilePath, {\n version: currentVersion + 1,\n content,\n });\n\n if (!rootFileNames.has(canonicalFilePath)) {\n rootFileNames.add(canonicalFilePath);\n }\n\n projectVersion += 1;\n },\n closeFile(filePath: string): void {\n const canonicalFilePath = canonicalPath(filePath);\n inMemoryFiles.delete(canonicalFilePath);\n projectVersion += 1;\n },\n getProgram(): ts.Program | null {\n return languageService.getProgram() ?? null;\n },\n getSemanticDiagnostics(filePath: string): readonly ts.Diagnostic[] {\n const canonicalFilePath = canonicalPath(filePath);\n return languageService.getSemanticDiagnostics(canonicalFilePath);\n },\n getDefinition(\n filePath: string,\n position: number,\n ): readonly ts.DefinitionInfo[] | undefined {\n const canonicalFilePath = canonicalPath(filePath);\n return languageService.getDefinitionAtPosition(canonicalFilePath, position);\n },\n dispose(): void {\n languageService.dispose();\n },\n };\n}\n\n"],"mappings":"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","names":["realpathSync","statSync","ts","path","realpathSync","canonicalPath","path","realpathSync","ts","realpathSync","statSync"]}
|
|
1
|
+
{"version":3,"sources":["../index.ts","../../../modules/annotation/annotate-file.ts","../../../modules/file-parser/utils.ts","../../../modules/file-parser/walk.ts","../../../modules/file-parser/scope-analyzer/scopeAnalyzer.ts","../type-analyzer.ts","../../../modules/typescript/workspace-service.ts"],"sourcesContent":["import { realpathSync, statSync } from \"node:fs\";\nimport { relative } from \"node:path\";\n\nimport {\n annotateFile,\n type ElementInfo,\n type PropValue,\n} from \"../../modules/annotation/annotate-file.ts\";\nimport { createFilter, type FilterPattern } from \"@rollup/pluginutils\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport type { Plugin, ResolvedConfig, ViteDevServer } from \"vite\";\nimport { createTypeAnalyzer, type TypeAnalyzer } from \"./type-analyzer.ts\";\n\nconst DEFAULT_INCLUDE: FilterPattern = [\"**/*.tsx\", \"**/*.jsx\"];\nconst DEFAULT_EXCLUDE: FilterPattern = [\"**/node_modules/**\"];\nconst ROOT_META_NAME = \"tempo-project-root\";\n\nfunction cleanModuleId(id: string): string {\n return id.replace(/[?#].*$/, \"\");\n}\n\nfunction resolveCanonicalRoot(root: string): string {\n try {\n return realpathSync.native(root);\n } catch {\n return root;\n }\n}\n\nfunction hasRootMetaTag(html: string): boolean {\n return /<meta[^>]+name=[\"']tempo-project-root[\"'][^>]*>/i.test(html);\n}\n\nfunction isIntrinsicElement(tagName: string): boolean {\n return tagName.length > 0 && tagName[0] === tagName[0]!.toLowerCase();\n}\n\nfunction defaultComputeAnnotatedProps(\n info: ElementInfo,\n filepath: string,\n modifiedtsMs: number,\n): Record<string, PropValue> {\n return {\n \"data-tempo-filepath\": filepath,\n \"data-tempo-position\": info.element.openingElement.start,\n \"data-tempo-modifiedts\": modifiedtsMs,\n };\n}\n\n/** Error payload captured from Vite's HMR error events or console interception. */\nexport type TempoViteError = {\n type: \"compile\" | \"evaluation\";\n message: string;\n stack?: string;\n file?: string;\n frame?: string;\n plugin?: string;\n loc?: { file?: string; line?: number; column?: number };\n};\n\nexport type ComputeAnnotatedProps = (\n info: ElementInfo,\n filepath: string,\n modifiedtsMs: number,\n) => Record<string, PropValue>;\n\nexport interface TempoAnnotateOptions {\n computeAnnotatedProps?: ComputeAnnotatedProps;\n include?: FilterPattern;\n exclude?: FilterPattern;\n}\n\n// ---------------------------------------------------------------------------\n// Error tracking server — tracks per-module errors and broadcasts the\n// highest-priority error (or null) to SSE clients.\n//\n// Vite's HMR operates per-module: a successful update for module A should not\n// clear an unrelated compile error in module B. We key errors by the file\n// that caused them so that clears are scoped to the module that was fixed.\n// ---------------------------------------------------------------------------\n\ntype SseClient = ServerResponse<IncomingMessage>;\n\nexport interface TempoErrorServer {\n /** The error currently broadcast to SSE clients (most recent across all modules). */\n readonly currentError: TempoViteError | null;\n clients: Set<SseClient>;\n setError(error: TempoViteError): void;\n /** Clear errors for modules present in the HMR update. */\n clearErrorForModules(updatedPaths: string[]): void;\n /** Clear all errors unconditionally (e.g. report-clear endpoint). */\n clearAll(): void;\n}\n\nfunction createErrorServer(): TempoErrorServer {\n // Per-module error map keyed by file path (or \"unknown\" for errors without a file)\n const errorsByFile = new Map<string, TempoViteError>();\n\n function getMostRecentError(): TempoViteError | null {\n if (errorsByFile.size === 0) return null;\n // Map iterates in insertion order; setError does delete+set to push to end,\n // so the last entry is the most recently set error\n let latest: TempoViteError | null = null;\n for (const err of errorsByFile.values()) latest = err;\n return latest;\n }\n\n function broadcastCurrentState(): void {\n const data = JSON.stringify(server.currentError);\n for (const client of server.clients) {\n client.write(`data: ${data}\\n\\n`);\n }\n }\n\n const server: TempoErrorServer = {\n get currentError(): TempoViteError | null {\n return getMostRecentError();\n },\n clients: new Set(),\n\n setError(error: TempoViteError) {\n const key = error.file ?? error.loc?.file ?? \"unknown\";\n // Delete + set to move to end of Map iteration order (most recent)\n errorsByFile.delete(key);\n errorsByFile.set(key, error);\n broadcastCurrentState();\n },\n\n clearErrorForModules(updatedPaths: string[]) {\n if (errorsByFile.size === 0) return;\n let changed = false;\n for (const errorKey of [...errorsByFile.keys()]) {\n // An error is cleared when the module that caused it appears in the\n // HMR update's path list. We check with endsWith because Vite update\n // paths are root-relative while error file paths may be absolute.\n if (\n errorKey === \"unknown\" ||\n updatedPaths.some(\n (p) => errorKey.endsWith(p) || p.endsWith(errorKey),\n )\n ) {\n errorsByFile.delete(errorKey);\n changed = true;\n }\n }\n if (changed) broadcastCurrentState();\n },\n\n clearAll() {\n // Broadcast null even if already empty — runtime evaluation errors\n // (caught by the frame preload's console.error interception) are\n // forwarded as diagnostics without going through the SSE pipeline.\n // Broadcasting null ensures the frame preload receives a clear signal.\n errorsByFile.clear();\n broadcastCurrentState();\n },\n };\n return server;\n}\n\nfunction readBody(req: IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n req.on(\"end\", () => resolve(Buffer.concat(chunks).toString()));\n req.on(\"error\", reject);\n });\n}\n\nfunction handleErrorMiddleware(\n errorServer: TempoErrorServer,\n req: IncomingMessage,\n res: ServerResponse,\n next: () => void,\n) {\n const url = req.url;\n if (!url?.startsWith(\"/__tempo/\")) return next();\n\n if (url === \"/__tempo/report-error\" && req.method === \"POST\") {\n readBody(req).then((body) => {\n try {\n errorServer.setError(JSON.parse(body) as TempoViteError);\n } catch {\n // ignore malformed JSON\n }\n res.statusCode = 200;\n res.end(\"ok\");\n });\n return;\n }\n\n if (url === \"/__tempo/report-clear\" && req.method === \"POST\") {\n errorServer.clearAll();\n res.statusCode = 200;\n res.end(\"ok\");\n return;\n }\n\n // Allow CORS preflight for SSE endpoint (browser fetches from a different port)\n if (url === \"/__tempo/errors\" && req.method === \"OPTIONS\") {\n res.writeHead(204, {\n \"Access-Control-Allow-Origin\": \"*\",\n \"Access-Control-Allow-Methods\": \"GET, OPTIONS\",\n \"Access-Control-Allow-Headers\": \"*\",\n });\n res.end();\n return;\n }\n\n if (url === \"/__tempo/errors\" && req.method === \"GET\") {\n res.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"Access-Control-Allow-Origin\": \"*\",\n });\n\n // Send current state immediately\n res.write(`data: ${JSON.stringify(errorServer.currentError)}\\n\\n`);\n errorServer.clients.add(res);\n\n req.on(\"close\", () => {\n errorServer.clients.delete(res);\n });\n return;\n }\n\n next();\n}\n\n\nexport function tempoVitePlugin(options: TempoAnnotateOptions = {}): Plugin {\n // The plugin is a complete no-op unless the TEMPO env var is set.\n // This lets users keep tempoVitePlugin() in their vite config permanently\n // while only paying for annotation when running via Tempo's start script\n // (which sets TEMPO=true).\n const enabled = !!process.env.TEMPO;\n\n const include =\n options.include === undefined ? DEFAULT_INCLUDE : options.include;\n const exclude =\n options.exclude === undefined ? DEFAULT_EXCLUDE : options.exclude;\n const filter = createFilter(include, exclude);\n\n const computeAnnotatedProps =\n options.computeAnnotatedProps ?? defaultComputeAnnotatedProps;\n\n let canonicalRoot = process.cwd();\n let command: ResolvedConfig[\"command\"] = \"serve\";\n let typeAnalyzer: TypeAnalyzer | null = null;\n const errorServer = createErrorServer();\n\n return {\n name: \"tempo-annotate\",\n enforce: \"pre\",\n\n // Expose error server for testing\n _tempoErrorServer: errorServer,\n\n configResolved(resolvedConfig) {\n if (!enabled) return;\n\n command = resolvedConfig.command;\n canonicalRoot = resolveCanonicalRoot(resolvedConfig.root);\n\n if (command === \"serve\") {\n typeAnalyzer = createTypeAnalyzer(canonicalRoot);\n }\n },\n\n configureServer(server: ViteDevServer) {\n if (!enabled) return;\n server.middlewares.use((req, res, next) => {\n handleErrorMiddleware(errorServer, req, res, next);\n });\n\n\n // Intercept outgoing HMR messages to capture errors and clears\n // server-side, so we don't depend on client-side script execution\n // (which fails in contexts where import.meta.hot is unavailable,\n // e.g. Electron WebContentsViews).\n //\n // In Vite 7, error middleware calls environment.hot.send() which may\n // be a different wrapper than server.ws. Intercept both to ensure\n // errors are captured regardless of which path Vite uses.\n const interceptHmrSend = (target: { send: Function }) => {\n const orig = target.send.bind(target);\n target.send = ((...args: unknown[]) => {\n const payload = args[0];\n if (typeof payload === \"object\" && payload !== null && \"type\" in payload) {\n const typed = payload as { type: string; err?: Record<string, unknown> };\n if (typed.type === \"error\") {\n const err = typed.err;\n errorServer.setError({\n type: \"compile\",\n message: (err?.message as string) ?? \"Unknown error\",\n stack: err?.stack as string | undefined,\n file: (err?.id as string) ?? (err?.loc as { file?: string })?.file,\n frame: err?.frame as string | undefined,\n plugin: err?.plugin as string | undefined,\n loc: err?.loc as\n | { file?: string; line?: number; column?: number }\n | undefined,\n });\n } else if (typed.type === \"update\") {\n const updates = (typed as { updates?: Array<{ path?: string }> }).updates;\n const paths = updates\n ?.map((u) => u.path)\n .filter((p): p is string => typeof p === \"string\") ?? [];\n errorServer.clearErrorForModules(paths);\n }\n }\n return (orig as Function)(...args);\n }) as typeof orig;\n };\n\n interceptHmrSend(server.ws as { send: Function });\n const clientHot = (server as any).environments?.client?.hot;\n if (clientHot && clientHot !== server.ws) {\n interceptHmrSend(clientHot as { send: Function });\n }\n },\n\n transformIndexHtml(html) {\n if (!enabled || command !== \"serve\") {\n return;\n }\n\n if (hasRootMetaTag(html)) {\n return;\n }\n\n return [\n {\n tag: \"meta\",\n attrs: {\n name: ROOT_META_NAME,\n content: canonicalRoot,\n },\n injectTo: \"head\" as const,\n },\n ];\n },\n\n transform(source, id) {\n if (!enabled || command === \"build\") {\n return null;\n }\n\n if (id.startsWith(\"\\0\")) {\n return null;\n }\n\n const cleanId = cleanModuleId(id);\n if (!filter(cleanId)) {\n return null;\n }\n\n try {\n const filepath = relative(canonicalRoot, cleanId);\n const modifiedtsMs = Math.trunc(statSync(cleanId).mtimeMs);\n\n const typeInfo = typeAnalyzer?.analyzeFileTypes(cleanId, source);\n\n const result = annotateFile(source, cleanId, (info) => {\n const userProps = computeAnnotatedProps(info, filepath, modifiedtsMs);\n\n // Intrinsic HTML elements always support style and className — no annotation needed\n if (isIntrinsicElement(info.tagName)) {\n return userProps;\n }\n\n // Custom components: look up type support by position, default to false\n const support = typeInfo?.get(info.element.openingElement.start);\n return {\n ...userProps,\n \"data-tempo-supports-style\": support?.supportsStyle ?? false,\n \"data-tempo-supports-classname\":\n support?.supportsClassName ?? false,\n };\n });\n\n if (result.elementsAnnotated === 0) {\n return null;\n }\n\n return {\n code: result.annotatedSource,\n map: null,\n };\n } catch {\n return null;\n }\n },\n } as Plugin & { _tempoErrorServer: TempoErrorServer };\n}\n\n/** @deprecated Use `tempoVitePlugin` instead. */\nexport const tempoAnnotate = tempoVitePlugin;\n\nexport type { ElementInfo, PropValue };\n\nexport type {\n ElementTypeSupport,\n FileTypeInfo,\n TypeAnalyzer,\n} from \"./type-analyzer.ts\";\nexport { createTypeAnalyzer } from \"./type-analyzer.ts\";\n\nexport type {\n TempoPage,\n StoryboardLayout,\n TempoStoryboard,\n TempoRouteStoryboard,\n} from \"./page.ts\";\n","import { parseSync, type JSXElement, type ParseResult } from 'oxc-parser';\nimport { getTagName, walk } from '@modules/file-parser';\n\nexport interface ElementInfo {\n element: JSXElement;\n /** e.g. \"div\", \"Button\", \"Foo.Bar\" */\n tagName: string;\n}\n\n/** Supported prop value types. `undefined` values are skipped. */\nexport type PropValue =\n | string\n | number\n | boolean\n | null\n | undefined\n | ((...args: unknown[]) => unknown)\n | object;\n\nfunction formatPropValue(value: PropValue): string | null {\n if (value === undefined) return null;\n if (typeof value === 'function') return value.toString();\n return JSON.stringify(value);\n}\n\nfunction formatProps(props: Record<string, PropValue>): string {\n return Object.entries(props)\n .map(([key, value]) => {\n const formatted = formatPropValue(value);\n if (formatted === null) return null;\n return ` ${key}={${formatted}}`;\n })\n .filter((s): s is string => s !== null)\n .join('');\n}\n\nexport interface AnnotateFileResult {\n annotatedSource: string;\n elementsAnnotated: number;\n /** The oxc-parser result. Check `parseResult.errors` for any parse errors. */\n parseResult: ParseResult;\n}\n\n/** Tag names that represent React fragments and should not be annotated */\nconst FRAGMENT_TAG_NAMES = new Set(['Fragment', 'React.Fragment']);\n\n/**\n * Annotates JSX elements in a source file by inserting additional props.\n *\n * Visits every JSXElement in source order and calls the handler to compute\n * props to insert. Props are inserted at the end of each opening tag.\n *\n * Fragments are skipped since they don't render DOM elements:\n * - JSX shorthand fragments (`<>...</>`) are a separate AST node type\n * - `React.Fragment` and `Fragment` are skipped by tag name\n */\nexport function annotateFile(\n source: string,\n filename: string,\n computeAnnotatedProps: (info: ElementInfo) => Record<string, PropValue>\n): AnnotateFileResult {\n const parseResult = parseSync(filename, source);\n const insertions: { position: number; text: string }[] = [];\n\n // Walk the AST to find all JSXElement nodes\n // Uses our own walker instead of oxc-parser's Visitor (CJS/ESM interop issue in Electron)\n walk(parseResult.program, {\n JSXElement(node: unknown) {\n const element = node as JSXElement;\n const tagName = getTagName(element.openingElement.name);\n if (FRAGMENT_TAG_NAMES.has(tagName)) return;\n\n const props = computeAnnotatedProps({ element, tagName });\n if (Object.keys(props).length === 0) return;\n\n const openingElement = element.openingElement;\n\n // Insert before \">\" or \"/>\" at end of opening tag, skipping whitespace before \"/>\"\n let insertPos = openingElement.end - 1;\n if (openingElement.selfClosing) {\n insertPos--;\n while (insertPos > openingElement.start && /\\s/.test(source[insertPos - 1]!)) {\n insertPos--;\n }\n }\n\n insertions.push({ position: insertPos, text: formatProps(props) });\n },\n });\n\n // Build result in a single pass using chunks (O(n) instead of O(n*m))\n insertions.sort((a, b) => a.position - b.position);\n\n const chunks: string[] = [];\n let lastPos = 0;\n for (const { position, text } of insertions) {\n chunks.push(source.slice(lastPos, position));\n chunks.push(text);\n lastPos = position;\n }\n chunks.push(source.slice(lastPos));\n\n return { annotatedSource: chunks.join(''), elementsAnnotated: insertions.length, parseResult };\n}\n","\n/**\n * Annotation Utilities\n *\n * Helper functions for working with oxc-parser AST nodes.\n */\n\nimport type { JSXElementName, JSXMemberExpression } from 'oxc-parser';\n\n/**\n * Extracts the tag name string from any JSXElementName variant.\n *\n * JSXElementName is a union of three types representing different JSX tag syntaxes:\n * - JSXIdentifier: `<button>` → \"button\", `<Button>` → \"Button\"\n * - JSXNamespacedName: `<svg:rect>` → \"svg:rect\" (rare, used in SVG/XML)\n * - JSXMemberExpression: `<Foo.Bar>` → \"Foo.Bar\", `<UI.Button.Primary>` → \"UI.Button.Primary\"\n */\nexport function getTagName(name: JSXElementName): string {\n switch (name.type) {\n case 'JSXIdentifier':\n return name.name;\n case 'JSXNamespacedName':\n return `${name.namespace.name}:${name.name.name}`;\n case 'JSXMemberExpression':\n return getJSXMemberExpressionName(name);\n }\n}\n\n/**\n * Recursively builds the dotted name string from a JSXMemberExpression.\n *\n * JSXMemberExpression represents dotted component names like `<Foo.Bar.Baz>`:\n * - object: either a JSXIdentifier or another JSXMemberExpression (for chaining)\n * - property: always a JSXIdentifier (the rightmost part)\n */\nexport function getJSXMemberExpressionName(node: JSXMemberExpression): string {\n const object = node.object.type === 'JSXIdentifier' ? node.object.name : getJSXMemberExpressionName(node.object);\n return `${object}.${node.property.name}`;\n}\n","/**\n * AST Walker\n *\n * Simple recursive walker for oxc-parser ASTs. Visits every node in the tree\n * and calls type-specific callbacks. Drop-in replacement for oxc-parser's\n * Visitor class, which has CJS/ESM interop issues in Electron.\n *\n * Once oxc-parser's Visitor works in Electron, this can be swapped back.\n */\n\n/**\n * Callbacks for specific AST node types.\n * Add more node types here as needed.\n */\nexport interface WalkCallbacks {\n JSXElement?: (node: any) => void;\n}\n\n/**\n * Recursively walks an oxc-parser AST, calling callbacks for matching node types.\n * Traverses all object properties and array elements depth-first.\n */\nexport function walk(node: unknown, callbacks: WalkCallbacks): void {\n if (node === null || node === undefined || typeof node !== \"object\") return;\n\n if (Array.isArray(node)) {\n for (const child of node) {\n walk(child, callbacks);\n }\n return;\n }\n\n const record = node as Record<string, unknown>;\n const type = record.type;\n\n if (typeof type === \"string\" && type in callbacks) {\n (callbacks as Record<string, (node: unknown) => void>)[type]!(node);\n }\n\n for (const value of Object.values(record)) {\n if (value !== null && typeof value === \"object\") {\n walk(value, callbacks);\n }\n }\n}\n","import { parseSync, visitorKeys, type ParserOptions } from 'oxc-parser';\nimport {\n BindingKind,\n type Binding,\n type Range,\n type Reference,\n type Scope,\n ScopeAnalysis,\n ScopeFlags,\n ScopeType,\n} from './types';\n\ntype AnyNode = {\n type: string;\n start?: number;\n end?: number;\n [key: string]: any;\n};\n\ntype AnalyzeOptions = {\n filename?: string;\n parserOptions?: ParserOptions;\n};\n\ntype ScopeStackItem = {\n scopeId: number;\n};\n\nenum ReferenceContext {\n Value = 'value',\n Type = 'type',\n ValueAsType = 'value-as-type',\n}\n\nconst DEFAULT_FILENAME = 'file.tsx';\n\nexport function analyzeScopes(source: string, options: AnalyzeOptions = {}): ScopeAnalysis {\n const filename = options.filename ?? DEFAULT_FILENAME;\n const parseResult = parseSync(filename, source, options.parserOptions ?? undefined);\n const program = parseResult.program as AnyNode;\n\n const scopes: Scope[] = [];\n const bindings: Binding[] = [];\n const references: Reference[] = [];\n\n const scopeIdByNode = new WeakMap<object, number>();\n\n const rootScopeId = createScope({\n scopes,\n type: ScopeType.Program,\n start: 0,\n end: source.length,\n parentId: null,\n flags: getProgramFlags(program),\n });\n\n scopeIdByNode.set(program, rootScopeId);\n\n const bindingBuilder = new ScopeBuilder({\n source,\n scopes,\n bindings,\n scopeIdByNode,\n });\n\n bindingBuilder.walkBindings(program);\n\n const referenceBuilder = new ReferenceBuilder({\n scopes,\n bindings,\n references,\n scopeIdByNode,\n });\n\n referenceBuilder.walkReferences(program);\n\n return {\n source,\n scopes,\n bindings,\n references,\n };\n}\n\nexport function getReferenceAtPosition(\n analysis: ScopeAnalysis,\n position: number,\n): Reference | null {\n let best: Reference | null = null;\n for (const ref of analysis.references) {\n if (position >= ref.start && position < ref.end) {\n if (!best || (ref.end - ref.start) < (best.end - best.start)) {\n best = ref;\n }\n }\n }\n return best;\n}\n\nexport function getBindingForReference(\n analysis: ScopeAnalysis,\n reference: Reference,\n): Binding | null {\n if (reference.bindingId == null) {\n return null;\n }\n return analysis.bindings[reference.bindingId] ?? null;\n}\n\nexport function getValidRangesAtPosition(\n analysis: ScopeAnalysis,\n position: number,\n): Range[] {\n const ref = getReferenceAtPosition(analysis, position);\n if (!ref) return [];\n return getValidRangesForReference(analysis, ref);\n}\n\nexport function getValidRangesForReference(\n analysis: ScopeAnalysis,\n reference: Reference,\n): Range[] {\n const binding = getBindingForReference(analysis, reference);\n if (!binding) return [];\n const baseRange = getBindingVisibilityRange(analysis, binding);\n const shadowed = getShadowedRanges(analysis, binding, baseRange);\n return subtractRanges(baseRange, shadowed);\n}\n\nexport function getBindingVisibilityRange(\n analysis: ScopeAnalysis,\n binding: Binding,\n): Range {\n const scope = analysis.scopes[binding.scopeId];\n const start = isBlockScopedBindingKind(binding.kind)\n ? binding.declStart\n : scope.start;\n return { start, end: scope.end };\n}\n\nfunction getShadowedRanges(\n analysis: ScopeAnalysis,\n binding: Binding,\n baseRange: Range,\n): Range[] {\n const rootScope = analysis.scopes[binding.scopeId];\n const ranges: Range[] = [];\n const stack = [...rootScope.childIds];\n const useTypeBindings = usesTypeBindingsForShadowing(binding.kind);\n\n while (stack.length) {\n const scopeId = stack.pop() as number;\n const scope = analysis.scopes[scopeId];\n const shadowBindingId = useTypeBindings\n ? scope.typeBindings.get(binding.name)\n : scope.bindings.get(binding.name);\n\n if (shadowBindingId != null && shadowBindingId !== binding.id) {\n ranges.push({ start: scope.start, end: scope.end });\n continue;\n }\n\n stack.push(...scope.childIds);\n }\n\n return ranges\n .map((range) => clampRange(range, baseRange))\n .filter((range): range is Range => range !== null);\n}\n\nfunction clampRange(range: Range, base: Range): Range | null {\n const start = Math.max(range.start, base.start);\n const end = Math.min(range.end, base.end);\n if (start >= end) return null;\n return { start, end };\n}\n\nfunction subtractRanges(base: Range, exclusions: Range[]): Range[] {\n if (!exclusions.length) return [base];\n const normalized = normalizeRanges(exclusions);\n let current: Range[] = [base];\n\n for (const ex of normalized) {\n const next: Range[] = [];\n for (const range of current) {\n next.push(...subtractOne(range, ex));\n }\n current = next;\n if (!current.length) break;\n }\n\n return current;\n}\n\nfunction subtractOne(range: Range, exclude: Range): Range[] {\n if (exclude.end <= range.start || exclude.start >= range.end) {\n return [range];\n }\n\n const result: Range[] = [];\n if (exclude.start > range.start) {\n result.push({ start: range.start, end: Math.min(exclude.start, range.end) });\n }\n if (exclude.end < range.end) {\n result.push({ start: Math.max(exclude.end, range.start), end: range.end });\n }\n return result;\n}\n\nfunction normalizeRanges(ranges: Range[]): Range[] {\n const sorted = [...ranges].sort((a, b) => a.start - b.start);\n const result: Range[] = [];\n\n for (const range of sorted) {\n const last = result[result.length - 1];\n if (!last || range.start > last.end) {\n result.push({ ...range });\n } else {\n last.end = Math.max(last.end, range.end);\n }\n }\n\n return result;\n}\n\nfunction isNode(value: unknown): value is AnyNode {\n return Boolean(value && typeof value === 'object' && (value as AnyNode).type);\n}\n\nfunction getParamList(params: AnyNode | AnyNode[] | null | undefined): AnyNode[] {\n if (!params) return [];\n if (Array.isArray(params)) return params;\n if (Array.isArray(params.params)) return params.params;\n if (Array.isArray(params.items)) return params.items;\n return [];\n}\n\nfunction getProgramFlags(program: AnyNode): ScopeFlags {\n const sourceType = program.sourceType ?? program.source_type ?? 'module';\n let flags = ScopeFlags.Top;\n if (sourceType === 'module') {\n flags |= ScopeFlags.Module | ScopeFlags.StrictMode;\n }\n if (hasUseStrictDirective(program)) {\n flags |= ScopeFlags.StrictMode;\n }\n return flags;\n}\n\nfunction hasUseStrictDirective(container: AnyNode | null | undefined): boolean {\n if (!container) return false;\n const directives = container.directives;\n if (Array.isArray(directives)) {\n for (const directive of directives) {\n if (getDirectiveText(directive) === 'use strict') {\n return true;\n }\n }\n }\n\n const statements =\n container.body ??\n container.statements ??\n container.body?.body ??\n container.body?.statements;\n if (Array.isArray(statements)) {\n for (const stmt of statements) {\n if (!stmt) continue;\n const directiveText = getDirectiveText(stmt);\n if (directiveText === 'use strict') {\n return true;\n }\n if (directiveText == null) {\n break;\n }\n }\n }\n\n return false;\n}\n\nfunction getDirectiveText(node: AnyNode | null | undefined): string | null {\n if (!node) return null;\n if (typeof node.directive === 'string') {\n return node.directive;\n }\n const expr = node.expression ?? node;\n if (expr?.type === 'StringLiteral' || expr?.type === 'Literal') {\n const value = expr.value ?? expr.rawValue ?? expr.raw;\n if (typeof value === 'string') return value;\n }\n return null;\n}\n\nfunction isStrictScope(node: AnyNode, type: ScopeType): boolean {\n if (type === ScopeType.Class) return true;\n if (type === ScopeType.Function) {\n return hasUseStrictDirective(node.body);\n }\n if (type === ScopeType.TsModule) {\n return hasUseStrictDirective(node.body);\n }\n return false;\n}\n\nfunction shouldAddToValueBindings(kind: BindingKind): boolean {\n switch (kind) {\n case BindingKind.Var:\n case BindingKind.Let:\n case BindingKind.Const:\n case BindingKind.Function:\n case BindingKind.Class:\n case BindingKind.Param:\n case BindingKind.Import:\n case BindingKind.Enum:\n case BindingKind.EnumMember:\n case BindingKind.Namespace:\n return true;\n default:\n return false;\n }\n}\n\nfunction shouldAddToTypeBindings(kind: BindingKind): boolean {\n switch (kind) {\n case BindingKind.TypeImport:\n case BindingKind.TypeAlias:\n case BindingKind.Interface:\n case BindingKind.TypeParam:\n case BindingKind.Class:\n case BindingKind.Enum:\n case BindingKind.EnumMember:\n case BindingKind.Namespace:\n case BindingKind.Import:\n return true;\n default:\n return false;\n }\n}\n\nfunction isBlockScopedBindingKind(kind: BindingKind): boolean {\n return (\n kind === BindingKind.Let ||\n kind === BindingKind.Const ||\n kind === BindingKind.Class ||\n kind === BindingKind.EnumMember\n );\n}\n\nfunction usesTypeBindingsForShadowing(kind: BindingKind): boolean {\n return (\n kind === BindingKind.TypeImport ||\n kind === BindingKind.TypeAlias ||\n kind === BindingKind.Interface ||\n kind === BindingKind.TypeParam\n );\n}\n\nfunction canReferenceAsType(kind: BindingKind): boolean {\n return (\n kind === BindingKind.TypeImport ||\n kind === BindingKind.TypeAlias ||\n kind === BindingKind.Interface ||\n kind === BindingKind.TypeParam ||\n kind === BindingKind.Class ||\n kind === BindingKind.Enum ||\n kind === BindingKind.EnumMember ||\n kind === BindingKind.Namespace ||\n kind === BindingKind.Import\n );\n}\n\nfunction canReferenceAsValueAsType(kind: BindingKind): boolean {\n return kind === BindingKind.TypeImport || shouldAddToValueBindings(kind);\n}\n\nclass ScopeBuilder {\n private source: string;\n private scopes: Scope[];\n private bindings: Binding[];\n private scopeIdByNode: WeakMap<object, number>;\n private scopeStack: ScopeStackItem[] = [];\n\n constructor({\n source,\n scopes,\n bindings,\n scopeIdByNode,\n }: {\n source: string;\n scopes: Scope[];\n bindings: Binding[];\n scopeIdByNode: WeakMap<object, number>;\n }) {\n this.source = source;\n this.scopes = scopes;\n this.bindings = bindings;\n this.scopeIdByNode = scopeIdByNode;\n this.scopeStack.push({ scopeId: 0 });\n }\n\n walkBindings(node: AnyNode, ancestors: AnyNode[] = []): void {\n if (!node) return;\n switch (node.type) {\n case 'Program':\n this.registerScope(node, 0);\n this.walkChildren(node, ancestors);\n return;\n case 'BlockStatement':\n this.withScope(node, ScopeType.Block, () => {\n this.walkChildren(node, ancestors);\n });\n return;\n case 'FunctionDeclaration':\n if (node.id?.name) {\n this.declareBinding(this.currentScopeId(), node.id.name, BindingKind.Function, node.id);\n }\n this.withScope(node, ScopeType.Function, () => {\n const params = getParamList(node.params);\n this.declareTypeParameters(node.typeParameters ?? node.type_parameters, ancestors);\n this.declareParams(params);\n this.walkParams(params, ancestors);\n this.walkNode(node.body, ancestors);\n });\n return;\n case 'FunctionExpression':\n this.withScope(node, ScopeType.Function, () => {\n if (node.id?.name) {\n this.declareBinding(this.currentScopeId(), node.id.name, BindingKind.Function, node.id);\n }\n const params = getParamList(node.params);\n this.declareTypeParameters(node.typeParameters ?? node.type_parameters, ancestors);\n this.declareParams(params);\n this.walkParams(params, ancestors);\n this.walkNode(node.body, ancestors);\n });\n return;\n case 'ArrowFunctionExpression':\n this.withScope(node, ScopeType.Function, () => {\n const params = getParamList(node.params);\n this.declareTypeParameters(node.typeParameters ?? node.type_parameters, ancestors);\n this.declareParams(params);\n this.walkParams(params, ancestors);\n if (node.body) {\n this.walkNode(node.body, ancestors);\n }\n });\n return;\n case 'VariableDeclaration':\n this.handleVariableDeclaration(node, ancestors);\n return;\n case 'ClassDeclaration':\n if (node.id?.name) {\n this.declareBinding(this.currentScopeId(), node.id.name, BindingKind.Class, node.id);\n }\n this.withScope(node, ScopeType.Class, () => {\n this.declareTypeParameters(node.typeParameters ?? node.type_parameters, ancestors);\n this.walkNode(node.body, ancestors);\n });\n return;\n case 'ClassExpression':\n this.withScope(node, ScopeType.Class, () => {\n if (node.id?.name) {\n this.declareBinding(this.currentScopeId(), node.id.name, BindingKind.Class, node.id);\n }\n this.declareTypeParameters(node.typeParameters ?? node.type_parameters, ancestors);\n this.walkNode(node.body, ancestors);\n });\n return;\n case 'StaticBlock':\n this.withScope(node, ScopeType.ClassStaticBlock, () => {\n this.walkChildren(node, ancestors);\n });\n return;\n case 'CatchClause':\n this.withScope(node, ScopeType.Catch, () => {\n if (node.param) {\n this.declarePattern(node.param, BindingKind.Param);\n this.walkPatternExpressions(node.param, ancestors);\n }\n this.walkNode(node.body, ancestors);\n });\n return;\n case 'ForStatement':\n case 'ForInStatement':\n case 'ForOfStatement':\n this.withScope(node, ScopeType.For, () => {\n this.walkChildren(node, ancestors);\n });\n return;\n case 'SwitchStatement':\n this.withScope(node, ScopeType.Switch, () => {\n this.walkChildren(node, ancestors);\n });\n return;\n case 'ImportDeclaration':\n this.handleImportDeclaration(node);\n return;\n case 'TSImportEqualsDeclaration':\n this.handleImportEqualsDeclaration(node);\n return;\n case 'TSModuleDeclaration':\n this.handleTsModuleDeclaration(node, ancestors);\n return;\n case 'TSGlobalDeclaration':\n this.withScope(node, ScopeType.TsModule, () => {\n if (node.body) {\n this.walkNode(node.body, ancestors);\n }\n });\n return;\n case 'TSTypeAliasDeclaration':\n if (node.id?.name) {\n this.declareBinding(this.currentScopeId(), node.id.name, BindingKind.TypeAlias, node.id);\n }\n this.withScope(node, ScopeType.Type, () => {\n this.declareTypeParameters(node.typeParameters ?? node.type_parameters, ancestors);\n this.walkNode(node.typeAnnotation ?? node.type_annotation, ancestors);\n });\n return;\n case 'TSInterfaceDeclaration':\n if (node.id?.name) {\n this.declareBinding(this.currentScopeId(), node.id.name, BindingKind.Interface, node.id);\n }\n this.withScope(node, ScopeType.Type, () => {\n this.declareTypeParameters(node.typeParameters ?? node.type_parameters, ancestors);\n if (node.extends) {\n for (const heritage of node.extends) {\n this.walkNode(heritage, ancestors);\n }\n }\n if (node.body) {\n this.walkNode(node.body, ancestors);\n }\n });\n return;\n case 'TSEnumDeclaration':\n if (node.id?.name) {\n this.declareBinding(this.currentScopeId(), node.id.name, BindingKind.Enum, node.id);\n }\n if (node.body) {\n this.walkNode(node.body, ancestors);\n }\n return;\n case 'TSEnumBody':\n this.withScope(node, ScopeType.Enum, () => {\n this.walkChildren(node, ancestors);\n });\n return;\n case 'TSEnumMember':\n if (node.id?.name) {\n this.declareBinding(this.currentScopeId(), node.id.name, BindingKind.EnumMember, node.id);\n }\n if (node.initializer) {\n this.walkNode(node.initializer, ancestors);\n }\n return;\n case 'TSTypeParameter':\n if (node.name?.name) {\n this.declareBinding(this.currentScopeId(), node.name.name, BindingKind.TypeParam, node.name);\n }\n if (node.constraint) {\n this.walkNode(node.constraint, ancestors);\n }\n if (node.default) {\n this.walkNode(node.default, ancestors);\n }\n return;\n case 'TSTypeParameterDeclaration':\n for (const param of node.params ?? []) {\n this.walkNode(param, ancestors);\n }\n return;\n case 'TSMappedType':\n this.withScope(node, ScopeType.Type, () => {\n if (node.key?.name) {\n this.declareBinding(this.currentScopeId(), node.key.name, BindingKind.TypeParam, node.key);\n }\n this.walkNode(node.constraint, ancestors);\n if (node.nameType ?? node.name_type) {\n this.walkNode(node.nameType ?? node.name_type, ancestors);\n }\n if (node.typeAnnotation ?? node.type_annotation) {\n this.walkNode(node.typeAnnotation ?? node.type_annotation, ancestors);\n }\n });\n return;\n case 'TSConditionalType':\n if (node.checkType ?? node.check_type) {\n this.walkNode(node.checkType ?? node.check_type, ancestors);\n }\n this.withScope(node, ScopeType.TsConditional, () => {\n if (node.extendsType ?? node.extends_type) {\n this.walkNode(node.extendsType ?? node.extends_type, ancestors);\n }\n if (node.trueType ?? node.true_type) {\n this.walkNode(node.trueType ?? node.true_type, ancestors);\n }\n });\n if (node.falseType ?? node.false_type) {\n this.walkNode(node.falseType ?? node.false_type, ancestors);\n }\n return;\n case 'TSFunctionType':\n case 'TSConstructorType':\n case 'TSCallSignatureDeclaration':\n case 'TSMethodSignature':\n case 'TSConstructSignatureDeclaration':\n this.withScope(node, ScopeType.Type, () => {\n this.declareTypeParameters(node.typeParameters ?? node.type_parameters, ancestors);\n const params = getParamList(node.params);\n this.declareParams(params);\n this.walkParams(params, ancestors);\n if (node.returnType ?? node.return_type) {\n this.walkNode(node.returnType ?? node.return_type, ancestors);\n }\n });\n return;\n default:\n this.walkChildren(node, ancestors);\n }\n }\n\n private currentScopeId(): number {\n return this.scopeStack[this.scopeStack.length - 1]?.scopeId ?? 0;\n }\n\n private withScope(node: AnyNode, type: ScopeType, fn: () => void): void {\n const parentScopeId = this.currentScopeId();\n const parentScope = this.scopes[parentScopeId];\n let flags = getScopeFlags(type, parentScope.flags);\n if (isStrictScope(node, type)) {\n flags |= ScopeFlags.StrictMode;\n }\n flags = inheritStrictMode(flags, parentScope.flags);\n const start = getScopeStart(node, type, this.source);\n const end = getScopeEnd(node, type, this.source);\n const scopeId = createScope({\n scopes: this.scopes,\n type,\n start,\n end,\n parentId: parentScopeId,\n flags,\n });\n this.registerScope(node, scopeId);\n if (type === ScopeType.Block && parentScope.type === ScopeType.Catch) {\n this.moveBindingsToChild(parentScopeId, scopeId);\n }\n this.scopeStack.push({ scopeId });\n fn();\n this.scopeStack.pop();\n }\n\n private registerScope(node: AnyNode, scopeId: number): void {\n if (node && typeof node === 'object') {\n this.scopeIdByNode.set(node, scopeId);\n }\n }\n\n private moveBindingsToChild(parentScopeId: number, childScopeId: number): void {\n const parentScope = this.scopes[parentScopeId];\n if (!parentScope.bindings.size) return;\n const childScope = this.scopes[childScopeId];\n for (const [name, bindingId] of parentScope.bindings.entries()) {\n childScope.bindings.set(name, bindingId);\n const binding = this.bindings[bindingId];\n if (binding) {\n binding.scopeId = childScopeId;\n }\n }\n parentScope.bindings.clear();\n }\n\n private declareBinding(scopeId: number, name: string, kind: BindingKind, node: AnyNode): number {\n const scope = this.scopes[scopeId];\n const addValue = shouldAddToValueBindings(kind);\n const addType = shouldAddToTypeBindings(kind);\n if (addValue) {\n const existingValue = scope.bindings.get(name);\n if (existingValue != null) {\n if (kind === BindingKind.Var || kind === BindingKind.Function) {\n return existingValue;\n }\n return existingValue;\n }\n } else if (addType) {\n const existingType = scope.typeBindings.get(name);\n if (existingType != null) {\n return existingType;\n }\n }\n const binding: Binding = {\n id: this.bindings.length,\n name,\n kind,\n declStart: node.start ?? 0,\n declEnd: node.end ?? (node.start ?? 0),\n scopeId,\n };\n this.bindings.push(binding);\n if (addValue) {\n scope.bindings.set(name, binding.id);\n }\n if (addType && !scope.typeBindings.has(name)) {\n scope.typeBindings.set(name, binding.id);\n }\n return binding.id;\n }\n\n private declareParams(params: AnyNode[]): void {\n for (const param of params) {\n this.declarePattern(param, BindingKind.Param);\n }\n }\n\n private declareTypeParameters(typeParameters: AnyNode | null | undefined, ancestors: AnyNode[]): void {\n if (!typeParameters) return;\n const params = typeParameters.params ?? typeParameters.parameters;\n const list = Array.isArray(params)\n ? params\n : Array.isArray(typeParameters.items)\n ? typeParameters.items\n : Array.isArray(typeParameters)\n ? typeParameters\n : [];\n for (const param of list) {\n this.walkNode(param, ancestors);\n }\n }\n\n private declarePattern(pattern: AnyNode, kind: BindingKind): void {\n const identifiers: AnyNode[] = [];\n collectBindingIdentifiers(pattern, identifiers);\n for (const ident of identifiers) {\n if (ident?.name) {\n this.declareBinding(this.currentScopeId(), ident.name, kind, ident);\n }\n }\n }\n\n private handleVariableDeclaration(node: AnyNode, ancestors: AnyNode[]): void {\n const kind = node.kind;\n const bindingKind = kind === 'var' ? BindingKind.Var : kind === 'let' ? BindingKind.Let : BindingKind.Const;\n for (const decl of node.declarations ?? []) {\n if (!decl) continue;\n const scopeId =\n bindingKind === BindingKind.Var\n ? this.findVarScopeId()\n : this.currentScopeId();\n const identifiers: AnyNode[] = [];\n collectBindingIdentifiers(decl.id, identifiers);\n for (const ident of identifiers) {\n if (ident?.name) {\n this.declareBinding(scopeId, ident.name, bindingKind, ident);\n }\n }\n if (decl.id) {\n this.walkPatternExpressions(decl.id, ancestors);\n }\n if (decl.init) {\n this.walkNode(decl.init, ancestors);\n }\n }\n }\n\n private handleImportDeclaration(node: AnyNode): void {\n const rootScopeId = 0;\n const declarationKind =\n (node.importKind ?? node.import_kind) === 'type'\n ? BindingKind.TypeImport\n : BindingKind.Import;\n for (const spec of node.specifiers ?? []) {\n const local = spec?.local;\n if (!local?.name) continue;\n let kind = declarationKind;\n if (spec?.type === 'ImportSpecifier') {\n const specKind = spec.importKind ?? spec.import_kind;\n if (declarationKind === BindingKind.Import && specKind === 'type') {\n kind = BindingKind.TypeImport;\n }\n }\n this.declareBinding(rootScopeId, local.name, kind, local);\n }\n }\n\n private handleImportEqualsDeclaration(node: AnyNode): void {\n const kind =\n (node.importKind ?? node.import_kind) === 'type'\n ? BindingKind.TypeImport\n : BindingKind.Import;\n if (node.id?.name) {\n this.declareBinding(this.currentScopeId(), node.id.name, kind, node.id);\n }\n }\n\n private handleTsModuleDeclaration(node: AnyNode, ancestors: AnyNode[]): void {\n const id = node.id;\n if (id?.type === 'Identifier' && id.name) {\n this.declareBinding(this.currentScopeId(), id.name, BindingKind.Namespace, id);\n }\n this.withScope(node, ScopeType.TsModule, () => {\n if (node.body) {\n this.walkNode(node.body, ancestors);\n }\n });\n }\n\n private walkParams(params: AnyNode[], ancestors: AnyNode[]): void {\n for (const param of params) {\n this.walkPatternExpressions(param, ancestors);\n }\n }\n\n private walkPatternExpressions(pattern: AnyNode, ancestors: AnyNode[]): void {\n if (!pattern) return;\n if (pattern.type === 'TSParameterProperty' && pattern.parameter) {\n this.walkPatternExpressions(pattern.parameter, ancestors.concat(pattern));\n return;\n }\n if (pattern.typeAnnotation) {\n this.walkNode(pattern.typeAnnotation, ancestors.concat(pattern));\n }\n switch (pattern.type) {\n case 'Identifier':\n return;\n case 'RestElement':\n this.walkPatternExpressions(pattern.argument, ancestors);\n return;\n case 'AssignmentPattern':\n this.walkPatternExpressions(pattern.left, ancestors);\n if (pattern.right) {\n this.walkNode(pattern.right, ancestors);\n }\n return;\n case 'ArrayPattern':\n for (const element of pattern.elements ?? []) {\n if (element) {\n this.walkPatternExpressions(element, ancestors);\n }\n }\n return;\n case 'ObjectPattern':\n for (const prop of pattern.properties ?? []) {\n if (!prop) continue;\n if (prop.type === 'Property') {\n if (prop.computed && prop.key) {\n this.walkNode(prop.key, ancestors);\n }\n if (prop.value) {\n this.walkPatternExpressions(prop.value, ancestors);\n }\n } else if (prop.type === 'RestElement') {\n this.walkPatternExpressions(prop.argument, ancestors);\n }\n }\n return;\n default:\n this.walkNode(pattern, ancestors);\n }\n }\n\n private findVarScopeId(): number {\n for (let i = this.scopeStack.length - 1; i >= 0; i -= 1) {\n const scope = this.scopes[this.scopeStack[i].scopeId];\n if (\n scope.type === ScopeType.Function ||\n scope.type === ScopeType.Program ||\n scope.type === ScopeType.ClassStaticBlock ||\n scope.type === ScopeType.TsModule\n ) {\n return scope.id;\n }\n }\n return 0;\n }\n\n private walkNode(node: AnyNode, ancestors: AnyNode[]): void {\n if (!node) return;\n this.walkBindings(node, ancestors.concat(node));\n }\n\n private walkChildren(node: AnyNode, ancestors: AnyNode[]): void {\n const keys = visitorKeys[node.type] ?? [];\n for (const key of keys) {\n const value = node[key];\n if (Array.isArray(value)) {\n for (const child of value) {\n if (isNode(child)) {\n this.walkBindings(child, ancestors.concat(node));\n }\n }\n } else if (isNode(value)) {\n this.walkBindings(value, ancestors.concat(node));\n }\n }\n }\n}\n\nclass ReferenceBuilder {\n private scopes: Scope[];\n private bindings: Binding[];\n private references: Reference[];\n private scopeIdByNode: WeakMap<object, number>;\n private scopeStack: ScopeStackItem[] = [];\n private contextStack: ReferenceContext[] = [ReferenceContext.Value];\n\n constructor({\n scopes,\n bindings,\n references,\n scopeIdByNode,\n }: {\n scopes: Scope[];\n bindings: Binding[];\n references: Reference[];\n scopeIdByNode: WeakMap<object, number>;\n }) {\n this.scopes = scopes;\n this.bindings = bindings;\n this.references = references;\n this.scopeIdByNode = scopeIdByNode;\n this.scopeStack.push({ scopeId: 0 });\n }\n\n walkReferences(node: AnyNode, ancestors: AnyNode[] = []): void {\n if (!node) return;\n switch (node.type) {\n case 'Program':\n case 'BlockStatement':\n case 'ForStatement':\n case 'ForInStatement':\n case 'ForOfStatement':\n case 'SwitchStatement':\n this.withScope(node, () => {\n this.walkChildren(node, ancestors);\n });\n return;\n case 'FunctionDeclaration':\n case 'FunctionExpression':\n case 'ArrowFunctionExpression':\n this.withScope(node, () => {\n const params = getParamList(node.params);\n const typeParameters = node.typeParameters ?? node.type_parameters;\n const returnType = node.returnType ?? node.return_type;\n this.withTypeContext(() => {\n if (typeParameters) {\n this.walkReferences(typeParameters, ancestors.concat(node));\n }\n if (returnType) {\n this.walkReferences(returnType, ancestors.concat(node));\n }\n });\n for (const param of params) {\n this.walkPatternReferences(param, ancestors.concat(node));\n }\n if (node.body) {\n this.walkReferences(node.body, ancestors.concat(node));\n }\n });\n return;\n case 'CatchClause':\n this.withScope(node, () => {\n if (node.param) {\n this.walkPatternReferences(node.param, ancestors.concat(node));\n }\n if (node.body) {\n this.walkReferences(node.body, ancestors.concat(node));\n }\n });\n return;\n case 'ClassDeclaration':\n case 'ClassExpression':\n this.withScope(node, () => {\n if (node.superClass) {\n this.walkReferences(node.superClass, ancestors.concat(node));\n }\n const typeParameters = node.typeParameters ?? node.type_parameters;\n const superTypeArguments =\n node.superTypeArguments ??\n node.super_type_arguments ??\n node.superTypeParameters ??\n node.super_type_parameters;\n if (typeParameters) {\n this.withTypeContext(() => {\n this.walkReferences(typeParameters, ancestors.concat(node));\n });\n }\n if (superTypeArguments) {\n this.withTypeContext(() => {\n this.walkReferences(superTypeArguments, ancestors.concat(node));\n });\n }\n if (node.implements) {\n this.withTypeContext(() => {\n for (const impl of node.implements ?? []) {\n this.walkReferences(impl, ancestors.concat(node));\n }\n });\n }\n if (node.body) {\n this.walkReferences(node.body, ancestors.concat(node));\n }\n });\n return;\n case 'StaticBlock':\n this.withScope(node, () => {\n this.walkChildren(node, ancestors);\n });\n return;\n case 'VariableDeclarator':\n if (node.id) {\n this.walkPatternReferences(node.id, ancestors);\n }\n if (node.init) {\n this.walkReferences(node.init, ancestors.concat(node));\n }\n return;\n case 'ImportDeclaration':\n return;\n case 'TSImportEqualsDeclaration':\n if (node.moduleReference ?? node.module_reference) {\n this.walkReferences(node.moduleReference ?? node.module_reference, ancestors.concat(node));\n }\n return;\n case 'TSModuleDeclaration':\n case 'TSGlobalDeclaration':\n this.withScope(node, () => {\n if (node.body) {\n this.walkReferences(node.body, ancestors.concat(node));\n }\n });\n return;\n case 'TSModuleBlock':\n this.walkChildren(node, ancestors);\n return;\n case 'TSTypeAliasDeclaration':\n this.withScope(node, () => {\n const typeParameters = node.typeParameters ?? node.type_parameters;\n const typeAnnotation = node.typeAnnotation ?? node.type_annotation;\n this.withTypeContext(() => {\n if (typeParameters) {\n this.walkReferences(typeParameters, ancestors.concat(node));\n }\n if (typeAnnotation) {\n this.walkReferences(typeAnnotation, ancestors.concat(node));\n }\n });\n });\n return;\n case 'TSInterfaceDeclaration':\n this.withScope(node, () => {\n const typeParameters = node.typeParameters ?? node.type_parameters;\n this.withTypeContext(() => {\n if (typeParameters) {\n this.walkReferences(typeParameters, ancestors.concat(node));\n }\n if (node.extends) {\n for (const heritage of node.extends ?? []) {\n this.walkReferences(heritage, ancestors.concat(node));\n }\n }\n if (node.body) {\n this.walkReferences(node.body, ancestors.concat(node));\n }\n });\n });\n return;\n case 'TSEnumDeclaration':\n if (node.body) {\n this.walkReferences(node.body, ancestors.concat(node));\n }\n return;\n case 'TSEnumBody':\n this.withScope(node, () => {\n this.walkChildren(node, ancestors);\n });\n return;\n case 'TSEnumMember':\n if (node.initializer) {\n this.walkReferences(node.initializer, ancestors.concat(node));\n }\n return;\n case 'TSMappedType':\n this.withScope(node, () => {\n const nameType = node.nameType ?? node.name_type;\n const typeAnnotation = node.typeAnnotation ?? node.type_annotation;\n this.withTypeContext(() => {\n if (node.constraint) {\n this.walkReferences(node.constraint, ancestors.concat(node));\n }\n if (nameType) {\n this.walkReferences(nameType, ancestors.concat(node));\n }\n if (typeAnnotation) {\n this.walkReferences(typeAnnotation, ancestors.concat(node));\n }\n });\n });\n return;\n case 'TSConditionalType':\n this.withTypeContext(() => {\n const checkType = node.checkType ?? node.check_type;\n const extendsType = node.extendsType ?? node.extends_type;\n const trueType = node.trueType ?? node.true_type;\n const falseType = node.falseType ?? node.false_type;\n if (checkType) {\n this.walkReferences(checkType, ancestors.concat(node));\n }\n this.withScope(node, () => {\n if (extendsType) {\n this.walkReferences(extendsType, ancestors.concat(node));\n }\n if (trueType) {\n this.walkReferences(trueType, ancestors.concat(node));\n }\n });\n if (falseType) {\n this.walkReferences(falseType, ancestors.concat(node));\n }\n });\n return;\n case 'TSFunctionType':\n case 'TSConstructorType':\n case 'TSCallSignatureDeclaration':\n case 'TSConstructSignatureDeclaration':\n this.withScope(node, () => {\n const typeParameters = node.typeParameters ?? node.type_parameters;\n const returnType = node.returnType ?? node.return_type;\n const params = getParamList(node.params);\n this.withTypeContext(() => {\n if (typeParameters) {\n this.walkReferences(typeParameters, ancestors.concat(node));\n }\n for (const param of params) {\n this.walkPatternReferences(param, ancestors.concat(node));\n }\n if (returnType) {\n this.walkReferences(returnType, ancestors.concat(node));\n }\n });\n });\n return;\n case 'TSMethodSignature':\n this.withScope(node, () => {\n const typeParameters = node.typeParameters ?? node.type_parameters;\n const returnType = node.returnType ?? node.return_type;\n const params = getParamList(node.params);\n this.withTypeContext(() => {\n if (node.key && node.computed) {\n this.withValueAsTypeContext(() => {\n this.walkReferences(node.key, ancestors.concat(node));\n });\n }\n if (typeParameters) {\n this.walkReferences(typeParameters, ancestors.concat(node));\n }\n for (const param of params) {\n this.walkPatternReferences(param, ancestors.concat(node));\n }\n if (returnType) {\n this.walkReferences(returnType, ancestors.concat(node));\n }\n });\n });\n return;\n case 'TSPropertySignature':\n this.withTypeContext(() => {\n if (node.key && node.computed) {\n this.withValueAsTypeContext(() => {\n this.walkReferences(node.key, ancestors.concat(node));\n });\n }\n if (node.typeAnnotation ?? node.type_annotation) {\n this.walkReferences(node.typeAnnotation ?? node.type_annotation, ancestors.concat(node));\n }\n });\n return;\n case 'TSTypeQuery':\n if (node.exprName ?? node.expr_name) {\n this.withValueAsTypeContext(() => {\n this.walkReferences(node.exprName ?? node.expr_name, ancestors.concat(node));\n });\n }\n if (node.typeArguments ?? node.type_arguments) {\n this.withTypeContext(() => {\n this.walkReferences(node.typeArguments ?? node.type_arguments, ancestors.concat(node));\n });\n }\n return;\n case 'TSAsExpression':\n case 'TSSatisfiesExpression':\n case 'TSTypeAssertion':\n if (node.expression) {\n this.walkReferences(node.expression, ancestors.concat(node));\n }\n if (node.typeAnnotation ?? node.type_annotation) {\n this.withTypeContext(() => {\n this.walkReferences(node.typeAnnotation ?? node.type_annotation, ancestors.concat(node));\n });\n }\n return;\n case 'TSInstantiationExpression':\n if (node.expression) {\n this.walkReferences(node.expression, ancestors.concat(node));\n }\n if (node.typeArguments ?? node.type_arguments) {\n this.withTypeContext(() => {\n this.walkReferences(node.typeArguments ?? node.type_arguments, ancestors.concat(node));\n });\n }\n return;\n case 'TSNonNullExpression':\n if (node.expression) {\n this.walkReferences(node.expression, ancestors.concat(node));\n }\n return;\n case 'PropertyDefinition':\n case 'ClassProperty':\n if (node.computed && node.key) {\n this.walkReferences(node.key, ancestors.concat(node));\n }\n if (node.typeAnnotation ?? node.type_annotation) {\n this.withTypeContext(() => {\n this.walkReferences(node.typeAnnotation ?? node.type_annotation, ancestors.concat(node));\n });\n }\n if (node.value) {\n this.walkReferences(node.value, ancestors.concat(node));\n }\n return;\n default:\n break;\n }\n\n if (node.type === 'JSXIdentifier') {\n const parent = ancestors[ancestors.length - 1];\n const grandparent = ancestors[ancestors.length - 2];\n const parentKey = getParentKey(parent, node);\n if (isJsxIdentifierReference(node, parent, grandparent, parentKey)) {\n this.addReference(node);\n }\n return;\n }\n\n if (node.type === 'Identifier') {\n const parent = ancestors[ancestors.length - 1];\n const grandparent = ancestors[ancestors.length - 2];\n const parentKey = getParentKey(parent, node);\n if (isIdentifierReference(node, parent, grandparent, parentKey)) {\n this.addReference(node);\n }\n return;\n }\n\n this.walkChildren(node, ancestors);\n }\n\n private withScope(node: AnyNode, fn: () => void): void {\n const scopeId = this.scopeIdByNode.get(node);\n if (scopeId == null) {\n fn();\n return;\n }\n this.scopeStack.push({ scopeId });\n fn();\n this.scopeStack.pop();\n }\n\n private withContext(context: ReferenceContext, fn: () => void): void {\n this.contextStack.push(context);\n fn();\n this.contextStack.pop();\n }\n\n private withTypeContext(fn: () => void): void {\n this.withContext(ReferenceContext.Type, fn);\n }\n\n private withValueAsTypeContext(fn: () => void): void {\n this.withContext(ReferenceContext.ValueAsType, fn);\n }\n\n private currentContext(): ReferenceContext {\n return this.contextStack[this.contextStack.length - 1] ?? ReferenceContext.Value;\n }\n\n private addReference(node: AnyNode): void {\n const name = node.name;\n const scopeId = this.currentScopeId();\n const bindingId = this.resolveBinding(scopeId, name, this.currentContext());\n const ref: Reference = {\n id: this.references.length,\n name,\n start: node.start ?? 0,\n end: node.end ?? (node.start ?? 0),\n scopeId,\n bindingId,\n };\n this.references.push(ref);\n }\n\n private currentScopeId(): number {\n return this.scopeStack[this.scopeStack.length - 1]?.scopeId ?? 0;\n }\n\n private resolveBinding(\n scopeId: number,\n name: string,\n context: ReferenceContext,\n ): number | null {\n let currentScopeId: number | null = scopeId;\n while (currentScopeId != null) {\n const scope = this.scopes[currentScopeId];\n if (context === ReferenceContext.Type) {\n const typeBindingId = scope.typeBindings.get(name);\n if (typeBindingId != null) return typeBindingId;\n const valueBindingId = scope.bindings.get(name);\n if (valueBindingId != null) {\n const bindingKind = this.bindings[valueBindingId]?.kind;\n if (bindingKind && canReferenceAsType(bindingKind)) {\n return valueBindingId;\n }\n }\n } else if (context === ReferenceContext.ValueAsType) {\n const valueBindingId = scope.bindings.get(name);\n if (valueBindingId != null) return valueBindingId;\n const typeBindingId = scope.typeBindings.get(name);\n if (typeBindingId != null) {\n const bindingKind = this.bindings[typeBindingId]?.kind;\n if (bindingKind && canReferenceAsValueAsType(bindingKind)) {\n return typeBindingId;\n }\n }\n } else {\n const bindingId = scope.bindings.get(name);\n if (bindingId != null) return bindingId;\n }\n currentScopeId = scope.parentId;\n }\n return null;\n }\n\n private walkPatternReferences(pattern: AnyNode, ancestors: AnyNode[]): void {\n if (!pattern) return;\n if (pattern.type === 'TSParameterProperty' && pattern.parameter) {\n this.walkPatternReferences(pattern.parameter, ancestors.concat(pattern));\n return;\n }\n if (pattern.typeAnnotation) {\n this.withTypeContext(() => {\n this.walkReferences(pattern.typeAnnotation, ancestors.concat(pattern));\n });\n }\n switch (pattern.type) {\n case 'Identifier':\n return;\n case 'RestElement':\n this.walkPatternReferences(pattern.argument, ancestors.concat(pattern));\n return;\n case 'AssignmentPattern':\n this.walkPatternReferences(pattern.left, ancestors.concat(pattern));\n if (pattern.right) {\n this.walkReferences(pattern.right, ancestors.concat(pattern));\n }\n return;\n case 'ArrayPattern':\n for (const element of pattern.elements ?? []) {\n if (element) {\n this.walkPatternReferences(element, ancestors.concat(pattern));\n }\n }\n return;\n case 'ObjectPattern':\n for (const prop of pattern.properties ?? []) {\n if (!prop) continue;\n if (prop.type === 'Property') {\n if (prop.computed && prop.key) {\n this.walkReferences(prop.key, ancestors.concat(prop));\n }\n if (prop.value) {\n this.walkPatternReferences(prop.value, ancestors.concat(prop));\n }\n } else if (prop.type === 'RestElement') {\n this.walkPatternReferences(prop.argument, ancestors.concat(prop));\n }\n }\n return;\n default:\n this.walkReferences(pattern, ancestors.concat(pattern));\n }\n }\n\n private walkChildren(node: AnyNode, ancestors: AnyNode[]): void {\n const keys = visitorKeys[node.type] ?? [];\n for (const key of keys) {\n const value = node[key];\n if (Array.isArray(value)) {\n for (const child of value) {\n if (isNode(child)) {\n this.walkReferences(child, ancestors.concat(node));\n }\n }\n } else if (isNode(value)) {\n this.walkReferences(value, ancestors.concat(node));\n }\n }\n }\n}\n\nfunction createScope({\n scopes,\n type,\n start,\n end,\n parentId,\n flags,\n}: {\n scopes: Scope[];\n type: ScopeType;\n start: number;\n end: number;\n parentId: number | null;\n flags: ScopeFlags;\n}): number {\n const scopeId = scopes.length;\n const scope: Scope = {\n id: scopeId,\n type,\n flags,\n start,\n end,\n parentId,\n childIds: [],\n bindings: new Map(),\n typeBindings: new Map(),\n };\n scopes.push(scope);\n if (parentId != null) {\n scopes[parentId].childIds.push(scopeId);\n }\n return scopeId;\n}\n\nfunction inheritStrictMode(flags: ScopeFlags, parentFlags: ScopeFlags): ScopeFlags {\n if (parentFlags & ScopeFlags.StrictMode) {\n return flags | ScopeFlags.StrictMode;\n }\n return flags;\n}\n\nfunction getScopeFlags(type: ScopeType, parentFlags: ScopeFlags): ScopeFlags {\n switch (type) {\n case ScopeType.Program:\n return parentFlags;\n case ScopeType.Function:\n return ScopeFlags.Function;\n case ScopeType.Block:\n return ScopeFlags.Block;\n case ScopeType.Catch:\n return ScopeFlags.Catch | ScopeFlags.Block;\n case ScopeType.Class:\n return ScopeFlags.Class;\n case ScopeType.ClassStaticBlock:\n return ScopeFlags.ClassStaticBlock;\n case ScopeType.For:\n return ScopeFlags.For | ScopeFlags.Block;\n case ScopeType.Switch:\n return ScopeFlags.Switch | ScopeFlags.Block;\n case ScopeType.TsModule:\n return ScopeFlags.TsModuleBlock;\n case ScopeType.Enum:\n return ScopeFlags.Enum;\n case ScopeType.Type:\n return ScopeFlags.Type;\n case ScopeType.TsConditional:\n return ScopeFlags.TsConditional;\n default:\n return ScopeFlags.Block;\n }\n}\n\nfunction getScopeStart(node: AnyNode, type: ScopeType, source: string): number {\n if (type === ScopeType.Program) return 0;\n if (type === ScopeType.TsModule) {\n const body = node.body;\n if (body?.start != null) return body.start;\n }\n if (type === ScopeType.Function) {\n const body = node.body;\n if (body?.start != null) return body.start;\n }\n return node.start ?? 0;\n}\n\nfunction getScopeEnd(node: AnyNode, type: ScopeType, source: string): number {\n if (type === ScopeType.Program) return source.length;\n if (type === ScopeType.TsModule) {\n const body = node.body;\n if (body?.end != null) return body.end;\n }\n if (type === ScopeType.Function) {\n const body = node.body;\n if (body?.end != null) return body.end;\n }\n return node.end ?? source.length;\n}\n\nfunction collectBindingIdentifiers(pattern: AnyNode, out: AnyNode[]): void {\n if (!pattern) return;\n switch (pattern.type) {\n case 'Identifier':\n out.push(pattern);\n return;\n case 'TSParameterProperty':\n collectBindingIdentifiers(pattern.parameter, out);\n return;\n case 'RestElement':\n collectBindingIdentifiers(pattern.argument, out);\n return;\n case 'AssignmentPattern':\n collectBindingIdentifiers(pattern.left, out);\n return;\n case 'ArrayPattern':\n for (const element of pattern.elements ?? []) {\n if (element) collectBindingIdentifiers(element, out);\n }\n return;\n case 'ObjectPattern':\n for (const prop of pattern.properties ?? []) {\n if (!prop) continue;\n if (prop.type === 'Property') {\n collectBindingIdentifiers(prop.value, out);\n } else if (prop.type === 'RestElement') {\n collectBindingIdentifiers(prop.argument, out);\n }\n }\n return;\n default:\n return;\n }\n}\n\nfunction getParentKey(parent: AnyNode | undefined, node: AnyNode): string | null {\n if (!parent) return null;\n for (const key of Object.keys(parent)) {\n const value = parent[key];\n if (value === node) return key;\n if (Array.isArray(value)) {\n if (value.includes(node)) return key;\n }\n }\n return null;\n}\n\nfunction isIdentifierReference(\n node: AnyNode,\n parent?: AnyNode,\n grandparent?: AnyNode,\n parentKey?: string | null,\n): boolean {\n if (!parent) return true;\n\n if (\n parent.type === 'MemberExpression' &&\n parentKey === 'property' &&\n parent.computed === false\n ) {\n return false;\n }\n\n if (\n parent.type === 'Property' &&\n parentKey === 'key' &&\n parent.computed === false\n ) {\n return false;\n }\n\n if (\n parent.type === 'MethodDefinition' &&\n parentKey === 'key' &&\n parent.computed === false\n ) {\n return false;\n }\n\n if (\n parent.type === 'PropertyDefinition' &&\n parentKey === 'key' &&\n parent.computed === false\n ) {\n return false;\n }\n if (\n parent.type === 'ClassProperty' &&\n parentKey === 'key' &&\n parent.computed === false\n ) {\n return false;\n }\n if (\n (parent.type === 'TSPropertySignature' || parent.type === 'TSMethodSignature') &&\n parentKey === 'key' &&\n parent.computed === false\n ) {\n return false;\n }\n\n if (parent.type === 'Property' && grandparent?.type === 'ObjectPattern') {\n if (parentKey === 'value') return false;\n if (parentKey === 'key' && parent.computed === false) return false;\n }\n\n if (\n parent.type === 'VariableDeclarator' &&\n parentKey === 'id'\n ) {\n return false;\n }\n\n if (\n (parent.type === 'FunctionDeclaration' ||\n parent.type === 'FunctionExpression' ||\n parent.type === 'ArrowFunctionExpression') &&\n (parentKey === 'id' || parentKey === 'params')\n ) {\n return false;\n }\n\n if (\n (parent.type === 'ClassDeclaration' || parent.type === 'ClassExpression') &&\n parentKey === 'id'\n ) {\n return false;\n }\n if (\n (parent.type === 'TSTypeAliasDeclaration' ||\n parent.type === 'TSInterfaceDeclaration' ||\n parent.type === 'TSEnumDeclaration' ||\n parent.type === 'TSModuleDeclaration' ||\n parent.type === 'TSImportEqualsDeclaration') &&\n parentKey === 'id'\n ) {\n return false;\n }\n if (parent.type === 'TSEnumMember' && parentKey === 'id') {\n return false;\n }\n if (parent.type === 'TSTypeParameter' && parentKey === 'name') {\n return false;\n }\n if (parent.type === 'TSMappedType' && parentKey === 'key') {\n return false;\n }\n if (parent.type === 'TSIndexSignature' && parentKey === 'parameters') {\n return false;\n }\n if (\n (parent.type === 'TSQualifiedName' || parent.type === 'TSImportTypeQualifiedName') &&\n parentKey === 'right'\n ) {\n return false;\n }\n\n if (\n (parent.type === 'ImportSpecifier' ||\n parent.type === 'ImportDefaultSpecifier' ||\n parent.type === 'ImportNamespaceSpecifier') &&\n parentKey === 'local'\n ) {\n return false;\n }\n\n if (parent.type === 'CatchClause' && parentKey === 'param') {\n return false;\n }\n\n if (parent.type === 'LabeledStatement' && parentKey === 'label') {\n return false;\n }\n if (\n (parent.type === 'BreakStatement' || parent.type === 'ContinueStatement') &&\n parentKey === 'label'\n ) {\n return false;\n }\n\n return true;\n}\n\nfunction isJsxIdentifierReference(\n node: AnyNode,\n parent?: AnyNode,\n grandparent?: AnyNode,\n parentKey?: string | null,\n): boolean {\n if (!parent) return true;\n if (parent.type === 'JSXAttribute' && parentKey === 'name') {\n return false;\n }\n if (parent.type === 'JSXMemberExpression' && parentKey === 'property') {\n return false;\n }\n if (parent.type === 'JSXNamespacedName') {\n return false;\n }\n return true;\n}\n","/**\n * TypeScript type analyzer for JSX element prop support.\n *\n * Uses a TypeScript Language Service to determine whether each custom React\n * component supports `style` (CSSProperties-compatible) and `className`\n * (string-compatible) props. Correlation between OXC and TypeScript ASTs is\n * position-based — both use UTF-16 string indices, so element positions can\n * be compared directly.\n *\n * Intrinsic HTML elements are excluded from the analysis entirely — the\n * consumer already knows they support both from the HTML standard.\n */\nimport ts from \"typescript\";\nimport path from \"node:path\";\nimport { realpathSync } from \"node:fs\";\nimport {\n createTsWorkspaceService,\n type TsProjectDescriptor,\n type TsWorkspaceService,\n} from \"@modules/typescript\";\n\nexport interface ElementTypeSupport {\n supportsStyle: boolean;\n supportsClassName: boolean;\n}\n\n/** Position → type support for custom components only */\nexport type FileTypeInfo = Map<number, ElementTypeSupport>;\n\nexport interface TypeAnalyzer {\n analyzeFileTypes(filePath: string, source: string): FileTypeInfo | null;\n}\n\n/** Custom component if first character is uppercase or it's a member expression */\nfunction isCustomComponent(node: ts.JsxTagNameExpression): boolean {\n const text = node.getText();\n return text.length > 0 && text[0] !== text[0]!.toLowerCase();\n}\n\ninterface TempobookTsMetadata {\n managedTsconfigPaths?: unknown;\n}\n\nfunction canonicalPath(value: string): string {\n const resolved = path.resolve(value);\n try {\n return realpathSync.native(resolved);\n } catch {\n return resolved;\n }\n}\n\nfunction parseManagedTsconfigPaths(rawConfig: unknown): string[] {\n if (!rawConfig || typeof rawConfig !== \"object\" || Array.isArray(rawConfig)) {\n return [];\n }\n\n const metadata = (rawConfig as { tempobook?: TempobookTsMetadata }).tempobook;\n if (!metadata || typeof metadata !== \"object\" || Array.isArray(metadata)) {\n return [];\n }\n\n const managedPaths = metadata.managedTsconfigPaths;\n if (!Array.isArray(managedPaths)) {\n return [];\n }\n\n return managedPaths.filter(\n (entry): entry is string =>\n typeof entry === \"string\" && entry.trim().length > 0,\n );\n}\n\nfunction parseProjectReferenceTsconfigPaths(\n references: readonly ts.ProjectReference[] | undefined,\n configDir: string,\n): string[] {\n if (!references || references.length === 0) {\n return [];\n }\n\n const entries: string[] = [];\n for (const reference of references) {\n const referencePath = reference.path;\n if (!referencePath) {\n continue;\n }\n\n const resolvedPath = path.resolve(configDir, referencePath);\n const tsconfigPath = resolvedPath.endsWith(\".json\")\n ? resolvedPath\n : path.join(resolvedPath, \"tsconfig.json\");\n\n entries.push(tsconfigPath);\n }\n\n return entries;\n}\n\nfunction buildWorkspaceProjects(\n root: string,\n configPath: string,\n parsedConfig: ts.ParsedCommandLine,\n rawConfig: unknown,\n): TsProjectDescriptor[] {\n const configDir = path.dirname(configPath);\n const projects: TsProjectDescriptor[] = [\n {\n id: \"root\",\n rootDir: root,\n tsconfigPath: configPath,\n priority: 0,\n },\n ];\n\n const seenTsconfigPaths = new Set<string>([canonicalPath(configPath)]);\n const referencedTsconfigPaths = parseProjectReferenceTsconfigPaths(\n parsedConfig.projectReferences,\n configDir,\n );\n const managedTsconfigPaths = parseManagedTsconfigPaths(rawConfig).map(\n (entry) => path.resolve(configDir, entry),\n );\n\n const candidateTsconfigPaths = [\n ...managedTsconfigPaths,\n ...referencedTsconfigPaths,\n ];\n for (const candidatePath of candidateTsconfigPaths) {\n const normalizedPath = canonicalPath(candidatePath);\n if (seenTsconfigPaths.has(normalizedPath)) {\n continue;\n }\n if (!ts.sys.fileExists(normalizedPath)) {\n continue;\n }\n\n seenTsconfigPaths.add(normalizedPath);\n projects.push({\n id: `external-${projects.length}`,\n rootDir: path.dirname(normalizedPath),\n tsconfigPath: normalizedPath,\n priority: 0,\n });\n }\n\n return projects;\n}\n\n/**\n * Create a type analyzer backed by a TypeScript Language Service.\n *\n * The Language Service provides incremental updates — when a file changes,\n * we bump its version and provide the new content. The service lazily\n * recomputes only what's needed.\n *\n * Returns null if no tsconfig.json is found.\n */\nexport function createTypeAnalyzer(root: string): TypeAnalyzer | null {\n const configPath = ts.findConfigFile(\n root,\n ts.sys.fileExists,\n \"tsconfig.json\",\n );\n if (!configPath) return null;\n\n const configFile = ts.readConfigFile(configPath, ts.sys.readFile);\n if (configFile.error) return null;\n\n const parsedConfig = ts.parseJsonConfigFileContent(\n configFile.config,\n ts.sys,\n root,\n );\n\n const workspaceProjects = buildWorkspaceProjects(\n root,\n configPath,\n parsedConfig,\n configFile.config,\n );\n const workspaceProjectRoots = workspaceProjects.map((project) =>\n canonicalPath(project.rootDir),\n );\n\n const workspaceService: TsWorkspaceService = createTsWorkspaceService({\n workspaceRoot: root,\n primaryProjectId: \"root\",\n projects: workspaceProjects,\n });\n\n // Lazily resolved CSSProperties type for style compatibility checks.\n // Resolved from JSX.IntrinsicElements['div'].style, with the undefined\n // union member stripped to get the pure CSSProperties type.\n let cssPropertiesType: ts.Type | null | undefined;\n\n function resolveCSSPropertiesType(\n checker: ts.TypeChecker,\n sourceFile: ts.SourceFile,\n ): ts.Type | null {\n if (cssPropertiesType !== undefined) return cssPropertiesType;\n\n try {\n // Try resolving JSX namespace — works for global JSX (React 18 and earlier)\n let jsxNs: ts.Symbol | undefined = (checker as any).resolveName(\n \"JSX\",\n sourceFile,\n ts.SymbolFlags.Namespace,\n false,\n );\n\n // React 19+ / react-jsx: JSX is not global, resolve from jsx-runtime module\n if (!jsxNs) {\n const program = workspaceService.getProgram();\n const compilerOptions = program?.getCompilerOptions();\n const jsxImportSource = compilerOptions?.jsxImportSource ?? \"react\";\n const resolved = ts.resolveModuleName(\n `${jsxImportSource}/jsx-runtime`,\n sourceFile.fileName,\n compilerOptions!,\n ts.sys,\n );\n if (resolved.resolvedModule) {\n const runtimeFile = program?.getSourceFile(\n resolved.resolvedModule.resolvedFileName,\n );\n if (runtimeFile) {\n const runtimeSymbol = checker.getSymbolAtLocation(runtimeFile);\n if (runtimeSymbol) {\n const exports = checker.getExportsOfModule(runtimeSymbol);\n jsxNs = exports.find((s) => s.name === \"JSX\");\n }\n }\n }\n }\n\n if (!jsxNs) {\n cssPropertiesType = null;\n return null;\n }\n\n const intrinsicSym =\n checker\n .getExportsOfModule(jsxNs)\n .find((s) => s.name === \"IntrinsicElements\") ??\n checker\n .getDeclaredTypeOfSymbol(jsxNs)\n .getProperty(\"IntrinsicElements\");\n if (!intrinsicSym) {\n cssPropertiesType = null;\n return null;\n }\n\n const divSym = checker\n .getDeclaredTypeOfSymbol(intrinsicSym)\n .getProperty(\"div\");\n if (!divSym) {\n cssPropertiesType = null;\n return null;\n }\n\n const styleSym = checker.getTypeOfSymbol(divSym).getProperty(\"style\");\n if (!styleSym) {\n cssPropertiesType = null;\n return null;\n }\n\n const styleType = checker.getTypeOfSymbol(styleSym);\n\n // styleType is CSSProperties | undefined — extract the non-undefined member\n if (styleType.isUnion()) {\n const nonUndefined = styleType.types.find(\n (t) => !(t.flags & ts.TypeFlags.Undefined),\n );\n cssPropertiesType = nonUndefined ?? null;\n } else {\n cssPropertiesType = styleType;\n }\n } catch {\n cssPropertiesType = null;\n }\n\n return cssPropertiesType;\n }\n\n return {\n analyzeFileTypes(filePath: string, source: string): FileTypeInfo | null {\n const canonicalFilePath = canonicalPath(filePath);\n const belongsToWorkspace = workspaceProjectRoots.some(\n (projectRoot) =>\n canonicalFilePath === projectRoot ||\n canonicalFilePath.startsWith(`${projectRoot}${path.sep}`),\n );\n if (!belongsToWorkspace) {\n return null;\n }\n\n // Update the workspace service with current in-memory source.\n workspaceService.updateFile(filePath, source);\n\n const program = workspaceService.getProgram();\n if (!program) return null;\n\n const sourceFile = program.getSourceFile(filePath);\n if (!sourceFile) return null;\n\n const checker = program.getTypeChecker();\n const cssProps = resolveCSSPropertiesType(checker, sourceFile);\n const result: FileTypeInfo = new Map();\n\n function visit(node: ts.Node) {\n if (\n (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) &&\n isCustomComponent(node.tagName)\n ) {\n const position = node.getStart(sourceFile);\n const attrsType = checker.getContextualType(node.attributes);\n\n let supportsStyle = false;\n let supportsClassName = false;\n\n if (attrsType) {\n // className: check that `string` is assignable to the prop's type\n const classNameProp = attrsType.getProperty(\"className\");\n if (classNameProp) {\n const propType = checker.getTypeOfSymbol(classNameProp);\n supportsClassName = (checker as any).isTypeAssignableTo(\n checker.getStringType(),\n propType,\n );\n }\n\n // style: check that CSSProperties is assignable to the prop's type\n const styleProp = attrsType.getProperty(\"style\");\n if (styleProp && cssProps) {\n const propType = checker.getTypeOfSymbol(styleProp);\n supportsStyle = (checker as any).isTypeAssignableTo(\n cssProps,\n propType,\n );\n }\n }\n\n result.set(position, { supportsStyle, supportsClassName });\n }\n\n ts.forEachChild(node, visit);\n }\n\n visit(sourceFile);\n return result;\n },\n };\n}\n","import { realpathSync, statSync } from \"node:fs\";\nimport path from \"node:path\";\n\nimport ts from \"typescript\";\n\nexport interface TsProjectDescriptor {\n id: string;\n rootDir: string;\n tsconfigPath: string;\n priority?: number;\n}\n\nexport interface TsWorkspaceDescriptor {\n workspaceRoot: string;\n projects: TsProjectDescriptor[];\n primaryProjectId?: string;\n}\n\nexport interface TsWorkspaceService {\n updateFile(filePath: string, content: string): void;\n closeFile(filePath: string): void;\n getProgram(): ts.Program | null;\n getSemanticDiagnostics(filePath: string): readonly ts.Diagnostic[];\n getDefinition(\n filePath: string,\n position: number,\n ): readonly ts.DefinitionInfo[] | undefined;\n dispose(): void;\n}\n\ninterface LoadedProject {\n id: string;\n rootDir: string;\n tsconfigPath: string;\n priority: number;\n options: ts.CompilerOptions;\n fileNames: string[];\n moduleResolutionCache: ts.ModuleResolutionCache;\n}\n\ninterface InMemoryFile {\n version: number;\n content: string;\n}\n\nfunction canonicalPath(input: string): string {\n const resolved = path.resolve(input);\n try {\n return realpathSync.native(resolved);\n } catch {\n return resolved;\n }\n}\n\nfunction compareProjectPriority(\n left: LoadedProject,\n right: LoadedProject,\n): number {\n const depthDelta = right.rootDir.length - left.rootDir.length;\n if (depthDelta !== 0) {\n return depthDelta;\n }\n\n const priorityDelta = right.priority - left.priority;\n if (priorityDelta !== 0) {\n return priorityDelta;\n }\n\n return left.id.localeCompare(right.id);\n}\n\nfunction isWithinRoot(filePath: string, rootDir: string): boolean {\n return filePath === rootDir || filePath.startsWith(`${rootDir}${path.sep}`);\n}\n\nfunction loadProject(descriptor: TsProjectDescriptor): LoadedProject {\n const id = descriptor.id.trim();\n if (!id) {\n throw new Error(\"Project id must be a non-empty string\");\n }\n\n const tsconfigPath = canonicalPath(descriptor.tsconfigPath);\n const rootDir = canonicalPath(descriptor.rootDir);\n\n const readResult = ts.readConfigFile(tsconfigPath, ts.sys.readFile);\n if (readResult.error) {\n throw new Error(\n `Failed to read tsconfig at ${tsconfigPath}: ${ts.flattenDiagnosticMessageText(\n readResult.error.messageText,\n \"\\n\",\n )}`,\n );\n }\n\n const parsed = ts.parseJsonConfigFileContent(\n readResult.config,\n ts.sys,\n path.dirname(tsconfigPath),\n );\n\n if (parsed.errors.length > 0) {\n const message = parsed.errors\n .map((diagnostic) =>\n ts.flattenDiagnosticMessageText(diagnostic.messageText, \"\\n\"),\n )\n .join(\"; \");\n throw new Error(`Failed to parse tsconfig at ${tsconfigPath}: ${message}`);\n }\n\n const normalizedFiles = parsed.fileNames.map((fileName) =>\n canonicalPath(fileName),\n );\n return {\n id,\n rootDir,\n tsconfigPath,\n priority: descriptor.priority ?? 0,\n options: parsed.options,\n fileNames: normalizedFiles,\n moduleResolutionCache: ts.createModuleResolutionCache(\n rootDir,\n (value) => value,\n parsed.options,\n ),\n };\n}\n\nfunction getFileMtimeVersion(filePath: string): string {\n try {\n const stats = statSync(filePath);\n return String(stats.mtimeMs);\n } catch {\n return \"0\";\n }\n}\n\nexport function createTsWorkspaceService(\n descriptor: TsWorkspaceDescriptor,\n): TsWorkspaceService {\n if (descriptor.projects.length === 0) {\n throw new Error(\n \"TsWorkspaceDescriptor.projects must include at least one project\",\n );\n }\n\n const workspaceRoot = canonicalPath(descriptor.workspaceRoot);\n const loadedProjects = descriptor.projects.map((project) =>\n loadProject(project),\n );\n\n const projectsById = new Map<string, LoadedProject>();\n for (const project of loadedProjects) {\n if (projectsById.has(project.id)) {\n throw new Error(`Duplicate project id: ${project.id}`);\n }\n projectsById.set(project.id, project);\n }\n\n const primaryProject =\n (descriptor.primaryProjectId\n ? projectsById.get(descriptor.primaryProjectId)\n : null) ?? loadedProjects[0]!;\n if (\n descriptor.primaryProjectId &&\n !projectsById.has(descriptor.primaryProjectId)\n ) {\n throw new Error(\n `Primary project id not found: ${descriptor.primaryProjectId}`,\n );\n }\n\n const orderedProjects = [...loadedProjects].sort(compareProjectPriority);\n const rootFileNames = new Set<string>();\n for (const project of loadedProjects) {\n for (const fileName of project.fileNames) {\n rootFileNames.add(fileName);\n }\n }\n\n const inMemoryFiles = new Map<string, InMemoryFile>();\n let projectVersion = 0;\n\n function getOwningProject(filePath: string): LoadedProject {\n const canonicalFilePath = canonicalPath(filePath);\n for (const project of orderedProjects) {\n if (isWithinRoot(canonicalFilePath, project.rootDir)) {\n return project;\n }\n }\n return primaryProject;\n }\n\n const moduleResolutionHost: ts.ModuleResolutionHost = {\n fileExists: ts.sys.fileExists,\n readFile: ts.sys.readFile,\n realpath: ts.sys.realpath,\n directoryExists: ts.sys.directoryExists,\n getCurrentDirectory: () => workspaceRoot,\n getDirectories: ts.sys.getDirectories,\n };\n\n const host: ts.LanguageServiceHost = {\n getScriptFileNames: () => Array.from(rootFileNames),\n getScriptVersion: (filePath) => {\n const canonicalFilePath = canonicalPath(filePath);\n const inMemory = inMemoryFiles.get(canonicalFilePath);\n if (inMemory) {\n return String(inMemory.version);\n }\n return getFileMtimeVersion(canonicalFilePath);\n },\n getScriptSnapshot: (filePath) => {\n const canonicalFilePath = canonicalPath(filePath);\n const inMemory = inMemoryFiles.get(canonicalFilePath);\n if (inMemory) {\n return ts.ScriptSnapshot.fromString(inMemory.content);\n }\n\n const diskContent = ts.sys.readFile(canonicalFilePath);\n if (diskContent === undefined) {\n return undefined;\n }\n return ts.ScriptSnapshot.fromString(diskContent);\n },\n getCurrentDirectory: () => workspaceRoot,\n getCompilationSettings: () => primaryProject.options,\n getDefaultLibFileName: (options) => ts.getDefaultLibFilePath(options),\n fileExists: ts.sys.fileExists,\n readFile: ts.sys.readFile,\n readDirectory: ts.sys.readDirectory,\n directoryExists: ts.sys.directoryExists,\n getDirectories: ts.sys.getDirectories,\n useCaseSensitiveFileNames: () => ts.sys.useCaseSensitiveFileNames,\n getProjectVersion: () => String(projectVersion),\n resolveModuleNames: (moduleNames, containingFile) => {\n const owningProject = getOwningProject(containingFile);\n\n return moduleNames.map((moduleName) => {\n const ownerResolution = ts.resolveModuleName(\n moduleName,\n containingFile,\n owningProject.options,\n moduleResolutionHost,\n owningProject.moduleResolutionCache,\n ).resolvedModule;\n\n if (ownerResolution) {\n return ownerResolution;\n }\n\n if (owningProject.id === primaryProject.id) {\n return undefined;\n }\n\n return ts.resolveModuleName(\n moduleName,\n containingFile,\n primaryProject.options,\n moduleResolutionHost,\n primaryProject.moduleResolutionCache,\n ).resolvedModule;\n });\n },\n };\n\n const languageService = ts.createLanguageService(\n host,\n ts.createDocumentRegistry(),\n );\n\n return {\n updateFile(filePath: string, content: string): void {\n const canonicalFilePath = canonicalPath(filePath);\n const currentVersion = inMemoryFiles.get(canonicalFilePath)?.version ?? 0;\n inMemoryFiles.set(canonicalFilePath, {\n version: currentVersion + 1,\n content,\n });\n\n if (!rootFileNames.has(canonicalFilePath)) {\n rootFileNames.add(canonicalFilePath);\n }\n\n projectVersion += 1;\n },\n closeFile(filePath: string): void {\n const canonicalFilePath = canonicalPath(filePath);\n inMemoryFiles.delete(canonicalFilePath);\n projectVersion += 1;\n },\n getProgram(): ts.Program | null {\n return languageService.getProgram() ?? null;\n },\n getSemanticDiagnostics(filePath: string): readonly ts.Diagnostic[] {\n const canonicalFilePath = canonicalPath(filePath);\n return languageService.getSemanticDiagnostics(canonicalFilePath);\n },\n getDefinition(\n filePath: string,\n position: number,\n ): readonly ts.DefinitionInfo[] | undefined {\n const canonicalFilePath = canonicalPath(filePath);\n return languageService.getDefinitionAtPosition(\n canonicalFilePath,\n position,\n );\n },\n dispose(): void {\n languageService.dispose();\n },\n };\n}\n"],"mappings":"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","names":["realpathSync","statSync","parseSync","parseSync","ts","path","realpathSync","canonicalPath","path","realpathSync","ts","realpathSync","statSync"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tempo-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Tempo SDK — Vite plugin for JSX annotation and shared page/storyboard types",
|
|
6
6
|
"exports": {
|
|
@@ -9,10 +9,14 @@
|
|
|
9
9
|
"types": "./dist/index.d.ts"
|
|
10
10
|
}
|
|
11
11
|
},
|
|
12
|
-
"files": [
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
13
15
|
"scripts": {
|
|
14
16
|
"build": "tsup",
|
|
17
|
+
"prepare": "tsup",
|
|
15
18
|
"prepublishOnly": "pnpm build",
|
|
19
|
+
"demo": "TEMPO=true pnpm --filter @tempo-modules/vite-annotate-plugin-demo dev",
|
|
16
20
|
"test": "vitest run",
|
|
17
21
|
"test:watch": "vitest"
|
|
18
22
|
},
|
|
@@ -31,6 +35,7 @@
|
|
|
31
35
|
"@types/node": "^25.0.8",
|
|
32
36
|
"@types/react": "catalog:",
|
|
33
37
|
"@vitejs/plugin-react": "catalog:",
|
|
38
|
+
"playwright-core": "^1.58.2",
|
|
34
39
|
"tsup": "^8.5.1",
|
|
35
40
|
"typescript": "catalog:",
|
|
36
41
|
"vite": "catalog:",
|
package/dist/page.d.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { ComponentType, ComponentProps, ReactElement } from 'react';
|
|
2
|
-
|
|
3
|
-
interface TempoPage {
|
|
4
|
-
name: string;
|
|
5
|
-
}
|
|
6
|
-
interface StoryboardLayout {
|
|
7
|
-
x: number;
|
|
8
|
-
y: number;
|
|
9
|
-
width: number;
|
|
10
|
-
height: number;
|
|
11
|
-
zIndex?: number;
|
|
12
|
-
}
|
|
13
|
-
interface TempoStoryboard<C extends ComponentType<any>> {
|
|
14
|
-
component: C;
|
|
15
|
-
name?: string;
|
|
16
|
-
args?: Partial<ComponentProps<C>>;
|
|
17
|
-
layout: StoryboardLayout;
|
|
18
|
-
container?: (Story: ComponentType<any>) => ReactElement;
|
|
19
|
-
}
|
|
20
|
-
interface TempoRouteStoryboard {
|
|
21
|
-
route: string;
|
|
22
|
-
name?: string;
|
|
23
|
-
layout: StoryboardLayout;
|
|
24
|
-
container?: (Story: ComponentType<any>) => ReactElement;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export type { StoryboardLayout, TempoPage, TempoRouteStoryboard, TempoStoryboard };
|
package/dist/page.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
//# sourceMappingURL=page.js.map
|
package/dist/page.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|