spec-up-t 1.3.1 → 1.4.0
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/.github/copilot-instructions.md +13 -0
- package/assets/compiled/body.js +17 -11
- package/assets/compiled/head.css +6 -4
- package/assets/css/collapse-definitions.css +0 -1
- package/assets/css/create-pdf.css +4 -2
- package/assets/css/create-term-filter.css +4 -4
- package/assets/css/definition-buttons-container.css +60 -0
- package/assets/css/insert-trefs.css +7 -0
- package/assets/css/sidebar-toc.css +2 -1
- package/assets/css/terms-and-definitions.css +73 -22
- package/assets/js/add-href-to-snapshot-link.js +16 -9
- package/assets/js/addAnchorsToTerms.js +1 -1
- package/assets/js/charts.js +10 -0
- package/assets/js/collapse-definitions.js +13 -2
- package/assets/js/collapse-meta-info.js +11 -9
- package/assets/js/definition-button-container-utils.js +82 -0
- package/assets/js/edit-term-buttons.js +77 -20
- package/assets/js/github-issues.js +35 -0
- package/assets/js/github-repo-info.js +144 -0
- package/assets/js/highlight-heading-plus-sibling-nodes.test.js +18 -0
- package/assets/js/insert-trefs.js +62 -13
- package/assets/js/mermaid-diagrams.js +11 -0
- package/assets/js/terminology-section-utility-container/README.md +107 -0
- package/assets/js/terminology-section-utility-container/create-alphabet-index.js +17 -0
- package/assets/js/{create-term-filter.js → terminology-section-utility-container/create-term-filter.js} +11 -44
- package/assets/js/terminology-section-utility-container/hide-show-utility-container.js +21 -0
- package/assets/js/terminology-section-utility-container/search.js +203 -0
- package/assets/js/terminology-section-utility-container.js +203 -0
- package/assets/js/tooltips.js +283 -0
- package/config/asset-map.json +24 -16
- package/index.js +57 -390
- package/package.json +5 -2
- package/src/add-remove-xref-source.js +20 -21
- package/src/collect-external-references.js +8 -337
- package/src/collect-external-references.test.js +440 -33
- package/src/configure.js +8 -109
- package/src/create-docx.js +7 -6
- package/src/create-pdf.js +15 -14
- package/src/freeze-spec-data.js +46 -0
- package/src/git-info.test.js +76 -0
- package/src/health-check/destination-gitignore-checker.js +5 -3
- package/src/health-check/external-specs-checker.js +5 -4
- package/src/health-check/specs-configuration-checker.js +2 -1
- package/src/health-check/term-references-checker.js +5 -3
- package/src/health-check/terms-intro-checker.js +2 -1
- package/src/health-check/tref-term-checker.js +8 -7
- package/src/health-check.js +8 -7
- package/src/init.js +3 -2
- package/src/install-from-boilerplate/add-gitignore-entries.js +3 -2
- package/src/install-from-boilerplate/add-scripts-keys.js +5 -4
- package/src/install-from-boilerplate/boilerplate/README.md +1 -1
- package/src/install-from-boilerplate/boilerplate/spec/example-markup-in-markdown.md +1 -1
- package/src/install-from-boilerplate/boilerplate/spec/spec-head.md +1 -1
- package/src/install-from-boilerplate/boilerplate/specs.json +2 -1
- package/src/install-from-boilerplate/config-scripts-keys.js +3 -3
- package/src/install-from-boilerplate/copy-boilerplate.js +2 -1
- package/src/install-from-boilerplate/copy-system-files.js +4 -3
- package/src/install-from-boilerplate/custom-update.js +12 -1
- package/src/install-from-boilerplate/help.txt +1 -1
- package/src/install-from-boilerplate/menu.sh +6 -6
- package/src/json-key-validator.js +17 -11
- package/src/markdown-it/README.md +207 -0
- package/src/markdown-it/definition-lists.js +397 -0
- package/src/markdown-it/index.js +83 -0
- package/src/markdown-it/link-enhancement.js +98 -0
- package/src/markdown-it/plugins.js +118 -0
- package/src/markdown-it/table-enhancement.js +97 -0
- package/src/markdown-it/template-tag-syntax.js +152 -0
- package/src/parsers/index.js +16 -0
- package/src/parsers/spec-parser.js +152 -0
- package/src/parsers/spec-parser.test.js +109 -0
- package/src/parsers/template-tag-parser.js +277 -0
- package/src/parsers/template-tag-parser.test.js +107 -0
- package/src/pipeline/configuration/configure-starterpack.js +200 -0
- package/src/{create-external-specs-list.js → pipeline/configuration/create-external-specs-list.js} +13 -12
- package/src/{create-term-index.js → pipeline/configuration/create-term-index.js} +19 -18
- package/src/{create-versions-index.js → pipeline/configuration/create-versions-index.js} +4 -3
- package/src/{insert-term-index.js → pipeline/configuration/insert-term-index.js} +2 -2
- package/src/pipeline/configuration/prepare-spec-configuration.js +70 -0
- package/src/pipeline/parsing/apply-markdown-it-extensions.js +35 -0
- package/src/pipeline/parsing/create-markdown-parser.js +94 -0
- package/src/pipeline/parsing/create-markdown-parser.test.js +49 -0
- package/src/{html-dom-processor.js → pipeline/postprocessing/definition-list-postprocessor.js} +69 -10
- package/src/{escape-handler.js → pipeline/preprocessing/escape-processor.js} +3 -1
- package/src/{fix-markdown-files.js → pipeline/preprocessing/normalize-terminology-markdown.js} +41 -31
- package/src/pipeline/references/collect-external-references.js +307 -0
- package/src/pipeline/references/external-references-service.js +231 -0
- package/src/pipeline/references/fetch-terms-from-index.js +198 -0
- package/src/pipeline/references/match-term.js +34 -0
- package/src/{collectExternalReferences/matchTerm.test.js → pipeline/references/match-term.test.js} +8 -2
- package/src/pipeline/references/process-xtrefs-data.js +94 -0
- package/src/pipeline/references/xtref-utils.js +166 -0
- package/src/pipeline/rendering/render-spec-document.js +146 -0
- package/src/pipeline/rendering/render-utils.js +154 -0
- package/src/utils/LOGGER.md +81 -0
- package/src/utils/{doesUrlExist.js → does-url-exist.js} +4 -3
- package/src/utils/fetch.js +5 -4
- package/src/utils/file-opener.js +3 -2
- package/src/utils/git-info.js +77 -0
- package/src/utils/logger.js +74 -0
- package/src/utils/regex-patterns.js +471 -0
- package/src/utils/regex-patterns.test.js +281 -0
- package/templates/template.html +56 -21
- package/assets/js/create-alphabet-index.js +0 -60
- package/assets/js/hide-show-utility-container.js +0 -16
- package/assets/js/index.js +0 -87
- package/assets/js/search.js +0 -365
- package/src/collectExternalReferences/fetchTermsFromIndex.js +0 -284
- package/src/collectExternalReferences/matchTerm.js +0 -32
- package/src/collectExternalReferences/processXTrefsData.js +0 -108
- package/src/freeze.js +0 -90
- package/src/markdown-it-extensions.js +0 -395
- package/src/references.js +0 -114
- /package/assets/css/{bootstrap.min.css → embedded-libraries/bootstrap.min.css} +0 -0
- /package/assets/css/{prism.css → embedded-libraries/prism.css} +0 -0
- /package/assets/css/{prism.dark.css → embedded-libraries/prism.dark.css} +0 -0
- /package/assets/css/{prism.default.css → embedded-libraries/prism.default.css} +0 -0
- /package/assets/js/{bootstrap.bundle.min.js → embedded-libraries/bootstrap.bundle.min.js} +0 -0
- /package/assets/js/{chart.js → embedded-libraries/chart.js} +0 -0
- /package/assets/js/{diff.min.js → embedded-libraries/diff.min.js} +0 -0
- /package/assets/js/{font-awesome.js → embedded-libraries/font-awesome.js} +0 -0
- /package/assets/js/{mermaid.js → embedded-libraries/mermaid.js} +0 -0
- /package/assets/js/{notyf.js → embedded-libraries/notyf.js} +0 -0
- /package/assets/js/{popper.js → embedded-libraries/popper.js} +0 -0
- /package/assets/js/{prism.dark.js → embedded-libraries/prism.dark.js} +0 -0
- /package/assets/js/{prism.default.js → embedded-libraries/prism.default.js} +0 -0
- /package/assets/js/{prism.js → embedded-libraries/prism.js} +0 -0
- /package/assets/js/{tippy.js → embedded-libraries/tippy.js} +0 -0
- /package/src/{escape-mechanism.js → pipeline/preprocessing/escape-placeholder-utils.js} +0 -0
- /package/src/utils/{isLineWithDefinition.js → is-line-with-definition.js} +0 -0
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const { fetchTermsFromIndex, fetchAllTermsFromIndex } = require('./fetchTermsFromIndex.js');
|
|
3
|
-
const { matchTerm } = require('./matchTerm.js');
|
|
4
|
-
const { addPath, getPath, getAllPaths } = require('../../config/paths');
|
|
5
|
-
const path = require('path');
|
|
6
|
-
|
|
7
|
-
// Directory to store fetched data files
|
|
8
|
-
const CACHE_DIR = getPath('githubcache');
|
|
9
|
-
|
|
10
|
-
async function processXTrefsData(allXTrefs, GITHUB_API_TOKEN, outputPathJSON, outputPathJS, outputPathJSTimeStamped) {
|
|
11
|
-
try {
|
|
12
|
-
// Ensure the directory exists, so that we can store the fetched data
|
|
13
|
-
if (!fs.existsSync(CACHE_DIR)) {
|
|
14
|
-
fs.mkdirSync(CACHE_DIR, { recursive: true });
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Filter out incomplete xtrefs that don't have proper repository information
|
|
18
|
-
allXTrefs.xtrefs = allXTrefs.xtrefs.filter(xtref => {
|
|
19
|
-
if (!xtref.owner || !xtref.repo || !xtref.repoUrl) {
|
|
20
|
-
console.log(`⚠️ Removing incomplete reference: ${xtref.externalSpec}, ${xtref.term}`);
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
|
-
return true;
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
// Group xtrefs by repository to avoid multiple downloads of the same index.html
|
|
27
|
-
const xrefsByRepo = allXTrefs.xtrefs.reduce((groups, xtref) => {
|
|
28
|
-
const repoKey = `${xtref.owner}/${xtref.repo}`;
|
|
29
|
-
if (!groups[repoKey]) {
|
|
30
|
-
groups[repoKey] = {
|
|
31
|
-
owner: xtref.owner,
|
|
32
|
-
repo: xtref.repo,
|
|
33
|
-
xtrefs: []
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
groups[repoKey].xtrefs.push(xtref);
|
|
37
|
-
return groups;
|
|
38
|
-
}, {});
|
|
39
|
-
|
|
40
|
-
console.log(`✅ Grouped ${allXTrefs.xtrefs.length} terms into ${Object.keys(xrefsByRepo).length} repositories`);
|
|
41
|
-
|
|
42
|
-
// Process each repository once
|
|
43
|
-
for (const repoKey of Object.keys(xrefsByRepo)) {
|
|
44
|
-
const repoGroup = xrefsByRepo[repoKey];
|
|
45
|
-
console.log(`Processing repository: ${repoKey} (${repoGroup.xtrefs.length} terms)`);
|
|
46
|
-
|
|
47
|
-
// Get the GitHub Pages URL from the first xtref in this repo group
|
|
48
|
-
const ghPageUrl = repoGroup.xtrefs[0]?.ghPageUrl;
|
|
49
|
-
|
|
50
|
-
// First, fetch all terms from this repository
|
|
51
|
-
const allTermsData = await fetchAllTermsFromIndex(
|
|
52
|
-
GITHUB_API_TOKEN,
|
|
53
|
-
repoGroup.owner,
|
|
54
|
-
repoGroup.repo,
|
|
55
|
-
{
|
|
56
|
-
ghPageUrl: ghPageUrl // Pass the GitHub Pages URL
|
|
57
|
-
}
|
|
58
|
-
);
|
|
59
|
-
|
|
60
|
-
if (!allTermsData) {
|
|
61
|
-
console.log(`❌ Could not fetch terms from repository ${repoKey}`);
|
|
62
|
-
// Mark all terms from this repo as not found
|
|
63
|
-
repoGroup.xtrefs.forEach(xtref => {
|
|
64
|
-
xtref.commitHash = "not found";
|
|
65
|
-
xtref.content = "This term was not found in the external repository.";
|
|
66
|
-
xtref.avatarUrl = null;
|
|
67
|
-
});
|
|
68
|
-
continue; // Skip to next repository
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Now process each term in this repository
|
|
72
|
-
for (const xtref of repoGroup.xtrefs) {
|
|
73
|
-
// Find the term in the pre-fetched data
|
|
74
|
-
const foundTerm = allTermsData.terms.find(
|
|
75
|
-
t => t.term.toLowerCase() === xtref.term.toLowerCase()
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
if (foundTerm) {
|
|
79
|
-
xtref.commitHash = allTermsData.sha;
|
|
80
|
-
xtref.content = foundTerm.definition;
|
|
81
|
-
xtref.avatarUrl = allTermsData.avatarUrl;
|
|
82
|
-
console.log(`✅ Match found for term: ${xtref.term} in ${xtref.externalSpec}`);
|
|
83
|
-
} else {
|
|
84
|
-
xtref.commitHash = "not found";
|
|
85
|
-
xtref.content = "This term was not found in the external repository.";
|
|
86
|
-
xtref.avatarUrl = null;
|
|
87
|
-
console.log(`ℹ️ No match found for term: ${xtref.term} in ${xtref.externalSpec}`);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
console.log(`✅ Finished processing repository: ${repoKey}`);
|
|
92
|
-
console.log("============================================\n\n");
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const allXTrefsStr = JSON.stringify(allXTrefs, null, 2);
|
|
96
|
-
fs.writeFileSync(outputPathJSON, allXTrefsStr, 'utf8');
|
|
97
|
-
const stringReadyForFileWrite = `const allXTrefs = ${allXTrefsStr};`;
|
|
98
|
-
fs.writeFileSync(outputPathJS, stringReadyForFileWrite, 'utf8');
|
|
99
|
-
fs.writeFileSync(outputPathJSTimeStamped, stringReadyForFileWrite, 'utf8');
|
|
100
|
-
|
|
101
|
-
// This will run index.js
|
|
102
|
-
require('../../index.js')({ nowatch: true });
|
|
103
|
-
} catch (error) {
|
|
104
|
-
console.error("An error occurred:", error);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
module.exports.processXTrefsData = processXTrefsData;
|
package/src/freeze.js
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file freeze.js
|
|
3
|
-
* @description This script reads the output path from a specs.json file, finds the highest versioned directory in the destination path, and copies the index.html file to a new versioned directory with an incremented version number.
|
|
4
|
-
*
|
|
5
|
-
* @requires fs-extra - Module for file system operations with additional features.
|
|
6
|
-
* @requires path - Module for handling and transforming file paths.
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* // Assuming specs.json contains:
|
|
10
|
-
* // {
|
|
11
|
-
* // "specs": [
|
|
12
|
-
* // {
|
|
13
|
-
* // "output_path": "path/to/output"
|
|
14
|
-
* // }
|
|
15
|
-
* // ]
|
|
16
|
-
* // }
|
|
17
|
-
*
|
|
18
|
-
* // And the directory structure is:
|
|
19
|
-
* // path/to/output/versions/v1/index.html
|
|
20
|
-
* // path/to/output/versions/v2/index.html
|
|
21
|
-
*
|
|
22
|
-
* // Running this script will create:
|
|
23
|
-
* // path/to/output/versions/v3/index.html
|
|
24
|
-
*
|
|
25
|
-
* @version 1.0.0
|
|
26
|
-
* @license MIT
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
const fs = require('fs-extra'); // Import the fs-extra module for file system operations
|
|
30
|
-
const path = require('path'); // Import the path module for handling file paths
|
|
31
|
-
|
|
32
|
-
// Read and parse the specs.json file
|
|
33
|
-
const config = fs.readJsonSync('specs.json');
|
|
34
|
-
|
|
35
|
-
// Extract the output_path from the specs.json file
|
|
36
|
-
const outputPath = config.specs[0].output_path;
|
|
37
|
-
|
|
38
|
-
// Define the source file path
|
|
39
|
-
const sourceFile = path.join(outputPath, 'index.html');
|
|
40
|
-
|
|
41
|
-
// Define the destination directory path
|
|
42
|
-
const destDir = path.join(outputPath, 'versions');
|
|
43
|
-
|
|
44
|
-
// Ensure the destination directory exists, create it if it doesn't
|
|
45
|
-
if (!fs.existsSync(destDir)) {
|
|
46
|
-
fs.mkdirSync(destDir, { recursive: true });
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Get all directories in the destination directory
|
|
50
|
-
const dirs = fs.readdirSync(destDir).filter(file => fs.statSync(path.join(destDir, file)).isDirectory());
|
|
51
|
-
|
|
52
|
-
// Initialize the highest version number to 0
|
|
53
|
-
let highestVersion = 0;
|
|
54
|
-
|
|
55
|
-
// Define the pattern to match versioned directories
|
|
56
|
-
const versionPattern = /^v(\d+)$/;
|
|
57
|
-
|
|
58
|
-
// Iterate over each directory in the destination directory
|
|
59
|
-
dirs.forEach(dir => {
|
|
60
|
-
// Check if the directory matches the version pattern
|
|
61
|
-
const match = dir.match(versionPattern);
|
|
62
|
-
if (match) {
|
|
63
|
-
// Extract the version number from the directory name
|
|
64
|
-
const version = parseInt(match[1], 10);
|
|
65
|
-
// Update the highest version number if the latest version is higher
|
|
66
|
-
if (version > highestVersion) {
|
|
67
|
-
highestVersion = version;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
// Calculate the new version number
|
|
73
|
-
const newVersion = highestVersion + 1;
|
|
74
|
-
|
|
75
|
-
// Define the new version directory path
|
|
76
|
-
const newVersionDir = path.join(destDir, `v${newVersion}`);
|
|
77
|
-
|
|
78
|
-
// Ensure the new version directory exists, create it if it doesn't
|
|
79
|
-
if (!fs.existsSync(newVersionDir)) {
|
|
80
|
-
fs.mkdirSync(newVersionDir, { recursive: true });
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Define the destination file path within the new version directory
|
|
84
|
-
const destFile = path.join(newVersionDir, 'index.html');
|
|
85
|
-
|
|
86
|
-
// Copy the source file to the destination file
|
|
87
|
-
fs.copyFileSync(sourceFile, destFile);
|
|
88
|
-
|
|
89
|
-
// Log a message indicating the file has been copied
|
|
90
|
-
console.log(`✅ Created a freezed specification version in ${destFile}`);
|
|
@@ -1,395 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { ESCAPED_PLACEHOLDER } = require('./escape-handler');
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Configuration for custom template syntax [[example]] used throughout the markdown parsing
|
|
7
|
-
* These constants define how template markers are identified and processed
|
|
8
|
-
*/
|
|
9
|
-
const levels = 2; // Number of bracket characters used for template markers
|
|
10
|
-
const openString = '['.repeat(levels); // Opening delimiter for template markers, e.g., '[['
|
|
11
|
-
const closeString = ']'.repeat(levels); // Closing delimiter for template markers, e.g., ']]'
|
|
12
|
-
// Regular expression to extract template type and arguments from content between delimiters
|
|
13
|
-
// Captures: 1st group = template type (e.g., "ref", "tref"), 2nd group = optional arguments
|
|
14
|
-
const contentRegex = /\s*([^\s\[\]:]+):?\s*([^\]\n]+)?/i;
|
|
15
|
-
|
|
16
|
-
module.exports = function (md, templates = {}) {
|
|
17
|
-
|
|
18
|
-
// Add table renderer to apply Bootstrap classes to all tables by default
|
|
19
|
-
const originalTableRender = md.renderer.rules.table_open || function (tokens, idx, options, env, self) {
|
|
20
|
-
return self.renderToken(tokens, idx, options);
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
// Save the original table_close renderer
|
|
24
|
-
const originalTableCloseRender = md.renderer.rules.table_close || function (tokens, idx, options, env, self) {
|
|
25
|
-
return self.renderToken(tokens, idx, options);
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
// Override table_open to add both the classes and open a wrapper div
|
|
29
|
-
md.renderer.rules.table_open = function (tokens, idx, options, env, self) {
|
|
30
|
-
// Add Bootstrap classes to the table element
|
|
31
|
-
const token = tokens[idx];
|
|
32
|
-
const classIndex = token.attrIndex('class');
|
|
33
|
-
const tableClasses = 'table table-striped table-bordered table-hover';
|
|
34
|
-
|
|
35
|
-
if (classIndex < 0) {
|
|
36
|
-
token.attrPush(['class', tableClasses]);
|
|
37
|
-
} else {
|
|
38
|
-
// If a class attribute already exists, append our classes
|
|
39
|
-
const existingClasses = token.attrs[classIndex][1];
|
|
40
|
-
// Only add classes that aren't already present
|
|
41
|
-
const classesToAdd = tableClasses
|
|
42
|
-
.split(' ')
|
|
43
|
-
.filter(cls => !existingClasses.includes(cls))
|
|
44
|
-
.join(' ');
|
|
45
|
-
|
|
46
|
-
if (classesToAdd) {
|
|
47
|
-
token.attrs[classIndex][1] = existingClasses + ' ' + classesToAdd;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Add the responsive wrapper div before the table
|
|
52
|
-
return '<div class="table-responsive-md">' + originalTableRender(tokens, idx, options, env, self);
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
// Override table_close to close the wrapper div
|
|
56
|
-
md.renderer.rules.table_close = function (tokens, idx, options, env, self) {
|
|
57
|
-
// Close the table and add the closing div
|
|
58
|
-
return originalTableCloseRender(tokens, idx, options, env, self) + '</div>';
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Custom template syntax rule for markdown-it
|
|
63
|
-
* Processes template markers like [[template-type:arg1,arg2]] in markdown content
|
|
64
|
-
* and converts them to tokens that can be processed by template renderers
|
|
65
|
-
*/
|
|
66
|
-
md.inline.ruler.after('emphasis', 'templates', function templates_ruler(state, silent) {
|
|
67
|
-
// Get the current parsing position
|
|
68
|
-
var start = state.pos;
|
|
69
|
-
|
|
70
|
-
// Check if we're at an escaped placeholder - if so, skip processing
|
|
71
|
-
if (state.src.slice(start, start + ESCAPED_PLACEHOLDER.length) === ESCAPED_PLACEHOLDER) {
|
|
72
|
-
return false;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Check if we're at a template opening marker
|
|
76
|
-
let prefix = state.src.slice(start, start + levels);
|
|
77
|
-
if (prefix !== openString) return false;
|
|
78
|
-
// Find the matching closing marker
|
|
79
|
-
var indexOfClosingBrace = state.src.indexOf(closeString, start);
|
|
80
|
-
|
|
81
|
-
if (indexOfClosingBrace > 0) {
|
|
82
|
-
// Extract the template content using regex
|
|
83
|
-
let match = contentRegex.exec(state.src.slice(start + levels, indexOfClosingBrace));
|
|
84
|
-
if (!match) return false;
|
|
85
|
-
|
|
86
|
-
// Get template type and find a matching template handler
|
|
87
|
-
let type = match[1];
|
|
88
|
-
let template = templates.find(t => t.filter(type) && t);
|
|
89
|
-
if (!template) return false;
|
|
90
|
-
|
|
91
|
-
// Parse template arguments (comma-separated)
|
|
92
|
-
let args = match[2] ? match[2].trim().split(/\s*,+\s*/) : [];
|
|
93
|
-
// Create a template token to be processed during rendering
|
|
94
|
-
let token = state.push('template', '', 0);
|
|
95
|
-
token.content = match[0];
|
|
96
|
-
token.info = { type, template, args };
|
|
97
|
-
|
|
98
|
-
// If the template has a parse function, use it to preprocess the token
|
|
99
|
-
if (template.parse) {
|
|
100
|
-
token.content = template.parse(token, type, ...args) || token.content;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Advance the parser position past the template
|
|
104
|
-
state.pos = indexOfClosingBrace + levels;
|
|
105
|
-
return true;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return false;
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Renderer for template tokens
|
|
113
|
-
* Takes template tokens created during parsing and renders them using their associated template handler
|
|
114
|
-
*/
|
|
115
|
-
md.renderer.rules.template = function (tokens, idx, options, env, renderer) {
|
|
116
|
-
let token = tokens[idx];
|
|
117
|
-
let template = token.info.template;
|
|
118
|
-
if (template.render) {
|
|
119
|
-
return template.render(token, token.info.type, ...token.info.args) || (openString + token.content + closeString);
|
|
120
|
-
}
|
|
121
|
-
return token.content;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Regular expression to extract domains and path segments from URLs
|
|
126
|
-
* Used to add path-related attributes to links for styling and behavior
|
|
127
|
-
*/
|
|
128
|
-
let pathSegmentRegex = /(?:http[s]*:\/\/([^\/]*)|(?:\/([^\/?]*)))/g;
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Custom link_open renderer that adds path attributes for styling and behavior
|
|
132
|
-
* Extracts domain and path segments from href attributes and adds them as path-X attributes
|
|
133
|
-
*/
|
|
134
|
-
md.renderer.rules.link_open = function (tokens, idx, options, env, renderer) {
|
|
135
|
-
let token = tokens[idx];
|
|
136
|
-
let attrs = token.attrs.reduce((str, attr) => {
|
|
137
|
-
let name = attr[0];
|
|
138
|
-
let value = attr[1];
|
|
139
|
-
if (name === 'href') {
|
|
140
|
-
let index = 0;
|
|
141
|
-
value.replace(pathSegmentRegex, (m, domain, seg) => {
|
|
142
|
-
str += `path-${index++}="${domain || seg}"`;
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
return str += name + '="' + value + '" ';
|
|
146
|
-
}, '');
|
|
147
|
-
let anchor = `<a ${attrs}>`;
|
|
148
|
-
// Special handling for auto-detected links (linkify)
|
|
149
|
-
return token.markup === 'linkify' ? anchor + '<span>' : anchor;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
md.renderer.rules.link_close = function (tokens, idx, options, env, renderer) {
|
|
153
|
-
return tokens[idx].markup === 'linkify' ? '</span></a>' : '</a>';
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Add class to <dl> and the last <dd> in each series after a <dt>
|
|
157
|
-
const originalRender = md.renderer.rules.dl_open || function (tokens, idx, options, env, self) {
|
|
158
|
-
return self.renderToken(tokens, idx, options);
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
// Variable to keep track of whether the class has been added to the first <dl> after the target HTML
|
|
162
|
-
let classAdded = false;
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Helper function to locate a specific marker in the token stream
|
|
166
|
-
* Used to identify the terminology section in the document
|
|
167
|
-
*
|
|
168
|
-
* @param {Array} tokens - The token array to search through
|
|
169
|
-
* @param {String} targetHtml - The HTML string to look for in token content
|
|
170
|
-
* @return {Number} The index of the token containing targetHtml, or -1 if not found
|
|
171
|
-
*/
|
|
172
|
-
function findTargetIndex(tokens, targetHtml) {
|
|
173
|
-
for (let i = 0; i < tokens.length; i++) {
|
|
174
|
-
if (tokens[i].content && tokens[i].content.includes(targetHtml)) {
|
|
175
|
-
return i;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
return -1;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Helper function to identify and mark empty definition term elements
|
|
183
|
-
* Empty dt elements cause rendering and styling issues, so we mark them for special handling
|
|
184
|
-
*
|
|
185
|
-
* @param {Array} tokens - The token array to process
|
|
186
|
-
* @param {Number} startIdx - The index in the token array to start processing from
|
|
187
|
-
*/
|
|
188
|
-
function markEmptyDtElements(tokens, startIdx) {
|
|
189
|
-
for (let i = startIdx; i < tokens.length; i++) {
|
|
190
|
-
if (tokens[i].type === 'dl_close') {
|
|
191
|
-
break; // Stop when we reach the end of this definition list
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// An empty dt element is one where dt_open is immediately followed by dt_close
|
|
195
|
-
// with no content in between
|
|
196
|
-
if (tokens[i].type === 'dt_open' &&
|
|
197
|
-
i + 1 < tokens.length &&
|
|
198
|
-
tokens[i + 1].type === 'dt_close') {
|
|
199
|
-
// Mark both opening and closing tokens so they can be skipped during rendering
|
|
200
|
-
tokens[i].isEmpty = true;
|
|
201
|
-
tokens[i + 1].isEmpty = true;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Helper function to process definition description elements
|
|
209
|
-
* Identifies and marks the last dd element in each dt/dd group for special styling
|
|
210
|
-
*
|
|
211
|
-
* @param {Array} tokens - The token array to process
|
|
212
|
-
* @param {Number} startIdx - The index in the token array to start processing from
|
|
213
|
-
*/
|
|
214
|
-
function processLastDdElements(tokens, startIdx) {
|
|
215
|
-
let lastDdIndex = -1; // Tracks the most recent dd_open token
|
|
216
|
-
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Helper function to check if a definition list contains spec references
|
|
221
|
-
* Spec references have dt elements with id attributes starting with "ref:"
|
|
222
|
-
*
|
|
223
|
-
* @param {Array} tokens - The token array to search through
|
|
224
|
-
* @param {Number} startIdx - The index to start searching from (after dl_open)
|
|
225
|
-
* @return {Boolean} True if the dl contains spec references, false otherwise
|
|
226
|
-
*/
|
|
227
|
-
function containsSpecReferences(tokens, startIdx) {
|
|
228
|
-
for (let i = startIdx; i < tokens.length; i++) {
|
|
229
|
-
if (tokens[i].type === 'dl_close') {
|
|
230
|
-
break; // Stop when we reach the end of this definition list
|
|
231
|
-
}
|
|
232
|
-
if (isDtRef(tokens[i])) {
|
|
233
|
-
return true;
|
|
234
|
-
}
|
|
235
|
-
if (isHtmlRef(tokens[i])) {
|
|
236
|
-
return true;
|
|
237
|
-
}
|
|
238
|
-
if (isInlineRef(tokens[i])) {
|
|
239
|
-
return true;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
return false;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
function isDtRef(token) {
|
|
246
|
-
if (token.type !== 'dt_open' || !token.attrs) return false;
|
|
247
|
-
return token.attrs.some(attr => attr[0] === 'id' && attr[1].startsWith('ref:'));
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
function isHtmlRef(token) {
|
|
251
|
-
if (token.type !== 'html_block' && token.type !== 'html_inline') return false;
|
|
252
|
-
return token.content && token.content.includes('id="ref:');
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
function isInlineRef(token) {
|
|
256
|
-
if (token.type !== 'inline') return false;
|
|
257
|
-
return token.content && token.content.includes('id="ref:');
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Custom renderer for definition list opening tags
|
|
262
|
-
* Handles special styling for terminology sections and processes definition terms and descriptions
|
|
263
|
-
* This function was refactored to reduce cognitive complexity by extracting helper functions
|
|
264
|
-
*
|
|
265
|
-
* IMPORTANT FIX: This function now checks if a <dl> already has a class attribute OR contains
|
|
266
|
-
* spec references (dt elements with id="ref:...") before adding the 'terms-and-definitions-list'
|
|
267
|
-
* class. This prevents spec reference lists from being incorrectly classified as term definition lists.
|
|
268
|
-
*
|
|
269
|
-
* @param {Array} tokens - The token array being processed
|
|
270
|
-
* @param {Number} idx - The index of the current token
|
|
271
|
-
* @param {Object} options - Rendering options
|
|
272
|
-
* @param {Object} env - Environment variables
|
|
273
|
-
* @param {Object} self - Reference to the renderer
|
|
274
|
-
* @return {String} The rendered HTML output
|
|
275
|
-
*/
|
|
276
|
-
md.renderer.rules.dl_open = function (tokens, idx, options, env, self) {
|
|
277
|
-
const targetHtml = 'terminology-section-start';
|
|
278
|
-
let targetIndex = findTargetIndex(tokens, targetHtml);
|
|
279
|
-
|
|
280
|
-
// Check if the dl already has a class attribute (e.g., reference-list)
|
|
281
|
-
const existingClassIndex = tokens[idx].attrIndex('class');
|
|
282
|
-
const hasExistingClass = existingClassIndex >= 0;
|
|
283
|
-
|
|
284
|
-
// Check if this dl contains spec references (dt elements with id="ref:...")
|
|
285
|
-
const hasSpecReferences = containsSpecReferences(tokens, idx + 1);
|
|
286
|
-
|
|
287
|
-
// Only add terms-and-definitions-list class if:
|
|
288
|
-
// 1. It comes after the target HTML
|
|
289
|
-
// 2. We haven't added the class yet
|
|
290
|
-
// 3. The dl doesn't already have a class (to avoid overriding reference-list)
|
|
291
|
-
// 4. The dl doesn't contain spec references
|
|
292
|
-
if (targetIndex !== -1 && idx > targetIndex && !classAdded && !hasExistingClass && !hasSpecReferences) {
|
|
293
|
-
tokens[idx].attrPush(['class', 'terms-and-definitions-list']);
|
|
294
|
-
classAdded = true;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// First pass - mark empty dt elements
|
|
298
|
-
markEmptyDtElements(tokens, idx + 1);
|
|
299
|
-
|
|
300
|
-
// Second pass - process last dd elements
|
|
301
|
-
processLastDdElements(tokens, idx + 1);
|
|
302
|
-
|
|
303
|
-
return originalRender(tokens, idx, options, env, self);
|
|
304
|
-
};
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* Helper function to determine if a definition term is transcluded from another source
|
|
308
|
-
* Transcluded terms require special styling and handling
|
|
309
|
-
*
|
|
310
|
-
* @param {Array} tokens - The token array to process
|
|
311
|
-
* @param {Number} dtOpenIndex - The index of the dt_open token to check
|
|
312
|
-
* @return {Boolean} True if the term is transcluded, false otherwise
|
|
313
|
-
*/
|
|
314
|
-
function isTermTranscluded(tokens, dtOpenIndex) {
|
|
315
|
-
for (let i = dtOpenIndex + 1; i < tokens.length; i++) {
|
|
316
|
-
if (tokens[i].type === 'dt_close') {
|
|
317
|
-
break; // Only examine tokens within this definition term
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// Look for inline content that contains template tokens of type 'tref'
|
|
321
|
-
// These are transcluded term references
|
|
322
|
-
if (tokens[i].type === 'inline' && tokens[i].children) {
|
|
323
|
-
for (let child of tokens[i].children) {
|
|
324
|
-
if (child.type === 'template' &&
|
|
325
|
-
child.info &&
|
|
326
|
-
child.info.type === 'tref') {
|
|
327
|
-
return true;
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
return false;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// Override the rendering of dt elements to properly handle transcluded terms
|
|
336
|
-
const originalDtRender = md.renderer.rules.dt_open || function (tokens, idx, options, env, self) {
|
|
337
|
-
return self.renderToken(tokens, idx, options);
|
|
338
|
-
};
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Custom renderer for definition term opening tags
|
|
342
|
-
* Handles special cases like empty terms and transcluded terms
|
|
343
|
-
*
|
|
344
|
-
* @param {Array} tokens - The token array being processed
|
|
345
|
-
* @param {Number} idx - The index of the current token
|
|
346
|
-
* @param {Object} options - Rendering options
|
|
347
|
-
* @param {Object} env - Environment variables
|
|
348
|
-
* @param {Object} self - Reference to the renderer
|
|
349
|
-
* @return {String} The rendered HTML output or empty string for skipped elements
|
|
350
|
-
*/
|
|
351
|
-
md.renderer.rules.dt_open = function (tokens, idx, options, env, self) {
|
|
352
|
-
// Skip rendering empty dt elements that were marked during preprocessing
|
|
353
|
-
if (tokens[idx].isEmpty) {
|
|
354
|
-
return '';
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
// Check if this dt is part of a transcluded term and add appropriate class
|
|
358
|
-
if (isTermTranscluded(tokens, idx)) {
|
|
359
|
-
const classIndex = tokens[idx].attrIndex('class');
|
|
360
|
-
if (classIndex < 0) {
|
|
361
|
-
tokens[idx].attrPush(['class', 'transcluded-xref-term']);
|
|
362
|
-
} else {
|
|
363
|
-
tokens[idx].attrs[classIndex][1] += ' transcluded-xref-term';
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
return originalDtRender(tokens, idx, options, env, self);
|
|
368
|
-
};
|
|
369
|
-
|
|
370
|
-
// Similarly override dt_close to skip empty dts
|
|
371
|
-
const originalDtCloseRender = md.renderer.rules.dt_close || function (tokens, idx, options, env, self) {
|
|
372
|
-
return self.renderToken(tokens, idx, options);
|
|
373
|
-
};
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
* Custom renderer for definition term closing tags
|
|
377
|
-
* Ensures empty terms are not rendered in the final output
|
|
378
|
-
*
|
|
379
|
-
* @param {Array} tokens - The token array being processed
|
|
380
|
-
* @param {Number} idx - The index of the current token
|
|
381
|
-
* @param {Object} options - Rendering options
|
|
382
|
-
* @param {Object} env - Environment variables
|
|
383
|
-
* @param {Object} self - Reference to the renderer
|
|
384
|
-
* @return {String} The rendered HTML output or empty string for skipped elements
|
|
385
|
-
*/
|
|
386
|
-
md.renderer.rules.dt_close = function (tokens, idx, options, env, self) {
|
|
387
|
-
// Skip rendering the closing </dt> tag for empty dt elements
|
|
388
|
-
// This completes the fix for empty dt elements by ensuring neither
|
|
389
|
-
// the opening nor closing tags are rendered
|
|
390
|
-
if (tokens[idx].isEmpty) {
|
|
391
|
-
return '';
|
|
392
|
-
}
|
|
393
|
-
return originalDtCloseRender(tokens, idx, options, env, self);
|
|
394
|
-
};
|
|
395
|
-
};
|