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.
- package/.sonarlint/connectedMode.json +5 -0
- package/assets/compiled/body.js +31 -31
- package/assets/compiled/head.css +5 -5
- package/assets/compiled/head.js +3 -3
- package/assets/css/adjust-font-size.css +6 -11
- package/assets/css/backToTop.css +0 -1
- package/assets/css/index.css +1 -2
- package/assets/css/pdf-styles.css +23 -27
- package/assets/css/repo-issues.css +0 -6
- package/assets/css/search.css +0 -1
- package/assets/css/sidebar-toc.css +13 -12
- package/assets/css/terms-and-definitions.css +43 -37
- package/assets/js/add-href-to-snapshot-link.js +2 -1
- package/assets/js/addAnchorsToTerms.js +0 -1
- package/assets/js/adjust-font-size.js +0 -9
- package/assets/js/create-alphabet-index.js +1 -1
- package/assets/js/custom-elements.js +13 -18
- package/assets/js/declare-markdown-it.js +1 -1
- package/assets/js/highlightMenuItems.js +3 -3
- package/assets/js/index.js +1 -5
- package/assets/js/insert-trefs.js +2 -2
- package/assets/js/modal.js +3 -3
- package/assets/js/search.js +3 -3
- package/assets/js/utils.js +2 -3
- package/index.js +5 -15
- package/package.json +2 -2
- package/src/add-remove-xref-source.js +0 -2
- package/src/collect-external-references.js +187 -179
- package/src/collectExternalReferences/fetchTermsFromIndex.js +2 -1
- package/src/create-external-specs-list.js +1 -1
- package/src/fix-markdown-files.js +152 -90
- package/src/health-check/external-specs-checker.js +173 -94
- package/src/health-check/output-gitignore-checker.js +327 -191
- package/src/health-check/specs-configuration-checker.js +288 -210
- package/src/health-check/term-references-checker.js +200 -123
- package/src/health-check/tref-term-checker.js +264 -179
- package/src/health-check.js +51 -35
- package/src/init.js +0 -3
- package/src/install-from-boilerplate/boilerplate/gitignore +2 -1
- package/src/install-from-boilerplate/config-system-files.js +9 -1
- package/src/install-from-boilerplate/copy-system-files.js +1 -1
- package/src/install-from-boilerplate/menu.sh +17 -3
- package/src/markdown-it-extensions.js +199 -106
- package/src/references.js +1 -2
- package/src/utils/doesUrlExist.js +7 -5
- package/templates/template.html +1 -2
- package/assets/js/insert-xrefs.js +0 -370
- package/src/create-term-relations.js +0 -131
- package/src/prepare-tref.js +0 -174
package/src/health-check.js
CHANGED
|
@@ -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
|
|
73
|
-
specs
|
|
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(/
|
|
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 =
|
|
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="
|
|
284
|
-
|
|
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
|
-
|
|
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
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
-
const systemFiles = [
|
|
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
|
-
|
|
105
|
-
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
131
|
-
break;
|
|
167
|
+
return i;
|
|
132
168
|
}
|
|
133
169
|
}
|
|
170
|
+
return -1;
|
|
171
|
+
}
|
|
134
172
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
185
|
-
|
|
186
|
-
|
|
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
|
|
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
|
|
226
|
-
|
|
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
|
-
|
|
11
|
+
// Reference is resolved
|
|
13
12
|
} else {
|
|
14
13
|
unresolvedRefs.push(ref);
|
|
15
14
|
}
|