spec-up-t 1.0.73 → 1.0.75

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,366 @@
1
+
2
+ module.exports = function (options = {}) {
3
+
4
+ const {
5
+ fetchExternalSpecs,
6
+ validateReferences,
7
+ findExternalSpecByKey
8
+ } = require('./references.js');
9
+ const gulp = require('gulp');
10
+ const fs = require('fs-extra');
11
+ const path = require('path');
12
+ const findPkgDir = require('find-pkg-dir');
13
+ const modulePath = findPkgDir(__dirname);
14
+ let config = fs.readJsonSync('./specs.json');
15
+ let assets = fs.readJsonSync(modulePath + '/src/asset-map.json');
16
+ let externalReferences;
17
+ let references = [];
18
+ let definitions = [];
19
+
20
+ const katexRules = ['math_block', 'math_inline']
21
+ const replacerRegex = /\[\[\s*([^\s\[\]:]+):?\s*([^\]\n]+)?\]\]/img;
22
+ const replacerArgsRegex = /\s*,+\s*/;
23
+ const replacers = [
24
+ {
25
+ test: 'insert',
26
+ transform: function (path) {
27
+ if (!path) return '';
28
+ return fs.readFileSync(path, 'utf8');
29
+ }
30
+ }
31
+ ];
32
+
33
+ function applyReplacers(doc) {
34
+ return doc.replace(replacerRegex, function (match, type, args) {
35
+ let replacer = replacers.find(r => type.trim().match(r.test));
36
+ return replacer ? replacer.transform(...args.trim().split(replacerArgsRegex)) : match;
37
+ });
38
+ }
39
+
40
+ function normalizePath(path) {
41
+ return path.trim().replace(/\/$/g, '') + '/';
42
+ }
43
+
44
+ function renderRefGroup(type) {
45
+ let group = specGroups[type];
46
+ if (!group) return '';
47
+ let html = Object.keys(group).sort().reduce((html, name) => {
48
+ let ref = group[name];
49
+ return html += `
50
+ <dt id="ref:${name}">${name}</dt>
51
+ <dd>
52
+ <cite><a href="${ref.href}">${ref.title}</a></cite>.
53
+ ${ref.authors.join('; ')}; ${ref.rawDate}. <span class="reference-status">Status: ${ref.status}</span>.
54
+ </dd>
55
+ `;
56
+ }, '<dl class="reference-list">')
57
+ return `\n${html}\n</dl>\n`;
58
+ }
59
+
60
+ function findKatexDist() {
61
+ const relpath = "node_modules/katex/dist";
62
+ const paths = [
63
+ path.join(process.cwd(), relpath),
64
+ path.join(__dirname, relpath),
65
+ ];
66
+ for (const abspath of paths) {
67
+ if (fs.existsSync(abspath)) {
68
+ return abspath
69
+ }
70
+ }
71
+ throw Error("katex distribution could not be located");
72
+ }
73
+
74
+ try {
75
+
76
+ var toc;
77
+ var specGroups = {};
78
+ const noticeTypes = {
79
+ note: 1,
80
+ issue: 1,
81
+ example: 1,
82
+ warning: 1,
83
+ todo: 1
84
+ };
85
+ const spaceRegex = /\s+/g;
86
+ const specNameRegex = /^spec$|^spec[-]*\w+$/i;
87
+ const terminologyRegex = /^def$|^ref$|^xref/i;
88
+ const specCorpus = fs.readJsonSync(modulePath + '/assets/compiled/refs.json');
89
+ const containers = require('markdown-it-container');
90
+ const md = require('markdown-it')({
91
+ html: true,
92
+ linkify: true,
93
+ typographer: true
94
+ })
95
+ .use(require('./src/markdown-it-extensions.js'), [
96
+ {
97
+ filter: type => type.match(terminologyRegex),
98
+ parse(token, type, primary) {
99
+ if (!primary) return;
100
+ if (type === 'def') {
101
+ definitions.push(token.info.args);
102
+ return token.info.args.reduce((acc, syn) => {
103
+ return `<span id="term:${syn.replace(spaceRegex, '-').toLowerCase()}">${acc}</span>`;
104
+ }, primary);
105
+ }
106
+ else if (type === 'xref') {
107
+ const url = findExternalSpecByKey(config, token.info.args[0]);
108
+ const term = token.info.args[1].replace(spaceRegex, '-').toLowerCase();
109
+ return `<a class="term-reference" data-local-href="#term:${token.info.args[0]}:${term}"
110
+ href="${url}#term:${term}">${token.info.args[1]}</a>`;
111
+ }
112
+ else {
113
+ references.push(primary);
114
+ return `<a class="term-reference" href="#term:${primary.replace(spaceRegex, '-').toLowerCase()}">${primary}</a>`;
115
+ }
116
+ }
117
+ },
118
+ {
119
+ filter: type => type.match(specNameRegex),
120
+ parse(token, type, name) {
121
+ if (name) {
122
+ let _name = name.replace(spaceRegex, '-').toUpperCase();
123
+ let spec = specCorpus[_name] ||
124
+ specCorpus[_name.toLowerCase()] ||
125
+ specCorpus[name.toLowerCase()] ||
126
+ specCorpus[name];
127
+ if (spec) {
128
+ spec._name = _name;
129
+ let group = specGroups[type] = specGroups[type] || {};
130
+ token.info.spec = group[_name] = spec;
131
+ }
132
+ }
133
+ },
134
+ render(token, type, name) {
135
+ if (name) {
136
+ let spec = token.info.spec;
137
+ if (spec) return `[<a class="spec-reference" href="#ref:${spec._name}">${spec._name}</a>]`;
138
+ }
139
+ else return renderRefGroup(type);
140
+ }
141
+ }
142
+ ])
143
+ .use(require('markdown-it-attrs'))
144
+ .use(require('markdown-it-chart').default)
145
+ .use(require('markdown-it-deflist'))
146
+ .use(require('markdown-it-references'))
147
+ .use(require('markdown-it-icons').default, 'font-awesome')
148
+ .use(require('markdown-it-ins'))
149
+ .use(require('markdown-it-mark'))
150
+ .use(require('markdown-it-textual-uml'))
151
+ .use(require('markdown-it-sub'))
152
+ .use(require('markdown-it-sup'))
153
+ .use(require('markdown-it-task-lists'))
154
+ .use(require('markdown-it-multimd-table'), {
155
+ multiline: true,
156
+ rowspan: true,
157
+ headerless: true
158
+ })
159
+ .use(containers, 'notice', {
160
+ validate: function (params) {
161
+ let matches = params.match(/(\w+)\s?(.*)?/);
162
+ return matches && noticeTypes[matches[1]];
163
+ },
164
+ render: function (tokens, idx) {
165
+ let matches = tokens[idx].info.match(/(\w+)\s?(.*)?/);
166
+ if (matches && tokens[idx].nesting === 1) {
167
+ let id;
168
+ let type = matches[1];
169
+ if (matches[2]) {
170
+ id = matches[2].trim().replace(/\s+/g, '-').toLowerCase();
171
+ if (noticeTitles[id]) id += '-' + noticeTitles[id]++;
172
+ else noticeTitles[id] = 1;
173
+ }
174
+ else id = type + '-' + noticeTypes[type]++;
175
+ return `<div id="${id}" class="notice ${type}"><a class="notice-link" href="#${id}">${type.toUpperCase()}</a>`;
176
+ }
177
+ else return '</div>\n';
178
+ }
179
+ })
180
+ .use(require('markdown-it-prism'))
181
+ .use(require('markdown-it-toc-and-anchor').default, {
182
+ tocClassName: 'toc',
183
+ tocFirstLevel: 2,
184
+ tocLastLevel: 4,
185
+ tocCallback: (_md, _tokens, html) => toc = html,
186
+ anchorLinkSymbol: '§',
187
+ anchorClassName: 'toc-anchor'
188
+ })
189
+ .use(require('@traptitech/markdown-it-katex'))
190
+
191
+ async function render(spec, assets) {
192
+ try {
193
+ noticeTitles = {};
194
+ specGroups = {};
195
+ console.log('Rendering: ' + spec.title);
196
+ return new Promise(async (resolve, reject) => {
197
+ Promise.all((spec.markdown_paths || ['spec.md']).map(_path => {
198
+ return fs.readFile(spec.spec_directory + _path, 'utf8').catch(e => reject(e))
199
+ })).then(async docs => {
200
+ const features = (({ source, logo }) => ({ source, logo }))(spec);
201
+ if (spec.external_specs && !externalReferences) {
202
+ externalReferences = await fetchExternalSpecs(spec);
203
+ }
204
+ let doc = docs.join("\n");
205
+ doc = applyReplacers(doc);
206
+ md[spec.katex ? "enable" : "disable"](katexRules);
207
+ const render = md.render(doc);
208
+ fs.writeFile(path.join(spec.destination, 'index.html'), `
209
+ <!DOCTYPE html>
210
+ <html lang="en">
211
+ <head>
212
+ <meta charset="utf-8">
213
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
214
+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
215
+
216
+ <title>${spec.title}</title>
217
+
218
+ <link href="https://fonts.googleapis.com/css2?family=Heebo:wght@300;400&display=swap" rel="stylesheet">
219
+
220
+ ${assets.head}
221
+ </head>
222
+ <body features="${Object.keys(features).join(' ')}">
223
+
224
+ ${assets.svg}
225
+
226
+ <main>
227
+
228
+ <header id="header" class="panel-header">
229
+ <span id="toc_toggle" panel-toggle="toc">
230
+ <svg icon><use xlink:href="#svg-nested-list"></use></svg>
231
+ </span>
232
+ <a id="logo" href="${spec.logo_link ? spec.logo_link : '#_'}">
233
+ <img src="${spec.logo}" />
234
+ </a>
235
+ <span issue-count animate panel-toggle="repo_issues">
236
+ <svg icon><use xlink:href="#svg-github"></use></svg>
237
+ </span>
238
+ </header>
239
+
240
+ <article id="content">
241
+ ${render}
242
+ </article>
243
+
244
+ </main>
245
+
246
+ <slide-panels id="slidepanels">
247
+ <slide-panel id="repo_issues" options="right">
248
+ <header class="panel-header">
249
+ <span>
250
+ <svg icon><use xlink:href="#svg-github"></use></svg>
251
+ <span issue-count></span>
252
+ </span>
253
+ <span class="repo-issue-toggle" panel-toggle="repo_issues">✕</span>
254
+ </header>
255
+ <ul id="repo_issue_list"></ul>
256
+ </slide-panel>
257
+
258
+ <slide-panel id="toc">
259
+ <header class="panel-header">
260
+ <span>Table of Contents</span>
261
+ <span panel-toggle="toc">✕</span>
262
+ </header>
263
+ <div id="toc_list">
264
+ ${toc}
265
+ </div>
266
+ </slide-panel>
267
+
268
+ </slide-panels>
269
+ <div style="display: none;">
270
+ ${externalReferences}
271
+ </div>
272
+ </body>
273
+ <script>window.specConfig = ${JSON.stringify(spec)}</script>
274
+ ${assets.body}
275
+ </html>
276
+ `, function (err, data) {
277
+ if (err) {
278
+ reject(err);
279
+ }
280
+ else {
281
+ resolve();
282
+ }
283
+ });
284
+ validateReferences(references, definitions, render);
285
+ references = [];
286
+ definitions = [];
287
+ });
288
+ });
289
+ }
290
+ catch (e) {
291
+ console.error(e);
292
+ }
293
+ }
294
+
295
+ config.specs.forEach(spec => {
296
+ spec.spec_directory = normalizePath(spec.spec_directory);
297
+ spec.destination = normalizePath(spec.output_path || spec.spec_directory);
298
+
299
+ fs.ensureDirSync(spec.destination);
300
+
301
+ let assetTags = {
302
+ svg: fs.readFileSync(modulePath + '/assets/icons.svg', 'utf8') || ''
303
+ };
304
+
305
+ let customAssets = (spec.assets || []).reduce((assets, asset) => {
306
+ let ext = asset.path.split('.').pop();
307
+ if (ext === 'css') {
308
+ assets.css += `<link href="${asset.path}" rel="stylesheet"/>`;
309
+ }
310
+ if (ext === 'js') {
311
+ assets.js[asset.inject || 'body'] += `<script src="${asset.path}" ${asset.module ? 'type="module"' : ''} ></script>`;
312
+ }
313
+ return assets;
314
+ }, {
315
+ css: '',
316
+ js: { head: '', body: '' }
317
+ });
318
+
319
+ if (options.dev) {
320
+ assetTags.head = assets.head.css.map(_path => `<link href="${_path}" rel="stylesheet"/>`).join('') +
321
+ customAssets.css +
322
+ assets.head.js.map(_path => `<script src="${_path}"></script>`).join('') +
323
+ customAssets.js.head;
324
+ assetTags.body = assets.body.js.map(_path => `<script src="${_path}" data-manual></script>`).join('') +
325
+ customAssets.js.body;
326
+ }
327
+ else {
328
+ assetTags.head = `
329
+ <style>${fs.readFileSync(modulePath + '/assets/compiled/head.css', 'utf8')}</style>
330
+ ${customAssets.css}
331
+ <script>${fs.readFileSync(modulePath + '/assets/compiled/head.js', 'utf8')}</script>
332
+ ${customAssets.js.head}
333
+ `;
334
+ assetTags.body = `<script>${fs.readFileSync(modulePath + '/assets/compiled/body.js', 'utf8')}</script>
335
+ ${customAssets.js.body}`;
336
+ }
337
+
338
+ if (spec.katex) {
339
+ const katexDist = findKatexDist();
340
+ assetTags.body += `<script>/* katex */${fs.readFileSync(path.join(katexDist, 'katex.min.js'),
341
+ 'utf8')}</script>`;
342
+ assetTags.body += `<style>/* katex */${fs.readFileSync(path.join(katexDist, 'katex.min.css'),
343
+ 'utf8')}</style>`;
344
+
345
+ fs.copySync(path.join(katexDist, 'fonts'), path.join(spec.destination, 'fonts'));
346
+ }
347
+
348
+ if (!options.nowatch) {
349
+ gulp.watch(
350
+ [spec.spec_directory + '**/*', '!' + path.join(spec.destination, 'index.html')],
351
+ render.bind(null, spec, assetTags)
352
+ )
353
+ }
354
+
355
+ render(spec, assetTags).then(() => {
356
+ if (options.nowatch) process.exit(0)
357
+ }).catch(() => process.exit(1));
358
+
359
+ });
360
+
361
+ }
362
+ catch (e) {
363
+ console.error(e);
364
+ }
365
+
366
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spec-up-t",
3
- "version": "1.0.73",
3
+ "version": "1.0.75",
4
4
  "description": "Technical specification drafting tool that generates rich specification documents from markdown. Forked from https://github.com/decentralized-identity/spec-up by Daniel Buchner (https://github.com/csuwildcat)",
