spec-up-t 1.2.2 → 1.2.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.
Files changed (49) hide show
  1. package/.sonarlint/connectedMode.json +5 -0
  2. package/assets/compiled/body.js +31 -31
  3. package/assets/compiled/head.css +5 -5
  4. package/assets/compiled/head.js +3 -3
  5. package/assets/css/adjust-font-size.css +6 -11
  6. package/assets/css/backToTop.css +0 -1
  7. package/assets/css/index.css +1 -2
  8. package/assets/css/pdf-styles.css +23 -27
  9. package/assets/css/repo-issues.css +0 -6
  10. package/assets/css/search.css +0 -1
  11. package/assets/css/sidebar-toc.css +13 -12
  12. package/assets/css/terms-and-definitions.css +43 -37
  13. package/assets/js/add-href-to-snapshot-link.js +2 -1
  14. package/assets/js/addAnchorsToTerms.js +0 -1
  15. package/assets/js/adjust-font-size.js +0 -9
  16. package/assets/js/create-alphabet-index.js +1 -1
  17. package/assets/js/custom-elements.js +13 -18
  18. package/assets/js/declare-markdown-it.js +1 -1
  19. package/assets/js/highlightMenuItems.js +3 -3
  20. package/assets/js/index.js +1 -5
  21. package/assets/js/insert-trefs.js +2 -2
  22. package/assets/js/modal.js +3 -3
  23. package/assets/js/search.js +3 -3
  24. package/assets/js/utils.js +2 -3
  25. package/index.js +5 -15
  26. package/package.json +2 -2
  27. package/src/add-remove-xref-source.js +0 -2
  28. package/src/collect-external-references.js +187 -179
  29. package/src/collectExternalReferences/fetchTermsFromIndex.js +2 -1
  30. package/src/create-external-specs-list.js +1 -1
  31. package/src/fix-markdown-files.js +152 -90
  32. package/src/health-check/external-specs-checker.js +173 -94
  33. package/src/health-check/output-gitignore-checker.js +327 -191
  34. package/src/health-check/specs-configuration-checker.js +288 -210
  35. package/src/health-check/term-references-checker.js +200 -123
  36. package/src/health-check/tref-term-checker.js +264 -179
  37. package/src/health-check.js +51 -35
  38. package/src/init.js +0 -3
  39. package/src/install-from-boilerplate/boilerplate/gitignore +2 -1
  40. package/src/install-from-boilerplate/config-system-files.js +9 -1
  41. package/src/install-from-boilerplate/copy-system-files.js +1 -1
  42. package/src/install-from-boilerplate/menu.sh +17 -3
  43. package/src/markdown-it-extensions.js +199 -106
  44. package/src/references.js +1 -2
  45. package/src/utils/doesUrlExist.js +7 -5
  46. package/templates/template.html +1 -2
  47. package/assets/js/insert-xrefs.js +0 -370
  48. package/src/create-term-relations.js +0 -131
  49. package/src/prepare-tref.js +0 -174
