spec-up-t 1.2.4 → 1.2.6

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 (42) hide show
  1. package/assets/compiled/body.js +11 -8
  2. package/assets/compiled/head.css +7 -4
  3. package/assets/css/add-bootstrap-classes-to-images.css +34 -0
  4. package/assets/css/collapse-definitions.css +41 -0
  5. package/assets/css/create-pdf.css +339 -0
  6. package/assets/css/horizontal-scroll-hint.css +6 -0
  7. package/assets/css/image-full-size.css +40 -0
  8. package/assets/css/index.css +5 -0
  9. package/assets/css/search.css +8 -8
  10. package/assets/css/terms-and-definitions.css +3 -3
  11. package/assets/js/add-bootstrap-classes-to-images.js +95 -0
  12. package/assets/js/collapse-definitions.js +259 -34
  13. package/assets/js/collapse-meta-info.js +37 -10
  14. package/assets/js/collapsibleMenu.js +0 -24
  15. package/assets/js/create-alphabet-index.js +11 -2
  16. package/assets/js/create-term-filter.js +47 -17
  17. package/assets/js/hide-show-utility-container.js +16 -0
  18. package/assets/js/horizontal-scroll-hint.js +2 -2
  19. package/assets/js/image-full-size.js +74 -0
  20. package/assets/js/insert-trefs.js +135 -49
  21. package/assets/js/search.js +46 -20
  22. package/{src → config}/asset-map.json +6 -0
  23. package/{src/config → config}/paths.js +2 -2
  24. package/gulpfile.js +1 -1
  25. package/index.js +5 -5
  26. package/package.json +1 -1
  27. package/src/README.md +3 -3
  28. package/src/collect-external-references.js +2 -2
  29. package/src/collectExternalReferences/fetchTermsFromIndex.1.js +340 -0
  30. package/src/collectExternalReferences/fetchTermsFromIndex.js +1 -1
  31. package/src/collectExternalReferences/processXTrefsData.js +1 -1
  32. package/src/create-pdf.js +330 -21
  33. package/src/create-term-index.js +126 -22
  34. package/src/health-check/tref-term-checker.js +1 -1
  35. package/src/health-check.js +1 -1
  36. package/src/init.js +1 -1
  37. package/src/insert-term-index.js +5 -5
  38. package/src/install-from-boilerplate/add-scripts-keys.js +3 -1
  39. package/src/install-from-boilerplate/boilerplate/gitignore +1 -1
  40. package/src/markdown-it-extensions.js +2 -2
  41. package/src/utils/fetch.js +14 -14
  42. package/assets/css/pdf-styles.css +0 -170
package/src/create-pdf.js CHANGED
@@ -29,7 +29,7 @@ const pdfLib = require('pdf-lib');
29
29
  let bootstrapCss = bootstrapExists ? fs.readFileSync(bootstrapCssPath, 'utf8') : '';
30
30
 
31
31
  // Path to PDF styles CSS
32
- const pdfStylesPath = path.resolve(process.cwd(), 'assets/css/pdf-styles.css');
32
+ const pdfStylesPath = path.resolve(process.cwd(), 'assets/css/create-pdf.css');
33
33
  const pdfStylesExist = fs.existsSync(pdfStylesPath);
34
34
  const pdfStylesCss = pdfStylesExist ? fs.readFileSync(pdfStylesPath, 'utf8') : '';
35
35
 
@@ -38,8 +38,14 @@ const pdfLib = require('pdf-lib');
38
38
 
39
39
  // Clean up unnecessary elements but be careful not to remove styles we need
40
40
  await page.evaluate(() => {
41
- document.querySelectorAll('[style*="display: none"], .d-print-none, script').forEach(element => element.remove());
41
+ // Preserve TOC if it exists (skip removing it even if it has display:none)
42
+ document.querySelectorAll('[style*="display: none"]:not(#toc):not(#toc *), .d-print-none:not(#toc):not(#toc *), script').forEach(element => element.remove());
42
43
  // Don't remove all style elements as some might be important
44
+
45
+ // If TOC doesn't exist, we'll need to create one
46
+ if (!document.getElementById('toc')) {
47
+ console.log('No TOC found in the document. Will create one.');
48
+ }
43
49
  });
