spec-up-t-healthcheck 1.0.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/lib/checks/console-messages.js +211 -0
- package/lib/checks/external-specs-urls.js +93 -16
- package/lib/checks/gitignore.js +1 -1
- package/lib/checks/link-checker.js +361 -0
- package/lib/checks/markdown-tables.js +463 -0
- package/lib/checks/package-json.js +1 -1
- package/lib/checks/spec-directory-and-files.js +356 -0
- package/lib/checks/specsjson.js +258 -5
- package/lib/formatters/result-details-formatter.js +559 -0
- package/lib/formatters.js +14 -0
- package/lib/health-check-registry.js +70 -3
- package/lib/health-checker.js +13 -3
- package/lib/html-formatter.js +139 -168
- package/lib/index.js +1 -1
- package/lib/providers-browser.js +73 -0
- package/lib/providers.js +24 -0
- package/lib/web.js +10 -6
- package/package.json +5 -17
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Markdown table validation health check module
|
|
3
|
+
*
|
|
4
|
+
* This module validates markdown tables in specification files, checking for:
|
|
5
|
+
* - Proper table structure (header, separator, body rows)
|
|
6
|
+
* - Consistent column counts across all rows
|
|
7
|
+
* - Valid separator line syntax
|
|
8
|
+
* - Problematic characters that can break table parsing
|
|
9
|
+
* - Mismatched quotes or backticks in table cells
|
|
10
|
+
*
|
|
11
|
+
* This validator catches errors that can cause issues with markdown-it-attrs
|
|
12
|
+
* and other markdown table parsing plugins.
|
|
13
|
+
*
|
|
14
|
+
* @author spec-up-t-healthcheck
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { createHealthCheckResult, createErrorResult } from '../health-check-utils.js';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The identifier for this health check, used in reports and registries.
|
|
21
|
+
* @type {string}
|
|
22
|
+
*/
|
|
23
|
+
export const CHECK_ID = 'markdown-tables';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Human-readable name for this health check.
|
|
27
|
+
* @type {string}
|
|
28
|
+
*/
|
|
29
|
+
export const CHECK_NAME = 'Markdown Table Validation';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Description of what this health check validates.
|
|
33
|
+
* @type {string}
|
|
34
|
+
*/
|
|
35
|
+
export const CHECK_DESCRIPTION = 'Validates markdown table structure and syntax';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Represents a table found in a markdown file.
|
|
39
|
+
* @typedef {Object} TableInfo
|
|
40
|
+
* @property {string} file - Path to the file containing the table
|
|
41
|
+
* @property {number} startLine - Line number where the table starts
|
|
42
|
+
* @property {number} endLine - Line number where the table ends
|
|
43
|
+
* @property {string[]} content - Array of table lines
|
|
44
|
+
* @property {number} headerColumns - Number of columns in header
|
|
45
|
+
* @property {number} separatorColumns - Number of columns in separator
|
|
46
|
+
* @property {boolean} hasSeparator - Whether table has a separator line
|
|
47
|
+
* @property {boolean} columnMismatch - Whether column counts don't match
|
|
48
|
+
* @property {Object[]} issues - Array of specific issues found
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Extracts all tables from markdown content.
|
|
53
|
+
*
|
|
54
|
+
* This function parses markdown content line by line and identifies table structures.
|
|
55
|
+
* Tables are detected by lines containing pipe characters (|).
|
|
56
|
+
*
|
|
57
|
+
* @param {string} content - The markdown content to parse
|
|
58
|
+
* @param {string} filePath - Path to the file (for error reporting)
|
|
59
|
+
* @returns {TableInfo[]} Array of table information objects
|
|
60
|
+
*/
|
|
61
|
+
function extractTables(content, filePath) {
|
|
62
|
+
const lines = content.split('\n');
|
|
63
|
+
const tables = [];
|
|
64
|
+
let inTable = false;
|
|
65
|
+
let tableStartLine = 0;
|
|
66
|
+
let tableContent = [];
|
|
67
|
+
|
|
68
|
+
for (let i = 0; i < lines.length; i++) {
|
|
69
|
+
const line = lines[i];
|
|
70
|
+
const trimmedLine = line.trim();
|
|
71
|
+
|
|
72
|
+
// Detect table start (line with pipes)
|
|
73
|
+
if (trimmedLine.match(/^\|.*\|/)) {
|
|
74
|
+
if (!inTable) {
|
|
75
|
+
inTable = true;
|
|
76
|
+
tableStartLine = i + 1; // 1-indexed for user display
|
|
77
|
+
tableContent = [];
|
|
78
|
+
}
|
|
79
|
+
tableContent.push({ lineNum: i + 1, content: line });
|
|
80
|
+
} else if (inTable && (trimmedLine === '' || !line.includes('|'))) {
|
|
81
|
+
// Table ended - analyze and store it
|
|
82
|
+
if (tableContent.length > 0) {
|
|
83
|
+
const tableInfo = analyzeTable(tableContent, tableStartLine, filePath);
|
|
84
|
+
tables.push(tableInfo);
|
|
85
|
+
}
|
|
86
|
+
inTable = false;
|
|
87
|
+
} else if (inTable && line.includes('|')) {
|
|
88
|
+
// Continuation of table (might be in code block or other context)
|
|
89
|
+
tableContent.push({ lineNum: i + 1, content: line });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Handle table at end of file
|
|
94
|
+
if (inTable && tableContent.length > 0) {
|
|
95
|
+
const tableInfo = analyzeTable(tableContent, tableStartLine, filePath);
|
|
96
|
+
tables.push(tableInfo);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return tables;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Analyzes a single table for structural issues.
|
|
104
|
+
*
|
|
105
|
+
* This function performs comprehensive validation of table structure:
|
|
106
|
+
* - Counts columns in each row
|
|
107
|
+
* - Validates separator line format
|
|
108
|
+
* - Checks for problematic characters
|
|
109
|
+
* - Detects mismatched quotes and backticks
|
|
110
|
+
*
|
|
111
|
+
* @param {Array<{lineNum: number, content: string}>} tableLines - Array of table lines
|
|
112
|
+
* @param {number} startLine - Starting line number
|
|
113
|
+
* @param {string} filePath - Path to the file
|
|
114
|
+
* @returns {TableInfo} Analyzed table information
|
|
115
|
+
*/
|
|
116
|
+
function analyzeTable(tableLines, startLine, filePath) {
|
|
117
|
+
const issues = [];
|
|
118
|
+
|
|
119
|
+
// Extract header and separator
|
|
120
|
+
const headerLine = tableLines[0];
|
|
121
|
+
const separatorLine = tableLines.length > 1 ? tableLines[1] : null;
|
|
122
|
+
|
|
123
|
+
// Count columns
|
|
124
|
+
const headerColumns = countColumns(headerLine.content);
|
|
125
|
+
const separatorColumns = separatorLine ? countColumns(separatorLine.content) : 0;
|
|
126
|
+
const hasSeparator = separatorLine && isSeparatorLine(separatorLine.content);
|
|
127
|
+
const columnMismatch = hasSeparator && (headerColumns !== separatorColumns);
|
|
128
|
+
|
|
129
|
+
// Check each row for issues
|
|
130
|
+
tableLines.forEach((line, idx) => {
|
|
131
|
+
const rowIssues = validateTableRow(line.content, line.lineNum, idx === 1);
|
|
132
|
+
issues.push(...rowIssues);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Validate separator line specifically
|
|
136
|
+
if (separatorLine && !hasSeparator) {
|
|
137
|
+
issues.push({
|
|
138
|
+
type: 'missing-separator',
|
|
139
|
+
line: separatorLine.lineNum,
|
|
140
|
+
message: 'Table separator line is missing or malformed (should contain only |, -, and :)',
|
|
141
|
+
content: separatorLine.content
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Check for column count mismatches
|
|
146
|
+
if (columnMismatch) {
|
|
147
|
+
issues.push({
|
|
148
|
+
type: 'column-mismatch',
|
|
149
|
+
line: startLine,
|
|
150
|
+
message: `Column count mismatch: header has ${headerColumns} columns, separator has ${separatorColumns}`,
|
|
151
|
+
content: `Header: ${headerLine.content}\nSeparator: ${separatorLine.content}`
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Validate body row column counts
|
|
156
|
+
if (hasSeparator && tableLines.length > 2) {
|
|
157
|
+
const expectedColumns = headerColumns;
|
|
158
|
+
for (let i = 2; i < tableLines.length; i++) {
|
|
159
|
+
const rowColumns = countColumns(tableLines[i].content);
|
|
160
|
+
if (rowColumns !== expectedColumns) {
|
|
161
|
+
issues.push({
|
|
162
|
+
type: 'row-column-mismatch',
|
|
163
|
+
line: tableLines[i].lineNum,
|
|
164
|
+
message: `Row has ${rowColumns} columns, expected ${expectedColumns}`,
|
|
165
|
+
content: tableLines[i].content
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
file: filePath,
|
|
173
|
+
startLine,
|
|
174
|
+
endLine: tableLines[tableLines.length - 1].lineNum,
|
|
175
|
+
content: tableLines.map(l => l.content),
|
|
176
|
+
headerColumns,
|
|
177
|
+
separatorColumns,
|
|
178
|
+
hasSeparator,
|
|
179
|
+
columnMismatch,
|
|
180
|
+
issues
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Counts the number of columns in a table row.
|
|
186
|
+
*
|
|
187
|
+
* Columns are delimited by pipe characters (|). The count excludes
|
|
188
|
+
* leading and trailing pipes.
|
|
189
|
+
*
|
|
190
|
+
* @param {string} line - The table row line
|
|
191
|
+
* @returns {number} Number of columns
|
|
192
|
+
*/
|
|
193
|
+
function countColumns(line) {
|
|
194
|
+
// Remove leading/trailing whitespace and pipes
|
|
195
|
+
const trimmed = line.trim();
|
|
196
|
+
if (!trimmed) return 0;
|
|
197
|
+
|
|
198
|
+
// Count pipe separators (excluding leading/trailing)
|
|
199
|
+
const withoutEnds = trimmed.replace(/^\|/, '').replace(/\|$/, '');
|
|
200
|
+
const pipes = (withoutEnds.match(/\|/g) || []).length;
|
|
201
|
+
|
|
202
|
+
return pipes + 1;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Checks if a line is a valid table separator.
|
|
207
|
+
*
|
|
208
|
+
* A valid separator line contains only pipes, hyphens, colons, and whitespace.
|
|
209
|
+
* Format: |:---:|:---:| or |---|---| etc.
|
|
210
|
+
*
|
|
211
|
+
* @param {string} line - The line to check
|
|
212
|
+
* @returns {boolean} True if the line is a valid separator
|
|
213
|
+
*/
|
|
214
|
+
function isSeparatorLine(line) {
|
|
215
|
+
const trimmed = line.trim();
|
|
216
|
+
// Valid separator contains only: |, -, :, and whitespace
|
|
217
|
+
return /^[\s|:\-]+$/.test(trimmed) && trimmed.includes('-');
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Validates a single table row for common issues.
|
|
222
|
+
*
|
|
223
|
+
* This function checks for:
|
|
224
|
+
* - Mismatched quotes (opening quote without closing)
|
|
225
|
+
* - Mismatched backticks
|
|
226
|
+
* - Problematic character combinations
|
|
227
|
+
* - Extra quotes inside code spans
|
|
228
|
+
*
|
|
229
|
+
* @param {string} line - The table row line
|
|
230
|
+
* @param {number} lineNum - Line number in file
|
|
231
|
+
* @param {boolean} isSeparator - Whether this is the separator row
|
|
232
|
+
* @returns {Array<Object>} Array of issues found
|
|
233
|
+
*/
|
|
234
|
+
function validateTableRow(line, lineNum, isSeparator) {
|
|
235
|
+
const issues = [];
|
|
236
|
+
|
|
237
|
+
// Skip validation for separator lines
|
|
238
|
+
if (isSeparator) {
|
|
239
|
+
return issues;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Split line into cells
|
|
243
|
+
const cells = line.split('|').slice(1, -1); // Remove first/last empty elements
|
|
244
|
+
|
|
245
|
+
cells.forEach((cell, cellIdx) => {
|
|
246
|
+
const trimmedCell = cell.trim();
|
|
247
|
+
|
|
248
|
+
// Check for problematic quote patterns in code spans
|
|
249
|
+
// Pattern: `'text or `text'
|
|
250
|
+
const backtickQuotePattern = /`['"]|['"]`/g;
|
|
251
|
+
const backtickWithQuote = trimmedCell.match(backtickQuotePattern);
|
|
252
|
+
if (backtickWithQuote) {
|
|
253
|
+
issues.push({
|
|
254
|
+
type: 'quote-in-code',
|
|
255
|
+
line: lineNum,
|
|
256
|
+
cell: cellIdx + 1,
|
|
257
|
+
message: `Cell ${cellIdx + 1} contains potentially problematic quote/backtick combination: ${backtickWithQuote.join(', ')}`,
|
|
258
|
+
content: cell,
|
|
259
|
+
severity: 'warning'
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Check for mismatched backticks in cell
|
|
264
|
+
const backticks = (trimmedCell.match(/`/g) || []).length;
|
|
265
|
+
if (backticks % 2 !== 0) {
|
|
266
|
+
issues.push({
|
|
267
|
+
type: 'mismatched-backticks',
|
|
268
|
+
line: lineNum,
|
|
269
|
+
cell: cellIdx + 1,
|
|
270
|
+
message: `Cell ${cellIdx + 1} has mismatched backticks`,
|
|
271
|
+
content: cell,
|
|
272
|
+
severity: 'error'
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Check for quote patterns that might indicate typos
|
|
277
|
+
// Pattern: opening quote at start of backtick span
|
|
278
|
+
const codeSpanStartQuote = /`'[^'`]*'?`/g;
|
|
279
|
+
const matches = trimmedCell.match(codeSpanStartQuote);
|
|
280
|
+
if (matches) {
|
|
281
|
+
// Check if any match has quotes both at start AND end inside backticks
|
|
282
|
+
matches.forEach(match => {
|
|
283
|
+
if (match.match(/`'.*'`/)) {
|
|
284
|
+
// This is likely intentional (quoted string in code)
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
// Only opening quote inside backticks - likely error
|
|
288
|
+
if (match.match(/`'[^']*`$/)) {
|
|
289
|
+
issues.push({
|
|
290
|
+
type: 'likely-typo',
|
|
291
|
+
line: lineNum,
|
|
292
|
+
cell: cellIdx + 1,
|
|
293
|
+
message: `Cell ${cellIdx + 1} has likely typo: opening quote inside backticks without matching closing quote`,
|
|
294
|
+
content: cell,
|
|
295
|
+
example: match,
|
|
296
|
+
severity: 'error'
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Check for attributes syntax {...} which requires careful table structure
|
|
303
|
+
if (trimmedCell.includes('{') && trimmedCell.includes('}')) {
|
|
304
|
+
const hasAttributes = /\{[.#][^\}]+\}/.test(trimmedCell);
|
|
305
|
+
if (hasAttributes) {
|
|
306
|
+
issues.push({
|
|
307
|
+
type: 'has-attributes',
|
|
308
|
+
line: lineNum,
|
|
309
|
+
cell: cellIdx + 1,
|
|
310
|
+
message: `Cell ${cellIdx + 1} contains attribute syntax {.class} or {#id} - ensure table structure is correct`,
|
|
311
|
+
content: cell,
|
|
312
|
+
severity: 'info'
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
return issues;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Checks markdown tables in specification files.
|
|
323
|
+
*
|
|
324
|
+
* This health check validates all markdown tables found in specification files,
|
|
325
|
+
* ensuring proper structure and catching common errors that can cause parsing issues.
|
|
326
|
+
*
|
|
327
|
+
* The check performs the following validations:
|
|
328
|
+
* - Table header and separator presence
|
|
329
|
+
* - Consistent column counts across rows
|
|
330
|
+
* - Valid separator line syntax
|
|
331
|
+
* - Detection of problematic characters
|
|
332
|
+
* - Validation of quotes and backticks in cells
|
|
333
|
+
*
|
|
334
|
+
* @param {import('../providers.js').Provider} provider - The provider instance for file operations
|
|
335
|
+
* @returns {Promise<import('../health-check-utils.js').HealthCheckResult>} The health check result
|
|
336
|
+
*
|
|
337
|
+
* @example
|
|
338
|
+
* ```javascript
|
|
339
|
+
* const provider = createLocalProvider('/path/to/repo');
|
|
340
|
+
* const result = await checkMarkdownTables(provider);
|
|
341
|
+
* console.log(result.status); // 'pass', 'warning', or 'error'
|
|
342
|
+
* ```
|
|
343
|
+
*/
|
|
344
|
+
export async function checkMarkdownTables(provider) {
|
|
345
|
+
try {
|
|
346
|
+
// Discover specification files
|
|
347
|
+
const specFiles = await discoverSpecificationFiles(provider);
|
|
348
|
+
|
|
349
|
+
if (specFiles.length === 0) {
|
|
350
|
+
return createHealthCheckResult(
|
|
351
|
+
CHECK_ID,
|
|
352
|
+
'warn',
|
|
353
|
+
'No specification files found to validate tables',
|
|
354
|
+
{
|
|
355
|
+
filesChecked: 0,
|
|
356
|
+
tablesFound: 0,
|
|
357
|
+
tablesWithIssues: 0
|
|
358
|
+
}
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
let totalTables = 0;
|
|
363
|
+
let tablesWithIssues = 0;
|
|
364
|
+
let totalIssues = 0;
|
|
365
|
+
const fileResults = [];
|
|
366
|
+
|
|
367
|
+
// Check each file for tables
|
|
368
|
+
for (const filePath of specFiles) {
|
|
369
|
+
try {
|
|
370
|
+
const content = await provider.readFile(filePath);
|
|
371
|
+
const tables = extractTables(content, filePath);
|
|
372
|
+
|
|
373
|
+
totalTables += tables.length;
|
|
374
|
+
|
|
375
|
+
// Analyze tables for issues
|
|
376
|
+
const tablesWithProblems = tables.filter(t => t.issues.length > 0);
|
|
377
|
+
tablesWithIssues += tablesWithProblems.length;
|
|
378
|
+
totalIssues += tablesWithProblems.reduce((sum, t) => sum + t.issues.length, 0);
|
|
379
|
+
|
|
380
|
+
if (tablesWithProblems.length > 0) {
|
|
381
|
+
fileResults.push({
|
|
382
|
+
file: filePath,
|
|
383
|
+
tablesCount: tables.length,
|
|
384
|
+
tablesWithIssues: tablesWithProblems.length,
|
|
385
|
+
tables: tablesWithProblems
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
} catch (error) {
|
|
389
|
+
console.warn(`Could not read file ${filePath}:`, error.message);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Determine status
|
|
394
|
+
let status = 'pass';
|
|
395
|
+
let message = `All ${totalTables} tables are valid`;
|
|
396
|
+
|
|
397
|
+
if (tablesWithIssues > 0) {
|
|
398
|
+
// Check if any issues are errors vs warnings
|
|
399
|
+
const hasErrors = fileResults.some(fr =>
|
|
400
|
+
fr.tables.some(t =>
|
|
401
|
+
t.issues.some(i => i.severity === 'error' || i.type === 'column-mismatch' || i.type === 'missing-separator')
|
|
402
|
+
)
|
|
403
|
+
);
|
|
404
|
+
|
|
405
|
+
status = hasErrors ? 'fail' : 'warn';
|
|
406
|
+
message = `Found ${totalIssues} issue(s) in ${tablesWithIssues} of ${totalTables} tables`;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return createHealthCheckResult(
|
|
410
|
+
CHECK_ID,
|
|
411
|
+
status,
|
|
412
|
+
message,
|
|
413
|
+
{
|
|
414
|
+
filesChecked: specFiles.length,
|
|
415
|
+
tablesFound: totalTables,
|
|
416
|
+
tablesWithIssues,
|
|
417
|
+
totalIssues,
|
|
418
|
+
details: fileResults
|
|
419
|
+
}
|
|
420
|
+
);
|
|
421
|
+
|
|
422
|
+
} catch (error) {
|
|
423
|
+
return createErrorResult(
|
|
424
|
+
CHECK_ID,
|
|
425
|
+
`Failed to validate markdown tables: ${error.message}`,
|
|
426
|
+
{ error: error.message, stack: error.stack }
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Discovers specification files to check for tables.
|
|
433
|
+
*
|
|
434
|
+
* This function searches for markdown files in common spec directories
|
|
435
|
+
* and the repository root.
|
|
436
|
+
*
|
|
437
|
+
* @param {import('../providers.js').Provider} provider - The provider instance
|
|
438
|
+
* @returns {Promise<string[]>} Array of file paths to check
|
|
439
|
+
* @private
|
|
440
|
+
*/
|
|
441
|
+
async function discoverSpecificationFiles(provider) {
|
|
442
|
+
const files = [];
|
|
443
|
+
const searchPaths = ['spec/', 'specs/', 'docs/', ''];
|
|
444
|
+
|
|
445
|
+
for (const searchPath of searchPaths) {
|
|
446
|
+
try {
|
|
447
|
+
const entries = await provider.listFiles(searchPath);
|
|
448
|
+
|
|
449
|
+
// Filter for markdown files only (not subdirectories)
|
|
450
|
+
const mdFiles = entries.filter(entry =>
|
|
451
|
+
entry.isFile &&
|
|
452
|
+
(entry.name.endsWith('.md') || entry.name.endsWith('.markdown'))
|
|
453
|
+
);
|
|
454
|
+
|
|
455
|
+
// Use the full path from the entry
|
|
456
|
+
files.push(...mdFiles.map(entry => entry.path));
|
|
457
|
+
} catch (error) {
|
|
458
|
+
// Directory doesn't exist or can't be read, continue
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return [...new Set(files)]; // Remove duplicates
|
|
463
|
+
}
|
|
@@ -24,7 +24,7 @@ export const CHECK_ID = 'package-json';
|
|
|
24
24
|
* Human-readable name for this health check.
|
|
25
25
|
* @type {string}
|
|
26
26
|
*/
|
|
27
|
-
export const CHECK_NAME = '
|
|
27
|
+
export const CHECK_NAME = 'package.json';
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* Description of what this health check validates.
|