@tenjuu99/blog 0.1.8 → 0.1.10
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 +2 -0
- package/helper/index.js +63 -0
- package/lib/applyTemplate.js +13 -5
- package/lib/cssGenerator.js +6 -5
- package/lib/distribute.js +40 -11
- package/lib/files.js +43 -0
- package/lib/filter.js +7 -48
- package/lib/generate.js +8 -5
- package/lib/includeFilter.js +15 -15
- package/lib/indexer.js +19 -24
- package/lib/pageData.js +1 -14
- package/lib/render.js +8 -4
- package/lib/replaceVariablesFilter.js +54 -0
- package/lib/server.js +2 -1
- package/package.json +1 -1
- package/src-sample/pages/index.md +1 -28
- package/src-sample/pages/post/1.md +1 -0
- package/src-sample/pages/post/2.md +1 -0
- package/src-sample/pages/post/3.md +1 -0
- package/src-sample/pages/post/index.md +7 -0
- package/src-sample/template/default.html +2 -5
- package/src-sample/template/index.html +0 -3
package/README.md
CHANGED
package/helper/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { allData } from "../lib/indexer.js";
|
|
2
2
|
import { replaceVariablesFilter } from "../lib/filter.js";
|
|
3
|
+
import config from '../lib/config.js'
|
|
3
4
|
|
|
4
5
|
export function readIndex (filter = null) {
|
|
5
6
|
const data = Object.entries(allData)
|
|
@@ -51,3 +52,65 @@ export function indexedItems() {
|
|
|
51
52
|
indexedItemsSorted = sorted
|
|
52
53
|
return indexedItemsSorted
|
|
53
54
|
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 配列を再帰的に順不同リストに変換する
|
|
58
|
+
* @param {Array|string} arrayOrText
|
|
59
|
+
* @returns {mixed}
|
|
60
|
+
*/
|
|
61
|
+
export function arrayToList(arrayOrText) {
|
|
62
|
+
if (typeof arrayOrText === 'string') {
|
|
63
|
+
return `<li>${arrayOrText}</li>`
|
|
64
|
+
}
|
|
65
|
+
if (Array.isArray(arrayOrText)) {
|
|
66
|
+
let resultListText = '<ul>'
|
|
67
|
+
for (const item of arrayOrText) {
|
|
68
|
+
if (Array.isArray(item)) {
|
|
69
|
+
resultListText += `<li>${arrayToList(item)}</li>`
|
|
70
|
+
} else {
|
|
71
|
+
resultListText += `<li>${item}</li>`
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
resultListText += '</ul>'
|
|
75
|
+
arrayOrText = resultListText
|
|
76
|
+
}
|
|
77
|
+
return arrayOrText
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function renderIndex(pages, nodate = 'nodate', headingTag = 'h3') {
|
|
81
|
+
if (!pages) {
|
|
82
|
+
pages = readIndex()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const renderList = {}
|
|
86
|
+
for (const page of pages) {
|
|
87
|
+
if (page.index) {
|
|
88
|
+
const url = config.relative_path ? config.relative_path + page.url : page.url
|
|
89
|
+
if (page.published === '1970-01-01') {
|
|
90
|
+
if (!renderList[nodate]) {
|
|
91
|
+
renderList[nodate] = []
|
|
92
|
+
}
|
|
93
|
+
renderList[nodate].push(`<a href="${url}">${page.title}</a>`)
|
|
94
|
+
continue
|
|
95
|
+
}
|
|
96
|
+
const published = new Date(page.published)
|
|
97
|
+
const year = `${published.getFullYear()}年`
|
|
98
|
+
const date = `${published.getMonth() +1}月${published.getDate()}日`
|
|
99
|
+
if (!renderList[year]) {
|
|
100
|
+
renderList[year] = []
|
|
101
|
+
}
|
|
102
|
+
renderList[year].push(`<a href="${url}">${page.title}</a> (${date})`)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const resultText = []
|
|
107
|
+
for (const key in renderList) {
|
|
108
|
+
resultText.push(`<${headingTag}>${key}</${headingTag}>`)
|
|
109
|
+
if (Array.isArray(renderList[key])) {
|
|
110
|
+
resultText.push(arrayToList(renderList[key]))
|
|
111
|
+
} else {
|
|
112
|
+
resultText.push(`<p>${renderList[key]}</p>`)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return resultText.join('\n')
|
|
116
|
+
}
|
package/lib/applyTemplate.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict"
|
|
2
|
-
import fs from "node:fs/promises";
|
|
3
2
|
import applyCss from './cssGenerator.js'
|
|
4
|
-
import
|
|
3
|
+
import includeFilter from './includeFilter.js'
|
|
5
4
|
import { templateDir, cssDir } from './dir.js'
|
|
5
|
+
import { staticFile, staticFiles, warmUp } from './files.js'
|
|
6
6
|
import { watchers } from './watcher.js'
|
|
7
7
|
|
|
8
8
|
let templates = {}
|
|
@@ -11,16 +11,24 @@ const applyTemplate = async (name = 'default.html') => {
|
|
|
11
11
|
if (templates[name]) {
|
|
12
12
|
return templates[name]
|
|
13
13
|
}
|
|
14
|
-
let templateContent = await
|
|
15
|
-
templateContent =
|
|
14
|
+
let templateContent = await staticFile(`template/${name}`)
|
|
15
|
+
templateContent = includeFilter(templateContent)
|
|
16
16
|
templateContent = await applyCss(templateContent)
|
|
17
17
|
templates[name] = templateContent
|
|
18
18
|
return templateContent
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
const warmUpTemplate = async () => {
|
|
22
|
+
await warmUp()
|
|
23
|
+
const templates = staticFiles()
|
|
24
|
+
.filter(file => file[0].indexOf('template/') === 0)
|
|
25
|
+
.map(f => applyTemplate(f[0].split('/')[1]))
|
|
26
|
+
await Promise.all(templates)
|
|
27
|
+
}
|
|
28
|
+
|
|
21
29
|
watchers.push({
|
|
22
30
|
paths: [cssDir, templateDir],
|
|
23
31
|
callback: () => { templates = {} }
|
|
24
32
|
})
|
|
25
33
|
|
|
26
|
-
export
|
|
34
|
+
export { applyTemplate, warmUpTemplate }
|
package/lib/cssGenerator.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use strict"
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
|
+
import { staticFile } from './files.js'
|
|
3
4
|
import { minifyCss } from './minify.js'
|
|
4
5
|
import { createHash } from 'crypto'
|
|
5
6
|
import path from 'path'
|
|
6
|
-
import { distDir
|
|
7
|
+
import { distDir, cssDir } from './dir.js'
|
|
7
8
|
import { watchers } from './watcher.js'
|
|
8
9
|
import { styleText } from 'node:util'
|
|
9
10
|
import config from './config.js'
|
|
@@ -26,14 +27,14 @@ const cssGenerator = async (src, dist) => {
|
|
|
26
27
|
}
|
|
27
28
|
let css = ''
|
|
28
29
|
for (const cssFile of src.split(',')) {
|
|
29
|
-
css +=
|
|
30
|
+
css += staticFile(`css/${cssFile}`)
|
|
30
31
|
}
|
|
31
32
|
css = minifyCss(css)
|
|
32
33
|
cacheBuster[key] = createHash('md5').update(css).digest('hex')
|
|
33
34
|
|
|
34
|
-
return await fs.mkdir(`${
|
|
35
|
-
fs.writeFile(`${
|
|
36
|
-
console.log(styleText('green', '[generate]'), `${src} => ${
|
|
35
|
+
return await fs.mkdir(`${distDir}${path.dirname(dist)}`, { recursive: true }).then(() => {
|
|
36
|
+
fs.writeFile(`${distDir}${dist}`, css)
|
|
37
|
+
console.log(styleText('green', '[generate]'), `${src} => ${distDir}${dist}`)
|
|
37
38
|
return cacheBuster[key]
|
|
38
39
|
})
|
|
39
40
|
}
|
package/lib/distribute.js
CHANGED
|
@@ -1,23 +1,33 @@
|
|
|
1
1
|
"use strict"
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
3
4
|
import path from 'path'
|
|
4
5
|
import { minifyHtml } from './minify.js'
|
|
5
6
|
import render from './render.js'
|
|
6
7
|
import { styleText } from 'node:util'
|
|
7
8
|
import config from './config.js'
|
|
9
|
+
import { cacheDir } from './dir.js'
|
|
10
|
+
import { applyTemplate, warmUpTemplate } from './applyTemplate.js'
|
|
8
11
|
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
const indexFile = `${cacheDir}/index.json`
|
|
13
|
+
|
|
14
|
+
const renderPage = async (page) => {
|
|
15
|
+
const template = page.template
|
|
16
|
+
return [page.name, await render(template, page)]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const distribute = async (data, srcDir, distDir) => {
|
|
20
|
+
await warmUpTemplate()
|
|
21
|
+
const promises = []
|
|
22
|
+
const newIndex = []
|
|
17
23
|
for (const name in data) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
24
|
+
promises.push(renderPage(data[name]))
|
|
25
|
+
newIndex.push({ name: data.name, url: data.url, __output: data.__output })
|
|
26
|
+
}
|
|
27
|
+
const renderedString = await Promise.all(promises)
|
|
28
|
+
for (const page of renderedString) {
|
|
29
|
+
const [ pageName, rendered ] = page
|
|
30
|
+
let writeTo = `${distDir}${data[pageName].__output}`
|
|
21
31
|
fs.mkdir(path.dirname(writeTo), { recursive: true}).then(() => {
|
|
22
32
|
fs.writeFile(writeTo, minifyHtml(rendered))
|
|
23
33
|
console.log(styleText('green', '[generate]'), writeTo)
|
|
@@ -33,6 +43,25 @@ const distribute = async (data, deleted, srcDir, distDir) => {
|
|
|
33
43
|
})
|
|
34
44
|
})
|
|
35
45
|
})
|
|
46
|
+
|
|
47
|
+
if (!existsSync(cacheDir)) {
|
|
48
|
+
mkdirSync(cacheDir)
|
|
49
|
+
}
|
|
50
|
+
fs.readFile(indexFile, 'utf8')
|
|
51
|
+
.then(text => {
|
|
52
|
+
const oldIndex = JSON.parse(text)
|
|
53
|
+
deleted = oldIndex.filter(oi => !newIndex.map(ni => ni.__output).includes(oi.__output))
|
|
54
|
+
fs.writeFile(indexFile, JSON.stringify(newIndex))
|
|
55
|
+
if (deleted) {
|
|
56
|
+
for (const obj of deleted) {
|
|
57
|
+
console.log(styleText('red', '[unlink]'), `${distDir}${obj.__output}`)
|
|
58
|
+
fs.unlink(`${distDir}${obj.__output}`)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
.catch(error => {
|
|
63
|
+
fs.writeFile(indexFile, JSON.stringify(newIndex))
|
|
64
|
+
})
|
|
36
65
|
}
|
|
37
66
|
|
|
38
67
|
export default distribute
|
package/lib/files.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import { templateDir, cssDir } from './dir.js'
|
|
3
|
+
import { watchers } from './watcher.js'
|
|
4
|
+
|
|
5
|
+
let staticFilesContainer = {}
|
|
6
|
+
let loaded = false
|
|
7
|
+
|
|
8
|
+
const warmUp = async () => {
|
|
9
|
+
if (loaded) {
|
|
10
|
+
return
|
|
11
|
+
}
|
|
12
|
+
const templateFiles = await fs.readdir(templateDir).then(files => files.map(f => [`template/${f}`, `${templateDir}/${f}`]))
|
|
13
|
+
const cssFiles = await fs.readdir(cssDir).then(files => files.map(f => [`css/${f}`, `${cssDir}/${f}`]))
|
|
14
|
+
const files = [...templateFiles, ...cssFiles]
|
|
15
|
+
const loadFiles = files.map(file => fs.readFile(file[1], 'utf8').then(content => [file[0], content]))
|
|
16
|
+
staticFilesContainer = Object.fromEntries(await Promise.all(loadFiles))
|
|
17
|
+
loaded = true
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const staticFile = (name) => {
|
|
21
|
+
if (!loaded) {
|
|
22
|
+
throw new Error('not initialized')
|
|
23
|
+
}
|
|
24
|
+
if (staticFilesContainer[name]) {
|
|
25
|
+
return staticFilesContainer[name]
|
|
26
|
+
}
|
|
27
|
+
if (name.indexOf('template/') === 0) {
|
|
28
|
+
return fs.readFile(templateDir + '/' + name.replace('template/', ''))
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const staticFiles = () => {
|
|
33
|
+
return Object.entries(staticFilesContainer)
|
|
34
|
+
}
|
|
35
|
+
watchers.push({
|
|
36
|
+
paths: [cssDir, templateDir],
|
|
37
|
+
callback: async () => {
|
|
38
|
+
loaded = false
|
|
39
|
+
await warmUp()
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
export { staticFile, staticFiles, warmUp }
|
package/lib/filter.js
CHANGED
|
@@ -1,38 +1,7 @@
|
|
|
1
1
|
import helper from '../lib/helper.js'
|
|
2
|
-
import includeFilter from './includeFilter.js'
|
|
3
2
|
import { srcDir } from './dir.js'
|
|
4
3
|
import config from './config.js'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* @param {string} text
|
|
8
|
-
* @params {object} variables
|
|
9
|
-
* @return {text}
|
|
10
|
-
*/
|
|
11
|
-
const replaceVariablesFilter = (text, variables) => {
|
|
12
|
-
const matched = [...text.matchAll(/(\\)?{{\s?([\w\d\s,-_\(\)]+)\s?}}/g)]
|
|
13
|
-
const replace = Object.fromEntries(matched.map(match => [match[2].toLowerCase(), {replace: match[0], backslash: match[1]}]))
|
|
14
|
-
let replaced = text
|
|
15
|
-
for (const elm in replace) {
|
|
16
|
-
const toBeReplace = replace[elm].replace
|
|
17
|
-
const toBeReplaceScript = toBeReplace.match(/([\w\d_]+)\((.*)\)/)
|
|
18
|
-
|
|
19
|
-
if (replace[elm].backslash) { // escape variable syntax
|
|
20
|
-
const removeBackslash = replace[elm].replace.replace(/\\/, '')
|
|
21
|
-
replaced = replaced.replaceAll(toBeReplace, removeBackslash)
|
|
22
|
-
} else if (toBeReplaceScript) { // execute helper
|
|
23
|
-
const func = toBeReplaceScript[1]
|
|
24
|
-
const args = toBeReplaceScript[2].split(',').map(v => variables[v.trim()] ?? undefined)
|
|
25
|
-
if (!helper[func]) {
|
|
26
|
-
throw new Error('helper function is missing. function name: ' + func);
|
|
27
|
-
}
|
|
28
|
-
const replaceText = helper[func].call(null, ...args)
|
|
29
|
-
replaced = replaced.replaceAll(toBeReplace, replaceText)
|
|
30
|
-
} else {
|
|
31
|
-
replaced = replaced.replaceAll(toBeReplace, variables[elm])
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return replaced
|
|
35
|
-
}
|
|
4
|
+
import replaceVariablesFilter from './replaceVariablesFilter.js'
|
|
36
5
|
|
|
37
6
|
/**
|
|
38
7
|
* @param {string} condition
|
|
@@ -136,22 +105,13 @@ const replaceScriptFilter = async (text, variables) => {
|
|
|
136
105
|
if (result instanceof Promise) {
|
|
137
106
|
result = await result
|
|
138
107
|
}
|
|
139
|
-
|
|
140
|
-
|
|
108
|
+
switch (typeof result) {
|
|
109
|
+
case 'undefined':
|
|
110
|
+
case 'null':
|
|
111
|
+
result = ''
|
|
141
112
|
}
|
|
142
113
|
if (Array.isArray(result)) {
|
|
143
|
-
result = arrayToList(result)
|
|
144
|
-
} else if (typeof result === 'object') {
|
|
145
|
-
const resultText = []
|
|
146
|
-
for (const key in result) {
|
|
147
|
-
resultText.push(`<h3>${key}</h3>`)
|
|
148
|
-
if (Array.isArray(result[key])) {
|
|
149
|
-
resultText.push(arrayToList(result[key]))
|
|
150
|
-
} else {
|
|
151
|
-
resultText.push(`<p>${result[key]}</p>`)
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
result = resultText.join('\n')
|
|
114
|
+
result = helper.arrayToList(result)
|
|
155
115
|
}
|
|
156
116
|
replaced = replaced.replace(script.replace, result)
|
|
157
117
|
}
|
|
@@ -161,6 +121,5 @@ const replaceScriptFilter = async (text, variables) => {
|
|
|
161
121
|
export {
|
|
162
122
|
replaceIfFilter,
|
|
163
123
|
replaceScriptFilter,
|
|
164
|
-
replaceVariablesFilter
|
|
165
|
-
includeFilter
|
|
124
|
+
replaceVariablesFilter
|
|
166
125
|
}
|
package/lib/generate.js
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
"use strict"
|
|
2
2
|
import distribute from './distribute.js'
|
|
3
|
-
import { indexing, allData
|
|
3
|
+
import { indexing, allData } from './indexer.js'
|
|
4
4
|
import { srcDir, distDir } from './dir.js'
|
|
5
5
|
import { styleText } from 'node:util'
|
|
6
6
|
|
|
7
7
|
const generate = async () => {
|
|
8
|
-
|
|
8
|
+
let start = performance.now()
|
|
9
9
|
await indexing()
|
|
10
|
+
let end = performance.now()
|
|
11
|
+
console.log(styleText('blue', '[indexing: ' + (end - start) + "ms]"))
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
start = performance.now()
|
|
14
|
+
await distribute(allData, srcDir, distDir)
|
|
15
|
+
end = performance.now()
|
|
16
|
+
console.log(styleText('blue', '[distribute: ' + (end - start) + "ms]"))
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
export default generate
|
package/lib/includeFilter.js
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
1
|
import { minifyCss } from './minify.js'
|
|
3
2
|
import { templateDir, cssDir } from './dir.js'
|
|
3
|
+
import { staticFile } from './files.js'
|
|
4
4
|
|
|
5
5
|
const alreadyLoaded = {}
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const includeRegexp = new RegExp(/\{\s*include\('(template|css)\/([\w\./]+)'\)\s*\}/g)
|
|
8
|
+
|
|
9
|
+
const includeFilter = (text) => {
|
|
8
10
|
let replaced = text
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
+
const include = [...text.matchAll(includeRegexp)].map(matched => {
|
|
12
|
+
return { toBeReplace: matched[0], type: matched[1], filename: matched[2] }
|
|
13
|
+
})
|
|
14
|
+
if (include.length === 0) {
|
|
15
|
+
return replaced
|
|
16
|
+
}
|
|
11
17
|
for (const index in include) {
|
|
12
|
-
const
|
|
18
|
+
const {toBeReplace, type, filename} = include[index]
|
|
13
19
|
let content
|
|
14
20
|
const cacheKey = `${type}/${filename}`
|
|
15
21
|
if (!alreadyLoaded[cacheKey]) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
break
|
|
20
|
-
case 'css':
|
|
21
|
-
content = await fs.readFile(`${cssDir}/${filename}`, 'utf8')
|
|
22
|
-
break
|
|
23
|
-
default:
|
|
24
|
-
throw new Error('type does not match neither `template` nor `css`.');
|
|
22
|
+
content = staticFile(cacheKey)
|
|
23
|
+
if (typeof content === 'undefined') {
|
|
24
|
+
throw new Error(cacheKey + ' is not found')
|
|
25
25
|
}
|
|
26
26
|
// include を再帰的に解決する
|
|
27
27
|
if (content.match(includeRegexp)) {
|
|
28
|
-
content =
|
|
28
|
+
content = includeFilter(content)
|
|
29
29
|
}
|
|
30
30
|
alreadyLoaded[cacheKey] = content
|
|
31
31
|
} else {
|
package/lib/indexer.js
CHANGED
|
@@ -1,45 +1,40 @@
|
|
|
1
1
|
"use strict"
|
|
2
|
-
import {
|
|
3
|
-
import { readdirSync
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { readdirSync } from "node:fs";
|
|
4
4
|
import { pageDir, cacheDir } from './dir.js'
|
|
5
5
|
import makePageData from './pageData.js'
|
|
6
6
|
|
|
7
|
-
const indexFile = `${cacheDir}/index.json`
|
|
8
|
-
|
|
9
|
-
let newIndex = []
|
|
10
7
|
let allData = {}
|
|
11
|
-
let deleted = []
|
|
12
8
|
|
|
13
|
-
|
|
9
|
+
/**
|
|
10
|
+
* @param {string} dir
|
|
11
|
+
* @param {string} namePrefix
|
|
12
|
+
* @param {Array<Promise>} promises
|
|
13
|
+
* @return {Promise<string[]>[]}
|
|
14
|
+
*/
|
|
15
|
+
const collect = (dir, namePrefix = '', promises = []) => {
|
|
14
16
|
const dirents = readdirSync(dir, { withFileTypes: true })
|
|
15
17
|
dirents.forEach((dirent) => {
|
|
16
18
|
if (dirent.isDirectory()) {
|
|
17
|
-
collect(`${dirent.path}/${dirent.name}`,
|
|
19
|
+
collect(`${dirent.path}/${dirent.name}`, namePrefix + dirent.name + '/', promises)
|
|
18
20
|
} else {
|
|
19
21
|
if (dirent.name.match(/\.(md|html)$/)) {
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
const { name, url, __output } = pageData
|
|
23
|
-
newIndex.push({ name, url, __output })
|
|
22
|
+
const name = `${namePrefix}${dirent.name}`
|
|
23
|
+
promises.push(readFile(`${dir}/${dirent.name}`, 'utf8').then(f => [name, f]))
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
})
|
|
27
|
+
return promises
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
const indexing = async () => {
|
|
30
|
-
newIndex = []
|
|
31
31
|
allData = {}
|
|
32
|
-
deleted = []
|
|
33
|
-
if (!existsSync(cacheDir)) {
|
|
34
|
-
mkdirSync(cacheDir)
|
|
35
|
-
}
|
|
36
|
-
const oldIndex = await readFile(indexFile, 'utf8').then(text => JSON.parse(text)).catch(error => [])
|
|
37
|
-
|
|
38
|
-
collect(pageDir)
|
|
39
|
-
writeFile(indexFile, JSON.stringify(newIndex))
|
|
40
32
|
|
|
41
|
-
|
|
42
|
-
|
|
33
|
+
const files = await Promise.all(collect(pageDir))
|
|
34
|
+
files.forEach((file) => {
|
|
35
|
+
const pageData = makePageData(file[0], file[1])
|
|
36
|
+
allData[pageData.name] = pageData
|
|
37
|
+
})
|
|
43
38
|
}
|
|
44
39
|
|
|
45
|
-
export { indexing, allData
|
|
40
|
+
export { indexing, allData }
|
package/lib/pageData.js
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
"use strict"
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
import { pageDir } from './dir.js'
|
|
4
2
|
import config from './config.js'
|
|
5
3
|
|
|
6
|
-
const
|
|
7
|
-
return fs.readFileSync(path, 'utf8')
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const makePageData = (filename) => {
|
|
11
|
-
const content = load(`${pageDir}/${filename}`)
|
|
4
|
+
const makePageData = (filename, content) => {
|
|
12
5
|
const [name, ext] = filename.split('.')
|
|
13
6
|
return parse(content, name, ext)
|
|
14
7
|
}
|
|
@@ -53,12 +46,6 @@ const parse = (content, name, ext) => {
|
|
|
53
46
|
})
|
|
54
47
|
)
|
|
55
48
|
const metaDataMerged = Object.assign(metaDataDefault, metaData)
|
|
56
|
-
if (!metaDataMerged.description) {
|
|
57
|
-
metaDataMerged.description = markdownReplaced.replace(/(<([^>]+)>)/gi, '').slice(0, 200).replaceAll("\n", '') + '...'
|
|
58
|
-
}
|
|
59
|
-
if (!metaDataMerged.og_description) {
|
|
60
|
-
metaDataMerged.og_description = metaDataMerged.og_description
|
|
61
|
-
}
|
|
62
49
|
metaDataMerged['__output'] = name === 'index' ? '/index.html' : `${metaDataMerged.url}.${metaDataMerged.ext}`
|
|
63
50
|
|
|
64
51
|
return metaDataMerged
|
package/lib/render.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
replaceIfFilter,
|
|
3
3
|
replaceScriptFilter,
|
|
4
|
-
replaceVariablesFilter
|
|
5
|
-
includeFilter
|
|
4
|
+
replaceVariablesFilter
|
|
6
5
|
} from './filter.js'
|
|
6
|
+
import includeFilter from './includeFilter.js'
|
|
7
7
|
import { marked } from "marked";
|
|
8
|
-
import applyTemplate from './applyTemplate.js'
|
|
8
|
+
import { applyTemplate } from './applyTemplate.js'
|
|
9
9
|
|
|
10
10
|
const render = async (templateName, data) => {
|
|
11
11
|
let template = await applyTemplate(templateName)
|
|
@@ -13,11 +13,15 @@ const render = async (templateName, data) => {
|
|
|
13
13
|
template = await replaceScriptFilter(template, data)
|
|
14
14
|
|
|
15
15
|
let markdown = data.markdown
|
|
16
|
-
markdown =
|
|
16
|
+
markdown = includeFilter(markdown)
|
|
17
17
|
markdown = await replaceIfFilter(markdown, data)
|
|
18
18
|
markdown = await replaceScriptFilter(markdown, data)
|
|
19
19
|
markdown = replaceVariablesFilter(markdown, data)
|
|
20
20
|
data.markdown = data.__filetype === 'md' ? marked.parse(markdown) : markdown
|
|
21
|
+
if (!data.description) {
|
|
22
|
+
data.description = data.markdown.replaceAll("\n", '').replace(/(<([^>]+)>)/gi, '').slice(0, 150) + '...'
|
|
23
|
+
data.og_description = data.description
|
|
24
|
+
}
|
|
21
25
|
|
|
22
26
|
return replaceVariablesFilter(template, data)
|
|
23
27
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import helper from './helper.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* テキストに含まれる変数を一括変換する
|
|
5
|
+
* ヘルパー処理もここで行われる
|
|
6
|
+
*
|
|
7
|
+
* @param {string} text
|
|
8
|
+
* @params {object} variables
|
|
9
|
+
* @return {text}
|
|
10
|
+
*/
|
|
11
|
+
const replaceVariablesFilter = (text, variables) => {
|
|
12
|
+
const matched = [...text.matchAll(/(\\)?{{[\s]*([^{}]+)[\s]*}}/g)]
|
|
13
|
+
const replace = Object.fromEntries(matched.map(match => [match[0], {variableName: match[2].trim().toLowerCase(), backslash: !!match[1]}]))
|
|
14
|
+
let replaced = text
|
|
15
|
+
for (const elm in replace) {
|
|
16
|
+
const [toBeReplace, replacedText] = replaceVariable(replace[elm].variableName, elm, replace[elm].backslash, variables)
|
|
17
|
+
replaced = replaced.replaceAll(toBeReplace, replacedText)
|
|
18
|
+
}
|
|
19
|
+
return replaced
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {string} target
|
|
24
|
+
* @param {string} replaceTargetRawText
|
|
25
|
+
* @param {boolean} backslash
|
|
26
|
+
* @param {object} variables
|
|
27
|
+
* @return {Array.<string>}
|
|
28
|
+
*/
|
|
29
|
+
const replaceVariable = (target, replaceTargetRawText, backslash, variables) => {
|
|
30
|
+
const toBeReplace = replaceTargetRawText
|
|
31
|
+
const toBeReplaceScript = toBeReplace.match(/([\w\d_]+)\((.*)\)/)
|
|
32
|
+
if (backslash) { // escape variable syntax
|
|
33
|
+
const removeBackslash = replaceTargetRawText.replace(/\\/, '')
|
|
34
|
+
return [replaceTargetRawText, removeBackslash]
|
|
35
|
+
} else if (toBeReplaceScript) { // execute helper
|
|
36
|
+
const func = toBeReplaceScript[1]
|
|
37
|
+
const args = toBeReplaceScript[2].split(',').map(v => {
|
|
38
|
+
const val = v.trim()
|
|
39
|
+
const argAsString = val.match(/^['"](.+)['"]$/)
|
|
40
|
+
if (argAsString) {
|
|
41
|
+
return argAsString[1]
|
|
42
|
+
}
|
|
43
|
+
return variables[val] ?? undefined
|
|
44
|
+
})
|
|
45
|
+
if (!helper[func]) {
|
|
46
|
+
throw new Error('helper function is missing. function name: ' + func);
|
|
47
|
+
}
|
|
48
|
+
const replaceText = helper[func].call(null, ...args)
|
|
49
|
+
return [replaceTargetRawText, replaceText]
|
|
50
|
+
}
|
|
51
|
+
return [replaceTargetRawText, variables[target]]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export default replaceVariablesFilter
|
package/lib/server.js
CHANGED
|
@@ -33,7 +33,8 @@ const contentType = (ext) => {
|
|
|
33
33
|
const server = () => {
|
|
34
34
|
return http.createServer((request, response) => {
|
|
35
35
|
const url = new URL(`http://${request.headers.host}${request.url}`)
|
|
36
|
-
|
|
36
|
+
const isIndex = url.pathname.match(/(.+)?\/$/)
|
|
37
|
+
let path = isIndex ? `${url.pathname}index.html` : decodeURIComponent(url.pathname)
|
|
37
38
|
if (!path.includes('.')) {
|
|
38
39
|
path += '.html'
|
|
39
40
|
}
|
package/package.json
CHANGED
|
@@ -2,34 +2,7 @@
|
|
|
2
2
|
title: INDEX
|
|
3
3
|
template: index.html
|
|
4
4
|
index: false
|
|
5
|
-
url: /
|
|
6
|
-
published: 2023-03-03 20:21
|
|
7
|
-
modified: 2023-03-03 20:21
|
|
8
5
|
---
|
|
9
6
|
## INDEX
|
|
10
7
|
|
|
11
|
-
{
|
|
12
|
-
const data = helper.readIndex()
|
|
13
|
-
|
|
14
|
-
const pages = {}
|
|
15
|
-
for (const page of data) {
|
|
16
|
-
if (page.index) {
|
|
17
|
-
const url = variables.relative_path ? variables.relative_path + page.url : page.url
|
|
18
|
-
if (page.published === '1970-01-01') {
|
|
19
|
-
if (!pages['日付なし']) {
|
|
20
|
-
pages['日付なし'] = []
|
|
21
|
-
}
|
|
22
|
-
pages['日付なし'].push(`<a href="${url}">${page.title}</a>`)
|
|
23
|
-
continue
|
|
24
|
-
}
|
|
25
|
-
const published = new Date(page.published)
|
|
26
|
-
const year = `${published.getFullYear()}年`
|
|
27
|
-
const date = `${published.getMonth() +1}月${published.getDate()}日`
|
|
28
|
-
if (!pages[year]) {
|
|
29
|
-
pages[year] = []
|
|
30
|
-
}
|
|
31
|
-
pages[year].push(`<a href="${url}">${page.title}</a> (${date})`)
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return pages
|
|
35
|
-
{/script}
|
|
8
|
+
{{ renderIndex(null, "日付なし") }}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
top > [post](/post/) > /post/1
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
top > [post](/post/) > /post/2
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
top > [post](/post/) > /post/3
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
2
|
<html lang="{{LANG}}">
|
|
3
3
|
<head prefix="og: http://ogp.me/ns#">
|
|
4
|
-
{if gtag_id}
|
|
5
|
-
{include('template/gtag.html')}
|
|
6
|
-
{/if}
|
|
7
4
|
<meta charset="UTF-8">
|
|
8
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
9
6
|
<title>{{TITLE}} | {{SITE_NAME}}</title>
|
|
@@ -37,9 +34,9 @@
|
|
|
37
34
|
<h1>{{TITLE}}</h1>
|
|
38
35
|
{if published != '1970-01-01'}
|
|
39
36
|
<aside class="published">
|
|
40
|
-
<p>投稿: {
|
|
37
|
+
<p>投稿: {{ dateFormat(published) }}
|
|
41
38
|
{/if}
|
|
42
|
-
{if modified} / 更新: {
|
|
39
|
+
{if modified} / 更新: {{ dateFormat(modified) }}{/if}
|
|
43
40
|
{if published}
|
|
44
41
|
</p>
|
|
45
42
|
</aside>
|