xdrs-core 0.15.3 → 0.15.4
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/lib/lint.js +27 -2
- package/lib/lint.test.js +26 -0
- package/package.json +1 -1
package/lib/lint.js
CHANGED
|
@@ -248,7 +248,6 @@ function lintXdrFile(xdrsRoot, scopeName, typeName, filePath, xdrNumbers, errors
|
|
|
248
248
|
}
|
|
249
249
|
|
|
250
250
|
const number = match[1];
|
|
251
|
-
const shortTitle = match[2];
|
|
252
251
|
const previous = xdrNumbers.get(number);
|
|
253
252
|
if (previous) {
|
|
254
253
|
errors.push(`Duplicate XDR number ${number} in ${scopeName}/${typeName}: ${toDisplayPath(previous)} and ${toDisplayPath(filePath)}`);
|
|
@@ -267,11 +266,37 @@ function lintXdrFile(xdrsRoot, scopeName, typeName, filePath, xdrNumbers, errors
|
|
|
267
266
|
errors.push(`XDR title must start with "${expectedHeader}": ${toDisplayPath(filePath)}`);
|
|
268
267
|
}
|
|
269
268
|
|
|
270
|
-
const expectedName =
|
|
269
|
+
const expectedName = extractExpectedXdrNameFromHeading(firstLine)
|
|
270
|
+
|| `${scopeName}-${TYPE_TO_ID[typeName]}-${number}-${match[2]}`;
|
|
271
271
|
lintXdrFrontmatter(content, expectedName, filePath, errors);
|
|
272
272
|
lintDocumentLinks(filePath, errors);
|
|
273
273
|
}
|
|
274
274
|
|
|
275
|
+
function extractExpectedXdrNameFromHeading(headingLine) {
|
|
276
|
+
const match = headingLine.match(/^#\s+([a-z0-9_]+-(?:adr|bdr|edr)-\d{3,}):\s+(.+?)\s*$/);
|
|
277
|
+
if (!match) {
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const identifier = match[1];
|
|
282
|
+
const titleSlug = slugifyTitle(match[2]);
|
|
283
|
+
if (!titleSlug) {
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return `${identifier}-${titleSlug}`;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function slugifyTitle(title) {
|
|
291
|
+
return title
|
|
292
|
+
.normalize('NFKD')
|
|
293
|
+
.replace(/[\u0300-\u036f]/g, '')
|
|
294
|
+
.toLowerCase()
|
|
295
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
296
|
+
.replace(/^-+|-+$/g, '')
|
|
297
|
+
.replace(/--+/g, '-');
|
|
298
|
+
}
|
|
299
|
+
|
|
275
300
|
function lintXdrFrontmatter(content, expectedName, filePath, errors) {
|
|
276
301
|
const fm = extractFrontmatter(content);
|
|
277
302
|
if (!fm.present) {
|
package/lib/lint.test.js
CHANGED
|
@@ -82,6 +82,32 @@ test('skips read-only files by default and checks them when ignoreReadOnly is fa
|
|
|
82
82
|
expect(allResult.errors.join('\n')).toContain('Broken local link in');
|
|
83
83
|
});
|
|
84
84
|
|
|
85
|
+
test('derives expected frontmatter name from the markdown heading title', () => {
|
|
86
|
+
const workspaceRoot = createWorkspace('heading-name-match', {
|
|
87
|
+
'.xdrs/index.md': rootIndex(),
|
|
88
|
+
'.xdrs/_local/adrs/index.md': localAdrIndex([
|
|
89
|
+
'- [002-scope-guidelines](principles/002-xdr-scope-guidelines.md) - Scope guidelines'
|
|
90
|
+
]),
|
|
91
|
+
'.xdrs/_local/adrs/principles/002-xdr-scope-guidelines.md': [
|
|
92
|
+
'---',
|
|
93
|
+
'name: _local-adr-002-xdr-scope-guidelines-for-agentme',
|
|
94
|
+
'description: Test XDR document',
|
|
95
|
+
'---',
|
|
96
|
+
'',
|
|
97
|
+
'# _local-adr-002: XDR scope guidelines for agentme',
|
|
98
|
+
'',
|
|
99
|
+
'## Context and Problem Statement',
|
|
100
|
+
'',
|
|
101
|
+
'Test body.',
|
|
102
|
+
''
|
|
103
|
+
].join('\n'),
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const result = lintWorkspace(workspaceRoot, { ignoreReadOnly: false });
|
|
107
|
+
|
|
108
|
+
expect(result.errors.join('\n')).not.toContain('XDR frontmatter name must be');
|
|
109
|
+
});
|
|
110
|
+
|
|
85
111
|
function createWorkspace(name, files) {
|
|
86
112
|
const workspaceRoot = path.join(tmpRoot, name);
|
|
87
113
|
fs.mkdirSync(workspaceRoot, { recursive: true });
|
package/package.json
CHANGED