@shepherdjerred/helm-types 0.0.0-dev.706
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 +209 -0
- package/dist/cli.js +22085 -0
- package/dist/index.js +23 -0
- package/package.json +66 -0
- package/src/chart-fetcher.ts +171 -0
- package/src/chart-info-parser.ts +72 -0
- package/src/cli.ts +215 -0
- package/src/code-generator.ts +226 -0
- package/src/comment-parser.ts +180 -0
- package/src/config.ts +147 -0
- package/src/helm-types.ts +16 -0
- package/src/index.ts +28 -0
- package/src/interface-generator.ts +238 -0
- package/src/reset.d.ts +1 -0
- package/src/schemas.ts +39 -0
- package/src/type-converter-helpers.ts +180 -0
- package/src/type-converter.ts +509 -0
- package/src/type-inference.ts +548 -0
- package/src/types.ts +38 -0
- package/src/utils.ts +76 -0
- package/src/yaml-comment-filters.ts +103 -0
- package/src/yaml-comment-regex-parser.ts +150 -0
- package/src/yaml-comments.ts +507 -0
- package/src/yaml-preprocess.ts +235 -0
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
import { parseDocument } from "yaml";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { filterCommentedOutYAML } from "./yaml-comment-filters.ts";
|
|
4
|
+
import { parseCommentsWithRegex } from "./yaml-comment-regex-parser.ts";
|
|
5
|
+
import { preprocessYAMLComments } from "./yaml-preprocess.ts";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Metadata about how a comment was extracted
|
|
9
|
+
*/
|
|
10
|
+
export type CommentMetadata = {
|
|
11
|
+
source: "AST" | "REGEX";
|
|
12
|
+
rawComment?: string;
|
|
13
|
+
indent?: number;
|
|
14
|
+
debugInfo?: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Comment with metadata for debugging
|
|
19
|
+
*/
|
|
20
|
+
export type CommentWithMetadata = {
|
|
21
|
+
text: string;
|
|
22
|
+
metadata: CommentMetadata;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Helper: Check if a line looks like a YAML key (e.g., "key: value" or "key:")
|
|
27
|
+
* Exported for testing purposes
|
|
28
|
+
*/
|
|
29
|
+
export function isYAMLKey(line: string): boolean {
|
|
30
|
+
return /^[\w.-]+:\s*(?:\||$)/.test(line);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Helper: Check if a line looks like a simple YAML value assignment
|
|
35
|
+
* Exported for testing purposes
|
|
36
|
+
*/
|
|
37
|
+
export function isSimpleYAMLValue(line: string): boolean {
|
|
38
|
+
const hasURL = line.includes("http://") || line.includes("https://");
|
|
39
|
+
const isRef = /^ref:/i.test(line);
|
|
40
|
+
return /^[\w.-]+:\s[^:]+$/.test(line) && !hasURL && !isRef;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Helper: Check if a line is a section header (short line followed by YAML config)
|
|
45
|
+
* Exported for testing purposes
|
|
46
|
+
*/
|
|
47
|
+
export function isSectionHeader(line: string, nextLine?: string): boolean {
|
|
48
|
+
if (nextLine == null || nextLine === "") {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const isFollowedByYAMLKey =
|
|
53
|
+
/^[\w.-]+:\s*\|/.test(nextLine) ||
|
|
54
|
+
/^[\w.-]+:\s*$/.test(nextLine) ||
|
|
55
|
+
/^[\w.-]+:\s+/.test(nextLine);
|
|
56
|
+
|
|
57
|
+
if (!isFollowedByYAMLKey) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const wordCount = line.split(/\s+/).length;
|
|
62
|
+
const hasConfigKeywords =
|
|
63
|
+
/\b(?:configuration|config|example|setup|settings?|options?|alternative)\b/i.test(
|
|
64
|
+
line,
|
|
65
|
+
);
|
|
66
|
+
const endsWithPunctuation = /[.!?]$/.test(line);
|
|
67
|
+
const hasURL = line.includes("http://") || line.includes("https://");
|
|
68
|
+
const startsWithArticle = /^(?:This|The|A|An)\s/i.test(line);
|
|
69
|
+
const startsWithCommonWord =
|
|
70
|
+
/^(?:This|The|A|An|It|For|To|If|When|You|We|Use|Configure)\s/i.test(line);
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
((wordCount === 2 && !startsWithCommonWord) ||
|
|
74
|
+
(hasConfigKeywords && !startsWithCommonWord)) &&
|
|
75
|
+
!endsWithPunctuation &&
|
|
76
|
+
!hasURL &&
|
|
77
|
+
!/^ref:/i.test(line) &&
|
|
78
|
+
!startsWithArticle
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Helper: Check if a line looks like code/YAML example
|
|
84
|
+
* Exported for testing purposes
|
|
85
|
+
*/
|
|
86
|
+
export function isCodeExample(line: string, wordCount: number): boolean {
|
|
87
|
+
const looksLikeYAMLKey = isYAMLKey(line);
|
|
88
|
+
const looksLikeSimpleYAMLValue = isSimpleYAMLValue(line);
|
|
89
|
+
const looksLikeYAMLList =
|
|
90
|
+
line.startsWith("-") && (line.includes(":") || /^-\s+\|/.test(line));
|
|
91
|
+
const looksLikePolicyRule = /^[pg],\s*/.test(line);
|
|
92
|
+
const hasIndentation = /^\s{2,}/.test(line);
|
|
93
|
+
const looksLikeCommand =
|
|
94
|
+
/^echo\s+/.test(line) ||
|
|
95
|
+
line.includes("$ARGOCD_") ||
|
|
96
|
+
line.includes("$KUBE_");
|
|
97
|
+
const isSeparator =
|
|
98
|
+
/^-{3,}/.test(line) ||
|
|
99
|
+
/^BEGIN .*(?:KEY|CERTIFICATE)/.test(line) ||
|
|
100
|
+
/^END .*(?:KEY|CERTIFICATE)/.test(line);
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
isSeparator ||
|
|
104
|
+
looksLikeYAMLKey ||
|
|
105
|
+
(looksLikeSimpleYAMLValue && wordCount <= 4) ||
|
|
106
|
+
looksLikeYAMLList ||
|
|
107
|
+
looksLikePolicyRule ||
|
|
108
|
+
hasIndentation ||
|
|
109
|
+
looksLikeCommand ||
|
|
110
|
+
line.startsWith("|")
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Helper: Check if a line looks like prose (real documentation)
|
|
116
|
+
* Exported for testing purposes
|
|
117
|
+
*/
|
|
118
|
+
export function looksLikeProse(line: string, wordCount: number): boolean {
|
|
119
|
+
const hasURL = line.includes("http://") || line.includes("https://");
|
|
120
|
+
const startsWithCapital = /^[A-Z]/.test(line);
|
|
121
|
+
const hasEndPunctuation = /[.!?:]$/.test(line);
|
|
122
|
+
const notYamlKey = !(isYAMLKey(line) && !hasURL && !/^ref:/i.test(line));
|
|
123
|
+
const reasonableLength = line.length > 10;
|
|
124
|
+
const hasMultipleWords = wordCount >= 3;
|
|
125
|
+
const startsWithArticle = /^(?:This|The|A|An)\s/i.test(line);
|
|
126
|
+
|
|
127
|
+
// Lines starting with markers like ^, ->, etc. are documentation references
|
|
128
|
+
const isReferenceMarker = /^(?:\^|->|→)\s/.test(line);
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
(startsWithCapital || isReferenceMarker) &&
|
|
132
|
+
(hasEndPunctuation ||
|
|
133
|
+
hasURL ||
|
|
134
|
+
startsWithArticle ||
|
|
135
|
+
hasMultipleWords ||
|
|
136
|
+
isReferenceMarker) &&
|
|
137
|
+
notYamlKey &&
|
|
138
|
+
reasonableLength
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Helper: Normalize a comment line by removing markers
|
|
144
|
+
* Exported for testing purposes
|
|
145
|
+
*/
|
|
146
|
+
export function normalizeCommentLine(line: string): string {
|
|
147
|
+
let normalized = line.trim();
|
|
148
|
+
normalized = normalized.replace(/^#+\s*/, ""); // Remove leading # symbols
|
|
149
|
+
normalized = normalized.replace(/^--\s*/, ""); // Remove Helm's -- marker
|
|
150
|
+
normalized = normalized.replace(/^@param\s+[\w.-]+\s+/, ""); // Remove Bitnami's @param prefix
|
|
151
|
+
normalized = normalized.replace(/^@section\s+/, ""); // Remove Bitnami's @section prefix
|
|
152
|
+
return normalized.trim();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Clean up YAML comment text for use in JSDoc
|
|
157
|
+
* Removes Helm-specific markers and filters out code examples and section headers
|
|
158
|
+
*/
|
|
159
|
+
export function cleanYAMLComment(comment: string): string {
|
|
160
|
+
if (!comment) {
|
|
161
|
+
return "";
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Normalize all lines
|
|
165
|
+
const lines = comment.split("\n").map((line) => normalizeCommentLine(line));
|
|
166
|
+
|
|
167
|
+
// Filter out code examples and section headers, keep documentation
|
|
168
|
+
const cleaned: string[] = [];
|
|
169
|
+
let inCodeBlock = false;
|
|
170
|
+
let inExample = false; // Track if we're in an "Example:" section (keep these!)
|
|
171
|
+
|
|
172
|
+
for (let i = 0; i < lines.length; i++) {
|
|
173
|
+
const line = lines[i] ?? "";
|
|
174
|
+
|
|
175
|
+
// Empty lines end code blocks (but not examples)
|
|
176
|
+
if (!line) {
|
|
177
|
+
if (inCodeBlock && !inExample) {
|
|
178
|
+
inCodeBlock = false;
|
|
179
|
+
}
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Skip @default lines (we'll generate our own)
|
|
184
|
+
if (line.startsWith("@default")) {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Check if this line starts an Example section (preserve these!)
|
|
189
|
+
if (/^Example:?$/i.test(line.trim())) {
|
|
190
|
+
inExample = true;
|
|
191
|
+
inCodeBlock = true; // Treat examples as special code blocks
|
|
192
|
+
cleaned.push(line);
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const nextLine = lines[i + 1];
|
|
197
|
+
const wordCount = line.split(/\s+/).length;
|
|
198
|
+
|
|
199
|
+
// Skip section headers
|
|
200
|
+
if (isSectionHeader(line, nextLine)) {
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// If we're in an example section, keep all lines (including code)
|
|
205
|
+
if (inExample) {
|
|
206
|
+
cleaned.push(line);
|
|
207
|
+
// Check if we're exiting the example section
|
|
208
|
+
if (line.startsWith("For more information") || line.startsWith("Ref:")) {
|
|
209
|
+
inExample = false;
|
|
210
|
+
inCodeBlock = false;
|
|
211
|
+
}
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Check if this line is a code example
|
|
216
|
+
if (isCodeExample(line, wordCount)) {
|
|
217
|
+
inCodeBlock = true;
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Resume prose when we hit a proper sentence
|
|
222
|
+
if (inCodeBlock) {
|
|
223
|
+
if (looksLikeProse(line, wordCount)) {
|
|
224
|
+
inCodeBlock = false;
|
|
225
|
+
} else {
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
cleaned.push(line);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return cleaned.join("\n").trim();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Parse Bitnami-style @param directives from a comment
|
|
238
|
+
* Format: @param key.path Description
|
|
239
|
+
* Returns: Map of extracted params and remaining non-param lines
|
|
240
|
+
* Exported for testing purposes
|
|
241
|
+
*/
|
|
242
|
+
export function parseBitnamiParams(comment: string): {
|
|
243
|
+
params: Map<string, string>;
|
|
244
|
+
remainingLines: string[];
|
|
245
|
+
} {
|
|
246
|
+
const lines = comment.split("\n");
|
|
247
|
+
const params = new Map<string, string>();
|
|
248
|
+
const remainingLines: string[] = [];
|
|
249
|
+
|
|
250
|
+
for (const line of lines) {
|
|
251
|
+
const trimmedLine = line
|
|
252
|
+
.trim()
|
|
253
|
+
.replace(/^#+\s*/, "")
|
|
254
|
+
.replace(/^--\s*/, "");
|
|
255
|
+
|
|
256
|
+
const paramMatch = /^@param\s+([\w.-]+)\s+(\S.*)$/.exec(trimmedLine);
|
|
257
|
+
if (paramMatch) {
|
|
258
|
+
const [, paramKey, description] = paramMatch;
|
|
259
|
+
if (
|
|
260
|
+
paramKey != null &&
|
|
261
|
+
paramKey !== "" &&
|
|
262
|
+
description != null &&
|
|
263
|
+
description !== ""
|
|
264
|
+
) {
|
|
265
|
+
params.set(paramKey, description);
|
|
266
|
+
}
|
|
267
|
+
} else if (trimmedLine) {
|
|
268
|
+
remainingLines.push(trimmedLine);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return { params, remainingLines };
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Parse YAML comments with metadata for debugging
|
|
277
|
+
* Returns comments with information about how they were extracted
|
|
278
|
+
* Exported for testing purposes
|
|
279
|
+
*/
|
|
280
|
+
export function parseYAMLCommentsWithMetadata(
|
|
281
|
+
yamlContent: string,
|
|
282
|
+
): Map<string, CommentWithMetadata> {
|
|
283
|
+
const comments = new Map<string, CommentWithMetadata>();
|
|
284
|
+
|
|
285
|
+
try {
|
|
286
|
+
// Pre-process to uncomment commented-out keys
|
|
287
|
+
// This allows Helm chart commented-out options to be parsed as real keys
|
|
288
|
+
const preprocessedYaml = preprocessYAMLComments(yamlContent);
|
|
289
|
+
const doc = parseDocument(preprocessedYaml);
|
|
290
|
+
|
|
291
|
+
// Build a regex-based fallback map for cases where YAML parser loses comments
|
|
292
|
+
// Use preprocessed YAML so commented-out keys are treated as real keys
|
|
293
|
+
const regexComments = parseCommentsWithRegex(preprocessedYaml);
|
|
294
|
+
|
|
295
|
+
// Zod schemas for YAML AST parsing, defined once
|
|
296
|
+
const MapNodeSchema = z.object({
|
|
297
|
+
items: z.array(z.unknown()),
|
|
298
|
+
commentBefore: z.unknown().optional(),
|
|
299
|
+
});
|
|
300
|
+
const PairSchema = z.object({ key: z.unknown(), value: z.unknown() });
|
|
301
|
+
const KeyValueSchema = z.object({ value: z.string() });
|
|
302
|
+
const CommentBeforeSchema = z.object({ commentBefore: z.unknown() });
|
|
303
|
+
const InlineCommentSchema = z.object({ comment: z.unknown() });
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Extract the commentBefore string from a YAML node
|
|
307
|
+
*/
|
|
308
|
+
function extractCommentBefore(node: unknown): string {
|
|
309
|
+
const check = CommentBeforeSchema.safeParse(node);
|
|
310
|
+
if (!check.success) {
|
|
311
|
+
return "";
|
|
312
|
+
}
|
|
313
|
+
const strCheck = z.string().safeParse(check.data.commentBefore);
|
|
314
|
+
return strCheck.success ? strCheck.data : "";
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Extract the inline comment string from a YAML value node
|
|
319
|
+
*/
|
|
320
|
+
function extractInlineComment(node: unknown): string {
|
|
321
|
+
const check = InlineCommentSchema.safeParse(node);
|
|
322
|
+
if (!check.success) {
|
|
323
|
+
return "";
|
|
324
|
+
}
|
|
325
|
+
const strCheck = z.string().safeParse(check.data.comment);
|
|
326
|
+
return strCheck.success ? strCheck.data : "";
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Collect all comment sources for a YAML pair (key comment, pair comment, inline comment)
|
|
331
|
+
*/
|
|
332
|
+
function collectItemComment(
|
|
333
|
+
pairData: { keyNode: unknown; item: unknown; valueNode: unknown },
|
|
334
|
+
context: { index: number; mapComment: string },
|
|
335
|
+
): string {
|
|
336
|
+
let comment = extractCommentBefore(pairData.keyNode);
|
|
337
|
+
const pairComment = extractCommentBefore(pairData.item);
|
|
338
|
+
if (pairComment) {
|
|
339
|
+
comment = comment ? `${pairComment}\n${comment}` : pairComment;
|
|
340
|
+
}
|
|
341
|
+
const inlineComment = extractInlineComment(pairData.valueNode);
|
|
342
|
+
if (inlineComment) {
|
|
343
|
+
comment = comment ? `${comment}\n${inlineComment}` : inlineComment;
|
|
344
|
+
}
|
|
345
|
+
// First item inherits map comment if it has none
|
|
346
|
+
if (context.index === 0 && !comment && context.mapComment) {
|
|
347
|
+
comment = context.mapComment;
|
|
348
|
+
}
|
|
349
|
+
if (comment) {
|
|
350
|
+
comment = filterCommentedOutYAML(comment);
|
|
351
|
+
}
|
|
352
|
+
return comment;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Store a comment (with @param handling) into the comments map
|
|
357
|
+
*/
|
|
358
|
+
function storeComment(comment: string, fullKey: string): void {
|
|
359
|
+
const hasParamDirective = comment.includes("@param ");
|
|
360
|
+
if (hasParamDirective) {
|
|
361
|
+
const { params, remainingLines } = parseBitnamiParams(comment);
|
|
362
|
+
for (const [paramKey, description] of params.entries()) {
|
|
363
|
+
comments.set(paramKey, {
|
|
364
|
+
text: description,
|
|
365
|
+
metadata: {
|
|
366
|
+
source: "AST",
|
|
367
|
+
rawComment: comment,
|
|
368
|
+
debugInfo: `Bitnami @param directive for ${paramKey}`,
|
|
369
|
+
},
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
const remainingCleaned =
|
|
373
|
+
remainingLines.length > 0
|
|
374
|
+
? cleanYAMLComment(remainingLines.join("\n"))
|
|
375
|
+
: "";
|
|
376
|
+
if (remainingCleaned) {
|
|
377
|
+
comments.set(fullKey, {
|
|
378
|
+
text: remainingCleaned,
|
|
379
|
+
metadata: {
|
|
380
|
+
source: "AST",
|
|
381
|
+
rawComment: comment,
|
|
382
|
+
debugInfo: `AST comment after extracting @param directives`,
|
|
383
|
+
},
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
} else if (comment) {
|
|
387
|
+
const cleaned = cleanYAMLComment(comment);
|
|
388
|
+
if (cleaned) {
|
|
389
|
+
comments.set(fullKey, {
|
|
390
|
+
text: cleaned,
|
|
391
|
+
metadata: {
|
|
392
|
+
source: "AST",
|
|
393
|
+
rawComment: comment,
|
|
394
|
+
debugInfo: `Direct AST comment for ${fullKey}`,
|
|
395
|
+
},
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Recursively walk the YAML AST and extract comments
|
|
402
|
+
function visitNode(
|
|
403
|
+
node: unknown,
|
|
404
|
+
keyPath: string[] = [],
|
|
405
|
+
inheritedComment = "",
|
|
406
|
+
): void {
|
|
407
|
+
if (node == null) {
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const mapNodeCheck = MapNodeSchema.safeParse(node);
|
|
412
|
+
if (!mapNodeCheck.success) {
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Extract the map's own comment (to be inherited by first child if needed)
|
|
417
|
+
let mapComment = inheritedComment;
|
|
418
|
+
const mapCommentCheck = z
|
|
419
|
+
.string()
|
|
420
|
+
.safeParse(mapNodeCheck.data.commentBefore);
|
|
421
|
+
if (mapCommentCheck.success) {
|
|
422
|
+
mapComment = mapCommentCheck.data;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
for (let i = 0; i < mapNodeCheck.data.items.length; i++) {
|
|
426
|
+
const item = mapNodeCheck.data.items[i];
|
|
427
|
+
const itemCheck = PairSchema.safeParse(item);
|
|
428
|
+
if (!itemCheck.success) {
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const keyNodeCheck = KeyValueSchema.safeParse(itemCheck.data.key);
|
|
433
|
+
if (!keyNodeCheck.success) {
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const key = keyNodeCheck.data.value;
|
|
438
|
+
const newPath = [...keyPath, key];
|
|
439
|
+
const fullKey = newPath.join(".");
|
|
440
|
+
|
|
441
|
+
const comment = collectItemComment(
|
|
442
|
+
{
|
|
443
|
+
keyNode: itemCheck.data.key,
|
|
444
|
+
item,
|
|
445
|
+
valueNode: itemCheck.data.value,
|
|
446
|
+
},
|
|
447
|
+
{ index: i, mapComment },
|
|
448
|
+
);
|
|
449
|
+
storeComment(comment, fullKey);
|
|
450
|
+
|
|
451
|
+
const valueInheritedComment = extractCommentBefore(
|
|
452
|
+
itemCheck.data.value,
|
|
453
|
+
);
|
|
454
|
+
|
|
455
|
+
if (itemCheck.data.value != null) {
|
|
456
|
+
visitNode(itemCheck.data.value, newPath, valueInheritedComment);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Start with the document contents
|
|
462
|
+
if (doc.contents) {
|
|
463
|
+
visitNode(doc.contents, []);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Merge regex comments as fallback - only use them if AST parsing didn't find a comment
|
|
467
|
+
// This handles cases where YAML parser loses comments due to:
|
|
468
|
+
// - Inconsistent indentation
|
|
469
|
+
// - Commented-out YAML keys mixed with documentation
|
|
470
|
+
// - Other edge cases
|
|
471
|
+
for (const [key, commentWithMeta] of regexComments.entries()) {
|
|
472
|
+
if (!comments.has(key)) {
|
|
473
|
+
const cleaned = cleanYAMLComment(commentWithMeta.text);
|
|
474
|
+
if (cleaned) {
|
|
475
|
+
comments.set(key, {
|
|
476
|
+
text: cleaned,
|
|
477
|
+
metadata: {
|
|
478
|
+
...commentWithMeta.metadata,
|
|
479
|
+
debugInfo: `${commentWithMeta.metadata.debugInfo ?? ""}\nCleaned from: "${commentWithMeta.text}"`,
|
|
480
|
+
},
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
} catch (error) {
|
|
486
|
+
// If YAML parsing fails, fall back to empty map
|
|
487
|
+
console.warn("Failed to parse YAML comments:", error);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
return comments;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Parse YAML comments and associate them with keys
|
|
495
|
+
* Returns a simple Map<string, string> for backward compatibility
|
|
496
|
+
* Exported for testing purposes
|
|
497
|
+
*/
|
|
498
|
+
export function parseYAMLComments(yamlContent: string): Map<string, string> {
|
|
499
|
+
const commentsWithMetadata = parseYAMLCommentsWithMetadata(yamlContent);
|
|
500
|
+
const simpleComments = new Map<string, string>();
|
|
501
|
+
|
|
502
|
+
for (const [key, commentWithMeta] of commentsWithMetadata.entries()) {
|
|
503
|
+
simpleComments.set(key, commentWithMeta.text);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
return simpleComments;
|
|
507
|
+
}
|