poops 1.1.0 → 1.2.0
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/README.md +515 -55
- package/lib/copy.js +7 -9
- package/lib/markup/collections.js +247 -0
- package/lib/markup/engines/liquid.js +240 -0
- package/lib/markup/engines/nunjucks.js +261 -0
- package/lib/markup/helpers.js +170 -0
- package/lib/markup/highlight.js +77 -0
- package/lib/markup/indexer.js +154 -0
- package/lib/markup/stop-words-en.json +25 -0
- package/lib/markups.js +223 -459
- package/lib/postcss.js +127 -0
- package/lib/{ssg.js → reactor.js} +36 -25
- package/lib/scripts.js +12 -13
- package/lib/styles.js +20 -12
- package/lib/utils/helpers.js +0 -56
- package/lib/utils/log.js +64 -0
- package/package.json +21 -3
- package/poops.js +87 -107
- package/lib/utils/print-style.js +0 -72
package/lib/markups.js
CHANGED
|
@@ -1,133 +1,80 @@
|
|
|
1
|
-
import { pathExists, pathIsDirectory, readDataFile,
|
|
1
|
+
import { pathExists, pathIsDirectory, readDataFile, mkDir, buildTime } from './utils/helpers.js'
|
|
2
|
+
import { replaceOutExtensions, getRelativePathPrefix, getPageUrl, getPageUrlRelativeToOutput, parseFrontMatter, clearFrontMatterCache } from './markup/helpers.js'
|
|
3
|
+
import { collectionAutoDiscovery, getCollectionDataBasedOnConfig, buildCollectionPaginationData, generateCollectionPaginationPages } from './markup/collections.js'
|
|
4
|
+
import { generateIndexFiles } from './markup/indexer.js'
|
|
5
|
+
import NunjucksEngine from './markup/engines/nunjucks.js'
|
|
6
|
+
import LiquidEngine from './markup/engines/liquid.js'
|
|
2
7
|
import fs from 'node:fs'
|
|
3
8
|
import { globSync } from 'glob'
|
|
4
|
-
import { parse as parseMarkdown } from 'marked'
|
|
5
|
-
import moment from 'moment'
|
|
6
|
-
import nunjucks from 'nunjucks'
|
|
7
9
|
import path from 'node:path'
|
|
8
|
-
import
|
|
9
|
-
import yaml from 'yaml'
|
|
10
|
+
import log from './utils/log.js'
|
|
10
11
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
constructor(templatesDir, includePaths) {
|
|
15
|
-
super()
|
|
16
|
-
this.templatesDir = templatesDir
|
|
17
|
-
this.includePaths = includePaths || []
|
|
18
|
-
this.includePaths.push('_*') // XXX: It is better to define templates and layouts directories in the config file? then all together include paths?
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
getSource(name) {
|
|
22
|
-
let fullPath = name
|
|
23
|
-
if (!fs.existsSync(name)) {
|
|
24
|
-
let pattern = `**/${name}`
|
|
25
|
-
if (this.includePaths) {
|
|
26
|
-
pattern = `{${this.includePaths.join(',')}}/${pattern}`
|
|
27
|
-
}
|
|
28
|
-
fullPath = globSync(path.join(this.templatesDir, pattern))[0]
|
|
29
|
-
}
|
|
30
|
-
if (!fs.existsSync(fullPath)) {
|
|
31
|
-
console.log(`${pstyle.cyanBright + pstyle.bold}[markup]${pstyle.reset} ${pstyle.redBright + pstyle.bold}[error]${pstyle.reset}${pstyle.dim} Template not found:${pstyle.reset} ${pstyle.italic + pstyle.underline}${name}${pstyle.reset}`)
|
|
32
|
-
// throw new Error(`Template not found: ${name}`)
|
|
33
|
-
return { src: '', path: fullPath, noCache: true }
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
let source = ''
|
|
37
|
-
let frontMatter = {}
|
|
38
|
-
|
|
39
|
-
try {
|
|
40
|
-
const frontMatterResult = parseFrontMatter(fullPath)
|
|
41
|
-
frontMatter = frontMatterResult.frontMatter
|
|
42
|
-
source = frontMatterResult.content
|
|
43
|
-
} catch (err) {
|
|
44
|
-
console.log(`${pstyle.redBright + pstyle.bold}[error]${pstyle.reset}${pstyle.dim} Failed parsing front matter:${pstyle.reset} ${pstyle.italic + pstyle.underline}${fullPath}${pstyle.reset}`)
|
|
45
|
-
console.log(err)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (path.extname(fullPath) === '.md') {
|
|
49
|
-
source = parseMarkdown(source)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (frontMatter.layout) {
|
|
53
|
-
source = `{% extends '${frontMatter.layout}.html' %}\n{% block content %}\n${source}\n{% endblock %}`
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return { src: source, path: fullPath, noCache: true }
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
resolve(from, to) {
|
|
60
|
-
return path.resolve(path.dirname(from), to)
|
|
61
|
-
}
|
|
12
|
+
const ENGINES = {
|
|
13
|
+
nunjucks: NunjucksEngine,
|
|
14
|
+
liquid: LiquidEngine
|
|
62
15
|
}
|
|
63
16
|
|
|
64
17
|
export default class Markups {
|
|
65
18
|
constructor(config) {
|
|
66
19
|
this.config = config
|
|
67
|
-
|
|
68
|
-
|
|
20
|
+
const moduleConfig = this.config.markup
|
|
21
|
+
|
|
22
|
+
if (!moduleConfig || !moduleConfig.in) return
|
|
23
|
+
if (!moduleConfig.options) moduleConfig.options = {}
|
|
24
|
+
|
|
25
|
+
// Determine engine
|
|
26
|
+
const engineName = moduleConfig.engine || moduleConfig.options.engine || 'nunjucks'
|
|
27
|
+
this.logTag = 'markup'
|
|
28
|
+
|
|
29
|
+
// Normalize config
|
|
30
|
+
this.markupIn = moduleConfig.in
|
|
31
|
+
this.markupOut = moduleConfig.out || process.cwd()
|
|
32
|
+
this.siteData = moduleConfig.site || moduleConfig.options.site || {}
|
|
33
|
+
this.timeDateFormat = moduleConfig.options.timeDateFormat || moduleConfig.timeDateFormat
|
|
34
|
+
this.collectionsConfig = moduleConfig.options.collections || moduleConfig.collections
|
|
35
|
+
this.includePaths = moduleConfig.includePaths || moduleConfig.options.includePaths || []
|
|
36
|
+
this.searchIndexConfig = moduleConfig.options.searchIndex || moduleConfig.searchIndex
|
|
37
|
+
this.sitemapConfig = moduleConfig.options.sitemap || moduleConfig.sitemap
|
|
69
38
|
this.dataFiles = []
|
|
70
|
-
this.includePaths = this.config.markup.includePaths || this.config.markup.options.includePaths || []
|
|
71
|
-
|
|
72
|
-
const options = {
|
|
73
|
-
autoescape: false,
|
|
74
|
-
watch: false,
|
|
75
|
-
noCache: true
|
|
76
|
-
}
|
|
77
39
|
|
|
78
|
-
|
|
79
|
-
|
|
40
|
+
// Instantiate engine
|
|
41
|
+
const EngineClass = ENGINES[engineName]
|
|
42
|
+
if (!EngineClass) {
|
|
43
|
+
log({ tag: 'error', text: `Unknown markup engine: ${engineName}` })
|
|
44
|
+
return
|
|
80
45
|
}
|
|
81
46
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
this.nunjucksEnv.addFilter('slugify', str => {
|
|
86
|
-
return str.trim().toLowerCase().replace(/[^a-z0-9]+/g, '-')
|
|
47
|
+
const templatesDir = path.join(process.cwd(), this.markupIn)
|
|
48
|
+
this.engine = new EngineClass(templatesDir, this.includePaths, {
|
|
49
|
+
autoescape: moduleConfig.autoescape || moduleConfig.options.autoescape || false
|
|
87
50
|
})
|
|
88
51
|
|
|
89
|
-
this.
|
|
90
|
-
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
this.nunjucksEnv.addFilter('markdown', str => {
|
|
94
|
-
return parseMarkdown(str)
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
this.nunjucksEnv.addFilter('date', (str, template) => {
|
|
98
|
-
if (!template) template = this.config.markup.options.timeDateFormat || this.config.markup.timeDateFormat
|
|
99
|
-
if (!template) return str
|
|
100
|
-
const date = !str || str.trim() === '' ? new Date() : new Date(str)
|
|
101
|
-
return moment(date).format(template)
|
|
102
|
-
})
|
|
52
|
+
this.engine.registerFilters({ timeDateFormat: this.timeDateFormat, markupOut: this.markupOut })
|
|
53
|
+
this.engine.registerTags(() => path.join(process.cwd(), this.markupOut))
|
|
103
54
|
|
|
104
55
|
// Load global variables
|
|
105
|
-
|
|
106
56
|
const pkgPath = path.join(process.cwd(), 'package.json')
|
|
107
|
-
|
|
108
57
|
if (fs.existsSync(pkgPath)) {
|
|
109
|
-
|
|
110
|
-
this.nunjucksEnv.addGlobal('package', pkg)
|
|
58
|
+
this.engine.setGlobal('package', JSON.parse(fs.readFileSync(pkgPath, 'utf-8')))
|
|
111
59
|
}
|
|
112
60
|
|
|
113
|
-
|
|
114
|
-
this.nunjucksEnv.addGlobal('site', siteData)
|
|
61
|
+
this.engine.setGlobal('site', this.siteData)
|
|
115
62
|
|
|
116
63
|
if (this.config.livereload_port) {
|
|
117
|
-
this.
|
|
64
|
+
this.engine.setGlobal('livereload_port', this.config.livereload_port)
|
|
118
65
|
}
|
|
119
66
|
|
|
120
|
-
if (this.config.
|
|
121
|
-
for (const [name, html] of Object.entries(this.config.
|
|
122
|
-
this.
|
|
67
|
+
if (this.config.reactorData) {
|
|
68
|
+
for (const [name, html] of Object.entries(this.config.reactorData)) {
|
|
69
|
+
this.engine.setGlobal(name, html)
|
|
123
70
|
}
|
|
124
71
|
}
|
|
125
72
|
|
|
126
|
-
const data =
|
|
73
|
+
const data = moduleConfig.data || moduleConfig.options.data
|
|
127
74
|
this.loadDataFiles(data)
|
|
128
75
|
|
|
129
|
-
if (!
|
|
130
|
-
|
|
76
|
+
if (!moduleConfig.out) {
|
|
77
|
+
moduleConfig.out = this.markupOut
|
|
131
78
|
}
|
|
132
79
|
}
|
|
133
80
|
|
|
@@ -139,30 +86,37 @@ export default class Markups {
|
|
|
139
86
|
files = [files]
|
|
140
87
|
}
|
|
141
88
|
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
89
|
+
const dataDir = pathIsDirectory(this.markupIn) ? this.markupIn : path.dirname(this.markupIn)
|
|
90
|
+
const resolved = []
|
|
91
|
+
for (const file of files) {
|
|
92
|
+
const fullPath = path.join(process.cwd(), dataDir, file)
|
|
93
|
+
if (pathIsDirectory(fullPath)) {
|
|
94
|
+
const dirFiles = globSync(path.join(fullPath, '**/*.+(json|yml|yaml)'))
|
|
95
|
+
for (const f of dirFiles) {
|
|
96
|
+
resolved.push(path.relative(path.join(process.cwd(), dataDir), f))
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
resolved.push(file)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
145
102
|
|
|
146
|
-
if (!this.dataFiles.length) this.dataFiles =
|
|
103
|
+
if (!this.dataFiles.length) this.dataFiles = resolved
|
|
147
104
|
|
|
148
|
-
for (const dataFile of
|
|
105
|
+
for (const dataFile of resolved) {
|
|
149
106
|
try {
|
|
150
|
-
const dataDir = pathIsDirectory(this.config.markup.in) ? this.config.markup.in : path.dirname(this.config.markup.in)
|
|
151
107
|
const data = readDataFile(path.join(process.cwd(), dataDir, dataFile))
|
|
152
108
|
const globalKeyName = path.basename(dataFile, path.extname(dataFile)).replace(/[.\-\s]/g, '_')
|
|
153
|
-
|
|
154
|
-
promises.push(this.nunjucksEnv.addGlobal(globalKeyName, data))
|
|
109
|
+
this.engine.setGlobal(globalKeyName, data)
|
|
155
110
|
} catch (err) {
|
|
156
|
-
|
|
111
|
+
log({ tag: 'error', text: 'Data file not found:', link: dataFile })
|
|
157
112
|
continue
|
|
158
113
|
}
|
|
159
114
|
}
|
|
160
|
-
|
|
161
|
-
return Promise.all(promises)
|
|
162
115
|
}
|
|
163
116
|
|
|
164
117
|
reloadDataFiles() {
|
|
165
|
-
|
|
118
|
+
this.loadDataFiles(this.dataFiles)
|
|
119
|
+
return Promise.resolve()
|
|
166
120
|
}
|
|
167
121
|
|
|
168
122
|
generateMarkupGlobPattern(excludes) {
|
|
@@ -176,224 +130,13 @@ export default class Markups {
|
|
|
176
130
|
markupDefaultExcludes.push(...this.config.includePaths)
|
|
177
131
|
}
|
|
178
132
|
|
|
179
|
-
markupDefaultExcludes.push('_*')
|
|
180
|
-
|
|
181
|
-
markupDefaultExcludes = [...new Set(markupDefaultExcludes)] // Remove duplicates
|
|
182
|
-
|
|
183
|
-
return `!(${markupDefaultExcludes.join('|')})/**/*.+(html|xml|rss|atom|json|njk|md)`
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
getFrontMatter(file, fileName) {
|
|
187
|
-
const source = fileName ? file : fs.readFileSync(file, 'utf-8')
|
|
188
|
-
if (!fileName) fileName = file
|
|
189
|
-
|
|
190
|
-
const match = source.match(/^\s*---\s*([\s\S]*?)---\s*/) // Front matter match
|
|
191
|
-
let frontMatter = {}
|
|
192
|
-
|
|
193
|
-
if (match) {
|
|
194
|
-
try {
|
|
195
|
-
frontMatter = yaml.parse(match[1]) // Pass front matter to the template
|
|
196
|
-
} catch (err) {
|
|
197
|
-
console.log(`${pstyle.redBright + pstyle.bold}[error]${pstyle.reset}${pstyle.dim} Failed parsing front matter:${pstyle.reset} ${pstyle.italic + pstyle.underline}${fileName}${pstyle.reset}`)
|
|
198
|
-
console.log(err)
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
return frontMatter
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
getSingleCollectionData(collectionName) {
|
|
206
|
-
const collectionData = []
|
|
207
|
-
globSync(path.join(process.cwd(), this.config.markup.in, collectionName, '**/*.+(html|njk|md)'), { ignore: ['**/index.+(html|njk|md)'] }).forEach((file) => {
|
|
208
|
-
let frontMatter = {}
|
|
209
|
-
|
|
210
|
-
try {
|
|
211
|
-
const frontMatterResult = parseFrontMatter(file)
|
|
212
|
-
frontMatter = frontMatterResult.frontMatter
|
|
213
|
-
} catch (err) {
|
|
214
|
-
console.log(`${pstyle.redBright + pstyle.bold}[error]${pstyle.reset}${pstyle.dim} Failed parsing front matter:${pstyle.reset} ${pstyle.italic + pstyle.underline}${file}${pstyle.reset}`)
|
|
215
|
-
console.log(err)
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
if (frontMatter.published === false) return // Skip unpublished items
|
|
219
|
-
|
|
220
|
-
if (!frontMatter.date) {
|
|
221
|
-
frontMatter.date = fs.statSync(file).ctime.toISOString().slice(0, 16)
|
|
222
|
-
}
|
|
223
|
-
frontMatter.fileName = path.basename(file)
|
|
224
|
-
frontMatter.filePath = path.relative(process.cwd(), file)
|
|
225
|
-
frontMatter.collection = collectionName
|
|
226
|
-
frontMatter.url = path.join(collectionName, path.basename(frontMatter.filePath))
|
|
227
|
-
|
|
228
|
-
frontMatter.url = this.replaceOutExtensions(frontMatter.url)
|
|
229
|
-
|
|
230
|
-
if (!frontMatter.title) {
|
|
231
|
-
frontMatter.title = path.basename(frontMatter.filePath, path.extname(frontMatter.filePath))
|
|
232
|
-
}
|
|
233
|
-
collectionData.push(frontMatter)
|
|
234
|
-
})
|
|
235
|
-
|
|
236
|
-
return collectionData
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
collectionAutoDiscovery() {
|
|
240
|
-
const indexFiles = globSync(path.join(process.cwd(), this.config.markup.in, '/**/index.+(html|njk|md)'))
|
|
241
|
-
|
|
242
|
-
const collectionData = {}
|
|
243
|
-
|
|
244
|
-
for (const indexFile of indexFiles) {
|
|
245
|
-
let frontMatter = {}
|
|
246
|
-
|
|
247
|
-
try {
|
|
248
|
-
const frontMatterResult = parseFrontMatter(indexFile)
|
|
249
|
-
frontMatter = frontMatterResult.frontMatter
|
|
250
|
-
} catch (err) {
|
|
251
|
-
console.log(`${pstyle.redBright + pstyle.bold}[error]${pstyle.reset}${pstyle.dim} Failed parsing front matter:${pstyle.reset} ${pstyle.italic + pstyle.underline}${indexFile}${pstyle.reset}`)
|
|
252
|
-
console.log(err)
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (!frontMatter.collection) continue
|
|
256
|
-
|
|
257
|
-
if (frontMatter.collection === true) {
|
|
258
|
-
frontMatter.collection = path.basename(path.dirname(indexFile))
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
const collectionName = frontMatter.collection.trim()
|
|
262
|
-
|
|
263
|
-
if (collectionName === '') continue
|
|
264
|
-
|
|
265
|
-
frontMatter.name = collectionName
|
|
266
|
-
const collection = this.buildCollectionObject(frontMatter)
|
|
267
|
-
if (!collection) continue
|
|
268
|
-
collectionData[collection.name] = collection
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
return collectionData
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
getCollectionDataBasedOnConfig(collectionConfig) {
|
|
275
|
-
if (!collectionConfig) return {}
|
|
276
|
-
|
|
277
|
-
const items = Array.isArray(collectionConfig)
|
|
278
|
-
? collectionConfig
|
|
279
|
-
: [collectionConfig]
|
|
280
|
-
|
|
281
|
-
const collectionData = {}
|
|
282
|
-
|
|
283
|
-
for (let item of items) {
|
|
284
|
-
if (typeof item === 'string') item = { name: item }
|
|
285
|
-
if (!item || !item.name) continue
|
|
286
|
-
const collection = this.buildCollectionObject(item)
|
|
287
|
-
if (collection) collectionData[item.name] = collection
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
return collectionData
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
buildCollectionObject(collectionProtoObject) {
|
|
294
|
-
const collection = {
|
|
295
|
-
name: collectionProtoObject.name,
|
|
296
|
-
items: this.getSingleCollectionData(collectionProtoObject.name)
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
if (collection.items.length === 0) return null
|
|
300
|
-
|
|
301
|
-
if (collectionProtoObject.paginate && !isNaN(parseInt(collectionProtoObject.paginate))) {
|
|
302
|
-
collection.paginate = parseInt(collectionProtoObject.paginate)
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
if (collectionProtoObject.sort) {
|
|
306
|
-
collection.sort = collectionProtoObject.sort
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
if (typeof collection.sort === 'string') {
|
|
310
|
-
collection.sort = { by: collection.sort }
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
if (!collection.sort) {
|
|
314
|
-
collection.sort = { by: 'date' }
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
if (!collection.sort.by) {
|
|
318
|
-
collection.sort.by = 'date'
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
if (collection.sort.by === 'date') {
|
|
322
|
-
collection.sort.type = 'date'
|
|
323
|
-
} else {
|
|
324
|
-
collection.sort.type = 'alphabetical'
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
if (!collection.sort.order) {
|
|
328
|
-
collection.sort.order = collection.sort.type === 'date' ? 'desc' : 'asc'
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
collection.items.sort((a, b) => {
|
|
332
|
-
if (collection.sort.type === 'date') {
|
|
333
|
-
if (collection.sort.order === 'asc') {
|
|
334
|
-
return new Date(a[collection.sort.by]) - new Date(b[collection.sort.by])
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
return new Date(b[collection.sort.by]) - new Date(a[collection.sort.by])
|
|
338
|
-
} else {
|
|
339
|
-
if (collection.sort.order === 'asc') {
|
|
340
|
-
return a[collection.sort.by] > b[collection.sort.by] ? 1 : -1
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
return a[collection.sort.by] < b[collection.sort.by] ? 1 : -1
|
|
344
|
-
}
|
|
345
|
-
})
|
|
346
|
-
|
|
347
|
-
return collection
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
clearCollectionOutputDir(collectionName) {
|
|
351
|
-
const collectionDirectoryPath = path.join(process.cwd(), this.config.markup.out, collectionName)
|
|
352
|
-
deleteDirectory(collectionDirectoryPath) // Remove collection directory
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
getRelativePathPrefix(outputDir, fromDir) {
|
|
356
|
-
let relativeDir = path.relative(process.cwd(), outputDir)
|
|
357
|
-
const fromRelativeDir = fromDir ? path.relative(process.cwd(), fromDir) : ''
|
|
358
|
-
|
|
359
|
-
if (fromRelativeDir && relativeDir.startsWith(fromRelativeDir)) {
|
|
360
|
-
relativeDir = relativeDir.replace(fromRelativeDir, '')
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
return this.getUpDirPrefix(relativeDir)
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
getUpDirPrefix(relativeDir) {
|
|
367
|
-
if (relativeDir.trim() === '') return ''
|
|
368
|
-
if (relativeDir.startsWith(path.sep)) relativeDir = relativeDir.slice(1)
|
|
369
|
-
if (relativeDir.endsWith(path.sep)) relativeDir = relativeDir.slice(0, -1)
|
|
370
|
-
const relativePathParts = relativeDir.split(path.sep)
|
|
371
|
-
let upDir = ''
|
|
372
|
-
for (let i = 0; i < relativePathParts.length; i++) {
|
|
373
|
-
upDir += `..${path.sep}`
|
|
374
|
-
}
|
|
375
|
-
return upDir
|
|
376
|
-
}
|
|
133
|
+
markupDefaultExcludes.push('_*')
|
|
134
|
+
markupDefaultExcludes = [...new Set(markupDefaultExcludes)]
|
|
377
135
|
|
|
378
|
-
|
|
379
|
-
outputPath = this.replaceOutExtensions(outputPath)
|
|
380
|
-
return /index\.[a-z]+$/.test(path.basename(outputPath)) ? path.relative(process.cwd(), path.dirname(outputPath)) : path.relative(process.cwd(), outputPath)
|
|
136
|
+
return `!(${markupDefaultExcludes.join('|')})/**/*.+(${this.engine.markupExtensions})`
|
|
381
137
|
}
|
|
382
138
|
|
|
383
|
-
|
|
384
|
-
switch (path.extname(outputPath)) {
|
|
385
|
-
case '.md':
|
|
386
|
-
outputPath = outputPath.replace(/\.md$/, '.html')
|
|
387
|
-
break
|
|
388
|
-
case '.njk':
|
|
389
|
-
outputPath = outputPath.replace(/\.njk$/, '.html')
|
|
390
|
-
break
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
return outputPath
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
compileEntry(templateName, additionalContext) {
|
|
139
|
+
async compileEntry(templateName, additionalContext) {
|
|
397
140
|
const context = { page: {} }
|
|
398
141
|
let pageUrl
|
|
399
142
|
|
|
@@ -409,184 +152,205 @@ export default class Markups {
|
|
|
409
152
|
const frontMatterResult = parseFrontMatter(templateName)
|
|
410
153
|
context.page = frontMatterResult.frontMatter
|
|
411
154
|
} catch (err) {
|
|
412
|
-
|
|
413
|
-
console.
|
|
155
|
+
log({ tag: 'error', text: 'Failed parsing front matter:', link: templateName })
|
|
156
|
+
console.error(err)
|
|
414
157
|
}
|
|
415
158
|
|
|
416
159
|
if (pageUrl) context.page.url = pageUrl
|
|
417
160
|
|
|
418
|
-
const
|
|
419
|
-
return new Promise((resolve, reject) => {
|
|
420
|
-
env.getTemplate(templateName).render(context, (error, result) => {
|
|
421
|
-
if (!error) {
|
|
422
|
-
resolve(result)
|
|
423
|
-
} else {
|
|
424
|
-
reject(error)
|
|
425
|
-
}
|
|
426
|
-
})
|
|
427
|
-
})
|
|
428
|
-
}
|
|
161
|
+
const frontMatter = context.page
|
|
429
162
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
return indexFiles[0]
|
|
434
|
-
}
|
|
163
|
+
if (frontMatter && frontMatter.published === false) {
|
|
164
|
+
return { result: '', frontMatter, skipped: true }
|
|
165
|
+
}
|
|
435
166
|
|
|
436
|
-
|
|
437
|
-
|
|
167
|
+
const result = await this.engine.render(templateName, context)
|
|
168
|
+
return { result, frontMatter }
|
|
169
|
+
}
|
|
438
170
|
|
|
439
|
-
|
|
440
|
-
|
|
171
|
+
async compileDirectory(markupIn, collectionData, pageEntries) {
|
|
172
|
+
const markupStart = performance.now()
|
|
173
|
+
const markupFiles = [
|
|
174
|
+
...globSync(path.join(markupIn, this.generateMarkupGlobPattern(this.includePaths))),
|
|
175
|
+
...globSync(path.join(markupIn, `*.+(${this.engine.markupExtensions})`))
|
|
176
|
+
]
|
|
177
|
+
const compilePromises = []
|
|
178
|
+
const indexableExtensions = this.engine.indexableExtensions
|
|
441
179
|
|
|
442
|
-
|
|
180
|
+
for (const file of markupFiles) {
|
|
181
|
+
const relativePath = path.relative(markupIn, file)
|
|
182
|
+
const relativePathParts = relativePath.split(path.sep)
|
|
443
183
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
pageItems = []
|
|
450
|
-
}
|
|
451
|
-
pageItems.push(item)
|
|
184
|
+
if (relativePathParts.length > 1 &&
|
|
185
|
+
collectionData[relativePathParts[0]] &&
|
|
186
|
+
relativePathParts[1].startsWith('index.') && indexableExtensions.has(path.extname(relativePathParts[1])) &&
|
|
187
|
+
collectionData[relativePathParts[0]].items.length > 0) {
|
|
188
|
+
continue
|
|
452
189
|
}
|
|
453
|
-
collection.pages.push(pageItems)
|
|
454
|
-
|
|
455
|
-
collection.totalPages = collection.pages.length
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
190
|
|
|
459
|
-
|
|
460
|
-
|
|
191
|
+
let markupOut = path.join(process.cwd(), this.markupOut, relativePath)
|
|
192
|
+
const fromPath = path.join(process.cwd(), this.markupOut)
|
|
193
|
+
const markupOutDir = path.dirname(markupOut)
|
|
461
194
|
|
|
462
|
-
|
|
463
|
-
const collection = collectionData[collectionName]
|
|
464
|
-
const file = this.getCollectionIndexFile(collectionName)
|
|
195
|
+
mkDir(markupOutDir)
|
|
465
196
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
197
|
+
const fileContext = {
|
|
198
|
+
...collectionData,
|
|
199
|
+
relativePathPrefix: getRelativePathPrefix(markupOutDir, fromPath),
|
|
200
|
+
_url: getPageUrl(markupOut)
|
|
469
201
|
}
|
|
470
202
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
203
|
+
const shouldIndex = pageEntries && indexableExtensions.has(path.extname(file))
|
|
204
|
+
const fileCollection = relativePathParts.length > 1 && collectionData[relativePathParts[0]]
|
|
205
|
+
? relativePathParts[0]
|
|
206
|
+
: null
|
|
207
|
+
|
|
208
|
+
const compilePromise = this.compileEntry(file, fileContext).then(({ result, frontMatter, skipped }) => {
|
|
209
|
+
if (skipped) {
|
|
210
|
+
const outFile = replaceOutExtensions(markupOut)
|
|
211
|
+
if (fs.existsSync(outFile)) {
|
|
212
|
+
fs.unlinkSync(outFile)
|
|
213
|
+
log({ tag: this.logTag, text: 'Removed unpublished:', link: path.relative(process.cwd(), outFile) })
|
|
214
|
+
}
|
|
215
|
+
return
|
|
481
216
|
}
|
|
217
|
+
markupOut = replaceOutExtensions(markupOut)
|
|
218
|
+
fs.writeFileSync(markupOut, result)
|
|
482
219
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
220
|
+
if (shouldIndex && frontMatter.published !== false) {
|
|
221
|
+
if (!frontMatter.title) frontMatter.title = path.basename(file, path.extname(file))
|
|
222
|
+
if (fileCollection && !frontMatter.collection) frontMatter.collection = fileCollection
|
|
486
223
|
|
|
487
|
-
|
|
224
|
+
pageEntries.push({
|
|
225
|
+
...frontMatter,
|
|
226
|
+
url: getPageUrlRelativeToOutput(markupOut, this.markupOut),
|
|
227
|
+
content: result,
|
|
228
|
+
isIndex: false
|
|
229
|
+
})
|
|
230
|
+
}
|
|
231
|
+
})
|
|
232
|
+
compilePromises.push(compilePromise)
|
|
233
|
+
}
|
|
488
234
|
|
|
489
|
-
|
|
490
|
-
|
|
235
|
+
try {
|
|
236
|
+
await Promise.all(compilePromises)
|
|
237
|
+
const markupEnd = performance.now()
|
|
238
|
+
log({ tag: this.logTag, text: `Compiled: ${markupFiles.length} file${markupFiles.length > 1 ? 's' : ''} into`, link: this.markupOut, time: buildTime(markupStart, markupEnd) })
|
|
239
|
+
} catch (err) {
|
|
240
|
+
log({ tag: this.logTag, error: true, text: 'Failed compiling' })
|
|
241
|
+
console.error(err)
|
|
242
|
+
throw err
|
|
243
|
+
} finally {
|
|
244
|
+
clearFrontMatterCache()
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
async compileSingleFile(markupIn, collectionData, pageEntries) {
|
|
249
|
+
const markupStart = performance.now()
|
|
250
|
+
let markupOut = path.join(process.cwd(), this.markupOut)
|
|
251
|
+
const markupOutDir = path.dirname(markupOut)
|
|
252
|
+
const indexableExtensions = this.engine.indexableExtensions
|
|
253
|
+
mkDir(markupOutDir)
|
|
254
|
+
|
|
255
|
+
const fileContext = {
|
|
256
|
+
...collectionData,
|
|
257
|
+
relativePathPrefix: getRelativePathPrefix(markupOutDir),
|
|
258
|
+
_url: getPageUrl(markupOut)
|
|
259
|
+
}
|
|
491
260
|
|
|
492
|
-
|
|
493
|
-
|
|
261
|
+
const shouldIndex = pageEntries && indexableExtensions.has(path.extname(markupIn))
|
|
262
|
+
|
|
263
|
+
try {
|
|
264
|
+
const { result, frontMatter, skipped } = await this.compileEntry(markupIn, fileContext)
|
|
265
|
+
markupOut = replaceOutExtensions(markupOut)
|
|
266
|
+
|
|
267
|
+
if (skipped) {
|
|
268
|
+
if (fs.existsSync(markupOut)) {
|
|
269
|
+
fs.unlinkSync(markupOut)
|
|
270
|
+
log({ tag: this.logTag, text: 'Removed unpublished:', link: path.relative(process.cwd(), markupOut) })
|
|
494
271
|
}
|
|
272
|
+
return
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
fs.writeFileSync(markupOut, result)
|
|
495
276
|
|
|
496
|
-
|
|
497
|
-
|
|
277
|
+
if (shouldIndex && frontMatter.published !== false) {
|
|
278
|
+
if (!frontMatter.title) frontMatter.title = path.basename(markupIn, path.extname(markupIn))
|
|
279
|
+
pageEntries.push({
|
|
280
|
+
...frontMatter,
|
|
281
|
+
url: getPageUrlRelativeToOutput(markupOut, this.markupOut),
|
|
282
|
+
content: result,
|
|
283
|
+
isIndex: false
|
|
498
284
|
})
|
|
499
|
-
compilePromises.push(compilePromise)
|
|
500
285
|
}
|
|
286
|
+
|
|
287
|
+
const markupEnd = performance.now()
|
|
288
|
+
log({ tag: this.logTag, text: 'Compiled:', link: path.relative(process.cwd(), path.join(process.cwd(), this.markupOut, path.basename(markupIn))), time: buildTime(markupStart, markupEnd) })
|
|
289
|
+
} catch (err) {
|
|
290
|
+
log({ tag: this.logTag, error: true, text: 'Failed compiling:', link: path.relative(process.cwd(), path.join(process.cwd(), this.markupOut, path.basename(markupIn))) })
|
|
291
|
+
console.error(err)
|
|
292
|
+
throw err
|
|
293
|
+
} finally {
|
|
294
|
+
clearFrontMatterCache()
|
|
501
295
|
}
|
|
502
296
|
}
|
|
503
297
|
|
|
504
|
-
compile() {
|
|
505
|
-
|
|
298
|
+
async compile() {
|
|
299
|
+
const moduleConfig = this.config.markup
|
|
300
|
+
if (!moduleConfig || !moduleConfig.in) return
|
|
506
301
|
|
|
507
|
-
if (this.config.
|
|
508
|
-
for (const [name, html] of Object.entries(this.config.
|
|
509
|
-
this.
|
|
302
|
+
if (this.config.reactorData) {
|
|
303
|
+
for (const [name, html] of Object.entries(this.config.reactorData)) {
|
|
304
|
+
this.engine.setGlobal(name, html)
|
|
510
305
|
}
|
|
511
306
|
}
|
|
512
307
|
|
|
513
|
-
const markupIn = path.join(process.cwd(), this.
|
|
514
|
-
const compilePromises = []
|
|
308
|
+
const markupIn = path.join(process.cwd(), this.markupIn)
|
|
515
309
|
|
|
516
310
|
if (!pathExists(markupIn)) {
|
|
517
|
-
|
|
311
|
+
log({ tag: 'error', text: 'Markup path does not exist:', link: markupIn })
|
|
518
312
|
return
|
|
519
313
|
}
|
|
520
314
|
|
|
521
315
|
const collectionData = {
|
|
522
|
-
...this.
|
|
523
|
-
...
|
|
316
|
+
...collectionAutoDiscovery(this.markupIn),
|
|
317
|
+
...getCollectionDataBasedOnConfig(this.markupIn, this.collectionsConfig)
|
|
524
318
|
}
|
|
525
319
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
this.generateCollectionPaginationPages(collectionData, compilePromises)
|
|
320
|
+
const shouldIndex = this.searchIndexConfig || this.sitemapConfig
|
|
321
|
+
const pageEntries = shouldIndex ? [] : null
|
|
529
322
|
|
|
530
|
-
|
|
531
|
-
|
|
323
|
+
buildCollectionPaginationData(collectionData)
|
|
324
|
+
const collectionPromises = generateCollectionPaginationPages(collectionData, this.markupIn, this.markupOut, this.compileEntry.bind(this))
|
|
532
325
|
|
|
533
|
-
|
|
534
|
-
const relativePath = path.relative(markupIn, file)
|
|
535
|
-
const relativePathParts = relativePath.split(path.sep)
|
|
326
|
+
await Promise.all(collectionPromises)
|
|
536
327
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
collectionData[relativePathParts[0]].items.length > 0) {
|
|
542
|
-
return
|
|
543
|
-
}
|
|
328
|
+
if (pageEntries) {
|
|
329
|
+
for (const collectionName of Object.keys(collectionData)) {
|
|
330
|
+
const collection = collectionData[collectionName]
|
|
331
|
+
const totalPages = collection.totalPages || 1
|
|
544
332
|
|
|
545
|
-
let
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
markupOut = this.replaceOutExtensions(markupOut)
|
|
556
|
-
fs.writeFileSync(markupOut, result)
|
|
557
|
-
})
|
|
558
|
-
compilePromises.push(compilePromise)
|
|
559
|
-
})
|
|
333
|
+
for (let i = 0; i < totalPages; i++) {
|
|
334
|
+
const pageUrl = i === 0 ? collectionName : `${collectionName}/${i + 1}`
|
|
335
|
+
pageEntries.push({
|
|
336
|
+
url: pageUrl,
|
|
337
|
+
title: collectionName,
|
|
338
|
+
isIndex: true
|
|
339
|
+
})
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
560
343
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
console.log(`${pstyle.cyanBright + pstyle.bold}[markup]${pstyle.reset} ${pstyle.dim}Compiled: ${pstyle.reset}${markupFiles.length} file${markupFiles.length > 1 ? 's' : ''} into ${pstyle.italic + pstyle.underline}${this.config.markup.out}${pstyle.reset}`)
|
|
564
|
-
clearFrontMatterCache()
|
|
565
|
-
resolve()
|
|
566
|
-
}).catch((err) => {
|
|
567
|
-
console.log(`${pstyle.cyanBright + pstyle.bold}[markup]${pstyle.reset} ${pstyle.redBright + pstyle.bold}[error]${pstyle.reset} ${pstyle.dim}Failed compiling${pstyle.reset + pstyle.bell}`)
|
|
568
|
-
console.log(err)
|
|
569
|
-
clearFrontMatterCache()
|
|
570
|
-
reject(err)
|
|
571
|
-
})
|
|
572
|
-
})
|
|
344
|
+
if (pathIsDirectory(markupIn)) {
|
|
345
|
+
await this.compileDirectory(markupIn, collectionData, pageEntries)
|
|
573
346
|
} else {
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
mkDir(markupOutDir)
|
|
577
|
-
|
|
578
|
-
collectionData.relativePathPrefix = this.getRelativePathPrefix(markupOutDir)
|
|
579
|
-
collectionData._url = this.getPageUrl(markupOut)
|
|
347
|
+
await this.compileSingleFile(markupIn, collectionData, pageEntries)
|
|
348
|
+
}
|
|
580
349
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
clearFrontMatterCache()
|
|
586
|
-
}).catch((err) => {
|
|
587
|
-
console.log(`${pstyle.cyanBright + pstyle.bold}[markup]${pstyle.reset} ${pstyle.redBright + pstyle.bold}[error]${pstyle.reset} ${pstyle.dim}Failed compiling:${pstyle.reset} ${pstyle.italic + pstyle.underline}${path.relative(process.cwd(), path.join(process.cwd(), this.config.markup.out, path.basename(markupIn)))}${pstyle.reset + pstyle.bell}`)
|
|
588
|
-
console.log(err)
|
|
589
|
-
clearFrontMatterCache()
|
|
350
|
+
if (shouldIndex && pageEntries) {
|
|
351
|
+
generateIndexFiles(pageEntries, this.markupOut, this.siteData.url, {
|
|
352
|
+
searchIndex: this.searchIndexConfig,
|
|
353
|
+
sitemap: this.sitemapConfig
|
|
590
354
|
})
|
|
591
355
|
}
|
|
592
356
|
}
|