spec-up-t 1.3.0 → 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.
Files changed (150) hide show
  1. package/.github/copilot-instructions.md +13 -0
  2. package/assets/compiled/body.js +18 -12
  3. package/assets/compiled/head.css +8 -6
  4. package/assets/css/collapse-definitions.css +0 -1
  5. package/assets/css/counter.css +10 -22
  6. package/assets/css/create-pdf.css +4 -2
  7. package/assets/css/create-term-filter.css +4 -4
  8. package/assets/css/definition-buttons-container.css +60 -0
  9. package/assets/css/{pdf-download.css → download-pdf-docx.css} +9 -5
  10. package/assets/css/insert-trefs.css +7 -0
  11. package/assets/css/sidebar-toc.css +2 -1
  12. package/assets/css/terms-and-definitions.css +73 -22
  13. package/assets/js/add-href-to-snapshot-link.js +16 -9
  14. package/assets/js/addAnchorsToTerms.js +2 -2
  15. package/assets/js/charts.js +10 -0
  16. package/assets/js/collapse-definitions.js +13 -2
  17. package/assets/js/collapse-meta-info.js +11 -9
  18. package/assets/js/definition-button-container-utils.js +82 -0
  19. package/assets/js/download-pdf-docx.js +68 -0
  20. package/assets/js/edit-term-buttons.js +77 -20
  21. package/assets/js/github-issues.js +35 -0
  22. package/assets/js/github-repo-info.js +144 -0
  23. package/assets/js/highlight-heading-plus-sibling-nodes.test.js +18 -0
  24. package/assets/js/insert-trefs.js +62 -13
  25. package/assets/js/mermaid-diagrams.js +11 -0
  26. package/assets/js/terminology-section-utility-container/README.md +107 -0
  27. package/assets/js/terminology-section-utility-container/create-alphabet-index.js +17 -0
  28. package/assets/js/{create-term-filter.js → terminology-section-utility-container/create-term-filter.js} +11 -44
  29. package/assets/js/terminology-section-utility-container/hide-show-utility-container.js +21 -0
  30. package/assets/js/terminology-section-utility-container/search.js +203 -0
  31. package/assets/js/terminology-section-utility-container.js +203 -0
  32. package/assets/js/tooltips.js +283 -0
  33. package/config/asset-map.json +26 -18
  34. package/index.js +57 -390
  35. package/package.json +5 -2
  36. package/src/add-remove-xref-source.js +20 -21
  37. package/src/collect-external-references.js +8 -337
  38. package/src/collect-external-references.test.js +440 -33
  39. package/src/configure.js +8 -109
  40. package/src/create-docx.js +7 -6
  41. package/src/create-pdf.js +15 -14
  42. package/src/freeze-spec-data.js +46 -0
  43. package/src/git-info.test.js +76 -0
  44. package/src/health-check/destination-gitignore-checker.js +5 -3
  45. package/src/health-check/external-specs-checker.js +5 -4
  46. package/src/health-check/specs-configuration-checker.js +2 -1
  47. package/src/health-check/term-references-checker.js +5 -3
  48. package/src/health-check/terms-intro-checker.js +2 -1
  49. package/src/health-check/tref-term-checker.js +8 -7
  50. package/src/health-check.js +8 -7
  51. package/src/init.js +3 -2
  52. package/src/install-from-boilerplate/add-gitignore-entries.js +3 -2
  53. package/src/install-from-boilerplate/add-scripts-keys.js +5 -4
  54. package/src/install-from-boilerplate/boilerplate/.github/workflows/menu.yml +74 -97
  55. package/src/install-from-boilerplate/boilerplate/README.md +1 -1
  56. package/src/install-from-boilerplate/boilerplate/spec/example-markup-in-markdown.md +1 -1
  57. package/src/install-from-boilerplate/boilerplate/spec/spec-head.md +2 -2
  58. package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/composability.md +3 -0
  59. package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/compost.md +3 -0
  60. package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/fertilizer.md +3 -0
  61. package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/mulch.md +3 -0
  62. package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/pruning.md +3 -0
  63. package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/seedling.md +3 -0
  64. package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/soil.md +11 -0
  65. package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/watering.md +3 -0
  66. package/src/install-from-boilerplate/boilerplate/specs.json +24 -10
  67. package/src/install-from-boilerplate/config-scripts-keys.js +3 -3
  68. package/src/install-from-boilerplate/config-system-files.js +0 -1
  69. package/src/install-from-boilerplate/copy-boilerplate.js +2 -1
  70. package/src/install-from-boilerplate/copy-system-files.js +4 -3
  71. package/src/install-from-boilerplate/custom-update.js +12 -1
  72. package/src/install-from-boilerplate/help.txt +1 -1
  73. package/src/install-from-boilerplate/menu.sh +6 -6
  74. package/src/json-key-validator.js +17 -11
  75. package/src/markdown-it/README.md +207 -0
  76. package/src/markdown-it/definition-lists.js +397 -0
  77. package/src/markdown-it/index.js +83 -0
  78. package/src/markdown-it/link-enhancement.js +98 -0
  79. package/src/markdown-it/plugins.js +118 -0
  80. package/src/markdown-it/table-enhancement.js +97 -0
  81. package/src/markdown-it/template-tag-syntax.js +152 -0
  82. package/src/parsers/index.js +16 -0
  83. package/src/parsers/spec-parser.js +152 -0
  84. package/src/parsers/spec-parser.test.js +109 -0
  85. package/src/parsers/template-tag-parser.js +277 -0
  86. package/src/parsers/template-tag-parser.test.js +107 -0
  87. package/src/pipeline/configuration/configure-starterpack.js +200 -0
  88. package/src/{create-external-specs-list.js → pipeline/configuration/create-external-specs-list.js} +13 -12
  89. package/src/{create-term-index.js → pipeline/configuration/create-term-index.js} +19 -18
  90. package/src/{create-versions-index.js → pipeline/configuration/create-versions-index.js} +4 -3
  91. package/src/{insert-term-index.js → pipeline/configuration/insert-term-index.js} +2 -2
  92. package/src/pipeline/configuration/prepare-spec-configuration.js +70 -0
  93. package/src/pipeline/parsing/apply-markdown-it-extensions.js +35 -0
  94. package/src/pipeline/parsing/create-markdown-parser.js +94 -0
  95. package/src/pipeline/parsing/create-markdown-parser.test.js +49 -0
  96. package/src/{html-dom-processor.js → pipeline/postprocessing/definition-list-postprocessor.js} +69 -10
  97. package/src/{escape-handler.js → pipeline/preprocessing/escape-processor.js} +3 -1
  98. package/src/{fix-markdown-files.js → pipeline/preprocessing/normalize-terminology-markdown.js} +41 -31
  99. package/src/pipeline/references/collect-external-references.js +307 -0
  100. package/src/pipeline/references/external-references-service.js +231 -0
  101. package/src/pipeline/references/fetch-terms-from-index.js +198 -0
  102. package/src/pipeline/references/match-term.js +34 -0
  103. package/src/{collectExternalReferences/matchTerm.test.js → pipeline/references/match-term.test.js} +8 -2
  104. package/src/pipeline/references/process-xtrefs-data.js +94 -0
  105. package/src/pipeline/references/xtref-utils.js +166 -0
  106. package/src/pipeline/rendering/render-spec-document.js +146 -0
  107. package/src/pipeline/rendering/render-utils.js +154 -0
  108. package/src/utils/LOGGER.md +81 -0
  109. package/src/utils/{doesUrlExist.js → does-url-exist.js} +4 -3
  110. package/src/utils/fetch.js +5 -4
  111. package/src/utils/file-opener.js +3 -2
  112. package/src/utils/git-info.js +77 -0
  113. package/src/utils/logger.js +74 -0
  114. package/src/utils/regex-patterns.js +471 -0
  115. package/src/utils/regex-patterns.test.js +281 -0
  116. package/templates/template.html +56 -21
  117. package/assets/js/create-alphabet-index.js +0 -60
  118. package/assets/js/hide-show-utility-container.js +0 -16
  119. package/assets/js/index.js +0 -87
  120. package/assets/js/pdf-download.js +0 -46
  121. package/assets/js/search.js +0 -365
  122. package/src/collectExternalReferences/fetchTermsFromIndex.js +0 -284
  123. package/src/collectExternalReferences/matchTerm.js +0 -32
  124. package/src/collectExternalReferences/processXTrefsData.js +0 -108
  125. package/src/freeze.js +0 -90
  126. package/src/install-from-boilerplate/boilerplate/.github/workflows/fetch-and-push-xrefs.yml.old +0 -42
  127. package/src/install-from-boilerplate/boilerplate/.github/workflows/render-specs.yml +0 -47
  128. package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/term-1.md +0 -13
  129. package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/term-2.md +0 -3
  130. package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/term-3.md +0 -3
  131. package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/term-4.md +0 -3
  132. package/src/markdown-it-extensions.js +0 -395
  133. package/src/references.js +0 -114
  134. /package/assets/css/{bootstrap.min.css → embedded-libraries/bootstrap.min.css} +0 -0
  135. /package/assets/css/{prism.css → embedded-libraries/prism.css} +0 -0
  136. /package/assets/css/{prism.dark.css → embedded-libraries/prism.dark.css} +0 -0
  137. /package/assets/css/{prism.default.css → embedded-libraries/prism.default.css} +0 -0
  138. /package/assets/js/{bootstrap.bundle.min.js → embedded-libraries/bootstrap.bundle.min.js} +0 -0
  139. /package/assets/js/{chart.js → embedded-libraries/chart.js} +0 -0
  140. /package/assets/js/{diff.min.js → embedded-libraries/diff.min.js} +0 -0
  141. /package/assets/js/{font-awesome.js → embedded-libraries/font-awesome.js} +0 -0
  142. /package/assets/js/{mermaid.js → embedded-libraries/mermaid.js} +0 -0
  143. /package/assets/js/{notyf.js → embedded-libraries/notyf.js} +0 -0
  144. /package/assets/js/{popper.js → embedded-libraries/popper.js} +0 -0
  145. /package/assets/js/{prism.dark.js → embedded-libraries/prism.dark.js} +0 -0
  146. /package/assets/js/{prism.default.js → embedded-libraries/prism.default.js} +0 -0
  147. /package/assets/js/{prism.js → embedded-libraries/prism.js} +0 -0
  148. /package/assets/js/{tippy.js → embedded-libraries/tippy.js} +0 -0
  149. /package/src/{escape-mechanism.js → pipeline/preprocessing/escape-placeholder-utils.js} +0 -0
  150. /package/src/utils/{isLineWithDefinition.js → is-line-with-definition.js} +0 -0
