spec-up-t 1.2.0 → 1.2.2

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/index.new.js DELETED
@@ -1,662 +0,0 @@
1
- const { initialize } = require('./src/init.js');
2
- const { fetchExternalTerms } = require('./src/utils/fetch.js');
3
-
4
- module.exports = async function (options = {}) {
5
- try {
6
- await initialize();
7
-
8
- const fs = require('fs-extra');
9
- const path = require('path');
10
- const gulp = require('gulp');
11
-
12
- const {
13
- fetchExternalSpecs,
14
- validateReferences,
15
- findExternalSpecByKey
16
- } = require('./src/references.js');
17
-
18
- const { runJsonKeyValidatorSync } = require('./src/json-key-validator.js');
19
- runJsonKeyValidatorSync();
20
-
21
- // const { createTermRelations } = require('./src/create-term-relations.js');
22
- // createTermRelations();
23
-
24
- const { createTermIndex } = require('./src/create-term-index.js');
25
- createTermIndex();
26
-
27
- const { insertTermIndex } = require('./src/insert-term-index.js');
28
- insertTermIndex();
29
-
30
- const findPkgDir = require('find-pkg-dir');
31
- const modulePath = findPkgDir(__dirname);
32
- let config = fs.readJsonSync('./output/specs-generated.json');
33
-
34
- const externalTerms = fetchExternalTerms();
35
-
36
- const createExternalSpecsList = require('./src/create-external-specs-list.js');
37
-
38
- const externalSpecsList = createExternalSpecsList(config);
39
-
40
- const createVersionsIndex = require('./src/create-versions-index.js');
41
- createVersionsIndex(config.specs[0].output_path);
42
-
43
- const { fixMarkdownFiles } = require('./src/fix-markdown-files.js');
44
-
45
- // const { prepareTref } = require('./src/prepare-tref.js');
46
-
47
- let template = fs.readFileSync(path.join(modulePath, 'templates/template.html'), 'utf8');
48
- let assets = fs.readJsonSync(modulePath + '/src/asset-map.json');
49
- let externalReferences;
50
- let references = [];
51
- let definitions = [];
52
- var toc;
53
- var specGroups = {};
54
- var noticeTitles = {};
55
-
56
- const noticeTypes = {
57
- note: 1,
58
- issue: 1,
59
- example: 1,
60
- warning: 1,
61
- todo: 1
62
- };
63
- const spaceRegex = /\s+/g;
64
- const specNameRegex = /^spec$|^spec[-]*\w+$/i;
65
- const terminologyRegex = /^def$|^ref$|^xref|^tref$/i;
66
- const specCorpus = fs.readJsonSync(modulePath + '/assets/compiled/refs.json');
67
- const containers = require('markdown-it-container');
68
-
69
- /*
70
- `const md` is assigned an instance of the markdown-it parser configured with various plugins and extensions. This instance (md) is intended to be used later to parse and render Markdown strings.
71
-
72
- The md function (which is an instance of the markdown-it parser) takes a Markdown string as its primary argument. It is called elsewhere as follows: `md.render(doc)`
73
- */
74
- const md = require('markdown-it')({
75
- html: true,
76
- linkify: true,
77
- typographer: true
78
- })
79
- .use(require('./src/markdown-it-extensions.js'), [
80
- {
81
- filter: type => type.match(terminologyRegex),
82
- parse(token, type, primary) {
83
- if (!primary) return;
84
- if (type === 'def') {
85
- definitions.push(token.info.args);
86
- return token.info.args.reduce((acc, syn) => {
87
- return `<span id="term:${syn.replace(spaceRegex, '-').toLowerCase()}">${acc}</span>`;
88
- }, primary);
89
- }
90
- else if (type === 'xref') {
91
- // Get the URL for the external specification reference, or default to '#' if not found
92
- const externalSpec = findExternalSpecByKey(config, token.info.args[0]);
93
- const url = externalSpec?.gh_page || '#';
94
-
95
- const term = token.info.args[1].replace(spaceRegex, '-').toLowerCase();
96
-
97
- // Find the commit hash in externalTerms if available
98
- let commitHash = '';
99
- if (externalTerms && externalTerms.xtrefs) {
100
- const xtref = externalTerms.xtrefs.find(x =>
101
- x.externalSpec === token.info.args[0] &&
102
- x.term === token.info.args[1]
103
- );
104
- if (xtref && xtref.commitHash && xtref.commitHash !== 'not found') {
105
- commitHash = xtref.commitHash;
106
- }
107
- }
108
-
109
- // Add commit hash to URL if available
110
- const hrefUrl = commitHash ? `${url.replace(/\/$/, '')}/tree/${commitHash}#term:${term}` : `${url}#term:${term}`;
111
-
112
- return `<a class="x-term-reference term-reference" data-local-href="#term:${token.info.args[0]}:${term}"
113
- href="${hrefUrl}">${token.info.args[1]}</a>`;
114
- }
115
- else if (type === 'tref') {
116
- return `<span class="transcluded-xref-term" id="term:${token.info.args[1]}">${token.info.args[1]}</span>`;
117
- }
118
- else {
119
- references.push(primary);
120
- return `<a class="term-reference" href="#term:${primary.replace(spaceRegex, '-').toLowerCase()}">${primary}</a>`;
121
- }
122
- }
123
- },
124
- {
125
- filter: type => type.match(specNameRegex),
126
- parse(token, type, name) {
127
- if (name) {
128
- let _name = name.replace(spaceRegex, '-').toUpperCase();
129
- let spec = specCorpus[_name] ||
130
- specCorpus[_name.toLowerCase()] ||
131
- specCorpus[name.toLowerCase()] ||
132
- specCorpus[name];
133
- if (spec) {
134
- spec._name = _name;
135
- let group = specGroups[type] = specGroups[type] || {};
136
- token.info.spec = group[_name] = spec;
137
- }
138
- }
139
- },
140
- render(token, type, name) {
141
- if (name) {
142
- let spec = token.info.spec;
143
- if (spec) return `[<a class="spec-reference" href="#ref:${spec._name}">${spec._name}</a>]`;
144
- }
145
- else return renderRefGroup(type);
146
- }
147
- }
148
- ])
149
- .use(require('markdown-it-attrs'))
150
- .use(require('markdown-it-chart').default)
151
- .use(require('markdown-it-deflist'))
152
- .use(require('markdown-it-references'))
153
- .use(require('markdown-it-icons').default, 'font-awesome')
154
- .use(require('markdown-it-ins'))
155
- .use(require('markdown-it-mark'))
156
- .use(require('markdown-it-textual-uml'))
157
- .use(require('markdown-it-sub'))
158
- .use(require('markdown-it-sup'))
159
- .use(require('markdown-it-task-lists'))
160
- .use(require('markdown-it-multimd-table'), {
161
- multiline: true,
162
- rowspan: true,
163
- headerless: true
164
- })
165
- .use(containers, 'notice', {
166
- validate: function (params) {
167
- let matches = params.match(/(\w+)\s?(.*)?/);
168
- return matches && noticeTypes[matches[1]];
169
- },
170
- render: function (tokens, idx) {
171
- let matches = tokens[idx].info.match(/(\w+)\s?(.*)?/);
172
- if (matches && tokens[idx].nesting === 1) {
173
- let id;
174
- let type = matches[1];
175
- if (matches[2]) {
176
- id = matches[2].trim().replace(/\s+/g, '-').toLowerCase();
177
- if (noticeTitles[id]) id += '-' + noticeTitles[id]++;
178
- else noticeTitles[id] = 1;
179
- }
180
- else id = type + '-' + noticeTypes[type]++;
181
- return `<div id="${id}" class="notice ${type}"><a class="notice-link" href="#${id}">${type.toUpperCase()}</a>`;
182
- }
183
- else return '</div>\n';
184
- }
185
- })
186
- .use(require('markdown-it-prism'))
187
- .use(require('markdown-it-toc-and-anchor').default, {
188
- tocClassName: 'toc',
189
- tocFirstLevel: 2,
190
- tocLastLevel: 4,
191
- tocCallback: (_md, _tokens, html) => toc = html,
192
- anchorLinkSymbol: '#', // was: §
193
- anchorClassName: 'toc-anchor d-print-none'
194
- })
195
- .use(require('@traptitech/markdown-it-katex'))
196
-
197
- const katexRules = ['math_block', 'math_inline'];
198
- const replacerRegex = /\[\[\s*([^\s\[\]:]+):?\s*([^\]\n]+)?\]\]/img;
199
- const replacerArgsRegex = /\s*,+\s*/;
200
- const replacers = [
201
- {
202
- test: 'insert',
203
- transform: function (originalMatch, type, path) {
204
- if (!path) return '';
205
- return fs.readFileSync(path, 'utf8');
206
- }
207
- },
208
- /**
209
- * Custom replacer for tref tags that converts them directly to HTML definition term elements.
210
- *
211
- * This is a critical part of our solution for fixing transcluded terms in definition lists.
212
- * When a [[tref:spec,term]] tag is found in the markdown, this replacer transforms it into
213
- * a proper <dt> element with the appropriate structure before the markdown parser processes it.
214
- *
215
- * By directly generating the HTML structure (instead of letting the markdown-it parser
216
- * handle it later), we prevent the issue where transcluded terms break the definition list.
217
- *
218
- * @param {string} originalMatch - The original [[tref:spec,term]] tag found in the markdown
219
- * @param {string} type - The tag type ('tref')
220
- * @param {string} spec - The specification identifier (e.g., 'wot-1')
221
- * @param {string} term - The term to transclude (e.g., 'DAR')
222
- * @returns {string} - HTML representation of the term as a dt element
223
- */
224
- {
225
- test: 'tref',
226
- transform: function (originalMatch, type, spec, term) {
227
- return `<dt class="transcluded-xref-term"><span class="transcluded-xref-term" id="term:${term.replace(/\s+/g, '-').toLowerCase()}">${term}</span></dt>`;
228
- }
229
- }
230
- ];
231
-
232
- // prepareTref(path.join(config.specs[0].spec_directory, config.specs[0].spec_terms_directory));
233
-
234
- // Synchronously process markdown files
235
- fixMarkdownFiles(path.join(config.specs[0].spec_directory, config.specs[0].spec_terms_directory));
236
-
237
- function createScriptElementWithXTrefDataForEmbeddingInHtml() {
238
- // Test if xtrefs-data.js exists, else make it an empty string
239
- const inputPath = path.join('output', 'xtrefs-data.js');
240
-
241
- let xtrefsData = '';
242
- if (fs.existsSync(inputPath)) {
243
- xtrefsData = '<script>' + fs.readFileSync(inputPath, 'utf8') + '</script>';
244
- }
245
-
246
- return xtrefsData;
247
- }
248
-
249
- const xtrefsData = createScriptElementWithXTrefDataForEmbeddingInHtml();
250
-
251
- /**
252
- * Processes custom tag patterns in markdown content and applies transformation functions.
253
- *
254
- * This function scans the document for special tag patterns like [[tref:spec,term]]
255
- * and replaces them with the appropriate HTML using the matching replacer.
256
- *
257
- * For tref tags, this is where the magic happens - we intercept them before
258
- * the markdown parser even sees them, and convert them directly to HTML structure
259
- * that will integrate properly with definition lists.
260
- *
261
- * @param {string} doc - The markdown document to process
262
- * @returns {string} - The processed document with tags replaced by their HTML equivalents
263
- */
264
- function applyReplacers(doc) {
265
- return doc.replace(replacerRegex, function (match, type, args) {
266
- let replacer = replacers.find(r => type.trim().match(r.test));
267
- if (replacer) {
268
- let argsArray = args ? args.trim().split(replacerArgsRegex) : [];
269
- return replacer.transform(match, type, ...argsArray);
270
- }
271
- return match;
272
- });
273
- }
274
-
275
- function normalizePath(path) {
276
- return path.trim().replace(/\/$/g, '') + '/';
277
- }
278
-
279
- function renderRefGroup(type) {
280
- let group = specGroups[type];
281
- if (!group) return '';
282
-
283
- /*
284
- The key advantage of localeCompare over simple comparison operators (<, >) is that it:
285
-
286
- - Properly handles language-specific sorting rules (via locale settings)
287
- - Correctly compares strings containing special characters or accents
288
- - Can be configured to be case-insensitive
289
- */
290
- let html = Object.keys(group).sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())).reduce((html, name) => {
291
- let ref = group[name];
292
- return html += `
293
- <dt id="ref:${name}">${name}</dt>
294
- <dd>
295
- <cite><a href="${ref.href}">${ref.title}</a></cite>.
296
- ${ref.authors.join('; ')}; ${ref.rawDate}. <span class="reference-status">Status: ${ref.status}</span>.
297
- </dd>
298
- `;
299
- }, '<dl class="reference-list">');
300
- return `\n${html}\n</dl>\n`;
301
- }
302
-
303
- function findKatexDist() {
304
- const relpath = "node_modules/katex/dist";
305
- const paths = [
306
- path.join(process.cwd(), relpath),
307
- path.join(__dirname, relpath),
308
- ];
309
- for (const abspath of paths) {
310
- if (fs.existsSync(abspath)) {
311
- return abspath
312
- }
313
- }
314
- throw Error("katex distribution could not be located");
315
- }
316
-
317
- function sortDefinitionTermsInHtml(html) {
318
- const { JSDOM } = require('jsdom');
319
- const dom = new JSDOM(html);
320
- const document = dom.window.document;
321
-
322
- // Find the terms and definitions list
323
- const dlElement = document.querySelector('.terms-and-definitions-list');
324
- if (!dlElement) return html; // If not found, return the original HTML
325
-
326
- // Collect all dt/dd pairs
327
- const pairs = [];
328
- let currentDt = null;
329
- let currentDds = [];
330
-
331
- // Process each child of the dl element
332
- Array.from(dlElement.children).forEach(child => {
333
- if (child.tagName === 'DT') {
334
- // If we already have a dt, save the current pair
335
- if (currentDt) {
336
- pairs.push({
337
- dt: currentDt,
338
- dds: [...currentDds],
339
- text: currentDt.textContent.trim().toLowerCase() // Use lowercase for sorting
340
- });
341
- currentDds = []; // Reset dds for the next dt
342
- }
343
- currentDt = child;
344
- } else if (child.tagName === 'DD' && currentDt) {
345
- currentDds.push(child);
346
- }
347
- });
348
-
349
- // Add the last pair if exists
350
- if (currentDt) {
351
- pairs.push({
352
- dt: currentDt,
353
- dds: [...currentDds],
354
- text: currentDt.textContent.trim().toLowerCase()
355
- });
356
- }
357
-
358
- // Sort pairs case-insensitively
359
- pairs.sort((a, b) => a.text.localeCompare(b.text));
360
-
361
- // Clear the dl element
362
- while (dlElement.firstChild) {
363
- dlElement.removeChild(dlElement.firstChild);
364
- }
365
-
366
- // Re-append elements in sorted order
367
- pairs.forEach(pair => {
368
- dlElement.appendChild(pair.dt);
369
- pair.dds.forEach(dd => {
370
- dlElement.appendChild(dd);
371
- });
372
- });
373
-
374
- // Return the modified HTML
375
- return dom.serialize();
376
- }
377
-
378
- // Function to fix broken definition list structures
379
- /**
380
- * This function repairs broken definition list (dl) structures in the HTML output.
381
- * Specifically, it addresses the issue where transcluded terms (tref tags) break
382
- * out of the definition list, creating separate lists instead of a continuous one.
383
- *
384
- * The strategy:
385
- * 1. Find all definition lists (dl elements) in the document
386
- * 2. Use the dl with class 'terms-and-definitions-list' as the main/target list
387
- * 3. Process each subsequent node after the this main dl:
388
- * - If another dl is found, merge all its children into the main dl
389
- * - If a standalone dt is found, move it into the main dl
390
- * - Remove any empty paragraphs that might be breaking the list continuity
391
- *
392
- * This ensures all terms appear in one continuous definition list,
393
- * regardless of how they were originally rendered in the markdown.
394
- *
395
- * @param {string} html - The HTML content to fix
396
- * @returns {string} - The fixed HTML content with merged definition lists
397
- */
398
- function fixDefinitionListStructure(html) {
399
- const { JSDOM } = require('jsdom');
400
- const dom = new JSDOM(html);
401
- const document = dom.window.document;
402
-
403
- // Find all dl elements first
404
- const allDls = Array.from(document.querySelectorAll('dl'));
405
-
406
- // Then filter to find the one with the terms-and-definitions-list class
407
- const dlElements = allDls.filter(dl => {
408
- return dl.classList && dl.classList.contains('terms-and-definitions-list');
409
- });
410
-
411
- // First special case - handle transcluded-xref-term dt that comes BEFORE the main dl
412
- const transcludedTermsBeforeMainDl = document.querySelectorAll('dt.transcluded-xref-term');
413
- let mainDl = null;
414
-
415
- if (dlElements.length > 0) {
416
- // Use the first terms-and-definitions-list as our main container
417
- mainDl = dlElements[0];
418
-
419
- // Special handling for transcluded terms that appear BEFORE the main dl
420
- transcludedTermsBeforeMainDl.forEach(dt => {
421
- // Check if this dt is not already inside a dl.terms-and-definitions-list
422
- if (!dt.parentElement.classList.contains('terms-and-definitions-list')) {
423
- // This is a dt outside our main list - move it into the main dl at the beginning
424
- const dtClone = dt.cloneNode(true);
425
- mainDl.insertBefore(dtClone, mainDl.firstChild);
426
- dt.parentNode.removeChild(dt);
427
- }
428
- });
429
-
430
- // Remove any empty dt elements that may exist
431
- const emptyDts = mainDl.querySelectorAll('dt:empty');
432
- emptyDts.forEach(emptyDt => {
433
- emptyDt.parentNode.removeChild(emptyDt);
434
- });
435
-
436
- // Process all subsequent content after the main dl
437
- let currentNode = mainDl.nextSibling;
438
-
439
- // Process all subsequent content
440
- while (currentNode) {
441
- // Save the next node before potentially modifying the DOM
442
- // (This is important because modifying the DOM can invalidate our references)
443
- const nextNode = currentNode.nextSibling;
444
-
445
- // Handle different node types
446
- if (currentNode.nodeType === 1) { // 1 = Element node
447
- if (currentNode.tagName === 'DL') {
448
- // Found another definition list - move all its children to the main dl
449
- // This effectively merges the two lists into one
450
- while (currentNode.firstChild) {
451
- mainDl.appendChild(currentNode.firstChild);
452
- }
453
-
454
- // Remove the now-empty dl element
455
- currentNode.parentNode.removeChild(currentNode);
456
- }
457
- else if (currentNode.tagName === 'DT') {
458
- // Found a standalone dt (like our transcluded tref terms)
459
- // Move it into the main dl to maintain continuity
460
- const dtClone = currentNode.cloneNode(true);
461
- mainDl.appendChild(dtClone);
462
- currentNode.parentNode.removeChild(currentNode);
463
- }
464
- else if (currentNode.tagName === 'P' &&
465
- (!currentNode.textContent || currentNode.textContent.trim() === '')) {
466
- // Remove empty paragraphs - these break the list structure
467
- // Empty <p></p> tags often appear between dl elements
468
- currentNode.parentNode.removeChild(currentNode);
469
- }
470
- }
471
-
472
- // Move to the next node we saved earlier
473
- currentNode = nextNode;
474
- }
475
- }
476
-
477
- // Return the fixed HTML
478
- return dom.serialize();
479
- }
480
-
481
- async function render(spec, assets) {
482
- try {
483
- noticeTitles = {};
484
- specGroups = {};
485
- console.log('ℹ️ Rendering: ' + spec.title);
486
-
487
- function interpolate(template, variables) {
488
- return template.replace(/\${(.*?)}/g, (match, p1) => variables[p1.trim()]);
489
- }
490
-
491
- const docs = await Promise.all(
492
- (spec.markdown_paths || ['spec.md']).map(_path =>
493
- fs.readFile(spec.spec_directory + _path, 'utf8')
494
- )
495
- );
496
-
497
- const features = (({ source, logo }) => ({ source, logo }))(spec);
498
- if (spec.external_specs && !externalReferences) {
499
- externalReferences = await fetchExternalSpecs(spec);
500
- }
501
-
502
- // Find the index of the terms-and-definitions-intro.md file
503
- const termsIndex = (spec.markdown_paths || ['spec.md']).indexOf('terms-and-definitions-intro.md');
504
- if (termsIndex !== -1) {
505
- // Append the HTML string to the content of terms-and-definitions-intro.md. This string is used to create a div that is used to insert an alphabet index, and a div that is used as the starting point of the terminology index. The newlines are essential for the correct rendering of the markdown.
506
- docs[termsIndex] += '\n\n<div id="terminology-section-start-h7vc6omi2hr2880"></div>\n\n';
507
- }
508
-
509
- let doc = docs.join("\n");
510
-
511
- // `doc` is markdown
512
- doc = applyReplacers(doc);
513
-
514
- md[spec.katex ? "enable" : "disable"](katexRules);
515
-
516
- // `render` is the rendered HTML
517
- let renderedHtml = md.render(doc);
518
-
519
- // Apply the fix for broken definition list structures
520
- renderedHtml = fixDefinitionListStructure(renderedHtml);
521
-
522
- // Sort definition terms case-insensitively before final rendering
523
- renderedHtml = sortDefinitionTermsInHtml(renderedHtml);
524
-
525
- const templateInterpolated = interpolate(template, {
526
- title: spec.title,
527
- description: spec.description,
528
- author: spec.author,
529
- toc: toc,
530
- render: renderedHtml,
531
- assetsHead: assets.head,
532
- assetsBody: assets.body,
533
- assetsSvg: assets.svg,
534
- features: Object.keys(features).join(' '),
535
- externalReferences: JSON.stringify(externalReferences),
536
- xtrefsData: xtrefsData,
537
- specLogo: spec.logo,
538
- specFavicon: spec.favicon,
539
- specLogoLink: spec.logo_link,
540
- spec: JSON.stringify(spec),
541
- externalSpecsList: externalSpecsList,
542
- });
543
-
544
- const outputPath = path.join(spec.destination, 'index.html');
545
- console.log('ℹ️ Attempting to write to:', outputPath);
546
-
547
- // Use promisified version instead of callback
548
- await fs.promises.writeFile(outputPath, templateInterpolated, 'utf8');
549
- console.log(`✅ Successfully wrote ${outputPath}`);
550
-
551
- validateReferences(references, definitions, renderedHtml);
552
- references = [];
553
- definitions = [];
554
- } catch (e) {
555
- console.error("❌ Render error: " + e.message);
556
- throw e;
557
- }
558
- }
559
-
560
- try {
561
- config.specs.forEach(spec => {
562
- spec.spec_directory = normalizePath(spec.spec_directory);
563
- spec.destination = normalizePath(spec.output_path || spec.spec_directory);
564
-
565
- if (!fs.existsSync(spec.destination)) {
566
- try {
567
- fs.mkdirSync(spec.destination, { recursive: true });
568
- console.log(`✅ Created directory: ${spec.destination}`);
569
- } catch (error) {
570
- console.error(`❌ Failed to create directory ${spec.destination}: ${error.message}`);
571
- throw error;
572
- }
573
- } else {
574
- console.log(`ℹ️ Directory already exists: ${spec.destination}`);
575
- }
576
-
577
- try {
578
- fs.ensureDirSync(spec.destination);
579
- console.log(`✅ Ensured directory is ready: ${spec.destination}`);
580
- } catch (error) {
581
- console.error(`❌ Failed to ensure directory ${spec.destination}: ${error.message}`);
582
- throw error;
583
- }
584
-
585
- let assetTags = {
586
- svg: fs.readFileSync(modulePath + '/assets/icons.svg', 'utf8') || ''
587
- };
588
-
589
- let customAssets = (spec.assets || []).reduce((assets, asset) => {
590
- let ext = asset.path.split('.').pop();
591
- if (ext === 'css') {
592
- assets.css += `<link href="${asset.path}" rel="stylesheet"/>`;
593
- }
594
- if (ext === 'js') {
595
- assets.js[asset.inject || 'body'] += `<script src="${asset.path}" ${asset.module ? 'type="module"' : ''} ></script>`;
596
- }
597
- return assets;
598
- }, {
599
- css: '',
600
- js: { head: '', body: '' }
601
- });
602
-
603
- if (options.dev) {
604
- assetTags.head = assets.head.css.map(_path => `<link href="${_path}" rel="stylesheet"/>`).join('') +
605
- customAssets.css +
606
- assets.head.js.map(_path => `<script src="${_path}"></script>`).join('') +
607
- customAssets.js.head;
608
- assetTags.body = assets.body.js.map(_path => `<script src="${_path}" data-manual></script>`).join('') +
609
- customAssets.js.body;
610
- }
611
- else {
612
- assetTags.head = `
613
- <style>${fs.readFileSync(modulePath + '/assets/compiled/head.css', 'utf8')}</style>
614
- ${customAssets.css}
615
- <script>${fs.readFileSync(modulePath + '/assets/compiled/head.js', 'utf8')}</script>
616
- ${customAssets.js.head}
617
- `;
618
- assetTags.body = `<script>${fs.readFileSync(modulePath + '/assets/compiled/body.js', 'utf8')}</script>
619
- ${customAssets.js.body}`;
620
- }
621
-
622
- if (spec.katex) {
623
- const katexDist = findKatexDist();
624
- assetTags.body += `<script>/* katex */${fs.readFileSync(path.join(katexDist, 'katex.min.js'),
625
- 'utf8')}</script>`;
626
- assetTags.body += `<style>/* katex */${fs.readFileSync(path.join(katexDist, 'katex.min.css'),
627
- 'utf8')}</style>`;
628
-
629
- fs.copySync(path.join(katexDist, 'fonts'), path.join(spec.destination, 'fonts'));
630
- }
631
-
632
- // Run render and wait for it
633
- render(spec, assetTags)
634
- .then(() => {
635
- console.log('ℹ️ Render completed for:', spec.destination);
636
- if (options.nowatch) {
637
- console.log('ℹ️ Exiting with nowatch');
638
- process.exit(0);
639
- }
640
- })
641
- .catch((e) => {
642
- console.error('❌ Render failed:', e.message);
643
- process.exit(1);
644
- });
645
-
646
- if (!options.nowatch) {
647
- gulp.watch(
648
- [spec.spec_directory + '**/*', '!' + path.join(spec.destination, 'index.html')],
649
- render.bind(null, spec, assetTags)
650
- );
651
- }
652
-
653
- });
654
- } catch (error) {
655
- console.error(`Error during initialization or module execution: ${error.message}`);
656
- throw error; // Re-throw to let the caller handle the error
657
- }
658
- } catch (error) {
659
- console.error(`Error during initialization: ${error.message}`);
660
- throw error; // Re-throw to let the caller handle the error
661
- }
662
- };