44
50
 
45
51
  // Handle dynamically fetched cross-reference terms
@@ -68,7 +74,7 @@ const pdfLib = require('pdf-lib');
68
74
  container.style.margin = '0 auto';
69
75
  container.style.padding = '0';
70
76
  });
71
-
77
+
72
78
  // Override any Bootstrap column constraints
73
79
  const columns = document.querySelectorAll('[class*="col-"]');
74
80
  columns.forEach(col => {
@@ -77,7 +83,7 @@ const pdfLib = require('pdf-lib');
77
83
  col.style.paddingLeft = '0';
78
84
  col.style.paddingRight = '0';
79
85
  });
80
-
86
+
81
87
  // Ensure body takes full width
82
88
  document.body.style.maxWidth = '100%';
83
89
  document.body.style.width = '100%';
@@ -113,18 +119,18 @@ const pdfLib = require('pdf-lib');
113
119
  document.querySelectorAll('table').forEach(table => {
114
120
  table.classList.add('table', 'table-bordered', 'table-striped', 'table-hover');
115
121
  });
116
-
122
+
117
123
  // Make sure meta-info is visible and toggle buttons are hidden
118
124
  document.querySelectorAll('.meta-info, .term-meta').forEach(meta => {
119
125
  meta.style.display = 'block';
120
126
  });
121
-
127
+
122
128
  document.querySelectorAll('.meta-toggle, button.meta-toggle').forEach(button => {
123
129
  button.style.display = 'none';
124
130
  });
125
131
  });
126
132
 
127
- // Inject logo, title, and description
133
+ // Inject logo, title, and description AND handle TOC creation if needed
128
134
  await page.evaluate((logo, logoLink, title, description) => {
129
135
  const titleWrapper = document.createElement('div');
130
136
  titleWrapper.className = 'text-center mb-5 pb-4 border-bottom';
@@ -155,9 +161,88 @@ const pdfLib = require('pdf-lib');
155
161
  }
156
162
 
157
163
  document.body.insertBefore(titleWrapper, document.body.firstChild);
164
+
165
+ // Create a Table of Contents if it doesn't exist
166
+ if (!document.getElementById('toc')) {
167
+ // Generate a TOC based on the headings in the document
168
+ const headings = Array.from(document.querySelectorAll('h1:not(.pdf-title), h2, h3, h4, h5, h6')).filter(h => {
169
+ // Only include headings with IDs that can be linked to
170
+ return h.id && h.id.trim() !== '';
171
+ });
172
+
173
+ if (headings.length > 0) {
174
+ // Create TOC container
175
+ const tocContainer = document.createElement('div');
176
+ tocContainer.id = 'toc';
177
+ tocContainer.className = 'toc-container';
178
+
179
+ // Create TOC heading
180
+ const tocHeading = document.createElement('h2');
181
+ tocHeading.textContent = 'Contents';
182
+ tocHeading.className = 'toc-heading';
183
+ tocContainer.appendChild(tocHeading);
184
+
185
+ // Create TOC list
186
+ const tocList = document.createElement('ul');
187
+ tocList.className = 'toc-list';
188
+ tocContainer.appendChild(tocList);
189
+
190
+ // Add all headings to the TOC
191
+ let currentLevel = 0;
192
+ let currentList = tocList;
193
+ let listStack = [tocList];
194
+
195
+ headings.forEach(heading => {
196
+ // Get heading level (1-6 for h1-h6)
197
+ const level = parseInt(heading.tagName[1]);
198
+
199
+ // Navigate to the correct nesting level
200
+ if (level > currentLevel) {
201
+ // Go deeper - create a new nested list
202
+ for (let i = currentLevel; i < level; i++) {
203
+ if (currentList.lastChild) {
204
+ const nestedList = document.createElement('ul');
205
+ currentList.lastChild.appendChild(nestedList);
206
+ listStack.push(nestedList);
207
+ currentList = nestedList;
208
+ } else {
209
+ // If no items exist yet, add a dummy item
210
+ const dummyItem = document.createElement('li');
211
+ const nestedList = document.createElement('ul');
212
+ dummyItem.appendChild(nestedList);
213
+ currentList.appendChild(dummyItem);
214
+ listStack.push(nestedList);
215
+ currentList = nestedList;
216
+ }
217
+ }
218
+ } else if (level < currentLevel) {
219
+ // Go up - pop from the stack
220
+ for (let i = currentLevel; i > level; i--) {
221
+ listStack.pop();
222
+ }
223
+ currentList = listStack[listStack.length - 1];
224
+ }
225
+
226
+ currentLevel = level;
227
+
228
+ // Create list item with link
229
+ const listItem = document.createElement('li');
230
+ const link = document.createElement('a');
231
+ link.href = '#' + heading.id;
232
+ link.textContent = heading.textContent;
233
+ listItem.appendChild(link);
234
+ currentList.appendChild(listItem);
235
+ });
236
+
237
+ // Insert the TOC after the title section
238
+ document.body.insertBefore(tocContainer, titleWrapper.nextSibling);
239
+
240
+ console.log('Generated a Table of Contents with ' + headings.length + ' entries.');
241
+ }
242
+ }
158
243
  }, logo, logoLink, title, description);
