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
package/src/configure.js CHANGED
@@ -1,113 +1,12 @@
1
1
  /**
2
- * This script configures the Spec-Up-T Starterpack by prompting the user for input
3
- * and updating the specs.json file accordingly. It performs the following tasks:
4
- *
5
- * 1. Resolves the path to the specs.json file in the root directory.
6
- * 2. Checks if the specs.json file exists, and exits with an error if it does not.
7
- * 3. Creates a readline interface to prompt the user for input.
8
- * 4. Defines a set of questions to gather information from the user.
9
- *
10
- * @module configure
2
+ * Legacy entry point preserved for backwards compatibility with existing installations.
3
+ *
4
+ * The original interactive implementation now lives in
5
+ * `src/pipeline/configuration/configure-starterpack.js`. Requiring that module keeps the
6
+ * behaviour identical (including auto-run) while new code can import the functional helper
7
+ * directly from the pipeline path.
11
8
  */
12
9
 
13
- const fs = require('fs');
14
- const path = require('path');
15
- const readline = require('readline');
10
+ const configurator = require('./pipeline/configuration/configure-starterpack');
16
11
 
17
- // Resolve the path to specs.json in the root directory
18
- const JSON_FILE_PATH = path.resolve(process.cwd(), 'specs.json');
19
-
20
- // Key for accessing specs in the JSON file
21
- const SPECS_KEY = 'specs';
22
-
23
- // Check if the JSON file exists
24
- if (!fs.existsSync(JSON_FILE_PATH)) {
25
- console.error(`Error: ${JSON_FILE_PATH} does not exist.`);
26
- process.exit(1);
27
- }
28
-
29
- // Create the readline interface
30
- const rl = readline.createInterface({
31
- input: process.stdin,
32
- output: process.stdout,
33
- });
34
-
35
- // Introduction text
36
- console.log(`
37
- Welcome to the Spec-Up-T Starterpack configuration tool!
38
-
39
- You will be asked a series of questions to customize your project.
40
- Here’s what each field means:
41
- - "Title": The name of your project.
42
- - "Description": A brief explanation of your project.
43
- - "Author": The name of the person or organization creating the project.
44
- - "Account": The GitHub account or organization where the repository will be hosted.
45
- - "Repo": The name of the GitHub repository.
46
-
47
- Note: "Author" refers to the creator of the project, while "Account" refers to the GitHub account or organization.
48
-
49
- Press Enter to accept the default value shown in parentheses.
50
- `);
51
-
52
- // Questions for user input
53
- const questions = [
54
- { field: 'title', prompt: 'Enter title: ', default: 'Spec-Up-T Starterpack' },
55
- { field: 'description', prompt: 'Enter description: ', default: 'Create technical specifications in markdown. Based on the original Spec-Up, extended with Terminology tooling' },
56
- { field: 'author', prompt: 'Enter author: ', default: 'Trust over IP Foundation' },
57
- { field: 'account', prompt: 'Enter account: ', default: 'trustoverip' },
58
- { field: 'repo', prompt: 'Enter repo: ', default: 'spec-up-t-starter-pack' },
59
- ];
60
-
61
- const userResponses = {};
62
-
63
- // Function to prompt the user for inputs
64
- function collectUserInputs(index = 0) {
65
- if (index === questions.length) {
66
- rl.close();
67
- applySpecFieldsToJSON();
68
- return;
69
- }
70
-
71
- const { prompt, default: defaultValue } = questions[index];
72
- rl.question(`${prompt} (${defaultValue}): `, (answer) => {
73
- userResponses[questions[index].field] = answer || defaultValue;
74
- collectUserInputs(index + 1);
75
- });
76
- }
77
-
78
- // Function to update JSON with user-provided spec fields
79
- function applySpecFieldsToJSON() {
80
- try {
81
- const data = JSON.parse(fs.readFileSync(JSON_FILE_PATH, 'utf8'));
82
-
83
- if (!data[SPECS_KEY] || !Array.isArray(data[SPECS_KEY]) || !data[SPECS_KEY][0]) {
84
- console.error(`Error: Invalid JSON structure. "${SPECS_KEY}[0]" is missing.`);
85
- process.exit(1);
86
- }
87
-
88
- // Ensure the "source" key exists in the JSON object
89
- if (!data[SPECS_KEY][0].source) {
90
- data[SPECS_KEY][0].source = {};
91
- }
92
-
93
- // Iterate over user responses and assign fields accordingly
94
- Object.entries(userResponses).forEach(([field, value]) => {
95
- if (['account', 'repo'].includes(field)) {
96
- // Add these fields to the "source" key
97
- data[SPECS_KEY][0].source[field] = value;
98
- } else {
99
- // Add all other fields to the root of the JSON object
100
- data[SPECS_KEY][0][field] = value;
101
- }
102
- });
103
-
104
- fs.writeFileSync(JSON_FILE_PATH, JSON.stringify(data, null, 2), 'utf8');
105
- console.log(`Successfully updated ${JSON_FILE_PATH}.`);
106
- } catch (error) {
107
- console.error(`Error: Could not update ${JSON_FILE_PATH}.`, error.message);
108
- process.exit(1);
109
- }
110
- }
111
-
112
- // Start user input collection
113
- collectUserInputs();
12
+ module.exports = configurator;
@@ -2,6 +2,7 @@ const fs = require('fs-extra');
2
2
  const path = require('path');
