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.
Files changed (130) hide show
  1. package/.github/copilot-instructions.md +13 -0
  2. package/assets/compiled/body.js +17 -11
  3. package/assets/compiled/head.css +6 -4
  4. package/assets/css/collapse-definitions.css +0 -1
  5. package/assets/css/create-pdf.css +4 -2
  6. package/assets/css/create-term-filter.css +4 -4
  7. package/assets/css/definition-buttons-container.css +60 -0
  8. package/assets/css/insert-trefs.css +7 -0
  9. package/assets/css/sidebar-toc.css +2 -1
  10. package/assets/css/terms-and-definitions.css +73 -22
  11. package/assets/js/add-href-to-snapshot-link.js +16 -9
  12. package/assets/js/addAnchorsToTerms.js +1 -1
  13. package/assets/js/charts.js +10 -0
  14. package/assets/js/collapse-definitions.js +13 -2
  15. package/assets/js/collapse-meta-info.js +11 -9
  16. package/assets/js/definition-button-container-utils.js +82 -0
  17. package/assets/js/edit-term-buttons.js +77 -20
  18. package/assets/js/github-issues.js +35 -0
  19. package/assets/js/github-repo-info.js +144 -0
  20. package/assets/js/highlight-heading-plus-sibling-nodes.test.js +18 -0
  21. package/assets/js/insert-trefs.js +62 -13
  22. package/assets/js/mermaid-diagrams.js +11 -0
  23. package/assets/js/terminology-section-utility-container/README.md +107 -0
  24. package/assets/js/terminology-section-utility-container/create-alphabet-index.js +17 -0
  25. package/assets/js/{create-term-filter.js → terminology-section-utility-container/create-term-filter.js} +11 -44
  26. package/assets/js/terminology-section-utility-container/hide-show-utility-container.js +21 -0
  27. package/assets/js/terminology-section-utility-container/search.js +203 -0
  28. package/assets/js/terminology-section-utility-container.js +203 -0
  29. package/assets/js/tooltips.js +283 -0
  30. package/config/asset-map.json +24 -16
  31. package/index.js +57 -390
  32. package/package.json +5 -2
  33. package/src/add-remove-xref-source.js +20 -21
  34. package/src/collect-external-references.js +8 -337
  35. package/src/collect-external-references.test.js +440 -33
  36. package/src/configure.js +8 -109
  37. package/src/create-docx.js +7 -6
  38. package/src/create-pdf.js +15 -14
  39. package/src/freeze-spec-data.js +46 -0
  40. package/src/git-info.test.js +76 -0
  41. package/src/health-check/destination-gitignore-checker.js +5 -3
  42. package/src/health-check/external-specs-checker.js +5 -4
  43. package/src/health-check/specs-configuration-checker.js +2 -1
  44. package/src/health-check/term-references-checker.js +5 -3
  45. package/src/health-check/terms-intro-checker.js +2 -1
  46. package/src/health-check/tref-term-checker.js +8 -7
  47. package/src/health-check.js +8 -7
  48. package/src/init.js +3 -2
  49. package/src/install-from-boilerplate/add-gitignore-entries.js +3 -2
  50. package/src/install-from-boilerplate/add-scripts-keys.js +5 -4
  51. package/src/install-from-boilerplate/boilerplate/README.md +1 -1
  52. package/src/install-from-boilerplate/boilerplate/spec/example-markup-in-markdown.md +1 -1
  53. package/src/install-from-boilerplate/boilerplate/spec/spec-head.md +1 -1
  54. package/src/install-from-boilerplate/boilerplate/specs.json +2 -1
  55. package/src/install-from-boilerplate/config-scripts-keys.js +3 -3
  56. package/src/install-from-boilerplate/copy-boilerplate.js +2 -1
  57. package/src/install-from-boilerplate/copy-system-files.js +4 -3
  58. package/src/install-from-boilerplate/custom-update.js +12 -1
  59. package/src/install-from-boilerplate/help.txt +1 -1
  60. package/src/install-from-boilerplate/menu.sh +6 -6
  61. package/src/json-key-validator.js +17 -11
  62. package/src/markdown-it/README.md +207 -0
  63. package/src/markdown-it/definition-lists.js +397 -0
  64. package/src/markdown-it/index.js +83 -0
  65. package/src/markdown-it/link-enhancement.js +98 -0
  66. package/src/markdown-it/plugins.js +118 -0
  67. package/src/markdown-it/table-enhancement.js +97 -0
  68. package/src/markdown-it/template-tag-syntax.js +152 -0
  69. package/src/parsers/index.js +16 -0
  70. package/src/parsers/spec-parser.js +152 -0
  71. package/src/parsers/spec-parser.test.js +109 -0
  72. package/src/parsers/template-tag-parser.js +277 -0
  73. package/src/parsers/template-tag-parser.test.js +107 -0
  74. package/src/pipeline/configuration/configure-starterpack.js +200 -0
  75. package/src/{create-external-specs-list.js → pipeline/configuration/create-external-specs-list.js} +13 -12
  76. package/src/{create-term-index.js → pipeline/configuration/create-term-index.js} +19 -18
  77. package/src/{create-versions-index.js → pipeline/configuration/create-versions-index.js} +4 -3
  78. package/src/{insert-term-index.js → pipeline/configuration/insert-term-index.js} +2 -2
  79. package/src/pipeline/configuration/prepare-spec-configuration.js +70 -0
  80. package/src/pipeline/parsing/apply-markdown-it-extensions.js +35 -0
  81. package/src/pipeline/parsing/create-markdown-parser.js +94 -0
  82. package/src/pipeline/parsing/create-markdown-parser.test.js +49 -0
  83. package/src/{html-dom-processor.js → pipeline/postprocessing/definition-list-postprocessor.js} +69 -10
  84. package/src/{escape-handler.js → pipeline/preprocessing/escape-processor.js} +3 -1
  85. package/src/{fix-markdown-files.js → pipeline/preprocessing/normalize-terminology-markdown.js} +41 -31
  86. package/src/pipeline/references/collect-external-references.js +307 -0
  87. package/src/pipeline/references/external-references-service.js +231 -0
  88. package/src/pipeline/references/fetch-terms-from-index.js +198 -0
  89. package/src/pipeline/references/match-term.js +34 -0
  90. package/src/{collectExternalReferences/matchTerm.test.js → pipeline/references/match-term.test.js} +8 -2
  91. package/src/pipeline/references/process-xtrefs-data.js +94 -0
  92. package/src/pipeline/references/xtref-utils.js +166 -0
  93. package/src/pipeline/rendering/render-spec-document.js +146 -0
  94. package/src/pipeline/rendering/render-utils.js +154 -0
  95. package/src/utils/LOGGER.md +81 -0
  96. package/src/utils/{doesUrlExist.js → does-url-exist.js} +4 -3
  97. package/src/utils/fetch.js +5 -4
  98. package/src/utils/file-opener.js +3 -2
  99. package/src/utils/git-info.js +77 -0
  100. package/src/utils/logger.js +74 -0
  101. package/src/utils/regex-patterns.js +471 -0
  102. package/src/utils/regex-patterns.test.js +281 -0
  103. package/templates/template.html +56 -21
  104. package/assets/js/create-alphabet-index.js +0 -60
  105. package/assets/js/hide-show-utility-container.js +0 -16
  106. package/assets/js/index.js +0 -87
  107. package/assets/js/search.js +0 -365
  108. package/src/collectExternalReferences/fetchTermsFromIndex.js +0 -284
  109. package/src/collectExternalReferences/matchTerm.js +0 -32
  110. package/src/collectExternalReferences/processXTrefsData.js +0 -108
  111. package/src/freeze.js +0 -90
  112. package/src/markdown-it-extensions.js +0 -395
  113. package/src/references.js +0 -114
  114. /package/assets/css/{bootstrap.min.css → embedded-libraries/bootstrap.min.css} +0 -0
  115. /package/assets/css/{prism.css → embedded-libraries/prism.css} +0 -0
  116. /package/assets/css/{prism.dark.css → embedded-libraries/prism.dark.css} +0 -0
  117. /package/assets/css/{prism.default.css → embedded-libraries/prism.default.css} +0 -0
  118. /package/assets/js/{bootstrap.bundle.min.js → embedded-libraries/bootstrap.bundle.min.js} +0 -0
  119. /package/assets/js/{chart.js → embedded-libraries/chart.js} +0 -0
  120. /package/assets/js/{diff.min.js → embedded-libraries/diff.min.js} +0 -0
  121. /package/assets/js/{font-awesome.js → embedded-libraries/font-awesome.js} +0 -0
  122. /package/assets/js/{mermaid.js → embedded-libraries/mermaid.js} +0 -0
  123. /package/assets/js/{notyf.js → embedded-libraries/notyf.js} +0 -0
  124. /package/assets/js/{popper.js → embedded-libraries/popper.js} +0 -0
  125. /package/assets/js/{prism.dark.js → embedded-libraries/prism.dark.js} +0 -0
  126. /package/assets/js/{prism.default.js → embedded-libraries/prism.default.js} +0 -0
  127. /package/assets/js/{prism.js → embedded-libraries/prism.js} +0 -0
  128. /package/assets/js/{tippy.js → embedded-libraries/tippy.js} +0 -0
  129. /package/src/{escape-mechanism.js → pipeline/preprocessing/escape-placeholder-utils.js} +0 -0
  130. /package/src/utils/{isLineWithDefinition.js → is-line-with-definition.js} +0 -0