159
-
160
- // Direct manipulation of definition lists to ensure proper styling in PDF
244
+
245
+ // Direct manipulation of definition lists and TOC to ensure proper styling in PDF
161
246
  await page.evaluate(() => {
162
247
  // Process all definition lists
163
248
  const definitionLists = document.querySelectorAll('dl.terms-and-definitions-list');
@@ -168,7 +253,7 @@ const pdfLib = require('pdf-lib');
168
253
  // Remove background and borders with !important to override any existing styles
169
254
  term.setAttribute('style', term.getAttribute('style') + '; background: transparent !important; background-color: transparent !important; background-image: none !important; border: none !important; border-radius: 0 !important; padding: 0.5rem 0 !important;');
170
255
  });
171
-
256
+
172
257
  // Ensure all meta-info content is visible
173
258
  const metaInfoContents = list.querySelectorAll('dd.meta-info-content-wrapper');
174
259
  metaInfoContents.forEach(content => {
@@ -179,42 +264,266 @@ const pdfLib = require('pdf-lib');
179
264
  content.style.padding = '0.5rem 0';
180
265
  content.style.margin = '0';
181
266
  content.style.lineHeight = 'normal';
182
-
267
+
183
268
  // Remove the collapsed class if present
184
269
  content.classList.remove('collapsed');
185
270
  });
186
-
271
+
187
272
  // Hide all meta-info toggle buttons
188
273
  const toggleButtons = list.querySelectorAll('.meta-info-toggle-button');
189
274
  toggleButtons.forEach(button => {
190
275
  button.style.display = 'none';
191
276
  });
192
277
  });
193
-
278
+
194
279
  // Special handling for ALL transcluded terms with blue background - no class restrictions
195
280
  document.querySelectorAll('.transcluded-xref-term').forEach(el => {
196
281
  // Use the most aggressive approach possible to override the blue background
197
282
  el.setAttribute('style', el.getAttribute('style') + '; background: transparent !important; background-color: transparent !important; background-image: none !important;');
198
-
283
+
199
284
  // Also process any child elements to ensure complete removal of background
200
285
  Array.from(el.children).forEach(child => {
201
286
  child.setAttribute('style', child.getAttribute('style') + '; background: transparent !important; background-color: transparent !important; background-image: none !important;');
202
287
  });
203
288
  });
204
-
289
+
205
290
  // Remove any inline styles that might be setting backgrounds
