1
1
#!/usr/bin/env node
2
2
3
- ///// CONFIG /////
4
-
5
- var contentpath = "content/" ;
6
- var buildpath = "./" ;
7
-
8
- ///// GLOBALS /////
9
-
10
- var fs = require ( 'fs' ) ;
11
- var path = require ( 'path' ) ;
12
- var peg = require ( 'pegjs' ) ;
13
- var markdown = require ( "markdown" ) . markdown ;
14
- marked = require ( "marked" ) ;
15
- marked . setOptions ( {
16
- renderer : new marked . Renderer ( ) ,
17
- gfm : true ,
18
- tables : true ,
19
- breaks : false ,
20
- pedantic : false ,
21
- sanitize : false ,
22
- smartLists : true ,
23
- smartypants : false
24
- } ) ;
25
-
26
- var markup = peg . buildParser ( fs . readFileSync ( "markup.pegjs" , "utf8" ) ) ;
27
-
28
- var convert = function ( input ) {
29
- var chunks = markup . parse ( input ) ;
30
- for ( i in chunks ) {
31
- var s = chunks [ i ] ;
32
- // ignore HTML chunks:
33
- if ( s . charAt ( 0 ) != "<" ) {
34
- chunks [ i ] = marked ( chunks [ i ] ) ;
35
- }
3
+ /*
4
+ Iterates every file in current folder
5
+ Every markdown file becomes an HTML file
6
+ markdown files can optionally start with a meta object (in HTML comments) containing a JSON object:
7
+
8
+ <!--
9
+ {
36
10
37
11
}
38
- return chunks . join ( "\n" ) ;
12
+ -->
13
+
14
+ If not given, the defaults will be given by const meta_default below.
15
+ */
16
+
17
+ const fs = require ( "fs" ) ,
18
+ path = require ( "path" )
19
+
20
+ const JSON5 = require ( "json5" )
21
+ const marked = require ( "marked" )
22
+ const hljs = require ( 'highlight.js' )
23
+ const template = require ( 'es6-dynamic-template' )
24
+
25
+ const meta_default = {
26
+ author : "Graham Wakefield" ,
27
+ template : "content/template.html" ,
28
+ title : "" ,
29
+ description : "Alice Lab for Computational Worldmaking, York University, Canada" ,
39
30
}
40
31
41
- var Handlebars = require ( "Handlebars" ) ;
32
+ function generate ( file ) {
33
+ let src = fs . readFileSync ( path . format ( file ) , "utf8" ) ;
42
34
43
- var has_md = / \. m d $ / ;
35
+ console . log ( "parsing" , file . name )
36
+ // lazy deep copy of meta defaults:
37
+ let meta = JSON . parse ( JSON . stringify ( meta_default ) )
38
+ meta . src = src ;
44
39
45
- function build ( ) {
46
- // read the template:
47
- var template = fs . readFileSync ( "content/template.html" , "utf8" ) ;
48
- template = Handlebars . compile ( template ) ;
40
+ // update metadata from JSON header:
41
+ let match = ( / < ! - - \s * ( \{ [ \S \s ] + ?\} ) \s * - - > / gm) . exec ( src )
42
+ if ( match ) {
43
+ try {
44
+ let header = JSON5 . parse ( match [ 1 ] )
45
+ Object . assign ( meta , header ) ;
46
+ src = src . replace ( / < ! - - \s * ( \{ [ \S \s ] + ?\} ) \s * - - > / gm, "" )
47
+ } catch ( e ) {
48
+ //console.warn("unable to find/parse JSON header for", file.name)
49
+ }
50
+ }
51
+ // parse title from md:
52
+ try {
53
+ if ( ! meta . title ) meta . title = ( / \n # \s + ( .+ ) / gm) . exec ( src ) [ 1 ] ;
54
+ } catch ( e ) {
55
+ //console.warn("unable to find/parse title")
56
+ }
49
57
50
- var files = fs . readdirSync ( contentpath ) ;
51
- for ( var i = 0 ; i < files . length ; i ++ ) {
52
- var filename = path . join ( contentpath , files [ i ] ) ;
53
- var stat = fs . lstatSync ( filename ) ;
54
- if ( ! stat . isDirectory ( ) ) {
55
-
56
- var ext = path . extname ( filename ) ;
57
- var base = path . basename ( filename , ext ) ;
58
-
59
- console . log ( base , ext ) ;
60
-
61
- if ( ext == ".md" ) {
62
- var src = fs . readFileSync ( filename , "utf8" ) ;
63
-
64
- var data = {
65
- title : base ,
66
- content : convert ( src ) , //markdown.toHTML(src),
67
- } ;
68
-
69
- var html = template ( data ) ;
70
-
71
- var outpath = path . join ( buildpath , base + ".html" ) ;
72
-
73
- console . log ( "writing" , outpath ) ;
74
-
75
- fs . writeFileSync ( outpath , html ) ;
76
- }
77
-
58
+ meta . src = meta . src
59
+ // auto hr break at heading 1 titles:
60
+ //.replace(/\n(#\s[^\n]+)/g, "\n---\n\n$1")
61
+ // replace @image :path as background contain
62
+ . replace ( / \n - - - i m a g e : ( [ ^ \s ] + ) / g, `\n<img src="$1" />\n` )
63
+ . replace ( / \n - - - y o u t u b e : ( [ ^ \s ] + ) / g, `<iframe width="720" height="540" src="https://youtube.com/embed/$1" frameborder="0" allowfullscreen></iframe>` )
64
+ . replace ( / \n - - - v i m e o : ( [ ^ \s ] + ) / g, `<div style="padding:56.25% 0 0 0;position:relative;"><iframe src="https://player.vimeo.com/video/$1?loop=1" style="position:absolute;top:0;left:0;width:100%;height:100%;" frameborder="0" allow="fullscreen" allowfullscreen></iframe></div><script src="https://player.vimeo.com/api/player.js"></script>` )
65
+ // auto-embed codepens:
66
+ . replace ( / \n - - - c o d e p e n : h t t p s ? : \/ \/ c o d e p e n .i o \/ + ( [ ^ \/ ] + ) \/ p e n \/ ( [ ^ \/ \n ] + ) \/ ? / g,
67
+ `<p class="codepen" data-height="520" data-default-tab="js,result" data-user="$1" data-slug-hash="$2" data-preview="true"><span><a href="https://codepen.io/$1/pen/$2">Open pen.</a></span></p><script async src="https://static.codepen.io/assets/embed/ei.js"></script>` )
68
+
69
+ let toc = [ ]
70
+ let renderer = new marked . Renderer ( ) ;
71
+ let heading = renderer . heading . bind ( renderer ) ;
72
+ renderer . heading = function ( text , level , ...args ) {
73
+ const html = heading ( text , level , ...args )
74
+ const match = / i d = " ( .+ ) " / gm. exec ( html )
75
+ if ( match && match . length > 1 ) {
76
+ const id = match [ 1 ]
77
+ console . log ( text , level , id )
78
+ toc . push ( {
79
+ level : level ,
80
+ text : text ,
81
+ id : id ,
82
+ } )
78
83
}
79
- } ;
84
+ return html
85
+ }
86
+
87
+ marked . setOptions ( {
88
+ renderer : renderer ,
89
+ highlight : function ( code , lang ) {
90
+ return hljs . highlight ( hljs . getLanguage ( lang ) ? lang : 'plaintext' , code ) . value ;
91
+ }
92
+ } ) ;
93
+
94
+ meta . body = marked ( meta . src ) ;
95
+ meta . toc = toc . length > 1 ? marked ( toc . map ( item => `${ " " . repeat ( item . level - 1 ) } - [${ item . text } ](#${ item . id } )` ) . join ( "\n" ) ) : "" ;
96
+ console . log ( meta . toc )
97
+
98
+ let html = template ( fs . readFileSync ( meta . template , "utf8" ) , meta ) ;
99
+
100
+ const writename = `${ file . name } .html`
101
+ const writepath = path . join ( __dirname , writename )
102
+ fs . writeFileSync ( writepath , html )
103
+ return writename ;
80
104
}
81
105
82
- build ( ) ;
83
106
84
- // todo: if any file in /content is modified, rebuild
107
+ console . log ( "written:" ,
108
+ fs . readdirSync ( __dirname , "utf8" )
109
+ . map ( file => path . parse ( file ) )
110
+ . filter ( file => file . ext == ".md" )
111
+ . map ( generate )
112
+ . join ( "\n" )
113
+ )
114
+
115
+
0 commit comments