3
3
  const { JSDOM } = require('jsdom');
4
4
  const { Document, Packer, Paragraph, TextRun, HeadingLevel, TableOfContents, Table, TableRow, TableCell, WidthType, AlignmentType } = require('docx');
5
+ const Logger = require('./utils/logger');
5
6
 
6
7
  /**
7
8
  * Creates DOCX metadata from config
@@ -249,7 +250,7 @@ function createTitlePage(config) {
249
250
 
250
251
  (async () => {
251
252
  try {
252
- console.log('Starting DOCX generation...');
253
+ Logger.info('Starting DOCX generation...');
253
254
 
254
255
  // Read and parse the specs.json file
255
256
  const config = fs.readJsonSync('specs.json');
@@ -261,8 +262,8 @@ function createTitlePage(config) {
261
262
 
262
263
  // Check if HTML file exists
263
264
  if (!fs.existsSync(filePath)) {
264
- console.error(`❌ HTML file not found at ${filePath}`);
265
- console.log('Please run "npm run render" first to generate the HTML file.');
265
+ Logger.error(`HTML file not found at ${filePath}`);
266
+ Logger.info('Please run "npm run render" first to generate the HTML file.');
266
267
  return;
267
268
  }
268
269
 
@@ -324,9 +325,9 @@ function createTitlePage(config) {
324
325
  // Write the DOCX file
325
326
  fs.writeFileSync(docxPath, buffer);
326
327
 
327
- console.log('DOCX generated successfully! Find the DOCX file in the docs directory.');
328
+ Logger.success('DOCX generated successfully! Find the DOCX file in the docs directory.');
328
329
  } catch (error) {
329
- console.error('Error generating DOCX:', error);
330
- process.exit(1);
330
+ Logger.error('Error generating DOCX:', error);
331
+ process.exit(1);
331
332
  }
332
333
  })();
package/src/create-pdf.js CHANGED
@@ -2,6 +2,7 @@ const fs = require('fs-extra');
2
2
  const puppeteer = require('puppeteer');
3
3
  const path = require('path');
4
4
  const pdfLib = require('pdf-lib');
5
+ const Logger = require('./utils/logger');
5
6
 
6
7
  // ISO compliance configuration
7
8
  const ISO_CONFIG = {
@@ -197,7 +198,7 @@ async function createTOCIfNeeded(page, logo, logoLink, title, description) {
197
198
  // Insert the TOC after the title section
198
199
  document.body.insertBefore(tocContainer, titleWrapper.nextSibling);
199
200
 
200
- console.log('Generated a Table of Contents with ' + headings.length + ' entries.');
201
+ console.log('Generated a Table of Contents with ' + headings.length + ' entries.');
201
202
  }
202
203
  }
203
204
  }, logo, logoLink, title, description);
@@ -253,7 +254,7 @@ async function createTOCIfNeeded(page, logo, logoLink, title, description) {
253
254
 
254
255
  // If TOC doesn't exist, we'll need to create one
255
256
  if (!document.getElementById('toc')) {
256
- console.log('No TOC found in the document. Will create one.');
257
+ console.log('No TOC found in the document. Will create one.');
257
258
  }
258
259
  });
259
260
 
@@ -377,7 +378,7 @@ async function createTOCIfNeeded(page, logo, logoLink, title, description) {
377
378
  });
378
379
 
379
380
  // Special handling for ALL transcluded terms with blue background - no class restrictions
380
- document.querySelectorAll('.transcluded-xref-term').forEach(el => {
381
+ document.querySelectorAll('.term-external, .term-local').forEach(el => {
381
382
  // Use the most aggressive approach possible to override the blue background
382
383
  el.setAttribute('style', el.getAttribute('style') + '; background: transparent !important; background-color: transparent !important; background-image: none !important;');
383
384
 
@@ -390,10 +391,10 @@ async function createTOCIfNeeded(page, logo, logoLink, title, description) {
390
391
  // Remove any inline styles that might be setting backgrounds
391
392
  document.querySelectorAll('style').forEach(styleTag => {
392
393
  let cssText = styleTag.textContent;
393
- // If the style tag contains transcluded-xref-term styles, modify them
394
- if (cssText.includes('transcluded-xref-term') && cssText.includes('background')) {
395
- cssText = cssText.replace(/dt\.transcluded-xref-term[^}]+}/g,
396
- 'dt.transcluded-xref-term, dd.transcluded-xref-term { background: transparent !important; background-color: transparent !important; background-image: none !important; }');
394
+ // If the style tag contains term-external styles, modify them
395
+ if (cssText.includes('term-external') && cssText.includes('background')) {
396
+ cssText = cssText.replace(/dt\.term-external[^}]+}/g,
397
+ 'dt.term-external, dd.term-external, dt.term-local, dd.term-local { background: transparent !important; background-color: transparent !important; background-image: none !important; }');
397
398
  styleTag.textContent = cssText;
398
399
  }
399
400
  }); // Format Table of Contents for book-like layout
@@ -507,7 +508,7 @@ async function createTOCIfNeeded(page, logo, logoLink, title, description) {
507
508
  }
508
509
  });
509
510
 
510
- console.log('Generating PDF with proper TOC page numbers...');
511
+ Logger.process('Generating PDF with proper TOC page numbers...');
511
512
 
512
513
  // First, generate a draft PDF to calculate the page positions of each heading
513
514
  const draftPdfBuffer = await page.pdf({
@@ -642,7 +643,7 @@ async function createTOCIfNeeded(page, logo, logoLink, title, description) {
642
643
 
643
644
  await browser.close();
644
645
 
645
- console.log('PDF generated by Puppeteer. Processing with pdf-lib for ISO compliance...');
646
+ Logger.success('PDF generated by Puppeteer. Processing with pdf-lib for ISO compliance...');
646
647
 
647
648
  // Optimize PDF with pdf-lib for ISO compliance
648
649
  try {
@@ -658,7 +659,7 @@ async function createTOCIfNeeded(page, logo, logoLink, title, description) {
658
659
  pdfDoc.setCreationDate(metadata.creationDate);
659
660
  pdfDoc.setModificationDate(metadata.modificationDate);
660
661
 
661
- console.log('ISO metadata applied successfully.');
662
+ Logger.success('ISO metadata applied successfully.');
662
663
 
663
664
  // Save with conservative settings to ensure compatibility
664
665
  const optimizedPdfBytes = await pdfDoc.save({
@@ -667,15 +668,15 @@ async function createTOCIfNeeded(page, logo, logoLink, title, description) {
667
668
  });
668
669
 
669
670
  fs.writeFileSync('docs/index.pdf', optimizedPdfBytes);
670
- console.log('PDF saved with ISO compliance features.');
671
+ Logger.success('PDF saved with ISO compliance features.');
671
672
  } catch (pdfError) {
672
- console.warn('⚠️ Warning: Could not apply ISO metadata, saving original PDF:', pdfError.message);
673
+ Logger.warn('Could not apply ISO metadata, saving original PDF: %s', pdfError.message);
673
674
  // Fallback: save the original PDF if post-processing fails
674
675
  fs.writeFileSync('docs/index.pdf', pdfBuffer);
675
676
  }
676
677
 
677
- console.log('PDF generated successfully! Find the PDF in the docs directory.');
678
+ Logger.success('PDF generated successfully! Find the PDF in the docs directory.');
678
679
  } catch (error) {
679
- console.error('Error generating PDF:', error);
680
+ Logger.error('Error generating PDF: %o', error);
680
681
  }
681
682
  })();
@@ -0,0 +1,46 @@
1
+ /**
2
+ * @file freeze-spec-data.js
3
+ * @description Reads the output path from specs.json, finds the highest versioned directory
4
+ * in the destination path, and copies index.html to a new directory with an incremented version.
5
+ */
6
+
7
+ const fs = require('fs-extra');
8
+ const path = require('path');
9
+ const Logger = require('./utils/logger');
10
+ const { versions } = require('./utils/regex-patterns');
11
+
12
+ const config = fs.readJsonSync('specs.json');
13
+ const outputPath = config.specs[0].output_path;
14
+
15
+ const sourceFile = path.join(outputPath, 'index.html');
16
+ const destDir = path.join(outputPath, 'versions');
17
+
18
+ if (!fs.existsSync(destDir)) {
19
+ fs.mkdirSync(destDir, { recursive: true });
20
+ }
21
+
22
+ const dirs = fs.readdirSync(destDir).filter(file => fs.statSync(path.join(destDir, file)).isDirectory());
23
+ let highestVersion = 0;
24
+ const versionPattern = versions.pattern;
25
+
26
+ dirs.forEach(dir => {
27
+ const match = dir.match(versionPattern);
28
+ if (match) {
29
+ const version = parseInt(match[1], 10);
30
+ if (version > highestVersion) {
31
+ highestVersion = version;
32
+ }
33
+ }
34
+ });
35
+
36
+ const newVersion = highestVersion + 1;
37
+ const newVersionDir = path.join(destDir, `v${newVersion}`);
38
+
39
+ if (!fs.existsSync(newVersionDir)) {
40
+ fs.mkdirSync(newVersionDir, { recursive: true });
41
+ }
42
+
43
+ const destFile = path.join(newVersionDir, 'index.html');
44
+ fs.copyFileSync(sourceFile, destFile);
45
+
46
+ Logger.success(`Created a freezed specification version in ${destFile}`);
@@ -0,0 +1,76 @@
1
+ /**
2
+ * @fileoverview Test file for git-info utility functions
3
+ * This test verifies that the GitHub repository information
4
+ * is correctly extracted and formatted for the meta tag.
5
+ */
6
+
7
+ const { getCurrentBranch, getGithubRepoInfo } = require('./utils/git-info');
8
+
9
+ // Tests for extracting and formatting Git repository information
10
+ describe('git-info utility', () => {
11
+ // Tests for getting the current Git branch name
12
+ describe('getCurrentBranch', () => {
13
+ // Test: Does the function return a valid branch name as a string?
14
+ test('should return a string branch name', () => {
15
+ const branch = getCurrentBranch();
16
+ expect(typeof branch).toBe('string');
17
+ expect(branch.length).toBeGreaterThan(0);
18
+ });
19
+
20
+ // Test: Is the branch name properly formatted (no whitespace)?
21
+ test('should not contain newlines or spaces', () => {
22
+ const branch = getCurrentBranch();
23
+ expect(branch).not.toMatch(/\n|\r|\s/);
24
+ });
25
+ });
26
+
27
+ // Tests for formatting GitHub repository information for meta tags
28
+ describe('getGithubRepoInfo', () => {
29
+ // Test: Can the system format complete repository information correctly?
30
+ test('should format github repo info correctly with valid spec', () => {
31
+ const spec = {
32
+ source: {
33
+ account: 'testuser',
34
+ repo: 'testrepo'
35
+ }
36
+ };
37
+
38
+ const repoInfo = getGithubRepoInfo(spec);
39
+ expect(repoInfo).toMatch(/^testuser,testrepo,.+$/);
40
+
41
+ const parts = repoInfo.split(',');
42
+ expect(parts).toHaveLength(3);
43
+ expect(parts[0]).toBe('testuser');
44
+ expect(parts[1]).toBe('testrepo');
45
+ expect(parts[2].length).toBeGreaterThan(0);
46
+ });
47
+
48
+ // Test: Does the system gracefully handle missing repository information?
49
+ test('should handle missing source object', () => {
50
+ const spec = {};
51
+ const repoInfo = getGithubRepoInfo(spec);
52
+ expect(repoInfo).toMatch(/^unknown,unknown,.+$/);
53
+ });
54
+
55
+ // Test: Can the system handle incomplete repository information?
56
+ test('should handle partial source information', () => {
57
+ const spec = {
58
+ source: {
59
+ account: 'onlyuser'
60
+ // missing repo
61
+ }
62
+ };
63
+
64
+ const repoInfo = getGithubRepoInfo(spec);
65
+ expect(repoInfo).toMatch(/^onlyuser,unknown,.+$/);
66
+ });
67
+
68
+ // Test: Does the system follow best practices for meta tag naming?
69
+ test('should use spec-up-t namespace prefix recommendation', () => {
70
+ // This test verifies our implementation follows the best practice
71
+ // of using namespaced property names as recommended in the user request
72
+ const metaTagProperty = 'spec-up-t:github-repo-info';
73
+ expect(metaTagProperty).toMatch(/^spec-up-t:/);
74
+ });
75
+ });
76
+ });
@@ -12,6 +12,8 @@ const fs = require('fs');
12
12
  const path = require('path');
