spec-up-t 1.1.54 → 1.2.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 (52) hide show
  1. package/assets/compiled/body.js +59 -8
  2. package/assets/compiled/head.css +13 -12
  3. package/assets/css/adjust-font-size.css +11 -0
  4. package/assets/css/collapse-meta-info.css +27 -8
  5. package/assets/css/create-term-filter.css +11 -0
  6. package/assets/css/index.css +15 -0
  7. package/assets/css/prism.css +176 -153
  8. package/assets/css/prism.dark.css +157 -0
  9. package/assets/css/prism.default.css +180 -0
  10. package/assets/css/terms-and-definitions.1.css +223 -0
  11. package/assets/css/terms-and-definitions.css +214 -100
  12. package/assets/js/addAnchorsToTerms.js +1 -1
  13. package/assets/js/collapse-definitions.js +2 -1
  14. package/assets/js/collapse-meta-info.js +25 -11
  15. package/assets/js/create-term-filter.js +61 -0
  16. package/assets/js/horizontal-scroll-hint.js +159 -0
  17. package/assets/js/index.1.js +137 -0
  18. package/assets/js/index.js +2 -1
  19. package/assets/js/insert-trefs.js +122 -116
  20. package/assets/js/insert-xrefs.1.js +372 -0
  21. package/assets/js/{show-commit-hashes.js → insert-xrefs.js} +67 -7
  22. package/assets/js/prism.dark.js +24 -0
  23. package/assets/js/prism.default.js +23 -0
  24. package/assets/js/prism.js +4 -5
  25. package/assets/js/search.js +1 -1
  26. package/assets/js/toggle-dense-info.js +40 -0
  27. package/branches.md +4 -29
  28. package/index.js +397 -189
  29. package/index.new.js +662 -0
  30. package/package.json +1 -1
  31. package/src/asset-map.json +9 -5
  32. package/src/collect-external-references.js +16 -9
  33. package/src/collectExternalReferences/fetchTermsFromIndex.js +328 -0
  34. package/src/collectExternalReferences/processXTrefsData.js +73 -18
  35. package/src/create-pdf.js +385 -89
  36. package/src/health-check/external-specs-checker.js +207 -0
  37. package/src/health-check/output-gitignore-checker.js +261 -0
  38. package/src/health-check/specs-configuration-checker.js +274 -0
  39. package/src/health-check/term-references-checker.js +191 -0
  40. package/src/health-check/terms-intro-checker.js +81 -0
  41. package/src/health-check/tref-term-checker.js +463 -0
  42. package/src/health-check.js +445 -0
  43. package/src/install-from-boilerplate/config-scripts-keys.js +2 -0
  44. package/src/install-from-boilerplate/menu.sh +6 -3
  45. package/src/markdown-it-extensions.js +134 -103
  46. package/src/prepare-tref.js +61 -24
  47. package/src/utils/fetch.js +100 -0
  48. package/templates/template.html +12 -7
  49. package/assets/js/css-helper.js +0 -30
  50. package/src/collectExternalReferences/fetchTermsFromGitHubRepository.js +0 -232
  51. package/src/collectExternalReferences/fetchTermsFromGitHubRepository.test.js +0 -385
  52. package/src/collectExternalReferences/octokitClient.js +0 -96
