@tenjuu99/blog 0.1.2 → 0.1.3
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/.github/workflows/github-page.yml +5 -0
- package/.node-version +1 -0
- package/bin/generate +4 -0
- package/bin/server +26 -0
- package/lib/applyTemplate.js +8 -31
- package/lib/cssGenerator.js +11 -11
- package/lib/dir.js +2 -0
- package/lib/distribute.js +10 -8
- package/lib/generate.js +6 -5
- package/lib/indexer.js +22 -71
- package/lib/pageData.js +66 -0
- package/lib/render.js +24 -0
- package/lib/server.js +64 -0
- package/lib/watcher.js +28 -0
- package/package.json +9 -6
- package/performance.js +10 -10
- package/src-sample/pages/aaaa.md +0 -0
- package/src-sample/pages/aaaab.md +0 -0
- package/src-sample/pages/aaaac.md +0 -0
- package/src-sample/pages/aaaad.md +0 -0
- package/src-sample/pages/hogehogehogehoge.md +0 -0
- package/generate.js +0 -4
- package/server.js +0 -58
package/.node-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
22.7.0
|
package/bin/generate
ADDED
package/bin/server
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import server from '../lib/server.js'
|
|
4
|
+
import { srcDir, pageDir } from '../lib/dir.js'
|
|
5
|
+
import { watchers, watch } from '../lib/watcher.js'
|
|
6
|
+
import generate from '../lib/generate.js'
|
|
7
|
+
|
|
8
|
+
watchers.push({
|
|
9
|
+
paths: srcDir,
|
|
10
|
+
watchOptions: {
|
|
11
|
+
ignored: pageDir
|
|
12
|
+
},
|
|
13
|
+
callback: generate
|
|
14
|
+
})
|
|
15
|
+
watchers.push({
|
|
16
|
+
paths: pageDir,
|
|
17
|
+
callback: generate,
|
|
18
|
+
watchOptions: {
|
|
19
|
+
ignoreInitial: true
|
|
20
|
+
},
|
|
21
|
+
event: ['change', 'unlink', 'add']
|
|
22
|
+
})
|
|
23
|
+
watch()
|
|
24
|
+
generate()
|
|
25
|
+
|
|
26
|
+
server().listen(process.env.PORT || 8000)
|
package/lib/applyTemplate.js
CHANGED
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
"use strict"
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
replaceIfFilter,
|
|
6
|
-
replaceScriptFilter,
|
|
7
|
-
replaceVariablesFilter,
|
|
8
|
-
includeFilter
|
|
9
|
-
} from './filter.js'
|
|
10
|
-
import { marked } from "marked";
|
|
3
|
+
import applyCss from './cssGenerator.js'
|
|
4
|
+
import { includeFilter } from './filter.js'
|
|
11
5
|
import { templateDir, cssDir } from './dir.js'
|
|
12
|
-
import
|
|
6
|
+
import { watchers } from './watcher.js'
|
|
13
7
|
|
|
14
8
|
let templates = {}
|
|
15
9
|
|
|
@@ -24,26 +18,9 @@ const applyTemplate = async (name = 'default.html') => {
|
|
|
24
18
|
return templateContent
|
|
25
19
|
}
|
|
26
20
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
21
|
+
watchers.push({
|
|
22
|
+
paths: [cssDir, templateDir],
|
|
23
|
+
callback: () => { templates = {} }
|
|
24
|
+
})
|
|
31
25
|
|
|
32
|
-
|
|
33
|
-
markdown = await includeFilter(markdown)
|
|
34
|
-
markdown = await replaceIfFilter(markdown, data)
|
|
35
|
-
markdown = await replaceScriptFilter(markdown, data)
|
|
36
|
-
data.markdown = data.__filetype === 'md' ? marked.parse(markdown) : markdown
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return replaceVariablesFilter(template, data)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const watchTemplate = () => {
|
|
43
|
-
chokidar.watch([cssDir, templateDir]).on('change', () => {
|
|
44
|
-
templates = {}
|
|
45
|
-
})
|
|
46
|
-
watchCss()
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export { render, watchTemplate }
|
|
26
|
+
export default applyTemplate
|
package/lib/cssGenerator.js
CHANGED
|
@@ -4,7 +4,8 @@ import { minifyCss } from './minify.js'
|
|
|
4
4
|
import { createHash } from 'crypto'
|
|
5
5
|
import path from 'path'
|
|
6
6
|
import { distDir as distRoot, cssDir } from './dir.js'
|
|
7
|
-
import
|
|
7
|
+
import { watchers } from './watcher.js'
|
|
8
|
+
import { styleText } from 'node:util'
|
|
8
9
|
|
|
9
10
|
let cacheBuster = {}
|
|
10
11
|
const cacheBusterQuery = 't'
|
|
@@ -29,16 +30,16 @@ const cssGenerator = async (src, dist) => {
|
|
|
29
30
|
css = minifyCss(css)
|
|
30
31
|
cacheBuster[key] = createHash('md5').update(css).digest('hex')
|
|
31
32
|
|
|
32
|
-
return await fs.mkdir(`${distRoot}${path.dirname(dist)}`, { recursive: true }).then(
|
|
33
|
-
|
|
34
|
-
console.log(
|
|
33
|
+
return await fs.mkdir(`${distRoot}${path.dirname(dist)}`, { recursive: true }).then(() => {
|
|
34
|
+
fs.writeFile(`${distRoot}${dist}`, css)
|
|
35
|
+
console.log(styleText('green', '[generate]'), `${src} => ${distRoot}${dist}`)
|
|
35
36
|
return cacheBuster[key]
|
|
36
37
|
})
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
/**
|
|
40
41
|
* 次のような記述を想定している。
|
|
41
|
-
* <link rel="stylesheet" href="${/css/layout.css
|
|
42
|
+
* <link rel="stylesheet" href="${/css/layout.css<<base.css,page.css}">
|
|
42
43
|
* href の記述は ${dist:src} の関係になっている。
|
|
43
44
|
*
|
|
44
45
|
* これを
|
|
@@ -59,10 +60,9 @@ const applyCss = async (text) => {
|
|
|
59
60
|
return text
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
63
|
+
watchers.push({
|
|
64
|
+
paths: cssDir,
|
|
65
|
+
callback: () => { cacheBuster = {} }
|
|
66
|
+
})
|
|
67
67
|
|
|
68
|
-
export
|
|
68
|
+
export default applyCss
|
package/lib/dir.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const rootDir = process.cwd()
|
|
2
2
|
const srcDir = `${rootDir}/${process.env.SRC_DIR}`
|
|
3
3
|
const distDir = `${rootDir}/${process.env.DIST_DIR}`
|
|
4
|
+
const pageDir = `${srcDir}/pages`
|
|
4
5
|
const templateDir = `${srcDir}/template`
|
|
5
6
|
const cssDir = `${srcDir}/css`
|
|
6
7
|
const cacheDir = `${rootDir}/.cache`
|
|
@@ -9,6 +10,7 @@ export {
|
|
|
9
10
|
rootDir,
|
|
10
11
|
srcDir,
|
|
11
12
|
distDir,
|
|
13
|
+
pageDir,
|
|
12
14
|
templateDir,
|
|
13
15
|
cssDir,
|
|
14
16
|
cacheDir,
|
package/lib/distribute.js
CHANGED
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import path from 'path'
|
|
4
4
|
import { minifyHtml } from './minify.js'
|
|
5
|
-
import
|
|
5
|
+
import render from './render.js'
|
|
6
|
+
import { styleText } from 'node:util'
|
|
6
7
|
|
|
7
|
-
const distribute = async (data, srcDir, distDir) => {
|
|
8
|
-
if (
|
|
9
|
-
for (const
|
|
10
|
-
console.log(
|
|
11
|
-
fs.unlink(`${distDir}${
|
|
8
|
+
const distribute = async (data, deleted, srcDir, distDir) => {
|
|
9
|
+
if (deleted) {
|
|
10
|
+
for (const obj of deleted) {
|
|
11
|
+
console.log(styleText('red', '[unlink]'), `${distDir}${obj.__output}`)
|
|
12
|
+
fs.unlink(`${distDir}${obj.__output}`)
|
|
12
13
|
}
|
|
13
14
|
delete data['__deleted']
|
|
14
15
|
}
|
|
@@ -18,7 +19,7 @@ const distribute = async (data, srcDir, distDir) => {
|
|
|
18
19
|
let writeTo = `${distDir}${data[name].__output}`
|
|
19
20
|
fs.mkdir(path.dirname(writeTo), { recursive: true}).then(() => {
|
|
20
21
|
fs.writeFile(writeTo, minifyHtml(rendered))
|
|
21
|
-
console.log(
|
|
22
|
+
console.log(styleText('green', '[generate]'), writeTo)
|
|
22
23
|
})
|
|
23
24
|
}
|
|
24
25
|
const distributeRaw = process.env.DISTRIBUTE_RAW.split(',')
|
|
@@ -27,9 +28,10 @@ const distribute = async (data, srcDir, distDir) => {
|
|
|
27
28
|
await fs.stat(`${distDir}/${copyDir}/`).catch(async err => await fs.mkdir(`${distDir}/${copyDir}/`))
|
|
28
29
|
files.forEach(file => {
|
|
29
30
|
fs.copyFile(`${srcDir}/${copyDir}/${file}`, `${distDir}/${copyDir}/${file}`)
|
|
31
|
+
console.log(styleText('green', '[copy]'), `${srcDir}/${copyDir}/${file} => ${distDir}/${copyDir}/${file}`)
|
|
30
32
|
})
|
|
31
33
|
})
|
|
32
34
|
})
|
|
33
35
|
}
|
|
34
36
|
|
|
35
|
-
export
|
|
37
|
+
export default distribute
|
package/lib/generate.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
"use strict"
|
|
2
|
-
import
|
|
3
|
-
import { indexing } from './indexer.js'
|
|
2
|
+
import distribute from './distribute.js'
|
|
3
|
+
import { indexing, allData, deleted } from './indexer.js'
|
|
4
4
|
import { srcDir, distDir } from './dir.js'
|
|
5
|
+
import { styleText } from 'node:util'
|
|
5
6
|
|
|
6
7
|
const generate = async () => {
|
|
7
8
|
const start = performance.now()
|
|
8
|
-
|
|
9
|
+
await indexing()
|
|
9
10
|
|
|
10
|
-
await distribute(
|
|
11
|
+
await distribute(allData, deleted, srcDir, distDir)
|
|
11
12
|
const end = performance.now()
|
|
12
|
-
console.log('build: ' + (end - start) + "ms")
|
|
13
|
+
console.log(styleText('blue', '[build: ' + (end - start) + "ms]"))
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
export default generate
|
package/lib/indexer.js
CHANGED
|
@@ -1,82 +1,33 @@
|
|
|
1
1
|
"use strict"
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
|
-
import { cacheDir } from './dir.js'
|
|
4
|
-
|
|
5
|
-
const parseMetaData = (markdown, filename) => {
|
|
6
|
-
const regexp = new RegExp(/^(<!|-)--(?<variables>[\s\S]*?)--(-|>)/)
|
|
7
|
-
const matched = markdown.match(regexp)
|
|
8
|
-
const markdownReplaced = markdown.replace(regexp, '')
|
|
9
|
-
const metaDataDefault = {
|
|
10
|
-
name: filename,
|
|
11
|
-
title: filename,
|
|
12
|
-
url: `/${filename}`,
|
|
13
|
-
description: '',
|
|
14
|
-
og_description: '',
|
|
15
|
-
published: '1970-01-01',
|
|
16
|
-
index: true,
|
|
17
|
-
noindex: false,
|
|
18
|
-
lang: 'ja',
|
|
19
|
-
site_name: process.env.SITE_NAME,
|
|
20
|
-
url_base: process.env.URL_BASE,
|
|
21
|
-
gtag_id: process.env.GTAG_ID,
|
|
22
|
-
markdown: markdownReplaced,
|
|
23
|
-
relative_path: process.env.RELATIVE_PATH || '',
|
|
24
|
-
template: 'default.html',
|
|
25
|
-
ext: 'html',
|
|
26
|
-
__output: `/${filename}.html`
|
|
27
|
-
}
|
|
28
|
-
if (!matched) {
|
|
29
|
-
return metaDataDefault
|
|
30
|
-
}
|
|
31
|
-
const metaData = Object.fromEntries(
|
|
32
|
-
matched.groups.variables.split('\n').filter(line => line.includes(':'))
|
|
33
|
-
.map(line => {
|
|
34
|
-
const index = line.indexOf(':')
|
|
35
|
-
const key = line.slice(0, index)
|
|
36
|
-
let value = line.slice(index + 1).trim()
|
|
37
|
-
if (value === 'true' || value === 'false') {
|
|
38
|
-
value = JSON.parse(value)
|
|
39
|
-
}
|
|
40
|
-
return [key, value]
|
|
41
|
-
})
|
|
42
|
-
)
|
|
43
|
-
const metaDataMerged = Object.assign(metaDataDefault, metaData)
|
|
44
|
-
if (!metaDataMerged.description) {
|
|
45
|
-
metaDataMerged.description = markdownReplaced.replace(/(<([^>]+)>)/gi, '').slice(0, 200).replaceAll("\n", '') + '...'
|
|
46
|
-
}
|
|
47
|
-
if (!metaDataMerged.og_description) {
|
|
48
|
-
metaDataMerged.og_description = metaDataMerged.og_description
|
|
49
|
-
}
|
|
50
|
-
metaDataMerged['__output'] = filename === 'index' ? '/index.html' : `${metaDataMerged.url}.${metaDataMerged.ext}`
|
|
51
|
-
|
|
52
|
-
return metaDataMerged
|
|
53
|
-
}
|
|
3
|
+
import { pageDir, cacheDir } from './dir.js'
|
|
4
|
+
import makePageData from './pageData.js'
|
|
54
5
|
|
|
55
6
|
const indexFile = `${cacheDir}/index.json`
|
|
56
7
|
|
|
57
|
-
|
|
58
|
-
|
|
8
|
+
let allData = {}
|
|
9
|
+
let deleted = []
|
|
59
10
|
|
|
60
|
-
const indexing = async (
|
|
61
|
-
const
|
|
62
|
-
|
|
11
|
+
const indexing = async () => {
|
|
12
|
+
const oldIndex = await fs.readFile(indexFile, 'utf8').then(text => JSON.parse(text)).catch(error => [])
|
|
13
|
+
|
|
14
|
+
const newIndex = []
|
|
15
|
+
allData = {}
|
|
16
|
+
deleted = []
|
|
17
|
+
await fs.readdir(pageDir).then(files => {
|
|
18
|
+
files
|
|
19
|
+
.filter(fileName => fileName.match('\.(md|html)$'))
|
|
20
|
+
.forEach(file => {
|
|
21
|
+
const metaData = makePageData(file)
|
|
22
|
+
allData[metaData.name] = metaData
|
|
23
|
+
const { name, url, __output } = metaData
|
|
24
|
+
newIndex.push({ name, url, __output })
|
|
25
|
+
})
|
|
63
26
|
})
|
|
64
|
-
|
|
65
|
-
const markdownText = await fs.readFile(`${targetDir}/${file}`, 'utf8')
|
|
66
|
-
const [target, ext] = file.split('.')
|
|
67
|
-
const metaData = parseMetaData(markdownText, target)
|
|
68
|
-
metaData.__filetype = ext
|
|
69
|
-
allData[target] = metaData
|
|
70
|
-
let { name, title, index, url, published, modified, __output } = metaData
|
|
71
|
-
newIndex.push({ name, title, index, url, published, modified, __output })
|
|
72
|
-
}
|
|
73
|
-
await fs.writeFile(indexFile, JSON.stringify(newIndex))
|
|
27
|
+
fs.writeFile(indexFile, JSON.stringify(newIndex))
|
|
74
28
|
|
|
75
29
|
// 旧インデックスから差分を計算して削除対象をピックアップする
|
|
76
|
-
|
|
77
|
-
const deleted = oldIndex.filter(oi => !newIndex.map(ni => ni.__output).includes(oi.__output))
|
|
78
|
-
allData['__deleted'] = deleted
|
|
79
|
-
return allData
|
|
30
|
+
deleted = oldIndex.filter(oi => !newIndex.map(ni => ni.__output).includes(oi.__output))
|
|
80
31
|
}
|
|
81
32
|
|
|
82
|
-
export { indexing, allData }
|
|
33
|
+
export { indexing, allData, deleted }
|
package/lib/pageData.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict"
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import { pageDir } from './dir.js'
|
|
4
|
+
|
|
5
|
+
const load = (path) => {
|
|
6
|
+
return fs.readFileSync(path, 'utf8')
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const makePageData = (filename) => {
|
|
10
|
+
const content = load(`${pageDir}/${filename}`)
|
|
11
|
+
const [name, ext] = filename.split('.')
|
|
12
|
+
return parse(content, name, ext)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const parse = (content, name, ext) => {
|
|
16
|
+
const regexp = new RegExp(/^(<!|-)--(?<variables>[\s\S]*?)--(-|>)/)
|
|
17
|
+
const matched = content.match(regexp)
|
|
18
|
+
const markdownReplaced = content.replace(regexp, '')
|
|
19
|
+
const metaDataDefault = {
|
|
20
|
+
name,
|
|
21
|
+
title: name,
|
|
22
|
+
url: `/${name}`,
|
|
23
|
+
description: '',
|
|
24
|
+
og_description: '',
|
|
25
|
+
published: '1970-01-01',
|
|
26
|
+
index: true,
|
|
27
|
+
noindex: false,
|
|
28
|
+
lang: 'ja',
|
|
29
|
+
site_name: process.env.SITE_NAME,
|
|
30
|
+
url_base: process.env.URL_BASE,
|
|
31
|
+
gtag_id: process.env.GTAG_ID,
|
|
32
|
+
markdown: markdownReplaced,
|
|
33
|
+
relative_path: process.env.RELATIVE_PATH || '',
|
|
34
|
+
template: 'default.html',
|
|
35
|
+
ext: 'html',
|
|
36
|
+
__output: `/${name}.html`,
|
|
37
|
+
__filetype: ext,
|
|
38
|
+
}
|
|
39
|
+
if (!matched) {
|
|
40
|
+
return metaDataDefault
|
|
41
|
+
}
|
|
42
|
+
const metaData = Object.fromEntries(
|
|
43
|
+
matched.groups.variables.split('\n').filter(line => line.includes(':'))
|
|
44
|
+
.map(line => {
|
|
45
|
+
const index = line.indexOf(':')
|
|
46
|
+
const key = line.slice(0, index)
|
|
47
|
+
let value = line.slice(index + 1).trim()
|
|
48
|
+
if (value === 'true' || value === 'false') {
|
|
49
|
+
value = JSON.parse(value)
|
|
50
|
+
}
|
|
51
|
+
return [key, value]
|
|
52
|
+
})
|
|
53
|
+
)
|
|
54
|
+
const metaDataMerged = Object.assign(metaDataDefault, metaData)
|
|
55
|
+
if (!metaDataMerged.description) {
|
|
56
|
+
metaDataMerged.description = markdownReplaced.replace(/(<([^>]+)>)/gi, '').slice(0, 200).replaceAll("\n", '') + '...'
|
|
57
|
+
}
|
|
58
|
+
if (!metaDataMerged.og_description) {
|
|
59
|
+
metaDataMerged.og_description = metaDataMerged.og_description
|
|
60
|
+
}
|
|
61
|
+
metaDataMerged['__output'] = name === 'index' ? '/index.html' : `${metaDataMerged.url}.${metaDataMerged.ext}`
|
|
62
|
+
|
|
63
|
+
return metaDataMerged
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export default makePageData
|
package/lib/render.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import {
|
|
2
|
+
replaceIfFilter,
|
|
3
|
+
replaceScriptFilter,
|
|
4
|
+
replaceVariablesFilter,
|
|
5
|
+
includeFilter
|
|
6
|
+
} from './filter.js'
|
|
7
|
+
import { marked } from "marked";
|
|
8
|
+
import applyTemplate from './applyTemplate.js'
|
|
9
|
+
|
|
10
|
+
const render = async (templateName, data) => {
|
|
11
|
+
let template = await applyTemplate(templateName)
|
|
12
|
+
template = replaceIfFilter(template, data)
|
|
13
|
+
template = await replaceScriptFilter(template, data)
|
|
14
|
+
|
|
15
|
+
let markdown = data.markdown
|
|
16
|
+
markdown = await includeFilter(markdown)
|
|
17
|
+
markdown = await replaceIfFilter(markdown, data)
|
|
18
|
+
markdown = await replaceScriptFilter(markdown, data)
|
|
19
|
+
data.markdown = data.__filetype === 'md' ? marked.parse(markdown) : markdown
|
|
20
|
+
|
|
21
|
+
return replaceVariablesFilter(template, data)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default render
|
package/lib/server.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import http from 'http'
|
|
2
|
+
import url from 'url'
|
|
3
|
+
import fs from 'node:fs'
|
|
4
|
+
import { distDir } from './dir.js'
|
|
5
|
+
import { styleText } from 'node:util'
|
|
6
|
+
|
|
7
|
+
const contentType = (ext) => {
|
|
8
|
+
switch (ext) {
|
|
9
|
+
case 'html':
|
|
10
|
+
case 'css':
|
|
11
|
+
return `text/${ext}`
|
|
12
|
+
case 'js':
|
|
13
|
+
return 'text/javascript'
|
|
14
|
+
case 'jpeg':
|
|
15
|
+
case 'png':
|
|
16
|
+
case 'webp':
|
|
17
|
+
case 'avif':
|
|
18
|
+
return `image/${ext}`
|
|
19
|
+
case 'jpg':
|
|
20
|
+
return 'image/jpeg'
|
|
21
|
+
case 'svg':
|
|
22
|
+
return 'image/svg+xml'
|
|
23
|
+
case 'xml':
|
|
24
|
+
case 'json':
|
|
25
|
+
return `application/${ext}`
|
|
26
|
+
case 'rdf':
|
|
27
|
+
return 'application/rdf+xml.rdf'
|
|
28
|
+
default:
|
|
29
|
+
return 'application/octet-stream'
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const server = () => {
|
|
34
|
+
return http.createServer((request, response) => {
|
|
35
|
+
const url = new URL(`http://${request.headers.host}${request.url}`)
|
|
36
|
+
let path = url.pathname === '/' ? '/index.html' : decodeURIComponent(url.pathname)
|
|
37
|
+
if (!path.includes('.')) {
|
|
38
|
+
path += '.html'
|
|
39
|
+
}
|
|
40
|
+
if (!fs.existsSync(`${distDir}${path}`)) {
|
|
41
|
+
console.log(styleText('red', `[${request.method}] 404`), request.url)
|
|
42
|
+
const errorContent = fs.readFileSync(`${distDir}/404.html`)
|
|
43
|
+
response.writeHead(404)
|
|
44
|
+
response.end(errorContent)
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
const content = fs.readFileSync(`${distDir}${path}`, 'binary')
|
|
49
|
+
|
|
50
|
+
const ext = path.split('.')[1]
|
|
51
|
+
console.log(styleText('green', `[${request.method}] 200`), request.url)
|
|
52
|
+
response.writeHead(200, { 'Content-Type': `${contentType(ext)}; charset=utf-8` })
|
|
53
|
+
response.end(content, 'binary')
|
|
54
|
+
} catch (e) {
|
|
55
|
+
console.log(e)
|
|
56
|
+
console.log(styleText('red', `[${request.method}] 500`), request.url)
|
|
57
|
+
const errorContent = fs.readFileSync(`${distDir}/404.html`)
|
|
58
|
+
response.writeHead(500)
|
|
59
|
+
response.end(errorContent)
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export default server
|
package/lib/watcher.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import chokidar from 'chokidar'
|
|
2
|
+
|
|
3
|
+
const container = []
|
|
4
|
+
|
|
5
|
+
const watchers = {
|
|
6
|
+
push({ paths, event = 'change', callback, watchOptions }) {
|
|
7
|
+
if (!paths || !callback || typeof callback !== 'function') {
|
|
8
|
+
throw new Error('Invalid object type for watcher.')
|
|
9
|
+
}
|
|
10
|
+
container.push({ paths, event, callback, watchOptions })
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const watch = () => {
|
|
15
|
+
container.forEach((watcher) => {
|
|
16
|
+
const { paths, event, callback, watchOptions } = watcher
|
|
17
|
+
const cwatcher = chokidar.watch(paths, watchOptions)
|
|
18
|
+
if (Array.isArray(event)) {
|
|
19
|
+
event.forEach(e => cwatcher.on(e, callback))
|
|
20
|
+
} else if (typeof event === 'string') {
|
|
21
|
+
cwatcher.on(event, callback)
|
|
22
|
+
} else {
|
|
23
|
+
throw new Error('Invalid event for watcher.')
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { watchers , watch }
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tenjuu99/blog",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "blog template",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"
|
|
8
|
-
"generate": "eval $(cat .env | tr \"\n\" \" \") node generate
|
|
7
|
+
"dev": "eval $(cat .env | tr \"\n\" \" \") node bin/server",
|
|
8
|
+
"generate": "eval $(cat .env | tr \"\n\" \" \") node bin/generate",
|
|
9
9
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
10
10
|
},
|
|
11
11
|
"bin": {
|
|
12
|
-
"generate": "generate
|
|
13
|
-
"server": "server
|
|
12
|
+
"generate": "bin/generate",
|
|
13
|
+
"server": "bin/server"
|
|
14
14
|
},
|
|
15
15
|
"author": "AmashigeSeiji",
|
|
16
16
|
"repository": {
|
|
@@ -22,5 +22,8 @@
|
|
|
22
22
|
"chokidar": "^3.6.0",
|
|
23
23
|
"marked": "^13.x"
|
|
24
24
|
},
|
|
25
|
-
"type": "module"
|
|
25
|
+
"type": "module",
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=21.7"
|
|
28
|
+
}
|
|
26
29
|
}
|
package/performance.js
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
"use strict"
|
|
2
2
|
import distribute from './lib/distribute.js'
|
|
3
|
-
import { indexing } from './lib/indexer.js'
|
|
3
|
+
import { indexing, allData, deleted } from './lib/indexer.js'
|
|
4
4
|
import { srcDir, distDir } from './lib/dir.js'
|
|
5
5
|
|
|
6
6
|
process.env.FORCE_BUILD = true
|
|
7
|
-
const
|
|
7
|
+
const doBuild = async () => {
|
|
8
8
|
const start = performance.now()
|
|
9
|
-
|
|
10
|
-
await distribute(
|
|
9
|
+
await indexing()
|
|
10
|
+
await distribute(allData, deleted, srcDir, distDir)
|
|
11
11
|
const end = performance.now()
|
|
12
12
|
return end - start
|
|
13
13
|
}
|
|
14
14
|
const times = 100
|
|
15
15
|
let executed = 0
|
|
16
|
-
|
|
17
|
-
//console.log(await executed())
|
|
18
|
-
const buildTime = await
|
|
19
|
-
|
|
16
|
+
for (let i = 0; i < times; i++) {
|
|
17
|
+
// console.log(await executed())
|
|
18
|
+
const buildTime = await doBuild()
|
|
19
|
+
executed += (buildTime)
|
|
20
20
|
console.log(buildTime)
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
}
|
|
22
|
+
console.log('build average: 100 times: ' + (executed/times) + "ms")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/generate.js
DELETED
package/server.js
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import http from 'http'
|
|
4
|
-
import url from 'url'
|
|
5
|
-
import fs from 'node:fs/promises'
|
|
6
|
-
import { srcDir, distDir } from './lib/dir.js'
|
|
7
|
-
import chokidar from 'chokidar'
|
|
8
|
-
import generate from './lib/generate.js'
|
|
9
|
-
import { watchTemplate } from './lib/applyTemplate.js'
|
|
10
|
-
|
|
11
|
-
chokidar.watch(srcDir).on('change', (event, path) => {
|
|
12
|
-
generate()
|
|
13
|
-
})
|
|
14
|
-
watchTemplate()
|
|
15
|
-
generate()
|
|
16
|
-
const contentType = (ext) => {
|
|
17
|
-
switch (ext) {
|
|
18
|
-
case 'html':
|
|
19
|
-
return 'text/html'
|
|
20
|
-
case 'css':
|
|
21
|
-
return 'text/css'
|
|
22
|
-
case 'js':
|
|
23
|
-
case 'javascript':
|
|
24
|
-
return 'text/javascript'
|
|
25
|
-
case 'json':
|
|
26
|
-
return 'application/json'
|
|
27
|
-
case 'jpg':
|
|
28
|
-
case 'jpeg':
|
|
29
|
-
return 'image/jpeg'
|
|
30
|
-
case 'svg':
|
|
31
|
-
return 'image/svg+xml'
|
|
32
|
-
case 'xml':
|
|
33
|
-
return 'application/xml'
|
|
34
|
-
case 'rdf':
|
|
35
|
-
return 'application/rdf+xml.rdf'
|
|
36
|
-
default:
|
|
37
|
-
return 'application/octet-stream'
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
http.createServer((request, response) => {
|
|
42
|
-
console.log(request.method, request.url)
|
|
43
|
-
const url = new URL(`http://${request.headers.host}${request.url}`)
|
|
44
|
-
let path = url.pathname === '/' ? '/index.html' : decodeURIComponent(url.pathname)
|
|
45
|
-
if (!path.includes('.')) {
|
|
46
|
-
path += '.html'
|
|
47
|
-
}
|
|
48
|
-
fs.readFile(`${distDir}${path}`, 'binary').catch(async (error) => {
|
|
49
|
-
const errorContent = await fs.readFile(`${distDir}/404.html`)
|
|
50
|
-
console.log(error)
|
|
51
|
-
response.writeHead(404)
|
|
52
|
-
response.end(errorContent)
|
|
53
|
-
}).then(file => {
|
|
54
|
-
const ext = path.split('.')[1]
|
|
55
|
-
response.writeHead(200, { 'Content-Type': `${contentType(ext)}; charset=utf-8` })
|
|
56
|
-
response.end(file, 'binary')
|
|
57
|
-
})
|
|
58
|
-
}).listen(process.env.PORT || 8000)
|