13
13
  const { spawnSync } = require('child_process');
14
14
  const fileOpener = require('../utils/file-opener');
15
+ const Logger = require('../utils/logger');
16
+ const { utils } = require('../utils/regex-patterns');
15
17
 
16
18
  /**
17
19
  * Checks if a path is gitignored
@@ -30,7 +32,7 @@ function isPathGitIgnored(projectRoot, targetPath) {
30
32
  });
31
33
  return result.status === 0; // Path is ignored (command exited with status 0)
32
34
  } catch (error) {
33
- console.log(`Error checking if path is gitignored: ${error.message}`);
35
+ Logger.info(`Error checking if path is gitignored: ${error.message}`);
34
36
  return false; // Path is not ignored (command exited with non-zero status)
35
37
  }
36
38
  }
@@ -208,7 +210,7 @@ function findOutputDirIgnorePatterns(lines, normalizedPath, dirName) {
208
210
  // Check for wildcards covering all directories
209
211
  trimmedLine === '*/' ||
210
212
  // Check for wildcards that might match our path using regex
211
- (trimmedLine.includes('*') && new RegExp('^' + trimmedLine.replace(/\*/g, '.*').replace(/\//g, '\\/') + '$').test(normalizedPath))
213
+ (trimmedLine.includes('*') && utils.createGitignoreRegex(trimmedLine).test(normalizedPath))
212
214
  ) {
213
215
  dirIgnorePatterns.push(trimmedLine);
214
216
  }
@@ -398,7 +400,7 @@ async function checkDestinationGitIgnore(projectRoot) {
398
400
 
399
401
  return results;
400
402
  } catch (error) {
401
- console.error('Error checking final destination directory gitignore status:', error);
403
+ Logger.error('Error checking final destination directory gitignore status:', error);
402
404
  return [{
403
405
  name: 'Final destination directory gitignore check',
404
406
  success: false,
@@ -2,6 +2,7 @@ const fs = require('fs');
2
2
  const path = require('path');
3
3
  const https = require('https');
4
4
  const url = require('url');
5
+ const Logger = require('../utils/logger');
5
6
 
6
7
  /**
7
8
  * Check if a given URL has correct GitHub Pages structure
@@ -17,7 +18,7 @@ function isValidGitHubPagesUrl(urlStr) {
17
18
  parsedUrl.hostname.endsWith('.github.io')
18
19
  );
19
20
  } catch (error) {
20
- console.error(`❌ Error validating GitHub Pages URL: ${error.message}`);
21
+ Logger.error(`Error validating GitHub Pages URL: ${error.message}`);
21
22
  return false;
22
23
  }
23
24
  }
@@ -35,7 +36,7 @@ function isValidGitHubRepoUrl(urlStr) {
35
36
  parsedUrl.pathname.split('/').filter(Boolean).length >= 2
36
37
  );
37
38
  } catch (error) {
38
- console.error(`❌ Error validating GitHub repo URL: ${error.message}`);
39
+ Logger.error(`Error validating GitHub repo URL: ${error.message}`);
39
40
  return false;
40
41
  }
41
42
  }
@@ -71,7 +72,7 @@ function urlExists(urlStr) {
71
72
 
72
73
  req.end();
73
74
  } catch (error) {
74
- console.error(`❌ URL Format Error: Invalid URL format for ${urlStr} - ${error.message}`);
75
+ Logger.error(`URL Format Error: Invalid URL format for ${urlStr} - ${error.message}`);
75
76
  resolve(false);
76
77
  }
77
78
  });
@@ -272,7 +273,7 @@ async function checkExternalSpecs(projectRoot) {
272
273
 
273
274
  return allResults;
274
275
  } catch (error) {
275
- console.error('Error checking external specs:', error);
276
+ Logger.error('Error checking external specs:', error);
276
277
  return [{
277
278
  name: 'External specs check',
278
279
  success: false,
@@ -29,6 +29,7 @@
29
29
 
30
30
  const fs = require('fs');
31
31
  const path = require('path');
32
+ const Logger = require('../utils/logger');
32
33
 
33
34
  /**
34
35
  * Field descriptions for specs.json keys
@@ -372,7 +373,7 @@ async function checkSpecsJsonConfiguration(projectRoot) {
372
373
  return [...results, ...summaryResults];
373
374
 
374
375
  } catch (error) {
375
- console.error('Error checking specs.json configuration:', error);
376
+ Logger.error('Error checking specs.json configuration:', error);
376
377
  return [{
377
378
  name: 'specs.json configuration check',
378
379
  success: false,
@@ -1,5 +1,7 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
+ const Logger = require('../utils/logger');
4
+ const { externalReferences } = require('../utils/regex-patterns');
3
5
 
4
6
  /**
5
7
  * Extracts the spec name from a tref tag at the beginning of a markdown file
@@ -13,13 +15,13 @@ function extractSpecNameFromTref(firstLine) {
13
15
 
14
16
  try {
15
17
  // Extract content between [[tref: and the next comma
16
- const match = firstLine.match(/\[\[tref:([^,]+)/);
18
+ const match = firstLine.match(externalReferences.trefSpecExtractor);
17
19
  if (match && match[1]) {
18
20
  // Trim whitespace
19
21
  return match[1].trim();
20
22
  }
21
23
  } catch (error) {
22
- console.error('Error extracting spec name from tref:', error);
24
+ Logger.error('Error extracting spec name from tref:', error);
23
25
  }
24
26
 
25
27
  return null;
@@ -254,7 +256,7 @@ async function checkTermReferences(projectRoot) {
254
256
 
255
257
  return results;
256
258
  } catch (error) {
257
- console.error('Error checking term references:', error);
259
+ Logger.error('Error checking term references:', error);
258
260
  return [{
259
261
  name: 'Term references check',
260
262
  success: false,
@@ -1,5 +1,6 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
+ const Logger = require('../utils/logger');
3
4
 
4
5
  /**
5
6
  * Check if the terms-and-definitions-intro.md file exists in the spec directory
@@ -67,7 +68,7 @@ async function checkTermsIntroFile(projectRoot) {
67
68
 
68
69
  return results;
69
70
  } catch (error) {
70
- console.error('Error checking terms-and-definitions-intro.md file:', error);
71
+ Logger.error('Error checking terms-and-definitions-intro.md file:', error);
71
72
  return [{
72
73
  name: 'Terms intro file check',
73
74
  success: false,
@@ -1,6 +1,7 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
3
  const { shouldProcessFile } = require('../utils/file-filter');
4
+ const Logger = require('../utils/logger');
4
5
 
5
6
  /**
6
7
  * Extracts the spec name and term from a tref tag at the beginning of a markdown file
@@ -33,7 +34,7 @@ function extractTrefInfo(firstLine) {
33
34
 
34
35
  return { repo, term };
35
36
  } catch (error) {
36
- console.error('Error extracting tref info:', error);
37
+ Logger.error('Error extracting tref info:', error);
37
38
  return null;
38
39
  }
39
40
  }
@@ -55,7 +56,7 @@ function findAllCacheFiles(cacheDir) {
55
56
 
56
57
  return files;
57
58
  } catch (error) {
58
- console.error(`Error finding cache files:`, error);
59
+ Logger.error(`Error finding cache files:`, error);
59
60
  return [];
60
61
  }
61
62
  }
@@ -76,7 +77,7 @@ function termExistsInRepo(filePath, term) {
76
77
 
77
78
  // Check if the file has a terms array
78
79
  if (!cacheData?.terms?.length) {
79
- console.log(`Warning: Cache file ${filePath} has no terms array`);
80
+ Logger.warn(`Cache file ${filePath} has no terms array`);
80
81
  return false;
81
82
  }
82
83
 
@@ -102,7 +103,7 @@ function termExistsInRepo(filePath, term) {
102
103
 
103
104
  return false;
104
105
  } catch (error) {
105
- console.error(`Error checking if term exists in file ${filePath}:`, error);
106
+ Logger.error(`Error checking if term exists in file ${filePath}:`, error);
106
107
  return false;
107
108
  }
108
109
  }
@@ -137,7 +138,7 @@ function findRepoForCacheFile(filePath, externalSpecs) {
137
138
 
138
139
  return null;
139
140
  } catch (error) {
140
- console.error(`Error finding repository for cache file ${filePath}:`, error);
141
+ Logger.error(`Error finding repository for cache file ${filePath}:`, error);
141
142
  return null; // Could not determine repository due to error reading or parsing cache file
142
143
  }
143
144
  }
@@ -218,7 +219,7 @@ function findCacheFileForRepo(cacheDir, specConfig) {
218
219
 
219
220
  return null;
220
221
  } catch (error) {
221
- console.error(`Error finding cache file for repo:`, error);
222
+ Logger.error(`Error finding cache file for repo:`, error);
222
223
  return null;
223
224
  }
224
225
  }
@@ -534,7 +535,7 @@ async function checkTrefTerms(projectRoot) {
534
535
 
535
536
  return results;
536
537
  } catch (error) {
537
- console.error('Error checking tref terms:', error);
538
+ Logger.error('Error checking tref terms:', error);
538
539
  return [{
539
540
  name: 'Term reference validation check',
540
541
  success: false,
@@ -44,6 +44,7 @@
44
44
 
45
45
  const fs = require('fs');
46
46
  const path = require('path');
47
+ const Logger = require('./utils/logger');
47
48
  const https = require('https');
48
49
  const fileOpener = require('./utils/file-opener');
49
50
 
@@ -112,7 +113,7 @@ async function getRepoInfo() {
112
113
  };
113
114
  }
114
115
  } catch (error) {
115
- console.error('Error reading boilerplate specs.json:', error);
116
+ Logger.error('Error reading boilerplate specs.json:', error);
116
117
  }
117
118
  }
118
119
 
@@ -176,10 +177,10 @@ async function getRepoInfo() {
176
177
  }
177
178
  }
178
179
  } else {
179
- console.log('specs.json not found');
180
+ Logger.info('specs.json not found');
180
181
  }
181
182
  } catch (error) {
182
- console.error('Error reading specs.json:', error);
183
+ Logger.error('Error reading specs.json:', error);
183
184
  }
184
185
 
185
186
  // Return default values if specs.json doesn't exist or doesn't contain the required information
@@ -221,19 +222,19 @@ function checkRepositoryExists(host, account, repo) {
221
222
  });
222
223
 
223
224
  request.on('error', (error) => {
224
- console.error('Error checking repository existence:', error.message);
225
+ Logger.error('Error checking repository existence:', error.message);
225
226
  resolve(false);
226
227
  });
227
228
 
228
229
  request.on('timeout', () => {
229
- console.error('Timeout checking repository existence');
230
+ Logger.error('Timeout checking repository existence');
230
231
  request.destroy();
231
232
  resolve(false);
232
233
  });
233
234
 
234
235
  request.end();
235
236
  } catch (error) {
236
- console.error('Error checking repository existence:', error.message);
237
+ Logger.error('Error checking repository existence:', error.message);
237
238
  resolve(false);
238
239
  }
239
240
  });
@@ -345,7 +346,7 @@ function getStatusDisplay(result) {
345
346
  * }
346
347
  */
347
348
  async function runHealthCheck() {
348
- console.log('Running health checks...');
349
+ Logger.info('Running health checks...');
349
350
 
350
351
  // Collection to store all check results
351
352
  const results = [];