@@ -4,7 +4,7 @@ const jsdom = require("jsdom");
4
4
const { JSDOM } = jsdom ;
5
5
const path = require ( "path" ) ;
6
6
const markedAlert = require ( "marked-alert" ) ;
7
-
7
+ const matter = require ( "gray-matter" ) ;
8
8
// Setup some options for our markdown renderer
9
9
marked . setOptions ( {
10
10
renderer : new marked . Renderer ( ) ,
@@ -34,9 +34,13 @@ marked.use(markedAlert());
34
34
async function renderit ( infile ) {
35
35
const gtag = ( await fs . readFile ( "gtag.html" , "utf-8" ) ) . trim ( ) ;
36
36
console . log ( `Reading ${ infile } ` ) ;
37
- basename = path . basename ( infile , ".md" ) ;
37
+ const basename = path . basename ( infile , ".md" ) ;
38
38
const outfile = path . join ( path . dirname ( infile ) , `${ basename } .html` ) ;
39
39
let f1 = await fs . readFile ( infile , "utf-8" ) ;
40
+ // any metadata on the file?
41
+ const { data, content } = matter ( f1 ) ;
42
+
43
+ f1 = content ; // skip the frontmatter (YAML block within ---)
40
44
41
45
// oh the irony of removing a BOM before posting to unicode.org
42
46
if ( f1 . charCodeAt ( 0 ) == 0xfeff ) {
@@ -215,6 +219,11 @@ async function renderit(infile) {
215
219
a . setAttribute ( "name" , parid ) ;
216
220
}
217
221
222
+ // If the document requests it, linkify terms
223
+ if ( data . linkify ) {
224
+ linkify ( dom . window . document ) ;
225
+ }
226
+
218
227
// OK, done munging the DOM, write it out.
219
228
console . log ( `Writing ${ outfile } ` ) ;
220
229
@@ -246,3 +255,70 @@ fixall().then(
246
255
process . exitCode = 1 ;
247
256
}
248
257
) ;
258
+
259
+ function linkify ( document ) {
260
+ const terms = findTerms ( document ) ;
261
+ const missing = new Set ( ) ;
262
+ const used = new Set ( ) ;
263
+ const links = document . querySelectorAll ( "em" ) ;
264
+
265
+ links . forEach ( ( item ) => {
266
+ const target = generateId ( item . textContent ) ;
267
+ if ( terms . has ( target ) ) {
268
+ const el = item . lastElementChild ?? item ;
269
+ el . innerHTML = `<a href="#${ target } ">${ item . textContent } </a>` ;
270
+
271
+ used . add ( target ) ;
272
+ } else {
273
+ missing . add ( target ) ;
274
+ }
275
+ } ) ;
276
+
277
+ if ( missing . size > 0 ) {
278
+ console . log ( "Potentially missing definitions:" ) ;
279
+ Array . from ( missing )
280
+ . sort ( )
281
+ . forEach ( ( item ) => {
282
+ console . log ( item ) ;
283
+ } ) ;
284
+ }
285
+
286
+ if ( terms . size === used . size ) return ;
287
+ console . log ( "Some definitions were not used:" ) ;
288
+ Array . from ( terms ) . forEach ( ( item ) => {
289
+ if ( ! used . has ( item ) ) {
290
+ console . log ( item ) ;
291
+ }
292
+ } ) ;
293
+ }
294
+
295
+ function findTerms ( document ) {
296
+ const terms = new Set ( ) ;
297
+ let duplicateCount = 0 ;
298
+ document . querySelectorAll ( "dfn" ) . forEach ( ( item ) => {
299
+ const term = generateId ( item . textContent ) ;
300
+ if ( term . length === 0 ) return ; // skip empty terms
301
+ if ( terms . has ( term ) ) {
302
+ console . log ( `Duplicate term: ${ term } ` ) ;
303
+ duplicateCount ++ ;
304
+ }
305
+ terms . add ( term ) ;
306
+ item . setAttribute ( "id" , term ) ;
307
+ } ) ;
308
+
309
+ if ( duplicateCount > 0 ) {
310
+ console . log ( "Duplicate Terms: " + duplicateCount ) ;
311
+ }
312
+ return terms ;
313
+ }
314
+
315
+ function generateId ( term ) {
316
+ const id = term . toLowerCase ( ) . replace ( / \s + / g, "-" ) ; // Replaces spaces safely
317
+ // TODO: do better than hardcoding the one case in message-format
318
+ if ( id . endsWith ( "rategies" ) ) {
319
+ return id . slice ( 0 , - 3 ) + "y" ;
320
+ } else if ( id . endsWith ( "s" ) && id !== "status" ) {
321
+ return id . slice ( 0 , - 1 ) ;
322
+ }
323
+ return id ;
324
+ }
0 commit comments