cabloy 5.1.199 → 5.1.200

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/.cabloy-version CHANGED
@@ -1 +1 @@
1
- 5.1.199
1
+ 5.1.200
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.1.200
4
+
5
+ ### Features
6
+
7
+ - Apply feature updates.
8
+
3
9
  ## 5.1.199
4
10
 
5
11
  ### Improvements
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cabloy",
3
- "version": "5.1.199",
3
+ "version": "5.1.200",
4
4
  "gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
5
5
  "description": "A Node.js fullstack framework",
6
6
  "keywords": [
@@ -208,6 +208,50 @@ test('local Markdown destinations, fragments and reference links are checked', a
208
208
  );
209
209
  });
210
210
 
211
+ test('link destinations are not authority references but labels and prose remain audited', async t => {
212
+ const records = {
213
+ ...BASELINE,
214
+ 'rollout.md':
215
+ '# Rollout\n\n[WBS-MISSING-01](./evidence/WBS-DEMO-10-01-current-source.md) and `ATP-MISSING-01`.\n\n![SRS-MISSING-01](<./evidence/WBS-DEMO-10-01-image.md>)\n\n[WBS-DEMO-10-01][proof]\n\n[proof]: ./evidence/WBS-DEMO-10-01-referenced.md\n',
216
+ 'evidence/WBS-DEMO-10-01-current-source.md': '# Current source\n',
217
+ 'evidence/WBS-DEMO-10-01-image.md': '# Image\n',
218
+ 'evidence/WBS-DEMO-10-01-referenced.md': '# Referenced\n',
219
+ };
220
+ const result = await auditPlanning(await fixture(t, records));
221
+ assert.deepEqual(
222
+ result.diagnostics.map(diagnostic => [
223
+ diagnostic.code,
224
+ diagnostic.file,
225
+ diagnostic.line,
226
+ diagnostic.id,
227
+ ]),
228
+ [
229
+ ['undefined-reference', 'rollout.md', 3, 'ATP-MISSING-01'],
230
+ ['undefined-reference', 'rollout.md', 3, 'WBS-MISSING-01'],
231
+ ['undefined-reference', 'rollout.md', 5, 'SRS-MISSING-01'],
232
+ ],
233
+ );
234
+ });
235
+
236
+ test('destination masking preserves local link and reference diagnostics', async t => {
237
+ const records = {
238
+ ...BASELINE,
239
+ 'rollout.md':
240
+ '# Rollout\n\n[Missing](./evidence/WBS-DEMO-10-01-missing.md)\n[Bad fragment](./evidence/WBS-DEMO-10-01-existing.md#absent)\n[Good fragment](./evidence/WBS-DEMO-10-01-existing.md#present)\n[Bad encoding](./evidence/WBS-DEMO-10-01-%ZZ.md)\n[Undefined][missing]\n\n[unused]: <./evidence/WBS-DEMO-10-01-unused.md>\n',
241
+ 'evidence/WBS-DEMO-10-01-existing.md': '# Present\n',
242
+ };
243
+ const result = await auditPlanning(await fixture(t, records));
244
+ assert.deepEqual(
245
+ result.diagnostics.map(diagnostic => [diagnostic.code, diagnostic.line]),
246
+ [
247
+ ['link-target', 3],
248
+ ['link-fragment', 4],
249
+ ['link-format', 6],
250
+ ['link-reference', 7],
251
+ ],
252
+ );
253
+ });
254
+
211
255
  test('exact range endpoints do not masquerade as exact traceability', async t => {
212
256
  const records = {
213
257
  ...BASELINE,
@@ -142,17 +142,31 @@ function headingIds(markdown) {
142
142
  return slugs;
143
143
  }
144
144
 
145
+ const LINK_DEFINITION = /^(\s*\[([^\]]+)\]:\s*)(<[^>]+>|\S+)/;
146
+ const INLINE_LINK = /(!?\[[^\]]*\]\(\s*)(<[^>]+>|[^\s)]+)((?:\s+["'][^\n]*["'])?\s*\))/g;
147
+
148
+ function referenceText(text) {
149
+ return text
150
+ .replace(
151
+ LINK_DEFINITION,
152
+ (_, start, _label, destination) => `${start}${' '.repeat(destination.length)}`,
153
+ )
154
+ .replace(
155
+ INLINE_LINK,
156
+ (_, start, destination, end) => `${start}${' '.repeat(destination.length)}${end}`,
157
+ );
158
+ }
159
+
145
160
  function markdownLinks(markdown) {
146
161
  const lines = markdownLines(markdown);
147
162
  const references = new Map();
148
163
  const links = [];
149
164
  for (const { text, line } of lines) {
150
- const declaration = text.match(/^\s*\[([^\]]+)\]:\s*(<[^>]+>|\S+)/);
165
+ const declaration = text.match(LINK_DEFINITION);
151
166
  if (declaration)
152
- references.set(declaration[1].trim().toLowerCase(), declaration[2].replace(/^<|>$/g, ''));
153
- const inline = /!?\[[^\]]*\]\(\s*(<[^>]+>|[^\s)]+)(?:\s+["'][^\n]*["'])?\s*\)/g;
154
- for (const match of text.matchAll(inline))
155
- links.push({ destination: match[1].replace(/^<|>$/g, ''), line });
167
+ references.set(declaration[2].trim().toLowerCase(), declaration[3].replace(/^<|>$/g, ''));
168
+ for (const match of text.matchAll(INLINE_LINK))
169
+ links.push({ destination: match[2].replace(/^<|>$/g, ''), line });
156
170
  const referenced = /!?\[([^\]]+)\]\[([^\]]*)\]/g;
157
171
  for (const match of text.matchAll(referenced))
158
172
  links.push({ reference: (match[2] || match[1]).trim().toLowerCase(), line });
@@ -260,7 +274,7 @@ export async function auditPlanning(
260
274
  for (const [file, markdown] of records) {
261
275
  for (const { text, line } of markdownLines(markdown)) {
262
276
  for (const prefix of OWNERS.keys()) {
263
- for (const id of findIdentifiers(text, prefix)) {
277
+ for (const id of findIdentifiers(referenceText(text), prefix)) {
264
278
  if (!definitions.has(id)) {
265
279
  report(
266
280
  'undefined-reference',