docguard-cli 0.9.10 → 0.9.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/cli/commands/score.mjs +24 -2
- package/cli/shared-ignore.mjs +43 -0
- package/cli/validators/docs-diff.mjs +14 -22
- package/package.json +1 -1
package/cli/commands/score.mjs
CHANGED
|
@@ -494,8 +494,30 @@ function calcTestingScore(dir, config) {
|
|
|
494
494
|
}
|
|
495
495
|
|
|
496
496
|
// ── Check 4: CI test step (15 pts) ──
|
|
497
|
-
|
|
498
|
-
const
|
|
497
|
+
// Support multiple CI systems — not just GitHub Actions
|
|
498
|
+
const ciFiles = [
|
|
499
|
+
'.github/workflows/ci.yml', '.github/workflows/test.yml',
|
|
500
|
+
'.github/workflows/ci.yaml', '.github/workflows/test.yaml',
|
|
501
|
+
'buildspec.yml', 'buildspec.test.yml', // AWS CodeBuild
|
|
502
|
+
'amplify.yml', // AWS Amplify
|
|
503
|
+
'Jenkinsfile', // Jenkins
|
|
504
|
+
'.circleci/config.yml', // CircleCI
|
|
505
|
+
'.gitlab-ci.yml', // GitLab CI
|
|
506
|
+
'.travis.yml', // Travis CI
|
|
507
|
+
];
|
|
508
|
+
let hasCITest = ciFiles.some(f => existsSync(resolve(dir, f)));
|
|
509
|
+
|
|
510
|
+
// Also check turbo.json for "test" pipeline task
|
|
511
|
+
if (!hasCITest) {
|
|
512
|
+
const turboPath = resolve(dir, 'turbo.json');
|
|
513
|
+
if (existsSync(turboPath)) {
|
|
514
|
+
try {
|
|
515
|
+
const turboContent = readFileSync(turboPath, 'utf-8');
|
|
516
|
+
if (/"test"/.test(turboContent)) hasCITest = true;
|
|
517
|
+
} catch { /* skip */ }
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
499
521
|
if (hasCITest) score += 15;
|
|
500
522
|
|
|
501
523
|
return Math.min(100, score);
|
package/cli/shared-ignore.mjs
CHANGED
|
@@ -74,3 +74,46 @@ export function shouldIgnore(relPath, config, validatorKey) {
|
|
|
74
74
|
|
|
75
75
|
return false;
|
|
76
76
|
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Convert a glob pattern to a RegExp for POSITIVE matching.
|
|
80
|
+
* Unlike globToRegex (used for ignore filtering), this anchors the match
|
|
81
|
+
* to the full relative path from the project root.
|
|
82
|
+
*
|
|
83
|
+
* Supports: * (any chars except /), ** (any path segments), . (literal dot).
|
|
84
|
+
*
|
|
85
|
+
* @param {string} pattern - Glob pattern (e.g., "backend/**\/__tests__/**\/*.test.ts")
|
|
86
|
+
* @returns {RegExp}
|
|
87
|
+
*/
|
|
88
|
+
function globToMatchRegex(pattern) {
|
|
89
|
+
// Normalize: replace **/ with a placeholder that means "zero or more path segments"
|
|
90
|
+
let escaped = pattern
|
|
91
|
+
.replace(/\./g, '\\.')
|
|
92
|
+
.replace(/\*\*\//g, '§STARSTAR§') // **/ → zero-or-more segments
|
|
93
|
+
.replace(/\*\*/g, '.*') // standalone ** → any chars
|
|
94
|
+
.replace(/\*/g, '[^/]*') // single * → any chars except /
|
|
95
|
+
.replace(/§STARSTAR§/g, '(.*/)?'); // **/ → optional path prefix
|
|
96
|
+
return new RegExp(`^${escaped}$`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Check if a relative path matches ANY of the given glob patterns.
|
|
101
|
+
* Purpose-built for POSITIVE matching (e.g., "is this a test file?").
|
|
102
|
+
*
|
|
103
|
+
* ALWAYS rejects paths containing node_modules at any depth.
|
|
104
|
+
* This is the correct function for test file discovery — do NOT use
|
|
105
|
+
* buildIgnoreFilter() for this purpose.
|
|
106
|
+
*
|
|
107
|
+
* @param {string} relPath - Relative path from project root
|
|
108
|
+
* @param {string[]} patterns - Array of glob patterns to match against
|
|
109
|
+
* @returns {boolean} - true if path matches a pattern AND is not in node_modules
|
|
110
|
+
*/
|
|
111
|
+
export function globMatch(relPath, patterns) {
|
|
112
|
+
if (!relPath || !patterns || patterns.length === 0) return false;
|
|
113
|
+
|
|
114
|
+
// Always reject paths containing node_modules at any depth
|
|
115
|
+
if (/(?:^|[/\\])node_modules(?:[/\\]|$)/.test(relPath)) return false;
|
|
116
|
+
|
|
117
|
+
const regexes = patterns.map(p => globToMatchRegex(p));
|
|
118
|
+
return regexes.some(r => r.test(relPath));
|
|
119
|
+
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
13
13
|
import { resolve, join, extname, basename, relative } from 'node:path';
|
|
14
|
-
import { shouldIgnore,
|
|
14
|
+
import { shouldIgnore, globMatch } from '../shared-ignore.mjs';
|
|
15
15
|
|
|
16
16
|
const IGNORE_DIRS = new Set([
|
|
17
17
|
'node_modules', '.git', '.next', 'dist', 'build',
|
|
@@ -139,7 +139,7 @@ function diffEnvVars(dir) {
|
|
|
139
139
|
* Diff test files between TEST-SPEC.md and actual code.
|
|
140
140
|
* Uses config.testPatterns if available, otherwise falls back to
|
|
141
141
|
* scanning standard test directories.
|
|
142
|
-
* Always ignores node_modules via
|
|
142
|
+
* Always ignores node_modules via globMatch().
|
|
143
143
|
*/
|
|
144
144
|
function diffTests(dir, config) {
|
|
145
145
|
const testSpecPath = resolve(dir, 'docs-canonical/TEST-SPEC.md');
|
|
@@ -153,17 +153,12 @@ function diffTests(dir, config) {
|
|
|
153
153
|
docTests.add(match[1]);
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
// Collect test files from disk using globMatch (always excludes node_modules)
|
|
156
157
|
const codeTests = new Set();
|
|
157
|
-
|
|
158
|
-
// Use testPatterns from config if available
|
|
159
158
|
const testPatterns = config?.testPatterns || [];
|
|
159
|
+
|
|
160
160
|
if (testPatterns.length > 0) {
|
|
161
|
-
// Use configured patterns
|
|
162
|
-
const patternFilter = buildIgnoreFilter(testPatterns.map(p => {
|
|
163
|
-
// Invert the pattern: we WANT files matching these patterns
|
|
164
|
-
return p;
|
|
165
|
-
}));
|
|
166
|
-
// Walk the project and collect matching test files
|
|
161
|
+
// Use configured patterns — globMatch handles node_modules exclusion
|
|
167
162
|
const allTestFiles = getTestFilesFromPatterns(dir, testPatterns, config);
|
|
168
163
|
for (const f of allTestFiles) {
|
|
169
164
|
codeTests.add(f);
|
|
@@ -192,17 +187,18 @@ function diffTests(dir, config) {
|
|
|
192
187
|
|
|
193
188
|
/**
|
|
194
189
|
* Find test files matching configured testPatterns.
|
|
195
|
-
*
|
|
190
|
+
* Uses globMatch() for pattern matching — always excludes node_modules.
|
|
191
|
+
* Results are deduplicated via Set (handles overlapping patterns).
|
|
196
192
|
*/
|
|
197
193
|
function getTestFilesFromPatterns(dir, patterns, config) {
|
|
198
|
-
const results =
|
|
199
|
-
const testFileRegex = /\.(test|spec)\.(mjs|cjs|[jt]sx?)$/;
|
|
194
|
+
const results = new Set();
|
|
200
195
|
|
|
201
196
|
function walk(currentDir) {
|
|
202
197
|
let entries;
|
|
203
198
|
try { entries = readdirSync(currentDir); } catch { return; }
|
|
204
199
|
|
|
205
200
|
for (const entry of entries) {
|
|
201
|
+
// Skip node_modules and other ignored dirs at directory level (fast path)
|
|
206
202
|
if (IGNORE_DIRS.has(entry) || entry.startsWith('.')) continue;
|
|
207
203
|
const fullPath = join(currentDir, entry);
|
|
208
204
|
try {
|
|
@@ -211,15 +207,11 @@ function getTestFilesFromPatterns(dir, patterns, config) {
|
|
|
211
207
|
walk(fullPath);
|
|
212
208
|
} else if (stat.isFile()) {
|
|
213
209
|
const relPath = relative(dir, fullPath);
|
|
214
|
-
// Skip files in ignored paths
|
|
210
|
+
// Skip files in globally ignored paths
|
|
215
211
|
if (config && shouldIgnore(relPath, config)) continue;
|
|
216
|
-
//
|
|
217
|
-
if (
|
|
218
|
-
|
|
219
|
-
const patternFilter = buildIgnoreFilter(patterns);
|
|
220
|
-
if (patternFilter(relPath)) {
|
|
221
|
-
results.push(relPath);
|
|
222
|
-
}
|
|
212
|
+
// Use globMatch for positive pattern matching (rejects node_modules internally)
|
|
213
|
+
if (globMatch(relPath, patterns)) {
|
|
214
|
+
results.add(relPath);
|
|
223
215
|
}
|
|
224
216
|
}
|
|
225
217
|
} catch { /* skip */ }
|
|
@@ -227,7 +219,7 @@ function getTestFilesFromPatterns(dir, patterns, config) {
|
|
|
227
219
|
}
|
|
228
220
|
|
|
229
221
|
walk(dir);
|
|
230
|
-
return results;
|
|
222
|
+
return [...results];
|
|
231
223
|
}
|
|
232
224
|
|
|
233
225
|
function getFilesRecursive(dir, config) {
|
package/package.json
CHANGED