@@ -0,0 +1,445 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const http = require('http');
6
+ const https = require('https');
7
+ const { execSync } = require('child_process');
8
+
9
+ // Import modules from the health-check directory
10
+ const externalSpecsChecker = require('./health-check/external-specs-checker');
11
+ const termReferencesChecker = require('./health-check/term-references-checker');
12
+ const specsConfigurationChecker = require('./health-check/specs-configuration-checker');
13
+ const termsIntroChecker = require('./health-check/terms-intro-checker');
14
+ const outputGitignoreChecker = require('./health-check/output-gitignore-checker');
15
+ const trefTermChecker = require('./health-check/tref-term-checker');
16
+
17
+ // Configuration
18
+ const OUTPUT_DIR = path.join(process.cwd(), 'output');
19
+
20
+ // Create output directory if it doesn't exist
21
+ if (!fs.existsSync(OUTPUT_DIR)) {
22
+ fs.mkdirSync(OUTPUT_DIR, { recursive: true });
23
+ }
24
+
25
+ // Helper function to read specs.json file
26
+ function getRepoInfo() {
27
+ try {
28
+ // Path to the default boilerplate specs.json
29
+ const defaultSpecsPath = path.join(
30
+ __dirname,
31
+ 'install-from-boilerplate',
32
+ 'boilerplate',
33
+ 'specs.json'
34
+ );
35
+
36
+ let defaultValues = {
37
+ host: 'github',
38
+ account: 'blockchain-bird',
39
+ repo: 'spec-up-t'
40
+ };
41
+
42
+ // Try to load default values from boilerplate specs.json
43
+ if (fs.existsSync(defaultSpecsPath)) {
44
+ try {
45
+ const defaultSpecsContent = fs.readFileSync(defaultSpecsPath, 'utf8');
46
+ const defaultSpecs = JSON.parse(defaultSpecsContent);
47
+
48
+ if (defaultSpecs?.specs?.[0]?.source) {
49
+ const source = defaultSpecs.specs[0].source;
50
+ defaultValues = {
51
+ host: source.host || defaultValues.host,
52
+ account: source.account || defaultValues.account,
53
+ repo: source.repo || defaultValues.repo
54
+ };
55
+ }
56
+ } catch (error) {
57
+ console.error('Error reading boilerplate specs.json:', error);
58
+ }
59
+ }
60
+
61
+ // Look for specs.json in the current working directory (where the command is run from)
62
+ const specsPath = path.join(process.cwd(), 'specs.json');
63
+ // console.log(`Looking for specs.json at: ${specsPath}`);
64
+
65
+ if (fs.existsSync(specsPath)) {
66
+ // console.log('specs.json found!');
67
+ const specsContent = fs.readFileSync(specsPath, 'utf8');
68
+ const specs = JSON.parse(specsContent);
69
+
70
+ // Check if source field exists and has required properties
71
+ if (specs?.specs?.[0]?.source &&
72
+ specs.specs[0].source.host &&
73
+ specs.specs[0].source.account &&
74
+ specs.specs[0].source.repo) {
75
+
76
+ const sourceInfo = specs.specs[0].source;
77
+
78
+ // Check if values have been changed from defaults
79
+ const valuesChanged =
80
+ sourceInfo.host !== defaultValues.host ||
81
+ sourceInfo.account !== defaultValues.account ||
82
+ sourceInfo.repo !== defaultValues.repo;
83
+
84
+ // If values haven't changed, just return the default values
85
+ // This allows the user to manually change these later
86
+ if (!valuesChanged) {
87
+ return {
88
+ host: sourceInfo.host,
89
+ account: sourceInfo.account,
90
+ repo: sourceInfo.repo,
91
+ branch: sourceInfo.branch || defaultValues.branch
92
+ };
93
+ }
94
+
95
+ // If values have changed, verify the repository exists
96
+ const repoExists = checkRepositoryExists(
97
+ sourceInfo.host,
98
+ sourceInfo.account,
99
+ sourceInfo.repo
100
+ );
101
+
102
+ if (repoExists) {
103
+ // Repository exists, return the verified info
104
+ return {
105
+ host: sourceInfo.host,
106
+ account: sourceInfo.account,
107
+ repo: sourceInfo.repo,
108
+ branch: sourceInfo.branch || defaultValues.branch,
109
+ verified: true
110
+ };
111
+ } else {
112
+ // Repository doesn't exist, but values have been changed
113
+ // Return the values with a verification failed flag
114
+ return {
115
+ host: sourceInfo.host,
116
+ account: sourceInfo.account,
117
+ repo: sourceInfo.repo,
118
+ branch: sourceInfo.branch || defaultValues.branch,
119
+ verified: false
120
+ };
121
+ }
122
+ }
123
+ } else {
124
+ console.log('specs.json not found');
125
+ }
126
+ } catch (error) {
127
+ console.error('Error reading specs.json:', error);
128
+ }
129
+
130
+ // Return default values if specs.json doesn't exist or doesn't contain the required information
131
+ // console.log('Using default repository values');
132
+ return {
133
+ host: 'github',
134
+ account: 'blockchain-bird',
135
+ repo: 'spec-up-t',
136
+ branch: 'main'
137
+ };
138
+ }
139
+
140
+ // Helper function to check if a repository exists
141
+ function checkRepositoryExists(host, account, repo) {
142
+ try {
143
+ // For synchronous checking, we'll use a simple HTTP HEAD request
144
+ const url = `https://${host}.com/${account}/${repo}`;
145
+
146
+ // Simple synchronous HTTP request using execSync
147
+ const cmd = process.platform === 'win32'
148
+ ? `curl -s -o /nul -w "%{http_code}" -I ${url}`
149
+ : `curl -s -o /dev/null -w "%{http_code}" -I ${url}`;
150
+
151
+ const statusCode = execSync(cmd).toString().trim();
152
+
153
+ // 200, 301, 302 status codes indicate the repo exists
154
+ return ['200', '301', '302'].includes(statusCode);
155
+ } catch (error) {
156
+ console.error('Error checking repository existence:', error);
157
+ return false;
158
+ }
159
+ }
160
+
161
+ // Helper function to format current time for the filename
162
+ function getFormattedTimestamp() {
163
+ const now = new Date();
164
+ return now.toISOString()
165
+ .replace(/[T:]/g, '-')
166
+ .replace(/\..+/, '')
167
+ .replace(/[Z]/g, '');
168
+ }
169
+
170
+ // Helper function to generate a human-readable timestamp for display
171
+ function getHumanReadableTimestamp() {
172
+ return new Date().toLocaleString();
173
+ }
174
+
175
+ // Main function to run all checks and generate the report
176
+ async function runHealthCheck() {
177
+ console.log('Running health checks...');
178
+
179
+ // Collection to store all check results
180
+ const results = [];
181
+
182
+ try {
183
+
184
+ // Run term reference tref check
185
+ const trefTermResults = await trefTermChecker.checkTrefTerms(process.cwd());
186
+ results.push({
187
+ title: 'Check Trefs in all external specs',
188
+ results: trefTermResults
189
+ });
190
+
191
+ // Run external specs URL check
192
+ const externalSpecsResults = await externalSpecsChecker.checkExternalSpecs(process.cwd());
193
+ results.push({
194
+ title: 'Check External Specs URL',
195
+ results: externalSpecsResults
196
+ });
197
+
198
+ // Run term references check
199
+ const termReferencesResults = await termReferencesChecker.checkTermReferences(process.cwd());
200
+ results.push({
201
+ title: 'Check Term References',
202
+ results: termReferencesResults
203
+ });
204
+
205
+ // Run specs.json configuration check
206
+ const specsConfigResults = await specsConfigurationChecker.checkSpecsJsonConfiguration(process.cwd());
207
+ results.push({
208
+ title: 'Check <code>specs.json</code> configuration',
209
+ results: specsConfigResults
210
+ });
211
+
212
+ // Run terms-and-definitions-intro.md check
213
+ const termsIntroResults = await termsIntroChecker.checkTermsIntroFile(process.cwd());
214
+ results.push({
215
+ title: 'Check <code>terms-and-definitions-intro.md</code>',
216
+ results: termsIntroResults
217
+ });
218
+
219
+ // Run output directory gitignore check
220
+ const outputGitignoreResults = await outputGitignoreChecker.checkOutputDirGitIgnore(process.cwd());
221
+ results.push({
222
+ title: 'Check <code>.gitignore</code>',
223
+ results: outputGitignoreResults
224
+ });
225
+
226
+ // Add more checks here in the future
227
+
228
+ // Generate and open the report
229
+ generateReport(results);
230
+ } catch (error) {
231
+ console.error('Error running health checks:', error);
232
+ process.exit(1);
233
+ }
234
+ }
235
+
236
+ // Generate HTML report
237
+ function generateReport(checkResults) {
238
+ const timestamp = getFormattedTimestamp();
239
+ // Get repository information from specs.json
240
+ const repoInfo = getRepoInfo();
241
+ const reportFileName = `health-check-${timestamp}-${repoInfo.account}-${repoInfo.repo}.html`;
242
+ const reportPath = path.join(OUTPUT_DIR, reportFileName);
243
+
244
+
245
+ const htmlContent = generateHtmlReport(checkResults, getHumanReadableTimestamp(), repoInfo);
246
+
247
+ fs.writeFileSync(reportPath, htmlContent);
248
+ console.log(`Report generated: ${reportPath}`);
249
+
250
+ // Open the report in the default browser
251
+ try {
252
+ const openCommand = process.platform === 'win32' ? 'start' :
253
+ process.platform === 'darwin' ? 'open' : 'xdg-open';
254
+ execSync(`${openCommand} "${reportPath}"`);
255
+ } catch (error) {
256
+ console.error('Failed to open the report:', error);
257
+ }
258
+ }
259
+
260
+ // Generate HTML content
261
+ function generateHtmlReport(checkResults, timestamp, repoInfo) {
262
+ let resultsHtml = '';
263
+
264
+ // Add repository verification check at the beginning if needed
265
+ if (repoInfo && repoInfo.verified === false) {
266
+ // Create a new section at the top for repository verification
267
+ resultsHtml += `
268
+ <div class="card mb-4 results-card alert-danger" data-section="repository-verification">
269
+ <div class="card-header bg-danger text-white">
270
+ <h5>Repository Verification</h5>
271
+ </div>
272
+ <div class="card-body">
273
+ <table class="table table-striped">
274
+ <thead>
275
+ <tr>
276
+ <th>Status</th>
277
+ <th>Check</th>
278
+ <th>Details</th>
279
+ </tr>
280
+ </thead>
281
+ <tbody>
282
+ <tr data-status="fail" class="check-row">
283
+ <td class="text-danger" style="white-space: nowrap;">
284
+ <i class="bi bi-x-circle-fill"></i> <span style="vertical-align: middle;">Fail</span>
285
+ </td>
286
+ <td>Repository existence check</td>
287
+ <td>The repository at https://${repoInfo.host}.com/${repoInfo.account}/${repoInfo.repo} does not exist or is not accessible. Please verify the repository information in specs.json.</td>
288
+ </tr>
289
+ </tbody>
290
+ </table>
291
+ </div>
292
+ </div>
293
+ `;
294
+ }
295
+
296
+ checkResults.forEach(section => {
297
+ resultsHtml += `
298
+ <div class="card mb-4 results-card" data-section="${section.title.replace(/[^a-zA-Z0-9]/g, '-').toLowerCase()}">
299
+ <div class="card-header">
300
+ <h5>${section.title}</h5>
301
+ </div>
302
+ <div class="card-body">
303
+ <table class="table table-striped">
304
+ <thead>
305
+ <tr>
306
+ <th>Status</th>
307
+ <th>Check</th>
308
+ <th>Details</th>
309
+ </tr>
310
+ </thead>
311
+ <tbody>
312
+ `;
313
+
314
+ section.results.forEach(result => {
315
+ let statusClass, statusIcon, statusText;
316
+
317
+ if (result.status === 'warning') {
318
+ // Warning status
319
+ statusClass = 'text-warning';
320
+ statusIcon = '<i class="bi bi-exclamation-triangle-fill"></i>';
321
+ statusText = 'Warning';
322
+ } else if (result.success === 'partial') {
323
+ // Partial success (warning) status
324
+ statusClass = 'text-warning';
325
+ statusIcon = '<i class="bi bi-exclamation-triangle-fill"></i>';
326
+ statusText = 'Warning';
327
+ } else if (result.success) {
328
+ // Pass status
329
+ statusClass = 'text-success';
330
+ statusIcon = '<i class="bi bi-check-circle-fill"></i>';
331
+ statusText = 'Pass';
332
+ } else {
333
+ // Fail status
334
+ statusClass = 'text-danger';
335
+ statusIcon = '<i class="bi bi-x-circle-fill"></i>';
336
+ statusText = 'Fail';
337
+ }
338
+
339
+ // Add data-status attribute to identify rows by status and reorder columns to put status first
340
+ resultsHtml += `
341
+ <tr data-status="${statusText.toLowerCase()}" class="check-row">
342
+ <td class="${statusClass}" style="white-space: nowrap;">
343
+ ${statusIcon} <span style="vertical-align: middle;">${statusText}</span>
344
+ </td>
345
+ <td>${result.name}</td>
346
+ <td>${result.details || ''}</td>
347
+ </tr>
348
+ `;
349
+ });
350
+
351
+ resultsHtml += `
352
+ </tbody>
353
+ </table>
354
+ </div>
355
+ </div>
356
+ `;
357
+ });
358
+
359
+ return `
360
+ <!DOCTYPE html>
361
+ <html lang="en">
362
+ <head>
363
+ <meta charset="UTF-8">
364
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
365
+ <title>Spec-Up-T Health Check Report</title>
366
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
367
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
368
+ <style>
369
+ body {
370
+ padding-top: 2rem;
371
+ padding-bottom: 2rem;
372
+ }
373
+ .report-header {
374
+ margin-bottom: 2rem;
375
+ }
376
+ .timestamp {
377
+ color: #6c757d;
378
+ }
379
+ .filter-toggle {
380
+ margin-bottom: 1rem;
381
+ }
382
+ .hidden-item {
383
+ display: none;
384
+ }
385
+ </style>
386
+ </head>
387
+ <body>
388
+ <div class="container">
389
+ <div class="report-header">
390
+ <h1>Spec-Up-T Health Check Report</h1>
391
+ <p class="timestamp">Generated: ${timestamp}</p>
392
+ <p class="repo-info">
393
+ Repository: <a href="https://${repoInfo.host}.com/${repoInfo.account}/${repoInfo.repo}" target="_blank">https://${repoInfo.host}.com/${repoInfo.account}/${repoInfo.repo}</a><br>
394
+ Username: ${repoInfo.account}<br>
395
+ Repo name: ${repoInfo.repo}
396
+ </p>
397
+ </div>
398
+
399
+ <div class="filter-toggle form-check form-switch">
400
+ <input class="form-check-input" type="checkbox" id="togglePassingChecks" checked>
401
+ <label class="form-check-label" for="togglePassingChecks">Show / hide passing checks</label>
402
+ </div>
403
+
404
+ <div class="results">
405
+ ${resultsHtml}
406
+ </div>
407
+ </div>
408
+
409
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
410
+ <script>
411
+ // Toggle function for passing checks
412
+ document.getElementById('togglePassingChecks').addEventListener('change', function() {
413
+ const showPassing = this.checked;
414
+ const passingRows = document.querySelectorAll('tr[data-status="pass"]');
415
+
416
+ // Hide/show passing rows
417
+ passingRows.forEach(row => {
418
+ if (showPassing) {
419
+ row.classList.remove('hidden-item');
420
+ } else {
421
+ row.classList.add('hidden-item');
422
+ }
423
+ });
424
+
425
+ // Check each results card to see if it should be hidden
426
+ document.querySelectorAll('.results-card').forEach(card => {
427
+ const visibleRows = card.querySelectorAll('tr.check-row:not(.hidden-item)');
428
+
429
+ if (visibleRows.length === 0) {
430
+ // If no visible rows, hide the entire card
431
+ card.classList.add('hidden-item');
432
+ } else {
433
+ // Otherwise show it
434
+ card.classList.remove('hidden-item');
435
+ }
436
+ });
437
+ });
438
+ </script>
439
+ </body>
440
+ </html>
441
+ `;
442
+ }
443
+
444
+ // Run the health check
445
+ runHealthCheck();
@@ -11,6 +11,7 @@ const configScriptsKeys = {
11
11
  "menu": "bash ./node_modules/spec-up-t/src/install-from-boilerplate/menu.sh",
12
12
  "addremovexrefsource": "node --no-warnings -e \"require('spec-up-t/src/add-remove-xref-source.js')\"",
13
13
  "configure": "node --no-warnings -e \"require('spec-up-t/src/configure.js')\"",
14
+ "healthCheck": "node --no-warnings -e \"require('spec-up-t/src/health-check.js')\"",
14
15
  "custom-update": "npm update && node -e \"require('spec-up-t/src/install-from-boilerplate/custom-update.js')\""
15
16
  };