@@ -60,18 +60,15 @@ function getRepoInfo() {
60
60
 
61
61
  // Look for specs.json in the current working directory (where the command is run from)
62
62
  const specsPath = path.join(process.cwd(), 'specs.json');
63
- // console.log(`Looking for specs.json at: ${specsPath}`);
64
63
 
65
64
  if (fs.existsSync(specsPath)) {
66
- // console.log('specs.json found!');
67
65
  const specsContent = fs.readFileSync(specsPath, 'utf8');
68
66
  const specs = JSON.parse(specsContent);
69
67
 
70
68
  // Check if source field exists and has required properties
71
- if (specs?.specs?.[0]?.source &&
72
- specs.specs[0].source.host &&
73
- specs.specs[0].source.account &&
74
- specs.specs[0].source.repo) {
69
+ if (specs?.specs?.[0]?.source?.host &&
70
+ specs?.specs?.[0]?.source?.account &&
71
+ specs?.specs?.[0]?.source?.repo) {
75
72
 
76
73
  const sourceInfo = specs.specs[0].source;
77
74
 
@@ -128,7 +125,6 @@ function getRepoInfo() {
128
125
  }
129
126
 
130
127
  // Return default values if specs.json doesn't exist or doesn't contain the required information
131
- // console.log('Using default repository values');
132
128
  return {
133
129
  host: 'github',
134
130
  account: 'blockchain-bird',
@@ -158,13 +154,24 @@ function checkRepositoryExists(host, account, repo) {
158
154
  }
159
155
  }
160
156
 
157
+ // Helper function to get the appropriate file open command based on platform
158
+ function getOpenCommand() {
159
+ if (process.platform === 'win32') {
160
+ return 'start';
161
+ } else if (process.platform === 'darwin') {
162
+ return 'open';
163
+ } else {
164
+ return 'xdg-open';
165
+ }
166
+ }
167
+
161
168
  // Helper function to format current time for the filename
162
169
  function getFormattedTimestamp() {
163
170
  const now = new Date();
164
171
  return now.toISOString()
165
172
  .replace(/[T:]/g, '-')
166
173
  .replace(/\..+/, '')
167
- .replace(/[Z]/g, '');
174
+ .replace(/Z/g, 'Z');
168
175
  }
169
176
 
170
177
  // Helper function to generate a human-readable timestamp for display
@@ -172,6 +179,32 @@ function getHumanReadableTimestamp() {
172
179
  return new Date().toLocaleString();
173
180
  }
174
181
 
182
+ // Helper function to determine status display parameters based on result
183
+ function getStatusDisplay(result) {
184
+ if (result.status === 'warning' || result.success === 'partial') {
185
+ // Warning status
186
+ return {
187
+ statusClass: 'text-warning',
188
+ statusIcon: '<i class="bi bi-exclamation-triangle-fill"></i>',
189
+ statusText: 'Warning'
190
+ };
191
+ } else if (result.success) {
192
+ // Pass status
193
+ return {
194
+ statusClass: 'text-success',
195
+ statusIcon: '<i class="bi bi-check-circle-fill"></i>',
196
+ statusText: 'Pass'
197
+ };
198
+ } else {
199
+ // Fail status
200
+ return {
201
+ statusClass: 'text-danger',
202
+ statusIcon: '<i class="bi bi-x-circle-fill"></i>',
203
+ statusText: 'Fail'
204
+ };
205
+ }
206
+ }
207
+
175
208
  // Main function to run all checks and generate the report
176
209
  async function runHealthCheck() {
177
210
  console.log('Running health checks...');
@@ -249,8 +282,7 @@ function generateReport(checkResults) {
249
282
 
250
283
  // Open the report in the default browser
251
284
  try {
252
- const openCommand = process.platform === 'win32' ? 'start' :
253
- process.platform === 'darwin' ? 'open' : 'xdg-open';
285
+ const openCommand = getOpenCommand();
254
286
  execSync(`${openCommand} "${reportPath}"`);
255
287
  } catch (error) {
256
288
  console.error('Failed to open the report:', error);
@@ -263,6 +295,12 @@ function generateHtmlReport(checkResults, timestamp, repoInfo) {
263
295
 
264
296
  // Add repository verification check at the beginning if needed
265
297
  if (repoInfo && repoInfo.verified === false) {
298
+ const failStatus = {
299
+ statusClass: 'text-danger',
300
+ statusIcon: '<i class="bi bi-x-circle-fill"></i>',
301
+ statusText: 'Fail'
302
+ };
303
+
266
304
  // Create a new section at the top for repository verification
267
305
  resultsHtml += `
268
306
  <div class="card mb-4 results-card alert-danger" data-section="repository-verification">
@@ -280,8 +318,8 @@ function generateHtmlReport(checkResults, timestamp, repoInfo) {
280
318
  </thead>
281
319
  <tbody>
282
320
  <tr data-status="fail" class="check-row">
283
- <td class="text-danger" style="white-space: nowrap;">
284
- <i class="bi bi-x-circle-fill"></i> <span style="vertical-align: middle;">Fail</span>
321
+ <td class="${failStatus.statusClass}" style="white-space: nowrap;">
322
+ ${failStatus.statusIcon} <span style="vertical-align: middle;">${failStatus.statusText}</span>
285
323
  </td>
286
324
  <td>Repository existence check</td>
287
325
  <td>The repository at https://${repoInfo.host}.com/${repoInfo.account}/${repoInfo.repo} does not exist or is not accessible. Please verify the repository information in specs.json.</td>
@@ -312,29 +350,7 @@ function generateHtmlReport(checkResults, timestamp, repoInfo) {
312
350
  `;
313
351
 
314
352
  section.results.forEach(result => {
315
- let statusClass, statusIcon, statusText;
316
-
317
- if (result.status === 'warning') {
318
- // Warning status
319
- statusClass = 'text-warning';
320
- statusIcon = '<i class="bi bi-exclamation-triangle-fill"></i>';
321
- statusText = 'Warning';
322
- } else if (result.success === 'partial') {
323
- // Partial success (warning) status
324
- statusClass = 'text-warning';
325
- statusIcon = '<i class="bi bi-exclamation-triangle-fill"></i>';
326
- statusText = 'Warning';
327
- } else if (result.success) {
328
- // Pass status
329
- statusClass = 'text-success';
330
- statusIcon = '<i class="bi bi-check-circle-fill"></i>';
331
- statusText = 'Pass';
332
- } else {
333
- // Fail status
334
- statusClass = 'text-danger';
335
- statusIcon = '<i class="bi bi-x-circle-fill"></i>';
336
- statusText = 'Fail';
337
- }
353
+ const { statusClass, statusIcon, statusText } = getStatusDisplay(result);
338
354
 
339
355
  // Add data-status attribute to identify rows by status and reorder columns to put status first
340
356
  resultsHtml += `
package/src/init.js CHANGED
@@ -2,7 +2,6 @@ const fs = require('fs-extra');
2
2
  const path = require('path');
3
3
  const outputDir = path.join(process.cwd(), 'output');
4
4
  const initFlagPath = path.join(outputDir, 'init.flag');
5
- const collectExternalReferences = require('./collect-external-references.js').collectExternalReferences;
6
5
 
7
6
  async function initialize() {
8
7
  try {
@@ -13,8 +12,6 @@ async function initialize() {
13
12
 
14
13
  // Place the init script here
15
14
 
16
- // prepareTref(path.join(config.specs[0].spec_directory, config.specs[0].spec_terms_directory));
17
-
18
15
  // End of the init script
19
16
 
20
17
  // Create the init flag file
@@ -7,4 +7,5 @@ dist
7
7
  .env
8
8
  coverage
9
9
  build
10
- .history
10
+ .history
11
+ /output/
@@ -1,3 +1,11 @@
1
- const systemFiles = ['README.md', '.env.example', '.github/workflows/menu.yml', '.github/workflows/render-specs.yml', '.github/workflows/set-gh-pages.yml'];
1
+ const systemFiles = [
2
+ 'README.md',
3
+ '.env.example',
4
+ '.github/workflows/menu.yml',
5
+ '.github/workflows/render-specs.yml',
6
+ '.github/workflows/set-gh-pages.yml',
7
+ 'assets/test.json',
8
+ 'assets/test.text',
9
+ ];
2
10
 
3
11
  module.exports = { systemFiles };
@@ -20,7 +20,7 @@ function copySystemFiles() {
20
20
  fs.cpSync(srcPath, destPath, { recursive: true });
21
21
  console.log(`✅ Copied ${item} to ${destPath}`);
22
22
  } catch (error) {
23
- console.error(`❌ Failed to copy ${item} to ${destPath}:`);
23
+ console.error(`❌ Failed to copy ${item} to ${destPath}:`, error);
24
24
  }
25
25
  });
26
26
 
@@ -101,6 +101,20 @@ function show_progress() {
101
101
  }
102
102
 
103
103
  # Main script
104
- display_intro
105
- prompt_input
106
- handle_choice
104
+ if [[ -n "$1" && "$1" =~ ^[0-9]$ ]]; then
105
+ choice="$1"
106
+ handle_choice
107
+ else
108
+ display_intro
109
+ prompt_input
110
+ # Allow user to quit with Q/q
111
+ if [[ "$choice" =~ ^[Qq]$ ]]; then
112
+ clear
113
+ echo -e "\n\n ************************************"
114
+ echo " Goodbye! You chose to exit."
115
+ echo -e " ************************************\n\n"
116
+ echo -e "\n\n\nℹ️ Type 'npm run menu' to return to the main menu.\n"
117
+ exit 0
118
+ fi
119
+ handle_choice
120
+ fi
@@ -1,8 +1,14 @@
1
1
  'use strict';
2
2
 
3
- const levels = 2;
4
- const openString = '['.repeat(levels);
5
- const closeString = ']'.repeat(levels);
3
+ /**
4
+ * Configuration for custom template syntax [[example]] used throughout the markdown parsing
5
+ * These constants define how template markers are identified and processed
6
+ */
7
+ const levels = 2; // Number of bracket characters used for template markers
8
+ const openString = '['.repeat(levels); // Opening delimiter for template markers, e.g., '[['
9
+ const closeString = ']'.repeat(levels); // Closing delimiter for template markers, e.g., ']]'
10
+ // Regular expression to extract template type and arguments from content between delimiters
11
+ // Captures: 1st group = template type (e.g., "ref", "tref"), 2nd group = optional arguments
6
12
  const contentRegex = /\s*([^\s\[\]:]+):?\s*([^\]\n]+)?/i;
7
13
 
8
14
  module.exports = function (md, templates = {}) {
@@ -23,7 +29,7 @@ module.exports = function (md, templates = {}) {
23
29
  const token = tokens[idx];
24
30
  const classIndex = token.attrIndex('class');
25
31
  const tableClasses = 'table table-striped table-bordered table-hover';
26
-
32
+
27
33
  if (classIndex < 0) {
28
34
  token.attrPush(['class', tableClasses]);
29
35
  } else {
@@ -34,12 +40,12 @@ module.exports = function (md, templates = {}) {
34
40
  .split(' ')
35
41
  .filter(cls => !existingClasses.includes(cls))
36
42
  .join(' ');
37
-
43
+
38
44
  if (classesToAdd) {
39
45
  token.attrs[classIndex][1] = existingClasses + ' ' + classesToAdd;
40
46
  }
41
47
  }
42
-
48
+
43
49
  // Add the responsive wrapper div before the table
44
50
  return '<div class="table-responsive">' + originalTableRender(tokens, idx, options, env, self);
45
51
  };
@@ -50,30 +56,43 @@ module.exports = function (md, templates = {}) {
50
56
  return originalTableCloseRender(tokens, idx, options, env, self) + '</div>';
51
57
  };
52
58
 
59
+ /**
60
+ * Custom template syntax rule for markdown-it
61
+ * Processes template markers like [[template-type:arg1,arg2]] in markdown content
62
+ * and converts them to tokens that can be processed by template renderers
63
+ */
53
64
  md.inline.ruler.after('emphasis', 'templates', function templates_ruler(state, silent) {
54
-
65
+ // Get the current parsing position
55
66
  var start = state.pos;
67
+ // Check if we're at a template opening marker
56
68
  let prefix = state.src.slice(start, start + levels);
57
69
  if (prefix !== openString) return false;
70
+ // Find the matching closing marker
58
71
  var indexOfClosingBrace = state.src.indexOf(closeString, start);
59
72
 
60
73
  if (indexOfClosingBrace > 0) {
61
-
74
+ // Extract the template content using regex
62
75
  let match = contentRegex.exec(state.src.slice(start + levels, indexOfClosingBrace));
63
76
  if (!match) return false;
64
77
 
78
+ // Get template type and find a matching template handler
65
79
  let type = match[1];
66
80
  let template = templates.find(t => t.filter(type) && t);
67
81
  if (!template) return false;
68
82
 
83
+ // Parse template arguments (comma-separated)
69
84
  let args = match[2] ? match[2].trim().split(/\s*,+\s*/) : [];
85
+ // Create a template token to be processed during rendering
70
86
  let token = state.push('template', '', 0);
71
87
  token.content = match[0];
72
88
  token.info = { type, template, args };
89
+
90
+ // If the template has a parse function, use it to preprocess the token
73
91
  if (template.parse) {
74
92
  token.content = template.parse(token, type, ...args) || token.content;
75
93
  }
76
94
 
95
+ // Advance the parser position past the template
77
96
  state.pos = indexOfClosingBrace + levels;
78
97
  return true;
79
98
  }
@@ -81,6 +100,10 @@ module.exports = function (md, templates = {}) {
81
100
  return false;
82
101
  });
83
102
 
103
+ /**
104
+ * Renderer for template tokens
105
+ * Takes template tokens created during parsing and renders them using their associated template handler
106
+ */
84
107
  md.renderer.rules.template = function (tokens, idx, options, env, renderer) {
85
108
  let token = tokens[idx];
86
109
  let template = token.info.template;
@@ -90,7 +113,16 @@ module.exports = function (md, templates = {}) {
90
113
  return token.content;
91
114
  }
92
115
 
116
+ /**
117
+ * Regular expression to extract domains and path segments from URLs
118
+ * Used to add path-related attributes to links for styling and behavior
119
+ */
93
120
  let pathSegmentRegex = /(?:http[s]*:\/\/([^\/]*)|(?:\/([^\/?]*)))/g;
121
+
122
+ /**
123
+ * Custom link_open renderer that adds path attributes for styling and behavior
124
+ * Extracts domain and path segments from href attributes and adds them as path-X attributes
125
+ */
94
126
  md.renderer.rules.link_open = function (tokens, idx, options, env, renderer) {
95
127
  let token = tokens[idx];
96
128
  let attrs = token.attrs.reduce((str, attr) => {
@@ -105,6 +137,7 @@ module.exports = function (md, templates = {}) {
105
137
  return str += name + '="' + value + '" ';
106
138
  }, '');
107
139
  let anchor = `<a ${attrs}>`;
140
+ // Special handling for auto-detected links (linkify)
108
141
  return token.markup === 'linkify' ? anchor + '<span>' : anchor;
109
142
  }
110
143
 
@@ -120,131 +153,180 @@ module.exports = function (md, templates = {}) {
120
153
  // Variable to keep track of whether the class has been added to the first <dl> after the target HTML
121
154
  let classAdded = false;
122
155
 
123
- md.renderer.rules.dl_open = function (tokens, idx, options, env, self) {
124
- const targetHtml = 'terminology-section-start-h7vc6omi2hr2880';
125
- let targetIndex = -1;
126
-
127
- // Find the index of the target HTML
156
+ /**
157
+ * Helper function to locate a specific marker in the token stream
158
+ * Used to identify the terminology section in the document
159
+ *
160
+ * @param {Array} tokens - The token array to search through
161
+ * @param {String} targetHtml - The HTML string to look for in token content
162
+ * @return {Number} The index of the token containing targetHtml, or -1 if not found
163
+ */
164
+ function findTargetIndex(tokens, targetHtml) {
128
165
  for (let i = 0; i < tokens.length; i++) {
129
166
  if (tokens[i].content && tokens[i].content.includes(targetHtml)) {
130
- targetIndex = i;
131
- break;
167
+ return i;
132
168
  }
133
169
  }
170
+ return -1;
171
+ }
134
172
 
135
- // Add class to the first <dl> only if it comes after the target HTML
136
- if (targetIndex !== -1 && idx > targetIndex && !classAdded) {
137
- tokens[idx].attrPush(['class', 'terms-and-definitions-list']);
138
- classAdded = true;
139
- }
140
-
141
- let lastDdIndex = -1;
142
- let currentDtIndex = -1; // Track current dt to detect empty dt elements
143
-
144
- // First pass - check for and mark empty dt elements
145
- // This scan identifies definition terms that have no content (empty dt elements)
146
- // which is one of the root causes of the issues we're fixing
147
- for (let i = idx + 1; i < tokens.length; i++) {
173
+ /**
174
+ * Helper function to identify and mark empty definition term elements
175
+ * Empty dt elements cause rendering and styling issues, so we mark them for special handling
176
+ *
177
+ * @param {Array} tokens - The token array to process
178
+ * @param {Number} startIdx - The index in the token array to start processing from
179
+ */
180
+ function markEmptyDtElements(tokens, startIdx) {
181
+ for (let i = startIdx; i < tokens.length; i++) {
148
182
  if (tokens[i].type === 'dl_close') {
149
- break;
183
+ break; // Stop when we reach the end of this definition list
150
184
  }
151
-
152
- if (tokens[i].type === 'dt_open') {
153
- currentDtIndex = i;
154
- // Check if this is an empty dt (no content between dt_open and dt_close)
155
- // An empty dt is when a dt_close token immediately follows a dt_open token
156
- if (i + 1 < tokens.length && tokens[i + 1].type === 'dt_close') {
157
- // Mark this dt pair for handling by adding an isEmpty property
158
- // This property will be used later to skip rendering these empty elements
159
- tokens[i].isEmpty = true;
160
- tokens[i + 1].isEmpty = true;
161
- }
185
+
186
+ // An empty dt element is one where dt_open is immediately followed by dt_close
187
+ // with no content in between
188
+ if (tokens[i].type === 'dt_open' &&
189
+ i + 1 < tokens.length &&
190
+ tokens[i + 1].type === 'dt_close') {
191
+ // Mark both opening and closing tokens so they can be skipped during rendering
192
+ tokens[i].isEmpty = true;
193
+ tokens[i + 1].isEmpty = true;
162
194
  }
163
195
  }
196
+ }
164
197
 
165
- // Second pass - add classes and handle last-dd
166
- // Now that we've identified empty dt elements, we can process the tokens
167
- // while skipping the empty ones
168
- for (let i = idx + 1; i < tokens.length; i++) {
198
+ /**
199
+ * Helper function to add a 'last-dd' class to a dd token
200
+ * This enables special styling for the last definition description in a group
201
+ *
202
+ * @param {Array} tokens - The token array containing the dd token
203
+ * @param {Number} ddIndex - The index of the dd_open token to modify
204
+ */
205
+ function addLastDdClass(tokens, ddIndex) {
206
+ if (ddIndex === -1) return;
207
+
208
+ const ddToken = tokens[ddIndex];
209
+ const classIndex = ddToken.attrIndex('class');
210
+ if (classIndex < 0) {
211
+ ddToken.attrPush(['class', 'last-dd']);
212
+ } else {
213
+ ddToken.attrs[classIndex][1] += ' last-dd';
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Helper function to process definition description elements
219
+ * Identifies and marks the last dd element in each dt/dd group for special styling
220
+ *
221
+ * @param {Array} tokens - The token array to process
222
+ * @param {Number} startIdx - The index in the token array to start processing from
223
+ */
224
+ function processLastDdElements(tokens, startIdx) {
225
+ let lastDdIndex = -1; // Tracks the most recent dd_open token
226
+
227
+ for (let i = startIdx; i < tokens.length; i++) {
169
228
  if (tokens[i].type === 'dl_close') {
170
- // Add class to the last <dd> before closing <dl>
171
- if (lastDdIndex !== -1) {
172
- const ddToken = tokens[lastDdIndex];
173
- const classIndex = ddToken.attrIndex('class');
174
- if (classIndex < 0) {
175
- ddToken.attrPush(['class', 'last-dd']);
176
- } else {
177
- ddToken.attrs[classIndex][1] += ' last-dd';
178
- }
179
- }
229
+ // Add class to the last <dd> before closing the entire <dl>
230
+ addLastDdClass(tokens, lastDdIndex);
180
231
  break;
181
232
  }
182
233
 
183
- if (tokens[i].type === 'dt_open') {
184
- // Skip empty dt elements - this is where we use the isEmpty flag
185
- // to avoid processing empty definition terms
186
- if (tokens[i].isEmpty) {
187
- continue; // Skip to the next iteration without processing this empty dt
188
- }
189
-
190
- // Add class to the last <dd> before a new <dt>
191
- if (lastDdIndex !== -1) {
192
- const ddToken = tokens[lastDdIndex];
193
- const classIndex = ddToken.attrIndex('class');
194
- if (classIndex < 0) {
195
- ddToken.attrPush(['class', 'last-dd']);
196
- } else {
197
- ddToken.attrs[classIndex][1] += ' last-dd';
198
- }
199
- lastDdIndex = -1; // Reset for the next series
200
- }
234
+ if (tokens[i].type === 'dt_open' && !tokens[i].isEmpty) {
235
+ // When we find a non-empty dt, mark the previous dd as the last one in its group
236
+ addLastDdClass(tokens, lastDdIndex);
237
+ lastDdIndex = -1; // Reset for the next group
201
238
  }
202
239
 
203
240
  if (tokens[i].type === 'dd_open') {
204
- lastDdIndex = i;
241
+ lastDdIndex = i; // Track the most recently seen dd_open
205
242
  }
206
243
  }
244
+ }
245
+
246
+ /**
247
+ * Custom renderer for definition list opening tags
248
+ * Handles special styling for terminology sections and processes definition terms and descriptions
249
+ * This function was refactored to reduce cognitive complexity by extracting helper functions
250
+ *
251
+ * @param {Array} tokens - The token array being processed
252
+ * @param {Number} idx - The index of the current token
253
+ * @param {Object} options - Rendering options
254
+ * @param {Object} env - Environment variables
255
+ * @param {Object} self - Reference to the renderer
256
+ * @return {String} The rendered HTML output
257
+ */
258
+ md.renderer.rules.dl_open = function (tokens, idx, options, env, self) {
259
+ const targetHtml = 'terminology-section-start-h7vc6omi2hr2880';
260
+ let targetIndex = findTargetIndex(tokens, targetHtml);
261
+
262
+ // Add class to the first <dl> only if it comes after the target HTML
263
+ if (targetIndex !== -1 && idx > targetIndex && !classAdded) {
264
+ tokens[idx].attrPush(['class', 'terms-and-definitions-list']);
265
+ classAdded = true;
266
+ }
267
+
268
+ // First pass - mark empty dt elements
269
+ markEmptyDtElements(tokens, idx + 1);
270
+
271
+ // Second pass - process last dd elements
272
+ processLastDdElements(tokens, idx + 1);
207
273
 
208
274
  return originalRender(tokens, idx, options, env, self);
209
275
  };
210
-
276
+
277
+ /**
278
+ * Helper function to determine if a definition term is transcluded from another source
279
+ * Transcluded terms require special styling and handling
280
+ *
281
+ * @param {Array} tokens - The token array to process
282
+ * @param {Number} dtOpenIndex - The index of the dt_open token to check
283
+ * @return {Boolean} True if the term is transcluded, false otherwise
284
+ */
285
+ function isTermTranscluded(tokens, dtOpenIndex) {
286
+ for (let i = dtOpenIndex + 1; i < tokens.length; i++) {
287
+ if (tokens[i].type === 'dt_close') {
288
+ break; // Only examine tokens within this definition term
289
+ }
290
+
291
+ // Look for inline content that contains template tokens of type 'tref'
292
+ // These are transcluded term references
293
+ if (tokens[i].type === 'inline' && tokens[i].children) {
294
+ for (let child of tokens[i].children) {
295
+ if (child.type === 'template' &&
296
+ child.info &&
297
+ child.info.type === 'tref') {
298
+ return true;
299
+ }
300
+ }
301
+ }
302
+ }
303
+ return false;
304
+ }
305
+
211
306
  // Override the rendering of dt elements to properly handle transcluded terms
212
307
  const originalDtRender = md.renderer.rules.dt_open || function (tokens, idx, options, env, self) {
213
308
  return self.renderToken(tokens, idx, options);
214
309
  };
215
-
310
+
311
+ /**
312
+ * Custom renderer for definition term opening tags
313
+ * Handles special cases like empty terms and transcluded terms
314
+ *
315
+ * @param {Array} tokens - The token array being processed
316
+ * @param {Number} idx - The index of the current token
317
+ * @param {Object} options - Rendering options
318
+ * @param {Object} env - Environment variables
319
+ * @param {Object} self - Reference to the renderer
320
+ * @return {String} The rendered HTML output or empty string for skipped elements
321
+ */
216
322
  md.renderer.rules.dt_open = function (tokens, idx, options, env, self) {
217
- // Skip rendering empty dt elements - this is the first critical fix
218
- // When a dt has been marked as empty, we return an empty string
219
- // instead of rendering the <dt> tag. This effectively removes empty dt tags
220
- // from the output HTML.
323
+ // Skip rendering empty dt elements that were marked during preprocessing
221
324
  if (tokens[idx].isEmpty) {
222
325
  return '';
223
326
  }
224
-
225
- // Check if this dt is part of a transcluded term by looking at the next inline token
226
- // This is part of the second fix, to properly handle transcluded terms
227
- let isTranscluded = false;
228
- for (let i = idx + 1; i < tokens.length; i++) {
229
- if (tokens[i].type === 'dt_close') {
230
- break;
231
- }
232
- // Look for child tokens that are template tokens with type 'tref'
233
- // These represent transcluded terms from external sources
234
- if (tokens[i].type === 'inline' &&
235
- tokens[i].children &&
236
- tokens[i].children.some(child =>
237
- child.type === 'template' &&
238
- child.info &&
239
- child.info.type === 'tref')) {
240
- isTranscluded = true;
241
- break;
242
- }
243
- }
244
-
245
- // Add a class for transcluded terms to ensure proper styling
246
- // This helps maintain consistent styling for transcluded terms
247
- if (isTranscluded) {
327
+
328
+ // Check if this dt is part of a transcluded term and add appropriate class
329
+ if (isTermTranscluded(tokens, idx)) {
248
330
  const classIndex = tokens[idx].attrIndex('class');
249
331
  if (classIndex < 0) {
250
332
  tokens[idx].attrPush(['class', 'transcluded-xref-term']);
@@ -252,15 +334,26 @@ module.exports = function (md, templates = {}) {
252
334
  tokens[idx].attrs[classIndex][1] += ' transcluded-xref-term';
253
335
  }
254
336
  }
255
-
337
+
256
338
  return originalDtRender(tokens, idx, options, env, self);
257
339
  };
258
-
340
+
259
341
  // Similarly override dt_close to skip empty dts
260
342
  const originalDtCloseRender = md.renderer.rules.dt_close || function (tokens, idx, options, env, self) {
261
343
  return self.renderToken(tokens, idx, options);
262
344
  };
263
-
345
+
346
+ /**
347
+ * Custom renderer for definition term closing tags
348
+ * Ensures empty terms are not rendered in the final output
349
+ *
350
+ * @param {Array} tokens - The token array being processed
351
+ * @param {Number} idx - The index of the current token
352
+ * @param {Object} options - Rendering options
353
+ * @param {Object} env - Environment variables
354
+ * @param {Object} self - Reference to the renderer
355
+ * @return {String} The rendered HTML output or empty string for skipped elements
356
+ */
264
357
  md.renderer.rules.dt_close = function (tokens, idx, options, env, self) {
265
358
  // Skip rendering the closing </dt> tag for empty dt elements
266
359
  // This completes the fix for empty dt elements by ensuring neither
package/src/references.js CHANGED
@@ -4,12 +4,11 @@ const axios = require('axios').default;
4
4
  const spaceRegex = /\s+/g;
5
5
 
6
6
  function validateReferences(references, definitions, render) {
7
- const resolvedRefs = [];
8
7
  const unresolvedRefs = [];
9
8
  [...new Set(references)].forEach(
10
9
  ref => {
11
10
  if(render.includes(`id="term:${ref.replace(spaceRegex, '-').toLowerCase()}"`)) {
12
- resolvedRefs.push(ref);
11
+ // Reference is resolved
13
12
  } else {
14
13
  unresolvedRefs.push(ref);
15
14
  }