5
5
  "main": "./index",
6
6
  "repository": {
@@ -1,5 +1,10 @@
1
1
  /**
2
- * @file This file creates a JavaScript file with the data for the xrefs. It fetches the latest commit hash of the term files from the GitHub API and stores the data in a JavaScript file that is included in the HTML output of the specification. The outputted JS file serves as a source for the JS code that will be included in the html file.
2
+ * @file This script is responsible for fetching the latest commit hash of term files from the GitHub API and generating both a JavaScript file and a JSON file containing the data for the cross-references (xrefs).
3
+ *
4
+ * The generated JavaScript file is included in the HTML output of the specification, serving as a data source for the JavaScript code embedded in the HTML file.
5
+ *
6
+ * Additionally, the data is written to a JSON file for further processing or usage. This ensures that the xref data is available in both JavaScript and JSON formats, providing flexibility for different use cases.
7
+ *
3
8
  * @author Kor Dwarshuis
4
9
  * @version 1.0.0
5
10
  * @since 2024-06-09
@@ -106,7 +111,7 @@ async function fetchFileContentFromCommit(GITHUB_API_TOKEN, owner, repo, commitH
106
111
  return null;
107
112
  }
108
113
 
109
- function updateXrefs(GITHUB_API_TOKEN) {
114
+ function updateXrefs(GITHUB_API_TOKEN, skipExisting) {
110
115
  // Function to extend xref objects with additional information, such as repository URL and directory information.
111
116
  function extendXrefs(config, xrefs) {
112
117
  if (config.specs[0].external_specs_repos) {
@@ -211,20 +216,19 @@ function updateXrefs(GITHUB_API_TOKEN) {
211
216
 
212
217
  It checks if the xref object already has a commitHash and content.If both are present, it skips fetching the term information from GitHub. This ensures that existing commit hashes are not overwritten.
213
218
  */
214
- async function fetchAllTermsInfoFromGithub() {
219
+ async function fetchAllTermsInfoFromGithub(skipExisting) {
215
220
  for (let xref of allXrefs.xrefs) {
216
- if (!xref.commitHash || !xref.content) {
221
+ if (!skipExisting || (!xref.commitHash || !xref.content)) {
217
222
  const fetchedData = await fetchTermInfoFromGithub(GITHUB_API_TOKEN, xref);
218
223
  if (fetchedData) {
219
224
  xref.commitHash = fetchedData.commitHash;
220
225
  xref.content = fetchedData.content;
221
226
  }
222
- }
223
- }
227
+ } }
224
228
  }
225
229
 
226
230
  // Fetch all term information, then write the results to JSON and JS files.
227
- fetchAllTermsInfoFromGithub().then(() => {
231
+ fetchAllTermsInfoFromGithub(skipExisting).then(() => {
228
232
  const allXrefsStr = JSON.stringify(allXrefs, null, 2);
229
233
  fs.writeFileSync(outputPathJSON, allXrefsStr, 'utf8');
230
234
  const stringReadyForFileWrite = `const allXrefs = ${allXrefsStr};`;
@@ -238,96 +242,7 @@ function updateXrefs(GITHUB_API_TOKEN) {
238
242
  });
239
243
  }
240
244
 
241
- // Function to remove a specific xref from the JSON file, based on term and externalSpec.
242
- function removeXref(term, externalSpec) {
243
- let messages = [];
244
-
245
- try {
246
- // Read the JSON file
247
- let currentXrefs = fs.readJsonSync(outputPathJSON);
248
-
249
- // Check if the term and externalSpec exist
250
- const entryExists = currentXrefs.xrefs.some(xref => xref.term === term && xref.externalSpec === externalSpec);
251
-
252
- if (!entryExists) {
253
- messages.push(`\n SPEC-UP-T: Entry with term "${term}" and externalSpec "${externalSpec}" not found.\n`);
254
- return messages;
255
- }
256
-
257
- // Remove the entry from the JSON file
258
- currentXrefs.xrefs = currentXrefs.xrefs.filter(xref => {
259
- return !(xref.term === term && xref.externalSpec === externalSpec);
260
- });
261
-
262
- // Convert the JSON object back to a JSON string
263
- const currentXrefsStr = JSON.stringify(currentXrefs, null, 2);
264
-
265
- // Write the JSON code to a .json file
266
- fs.writeFileSync(outputPathJSON, currentXrefsStr, 'utf8');
267
-
268
- // Create the JS code for the assignment
269
- const stringReadyForFileWrite = `const allXrefs = ${currentXrefsStr};`;
270
-
271
- // Write the JS code to a .js file
272
- fs.writeFileSync(outputPathJS, stringReadyForFileWrite, 'utf8');
273
-
274
- messages.push(`\n SPEC-UP-T: Entry with term "${term}" and externalSpec "${externalSpec}" removed.\n`);
275
-
276
- // Run the render function to update the HTML file
277
- require('../index.js')({ nowatch: true });
278
-
279
- } catch (error) {
280
- messages.push(`\n SPEC-UP-T: An error occurred - ${error.message}\n`);
281
- }
282
-
283
- // TODO: messages are not used at the moment, since they apparently are not returned to the calling script. Fix this.
284
-
285
- return messages;
286
- }
287
-
288
- function addXref(term, externalSpec) {
289
- let messages = [];
290
-
291
- try {
292
- // Read the JSON file
293
- let currentXrefs = fs.readJsonSync(outputPathJSON);
294
-
295
- // Check if the term and externalSpec exist
296
- const entryExists = currentXrefs.xrefs.some(xref => xref.term === term && xref.externalSpec === externalSpec);
297
-
298
- if (entryExists) {
299
- messages.push(`\n SPEC-UP-T: Entry with term "${term}" and externalSpec "${externalSpec}" already exists.\n`);
300
- return messages;
301
- }
302
-
303
- // Add the entry to the JSON file
304
- currentXrefs.xrefs.push({ term, externalSpec });
305
-
306
- // Convert the JSON object back to a JSON string
307
- const currentXrefsStr = JSON.stringify(currentXrefs, null, 2);
308
-
309
- // Write the JSON code to a .json file
310
- fs.writeFileSync(outputPathJSON, currentXrefsStr, 'utf8');
311
-
312
- // Create the JS code for the assignment
313
- const stringReadyForFileWrite = `const allXrefs = ${currentXrefsStr};`;
314
-
315
- // Write the JS code to a .js file
316
- fs.writeFileSync(outputPathJS, stringReadyForFileWrite, 'utf8');
317
-
318
- messages.push(`\n SPEC-UP-T: Entry with term "${term}" and externalSpec "${externalSpec}" added.\n`);
319
-
320
- // Run the render function to update the HTML file
321
- require('../index.js')({ nowatch: true });
322
-
323
- } catch (error) {
324
- messages.push(`\n SPEC-UP-T: An error occurred - ${error.message}\n`);
325
- }
326
- }
327
-
328
245
  // Export the updateXrefs and removeXref functions for use in other modules.
329
246
  module.exports = {
330
- updateXrefs,
331
- removeXref,
332
- addXref
247
+ updateXrefs
333
248
  }
@@ -0,0 +1,7 @@
1
+ function prepareTref(tref) {
2
+
3
+
4
+
5
+ }
6
+
7
+ module.export = { prepareTref };