@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.
- package/README.md +449 -0
- package/bin/vat-compile-resources +8 -0
- package/dist/cli/compile-command.d.ts +11 -0
- package/dist/cli/compile-command.d.ts.map +1 -0
- package/dist/cli/compile-command.js +34 -0
- package/dist/cli/compile-command.js.map +1 -0
- package/dist/cli/compile-utils.d.ts +41 -0
- package/dist/cli/compile-utils.d.ts.map +1 -0
- package/dist/cli/compile-utils.js +51 -0
- package/dist/cli/compile-utils.js.map +1 -0
- package/dist/cli/generate-types-command.d.ts +11 -0
- package/dist/cli/generate-types-command.d.ts.map +1 -0
- package/dist/cli/generate-types-command.js +107 -0
- package/dist/cli/generate-types-command.js.map +1 -0
- package/dist/cli/index.d.ts +17 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +34 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/watch-command.d.ts +11 -0
- package/dist/cli/watch-command.d.ts.map +1 -0
- package/dist/cli/watch-command.js +88 -0
- package/dist/cli/watch-command.js.map +1 -0
- package/dist/compiler/dts-generator.d.ts +29 -0
- package/dist/compiler/dts-generator.d.ts.map +1 -0
- package/dist/compiler/dts-generator.js +133 -0
- package/dist/compiler/dts-generator.js.map +1 -0
- package/dist/compiler/index.d.ts +9 -0
- package/dist/compiler/index.d.ts.map +1 -0
- package/dist/compiler/index.js +9 -0
- package/dist/compiler/index.js.map +1 -0
- package/dist/compiler/javascript-generator.d.ts +22 -0
- package/dist/compiler/javascript-generator.d.ts.map +1 -0
- package/dist/compiler/javascript-generator.js +106 -0
- package/dist/compiler/javascript-generator.js.map +1 -0
- package/dist/compiler/markdown-compiler.d.ts +30 -0
- package/dist/compiler/markdown-compiler.d.ts.map +1 -0
- package/dist/compiler/markdown-compiler.js +125 -0
- package/dist/compiler/markdown-compiler.js.map +1 -0
- package/dist/compiler/markdown-parser.d.ts +32 -0
- package/dist/compiler/markdown-parser.d.ts.map +1 -0
- package/dist/compiler/markdown-parser.js +126 -0
- package/dist/compiler/markdown-parser.js.map +1 -0
- package/dist/compiler/types.d.ts +71 -0
- package/dist/compiler/types.d.ts.map +1 -0
- package/dist/compiler/types.js +5 -0
- package/dist/compiler/types.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/language-service/completions.d.ts +27 -0
- package/dist/language-service/completions.d.ts.map +1 -0
- package/dist/language-service/completions.js +147 -0
- package/dist/language-service/completions.js.map +1 -0
- package/dist/language-service/definitions.d.ts +14 -0
- package/dist/language-service/definitions.d.ts.map +1 -0
- package/dist/language-service/definitions.js +140 -0
- package/dist/language-service/definitions.js.map +1 -0
- package/dist/language-service/diagnostics.d.ts +13 -0
- package/dist/language-service/diagnostics.d.ts.map +1 -0
- package/dist/language-service/diagnostics.js +169 -0
- package/dist/language-service/diagnostics.js.map +1 -0
- package/dist/language-service/hover.d.ts +15 -0
- package/dist/language-service/hover.d.ts.map +1 -0
- package/dist/language-service/hover.js +125 -0
- package/dist/language-service/hover.js.map +1 -0
- package/dist/language-service/index.d.ts +26 -0
- package/dist/language-service/index.d.ts.map +1 -0
- package/dist/language-service/index.js +30 -0
- package/dist/language-service/index.js.map +1 -0
- package/dist/language-service/markdown-cache.d.ts +44 -0
- package/dist/language-service/markdown-cache.d.ts.map +1 -0
- package/dist/language-service/markdown-cache.js +77 -0
- package/dist/language-service/markdown-cache.js.map +1 -0
- package/dist/language-service/plugin.d.ts +15 -0
- package/dist/language-service/plugin.d.ts.map +1 -0
- package/dist/language-service/plugin.js +51 -0
- package/dist/language-service/plugin.js.map +1 -0
- package/dist/language-service/utils.d.ts +173 -0
- package/dist/language-service/utils.d.ts.map +1 -0
- package/dist/language-service/utils.js +341 -0
- package/dist/language-service/utils.js.map +1 -0
- package/dist/transformer/ast-helpers.d.ts +35 -0
- package/dist/transformer/ast-helpers.d.ts.map +1 -0
- package/dist/transformer/ast-helpers.js +153 -0
- package/dist/transformer/ast-helpers.js.map +1 -0
- package/dist/transformer/declaration-generator.d.ts +47 -0
- package/dist/transformer/declaration-generator.d.ts.map +1 -0
- package/dist/transformer/declaration-generator.js +53 -0
- package/dist/transformer/declaration-generator.js.map +1 -0
- package/dist/transformer/import-detector.d.ts +62 -0
- package/dist/transformer/import-detector.d.ts.map +1 -0
- package/dist/transformer/import-detector.js +115 -0
- package/dist/transformer/import-detector.js.map +1 -0
- package/dist/transformer/index.d.ts +11 -0
- package/dist/transformer/index.d.ts.map +1 -0
- package/dist/transformer/index.js +11 -0
- package/dist/transformer/index.js.map +1 -0
- package/dist/transformer/module-generator.d.ts +29 -0
- package/dist/transformer/module-generator.d.ts.map +1 -0
- package/dist/transformer/module-generator.js +48 -0
- package/dist/transformer/module-generator.js.map +1 -0
- package/dist/transformer/path-resolver.d.ts +32 -0
- package/dist/transformer/path-resolver.d.ts.map +1 -0
- package/dist/transformer/path-resolver.js +112 -0
- package/dist/transformer/path-resolver.js.map +1 -0
- package/dist/transformer/transformer.d.ts +46 -0
- package/dist/transformer/transformer.d.ts.map +1 -0
- package/dist/transformer/transformer.js +89 -0
- package/dist/transformer/transformer.js.map +1 -0
- package/dist/utils/copy-resources.d.ts +54 -0
- package/dist/utils/copy-resources.d.ts.map +1 -0
- package/dist/utils/copy-resources.js +77 -0
- package/dist/utils/copy-resources.js.map +1 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +5 -0
- package/dist/utils/index.js.map +1 -0
- 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
|