tempo-sdk 0.0.1

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.
@@ -0,0 +1,66 @@
1
+ import { JSXElement } from 'oxc-parser';
2
+ import { FilterPattern } from '@rollup/pluginutils';
3
+ import { Plugin } from 'vite';
4
+ import { ComponentType, ComponentProps, ReactElement } from 'react';
5
+
6
+ interface ElementInfo {
7
+ element: JSXElement;
8
+ /** e.g. "div", "Button", "Foo.Bar" */
9
+ tagName: string;
10
+ }
11
+ /** Supported prop value types. `undefined` values are skipped. */
12
+ type PropValue = string | number | boolean | null | undefined | ((...args: unknown[]) => unknown) | object;
13
+
14
+ interface ElementTypeSupport {
15
+ supportsStyle: boolean;
16
+ supportsClassName: boolean;
17
+ }
18
+ /** Position → type support for custom components only */
19
+ type FileTypeInfo = Map<number, ElementTypeSupport>;
20
+ interface TypeAnalyzer {
21
+ analyzeFileTypes(filePath: string, source: string): FileTypeInfo | null;
22
+ }
23
+ /**
24
+ * Create a type analyzer backed by a TypeScript Language Service.
25
+ *
26
+ * The Language Service provides incremental updates — when a file changes,
27
+ * we bump its version and provide the new content. The service lazily
28
+ * recomputes only what's needed.
29
+ *
30
+ * Returns null if no tsconfig.json is found.
31
+ */
32
+ declare function createTypeAnalyzer(root: string): TypeAnalyzer | null;
33
+
34
+ interface TempoPage {
35
+ name: string;
36
+ }
37
+ interface StoryboardLayout {
38
+ x: number;
39
+ y: number;
40
+ width: number;
41
+ height: number;
42
+ zIndex?: number;
43
+ }
44
+ interface TempoStoryboard<C extends ComponentType<any>> {
45
+ component: C;
46
+ name?: string;
47
+ args?: Partial<ComponentProps<C>>;
48
+ layout: StoryboardLayout;
49
+ container?: (Story: ComponentType<any>) => ReactElement;
50
+ }
51
+ interface TempoRouteStoryboard {
52
+ route: string;
53
+ name?: string;
54
+ layout: StoryboardLayout;
55
+ container?: (Story: ComponentType<any>) => ReactElement;
56
+ }
57
+
58
+ type ComputeAnnotatedProps = (info: ElementInfo, filepath: string, modifiedtsMs: number) => Record<string, PropValue>;
59
+ interface TempoAnnotateOptions {
60
+ computeAnnotatedProps?: ComputeAnnotatedProps;
61
+ include?: FilterPattern;
62
+ exclude?: FilterPattern;
63
+ }
64
+ declare function tempoAnnotate(options?: TempoAnnotateOptions): Plugin;
65
+
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 };
package/dist/index.js ADDED
@@ -0,0 +1,606 @@
1
+ // index.ts
2
+ import { realpathSync as realpathSync3, statSync as statSync2 } from "fs";
3
+ import { relative } from "path";
4
+
5
+ // ../../modules/annotation/annotate-file.ts
6
+ import { parseSync } from "oxc-parser";
7
+
8
+ // ../../modules/file-parser/utils.ts
9
+ function getTagName(name) {
10
+ switch (name.type) {
11
+ case "JSXIdentifier":
12
+ return name.name;
13
+ case "JSXNamespacedName":
14
+ return `${name.namespace.name}:${name.name.name}`;
15
+ case "JSXMemberExpression":
16
+ return getJSXMemberExpressionName(name);
17
+ }
18
+ }
19
+ function getJSXMemberExpressionName(node) {
20
+ const object = node.object.type === "JSXIdentifier" ? node.object.name : getJSXMemberExpressionName(node.object);
21
+ return `${object}.${node.property.name}`;
22
+ }
23
+
24
+ // ../../modules/file-parser/walk.ts
25
+ function walk(node, callbacks) {
26
+ if (node === null || node === void 0 || typeof node !== "object") return;
27
+ if (Array.isArray(node)) {
28
+ for (const child of node) {
29
+ walk(child, callbacks);
30
+ }
31
+ return;
32
+ }
33
+ const record = node;
34
+ const type = record.type;
35
+ if (typeof type === "string" && type in callbacks) {
36
+ callbacks[type](node);
37
+ }
38
+ for (const value of Object.values(record)) {
39
+ if (value !== null && typeof value === "object") {
40
+ walk(value, callbacks);
41
+ }
42
+ }
43
+ }
44
+
45
+ // ../../modules/annotation/annotate-file.ts
46
+ function formatPropValue(value) {
47
+ if (value === void 0) return null;
48
+ if (typeof value === "function") return value.toString();
49
+ return JSON.stringify(value);
50
+ }
51
+ function formatProps(props) {
52
+ return Object.entries(props).map(([key, value]) => {
53
+ const formatted = formatPropValue(value);
54
+ if (formatted === null) return null;
55
+ return ` ${key}={${formatted}}`;
56
+ }).filter((s) => s !== null).join("");
57
+ }
58
+ var FRAGMENT_TAG_NAMES = /* @__PURE__ */ new Set(["Fragment", "React.Fragment"]);
59
+ function annotateFile(source, filename, computeAnnotatedProps) {
60
+ const parseResult = parseSync(filename, source);
61
+ const insertions = [];
62
+ walk(parseResult.program, {
63
+ JSXElement(node) {
64
+ const element = node;
65
+ const tagName = getTagName(element.openingElement.name);
66
+ if (FRAGMENT_TAG_NAMES.has(tagName)) return;
67
+ const props = computeAnnotatedProps({ element, tagName });
68
+ if (Object.keys(props).length === 0) return;
69
+ const openingElement = element.openingElement;
70
+ let insertPos = openingElement.end - 1;
71
+ if (openingElement.selfClosing) {
72
+ insertPos--;
73
+ while (insertPos > openingElement.start && /\s/.test(source[insertPos - 1])) {
74
+ insertPos--;
75
+ }
76
+ }
77
+ insertions.push({ position: insertPos, text: formatProps(props) });
78
+ }
79
+ });
80
+ insertions.sort((a, b) => a.position - b.position);
81
+ const chunks = [];
82
+ let lastPos = 0;
83
+ for (const { position, text } of insertions) {
84
+ chunks.push(source.slice(lastPos, position));
85
+ chunks.push(text);
86
+ lastPos = position;
87
+ }
88
+ chunks.push(source.slice(lastPos));
89
+ return { annotatedSource: chunks.join(""), elementsAnnotated: insertions.length, parseResult };
90
+ }
91
+
92
+ // index.ts
93
+ import { createFilter } from "@rollup/pluginutils";
94
+
95
+ // type-analyzer.ts
96
+ import ts2 from "typescript";
97
+ import path2 from "path";
98
+ import { realpathSync as realpathSync2 } from "fs";
99
+
100
+ // ../../modules/typescript/workspace-service.ts
101
+ import { realpathSync, statSync } from "fs";
102
+ import path from "path";
103
+ import ts from "typescript";
104
+ function canonicalPath(input) {
105
+ const resolved = path.resolve(input);
106
+ try {
107
+ return realpathSync.native(resolved);
108
+ } catch {
109
+ return resolved;
110
+ }
111
+ }
112
+ function compareProjectPriority(left, right) {
113
+ const depthDelta = right.rootDir.length - left.rootDir.length;
114
+ if (depthDelta !== 0) {
115
+ return depthDelta;
116
+ }
117
+ const priorityDelta = right.priority - left.priority;
118
+ if (priorityDelta !== 0) {
119
+ return priorityDelta;
120
+ }
121
+ return left.id.localeCompare(right.id);
122
+ }
123
+ function isWithinRoot(filePath, rootDir) {
124
+ return filePath === rootDir || filePath.startsWith(`${rootDir}${path.sep}`);
125
+ }
126
+ function loadProject(descriptor) {
127
+ const id = descriptor.id.trim();
128
+ if (!id) {
129
+ throw new Error("Project id must be a non-empty string");
130
+ }
131
+ const tsconfigPath = canonicalPath(descriptor.tsconfigPath);
132
+ const rootDir = canonicalPath(descriptor.rootDir);
133
+ const readResult = ts.readConfigFile(tsconfigPath, ts.sys.readFile);
134
+ if (readResult.error) {
135
+ throw new Error(
136
+ `Failed to read tsconfig at ${tsconfigPath}: ${ts.flattenDiagnosticMessageText(
137
+ readResult.error.messageText,
138
+ "\n"
139
+ )}`
140
+ );
141
+ }
142
+ const parsed = ts.parseJsonConfigFileContent(
143
+ readResult.config,
144
+ ts.sys,
145
+ path.dirname(tsconfigPath)
146
+ );
147
+ if (parsed.errors.length > 0) {
148
+ const message = parsed.errors.map((diagnostic) => ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n")).join("; ");
149
+ throw new Error(`Failed to parse tsconfig at ${tsconfigPath}: ${message}`);
150
+ }
151
+ const normalizedFiles = parsed.fileNames.map((fileName) => canonicalPath(fileName));
152
+ return {
153
+ id,
154
+ rootDir,
155
+ tsconfigPath,
156
+ priority: descriptor.priority ?? 0,
157
+ options: parsed.options,
158
+ fileNames: normalizedFiles,
159
+ moduleResolutionCache: ts.createModuleResolutionCache(
160
+ rootDir,
161
+ (value) => value,
162
+ parsed.options
163
+ )
164
+ };
165
+ }
166
+ function getFileMtimeVersion(filePath) {
167
+ try {
168
+ const stats = statSync(filePath);
169
+ return String(stats.mtimeMs);
170
+ } catch {
171
+ return "0";
172
+ }
173
+ }
174
+ function createTsWorkspaceService(descriptor) {
175
+ if (descriptor.projects.length === 0) {
176
+ throw new Error("TsWorkspaceDescriptor.projects must include at least one project");
177
+ }
178
+ const workspaceRoot = canonicalPath(descriptor.workspaceRoot);
179
+ const loadedProjects = descriptor.projects.map((project) => loadProject(project));
180
+ const projectsById = /* @__PURE__ */ new Map();
181
+ for (const project of loadedProjects) {
182
+ if (projectsById.has(project.id)) {
183
+ throw new Error(`Duplicate project id: ${project.id}`);
184
+ }
185
+ projectsById.set(project.id, project);
186
+ }
187
+ const primaryProject = (descriptor.primaryProjectId ? projectsById.get(descriptor.primaryProjectId) : null) ?? loadedProjects[0];
188
+ if (descriptor.primaryProjectId && !projectsById.has(descriptor.primaryProjectId)) {
189
+ throw new Error(`Primary project id not found: ${descriptor.primaryProjectId}`);
190
+ }
191
+ const orderedProjects = [...loadedProjects].sort(compareProjectPriority);
192
+ const rootFileNames = /* @__PURE__ */ new Set();
193
+ for (const project of loadedProjects) {
194
+ for (const fileName of project.fileNames) {
195
+ rootFileNames.add(fileName);
196
+ }
197
+ }
198
+ const inMemoryFiles = /* @__PURE__ */ new Map();
199
+ let projectVersion = 0;
200
+ function getOwningProject(filePath) {
201
+ const canonicalFilePath = canonicalPath(filePath);
202
+ for (const project of orderedProjects) {
203
+ if (isWithinRoot(canonicalFilePath, project.rootDir)) {
204
+ return project;
205
+ }
206
+ }
207
+ return primaryProject;
208
+ }
209
+ const moduleResolutionHost = {
210
+ fileExists: ts.sys.fileExists,
211
+ readFile: ts.sys.readFile,
212
+ realpath: ts.sys.realpath,
213
+ directoryExists: ts.sys.directoryExists,
214
+ getCurrentDirectory: () => workspaceRoot,
215
+ getDirectories: ts.sys.getDirectories
216
+ };
217
+ const host = {
218
+ getScriptFileNames: () => Array.from(rootFileNames),
219
+ getScriptVersion: (filePath) => {
220
+ const canonicalFilePath = canonicalPath(filePath);
221
+ const inMemory = inMemoryFiles.get(canonicalFilePath);
222
+ if (inMemory) {
223
+ return String(inMemory.version);
224
+ }
225
+ return getFileMtimeVersion(canonicalFilePath);
226
+ },
227
+ getScriptSnapshot: (filePath) => {
228
+ const canonicalFilePath = canonicalPath(filePath);
229
+ const inMemory = inMemoryFiles.get(canonicalFilePath);
230
+ if (inMemory) {
231
+ return ts.ScriptSnapshot.fromString(inMemory.content);
232
+ }
233
+ const diskContent = ts.sys.readFile(canonicalFilePath);
234
+ if (diskContent === void 0) {
235
+ return void 0;
236
+ }
237
+ return ts.ScriptSnapshot.fromString(diskContent);
238
+ },
239
+ getCurrentDirectory: () => workspaceRoot,
240
+ getCompilationSettings: () => primaryProject.options,
241
+ getDefaultLibFileName: (options) => ts.getDefaultLibFilePath(options),
242
+ fileExists: ts.sys.fileExists,
243
+ readFile: ts.sys.readFile,
244
+ readDirectory: ts.sys.readDirectory,
245
+ directoryExists: ts.sys.directoryExists,
246
+ getDirectories: ts.sys.getDirectories,
247
+ useCaseSensitiveFileNames: () => ts.sys.useCaseSensitiveFileNames,
248
+ getProjectVersion: () => String(projectVersion),
249
+ resolveModuleNames: (moduleNames, containingFile) => {
250
+ const owningProject = getOwningProject(containingFile);
251
+ return moduleNames.map((moduleName) => {
252
+ const ownerResolution = ts.resolveModuleName(
253
+ moduleName,
254
+ containingFile,
255
+ owningProject.options,
256
+ moduleResolutionHost,
257
+ owningProject.moduleResolutionCache
258
+ ).resolvedModule;
259
+ if (ownerResolution) {
260
+ return ownerResolution;
261
+ }
262
+ if (owningProject.id === primaryProject.id) {
263
+ return void 0;
264
+ }
265
+ return ts.resolveModuleName(
266
+ moduleName,
267
+ containingFile,
268
+ primaryProject.options,
269
+ moduleResolutionHost,
270
+ primaryProject.moduleResolutionCache
271
+ ).resolvedModule;
272
+ });
273
+ }
274
+ };
275
+ const languageService = ts.createLanguageService(host, ts.createDocumentRegistry());
276
+ return {
277
+ updateFile(filePath, content) {
278
+ const canonicalFilePath = canonicalPath(filePath);
279
+ const currentVersion = inMemoryFiles.get(canonicalFilePath)?.version ?? 0;
280
+ inMemoryFiles.set(canonicalFilePath, {
281
+ version: currentVersion + 1,
282
+ content
283
+ });
284
+ if (!rootFileNames.has(canonicalFilePath)) {
285
+ rootFileNames.add(canonicalFilePath);
286
+ }
287
+ projectVersion += 1;
288
+ },
289
+ closeFile(filePath) {
290
+ const canonicalFilePath = canonicalPath(filePath);
291
+ inMemoryFiles.delete(canonicalFilePath);
292
+ projectVersion += 1;
293
+ },
294
+ getProgram() {
295
+ return languageService.getProgram() ?? null;
296
+ },
297
+ getSemanticDiagnostics(filePath) {
298
+ const canonicalFilePath = canonicalPath(filePath);
299
+ return languageService.getSemanticDiagnostics(canonicalFilePath);
300
+ },
301
+ getDefinition(filePath, position) {
302
+ const canonicalFilePath = canonicalPath(filePath);
303
+ return languageService.getDefinitionAtPosition(canonicalFilePath, position);
304
+ },
305
+ dispose() {
306
+ languageService.dispose();
307
+ }
308
+ };
309
+ }
310
+
311
+ // type-analyzer.ts
312
+ function isCustomComponent(node) {
313
+ const text = node.getText();
314
+ return text.length > 0 && text[0] !== text[0].toLowerCase();
315
+ }
316
+ function canonicalPath2(value) {
317
+ const resolved = path2.resolve(value);
318
+ try {
319
+ return realpathSync2.native(resolved);
320
+ } catch {
321
+ return resolved;
322
+ }
323
+ }
324
+ function parseManagedTsconfigPaths(rawConfig) {
325
+ if (!rawConfig || typeof rawConfig !== "object" || Array.isArray(rawConfig)) {
326
+ return [];
327
+ }
328
+ const metadata = rawConfig.tempobook;
329
+ if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) {
330
+ return [];
331
+ }
332
+ const managedPaths = metadata.managedTsconfigPaths;
333
+ if (!Array.isArray(managedPaths)) {
334
+ return [];
335
+ }
336
+ return managedPaths.filter((entry) => typeof entry === "string" && entry.trim().length > 0);
337
+ }
338
+ function parseProjectReferenceTsconfigPaths(references, configDir) {
339
+ if (!references || references.length === 0) {
340
+ return [];
341
+ }
342
+ const entries = [];
343
+ for (const reference of references) {
344
+ const referencePath = reference.path;
345
+ if (!referencePath) {
346
+ continue;
347
+ }
348
+ const resolvedPath = path2.resolve(configDir, referencePath);
349
+ const tsconfigPath = resolvedPath.endsWith(".json") ? resolvedPath : path2.join(resolvedPath, "tsconfig.json");
350
+ entries.push(tsconfigPath);
351
+ }
352
+ return entries;
353
+ }
354
+ function buildWorkspaceProjects(root, configPath, parsedConfig, rawConfig) {
355
+ const configDir = path2.dirname(configPath);
356
+ const projects = [
357
+ {
358
+ id: "root",
359
+ rootDir: root,
360
+ tsconfigPath: configPath,
361
+ priority: 0
362
+ }
363
+ ];
364
+ const seenTsconfigPaths = /* @__PURE__ */ new Set([canonicalPath2(configPath)]);
365
+ const referencedTsconfigPaths = parseProjectReferenceTsconfigPaths(parsedConfig.projectReferences, configDir);
366
+ const managedTsconfigPaths = parseManagedTsconfigPaths(rawConfig).map(
367
+ (entry) => path2.resolve(configDir, entry)
368
+ );
369
+ const candidateTsconfigPaths = [...managedTsconfigPaths, ...referencedTsconfigPaths];
370
+ for (const candidatePath of candidateTsconfigPaths) {
371
+ const normalizedPath = canonicalPath2(candidatePath);
372
+ if (seenTsconfigPaths.has(normalizedPath)) {
373
+ continue;
374
+ }
375
+ if (!ts2.sys.fileExists(normalizedPath)) {
376
+ continue;
377
+ }
378
+ seenTsconfigPaths.add(normalizedPath);
379
+ projects.push({
380
+ id: `external-${projects.length}`,
381
+ rootDir: path2.dirname(normalizedPath),
382
+ tsconfigPath: normalizedPath,
383
+ priority: 0
384
+ });
385
+ }
386
+ return projects;
387
+ }
388
+ function createTypeAnalyzer(root) {
389
+ const configPath = ts2.findConfigFile(root, ts2.sys.fileExists, "tsconfig.json");
390
+ if (!configPath) return null;
391
+ const configFile = ts2.readConfigFile(configPath, ts2.sys.readFile);
392
+ if (configFile.error) return null;
393
+ const parsedConfig = ts2.parseJsonConfigFileContent(
394
+ configFile.config,
395
+ ts2.sys,
396
+ root
397
+ );
398
+ const workspaceProjects = buildWorkspaceProjects(root, configPath, parsedConfig, configFile.config);
399
+ const workspaceProjectRoots = workspaceProjects.map((project) => canonicalPath2(project.rootDir));
400
+ const workspaceService = createTsWorkspaceService({
401
+ workspaceRoot: root,
402
+ primaryProjectId: "root",
403
+ projects: workspaceProjects
404
+ });
405
+ let cssPropertiesType;
406
+ function resolveCSSPropertiesType(checker, sourceFile) {
407
+ if (cssPropertiesType !== void 0) return cssPropertiesType;
408
+ try {
409
+ const jsxNs = checker.resolveName(
410
+ "JSX",
411
+ sourceFile,
412
+ ts2.SymbolFlags.Namespace,
413
+ false
414
+ );
415
+ if (!jsxNs) {
416
+ cssPropertiesType = null;
417
+ return null;
418
+ }
419
+ const intrinsicSym = checker.getExportsOfModule(jsxNs).find((s) => s.name === "IntrinsicElements");
420
+ if (!intrinsicSym) {
421
+ cssPropertiesType = null;
422
+ return null;
423
+ }
424
+ const divSym = checker.getDeclaredTypeOfSymbol(intrinsicSym).getProperty("div");
425
+ if (!divSym) {
426
+ cssPropertiesType = null;
427
+ return null;
428
+ }
429
+ const styleSym = checker.getTypeOfSymbol(divSym).getProperty("style");
430
+ if (!styleSym) {
431
+ cssPropertiesType = null;
432
+ return null;
433
+ }
434
+ const styleType = checker.getTypeOfSymbol(styleSym);
435
+ if (styleType.isUnion()) {
436
+ const nonUndefined = styleType.types.find(
437
+ (t) => !(t.flags & ts2.TypeFlags.Undefined)
438
+ );
439
+ cssPropertiesType = nonUndefined ?? null;
440
+ } else {
441
+ cssPropertiesType = styleType;
442
+ }
443
+ } catch {
444
+ cssPropertiesType = null;
445
+ }
446
+ return cssPropertiesType;
447
+ }
448
+ return {
449
+ analyzeFileTypes(filePath, source) {
450
+ const canonicalFilePath = canonicalPath2(filePath);
451
+ const belongsToWorkspace = workspaceProjectRoots.some(
452
+ (projectRoot) => canonicalFilePath === projectRoot || canonicalFilePath.startsWith(`${projectRoot}${path2.sep}`)
453
+ );
454
+ if (!belongsToWorkspace) {
455
+ return null;
456
+ }
457
+ workspaceService.updateFile(filePath, source);
458
+ const program = workspaceService.getProgram();
459
+ if (!program) return null;
460
+ const sourceFile = program.getSourceFile(filePath);
461
+ if (!sourceFile) return null;
462
+ const checker = program.getTypeChecker();
463
+ const cssProps = resolveCSSPropertiesType(checker, sourceFile);
464
+ const result = /* @__PURE__ */ new Map();
465
+ function visit(node) {
466
+ if ((ts2.isJsxOpeningElement(node) || ts2.isJsxSelfClosingElement(node)) && isCustomComponent(node.tagName)) {
467
+ const position = node.getStart(sourceFile);
468
+ const attrsType = checker.getContextualType(node.attributes);
469
+ let supportsStyle = false;
470
+ let supportsClassName = false;
471
+ if (attrsType) {
472
+ const classNameProp = attrsType.getProperty("className");
473
+ if (classNameProp) {
474
+ const propType = checker.getTypeOfSymbol(classNameProp);
475
+ supportsClassName = checker.isTypeAssignableTo(
476
+ checker.getStringType(),
477
+ propType
478
+ );
479
+ }
480
+ const styleProp = attrsType.getProperty("style");
481
+ if (styleProp && cssProps) {
482
+ const propType = checker.getTypeOfSymbol(styleProp);
483
+ supportsStyle = checker.isTypeAssignableTo(
484
+ cssProps,
485
+ propType
486
+ );
487
+ }
488
+ }
489
+ result.set(position, { supportsStyle, supportsClassName });
490
+ }
491
+ ts2.forEachChild(node, visit);
492
+ }
493
+ visit(sourceFile);
494
+ return result;
495
+ }
496
+ };
497
+ }
498
+
499
+ // index.ts
500
+ var DEFAULT_INCLUDE = ["**/*.tsx", "**/*.jsx"];
501
+ var DEFAULT_EXCLUDE = ["**/node_modules/**"];
502
+ var ROOT_META_NAME = "tempo-project-root";
503
+ function cleanModuleId(id) {
504
+ return id.replace(/[?#].*$/, "");
505
+ }
506
+ function resolveCanonicalRoot(root) {
507
+ try {
508
+ return realpathSync3.native(root);
509
+ } catch {
510
+ return root;
511
+ }
512
+ }
513
+ function hasRootMetaTag(html) {
514
+ return /<meta[^>]+name=["']tempo-project-root["'][^>]*>/i.test(html);
515
+ }
516
+ function isIntrinsicElement(tagName) {
517
+ return tagName.length > 0 && tagName[0] === tagName[0].toLowerCase();
518
+ }
519
+ function defaultComputeAnnotatedProps(info, filepath, modifiedtsMs) {
520
+ return {
521
+ "data-tempo-filepath": filepath,
522
+ "data-tempo-position": info.element.openingElement.start,
523
+ "data-tempo-modifiedts": modifiedtsMs
524
+ };
525
+ }
526
+ function tempoAnnotate(options = {}) {
527
+ const include = options.include === void 0 ? DEFAULT_INCLUDE : options.include;
528
+ const exclude = options.exclude === void 0 ? DEFAULT_EXCLUDE : options.exclude;
529
+ const filter = createFilter(include, exclude);
530
+ const computeAnnotatedProps = options.computeAnnotatedProps ?? defaultComputeAnnotatedProps;
531
+ let canonicalRoot = process.cwd();
532
+ let command = "serve";
533
+ let typeAnalyzer = null;
534
+ return {
535
+ name: "tempo-annotate",
536
+ enforce: "pre",
537
+ configResolved(resolvedConfig) {
538
+ command = resolvedConfig.command;
539
+ canonicalRoot = resolveCanonicalRoot(resolvedConfig.root);
540
+ if (command === "serve") {
541
+ typeAnalyzer = createTypeAnalyzer(canonicalRoot);
542
+ }
543
+ },
544
+ transformIndexHtml(html) {
545
+ if (command !== "serve") {
546
+ return;
547
+ }
548
+ if (hasRootMetaTag(html)) {
549
+ return;
550
+ }
551
+ return [
552
+ {
553
+ tag: "meta",
554
+ attrs: {
555
+ name: ROOT_META_NAME,
556
+ content: canonicalRoot
557
+ },
558
+ injectTo: "head"
559
+ }
560
+ ];
561
+ },
562
+ transform(source, id) {
563
+ if (command === "build") {
564
+ return null;
565
+ }
566
+ if (id.startsWith("\0")) {
567
+ return null;
568
+ }
569
+ const cleanId = cleanModuleId(id);
570
+ if (!filter(cleanId)) {
571
+ return null;
572
+ }
573
+ try {
574
+ const filepath = relative(canonicalRoot, cleanId);
575
+ const modifiedtsMs = Math.trunc(statSync2(cleanId).mtimeMs);
576
+ const typeInfo = typeAnalyzer?.analyzeFileTypes(cleanId, source);
577
+ const result = annotateFile(source, cleanId, (info) => {
578
+ const userProps = computeAnnotatedProps(info, filepath, modifiedtsMs);
579
+ if (isIntrinsicElement(info.tagName)) {
580
+ return userProps;
581
+ }
582
+ const support = typeInfo?.get(info.element.openingElement.start);
583
+ return {
584
+ ...userProps,
585
+ "data-tempo-supports-style": support?.supportsStyle ?? false,
586
+ "data-tempo-supports-classname": support?.supportsClassName ?? false
587
+ };
588
+ });
589
+ if (result.elementsAnnotated === 0) {
590
+ return null;
591
+ }
592
+ return {
593
+ code: result.annotatedSource,
594
+ map: null
595
+ };
596
+ } catch {
597
+ return null;
598
+ }
599
+ }
600
+ };
601
+ }
602
+ export {
603
+ createTypeAnalyzer,
604
+ tempoAnnotate
605
+ };
606
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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"]}
package/dist/page.d.ts ADDED
@@ -0,0 +1,27 @@
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 ADDED
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=page.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "tempo-sdk",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "description": "Tempo SDK — Vite plugin for JSX annotation and shared page/storyboard types",
6
+ "exports": {
7
+ ".": {
8
+ "import": "./dist/index.js",
9
+ "types": "./dist/index.d.ts"
10
+ }
11
+ },
12
+ "files": ["dist"],
13
+ "scripts": {
14
+ "build": "tsup",
15
+ "prepublishOnly": "pnpm build",
16
+ "test": "vitest run",
17
+ "test:watch": "vitest"
18
+ },
19
+ "dependencies": {
20
+ "@rollup/pluginutils": "^5.1.0",
21
+ "oxc-parser": "^0.110.0"
22
+ },
23
+ "peerDependencies": {
24
+ "react": "^18 || ^19",
25
+ "typescript": "^5.0.0",
26
+ "vite": "^5 || ^6 || ^7"
27
+ },
28
+ "devDependencies": {
29
+ "@modules/annotation": "workspace:*",
30
+ "@modules/typescript": "workspace:*",
31
+ "@types/node": "^25.0.8",
32
+ "@types/react": "catalog:",
33
+ "@vitejs/plugin-react": "catalog:",
34
+ "tsup": "^8.5.1",
35
+ "typescript": "catalog:",
36
+ "vite": "catalog:",
37
+ "vitest": "catalog:"
38
+ }
39
+ }