@@ -0,0 +1,98 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Markdown-it Link Enhancement Module
5
+ *
6
+ * This module enhances link rendering by adding path-based attributes to anchor tags.
7
+ * These attributes can be used for CSS styling or JavaScript behavior based on the
8
+ * link's destination (domain, path segments, etc.).
9
+ *
10
+ * For example, a link to "https://example.com/docs/api" would get attributes like:
11
+ * - path-0="example.com"
12
+ * - path-1="docs"
13
+ * - path-2="api"
14
+ *
15
+ * This allows for targeted styling of links based on their destination.
16
+ */
17
+
18
+ /**
19
+ * Regular expression to extract domains and path segments from URLs
20
+ *
21
+ * This regex has two capture groups:
22
+ * - Group 1: Domain from http(s):// URLs (e.g., "example.com" from "https://example.com/path")
23
+ * - Group 2: Path segments from relative URLs (e.g., "docs" from "/docs/page")
24
+ *
25
+ * The 'g' flag enables global matching to find all segments in a URL.
26
+ */
27
+ const pathSegmentRegex = /(?:http[s]*:\/\/([^\/]*)|(?:\/([^\/?]*)))/g;
28
+
29
+ /**
30
+ * Applies link enhancements to a markdown-it instance
31
+ *
32
+ * @param {Object} md - The markdown-it instance to enhance
33
+ *
34
+ * This function overrides the default link_open and link_close renderers
35
+ * to add path-based attributes and special handling for auto-detected links.
36
+ */
37
+ function applyLinkEnhancements(md) {
38
+
39
+ /**
40
+ * Custom link_open renderer that adds path attributes
41
+ *
42
+ * @param {Array} tokens - Array of all tokens being processed
43
+ * @param {Number} idx - Index of the current link_open token
44
+ * @param {Object} options - Markdown-it options
45
+ * @param {Object} env - Environment/context object
46
+ * @param {Object} renderer - The renderer instance
47
+ * @returns {String} HTML string for the opening anchor tag with path attributes
48
+ */
49
+ md.renderer.rules.link_open = function (tokens, idx, options, env, renderer) {
50
+ let token = tokens[idx];
51
+
52
+ // Process all attributes of the link token
53
+ let attrs = token.attrs.reduce((str, attr) => {
54
+ let name = attr[0]; // Attribute name (e.g., 'href', 'title')
55
+ let value = attr[1]; // Attribute value (e.g., 'https://example.com')
56
+
57
+ // Special processing for href attributes to add path information
58
+ if (name === 'href') {
59
+ let index = 0;
60
+
61
+ // Extract domain and path segments using the regex
62
+ value.replace(pathSegmentRegex, (match, domain, pathSegment) => {
63
+ // Add path-N attributes for each segment found
64
+ // domain OR pathSegment will be defined (not both, due to regex groups)
65
+ str += `path-${index++}="${domain || pathSegment}" `;
66
+ });
67
+ }
68
+
69
+ // Add the original attribute to the string
70
+ str += `${name}="${value}" `;
71
+ return str;
72
+ }, '');
73
+
74
+ // Create the opening anchor tag with all attributes
75
+ let anchor = `<a ${attrs}>`;
76
+
77
+ // Special handling for auto-detected links (linkify plugin)
78
+ // These get an extra <span> wrapper for styling purposes
79
+ return token.markup === 'linkify' ? anchor + '<span>' : anchor;
80
+ };
81
+
82
+ /**
83
+ * Custom link_close renderer
84
+ *
85
+ * @param {Array} tokens - Array of all tokens being processed
86
+ * @param {Number} idx - Index of the current link_close token
87
+ * @param {Object} options - Markdown-it options
88
+ * @param {Object} env - Environment/context object
89
+ * @param {Object} renderer - The renderer instance
90
+ * @returns {String} HTML string for the closing anchor tag
91
+ */
92
+ md.renderer.rules.link_close = function (tokens, idx, options, env, renderer) {
93
+ // Close the extra span for linkify links, or just close the anchor
94
+ return tokens[idx].markup === 'linkify' ? '</span></a>' : '</a>';
95
+ };
96
+ }
97
+
98
+ module.exports = applyLinkEnhancements;
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Configures and applies external markdown-it plugins to a given markdown-it instance.
3
+ * This module centralizes plugin loading to reduce complexity in the main index.js file.
4
+ * It handles only the external npm plugins (e.g., markdown-it-attrs), not custom extensions.
5
+ *
6
+ * @param {Object} md - The markdown-it instance to configure
7
+ * @param {Object} config - Configuration object (e.g., for TOC anchor symbol)
8
+ * @param {Object} containers - The markdown-it-container instance (passed to avoid re-requiring)
9
+ * @param {Object} noticeTypes - Object defining valid notice types (e.g., { note: 1, issue: 1, ... })
10
+ * @param {Object} noticeTitles - Object to track notice titles (passed to maintain state)
11
+ * @returns {Object} The configured markdown-it instance
12
+ */
13
+ function configurePlugins(md, config, containers, noticeTypes, noticeTitles, setToc) {
14
+ // Apply attribute support for elements (e.g., {.class} syntax)
15
+ // This enables adding CSS classes or IDs to markdown elements without HTML.
16
+ md.use(require('markdown-it-attrs'));
17
+
18
+ // Enable chart rendering from code blocks (e.g., ```chart ... ```)
19
+ // Useful for embedding diagrams directly in markdown.
20
+ md.use(require('markdown-it-chart').default);
21
+
22
+ // Support definition lists (e.g., term\n: definition)
23
+ // Enhances standard markdown with formal definition formatting.
24
+ md.use(require('markdown-it-deflist'));
25
+
26
+ // Add reference-style links and footnotes
27
+ // Allows cleaner link management in long documents.
28
+ md.use(require('markdown-it-references'));
29
+
30
+ // Enable Font Awesome icons (e.g., :fa-icon:)
31
+ // Integrates icon rendering for visual enhancements.
32
+ md.use(require('markdown-it-icons').default, 'font-awesome');
33
+
34
+ // Support <ins> tags for inserted text (e.g., ++text++)
35
+ // Useful for highlighting additions in specs.
36
+ md.use(require('markdown-it-ins'));
37
+
38
+ // Support <mark> tags for highlighted text (e.g., ==text==)
39
+ // Helps emphasize important terms or sections.
40
+ md.use(require('markdown-it-mark'));
41
+
42
+ // Enable textual UML diagrams (e.g., @startuml ... @enduml)
43
+ // Allows embedding diagrams without external tools.
44
+ md.use(require('markdown-it-textual-uml'));
45
+
46
+ // Support subscript text (e.g., H~2~O)
47
+ // Essential for scientific or technical writing.
48
+ md.use(require('markdown-it-sub'));
49
+
50
+ // Support superscript text (e.g., E=mc^2^)
51
+ // Complements subscript for mathematical expressions.
52
+ md.use(require('markdown-it-sup'));
53
+
54
+ // Enable task lists (e.g., - [x] Task)
55
+ // Great for checklists in documentation.
56
+ md.use(require('markdown-it-task-lists'));
57
+
58
+ // Support multi-line tables with rowspan/colspan
59
+ // Extends basic tables for complex layouts.
60
+ md.use(require('markdown-it-multimd-table'), {
61
+ multiline: true,
62
+ rowspan: true,
63
+ headerless: true
64
+ });
65
+
66
+ // Configure notice containers (e.g., ::: note ... :::)
67
+ // Uses the passed containers plugin for custom blocks like warnings or examples.
68
+ md.use(containers, 'notice', {
69
+ validate: function (params) {
70
+ // Check if the notice type is valid (e.g., 'note', 'issue')
71
+ let matches = params.match(/(\w+)\s?(.*)?/);
72
+ return matches && noticeTypes[matches[1]];
73
+ },
74
+ render: function (tokens, idx) {
75
+ // Render opening or closing tags for notices
76
+ let matches = tokens[idx].info.match(/(\w+)\s?(.*)?/);
77
+ if (matches && tokens[idx].nesting === 1) {
78
+ let id;
79
+ let type = matches[1];
80
+ if (matches[2]) {
81
+ // Custom ID from title
82
+ id = matches[2].trim().replace(/\s+/g, '-').toLowerCase();
83
+ if (noticeTitles[id]) id += '-' + noticeTitles[id]++;
84
+ else noticeTitles[id] = 1;
85
+ } else {
86
+ // Auto-generated ID
87
+ id = type + '-' + noticeTypes[type]++;
88
+ }
89
+ return `<div id="${id}" class="notice ${type}"><a class="notice-link" href="#${id}">${type.toUpperCase()}</a>`;
90
+ } else {
91
+ return '</div>\n';
92
+ }
93
+ }
94
+ });
95
+
96
+ // Add syntax highlighting with Prism.js
97
+ // Enhances code blocks with colorized syntax.
98
+ md.use(require('markdown-it-prism'));
99
+
100
+ // Generate table of contents with anchors
101
+ // Automatically creates TOC from headings, with customizable symbols.
102
+ md.use(require('markdown-it-toc-and-anchor').default, {
103
+ tocClassName: 'toc',
104
+ tocFirstLevel: 2,
105
+ tocLastLevel: 4,
106
+ tocCallback: (_md, _tokens, html) => setToc(html),
107
+ anchorLinkSymbol: config.specs[0].anchor_symbol || '§',
108
+ anchorClassName: 'toc-anchor d-print-none'
109
+ });
110
+
111
+ // Enable KaTeX for math rendering (e.g., $$...$$)
112
+ // Supports inline and block math expressions.
113
+ md.use(require('@traptitech/markdown-it-katex'));
114
+
115
+ return md;
116
+ }
117
+
118
+ module.exports = { configurePlugins };
@@ -0,0 +1,97 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Markdown-it Table Enhancement Module
5
+ *
6
+ * This module enhances the default table rendering by:
7
+ * 1. Adding Bootstrap CSS classes for styling (table, table-striped, table-bordered, table-hover)
8
+ * 2. Wrapping tables in a responsive container div for mobile-friendly display
9
+ *
10
+ * The markdown-it library uses a token-based rendering system where:
11
+ * - Tokens represent different parts of the markdown (table_open, table_close, etc.)
12
+ * - Renderer rules are functions that convert tokens to HTML
13
+ * - We override the default rules to add our custom behavior
14
+ */
15
+
16
+ /**
17
+ * Applies table enhancements to a markdown-it instance
18
+ *
19
+ * @param {Object} md - The markdown-it instance to enhance
20
+ *
21
+ * How this works:
22
+ * - Saves the original table_open and table_close renderers as fallbacks
23
+ * - Overrides them with custom functions that add Bootstrap classes and responsive wrapper
24
+ * - The table_open rule adds classes to the <table> element and opens a wrapper <div>
25
+ * - The table_close rule closes both the </table> and the wrapper </div>
26
+ */
27
+ function applyTableEnhancements(md) {
28
+ // Store the original table renderers so we can call them after our modifications
29
+ // If no custom renderer exists, markdown-it provides a default self.renderToken function
30
+ const originalTableRender = md.renderer.rules.table_open || function (tokens, idx, options, env, self) {
31
+ return self.renderToken(tokens, idx, options);
32
+ };
33
+
34
+ const originalTableCloseRender = md.renderer.rules.table_close || function (tokens, idx, options, env, self) {
35
+ return self.renderToken(tokens, idx, options);
36
+ };
37
+
38
+ /**
39
+ * Custom table_open renderer
40
+ *
41
+ * @param {Array} tokens - Array of all tokens being processed
42
+ * @param {Number} idx - Index of the current table_open token
43
+ * @param {Object} options - Markdown-it options
44
+ * @param {Object} env - Environment/context object
45
+ * @param {Object} self - The renderer instance
46
+ * @returns {String} HTML string for the opening table with wrapper
47
+ */
48
+ md.renderer.rules.table_open = function (tokens, idx, options, env, self) {
49
+ // Get the current table token to modify its attributes
50
+ const token = tokens[idx];
51
+
52
+ // Find if the table already has a class attribute
53
+ const classIndex = token.attrIndex('class');
54
+
55
+ // Define the Bootstrap classes we want to add
56
+ const tableClasses = 'table table-striped table-bordered table-hover';
57
+
58
+ if (classIndex < 0) {
59
+ // No existing class attribute - add our classes
60
+ token.attrPush(['class', tableClasses]);
61
+ } else {
62
+ // Class attribute exists - append our classes if they're not already present
63
+ const existingClasses = token.attrs[classIndex][1];
64
+
65
+ // Filter out classes that are already present to avoid duplicates
66
+ const classesToAdd = tableClasses
67
+ .split(' ')
68
+ .filter(cls => !existingClasses.includes(cls))
69
+ .join(' ');
70
+
71
+ // Only append if we have new classes to add
72
+ if (classesToAdd) {
73
+ token.attrs[classIndex][1] = existingClasses + ' ' + classesToAdd;
74
+ }
75
+ }
76
+
77
+ // Return the responsive wrapper div + the enhanced table opening tag
78
+ return '<div class="table-responsive-md">' + originalTableRender(tokens, idx, options, env, self);
79
+ };
80
+
81
+ /**
82
+ * Custom table_close renderer
83
+ *
84
+ * @param {Array} tokens - Array of all tokens being processed
85
+ * @param {Number} idx - Index of the current table_close token
86
+ * @param {Object} options - Markdown-it options
87
+ * @param {Object} env - Environment/context object
88
+ * @param {Object} self - The renderer instance
89
+ * @returns {String} HTML string for closing the table and wrapper
90
+ */
91
+ md.renderer.rules.table_close = function (tokens, idx, options, env, self) {
92
+ // Close both the table and the responsive wrapper div
93
+ return originalTableCloseRender(tokens, idx, options, env, self) + '</div>';
94
+ };
95
+ }
96
+
97
+ module.exports = applyTableEnhancements;
@@ -0,0 +1,152 @@
1
+ 'use strict';
2
+
3
+ const { ESCAPED_PLACEHOLDER } = require('../pipeline/preprocessing/escape-processor.js');
4
+
5
+ /**
6
+ * Markdown-it Template-Tag Syntax Module
7
+ *
8
+ * This module adds support for custom template-tag syntax like [[ref:spec,term]] in markdown.
9
+ * It handles the parsing and rendering of these special constructs.
10
+ *
11
+ * Template-tag syntax format: [[type:arg1,arg2,arg3]]
12
+ * Examples:
13
+ * - [[def:term,alias]] - Define a term with an alias
14
+ * - [[tref:external-spec,term]] - Reference a term from external specification
15
+ * - [[xref:spec,term]] - Cross-reference to another specification
16
+ *
17
+ * The module works in two phases:
18
+ * 1. PARSING: Scans markdown text for [[...]] patterns and creates template-tag tokens
19
+ * 2. RENDERING: Converts template-tag tokens to final HTML using template-tag handlers
20
+ */
21
+
22
+ /**
23
+ * Configuration constants for template-tag syntax
24
+ * These define the delimiters and parsing rules for template-tag markers
25
+ */
26
+ const { templateTags } = require('../utils/regex-patterns');
27
+
28
+ const levels = 2; // Number of bracket characters (e.g., 2 = [[]])
29
+ const openString = '['.repeat(levels); // Opening delimiter: '[['
30
+ const closeString = ']'.repeat(levels); // Closing delimiter: ']]'
31
+
32
+ // Regular expression to extract template-tag components from [[type:args]] syntax
33
+ // Group 1: template-tag type (e.g., 'ref', 'tref', 'def')
34
+ // Group 2: arguments (everything after the colon, comma-separated)
35
+ const contentRegex = templateTags.content;
36
+
37
+ /**
38
+ * Applies template-tag syntax support to a markdown-it instance
39
+ *
40
+ * @param {Object} md - The markdown-it instance to enhance
41
+ * @param {Array} templates - Array of template-tag handler objects
42
+ *
43
+ * Each template-tag handler should have:
44
+ * - filter(type): function that returns true if this handler processes the given type
45
+ * - parse(token, type, ...args): optional preprocessing function
46
+ * - render(token, type, ...args): function that returns HTML string
47
+ */
48
+ function applyTemplateTagSyntax(md, templates = []) {
49
+
50
+ /**
51
+ * Custom inline parsing rule for template-tag syntax
52
+ *
53
+ * This rule is added to markdown-it's inline ruler (which processes inline elements)
54
+ * and runs after the 'emphasis' rule to handle our [[template-tag]] syntax.
55
+ *
56
+ * @param {Object} state - Parser state object containing source text and position
57
+ * @param {Boolean} silent - If true, don't modify state (used for validation)
58
+ * @returns {Boolean} true if template-tag was found and processed, false otherwise
59
+ */
60
+ function templates_ruler(state, silent) {
61
+ // Get current position in the source text
62
+ var start = state.pos;
63
+
64
+ // Skip processing if we're at an escaped placeholder (handled by escape mechanism)
65
+ if (state.src.slice(start, start + ESCAPED_PLACEHOLDER.length) === ESCAPED_PLACEHOLDER) {
66
+ return false;
67
+ }
68
+
69
+ // Check if we're at the start of a template-tag marker [[
70
+ let prefix = state.src.slice(start, start + levels);
71
+ if (prefix !== openString) {
72
+ return false; // Not a template-tag, let other rules handle it
73
+ }
74
+
75
+ // Find the matching closing marker ]]
76
+ var indexOfClosingBrace = state.src.indexOf(closeString, start);
77
+
78
+ if (indexOfClosingBrace > 0) {
79
+ // Extract the content between [[ and ]]
80
+ let templateTagContent = state.src.slice(start + levels, indexOfClosingBrace);
81
+
82
+ // Parse the template-tag content using regex
83
+ let match = contentRegex.exec(templateTagContent);
84
+ if (!match) {
85
+ return false; // Invalid template-tag syntax
86
+ }
87
+
88
+ // Extract template-tag type and arguments
89
+ let type = match[1];
90
+ let template = templates.find(t => t.filter(type) && t);
91
+
92
+ if (!template) {
93
+ return false; // No handler found for this template-tag type
94
+ }
95
+
96
+ // Parse arguments (comma-separated list)
97
+ let args = match[2] ? match[2].trim().split(/\s*,+\s*/) : [];
98
+
99
+ // Create a new template-tag token in the token stream
100
+ // This token will be processed later during rendering
101
+ let token = state.push('template', '', 0);
102
+ token.content = match[0]; // Store original matched content
103
+ token.info = { type, template, args }; // Store parsed information
104
+
105
+ // If the template-tag has a parse function, call it for preprocessing
106
+ // This allows template-tags to modify their content during parsing
107
+ if (template.parse) {
108
+ token.content = template.parse(token, type, ...args) || token.content;
109
+ }
110
+
111
+ // Move parser position past the template-tag
112
+ state.pos = indexOfClosingBrace + levels;
113
+ return true; // Template-tag successfully processed
114
+ }
115
+
116
+ return false; // No closing marker found
117
+ }
118
+
119
+ // Register the template-tag parsing rule with markdown-it
120
+ // It runs after 'emphasis' to ensure proper precedence
121
+ md.inline.ruler.after('emphasis', 'templates', templates_ruler);
122
+
123
+ /**
124
+ * Renderer for template-tag tokens
125
+ *
126
+ * This function is called during the rendering phase to convert
127
+ * template-tag tokens into their final HTML representation.
128
+ *
129
+ * @param {Array} tokens - Array of all tokens being rendered
130
+ * @param {Number} idx - Index of current template-tag token
131
+ * @param {Object} options - Markdown-it options
132
+ * @param {Object} env - Environment/context object
133
+ * @param {Object} renderer - The renderer instance
134
+ * @returns {String} HTML string for this template-tag
135
+ */
136
+ md.renderer.rules.template = function (tokens, idx, options, env, renderer) {
137
+ let token = tokens[idx];
138
+ let template = token.info.template;
139
+
140
+ // Call the template-tag's render function if it exists
141
+ if (template.render) {
142
+ let result = template.render(token, token.info.type, ...token.info.args);
143
+ // Return the rendered result, or fall back to original syntax if render fails
144
+ return result || (openString + token.content + closeString);
145
+ }
146
+
147
+ // No render function - return the content as-is
148
+ return token.content;
149
+ };
150
+ }
151
+
152
+ module.exports = applyTemplateTagSyntax;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Parsers Module Index - Functional Style
3
+ *
4
+ * This module provides a centralized export point for all parser functions.
5
+ * It promotes clean imports and consistent API access across the codebase.
6
+ * The functional approach provides better tree-shaking and simpler testing.
7
+ */
8
+
9
+ const templateTagParser = require('./template-tag-parser');
10
+ const specParser = require('./spec-parser');
11
+
12
+ module.exports = {
13
+ // Parser factory functions
14
+ createTemplateTagParser: templateTagParser.createTemplateTagParser,
15
+ createSpecParser: specParser.createSpecParser
16
+ };
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Specification Parser - Functional Style
3
+ *
4
+ * This module provides pure functions for processing specification-related markdown extensions:
5
+ * - [[spec: name]] - Individual specification references
6
+ * - [[spec-*: name]] - Grouped specification references (e.g., spec-normative, spec-informative)
7
+ *
8
+ * The functional approach isolates specification logic from terminology concerns,
9
+ * reducing cognitive complexity and improving maintainability through pure functions.
10
+ */
11
+
12
+ const { renderRefGroup } = require('../pipeline/rendering/render-utils.js');
13
+ const { whitespace } = require('../utils/regex-patterns');
14
+
15
+ /**
16
+ * Normalizes a specification name for consistent lookup
17
+ * @param {string} name - The original specification name
18
+ * @returns {string} The normalized name (uppercase with hyphens)
19
+ */
20
+ function normalizeSpecName(name) {
21
+ return name.replace(whitespace.oneOrMore, '-').toUpperCase();
22
+ }
23
+
24
+ /**
25
+ * Looks up a specification in the corpus using various name formats
26
+ * @param {Object} specCorpus - The specification corpus
27
+ * @param {string} name - The specification name to look up
28
+ * @returns {Object|null} The found specification or null
29
+ */
30
+ function findSpecInCorpus(specCorpus, name) {
31
+ const normalizedName = normalizeSpecName(name);
32
+
33
+ return specCorpus[normalizedName] ||
34
+ specCorpus[normalizedName.toLowerCase()] ||
35
+ specCorpus[name.toLowerCase()] ||
36
+ specCorpus[name] ||
37
+ null;
38
+ }
39
+
40
+ /**
41
+ * Processes specification reference tokens during parsing phase
42
+ * Looks up specification details and stores them for later rendering
43
+ * @param {Object} specCorpus - The loaded specification corpus
44
+ * @param {Object} globalState - Global state object containing specGroups
45
+ * @param {Object} token - The markdown-it token being processed
46
+ * @param {string} type - The type of spec construct (e.g., 'spec', 'spec-normative')
47
+ * @param {string} name - The specification name to look up
48
+ */
49
+ function parseSpecReference(specCorpus, globalState, token, type, name) {
50
+ if (!name) return;
51
+
52
+ const spec = findSpecInCorpus(specCorpus, name);
53
+
54
+ if (spec) {
55
+ // Store the normalized name for consistent referencing
56
+ const normalizedName = normalizeSpecName(name);
57
+ spec._name = normalizedName;
58
+
59
+ // Organize specifications by type/group for rendering
60
+ const group = globalState.specGroups[type] = globalState.specGroups[type] || {};
61
+ token.info.spec = group[normalizedName] = spec;
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Renders an individual specification reference as a link
67
+ * @param {Object} token - The token containing spec information
68
+ * @returns {string} HTML anchor element linking to the specification
69
+ */
70
+ function renderIndividualSpec(token) {
71
+ const spec = token.info.spec;
72
+ if (!spec) return '';
73
+
74
+ // Create a reference link to the specification
75
+ // The href uses a 'ref:' prefix to distinguish from term references
76
+ return `[<a class="spec-reference" href="#ref:${spec._name}">${spec._name}</a>]`;
77
+ }
78
+
79
+ /**
80
+ * Renders a group of specifications as a formatted list
81
+ * @param {string} type - The specification group type
82
+ * @param {Object} specGroups - The global specGroups object
83
+ * @returns {string} HTML representation of the specification group
84
+ */
85
+ function renderSpecGroup(type, specGroups) {
86
+ // Delegate to the render utility which handles the complex group formatting
87
+ return renderRefGroup(type, specGroups);
88
+ }
89
+
90
+ /**
91
+ * Renders specification references during the rendering phase
92
+ * Creates either individual spec links or grouped spec lists
93
+ * @param {Object} specGroups - The global specGroups object
94
+ * @param {Object} token - The markdown-it token being rendered
95
+ * @param {string} type - The type of spec construct
96
+ * @param {string} name - The specification name (if individual reference)
97
+ * @returns {string} HTML for the specification reference or group
98
+ */
99
+ function renderSpecReference(specGroups, token, type, name) {
100
+ if (name) {
101
+ // Render individual specification reference
102
+ return renderIndividualSpec(token);
103
+ } else {
104
+ // Render grouped specification references (when no specific name is provided)
105
+ return renderSpecGroup(type, specGroups);
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Gets all specifications in a specific group
111
+ * Utility function for accessing grouped specifications
112
+ * @param {Object} specGroups - The global specGroups object
113
+ * @param {string} type - The specification group type
114
+ * @returns {Object} Object containing all specs in the group
115
+ */
116
+ function getSpecGroup(specGroups, type) {
117
+ return specGroups[type] || {};
118
+ }
119
+
120
+ /**
121
+ * Checks if a specification exists in the corpus
122
+ * @param {Object} specCorpus - The specification corpus
123
+ * @param {string} name - The specification name to check
124
+ * @returns {boolean} True if the specification exists
125
+ */
126
+ function hasSpec(specCorpus, name) {
127
+ return findSpecInCorpus(specCorpus, name) !== null;
128
+ }
129
+
130
+ /**
131
+ * Creates a specification parser function with bound corpus and global state.
132
+ * This provides a clean interface similar to the class-based approach but with functional benefits.
133
+ * @param {Object} specCorpus - The specification corpus
134
+ * @param {Object} globalState - Global state object
135
+ * @returns {Object} An object with parser and renderer functions
136
+ */
137
+ function createSpecParser(specCorpus, globalState) {
138
+ return {
139
+ parseSpecReference: (token, type, name) => parseSpecReference(specCorpus, globalState, token, type, name),
140
+ renderSpecReference: (token, type, name) => renderSpecReference(globalState.specGroups, token, type, name),
141
+ hasSpec: (name) => hasSpec(specCorpus, name),
142
+ getSpecGroup: (type) => getSpecGroup(globalState.specGroups, type)
143
+ };
144
+ }
145
+
146
+ module.exports = {
147
+ createSpecParser,
148
+ // Export individual functions for testing purposes
149
+ parseSpecReference,
150
+ renderIndividualSpec,
151
+ hasSpec
152
+ };