3
3
const ware = require ( 'ware' )
4
4
const each = require ( 'lodash/collection/each' )
5
5
const markdownIt = require ( 'markdown-it' )
6
+ const cheerio = require ( 'cheerio' )
6
7
7
8
const tocify = require ( './tocify' )
8
9
const indexify = require ( './indexify' )
@@ -11,50 +12,112 @@ const syntaxHighlight = require('./syntax_highlight')
11
12
12
13
const assign = Object . assign
13
14
14
- module . exports = function ( options ) {
15
- var app = ware ( )
16
- . use ( buildIndex )
17
- . use ( renderMarkdown )
18
- . use ( cleanFiles )
19
-
20
- return function ( files , ms , done ) {
21
- app . run ( files , ms , done )
22
- }
23
- }
24
-
25
15
/**
26
- * Builds docpress.json (`files['docpress.json']`). It is as JSON file, which
27
- * has the following metadata:
16
+ * Metalsmith Middleware that takes a source tree and generates a site from it.
17
+ * It turns markdown into HTML files, but these files are bare and don't have
18
+ * anything other than rendered markup.
19
+ *
20
+ * It also builds docpress.json which has reusable
21
+ * metadata for subsequent tools (like `docpress-base`).
28
22
*
29
- * - `index` — pages index.
30
- * - `toc` — table of contents (as a tree).
31
- * — `sources` — a mapping of source to destination filenames.
23
+ * ### docpress.json
24
+ * `docpress.json` is a JSON file with the following metadata:
25
+ *
26
+ * - `index` (Object) — pages index.
27
+ * - `toc` (Object) — table of contents (as a tree).
28
+ * — `sources` (Object) — a mapping of source to destination filenames.
32
29
*
33
30
* These datas can be obtained via `files['docpress.json'].index` (ie, as
34
31
* Metalsmith file metadata) or by parsing docpress.json.
35
32
*
36
- * ### Table of contents
37
- * Each toc item has:
38
- *
39
- * - `sections` (key/value of sections)
40
- * - `source` (path to source)
41
- * - `title` (title)
42
- * - `slug` (slug for the item)
43
- * - `url` (URL, if applicable)
44
- * - `headings` (array of `{ title, depth, id }`)
33
+ * files['docpress.json'].index
34
+ * files['docpress.json'].sources
35
+ * files['docpress.json'].toc
45
36
*
46
37
* ### Index
47
- * Each index item has:
38
+ * Each ` index` item has:
48
39
*
49
40
* - `source` — path to source
50
41
* - `title` — the page title according to TOC
51
42
* - `slug` — slug for the page
52
43
* - `headings` — an array of headings
53
44
*
54
- * ### Files
55
- * It will modify files with new metadata:
45
+ * index = files['docpress.json'].index
46
+ * index['index.html']
47
+ * => {
48
+ * source: 'README.md',
49
+ * title: 'My project',
50
+ * slug: 'index',
51
+ * headings: [
52
+ * { title: 'Overview', depth: 2, id: 'overview' },
53
+ * { title: 'Usage', depth: 2, id: 'usage', headings: [
54
+ * { title: 'via npm', depth: 3, id: 'via-npm' },
55
+ * ]},
56
+ * ]
57
+ * }
58
+ *
59
+ * ### Sources
60
+ * `sources` is a key-value pairing of source files to built files.
61
+ *
62
+ * files['docpress.json'].sources
63
+ * => {
64
+ * "README.md": "index.html",
65
+ * "docs/usage.md": "usage.html",
66
+ * "docs/install/windows.md": "install/windows.html"
67
+ * }
68
+ *
69
+ * ### Table of contents
70
+ * Each `toc` item has:
71
+ *
72
+ * - `sections` — array of sections
73
+ * - `source` — path to source
74
+ * - `title` — title
75
+ * - `slug` — slug for the item
76
+ * - `url` — URL, if applicable
77
+ * - `headings` — array of `{ title, depth, id }`
78
+ *
79
+ * toc = files['docpress.json'].toc
80
+ * toc = {
81
+ * sections: [
82
+ * {
83
+ * title: 'My project',
84
+ * source: 'README.md',
85
+ * url: 'index.html',
86
+ * slug: 'index',
87
+ * headings: [ ... ]
88
+ * }, ...
89
+ * ]
90
+ * }
56
91
*
57
- * - `newName` — the build output filename
92
+ * ### Each file
93
+ * Each HTML file will have these metadata available:
94
+ *
95
+ * file = files['index.html']
96
+ * file._processed //=> true
97
+ * file.title //=> "My project"
98
+ * file.slug //=> "index" (perfect for HTML IDs)
99
+ * file.source //=> "README.md" (where it was rendered from)
100
+ * file.filename //=> "index.html" (new filename)
101
+ * file.$ // Cheerio instance
102
+ * file.markdown // Markdown source
103
+ * file.html // Rendered HTML
104
+ * file.contents // Same as `.html`
105
+ */
106
+
107
+ module . exports = function core ( options ) {
108
+ var app = ware ( )
109
+ . use ( buildIndex )
110
+ . use ( renderMarkdown )
111
+ . use ( cleanFiles )
112
+
113
+ return function ( files , ms , done ) {
114
+ app . run ( files , ms , done )
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Builds `docpress.json`. See `core()` for a description on what it is.
120
+ * It will also modify files with the `.filenmae` attribute.
58
121
*/
59
122
60
123
function buildIndex ( files , ms , done ) {
@@ -66,23 +129,28 @@ function buildIndex (files, ms, done) {
66
129
return done ( err )
67
130
}
68
131
132
+ // Build `toc`
69
133
var toc = tocify ( files [ `${ docs } /README.md` ] . contents . toString ( ) , files , { docs } )
134
+
135
+ // Build `index` and `sources`
70
136
var indexes = indexify ( toc , { docs } )
71
137
138
+ // Generate source mappings for things outside the TOC
72
139
Object . keys ( files ) . forEach ( ( fname ) => {
73
140
const file = files [ fname ]
74
141
if ( ! indexes . sources [ fname ] ) {
75
142
// rename images
76
- const newName = fname . replace ( docsExpr , '' )
77
- if ( newName !== fname && ! fname . match ( / \. m d $ / ) ) {
78
- file . newName = newName
79
- indexes . sources [ fname ] = newName
143
+ const filename = fname . replace ( docsExpr , '' )
144
+ if ( filename !== fname && ! fname . match ( / \. m d $ / ) ) {
145
+ file . filename = filename
146
+ indexes . sources [ fname ] = filename
80
147
}
81
148
} else {
82
- file . newName = indexes . sources [ fname ]
149
+ file . filename = indexes . sources [ fname ]
83
150
}
84
151
} )
85
152
153
+ // Ensure every link in the TOC works
86
154
verifyIndex ( indexes . index , files )
87
155
88
156
var data = {
@@ -91,23 +159,17 @@ function buildIndex (files, ms, done) {
91
159
sources : indexes . sources
92
160
}
93
161
94
- files [ 'docpress.json' ] = assign ( { } , data , { contents : JSON . stringify ( data , null , 2 ) + '\n' } )
162
+ // Save
163
+ files [ 'docpress.json' ] = assign ( { } , data , {
164
+ contents : JSON . stringify ( data , null , 2 ) + '\n'
165
+ } )
95
166
96
167
done ( )
97
168
}
98
169
99
170
/**
100
171
* Private: Converts .md to .html.
101
- *
102
- * At the end of this, you get a site with `.html` files (bare, no layout)
103
- * and a `toc.json` and `index.json`.
104
- *
105
- * Each html also has:
106
- *
107
- * - `title` (title according to TOC)
108
- * - `source` (path of source)
109
- * - `markdown` (raw Markdown source)
110
- * - `html` (rendered HTML)
172
+ * At the end of this, you get a site with `.html` files (bare, no layout).
111
173
*/
112
174
113
175
function renderMarkdown ( files , ms , done ) {
@@ -122,10 +184,13 @@ function renderMarkdown (files, ms, done) {
122
184
highlight : syntaxHighlight ,
123
185
html : true
124
186
} ) . render ( file . contents . toString ( ) )
187
+ const $ = cheerio . load ( html )
125
188
189
+ fixHtml ( $ , fname , sources , files )
190
+ file . $ = $
126
191
file . _processed = true
127
192
file . markdown = file . contents
128
- file . html = fixHtml ( html , fname , sources , files )
193
+ file . html = $ . html ( )
129
194
file . title = page . title
130
195
file . source = page . source
131
196
file . slug = page . slug
@@ -154,9 +219,9 @@ function cleanFiles (files, ms, done) {
154
219
}
155
220
156
221
// rename docs/images/pic.png => images/pic.png
157
- // (`newName ` is left by an earlier step)
158
- if ( file . newName ) {
159
- files [ file . newName ] = files [ fname ]
222
+ // (`filename ` is left by an earlier step)
223
+ if ( file . filename ) {
224
+ files [ file . filename ] = files [ fname ]
160
225
delete files [ fname ]
161
226
}
162
227
} )
@@ -176,4 +241,3 @@ function verifyIndex (index, files) {
176
241
}
177
242
} )
178
243
}
179
-
0 commit comments