@@ -12,13 +12,14 @@
12
12
  *
13
13
  * @requires fs-extra - File system operations with extra methods.
14
14
  * @requires path - Utilities for working with file and directory paths.
15
- * @file src/create-term-index.js
15
+ * @file src/pipeline/configuration/create-term-index.js
16
16
  * @author Kor Dwarshuis
17
17
  * @version 1.1.0
18
18
  * @since 2024-09-02
19
19
  */
20
20
 
21
- const { shouldProcessFile } = require('./utils/file-filter');
21
+ const { shouldProcessFile } = require('../../utils/file-filter.js');
22
+ const Logger = require('../../utils/logger.js');
22
23
 
23
24
  function createTermIndex() {
24
25
  try {
@@ -28,41 +29,41 @@ function createTermIndex() {
28
29
 
29
30
  // Check if specs.json exists
30
31
  if (!fs.existsSync(configPath)) {
31
- console.warn(`Config file '${configPath}' not found. Using default configuration.`);
32
+ Logger.warn(`Config file '${configPath}' not found. Using default configuration.`);
32
33
  var config = { specs: [] };
33
34
  } else {
34
35
  // Read config with try-catch to handle parsing errors
35
36
  try {
36
37
  var config = fs.readJsonSync(configPath);
37
38
  } catch (readError) {
38
- console.warn(`Error reading config file: ${readError.message}. Using default configuration.`);
39
+ Logger.warn(`Error reading config file: ${readError.message}. Using default configuration.`);
39
40
  var config = { specs: [] };
40
41
  }
41
42
  }
42
43
 
43
44
  // Provide defaults for missing config
44
45
  if (!config) {
45
- console.warn('Config file is empty or invalid. Using defaults.');
46
+ Logger.warn('Config file is empty or invalid. Using defaults.');
46
47
  config = { specs: [] };
47
48
  }
48
49
 
49
50
  if (!config.specs) {
50
- console.warn('No specs array found in config. Creating empty specs array.');
51
+ Logger.warn('No specs array found in config. Creating empty specs array.');
51
52
  config.specs = [];
52
53
  } else if (!Array.isArray(config.specs)) {
53
- console.warn('Config specs is not an array. Converting to array.');
54
+ Logger.warn('Config specs is not an array. Converting to array.');
54
55
  config.specs = [config.specs]; // Convert to array if it's an object
55
56
  }
56
57
 
57
58
  // If no valid specs, create an empty term index
58
59
  if (config.specs.length === 0) {
59
- console.warn('No specs found in configuration. Creating an empty term index.');
60
+ Logger.warn('No specs found in configuration. Creating an empty term index.');
60
61
  }
61
62
 
62
63
  // Extract spec directories with fallback to current directory
63
64
  const specDirectories = config.specs.map((spec, index) => {
64
65
  if (!spec.spec_directory) {
65
- console.warn(`Warning: spec_directory missing in specs.json entry #${index + 1}. Using current directory.`);
66
+ Logger.warn(`spec_directory missing in specs.json entry #${index + 1}. Using current directory.`);
66
67
  return '.'; // Default to current directory
67
68
  }
68
69
  return spec.spec_directory;
@@ -71,7 +72,7 @@ function createTermIndex() {
71
72
  // Extract term directories with fallback to default value
72
73
  const specTermDirectoryName = config.specs.map((spec, index) => {
73
74
  if (!spec.spec_terms_directory) {
74
- console.warn(`Warning: spec_terms_directory missing in specs.json entry #${index + 1}. Using default 'terms' directory.`);
75
+ Logger.warn(`spec_terms_directory missing in specs.json entry #${index + 1}. Using default 'terms' directory.`);
75
76
  return 'terms'; // Default directory name for terms
76
77
  }
77
78
  return spec.spec_terms_directory;
@@ -79,7 +80,7 @@ function createTermIndex() {
79
80
 
80
81
  // Safety check - if we have no valid entries, warn and exit cleanly
81
82
  if (specDirectories.length === 0 || specTermDirectoryName.length === 0) {
82
- console.log('No term directories found in configuration. Creating empty term index.');
83
+ Logger.info('No term directories found in configuration. Creating empty term index.');
83
84
 
84
85
  // Create an empty term index
85
86
  const outputPathJSON = path.join('.cache', 'term-index.json');
@@ -87,14 +88,14 @@ function createTermIndex() {
87
88
  fs.mkdirSync('.cache', { recursive: true });
88
89
  }
89
90
  fs.writeJsonSync(outputPathJSON, [], { spaces: 2 });
90
- console.log(`✅ Empty term index created at: ${outputPathJSON}`);
91
+ Logger.success(`Empty term index created at: ${outputPathJSON}`);
91
92
  return; // Exit function early
92
93
  }
93
94
 
94
95
  // Verify that the base spec directory exists
95
96
  const baseSpecDir = specDirectories[0];
96
97
  if (!baseSpecDir || !fs.existsSync(baseSpecDir)) {
97
- console.warn(`Spec directory '${baseSpecDir}' does not exist. Creating empty term index.`);
98
+ Logger.warn(`Spec directory '${baseSpecDir}' does not exist. Creating empty term index.`);
98
99
 
99
100
  // Create an empty term index
100
101
  const outputPathJSON = path.join('.cache', 'term-index.json');
@@ -102,7 +103,7 @@ function createTermIndex() {
102
103
  fs.mkdirSync('.cache', { recursive: true });
103
104
  }
104
105
  fs.writeJsonSync(outputPathJSON, [], { spaces: 2 });
105
- console.log(`✅ Empty term index created at: ${outputPathJSON}`);
106
+ Logger.success(`Empty term index created at: ${outputPathJSON}`);
106
107
  return; // Exit function early
107
108
  }
108
109
 
@@ -111,14 +112,14 @@ function createTermIndex() {
111
112
 
112
113
  let files = [];
113
114
  if (!fs.existsSync(termsDir)) {
114
- console.warn(`Terms directory '${termsDir}' does not exist. Creating an empty term index.`);
115
+ Logger.warn(`Terms directory '${termsDir}' does not exist. Creating an empty term index.`);
115
116
  } else {
116
117
  // Get list of files and filter them
117
118
  files = fs.readdirSync(termsDir).filter(shouldProcessFile);
118
119
  }
119
120
 
120
121
  if (files.length === 0) {
121
- console.log('Warning: No term files found to process.');
122
+ Logger.warn('No term files found to process.');
122
123
  }
123
124
 
124
125
  const filePaths = files.map(file => specTermDirectoryName[0] + '/' + file);
@@ -132,12 +133,12 @@ function createTermIndex() {
132
133
  // Write the term index file
133
134
  try {
134
135
  fs.writeJsonSync(outputPathJSON, filePaths, { spaces: 2 });
135
- console.log(`✅ Term index created with ${files.length} terms. Output: ${outputPathJSON}`);
136
+ Logger.success(`Term index created with ${files.length} terms. Output: ${outputPathJSON}`);
136
137
  } catch (writeError) {
137
138
  throw new Error(`Failed to write term index file: ${writeError.message}`);
138
139
  }
139
140
  } catch (error) {
140
- console.error(`❌ Error creating term index: ${error.message}`);
141
+ Logger.error(`Error creating term index: ${error.message}`);
141
142
  throw error;
142
143
  }
143
144
  }
@@ -14,6 +14,7 @@
14
14
 
15
15
  const fs = require('fs-extra');
16
16
  const path = require('path');
17
+ const Logger = require('../../utils/logger.js');
17
18
 
18
19
  function createVersionsIndex(outputPath) {
19
20
  // Directory containing the version files
@@ -22,7 +23,7 @@ function createVersionsIndex(outputPath) {
22
23
  // Check if the directory that holds the versions / snapshots exists, if not create it
23
24
  if (!fs.existsSync(versionsDir)) {
24
25
  fs.mkdirSync(versionsDir, { recursive: true });
25
- console.log('Directory created:', versionsDir);
26
+ Logger.info('Directory created:', versionsDir);
26
27
  }
27
28
 
28
29
  // Get all directories in the destination directory
@@ -71,9 +72,9 @@ function createVersionsIndex(outputPath) {
71
72
  const indexPath = path.join(versionsDir, 'index.html');
72
73
  fs.writeFile(indexPath, htmlContent, (err) => {
73
74
  if (err) {
74
- console.error(`❌ Error writing index file: ${err}`);
75
+ Logger.error(`Error writing index file: ${err}`);
75
76
  } else {
76
- console.log(`✅ Index file created at ${indexPath}`);
77
+ Logger.success(`Index file created at ${indexPath}`);
77
78
  }
78
79
  });
79
80
  }
@@ -9,7 +9,7 @@
9
9
  * @function
10
10
  * @name insertTermIndex
11
11
  * @returns {void}
12
- * @file src/insert-term-index.js
12
+ * @file src/pipeline/configuration/insert-term-index.js
13
13
  * @author Kor Dwarshuis
14
14
  * @version 1.0.0
15
15
  * @since 2024-09-02
@@ -26,7 +26,7 @@ function insertTermIndex() {
26
26
 
27
27
  // Find the index of the "terms-and-definitions-intro.md" file in the markdown paths
28
28
  const index = markdownPaths.indexOf('terms-and-definitions-intro.md');
29
-
29
+
30
30
  //
31
31
  if (index !== -1) {
32
32
  // Insert the items of the "terms" array after the found index
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Handles configuration and initialization tasks before rendering specs.
3
+ * This module centralizes setup logic to reduce complexity in index.js.
4
+ * It loads config, runs validators, prepares external specs, and initializes shared variables.
5
+ */
6
+
7
+ const fs = require('fs-extra');
8
+ const path = require('path');
9
+ const findPkgDir = require('find-pkg-dir');
10
+
11
+ const { initialize } = require('../../init.js');
12
+ const Logger = require('../../utils/logger.js');
13
+ const { runJsonKeyValidatorSync } = require('../../json-key-validator.js');
14
+ const { createTermIndex } = require('./create-term-index.js');
15
+ const { insertTermIndex } = require('./insert-term-index.js');
16
+ const { normalizeTerminologyMarkdown } = require('../preprocessing/normalize-terminology-markdown.js');
17
+
18
+ /**
19
+ * Initializes configuration and shared variables for spec processing.
20
+ * @param {Object} options - Options passed to the main function.
21
+ * @returns {Object} An object containing config, externalSpecsList, template, assets, and other shared variables.
22
+ */
23
+ async function initializeConfig(options = {}) {
24
+ try {
25
+ await initialize();
26
+
27
+ runJsonKeyValidatorSync();
28
+ createTermIndex();
29
+ insertTermIndex();
30
+
31
+ const modulePath = findPkgDir(__dirname);
32
+ let config = fs.readJsonSync('./.cache/specs-generated.json');
33
+
34
+ const createExternalSpecsList = require('./create-external-specs-list.js');
35
+ const externalSpecsList = createExternalSpecsList(config);
36
+
37
+ const createVersionsIndex = require('./create-versions-index.js');
38
+ createVersionsIndex(config.specs[0].output_path);
39
+
40
+ normalizeTerminologyMarkdown(path.join(config.specs[0].spec_directory, config.specs[0].spec_terms_directory));
41
+
42
+ let template = fs.readFileSync(path.join(modulePath, 'templates/template.html'), 'utf8');
43
+ let assets = fs.readJsonSync(modulePath + '/config/asset-map.json');
44
+
45
+ // Initialize shared variables
46
+ let externalReferences;
47
+ let references = [];
48
+ let definitions = [];
49
+ let toc;
50
+ let specGroups = {};
51
+ let noticeTitles = {};
52
+
53
+ return {
54
+ config,
55
+ externalSpecsList,
56
+ template,
57
+ assets,
58
+ externalReferences,
59
+ references,
60
+ definitions,
61
+ specGroups,
62
+ noticeTitles
63
+ };
64
+ } catch (error) {
65
+ Logger.error(`Error during configuration initialization: ${error.message}`);
66
+ throw error;
67
+ }
68
+ }
69
+
70
+ module.exports = { initializeConfig };
@@ -0,0 +1,35 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Markdown-it Extensions - Legacy Interface
5
+ *
6
+ * This file provides backward compatibility for the refactored markdown-it extensions.
7
+ * All the complex logic has been moved to specialized modules in the './markdown-it/' directory
8
+ * for better maintainability and understanding.
9
+ *
10
+ * The modular approach breaks down the functionality into:
11
+ * - Table enhancements (Bootstrap styling, responsive wrappers)
12
+ * - Template-tag syntax processing ([[def:term]], [[tref:spec,term]], etc.)
13
+ * - Link enhancements (path-based attributes)
14
+ * - Definition list processing (terminology vs references, term classification)
15
+ *
16
+ * This refactoring reduces cognitive complexity and makes the code more approachable
17
+ * for developers who are not familiar with the markdown-it library.
18
+ */
19
+
20
+ // Import the new modular markdown-it extensions
21
+ const applyMarkdownItExtensions = require('../../markdown-it');
22
+
23
+ /**
24
+ * Legacy interface function that maintains compatibility with existing code
25
+ *
26
+ * @param {Object} md - The markdown-it instance to enhance
27
+ * @param {Array} templates - Array of template-tag handler objects for custom syntax
28
+ *
29
+ * This function simply delegates to the new modular system while maintaining
30
+ * the same interface that consuming code expects.
31
+ */
32
+ module.exports = function (md, templates = []) {
33
+ // Apply all markdown-it enhancements using the new modular system
34
+ applyMarkdownItExtensions(md, templates);
35
+ };
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Configures and exports a fully set up markdown-it instance.
3
+ * This module integrates custom extensions, plugins, and related constants.
4
+ * It centralizes markdown parsing setup to reduce complexity in index.js.
5
+ */
6
+
7
+ const MarkdownIt = require('markdown-it');
8
+ const containers = require('markdown-it-container');
9
+ const path = require('path');
10
+ const fs = require('fs-extra');
11
+ const findPkgDir = require('find-pkg-dir');
12
+
13
+ const { configurePlugins } = require('../../markdown-it/plugins');
14
+ const { createTemplateTagParser, createSpecParser } = require('../../parsers');
15
+ const { whitespace, templateTags } = require('../../utils/regex-patterns.js');
16
+
17
+ // Constants used in markdown parsing
18
+ const noticeTypes = {
19
+ note: 1,
20
+ issue: 1,
21
+ example: 1,
22
+ warning: 1,
23
+ todo: 1
24
+ };
25
+ // Domain-specific regex patterns for markdown parsing (now centralized)
26
+ const specNameRegex = templateTags.specName;
27
+ const templateTagRegex = templateTags.terminology;
28
+
29
+ // Load spec corpus
30
+ const modulePath = findPkgDir(__dirname);
31
+ const specCorpus = fs.readJsonSync(path.join(modulePath, 'assets/compiled/refs.json'));
32
+
33
+ // Global variables (shared across renders)
34
+ let definitions = global.definitions;
35
+ let references = global.references;
36
+ let specGroups = global.specGroups;
37
+ let noticeTitles = global.noticeTitles;
38
+
39
+ /**
40
+ * Creates and configures a markdown-it instance with extensions and plugins.
41
+ * @param {Object} config - Configuration object (e.g., for anchor symbol).
42
+ * @param {Function} setToc - Function to set the table of contents HTML.
43
+ * @returns {Object} The configured markdown-it instance.
44
+ */
45
+ function createMarkdownParser(config, setToc) {
46
+ // Create parser functions with bound dependencies - cleaner than classes
47
+ const templateTagParser = createTemplateTagParser(config, global);
48
+ const specParser = createSpecParser(specCorpus, global);
49
+
50
+ let md = MarkdownIt({
51
+ html: true,
52
+ linkify: true,
53
+ typographer: true
54
+ })
55
+ .use(require('./apply-markdown-it-extensions.js'), [
56
+ /*
57
+ The first extension focuses on template-tag constructs.
58
+ All complex logic is now delegated to pure functions.
59
+ */
60
+ {
61
+ filter: type => type.match(templateTagRegex),
62
+ parse: (token, type, primary) => templateTagParser(token, type, primary)
63
+ },
64
+ /*
65
+ The second extension handles specification references.
66
+ All complex logic is now delegated to pure functions.
67
+ */
68
+ {
69
+ filter: type => type.match(specNameRegex),
70
+ parse: (token, type, name) => specParser.parseSpecReference(token, type, name),
71
+ render: (token, type, name) => specParser.renderSpecReference(token, type, name)
72
+ }
73
+ ]);
74
+
75
+ md = configurePlugins(md, config, containers, noticeTypes, global.noticeTitles, setToc);
76
+
77
+ return md;
78
+ }
79
+
80
+ module.exports = {
81
+ createMarkdownParser,
82
+ noticeTypes,
83
+ spaceRegex: whitespace.oneOrMore,
84
+ specNameRegex,
85
+ templateTagRegex,
86
+ specCorpus,
87
+ definitions,
88
+ references,
89
+ specGroups,
90
+ noticeTitles,
91
+ // Export parsers for direct access if needed
92
+ createTemplateTagParser,
93
+ createSpecParser
94
+ };
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Markdown Parser Integration Tests - Functional Style
3
+ *
4
+ * These tests verify that the markdown parser integration works correctly
5
+ * and maintains backward compatibility while providing better testability.
6
+ */
7
+
8
+ const {
9
+ createMarkdownParser
10
+ } = require('./create-markdown-parser.js');
11
+
12
+ // Tests for integrating the functional parser system with markdown processing
13
+ describe('Markdown Parser Integration', () => {
14
+ let mockConfig;
15
+
16
+ beforeEach(() => {
17
+ mockConfig = {
18
+ specs: [{
19
+ external_specs: [{
20
+ external_spec: 'test-spec',
21
+ gh_page: 'https://example.com/spec'
22
+ }]
23
+ }]
24
+ };
25
+ });
26
+
27
+ // Test: Can the system create a markdown parser using the functional approach?
28
+ test('should create parser with functional system', () => {
29
+ const mockSetToc = jest.fn();
30
+ const parser = createMarkdownParser(mockConfig, mockSetToc);
31
+
32
+ expect(parser).toBeDefined();
33
+ expect(parser.render).toBeDefined();
34
+ });
35
+
36
+ // Test: Does the refactored system maintain compatibility with existing markdown processing?
37
+ test('should maintain backward compatibility', () => {
38
+ const mockSetToc = jest.fn();
39
+ const parser = createMarkdownParser(mockConfig, mockSetToc);
40
+
41
+ // Test basic markdown rendering still works
42
+ const basicMarkdown = '# Test Heading\n\nSome content.';
43
+ const result = parser.render(basicMarkdown);
44
+
45
+ expect(result).toContain('<h1');
46
+ expect(result).toContain('Test Heading');
47
+ expect(result).toContain('<p>Some content.</p>');
48
+ });
49
+ });
@@ -100,7 +100,10 @@ function fixDefinitionListStructure(html) {
100
100
  });
101
101
 
102
102
  // Find any transcluded term dt elements anywhere in the document
103
- const transcludedTerms = document.querySelectorAll('dt.transcluded-xref-term');
103
+ const transcludedTerms = document.querySelectorAll('dt.term-external, dt.term-local');
104
+
105
+ // Also find any tref spans that are in paragraphs (standalone trefs)
106
+ const standaloneTrefSpans = document.querySelectorAll('p span.term-external');
104
107
 
105
108
  let mainDl = null;
106
109
 
@@ -109,7 +112,7 @@ function fixDefinitionListStructure(html) {
109
112
  mainDl = dlElements[0]; // Use the first one
110
113
  }
111
114
  // If we have transcluded terms but no main dl, we need to create one
112
- else if (transcludedTerms.length > 0) {
115
+ else if (transcludedTerms.length > 0 || standaloneTrefSpans.length > 0) {
113
116
  // Create a new dl element with the right class
114
117
  mainDl = document.createElement('dl');
115
118
  mainDl.className = 'terms-and-definitions-list';
@@ -125,10 +128,18 @@ function fixDefinitionListStructure(html) {
125
128
  marker.parentNode.appendChild(mainDl);
126
129
  }
127
130
  } else {
128
- // Fallback to the original approach if marker isn't found
129
- const firstTerm = transcludedTerms[0];
130
- const insertPoint = firstTerm.parentNode;
131
- insertPoint.parentNode.insertBefore(mainDl, insertPoint);
131
+ // Fallback - insert before the first term we can find (dt or standalone span)
132
+ let firstTerm = transcludedTerms[0];
133
+ if (!firstTerm && standaloneTrefSpans.length > 0) {
134
+ firstTerm = standaloneTrefSpans[0].closest('p');
135
+ }
136
+ if (firstTerm) {
137
+ const insertPoint = firstTerm.parentNode;
138
+ insertPoint.parentNode.insertBefore(mainDl, insertPoint);
139
+ } else {
140
+ // Last resort - append to body
141
+ document.body.appendChild(mainDl);
142
+ }
132
143
  }
133
144
  }
134
145
 
@@ -160,6 +171,30 @@ function fixDefinitionListStructure(html) {
160
171
  return group;
161
172
  }
162
173
 
174
+ // First, process any standalone tref spans in paragraphs and convert them to dt elements
175
+ standaloneTrefSpans.forEach(trefSpan => {
176
+ const paragraph = trefSpan.closest('p');
177
+ if (paragraph) {
178
+ // Create a new dt element for this tref
179
+ const newDt = document.createElement('dt');
180
+ newDt.className = 'term-external';
181
+
182
+ // Move the tref span into the dt
183
+ newDt.appendChild(trefSpan.cloneNode(true));
184
+
185
+ // Create an empty dd element to satisfy definition list structure
186
+ const newDd = document.createElement('dd');
187
+ newDd.innerHTML = ''; // Truly empty, no hacks
188
+
189
+ // Add both to the main dl
190
+ mainDl.appendChild(newDt);
191
+ mainDl.appendChild(newDd);
192
+
193
+ // Remove the paragraph
194
+ paragraph.parentNode.removeChild(paragraph);
195
+ }
196
+ });
197
+
163
198
  // Process all transcluded terms and move them with their dd elements
164
199
  transcludedTerms.forEach(dt => {
165
200
  // Check if this dt is not already inside our main dl
@@ -269,10 +304,34 @@ function fixDefinitionListStructure(html) {
269
304
  currentNode.parentNode.removeChild(currentNode);
270
305
  }
271
306
  }
272
- else if (currentNode.tagName === 'P' &&
273
- (!currentNode.textContent || currentNode.textContent.trim() === '')) {
274
- // Remove empty paragraphs - these break the list structure
275
- currentNode.parentNode.removeChild(currentNode);
307
+ else if (currentNode.tagName === 'P') {
308
+ // Check if this paragraph contains a standalone tref term
309
+ const trefSpan = currentNode.querySelector('span.term-external');
310
+ if (trefSpan) {
311
+ // Create a new dt element for this tref
312
+ const newDt = document.createElement('dt');
313
+ newDt.className = 'term-external';
314
+
315
+ // Move the tref span into the dt
316
+ newDt.appendChild(trefSpan.cloneNode(true));
317
+
318
+ // Create an empty dd element to satisfy definition list structure
319
+ const newDd = document.createElement('dd');
320
+ newDd.innerHTML = ''; // Truly empty, no hacks
321
+
322
+ // Add both to the main dl
323
+ mainDl.appendChild(newDt);
324
+ mainDl.appendChild(newDd);
325
+
326
+ // Remove the paragraph
327
+ currentNode.parentNode.removeChild(currentNode);
328
+ }
329
+ else if ((!currentNode.textContent || currentNode.textContent.trim() === '') &&
330
+ currentNode.children.length === 0) {
331
+ // Remove truly empty paragraphs (no text and no child elements) - these break the list structure
332
+ // This prevents removal of paragraphs containing only images or other elements
333
+ currentNode.parentNode.removeChild(currentNode);
334
+ }
276
335
  }
277
336
  }
278
337
 
@@ -8,6 +8,8 @@
8
8
  * 3. Post-processing: Restore escaped sequences as literals
9
9
  */
10
10
 
11
+ const { escaping } = require('../../utils/regex-patterns.js');
12
+
11
13
  const ESCAPED_PLACEHOLDER = '__SPEC_UP_ESCAPED_TAG__';
12
14
 
13
15
  /**
@@ -40,7 +42,7 @@ function postProcessEscapes(text) {
40
42
  }
41
43
 
42
44
  // Restore placeholders to literal [[
43
- return text.replace(new RegExp(ESCAPED_PLACEHOLDER, 'g'), '[[');
45
+ return text.replace(escaping.placeholderRegex, '[[');
44
46
  }
45
47
 
46
48
  /**