206
291
  document.querySelectorAll('style').forEach(styleTag => {
207
292
  let cssText = styleTag.textContent;
208
293
  // If the style tag contains transcluded-xref-term styles, modify them
209
294
  if (cssText.includes('transcluded-xref-term') && cssText.includes('background')) {
210
- cssText = cssText.replace(/dt\.transcluded-xref-term[^}]+}/g,
211
- 'dt.transcluded-xref-term, dd.transcluded-xref-term { background: transparent !important; background-color: transparent !important; background-image: none !important; }');
295
+ cssText = cssText.replace(/dt\.transcluded-xref-term[^}]+}/g,
296
+ 'dt.transcluded-xref-term, dd.transcluded-xref-term { background: transparent !important; background-color: transparent !important; background-image: none !important; }');
212
297
  styleTag.textContent = cssText;
213
298
  }
214
- });
299
+ }); // Format Table of Contents for book-like layout
300
+ const toc = document.getElementById('toc');
301
+ if (toc) {
302
+ // Make sure TOC is visible
303
+ toc.style.display = 'block';
304
+ toc.style.visibility = 'visible';
305
+ toc.style.opacity = '1';
306
+
307
+ // Create a new TOC div for the PDF using a completely different approach
308
+ const pdfToc = document.createElement('div');
309
+ pdfToc.id = 'pdf-toc';
310
+
311
+ // Ensure TOC has a header
312
+ const tocHeading = document.createElement('h2');
313
+ tocHeading.textContent = 'Contents';
314
+ tocHeading.style.textAlign = 'center';
315
+ tocHeading.style.fontWeight = 'bold';
316
+ tocHeading.style.marginBottom = '1.5rem';
317
+ tocHeading.style.paddingBottom = '0.5rem';
318
+ tocHeading.style.borderBottom = '1px solid #000';
319
+ pdfToc.appendChild(tocHeading);
320
+
321
+ // Create a fresh TOC structure - completely rebuilding it
322
+ const tocList = document.createElement('ul');
323
+ tocList.style.listStyleType = 'none';
324
+ tocList.style.padding = '0';
325
+ tocList.style.margin = '0';
326
+ pdfToc.appendChild(tocList);
327
+
328
+ // Find all section headings to include in the TOC
329
+ // Look for both original TOC entries and also scan document headings
330
+ const tocOriginalLinks = toc.querySelectorAll('a');
331
+
332
+ // Process each TOC link to create a new TOC item
333
+ tocOriginalLinks.forEach((link, index) => {
334
+ const li = document.createElement('li');
335
+ const rowDiv = document.createElement('div');
336
+ rowDiv.className = 'toc-row';
337
+
338
+ const title = document.createElement('a');
339
+ title.href = link.getAttribute('href');
340
+ title.textContent = link.textContent;
341
+ title.className = 'toc-title';
342
+ title.setAttribute('data-target-id', link.getAttribute('href').substring(1)); // Store the target id
343
+ // Ensure no blue color or underline for TOC links
344
+ title.style.color = '#000';
345
+ title.style.textDecoration = 'none';
346
+ title.style.borderBottom = 'none';
347
+ title.style.backgroundColor = 'transparent';
348
+
349
+ const leader = document.createElement('div');
350
+ leader.className = 'toc-leader';
351
+
352
+ // Create page number placeholder - we'll fill in actual page numbers later
353
+ const pageNumber = document.createElement('span');
354
+ pageNumber.className = 'toc-page-number';
355
+ pageNumber.textContent = ''; // Empty for now
356
+ pageNumber.setAttribute('data-for-id', link.getAttribute('href').substring(1));
357
+ pageNumber.style.position = 'absolute';
358
+ pageNumber.style.right = '0';
359
+
360
+ rowDiv.appendChild(title);
361
+ rowDiv.appendChild(leader);
362
+ li.appendChild(rowDiv);
363
+ li.appendChild(pageNumber);
364
+
365
+ // Determine nesting level from original TOC
366
+ let level = 0;
367
+ let parent = link.closest('li');
368
+ while (parent) {
369
+ const parentList = parent.closest('ul');
370
+ if (parentList && parentList !== toc) {
371
+ level++;
372
+ parent = parentList.closest('li');
373
+ } else {
374
+ break;
375
+ }
376
+ }
377
+
378
+ // Apply indentation based on level
379
+ if (level > 0) {
380
+ li.style.paddingLeft = (level * 15) + 'px';
381
+ }
382
+
383
+ tocList.appendChild(li);
384
+ });
385
+
386
+ // Insert the new TOC at the beginning of the document after the title
387
+ const titleWrapper = document.querySelector('.text-center.mb-5.pb-4.border-bottom');
388
+ if (titleWrapper && titleWrapper.nextSibling) {
389
+ document.body.insertBefore(pdfToc, titleWrapper.nextSibling);
390
+ } else {
391
+ document.body.insertBefore(pdfToc, document.body.firstChild);
392
+ }
393
+
394
+ // Force page break before TOC
395
+ pdfToc.style.breakBefore = 'page';
396
+ pdfToc.style.pageBreakBefore = 'always';
397
+
398
+ // Force page break after TOC
399
+ const tocNext = pdfToc.nextElementSibling;
400
+ if (tocNext) {
401
+ tocNext.style.breakBefore = 'page';
402
+ tocNext.style.pageBreakBefore = 'always';
403
+ }
404
+
405
+ // Hide the original TOC
406
+ toc.style.display = 'none';
407
+ }
408
+ });
409
+
410
+ console.log('Generating PDF with proper TOC page numbers...');
411
+
412
+ // First, generate a draft PDF to calculate the page positions of each heading
413
+ const draftPdfBuffer = await page.pdf({
414
+ format: 'A4',
415
+ displayHeaderFooter: true,
416
+ footerTemplate: `
417
+ <div style="width: 100%; text-align: center; font-size: 10pt; margin-top: 10mm;">
418
+ Page <span class="pageNumber"></span> of <span class="totalPages"></span>
419
+ </div>
420
+ `,
421
+ headerTemplate: '<div></div>',
422
+ preferCSSPageSize: true,
423
+ printBackground: true,
424
+ margin: { top: '10mm', bottom: '10mm', left: '10mm', right: '10mm' }
425
+ });
426
+
427
+ // Now extract the page numbers from the tooltips and update the TOC entries
428
+ await page.evaluate(() => {
429
+ // Find the PDF TOC
430
+ const pdfToc = document.getElementById('pdf-toc');
431
+ if (!pdfToc) return;
432
+
433
+ const tocEntries = pdfToc.querySelectorAll('.toc-page-number');
434
+ const originalToc = document.getElementById('toc');
435
+
436
+ if (originalToc) {
437
+ // Get all links from the original TOC that have tooltip data
438
+ const originalLinks = originalToc.querySelectorAll('a[title], a[data-bs-title]');
439
+
440
+ // Create a mapping from heading IDs to page numbers based on tooltips
441
+ const idToPageMap = {};
442
+
443
+ originalLinks.forEach(link => {
444
+ // Extract the heading ID from href
445
+ const href = link.getAttribute('href');
446
+ if (!href || !href.startsWith('#')) return;
447
+
448
+ const headingId = href.substring(1);
449
+
450
+ // Extract page number from tooltip text (e.g., "Go to page 5")
451
+ const tooltipText = link.getAttribute('title') || link.getAttribute('data-bs-title');
452
+ if (!tooltipText) return;
453
+
454
+ const pageNumberMatch = tooltipText.match(/Go to page (\d+)/i);
455
+ if (pageNumberMatch && pageNumberMatch[1]) {
456
+ const pageNumber = parseInt(pageNumberMatch[1], 10);
457
+ if (!isNaN(pageNumber)) {
458
+ idToPageMap[headingId] = pageNumber;
459
+ }
460
+ }
461
+ });
462
+
463
+ // Now update the TOC page numbers using the extracted values
464
+ tocEntries.forEach(entry => {
465
+ const targetId = entry.getAttribute('data-for-id');
466
+ if (targetId && idToPageMap[targetId]) {
467
+ entry.textContent = idToPageMap[targetId];
468
+ // Ensure page numbers are clearly visible with proper styling
469
+ entry.style.visibility = 'visible';
470
+ entry.style.opacity = '1';
471
+ entry.style.color = '#000';
472
+ entry.style.background = '#fff';
473
+ entry.style.padding = '0 4px';
474
+ entry.style.fontWeight = 'normal';
475
+ }
476
+ });
477
+ } else {
478
+ // Fallback to old estimation method if original TOC is not available
479
+ console.log('Original TOC not found, using page number estimation method');
480
+
481
+ // Find all headings with IDs (potential TOC targets)
482
+ const headingsWithIds = Array.from(document.querySelectorAll('h1, h2, h3, h4, h5, h6')).filter(h => h.id);
483
+
484
+ // Use real offsets for more accurate page numbers
485
+ const idToPosition = {};
486
+
487
+ headingsWithIds.forEach(heading => {
488
+ const rect = heading.getBoundingClientRect();
489
+ idToPosition[heading.id] = {
490
+ top: rect.top + window.scrollY,
491
+ id: heading.id
492
+ };
493
+ });
494
+
495
+ // Sort by vertical position
496
+ const sortedPositions = Object.values(idToPosition).sort((a, b) => a.top - b.top);
497
+
498
+ // A4 page height (mm to pixels at 96 DPI)
499
+ const pageHeight = 297 * 96 / 25.4;
500
+ const effectivePageHeight = pageHeight - 20; // Account for margins
501
+
502
+ // Start on page 3 (after title and TOC)
503
+ let currentPage = 3;
504
+ let currentOffset = 0;
505
+
506
+ // Calculate page numbers based on relative positions
507
+ sortedPositions.forEach(pos => {
508
+ while (pos.top > currentOffset + effectivePageHeight) {
509
+ currentPage++;
510
+ currentOffset += effectivePageHeight;
511
+ }
512
+
513
+ idToPosition[pos.id].page = currentPage;
514
+ });
515
+
516
+ // Update TOC entries with calculated page numbers
517
+ tocEntries.forEach(entry => {
518
+ const targetId = entry.getAttribute('data-for-id');
519
+ if (targetId && idToPosition[targetId] && idToPosition[targetId].page) {
520
+ entry.textContent = idToPosition[targetId].page;
521
+ }
522
+ });
523
+ }
215
524
  });
