spec-up-t 1.2.6 → 1.2.8

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.
@@ -0,0 +1,3 @@
1
+ - All code will have to pass SonarQube analysis
2
+ - Cognitive complexity should be kept ideally below 15
3
+ - Remove code if possible, instead of adding code
@@ -57,7 +57,8 @@ function collapseDefinitions() {
57
57
  "See more about",
58
58
  "See more on",
59
59
  "See more at",
60
- "More:"
60
+ "More:",
61
+ "Supporting definitions:"
61
62
  ];
62
63
  return definitionHidePrefixes.some(prefix => content.startsWith(prefix));
63
64
  }
package/index.js CHANGED
@@ -35,6 +35,7 @@ module.exports = async function (options = {}) {
35
35
  createVersionsIndex(config.specs[0].output_path);
36
36
 
37
37
  const { fixMarkdownFiles } = require('./src/fix-markdown-files.js');
38
+ const { processEscapedTags, restoreEscapedTags } = require('./src/escape-mechanism.js');
38
39
 
39
40
  let template = fs.readFileSync(path.join(modulePath, 'templates/template.html'), 'utf8');
40
41
  let assets = fs.readJsonSync(modulePath + '/config/asset-map.json');
@@ -528,7 +529,12 @@ module.exports = async function (options = {}) {
528
529
 
529
530
  let doc = docs.join("\n");
530
531
 
531
- // `doc` is markdown
532
+ // Handles backslash escape mechanism for substitution tags
533
+ // Phase 1: Pre-processing - Handle escaped tags
534
+ doc = processEscapedTags(doc);
535
+
536
+ // Handles backslash escape mechanism for substitution tags
537
+ // Phase 2: Tag Processing - Apply normal substitution logic
532
538
  doc = applyReplacers(doc);
533
539
 
534
540
  md[spec.katex ? "enable" : "disable"](katexRules);
@@ -542,6 +548,10 @@ module.exports = async function (options = {}) {
542
548
  // Sort definition terms case-insensitively before final rendering
543
549
  renderedHtml = sortDefinitionTermsInHtml(renderedHtml);
544
550
 
551
+ // Handles backslash escape mechanism for substitution tags
552
+ // Phase 3: Post-processing - Restore escaped sequences as literals
553
+ renderedHtml = restoreEscapedTags(renderedHtml);
554
+
545
555
  // Process external references to ensure they are inserted as raw HTML, not as JSON string
546
556
  const externalReferencesHtml = Array.isArray(externalReferences)
547
557
  ? externalReferences.join('')
package/jest.config.js ADDED
@@ -0,0 +1,20 @@
1
+ module.exports = {
2
+ collectCoverageFrom: [
3
+ 'src/**/*.{js,jsx}',
4
+ '!src/**/*.test.{js,jsx}',
5
+ '!src/**/__tests__/**',
6
+ '!src/**/__mocks__/**',
7
+ ],
8
+ coverageDirectory: 'coverage',
9
+ coverageReporters: ['text', 'lcov', 'html'],
10
+ coverageThreshold: {
11
+ global: {
12
+ branches: 80,
13
+ functions: 80,
14
+ lines: 80,
15
+ statements: 80,
16
+ },
17
+ },
18
+ testEnvironment: 'node',
19
+ verbose: true,
20
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spec-up-t",
3
- "version": "1.2.6",
3
+ "version": "1.2.8",
4
4
  "description": "Technical specification drafting tool that generates rich specification documents from markdown. Forked from https://github.com/decentralized-identity/spec-up by Daniel Buchner (https://github.com/csuwildcat)",
5
5
  "main": "./index",
6
6
  "repository": {
@@ -70,6 +70,7 @@
70
70
  "jest": "^29.7.0"
71
71
  },
72
72
  "scripts": {
73
- "test": "NODE_OPTIONS=--experimental-vm-modules jest"
73
+ "test": "jest",
74
+ "test:coverage": "jest --coverage"
74
75
  }
75
76
  }
@@ -270,13 +270,17 @@ function processExternalReferences(config, GITHUB_API_TOKEN, options) {
270
270
  *
271
271
  * @description
272
272
  * This function performs several key operations:
273
- * 1. Validates GitHub PAT availability and external repository configurations
273
+ * 1. Optionally uses GitHub PAT for better API performance and higher rate limits
274
274
  * 2. Checks validity of repository URLs
275
275
  * 3. Extracts xref/tref patterns from markdown content
276
276
  * 4. Extends references with repository metadata
277
277
  * 5. Processes references to fetch commit information
278
278
  * 6. Generates output files in both JS and JSON formats
279
279
  *
280
+ * Note: The function will run without a GitHub token but may encounter rate limits.
281
+ * For better performance, provide a GitHub Personal Access Token via environment
282
+ * variable or the options parameter.
283
+ *
280
284
  * @example
281
285
  * // Basic usage
282
286
  * collectExternalReferences();
@@ -289,17 +293,6 @@ function collectExternalReferences(options = {}) {
289
293
  const externalSpecsRepos = config.specs[0].external_specs;
290
294
  const GITHUB_API_TOKEN = options.pat || process.env.GITHUB_API_TOKEN;
291
295
 
292
- const explanationPAT =
293
- `❌ No GitHub Personal Access Token (PAT) was found.
294
-
295
- GitHub requires you to set up a PAT to retrieve external references.
296
-
297
- There is no point in continuing without a PAT, so we stop here.
298
-
299
- Find instructions on how to get a PAT at https://blockchainbird.github.io/spec-up-t-website/docs/getting-started/github-token
300
-
301
- `;
302
-
303
296
  const explanationNoExternalReferences =
304
297
  `❌ No external references were found in the specs.json file.
305
298
 
@@ -310,19 +303,13 @@ function collectExternalReferences(options = {}) {
310
303
  `;
311
304
 
312
305
  // First do some checks
313
-
314
- // Do not run the script if the GitHub API token is not set
306
+ // Show informational message if no token is available
315
307
  if (!GITHUB_API_TOKEN) {
316
- console.log(explanationPAT);
317
- const userInput = readlineSync.question('ℹ️ Press any key');
318
-
319
- // React to user pressing any key
320
- if (userInput.trim() !== '') {
321
- console.log('ℹ️ Stopping...');
322
- return;
323
- }
308
+ console.log('ℹ️ No GitHub Personal Access Token (PAT) found. Running without authentication (may hit rate limits).');
309
+ console.log('💡 For better performance, set up a PAT: https://blockchainbird.github.io/spec-up-t-website/docs/getting-started/github-token\n');
324
310
  }
325
- else if (externalSpecsRepos.length === 0) {
311
+
312
+ if (externalSpecsRepos.length === 0) {
326
313
  // Check if the URLs for the external specs repositories are valid, and prompt the user to abort if they are not.
327
314
  console.log(explanationNoExternalReferences);
328
315
  const userInput = readlineSync.question('Press any key');
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Escape Mechanism Module for Spec-Up Substitution Tags
3
+ *
4
+ * This module provides functions to handle backslash escape sequences for substitution tags,
5
+ * allowing users to display tag syntax literally in their documentation.
6
+ *
7
+ * The escape mechanism works in three phases:
8
+ * 1. Pre-processing: Convert escaped sequences to temporary placeholders
9
+ * 2. Tag processing: Normal substitution logic (handled elsewhere)
10
+ * 3. Post-processing: Restore escaped sequences as literals
11
+ *
12
+ * Supported escape pattern:
13
+ * - \[[tag: content]] → displays as literal [[tag: content]]
14
+ *
15
+ * @version 1.0.0
16
+ */
17
+
18
+ /**
19
+ * Handles backslash escape mechanism for substitution tags
20
+ *
21
+ * Use backslash escape sequences to allow literal [[ tags in markdown
22
+ *
23
+ * Phase 1: Pre-processing - Convert escaped sequences to temporary placeholders
24
+ *
25
+ * @param {string} doc - The markdown document to process
26
+ * @returns {string} - Document with escaped sequences converted to placeholders
27
+ */
28
+ function processEscapedTags(doc) {
29
+ // Replace \[[ with escape placeholder for literal display
30
+ // In markdown: \[[def: term]] should become [[def: term]] (literal tag syntax)
31
+ doc = doc.replace(/\\(\[\[)/g, '__SPEC_UP_ESCAPED_TAG__');
32
+
33
+ return doc;
34
+ }
35
+
36
+ /**
37
+ * Handles backslash escape mechanism for substitution tags
38
+ *
39
+ * Use backslash escape sequences to allow literal [[ tags in markdown
40
+ *
41
+ * Phase 3: Post-processing - Restore escaped sequences as literals
42
+ * Converts placeholders back to literal [[ characters
43
+ *
44
+ * @param {string} renderedHtml - The rendered HTML to process
45
+ * @returns {string} - HTML with placeholders restored to literal [[ tags
46
+ */
47
+ function restoreEscapedTags(renderedHtml) {
48
+ // Replace escaped tag placeholders with literal [[
49
+ renderedHtml = renderedHtml.replace(/__SPEC_UP_ESCAPED_TAG__/g, '[[');
50
+
51
+ return renderedHtml;
52
+ }
53
+
54
+ module.exports = {
55
+ processEscapedTags,
56
+ restoreEscapedTags
57
+ };
@@ -1,6 +1,17 @@
1
+ /**
2
+ * @file destination-gitignore-checker.js
3
+ * @description Checks if the final destination directory (from output_path in specs.json)
4
+ * is being ignored by Git. This is the directory where index.html is generated,
5
+ * NOT the temporary .cache directory (formerly called "output").
6
+ *
7
+ * Important: This file deals with concept #1 (output_path from specs.json),
8
+ * not concept #2 (the temporary .cache directory for build artifacts).
9
+ */
10
+
1
11
  const fs = require('fs');
2
12
  const path = require('path');
3
- const { execSync } = require('child_process');
13
+ const { spawnSync } = require('child_process');
14
+ const fileOpener = require('../utils/file-opener');
4
15
 
5
16
  /**
6
17
  * Checks if a path is gitignored
@@ -13,8 +24,11 @@ function isPathGitIgnored(projectRoot, targetPath) {
13
24
  // Use git check-ignore to determine if the path is ignored
14
25
  // If command exits with status 0, path is ignored
15
26
  // If command exits with status 1, path is not ignored
16
- execSync(`git -C "${projectRoot}" check-ignore -q "${targetPath}"`, { stdio: 'ignore' });
17
- return true; // Path is ignored (command exited with status 0)
27
+ const gitPath = fileOpener.getCommandPath('git');
28
+ const result = spawnSync(gitPath, ['-C', projectRoot, 'check-ignore', '-q', targetPath], {
29
+ stdio: 'ignore'
30
+ });
31
+ return result.status === 0; // Path is ignored (command exited with status 0)
18
32
  } catch (error) {
19
33
  console.log(`Error checking if path is gitignored: ${error.message}`);
20
34
  return false; // Path is not ignored (command exited with non-zero status)
@@ -87,11 +101,11 @@ function extractOutputPath(specsPath) {
87
101
  }
88
102
 
89
103
  /**
90
- * Check if the output directory exists
104
+ * Check if the final destination directory (from output_path) exists
91
105
  * @param {string} projectRoot - Root directory of the project
92
106
  * @param {string} outputPath - Output path from specs.json
93
107
  * @param {string} normalizedPath - Normalized output path
94
- * @returns {Object} - Result with output directory check
108
+ * @returns {Object} - Result with final destination directory check
95
109
  */
96
110
  function checkOutputDirExists(projectRoot, outputPath, normalizedPath) {
97
111
  // Check if the path exists
@@ -100,17 +114,17 @@ function checkOutputDirExists(projectRoot, outputPath, normalizedPath) {
100
114
 
101
115
  if (!outputPathExists) {
102
116
  return {
103
- name: 'Output directory existence',
117
+ name: 'Final destination directory existence',
104
118
  status: 'warning',
105
119
  success: true, // Still considered a "success" for backward compatibility
106
- details: `Output directory "${outputPath}" does not exist yet. This is OK if you haven't rendered the specs yet.`
120
+ details: `Final destination directory "${outputPath}" does not exist yet. This is OK if you haven't rendered the specs yet.`
107
121
  };
108
122
  }
109
123
 
110
124
  return {
111
- name: 'Output directory existence',
125
+ name: 'Final destination directory existence',
112
126
  success: true,
113
- details: `Output directory "${outputPath}" exists`
127
+ details: `Final destination directory "${outputPath}" exists`
114
128
  };
115
129
  }
116
130
 
@@ -245,7 +259,7 @@ function findComplexHtmlPatterns(lines) {
245
259
  }
246
260
 
247
261
  /**
248
- * Check if HTML files in the output directory are being ignored by Git
262
+ * Check if HTML files in the final destination directory are being ignored by Git
249
263
  * @param {string} projectRoot - Root directory of the project
250
264
  * @param {string} normalizedPath - Normalized output path
251
265
  * @param {string} outputPath - Original output path
@@ -273,8 +287,8 @@ function checkHtmlFilesGitignore(projectRoot, normalizedPath, outputPath, releva
273
287
  name: 'Check if index.html files are gitignored',
274
288
  success: !isIndexHtmlIgnored,
275
289
  details: isIndexHtmlIgnored
276
- ? `index.html files in the output directory would be ignored by Git. This is problematic as they're crucial output files.`
277
- : `index.html files in the output directory are properly tracked by Git.`
290
+ ? `index.html files in the final destination directory would be ignored by Git. This is problematic as they're crucial output files.`
291
+ : `index.html files in the final destination directory are properly tracked by Git.`
278
292
  });
279
293
 
280
294
  // If index.html is ignored but we couldn't find an explicit pattern, look for more complex patterns
@@ -295,22 +309,22 @@ function checkHtmlFilesGitignore(projectRoot, normalizedPath, outputPath, releva
295
309
  }
296
310
 
297
311
  /**
298
- * Check if output directory is being ignored by Git
312
+ * Check if final destination directory (from output_path) is being ignored by Git
299
313
  * @param {string} projectRoot - Root directory of the project
300
314
  * @param {string} normalizedPath - Normalized output path
301
315
  * @param {string} outputPath - Original output path
302
316
  * @param {string} dirName - Directory name from path
303
317
  * @param {Array} relevantLines - Relevant lines from .gitignore
304
- * @returns {Array} - Results for output directory gitignore check
318
+ * @returns {Array} - Results for final destination directory gitignore check
305
319
  */
306
320
  function checkOutputDirIgnorePatterns(projectRoot, normalizedPath, outputPath, dirName, relevantLines) {
307
321
  const dirIgnorePatterns = findOutputDirIgnorePatterns(relevantLines, normalizedPath, dirName);
308
322
 
309
323
  if (dirIgnorePatterns.length > 0) {
310
324
  return [{
311
- name: 'Check if output directory is gitignored',
325
+ name: 'Check if final destination directory is gitignored',
312
326
  success: false,
313
- details: `Found patterns in .gitignore that would ignore the output directory: ${dirIgnorePatterns.join(', ')}. Remove these entries to ensure generated content is tracked.`
327
+ details: `Found patterns in .gitignore that would ignore the final destination directory: ${dirIgnorePatterns.join(', ')}. Remove these entries to ensure generated content is tracked.`
314
328
  }];
315
329
  }
316
330
 
@@ -318,20 +332,21 @@ function checkOutputDirIgnorePatterns(projectRoot, normalizedPath, outputPath, d
318
332
  const isIgnored = isPathGitIgnored(projectRoot, normalizedPath);
319
333
 
320
334
  return [{
321
- name: 'Check if output directory is gitignored',
335
+ name: 'Check if final destination directory is gitignored',
322
336
  success: !isIgnored,
323
337
  details: isIgnored
324
- ? `Output directory "${outputPath}" is being ignored by Git. This could be due to a complex pattern in .gitignore. Remove any entries that might affect this directory.`
325
- : `Output directory "${outputPath}" is not being ignored by Git, which is good.`
338
+ ? `Final destination directory "${outputPath}" is being ignored by Git. This could be due to a complex pattern in .gitignore. Remove any entries that might affect this directory.`
339
+ : `Final destination directory "${outputPath}" is not being ignored by Git, which is good.`
326
340
  }];
327
341
  }
328
342
 
329
343
  /**
330
- * Check if the output directory is being ignored by Git
344
+ * Check if the final destination directory (from output_path in specs.json) is being ignored by Git
345
+ * This checks the directory where index.html is generated, NOT the temporary .cache directory
331
346
  * @param {string} projectRoot - Root directory of the project
332
347
  * @returns {Promise<Array>} - Array of check results
333
348
  */
334
- async function checkOutputDirGitIgnore(projectRoot) {
349
+ async function checkDestinationGitIgnore(projectRoot) {
335
350
  const results = [];
336
351
 
337
352
  try {
@@ -369,7 +384,7 @@ async function checkOutputDirGitIgnore(projectRoot) {
369
384
  const relevantLines = getRelevantGitignoreLines(gitignoreContent);
370
385
  const dirName = path.basename(normalizedPath);
371
386
 
372
- // Check output directory ignore patterns
387
+ // Check final destination directory ignore patterns
373
388
  const dirResults = checkOutputDirIgnorePatterns(
374
389
  projectRoot, normalizedPath, outputPath, dirName, relevantLines
375
390
  );
@@ -383,9 +398,9 @@ async function checkOutputDirGitIgnore(projectRoot) {
383
398
 
384
399
  return results;
385
400
  } catch (error) {
386
- console.error('Error checking output directory gitignore status:', error);
401
+ console.error('Error checking final destination directory gitignore status:', error);
387
402
  return [{
388
- name: 'Output directory gitignore check',
403
+ name: 'Final destination directory gitignore check',
389
404
  success: false,
390
405
  details: `Error: ${error.message}`
391
406
  }];
@@ -393,5 +408,5 @@ async function checkOutputDirGitIgnore(projectRoot) {
393
408
  }
394
409
 
395
410
  module.exports = {
396
- checkOutputDirGitIgnore
411
+ checkDestinationGitIgnore
397
412
  };