spec-up-t 1.2.9 → 1.3.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.
- package/.github/copilot-instructions.md +2 -1
- package/assets/compiled/body.js +5 -5
- package/assets/compiled/head.css +1 -0
- package/assets/css/counter.css +104 -0
- package/assets/js/addAnchorsToTerms.js +13 -5
- package/assets/js/collapse-definitions.js +0 -3
- package/assets/js/custom-elements.js +25 -27
- package/assets/js/fix-last-dd.js +6 -3
- package/assets/js/highlight-heading-plus-sibling-nodes.js +0 -1
- package/assets/js/highlight-heading-plus-sibling-nodes.test.js +120 -0
- package/assets/js/insert-trefs.js +32 -28
- package/config/asset-map.json +1 -0
- package/index.js +33 -227
- package/package.json +4 -2
- package/sonar-project.properties +7 -0
- package/src/collect-external-references.js +22 -11
- package/src/collect-external-references.test.js +153 -2
- package/src/collectExternalReferences/fetchTermsFromIndex.js +65 -110
- package/src/collectExternalReferences/processXTrefsData.js +9 -11
- package/src/create-docx.js +332 -0
- package/src/create-pdf.js +243 -122
- package/src/fix-markdown-files.js +31 -34
- package/src/html-dom-processor.js +290 -0
- package/src/init.js +3 -0
- package/src/install-from-boilerplate/boilerplate/.github/workflows/menu.yml +51 -36
- package/src/install-from-boilerplate/boilerplate/spec/example-markup-in-markdown.md +0 -1
- package/src/install-from-boilerplate/boilerplate/spec/terms-and-definitions-intro.md +1 -5
- package/src/install-from-boilerplate/config-scripts-keys.js +4 -4
- package/src/install-from-boilerplate/menu.sh +6 -6
- package/src/markdown-it-extensions.js +54 -33
- package/src/references.js +18 -6
- package/templates/template.html +2 -0
package/src/create-pdf.js
CHANGED
|
@@ -3,14 +3,220 @@ const puppeteer = require('puppeteer');
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const pdfLib = require('pdf-lib');
|
|
5
5
|
|
|
6
|
+
// ISO compliance configuration
|
|
7
|
+
const ISO_CONFIG = {
|
|
8
|
+
embedFonts: true,
|
|
9
|
+
deviceIndependentColor: true,
|
|
10
|
+
tagged: true, // For PDF/UA accessibility
|
|
11
|
+
pdfVersion: '1.7', // ISO 32000-1 compliant
|
|
12
|
+
metadata: {
|
|
13
|
+
format: 'PDF/A-2b', // Archive-friendly format
|
|
14
|
+
conformance: 'B' // Basic conformance level
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Creates ISO-compliant PDF metadata
|
|
20
|
+
*/
|
|
21
|
+
function createISOMetadata(config) {
|
|
22
|
+
const now = new Date();
|
|
23
|
+
return {
|
|
24
|
+
title: config.specs[0].title || 'Untitled Document',
|
|
25
|
+
author: config.specs[0].author || '',
|
|
26
|
+
subject: config.specs[0].description || '',
|
|
27
|
+
keywords: config.specs[0].keywords || [],
|
|
28
|
+
creator: 'Spec-Up PDF Generator',
|
|
29
|
+
producer: 'Spec-Up with ISO Compliance',
|
|
30
|
+
creationDate: now,
|
|
31
|
+
modificationDate: now
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Configures Puppeteer page for ISO compliance
|
|
37
|
+
*/
|
|
38
|
+
async function configurePageForISO(page) {
|
|
39
|
+
// Set device-independent color profile and font embedding
|
|
40
|
+
await page.emulateMediaType('print');
|
|
41
|
+
await page.evaluateOnNewDocument(() => {
|
|
42
|
+
// Force device-independent color rendering
|
|
43
|
+
document.documentElement.style.colorRendering = 'optimizeQuality';
|
|
44
|
+
document.documentElement.style.textRendering = 'optimizeLegibility';
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Applies accessibility tags for PDF/UA compliance
|
|
50
|
+
*/
|
|
51
|
+
async function applyAccessibilityTags(page) {
|
|
52
|
+
await page.evaluate(() => {
|
|
53
|
+
// Add semantic structure for accessibility
|
|
54
|
+
const main = document.querySelector('main') || document.body;
|
|
55
|
+
if (main && !main.getAttribute('role')) {
|
|
56
|
+
main.setAttribute('role', 'main');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Tag headings for proper structure
|
|
60
|
+
document.querySelectorAll('h1, h2, h3, h4, h5, h6').forEach(heading => {
|
|
61
|
+
if (!heading.getAttribute('role')) {
|
|
62
|
+
heading.setAttribute('role', 'heading');
|
|
63
|
+
heading.setAttribute('aria-level', heading.tagName.charAt(1));
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Tag navigation elements
|
|
68
|
+
const toc = document.getElementById('toc') || document.getElementById('pdf-toc');
|
|
69
|
+
if (toc && !toc.getAttribute('role')) {
|
|
70
|
+
toc.setAttribute('role', 'navigation');
|
|
71
|
+
toc.setAttribute('aria-label', 'Table of Contents');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Tag tables for accessibility
|
|
75
|
+
document.querySelectorAll('table').forEach(table => {
|
|
76
|
+
if (!table.getAttribute('role')) {
|
|
77
|
+
table.setAttribute('role', 'table');
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// Add alt text to images if missing
|
|
82
|
+
document.querySelectorAll('img').forEach(img => {
|
|
83
|
+
if (!img.getAttribute('alt')) {
|
|
84
|
+
img.setAttribute('alt', img.getAttribute('title') || 'Image');
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Creates table of contents for PDF
|
|
92
|
+
*/
|
|
93
|
+
async function createTOCIfNeeded(page, logo, logoLink, title, description) {
|
|
94
|
+
await page.evaluate((logo, logoLink, title, description) => {
|
|
95
|
+
const titleWrapper = document.createElement('div');
|
|
96
|
+
titleWrapper.className = 'text-center mb-5 pb-4 border-bottom';
|
|
97
|
+
|
|
98
|
+
if (logo) {
|
|
99
|
+
const logoContainer = document.createElement('a');
|
|
100
|
+
logoContainer.href = logoLink;
|
|
101
|
+
logoContainer.className = 'd-block mb-3';
|
|
102
|
+
const logoImg = document.createElement('img');
|
|
103
|
+
logoImg.src = logo;
|
|
104
|
+
logoImg.className = 'img-fluid';
|
|
105
|
+
logoContainer.appendChild(logoImg);
|
|
106
|
+
titleWrapper.appendChild(logoContainer);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (title) {
|
|
110
|
+
const titleElement = document.createElement('h1');
|
|
111
|
+
titleElement.textContent = title;
|
|
112
|
+
titleElement.className = 'display-4 mb-2 pdf-title';
|
|
113
|
+
titleWrapper.appendChild(titleElement);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (description) {
|
|
117
|
+
const descriptionElement = document.createElement('p');
|
|
118
|
+
descriptionElement.textContent = description;
|
|
119
|
+
descriptionElement.className = 'lead mb-0';
|
|
120
|
+
titleWrapper.appendChild(descriptionElement);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
document.body.insertBefore(titleWrapper, document.body.firstChild);
|
|
124
|
+
|
|
125
|
+
// Create a Table of Contents if it doesn't exist
|
|
126
|
+
if (!document.getElementById('toc')) {
|
|
127
|
+
// Generate a TOC based on the headings in the document
|
|
128
|
+
const headings = Array.from(document.querySelectorAll('h1:not(.pdf-title), h2, h3, h4, h5, h6')).filter(h => {
|
|
129
|
+
// Only include headings with IDs that can be linked to
|
|
130
|
+
return h.id && h.id.trim() !== '';
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
if (headings.length > 0) {
|
|
134
|
+
// Create TOC container
|
|
135
|
+
const tocContainer = document.createElement('div');
|
|
136
|
+
tocContainer.id = 'toc';
|
|
137
|
+
tocContainer.className = 'toc-container';
|
|
138
|
+
|
|
139
|
+
// Create TOC heading
|
|
140
|
+
const tocHeading = document.createElement('h2');
|
|
141
|
+
tocHeading.textContent = 'Contents';
|
|
142
|
+
tocHeading.className = 'toc-heading';
|
|
143
|
+
tocContainer.appendChild(tocHeading);
|
|
144
|
+
|
|
145
|
+
// Create TOC list
|
|
146
|
+
const tocList = document.createElement('ul');
|
|
147
|
+
tocList.className = 'toc-list';
|
|
148
|
+
tocContainer.appendChild(tocList);
|
|
149
|
+
|
|
150
|
+
// Add all headings to the TOC
|
|
151
|
+
let currentLevel = 0;
|
|
152
|
+
let currentList = tocList;
|
|
153
|
+
let listStack = [tocList];
|
|
154
|
+
|
|
155
|
+
headings.forEach(heading => {
|
|
156
|
+
// Get heading level (1-6 for h1-h6)
|
|
157
|
+
const level = parseInt(heading.tagName[1]);
|
|
158
|
+
|
|
159
|
+
// Navigate to the correct nesting level
|
|
160
|
+
if (level > currentLevel) {
|
|
161
|
+
// Go deeper - create a new nested list
|
|
162
|
+
for (let i = currentLevel; i < level; i++) {
|
|
163
|
+
if (currentList.lastChild) {
|
|
164
|
+
const nestedList = document.createElement('ul');
|
|
165
|
+
currentList.lastChild.appendChild(nestedList);
|
|
166
|
+
listStack.push(nestedList);
|
|
167
|
+
currentList = nestedList;
|
|
168
|
+
} else {
|
|
169
|
+
// If no items exist yet, add a dummy item
|
|
170
|
+
const dummyItem = document.createElement('li');
|
|
171
|
+
const nestedList = document.createElement('ul');
|
|
172
|
+
dummyItem.appendChild(nestedList);
|
|
173
|
+
currentList.appendChild(dummyItem);
|
|
174
|
+
listStack.push(nestedList);
|
|
175
|
+
currentList = nestedList;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
} else if (level < currentLevel) {
|
|
179
|
+
// Go up - pop from the stack
|
|
180
|
+
for (let i = currentLevel; i > level; i--) {
|
|
181
|
+
listStack.pop();
|
|
182
|
+
}
|
|
183
|
+
currentList = listStack[listStack.length - 1];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
currentLevel = level;
|
|
187
|
+
|
|
188
|
+
// Create list item with link
|
|
189
|
+
const listItem = document.createElement('li');
|
|
190
|
+
const link = document.createElement('a');
|
|
191
|
+
link.href = '#' + heading.id;
|
|
192
|
+
link.textContent = heading.textContent;
|
|
193
|
+
listItem.appendChild(link);
|
|
194
|
+
currentList.appendChild(listItem);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// Insert the TOC after the title section
|
|
198
|
+
document.body.insertBefore(tocContainer, titleWrapper.nextSibling);
|
|
199
|
+
|
|
200
|
+
console.log('Generated a Table of Contents with ' + headings.length + ' entries.');
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}, logo, logoLink, title, description);
|
|
204
|
+
}
|
|
205
|
+
|
|
6
206
|
(async () => {
|
|
7
207
|
try {
|
|
8
|
-
// Launch a new browser instance
|
|
9
|
-
const browser = await puppeteer.launch(
|
|
208
|
+
// Launch a new browser instance with ISO-compliant settings
|
|
209
|
+
const browser = await puppeteer.launch({
|
|
210
|
+
args: ['--disable-web-security', '--allow-running-insecure-content']
|
|
211
|
+
});
|
|
10
212
|
const page = await browser.newPage();
|
|
11
213
|
|
|
214
|
+
// Configure page for ISO compliance
|
|
215
|
+
await configurePageForISO(page);
|
|
216
|
+
|
|
12
217
|
// Read and parse the specs.json file
|
|
13
218
|
const config = fs.readJsonSync('specs.json');
|
|
219
|
+
const metadata = createISOMetadata(config);
|
|
14
220
|
|
|
15
221
|
// Extract configuration details
|
|
16
222
|
const outputPath = config.specs[0].output_path;
|
|
@@ -36,6 +242,9 @@ const pdfLib = require('pdf-lib');
|
|
|
36
242
|
// Navigate to the HTML file
|
|
37
243
|
await page.goto(fileUrl, { waitUntil: 'networkidle2' });
|
|
38
244
|
|
|
245
|
+
// Apply accessibility tags for PDF/UA compliance
|
|
246
|
+
await applyAccessibilityTags(page);
|
|
247
|
+
|
|
39
248
|
// Clean up unnecessary elements but be careful not to remove styles we need
|
|
40
249
|
await page.evaluate(() => {
|
|
41
250
|
// Preserve TOC if it exists (skip removing it even if it has display:none)
|
|
@@ -131,116 +340,7 @@ const pdfLib = require('pdf-lib');
|
|
|
131
340
|
});
|
|
132
341
|
|
|
133
342
|
// Inject logo, title, and description AND handle TOC creation if needed
|
|
134
|
-
await page
|
|
135
|
-
const titleWrapper = document.createElement('div');
|
|
136
|
-
titleWrapper.className = 'text-center mb-5 pb-4 border-bottom';
|
|
137
|
-
|
|
138
|
-
if (logo) {
|
|
139
|
-
const logoContainer = document.createElement('a');
|
|
140
|
-
logoContainer.href = logoLink;
|
|
141
|
-
logoContainer.className = 'd-block mb-3';
|
|
142
|
-
const logoImg = document.createElement('img');
|
|
143
|
-
logoImg.src = logo;
|
|
144
|
-
logoImg.className = 'img-fluid';
|
|
145
|
-
logoContainer.appendChild(logoImg);
|
|
146
|
-
titleWrapper.appendChild(logoContainer);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (title) {
|
|
150
|
-
const titleElement = document.createElement('h1');
|
|
151
|
-
titleElement.textContent = title;
|
|
152
|
-
titleElement.className = 'display-4 mb-2 pdf-title';
|
|
153
|
-
titleWrapper.appendChild(titleElement);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (description) {
|
|
157
|
-
const descriptionElement = document.createElement('p');
|
|
158
|
-
descriptionElement.textContent = description;
|
|
159
|
-
descriptionElement.className = 'lead mb-0';
|
|
160
|
-
titleWrapper.appendChild(descriptionElement);
|
|
161
|
-
}
|
|
162
|
-
|
|
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
|
-
}
|
|
243
|
-
}, logo, logoLink, title, description);
|
|
343
|
+
await createTOCIfNeeded(page, logo, logoLink, title, description);
|
|
244
344
|
|
|
245
345
|
// Direct manipulation of definition lists and TOC to ensure proper styling in PDF
|
|
246
346
|
await page.evaluate(() => {
|
|
@@ -542,16 +642,37 @@ const pdfLib = require('pdf-lib');
|
|
|
542
642
|
|
|
543
643
|
await browser.close();
|
|
544
644
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
645
|
+
console.log('✅ PDF generated by Puppeteer. Processing with pdf-lib for ISO compliance...');
|
|
646
|
+
|
|
647
|
+
// Optimize PDF with pdf-lib for ISO compliance
|
|
648
|
+
try {
|
|
649
|
+
const pdfDoc = await pdfLib.PDFDocument.load(pdfBuffer);
|
|
650
|
+
|
|
651
|
+
// Set ISO-compliant metadata (this is safer than XMP embedding)
|
|
652
|
+
pdfDoc.setTitle(metadata.title);
|
|
653
|
+
pdfDoc.setAuthor(metadata.author);
|
|
654
|
+
pdfDoc.setSubject(metadata.subject);
|
|
655
|
+
pdfDoc.setKeywords(metadata.keywords);
|
|
656
|
+
pdfDoc.setProducer(metadata.producer);
|
|
657
|
+
pdfDoc.setCreator(metadata.creator);
|
|
658
|
+
pdfDoc.setCreationDate(metadata.creationDate);
|
|
659
|
+
pdfDoc.setModificationDate(metadata.modificationDate);
|
|
660
|
+
|
|
661
|
+
console.log('✅ ISO metadata applied successfully.');
|
|
662
|
+
|
|
663
|
+
// Save with conservative settings to ensure compatibility
|
|
664
|
+
const optimizedPdfBytes = await pdfDoc.save({
|
|
665
|
+
useObjectStreams: false, // Required for PDF/A compliance
|
|
666
|
+
addDefaultPage: false
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
fs.writeFileSync('docs/index.pdf', optimizedPdfBytes);
|
|
670
|
+
console.log('✅ PDF saved with ISO compliance features.');
|
|
671
|
+
} catch (pdfError) {
|
|
672
|
+
console.warn('⚠️ Warning: Could not apply ISO metadata, saving original PDF:', pdfError.message);
|
|
673
|
+
// Fallback: save the original PDF if post-processing fails
|
|
674
|
+
fs.writeFileSync('docs/index.pdf', pdfBuffer);
|
|
675
|
+
}
|
|
555
676
|
|
|
556
677
|
console.log('✅ PDF generated successfully! Find the PDF in the docs directory.');
|
|
557
678
|
} catch (error) {
|
|
@@ -3,7 +3,24 @@ const path = require('path');
|
|
|
3
3
|
const { shouldProcessFile } = require('./utils/file-filter');
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* Checks if a term has a definition starting from the given line index
|
|
7
|
+
* @param {string[]} lines - Array of file lines
|
|
8
|
+
* @param {number} startIndex - Index to start checking from
|
|
9
|
+
* @returns {boolean} - True if definition exists, false otherwise
|
|
10
|
+
*/
|
|
11
|
+
function hasDefinition(lines, startIndex) {
|
|
12
|
+
for (let i = startIndex; i < lines.length; i++) {
|
|
13
|
+
const line = lines[i].trim();
|
|
14
|
+
if (line === '') continue; // Skip empty lines
|
|
15
|
+
if (line.startsWith('~')) return true; // Found definition
|
|
16
|
+
if (line.startsWith('[[def:') || line.startsWith('[[tref:')) return false; // Found next term
|
|
17
|
+
if (line.length > 0) return false; // Found other content
|
|
18
|
+
}
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Handles specific functionality for `[[def:` and `[[tref:` lines
|
|
7
24
|
* @param {string[]} lines - Array of file lines
|
|
8
25
|
* @returns {object} - Object containing modified lines and modification status
|
|
9
26
|
*/
|
|
@@ -12,10 +29,19 @@ function processDefLines(lines) {
|
|
|
12
29
|
let modified = false;
|
|
13
30
|
|
|
14
31
|
for (let i = 0; i < result.length; i++) {
|
|
15
|
-
if (result[i].startsWith('[[def:')) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
32
|
+
if (result[i].startsWith('[[def:') || result[i].startsWith('[[tref:')) {
|
|
33
|
+
let insertIndex = i + 1;
|
|
34
|
+
|
|
35
|
+
// Ensure a blank line immediately follows `[[def:` and `[[tref:` lines
|
|
36
|
+
if (insertIndex < result.length && result[insertIndex].trim() !== '') {
|
|
37
|
+
result.splice(insertIndex, 0, ''); // Insert blank line
|
|
38
|
+
insertIndex++;
|
|
39
|
+
modified = true;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Check if term has a definition
|
|
43
|
+
if (!hasDefinition(result, insertIndex)) {
|
|
44
|
+
result.splice(insertIndex, 0, '', '~ No local definition found.', '');
|
|
19
45
|
modified = true;
|
|
20
46
|
}
|
|
21
47
|
}
|
|
@@ -51,31 +77,6 @@ function normalizeParagraphSpacing(lines) {
|
|
|
51
77
|
return { lines: newLines, modified };
|
|
52
78
|
}
|
|
53
79
|
|
|
54
|
-
/**
|
|
55
|
-
* Prepends `~ ` to appropriate lines
|
|
56
|
-
* @param {string[]} lines - Array of file lines
|
|
57
|
-
* @returns {object} - Object containing modified lines and modification status
|
|
58
|
-
*/
|
|
59
|
-
function prependTildeToLines(lines) {
|
|
60
|
-
const result = [...lines];
|
|
61
|
-
let modified = false;
|
|
62
|
-
|
|
63
|
-
for (let i = 0; i < result.length; i++) {
|
|
64
|
-
if (
|
|
65
|
-
!result[i].startsWith('[[def:') &&
|
|
66
|
-
!result[i].startsWith('[[tref:') &&
|
|
67
|
-
result[i].trim() !== '' &&
|
|
68
|
-
!result[i].startsWith('~ ') &&
|
|
69
|
-
!result[i].trim().startsWith('<!--')
|
|
70
|
-
) {
|
|
71
|
-
result[i] = `~ ${result[i]}`;
|
|
72
|
-
modified = true;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return { lines: result, modified };
|
|
77
|
-
}
|
|
78
|
-
|
|
79
80
|
/**
|
|
80
81
|
* Ensures there is exactly one blank line at the end of the file
|
|
81
82
|
* @param {string[]} lines - Array of file lines
|
|
@@ -116,10 +117,6 @@ function processMarkdownFile(filePath, fileName) {
|
|
|
116
117
|
lines = spacingResult.lines;
|
|
117
118
|
modified = modified || spacingResult.modified;
|
|
118
119
|
|
|
119
|
-
const tildeResult = prependTildeToLines(lines);
|
|
120
|
-
lines = tildeResult.lines;
|
|
121
|
-
modified = modified || tildeResult.modified;
|
|
122
|
-
|
|
123
120
|
const newlineResult = ensureTrailingNewline(lines);
|
|
124
121
|
lines = newlineResult.lines;
|
|
125
122
|
modified = modified || newlineResult.modified;
|