16
17
 
@@ -28,6 +29,7 @@ const configOverwriteScriptsKeys = {
28
29
  "menu": true,
29
30
  "addremovexrefsource": true,
30
31
  "configure": true,
32
+ "healthCheck": true,
31
33
  "custom-update": true
32
34
  };
33
35
 
@@ -10,11 +10,12 @@ function handle_choice() {
10
10
  "Collect external references (no cache, slower)" "collect_external_references_no_cache"
11
11
  "Add, remove or view xref source" "do_add_remove_xref_source"
12
12
  "Configure" "do_configure"
13
+ "Run health check" "do_health_check"
13
14
  "Open documentation website" "do_help"
14
15
  "Freeze specification" "do_freeze"
15
16
  )
16
17
 
17
- if [[ "$choice" =~ ^[0-8]$ ]]; then
18
+ if [[ "$choice" =~ ^[0-9]$ ]]; then
18
19
  local index=$((choice * 2))
19
20
  echo -e "\n\n ************************************"
20
21
  echo " ${options[index]}"
@@ -50,8 +51,9 @@ function display_intro() {
50
51
  [4] Collect external references (no cache, slower)
51
52
  [5] Add, remove or view xref source
52
53
  [6] Configure
53
- [7] Open documentation website
54
- [8] Freeze specification
54
+ [7] Run health check
55
+ [8] Open documentation website
56
+ [9] Freeze specification
55
57
  [Q] Quit
56
58
 
57
59
  An xref is a reference to another repository.
@@ -76,6 +78,7 @@ function collect_external_references_cache() { clear; npm run collectExternalRef
76
78
  function collect_external_references_no_cache() { clear; npm run collectExternalReferencesNoCache; }
77
79
  function do_add_remove_xref_source() { clear; npm run addremovexrefsource; }
78
80
  function do_configure() { clear; npm run configure; }
81
+ function do_health_check() { clear; npm run healthCheck; }
79
82
  function do_freeze() { clear; npm run freeze; }
80
83
 
81
84
  function do_help() {