216
525
 
217
- // Generate PDF
526
+ // Final PDF generation with correct page numbers
218
527
  const pdfBuffer = await page.pdf({
219
528
  path: path.resolve(process.cwd(), 'docs/index.pdf'),
220
529
  format: 'A4',
@@ -228,7 +537,7 @@ const pdfLib = require('pdf-lib');
228
537
  preferCSSPageSize: true,
229
538
  printBackground: true,
230
539
  quality: 100,
231
- margin: { top: '10mm', bottom: '10mm', left: '3mm', right: '3mm' }
540
+ margin: { top: '10mm', bottom: '10mm', left: '10mm', right: '10mm' }
232
541
  });
233
542
 
234
543
  await browser.close();
@@ -248,4 +557,4 @@ const pdfLib = require('pdf-lib');
248
557
  } catch (error) {
249
558
  console.error('❌ Error generating PDF:', error);
250
559
  }
251
- })();
560
+ })();
@@ -1,41 +1,145 @@
1
1
  /**
2
2
  * Steps:
3
- * 1. Reads the configuration from 'specs.json'.
4
- * 2. Extracts the directories containing the specifications and terms.
5
- * 3. Lists all file names in the specified terms directory.
3
+ * 1. Reads the configuration from 'specs.json', with fallbacks for missing or invalid files.
4
+ * 2. Extracts the directories containing the specifications and terms, with defaults for missing values.
5
+ * 3. Lists all file names in the specified terms directory if it exists.
6
6
  * 4. Joins each file name with the terms directory path.
7
- * 5. Creates an 'output' directory in the project root if it does not exist.
7
+ * 5. Creates an '.cache' directory in the project root if it does not exist.
8
8
  * 6. Writes the list of file paths to 'term-index.json' in the project root.
9
9
  *
10
+ * If any errors occur during the process, appropriate warnings are logged, and an empty term index
11
+ * will be created rather than throwing fatal errors.
12
+ *
10
13
  * @requires fs-extra - File system operations with extra methods.
11
14
  * @requires path - Utilities for working with file and directory paths.
12
15
  * @file src/create-term-index.js
13
16
  * @author Kor Dwarshuis
14
- * @version 1.0.0
17
+ * @version 1.1.0
15
18
  * @since 2024-09-02
16
19
  */
