@vibe-agent-toolkit/resource-compiler 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/README.md +449 -0
  2. package/bin/vat-compile-resources +8 -0
  3. package/dist/cli/compile-command.d.ts +11 -0
  4. package/dist/cli/compile-command.d.ts.map +1 -0
  5. package/dist/cli/compile-command.js +34 -0
  6. package/dist/cli/compile-command.js.map +1 -0
  7. package/dist/cli/compile-utils.d.ts +41 -0
  8. package/dist/cli/compile-utils.d.ts.map +1 -0
  9. package/dist/cli/compile-utils.js +51 -0
  10. package/dist/cli/compile-utils.js.map +1 -0
  11. package/dist/cli/generate-types-command.d.ts +11 -0
  12. package/dist/cli/generate-types-command.d.ts.map +1 -0
  13. package/dist/cli/generate-types-command.js +107 -0
  14. package/dist/cli/generate-types-command.js.map +1 -0
  15. package/dist/cli/index.d.ts +17 -0
  16. package/dist/cli/index.d.ts.map +1 -0
  17. package/dist/cli/index.js +34 -0
  18. package/dist/cli/index.js.map +1 -0
  19. package/dist/cli/watch-command.d.ts +11 -0
  20. package/dist/cli/watch-command.d.ts.map +1 -0
  21. package/dist/cli/watch-command.js +88 -0
  22. package/dist/cli/watch-command.js.map +1 -0
  23. package/dist/compiler/dts-generator.d.ts +29 -0
  24. package/dist/compiler/dts-generator.d.ts.map +1 -0
  25. package/dist/compiler/dts-generator.js +133 -0
  26. package/dist/compiler/dts-generator.js.map +1 -0
  27. package/dist/compiler/index.d.ts +9 -0
  28. package/dist/compiler/index.d.ts.map +1 -0
  29. package/dist/compiler/index.js +9 -0
  30. package/dist/compiler/index.js.map +1 -0
  31. package/dist/compiler/javascript-generator.d.ts +22 -0
  32. package/dist/compiler/javascript-generator.d.ts.map +1 -0
  33. package/dist/compiler/javascript-generator.js +106 -0
  34. package/dist/compiler/javascript-generator.js.map +1 -0
  35. package/dist/compiler/markdown-compiler.d.ts +30 -0
  36. package/dist/compiler/markdown-compiler.d.ts.map +1 -0
  37. package/dist/compiler/markdown-compiler.js +125 -0
  38. package/dist/compiler/markdown-compiler.js.map +1 -0
  39. package/dist/compiler/markdown-parser.d.ts +32 -0
  40. package/dist/compiler/markdown-parser.d.ts.map +1 -0
  41. package/dist/compiler/markdown-parser.js +126 -0
  42. package/dist/compiler/markdown-parser.js.map +1 -0
  43. package/dist/compiler/types.d.ts +71 -0
  44. package/dist/compiler/types.d.ts.map +1 -0
  45. package/dist/compiler/types.js +5 -0
  46. package/dist/compiler/types.js.map +1 -0
  47. package/dist/index.d.ts +7 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +7 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/language-service/completions.d.ts +27 -0
  52. package/dist/language-service/completions.d.ts.map +1 -0
  53. package/dist/language-service/completions.js +147 -0
  54. package/dist/language-service/completions.js.map +1 -0
  55. package/dist/language-service/definitions.d.ts +14 -0
  56. package/dist/language-service/definitions.d.ts.map +1 -0
  57. package/dist/language-service/definitions.js +140 -0
  58. package/dist/language-service/definitions.js.map +1 -0
  59. package/dist/language-service/diagnostics.d.ts +13 -0
  60. package/dist/language-service/diagnostics.d.ts.map +1 -0
  61. package/dist/language-service/diagnostics.js +169 -0
  62. package/dist/language-service/diagnostics.js.map +1 -0
  63. package/dist/language-service/hover.d.ts +15 -0
  64. package/dist/language-service/hover.d.ts.map +1 -0
  65. package/dist/language-service/hover.js +125 -0
  66. package/dist/language-service/hover.js.map +1 -0
  67. package/dist/language-service/index.d.ts +26 -0
  68. package/dist/language-service/index.d.ts.map +1 -0
  69. package/dist/language-service/index.js +30 -0
  70. package/dist/language-service/index.js.map +1 -0
  71. package/dist/language-service/markdown-cache.d.ts +44 -0
  72. package/dist/language-service/markdown-cache.d.ts.map +1 -0
  73. package/dist/language-service/markdown-cache.js +77 -0
  74. package/dist/language-service/markdown-cache.js.map +1 -0
  75. package/dist/language-service/plugin.d.ts +15 -0
  76. package/dist/language-service/plugin.d.ts.map +1 -0
  77. package/dist/language-service/plugin.js +51 -0
  78. package/dist/language-service/plugin.js.map +1 -0
  79. package/dist/language-service/utils.d.ts +173 -0
  80. package/dist/language-service/utils.d.ts.map +1 -0
  81. package/dist/language-service/utils.js +341 -0
  82. package/dist/language-service/utils.js.map +1 -0
  83. package/dist/transformer/ast-helpers.d.ts +35 -0
  84. package/dist/transformer/ast-helpers.d.ts.map +1 -0
  85. package/dist/transformer/ast-helpers.js +153 -0
  86. package/dist/transformer/ast-helpers.js.map +1 -0
  87. package/dist/transformer/declaration-generator.d.ts +47 -0
  88. package/dist/transformer/declaration-generator.d.ts.map +1 -0
  89. package/dist/transformer/declaration-generator.js +53 -0
  90. package/dist/transformer/declaration-generator.js.map +1 -0
  91. package/dist/transformer/import-detector.d.ts +62 -0
  92. package/dist/transformer/import-detector.d.ts.map +1 -0
  93. package/dist/transformer/import-detector.js +115 -0
  94. package/dist/transformer/import-detector.js.map +1 -0
  95. package/dist/transformer/index.d.ts +11 -0
  96. package/dist/transformer/index.d.ts.map +1 -0
  97. package/dist/transformer/index.js +11 -0
  98. package/dist/transformer/index.js.map +1 -0
  99. package/dist/transformer/module-generator.d.ts +29 -0
  100. package/dist/transformer/module-generator.d.ts.map +1 -0
  101. package/dist/transformer/module-generator.js +48 -0
  102. package/dist/transformer/module-generator.js.map +1 -0
  103. package/dist/transformer/path-resolver.d.ts +32 -0
  104. package/dist/transformer/path-resolver.d.ts.map +1 -0
  105. package/dist/transformer/path-resolver.js +112 -0
  106. package/dist/transformer/path-resolver.js.map +1 -0
  107. package/dist/transformer/transformer.d.ts +46 -0
  108. package/dist/transformer/transformer.d.ts.map +1 -0
  109. package/dist/transformer/transformer.js +89 -0
  110. package/dist/transformer/transformer.js.map +1 -0
  111. package/dist/utils/copy-resources.d.ts +54 -0
  112. package/dist/utils/copy-resources.d.ts.map +1 -0
  113. package/dist/utils/copy-resources.js +77 -0
  114. package/dist/utils/copy-resources.js.map +1 -0
  115. package/dist/utils/index.d.ts +6 -0
  116. package/dist/utils/index.d.ts.map +1 -0
  117. package/dist/utils/index.js +5 -0
  118. package/dist/utils/index.js.map +1 -0
  119. package/package.json +84 -0
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Cache for parsed markdown resources with mtime-based invalidation
3
+ * Prevents re-parsing markdown files on every IDE operation
4
+ */
5
+ import { statSync } from 'node:fs';
6
+ /**
7
+ * Global cache of parsed markdown resources
8
+ * Key: absolute file path
9
+ * Value: cache entry with resource and mtime
10
+ */
11
+ const cache = new Map();
12
+ /**
13
+ * Get a cached markdown resource or load it using the provided loader
14
+ * Invalidates cache if file has been modified since last load
15
+ *
16
+ * @param filePath - Absolute path to markdown file
17
+ * @param loader - Function to load and parse the markdown file
18
+ * @returns Parsed markdown resource
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * const resource = getMarkdownResource('/path/to/file.md', () => {
23
+ * const content = readFileSync('/path/to/file.md', 'utf-8');
24
+ * return parseMarkdown(content);
25
+ * });
26
+ * ```
27
+ */
28
+ export function getMarkdownResource(filePath, loader) {
29
+ // Get current file modification time
30
+ let currentMtime;
31
+ try {
32
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- Using validated path from TypeScript Language Service
33
+ const stats = statSync(filePath);
34
+ currentMtime = stats.mtimeMs;
35
+ }
36
+ catch {
37
+ // File doesn't exist or can't be accessed - always reload
38
+ currentMtime = 0;
39
+ }
40
+ // Check if cached version is still valid
41
+ const cached = cache.get(filePath);
42
+ if (cached?.mtime === currentMtime) {
43
+ return cached.resource;
44
+ }
45
+ // Load and cache the resource
46
+ const resource = loader();
47
+ cache.set(filePath, { resource, mtime: currentMtime });
48
+ return resource;
49
+ }
50
+ /**
51
+ * Clear the entire cache
52
+ * Useful for testing or manual cache invalidation
53
+ */
54
+ export function clearCache() {
55
+ cache.clear();
56
+ }
57
+ /**
58
+ * Remove a specific file from the cache
59
+ * Useful when a file is deleted or moved
60
+ *
61
+ * @param filePath - Absolute path to markdown file
62
+ */
63
+ export function invalidateFile(filePath) {
64
+ cache.delete(filePath);
65
+ }
66
+ /**
67
+ * Get cache statistics for debugging
68
+ *
69
+ * @returns Object with cache size and list of cached files
70
+ */
71
+ export function getCacheStats() {
72
+ return {
73
+ size: cache.size,
74
+ files: [...cache.keys()],
75
+ };
76
+ }
77
+ //# sourceMappingURL=markdown-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown-cache.js","sourceRoot":"","sources":["../../src/language-service/markdown-cache.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAWnC;;;;GAIG;AACH,MAAM,KAAK,GAAG,IAAI,GAAG,EAAsB,CAAC;AAE5C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,mBAAmB,CACjC,QAAgB,EAChB,MAA8B;IAE9B,qCAAqC;IACrC,IAAI,YAAoB,CAAC;IACzB,IAAI,CAAC;QACH,4HAA4H;QAC5H,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACjC,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,0DAA0D;QAC1D,YAAY,GAAG,CAAC,CAAC;IACnB,CAAC;IAED,yCAAyC;IACzC,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACnC,IAAI,MAAM,EAAE,KAAK,KAAK,YAAY,EAAE,CAAC;QACnC,OAAO,MAAM,CAAC,QAAQ,CAAC;IACzB,CAAC;IAED,8BAA8B;IAC9B,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC;IAC1B,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;IAEvD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU;IACxB,KAAK,CAAC,KAAK,EAAE,CAAC;AAChB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC7C,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AACzB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa;IAC3B,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,KAAK,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;KACzB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * TypeScript Language Service Plugin for markdown imports
3
+ * Provides autocomplete, go-to-definition, diagnostics, and hover support
4
+ */
5
+ /**
6
+ * Plugin initialization function
7
+ * Called by TypeScript when loading the plugin
8
+ */
9
+ declare function init(modules: {
10
+ typescript: any;
11
+ }): {
12
+ create: (info: any) => any;
13
+ };
14
+ export default init;
15
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/language-service/plugin.ts"],"names":[],"mappings":"AAAA;;;GAGG;AASH;;;GAGG;AACH,iBAAS,IAAI,CAAC,OAAO,EAAE;IAAE,UAAU,EAAE,GAAG,CAAA;CAAE;mBAMlB,GAAG,KAAG,GAAG;EA8ChC;AAED,eAAe,IAAI,CAAC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * TypeScript Language Service Plugin for markdown imports
3
+ * Provides autocomplete, go-to-definition, diagnostics, and hover support
4
+ */
5
+ /* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types -- TypeScript Language Service plugin API requires any */
6
+ import { enhanceCompletions } from './completions.js';
7
+ import { enhanceDefinitions } from './definitions.js';
8
+ import { enhanceDiagnostics } from './diagnostics.js';
9
+ import { enhanceHover } from './hover.js';
10
+ /**
11
+ * Plugin initialization function
12
+ * Called by TypeScript when loading the plugin
13
+ */
14
+ function init(modules) {
15
+ const ts = modules.typescript;
16
+ /**
17
+ * Create the Language Service plugin
18
+ */
19
+ function create(info) {
20
+ const logger = info.project.projectService.logger;
21
+ logger.info('markdown-import-plugin: Plugin initialized');
22
+ // Get the original Language Service
23
+ const languageService = info.languageService;
24
+ // Create proxy to intercept Language Service methods
25
+ const proxy = {
26
+ ...languageService,
27
+ // Enhance autocomplete with markdown fragment suggestions
28
+ getCompletionsAtPosition(fileName, position, options) {
29
+ const prior = languageService.getCompletionsAtPosition(fileName, position, options);
30
+ return enhanceCompletions(ts, info, prior, fileName, position);
31
+ },
32
+ // Enhance go-to-definition for markdown imports and fragments
33
+ getDefinitionAtPosition(fileName, position) {
34
+ return enhanceDefinitions(ts, info, fileName, position);
35
+ },
36
+ // Enhance diagnostics to check markdown file/fragment existence
37
+ getSemanticDiagnostics(fileName) {
38
+ return enhanceDiagnostics(ts, info, fileName);
39
+ },
40
+ // Enhance hover tooltips with markdown content
41
+ getQuickInfoAtPosition(fileName, position) {
42
+ const prior = languageService.getQuickInfoAtPosition(fileName, position);
43
+ return enhanceHover(ts, info, prior, fileName, position);
44
+ },
45
+ };
46
+ return proxy;
47
+ }
48
+ return { create };
49
+ }
50
+ export default init;
51
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/language-service/plugin.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,iKAAiK;AAEjK,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C;;;GAGG;AACH,SAAS,IAAI,CAAC,OAA4B;IACxC,MAAM,EAAE,GAAG,OAAO,CAAC,UAAU,CAAC;IAE9B;;OAEG;IACH,SAAS,MAAM,CAAC,IAAS;QACvB,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC;QAElD,MAAM,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;QAE1D,oCAAoC;QACpC,MAAM,eAAe,GAAG,IAAI,CAAC,eAAe,CAAC;QAE7C,qDAAqD;QACrD,MAAM,KAAK,GAAQ;YACjB,GAAG,eAAe;YAElB,0DAA0D;YAC1D,wBAAwB,CACtB,QAAgB,EAChB,QAAgB,EAChB,OAAY;gBAEZ,MAAM,KAAK,GAAG,eAAe,CAAC,wBAAwB,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;gBACpF,OAAO,kBAAkB,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YACjE,CAAC;YAED,8DAA8D;YAC9D,uBAAuB,CACrB,QAAgB,EAChB,QAAgB;gBAEhB,OAAO,kBAAkB,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC1D,CAAC;YAED,gEAAgE;YAChE,sBAAsB,CAAC,QAAgB;gBACrC,OAAO,kBAAkB,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;YAChD,CAAC;YAED,+CAA+C;YAC/C,sBAAsB,CAAC,QAAgB,EAAE,QAAgB;gBACvD,MAAM,KAAK,GAAG,eAAe,CAAC,sBAAsB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;gBACzE,OAAO,YAAY,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC3D,CAAC;SACF,CAAC;QAEF,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,CAAC;AACpB,CAAC;AAED,eAAe,IAAI,CAAC"}
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Utility functions for TypeScript AST traversal and markdown path resolution
3
+ */
4
+ import type { MarkdownResource } from '../compiler/types.js';
5
+ /**
6
+ * Find the AST node at a specific position in the source file
7
+ *
8
+ * @param ts - TypeScript module
9
+ * @param sourceFile - Source file to search
10
+ * @param position - Character position in the file
11
+ * @returns The deepest node containing the position, or undefined
12
+ */
13
+ export declare function findNodeAtPosition(ts: any, sourceFile: any, position: number): any;
14
+ /**
15
+ * Extract markdown file path from an import declaration or expression
16
+ *
17
+ * @param ts - TypeScript module
18
+ * @param node - AST node to check
19
+ * @returns Markdown file path if found, null otherwise
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * // For: import Core from './core.md';
24
+ * getMarkdownPathFromImport(node) // → './core.md'
25
+ *
26
+ * // For: const x = 5;
27
+ * getMarkdownPathFromImport(node) // → null
28
+ * ```
29
+ */
30
+ export declare function getMarkdownPathFromImport(ts: any, node: any): string | null;
31
+ /**
32
+ * Extract markdown file path and fragment property from a property access expression
33
+ *
34
+ * @param ts - TypeScript module
35
+ * @param node - AST node to check
36
+ * @param sourceFile - Source file containing the node
37
+ * @returns Object with markdown path and fragment name, or null
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * // For: Core.fragments.purposeDriven
42
+ * getMarkdownPathFromExpression(node, sourceFile)
43
+ * // → { markdownPath: './core.md', fragmentName: 'purposeDriven' }
44
+ * ```
45
+ */
46
+ export declare function getMarkdownPathFromExpression(ts: any, node: any, sourceFile: any): {
47
+ markdownPath: string;
48
+ fragmentName: string;
49
+ } | null;
50
+ /**
51
+ * Resolve a markdown import path to an absolute file path
52
+ *
53
+ * @param modulePath - Import path from TypeScript
54
+ * @param containingFile - Absolute path of the file doing the import
55
+ * @param compilerOptions - TypeScript compiler options
56
+ * @returns Absolute path to markdown file, or null if not found
57
+ */
58
+ export declare function resolveMarkdownPath(modulePath: string, containingFile: string, compilerOptions: any): string | null;
59
+ /**
60
+ * Load and parse a markdown file with caching
61
+ *
62
+ * @param filePath - Absolute path to markdown file
63
+ * @returns Parsed markdown resource
64
+ */
65
+ export declare function loadMarkdownResource(filePath: string): MarkdownResource;
66
+ /**
67
+ * Find the position (line/character) of a heading in markdown content
68
+ *
69
+ * @param content - Markdown file content
70
+ * @param heading - Heading text to find (without ## prefix)
71
+ * @returns Object with line and character position, or null if not found
72
+ */
73
+ export declare function findHeadingPosition(content: string, heading: string): {
74
+ line: number;
75
+ character: number;
76
+ } | null;
77
+ /**
78
+ * Common pattern: Get source file and node at position
79
+ * Returns null if source file or node not found
80
+ *
81
+ * @param ts - TypeScript module
82
+ * @param info - Plugin create info
83
+ * @param fileName - File being edited
84
+ * @param position - Cursor position
85
+ * @returns Object with sourceFile and node, or null
86
+ */
87
+ export declare function getSourceFileAndNode(ts: any, info: any, fileName: string, position: number): {
88
+ sourceFile: any;
89
+ node: any;
90
+ } | null;
91
+ /**
92
+ * Common pattern: Resolve markdown path and load resource
93
+ * Returns null if path cannot be resolved or file doesn't exist
94
+ *
95
+ * @param info - Plugin create info
96
+ * @param markdownPath - Import path to markdown file
97
+ * @param containingFile - File containing the import/access
98
+ * @returns Object with absolutePath and resource, or null
99
+ */
100
+ export declare function resolveAndLoadMarkdown(info: any, markdownPath: string, containingFile: string): {
101
+ absolutePath: string;
102
+ resource: MarkdownResource;
103
+ } | null;
104
+ /**
105
+ * Common pattern: Resolve markdown path, load resource, and find fragment
106
+ * Returns null if resolution fails or fragment doesn't exist
107
+ * This is the shared logic for fragment-based operations (hover, diagnostics, etc.)
108
+ *
109
+ * @param info - Plugin create info
110
+ * @param markdownPath - Import path to markdown file
111
+ * @param fragmentName - camelCase fragment property name
112
+ * @param containingFile - File containing the fragment access
113
+ * @returns Object with absolutePath, resource, and fragment, or null
114
+ */
115
+ export declare function resolveAndLoadFragment(info: any, markdownPath: string, fragmentName: string, containingFile: string): {
116
+ absolutePath: string;
117
+ resource: MarkdownResource;
118
+ fragment: any;
119
+ } | null;
120
+ /**
121
+ * Higher-order function for Language Service enhancement operations
122
+ * Encapsulates common pattern: get source file/node, try operation, catch errors
123
+ *
124
+ * @param ts - TypeScript module
125
+ * @param info - Plugin create info
126
+ * @param prior - Original result from TypeScript
127
+ * @param fileName - File being edited
128
+ * @param position - Cursor position
129
+ * @param operationName - Name for logging (e.g., "hover", "definitions")
130
+ * @param enhancer - Function to enhance the result
131
+ * @returns Enhanced result, or original if no enhancements needed
132
+ */
133
+ export declare function enhanceLanguageServiceOperation(ts: any, info: any, prior: any, fileName: string, position: number, operationName: string, enhancer: (context: {
134
+ sourceFile: any;
135
+ node: any;
136
+ }) => any): any;
137
+ /**
138
+ * Get the property name node from a node
139
+ * If the node is a PropertyAccessExpression, returns its name property
140
+ * Otherwise returns the node itself
141
+ *
142
+ * @param ts - TypeScript module
143
+ * @param node - AST node (property access or identifier)
144
+ * @returns The property name node for precise text spans
145
+ */
146
+ export declare function getPropertyNameNode(ts: any, node: any): any;
147
+ /**
148
+ * Options for withFragmentResolution
149
+ */
150
+ interface FragmentResolutionOptions {
151
+ info: any;
152
+ markdownPath: string;
153
+ fragmentName: string;
154
+ containingFile: string;
155
+ node: any;
156
+ operationName: string;
157
+ onSuccess: (result: {
158
+ absolutePath: string;
159
+ resource: MarkdownResource;
160
+ fragment: any;
161
+ }) => any;
162
+ onNotFound?: () => any;
163
+ }
164
+ /**
165
+ * Higher-order function for fragment-based operations (hover, diagnostics, etc.)
166
+ * Encapsulates common pattern: resolve markdown path, load resource, find fragment, handle errors
167
+ *
168
+ * @param options - Configuration options
169
+ * @returns Result from onSuccess or onNotFound callback
170
+ */
171
+ export declare function withFragmentResolution(options: FragmentResolutionOptions): any;
172
+ export {};
173
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/language-service/utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,341 @@
1
+ /**
2
+ * Utility functions for TypeScript AST traversal and markdown path resolution
3
+ */
4
+ /* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types, sonarjs/cognitive-complexity, security/detect-non-literal-regexp, import/order -- TypeScript Language Service plugin API requires any and complex AST traversal */
5
+ import { readFileSync } from 'node:fs';
6
+ import { parseMarkdown } from '../compiler/markdown-parser.js';
7
+ import { resolveMarkdownPath as resolveMarkdownPathFromTransformer } from '../transformer/path-resolver.js';
8
+ import { getMarkdownResource } from './markdown-cache.js';
9
+ /**
10
+ * Find the AST node at a specific position in the source file
11
+ *
12
+ * @param ts - TypeScript module
13
+ * @param sourceFile - Source file to search
14
+ * @param position - Character position in the file
15
+ * @returns The deepest node containing the position, or undefined
16
+ */
17
+ export function findNodeAtPosition(ts, sourceFile, position) {
18
+ let foundNode;
19
+ function visit(node) {
20
+ if (position >= node.pos && position < node.end) {
21
+ foundNode = node;
22
+ ts.forEachChild(node, visit);
23
+ }
24
+ }
25
+ visit(sourceFile);
26
+ return foundNode;
27
+ }
28
+ /**
29
+ * Extract markdown file path from an import declaration or expression
30
+ *
31
+ * @param ts - TypeScript module
32
+ * @param node - AST node to check
33
+ * @returns Markdown file path if found, null otherwise
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * // For: import Core from './core.md';
38
+ * getMarkdownPathFromImport(node) // → './core.md'
39
+ *
40
+ * // For: const x = 5;
41
+ * getMarkdownPathFromImport(node) // → null
42
+ * ```
43
+ */
44
+ export function getMarkdownPathFromImport(ts, node) {
45
+ // Check if it's an import declaration
46
+ if (ts.isImportDeclaration(node)) {
47
+ const moduleSpecifier = node.moduleSpecifier;
48
+ if (ts.isStringLiteral(moduleSpecifier) && moduleSpecifier.text.endsWith('.md')) {
49
+ return moduleSpecifier.text;
50
+ }
51
+ }
52
+ // Check if it's a dynamic import
53
+ if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword) {
54
+ const argument = node.arguments[0];
55
+ if (argument && ts.isStringLiteral(argument) && argument.text.endsWith('.md')) {
56
+ return argument.text;
57
+ }
58
+ }
59
+ return null;
60
+ }
61
+ /**
62
+ * Extract markdown file path and fragment property from a property access expression
63
+ *
64
+ * @param ts - TypeScript module
65
+ * @param node - AST node to check
66
+ * @param sourceFile - Source file containing the node
67
+ * @returns Object with markdown path and fragment name, or null
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * // For: Core.fragments.purposeDriven
72
+ * getMarkdownPathFromExpression(node, sourceFile)
73
+ * // → { markdownPath: './core.md', fragmentName: 'purposeDriven' }
74
+ * ```
75
+ */
76
+ export function getMarkdownPathFromExpression(ts, node, sourceFile) {
77
+ // Look for patterns like: Core.fragments.purposeDriven
78
+ if (!ts.isPropertyAccessExpression(node)) {
79
+ return null;
80
+ }
81
+ // Check if accessing a fragment property
82
+ const fragmentName = node.name.text;
83
+ // Check if parent is `.fragments`
84
+ const parent = node.expression;
85
+ if (!ts.isPropertyAccessExpression(parent)) {
86
+ return null;
87
+ }
88
+ if (parent.name.text !== 'fragments') {
89
+ return null;
90
+ }
91
+ // Get the imported identifier (e.g., 'Core')
92
+ const identifier = parent.expression;
93
+ if (!ts.isIdentifier(identifier)) {
94
+ return null;
95
+ }
96
+ // Find the import declaration for this identifier
97
+ const importPath = findImportPathForIdentifier(ts, identifier.text, sourceFile);
98
+ if (!importPath?.endsWith('.md')) {
99
+ return null;
100
+ }
101
+ return { markdownPath: importPath, fragmentName };
102
+ }
103
+ /**
104
+ * Find the import path for a given identifier in the source file
105
+ *
106
+ * @param ts - TypeScript module
107
+ * @param identifierName - Name of the imported identifier
108
+ * @param sourceFile - Source file to search
109
+ * @returns Import path string, or null if not found
110
+ */
111
+ function findImportPathForIdentifier(ts, identifierName, sourceFile) {
112
+ let importPath = null;
113
+ function visit(node) {
114
+ if (importPath) {
115
+ return;
116
+ }
117
+ // Check import declarations
118
+ if (ts.isImportDeclaration(node)) {
119
+ const clause = node.importClause;
120
+ if (!clause) {
121
+ return;
122
+ }
123
+ const moduleSpecifier = node.moduleSpecifier;
124
+ if (!ts.isStringLiteral(moduleSpecifier)) {
125
+ return;
126
+ }
127
+ // Check default import
128
+ const defaultName = clause.name;
129
+ if (defaultName?.text === identifierName) {
130
+ importPath = moduleSpecifier.text;
131
+ return;
132
+ }
133
+ // Check named imports
134
+ const namedBindings = clause.namedBindings;
135
+ if (namedBindings?.kind === ts.SyntaxKind.NamedImports) {
136
+ for (const element of namedBindings.elements) {
137
+ if (element.name.text === identifierName) {
138
+ importPath = moduleSpecifier.text;
139
+ return;
140
+ }
141
+ }
142
+ }
143
+ }
144
+ ts.forEachChild(node, visit);
145
+ }
146
+ visit(sourceFile);
147
+ return importPath;
148
+ }
149
+ /**
150
+ * Resolve a markdown import path to an absolute file path
151
+ *
152
+ * @param modulePath - Import path from TypeScript
153
+ * @param containingFile - Absolute path of the file doing the import
154
+ * @param compilerOptions - TypeScript compiler options
155
+ * @returns Absolute path to markdown file, or null if not found
156
+ */
157
+ export function resolveMarkdownPath(modulePath, containingFile, compilerOptions) {
158
+ return resolveMarkdownPathFromTransformer(modulePath, containingFile, compilerOptions);
159
+ }
160
+ /**
161
+ * Load and parse a markdown file with caching
162
+ *
163
+ * @param filePath - Absolute path to markdown file
164
+ * @returns Parsed markdown resource
165
+ */
166
+ export function loadMarkdownResource(filePath) {
167
+ return getMarkdownResource(filePath, () => {
168
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- Using validated path from TypeScript Language Service
169
+ const content = readFileSync(filePath, 'utf-8');
170
+ return parseMarkdown(content);
171
+ });
172
+ }
173
+ /**
174
+ * Find the position (line/character) of a heading in markdown content
175
+ *
176
+ * @param content - Markdown file content
177
+ * @param heading - Heading text to find (without ## prefix)
178
+ * @returns Object with line and character position, or null if not found
179
+ */
180
+ export function findHeadingPosition(content, heading) {
181
+ const lines = content.split('\n');
182
+ const headingPattern = new RegExp(String.raw `^##\s+${escapeRegex(heading)}\s*$`);
183
+ for (const [i, line] of lines.entries()) {
184
+ if (line && headingPattern.test(line)) {
185
+ return { line: i, character: 0 };
186
+ }
187
+ }
188
+ return null;
189
+ }
190
+ /**
191
+ * Escape special regex characters in a string
192
+ *
193
+ * @param str - String to escape
194
+ * @returns Escaped string safe for use in RegExp
195
+ */
196
+ function escapeRegex(str) {
197
+ return str.replaceAll(/[$()*+.?[\\\]^{|}]/g, String.raw `\$&`);
198
+ }
199
+ /**
200
+ * Common pattern: Get source file and node at position
201
+ * Returns null if source file or node not found
202
+ *
203
+ * @param ts - TypeScript module
204
+ * @param info - Plugin create info
205
+ * @param fileName - File being edited
206
+ * @param position - Cursor position
207
+ * @returns Object with sourceFile and node, or null
208
+ */
209
+ export function getSourceFileAndNode(ts, info, fileName, position) {
210
+ const program = info.languageService.getProgram();
211
+ if (!program) {
212
+ return null;
213
+ }
214
+ const sourceFile = program.getSourceFile(fileName);
215
+ if (!sourceFile) {
216
+ return null;
217
+ }
218
+ const node = findNodeAtPosition(ts, sourceFile, position);
219
+ if (!node) {
220
+ return null;
221
+ }
222
+ return { sourceFile, node };
223
+ }
224
+ /**
225
+ * Common pattern: Resolve markdown path and load resource
226
+ * Returns null if path cannot be resolved or file doesn't exist
227
+ *
228
+ * @param info - Plugin create info
229
+ * @param markdownPath - Import path to markdown file
230
+ * @param containingFile - File containing the import/access
231
+ * @returns Object with absolutePath and resource, or null
232
+ */
233
+ export function resolveAndLoadMarkdown(info, markdownPath, containingFile) {
234
+ const compilerOptions = info.project.getCompilerOptions();
235
+ const absolutePath = resolveMarkdownPath(markdownPath, containingFile, compilerOptions);
236
+ if (!absolutePath) {
237
+ return null;
238
+ }
239
+ const resource = loadMarkdownResource(absolutePath);
240
+ return { absolutePath, resource };
241
+ }
242
+ /**
243
+ * Common pattern: Resolve markdown path, load resource, and find fragment
244
+ * Returns null if resolution fails or fragment doesn't exist
245
+ * This is the shared logic for fragment-based operations (hover, diagnostics, etc.)
246
+ *
247
+ * @param info - Plugin create info
248
+ * @param markdownPath - Import path to markdown file
249
+ * @param fragmentName - camelCase fragment property name
250
+ * @param containingFile - File containing the fragment access
251
+ * @returns Object with absolutePath, resource, and fragment, or null
252
+ */
253
+ export function resolveAndLoadFragment(info, markdownPath, fragmentName, containingFile) {
254
+ // Resolve markdown path and load resource
255
+ const result = resolveAndLoadMarkdown(info, markdownPath, containingFile);
256
+ if (!result) {
257
+ return null;
258
+ }
259
+ const { absolutePath, resource } = result;
260
+ // Convert camelCase property name to heading text
261
+ const headingText = fragmentName
262
+ .replaceAll(/([A-Z])/g, ' $1')
263
+ .trim()
264
+ .split(' ')
265
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
266
+ .join(' ');
267
+ // Find the fragment in the markdown resource
268
+ const fragment = resource.fragments.find((frag) => frag.heading === headingText);
269
+ if (!fragment) {
270
+ return null;
271
+ }
272
+ return { absolutePath, resource, fragment };
273
+ }
274
+ /**
275
+ * Higher-order function for Language Service enhancement operations
276
+ * Encapsulates common pattern: get source file/node, try operation, catch errors
277
+ *
278
+ * @param ts - TypeScript module
279
+ * @param info - Plugin create info
280
+ * @param prior - Original result from TypeScript
281
+ * @param fileName - File being edited
282
+ * @param position - Cursor position
283
+ * @param operationName - Name for logging (e.g., "hover", "definitions")
284
+ * @param enhancer - Function to enhance the result
285
+ * @returns Enhanced result, or original if no enhancements needed
286
+ */
287
+ export function enhanceLanguageServiceOperation(ts, info, prior, fileName, position, operationName, enhancer) {
288
+ try {
289
+ // Get the source file and find the node at cursor
290
+ const context = getSourceFileAndNode(ts, info, fileName, position);
291
+ if (!context) {
292
+ return prior;
293
+ }
294
+ // Call the specific enhancement logic
295
+ const enhanced = enhancer(context);
296
+ return enhanced ?? prior;
297
+ }
298
+ catch (error) {
299
+ // Log error and return original result
300
+ info.project.projectService.logger.info(`markdown-import-plugin: Error enhancing ${operationName}: ${String(error)}`);
301
+ return prior;
302
+ }
303
+ }
304
+ /**
305
+ * Get the property name node from a node
306
+ * If the node is a PropertyAccessExpression, returns its name property
307
+ * Otherwise returns the node itself
308
+ *
309
+ * @param ts - TypeScript module
310
+ * @param node - AST node (property access or identifier)
311
+ * @returns The property name node for precise text spans
312
+ */
313
+ export function getPropertyNameNode(ts, node) {
314
+ if (ts.isPropertyAccessExpression(node)) {
315
+ return node.name;
316
+ }
317
+ return node;
318
+ }
319
+ /**
320
+ * Higher-order function for fragment-based operations (hover, diagnostics, etc.)
321
+ * Encapsulates common pattern: resolve markdown path, load resource, find fragment, handle errors
322
+ *
323
+ * @param options - Configuration options
324
+ * @returns Result from onSuccess or onNotFound callback
325
+ */
326
+ export function withFragmentResolution(options) {
327
+ const { info, markdownPath, fragmentName, containingFile, operationName, onSuccess, onNotFound } = options;
328
+ try {
329
+ // Resolve markdown path, load resource, and find fragment
330
+ const result = resolveAndLoadFragment(info, markdownPath, fragmentName, containingFile);
331
+ if (!result) {
332
+ return onNotFound ? onNotFound() : undefined;
333
+ }
334
+ return onSuccess(result);
335
+ }
336
+ catch (error) {
337
+ info.project.projectService.logger.info(`markdown-import-plugin: Error during ${operationName}: ${String(error)}`);
338
+ return undefined;
339
+ }
340
+ }
341
+ //# sourceMappingURL=utils.js.map