17
20
 
18
21
  const { shouldProcessFile } = require('./utils/file-filter');
19
22
 
20
23
  function createTermIndex() {
21
- const fs = require('fs-extra');
22
- const path = require('path');
23
- const config = fs.readJsonSync('specs.json');
24
- const specDirectories = config.specs.map(spec => spec.spec_directory);
25
- const specTermDirectoryName = config.specs.map(spec => spec.spec_terms_directory);
26
- const outputPathJSON = path.join('output', 'term-index.json');
27
- const files = fs.readdirSync(path.join(specDirectories[0], specTermDirectoryName[0]))
28
- .filter(shouldProcessFile);
29
-
30
- const filePaths = files.map(file => specTermDirectoryName[0] + '/' + file);
31
-
32
- if (!fs.existsSync('output')) {
33
- fs.mkdirSync('output');
24
+ try {
25
+ const fs = require('fs-extra');
26
+ const path = require('path');
27
+ const configPath = 'specs.json';
28
+
29
+ // Check if specs.json exists
30
+ if (!fs.existsSync(configPath)) {
31
+ console.warn(`Config file '${configPath}' not found. Using default configuration.`);
32
+ var config = { specs: [] };
33
+ } else {
34
+ // Read config with try-catch to handle parsing errors
35
+ try {
36
+ var config = fs.readJsonSync(configPath);
37
+ } catch (readError) {
38
+ console.warn(`Error reading config file: ${readError.message}. Using default configuration.`);
39
+ var config = { specs: [] };
40
+ }
41
+ }
42
+
43
+ // Provide defaults for missing config
44
+ if (!config) {
45
+ console.warn('Config file is empty or invalid. Using defaults.');
46
+ config = { specs: [] };
47
+ }
48
+
49
+ if (!config.specs) {
50
+ console.warn('No specs array found in config. Creating empty specs array.');
51
+ config.specs = [];
52
+ } else if (!Array.isArray(config.specs)) {
53
+ console.warn('Config specs is not an array. Converting to array.');
54
+ config.specs = [config.specs]; // Convert to array if it's an object
55
+ }
56
+
57
+ // If no valid specs, create an empty term index
58
+ if (config.specs.length === 0) {
59
+ console.warn('No specs found in configuration. Creating an empty term index.');
60
+ }
61
+
62
+ // Extract spec directories with fallback to current directory
63
+ const specDirectories = config.specs.map((spec, index) => {
64
+ if (!spec.spec_directory) {
65
+ console.warn(`Warning: spec_directory missing in specs.json entry #${index + 1}. Using current directory.`);
66
+ return '.'; // Default to current directory
67
+ }
68
+ return spec.spec_directory;
69
+ });
70
+
71
+ // Extract term directories with fallback to default value
72
+ const specTermDirectoryName = config.specs.map((spec, index) => {
73
+ if (!spec.spec_terms_directory) {
74
+ console.warn(`Warning: spec_terms_directory missing in specs.json entry #${index + 1}. Using default 'terms' directory.`);
75
+ return 'terms'; // Default directory name for terms
76
+ }
77
+ return spec.spec_terms_directory;
78
+ });
79
+
80
+ // Safety check - if we have no valid entries, warn and exit cleanly
81
+ if (specDirectories.length === 0 || specTermDirectoryName.length === 0) {
82
+ console.log('No term directories found in configuration. Creating empty term index.');
83
+
84
+ // Create an empty term index
85
+ const outputPathJSON = path.join('.cache', 'term-index.json');
86
+ if (!fs.existsSync('.cache')) {
87
+ fs.mkdirSync('.cache', { recursive: true });
88
+ }
89
+ fs.writeJsonSync(outputPathJSON, [], { spaces: 2 });
90
+ console.log(`✅ Empty term index created at: ${outputPathJSON}`);
91
+ return; // Exit function early
92
+ }
93
+
94
+ // Verify that the base spec directory exists
95
+ const baseSpecDir = specDirectories[0];
96
+ if (!baseSpecDir || !fs.existsSync(baseSpecDir)) {
97
+ console.warn(`Spec directory '${baseSpecDir}' does not exist. Creating empty term index.`);
98
+
99
+ // Create an empty term index
100
+ const outputPathJSON = path.join('.cache', 'term-index.json');
101
+ if (!fs.existsSync('.cache')) {
102
+ fs.mkdirSync('.cache', { recursive: true });
103
+ }
104
+ fs.writeJsonSync(outputPathJSON, [], { spaces: 2 });
105
+ console.log(`✅ Empty term index created at: ${outputPathJSON}`);
106
+ return; // Exit function early
107
+ }
108
+
109
+ // Verify that the terms directory exists
110
+ const termsDir = path.join(baseSpecDir, specTermDirectoryName[0]);
111
+
112
+ let files = [];
113
+ if (!fs.existsSync(termsDir)) {
114
+ console.warn(`Terms directory '${termsDir}' does not exist. Creating an empty term index.`);
115
+ } else {
116
+ // Get list of files and filter them
117
+ files = fs.readdirSync(termsDir).filter(shouldProcessFile);
118
+ }
119
+
120
+ if (files.length === 0) {
121
+ console.log('Warning: No term files found to process.');
122
+ }
123
+
124
+ const filePaths = files.map(file => specTermDirectoryName[0] + '/' + file);
125
+ const outputPathJSON = path.join('.cache', 'term-index.json');
126
+
127
+ // Create .cache directory if it doesn't exist
128
+ if (!fs.existsSync('.cache')) {
129
+ fs.mkdirSync('.cache', { recursive: true });
130
+ }
131
+
132
+ // Write the term index file
133
+ try {
134
+ fs.writeJsonSync(outputPathJSON, filePaths, { spaces: 2 });
135
+ console.log(`✅ Term index created with ${files.length} terms. Output: ${outputPathJSON}`);
136
+ } catch (writeError) {
137
+ throw new Error(`Failed to write term index file: ${writeError.message}`);
138
+ }
139
+ } catch (error) {
140
+ console.error(`❌ Error creating term index: ${error.message}`);
141
+ throw error;
34
142
  }
35
-
36
- fs.writeJsonSync(outputPathJSON, filePaths, { spaces: 2 });
37
-
38
- console.log(`✅ The new terms were added. All done.`);
39
143
  }
40
144
 
41
145
  module.exports = {
@@ -321,7 +321,7 @@ async function getProjectConfiguration(projectRoot) {
321
321
  externalSpecs,
322
322
  specDirs,
323
323
  valid: true,
324
- githubCacheDir: path.join(projectRoot, 'output', 'github-cache')
324
+ githubCacheDir: path.join(projectRoot, '.cache', 'github-cache')
325
325
  };
326
326
  }
327
327
 
@@ -15,7 +15,7 @@ const outputGitignoreChecker = require('./health-check/output-gitignore-checker'
15
15
  const trefTermChecker = require('./health-check/tref-term-checker');
16
16
 
17
17
  // Configuration
18
- const OUTPUT_DIR = path.join(process.cwd(), 'output');
18
+ const OUTPUT_DIR = path.join(process.cwd(), '.cache');
19
19
 
20
20
  // Create output directory if it doesn't exist
21
21
  if (!fs.existsSync(OUTPUT_DIR)) {
package/src/init.js CHANGED
@@ -1,6 +1,6 @@
1
1
  const fs = require('fs-extra');
2
2
  const path = require('path');
3
- const outputDir = path.join(process.cwd(), 'output');
3
+ const outputDir = path.join(process.cwd(), '.cache');
4
4
  const initFlagPath = path.join(outputDir, 'init.flag');
5
5
 
6
6
  async function initialize() {