@tenjuu99/blog 0.1.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/.env.prod.sample +8 -0
- package/.env.sample +8 -0
- package/.github/workflows/deploy.yml.sample +51 -0
- package/.github/workflows/github-page.yml +47 -0
- package/.gitignore +9 -0
- package/README.md +145 -0
- package/generate.js +11 -0
- package/helper/add.js +3 -0
- package/helper/index.js +53 -0
- package/lib/applyTemplate.js +41 -0
- package/lib/cssGenerator.js +61 -0
- package/lib/dir.js +15 -0
- package/lib/distribute.js +35 -0
- package/lib/filter.js +167 -0
- package/lib/includeFilter.js +38 -0
- package/lib/indexer.js +82 -0
- package/lib/minify.js +22 -0
- package/lib/replaceIfFilter.js +67 -0
- package/lib/replaceScriptFilter.js +71 -0
- package/package.json +40 -0
- package/performance.js +22 -0
- package/server.js +48 -0
- package/src-sample/css/color.css +5 -0
- package/src-sample/css/layout.css +143 -0
- package/src-sample/css/markdown.css +49 -0
- package/src-sample/css/page.css +0 -0
- package/src-sample/css/reset.css +2 -0
- package/src-sample/helper/index.js +3 -0
- package/src-sample/image/.gitkeep +0 -0
- package/src-sample/pages/abc.md +5 -0
- package/src-sample/pages/fuga.md +3 -0
- package/src-sample/pages/hogehoge.md +8 -0
- package/src-sample/pages/html_render.html +20 -0
- package/src-sample/pages/index.md +35 -0
- package/src-sample/pages/notfound.md +8 -0
- package/src-sample/pages/rss.md +8 -0
- package/src-sample/pages/sample.md +145 -0
- package/src-sample/template/css.html +6 -0
- package/src-sample/template/default.html +55 -0
- package/src-sample/template/footer.html +7 -0
- package/src-sample/template/gtag.html +9 -0
- package/src-sample/template/index.html +24 -0
- package/src-sample/template/prevNext.html +35 -0
- package/src-sample/template/rss.xml +41 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import { minifyCss } from './minify.js'
|
|
3
|
+
import { templateDir, cssDir } from './dir.js'
|
|
4
|
+
|
|
5
|
+
const alreadyLoaded = {}
|
|
6
|
+
|
|
7
|
+
const includeFilter = async (text) => {
|
|
8
|
+
let replaced = text
|
|
9
|
+
const includeRegexp = new RegExp(/\{\s*include\('(template|css)\/([\w\./]+)'\)\s*\}/g)
|
|
10
|
+
const include = [...text.matchAll(includeRegexp)].map(matched => [matched[0], matched[1], matched[2]])
|
|
11
|
+
for (const index in include) {
|
|
12
|
+
const [toBeReplace, type, filename] = [...include[index]]
|
|
13
|
+
let content
|
|
14
|
+
const cacheKey = `${type}/${filename}`
|
|
15
|
+
if (!alreadyLoaded[cacheKey]) {
|
|
16
|
+
switch (type) {
|
|
17
|
+
case 'template':
|
|
18
|
+
content = await fs.readFile(`${templateDir}/${filename}`, 'utf8')
|
|
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`.');
|
|
25
|
+
}
|
|
26
|
+
// include を再帰的に解決する
|
|
27
|
+
if (content.match(includeRegexp)) {
|
|
28
|
+
content = await includeFilter(content)
|
|
29
|
+
}
|
|
30
|
+
alreadyLoaded[cacheKey] = content
|
|
31
|
+
} else {
|
|
32
|
+
content = alreadyLoaded[cacheKey]
|
|
33
|
+
}
|
|
34
|
+
replaced = replaced.replace(toBeReplace, content)
|
|
35
|
+
}
|
|
36
|
+
return replaced
|
|
37
|
+
}
|
|
38
|
+
export default includeFilter
|
package/lib/indexer.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict"
|
|
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
|
+
}
|
|
54
|
+
|
|
55
|
+
const indexFile = `${cacheDir}/index.json`
|
|
56
|
+
|
|
57
|
+
const newIndex = []
|
|
58
|
+
const allData = {}
|
|
59
|
+
|
|
60
|
+
const indexing = async (targetDir) => {
|
|
61
|
+
const targets = await fs.readdir(targetDir).then(files => {
|
|
62
|
+
return files.filter(fileName => fileName.match('\.(md|html)$'))
|
|
63
|
+
})
|
|
64
|
+
for (const file of targets) {
|
|
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))
|
|
74
|
+
|
|
75
|
+
// 旧インデックスから差分を計算して削除対象をピックアップする
|
|
76
|
+
const oldIndex = await fs.readFile(indexFile, 'utf8').then(text => JSON.parse(text)).catch(error => [])
|
|
77
|
+
const deleted = oldIndex.filter(oi => !newIndex.map(ni => ni.__output).includes(oi.__output))
|
|
78
|
+
allData['__deleted'] = deleted
|
|
79
|
+
return allData
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export { indexing, allData }
|
package/lib/minify.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* @param {string} css
|
|
4
|
+
* @returns {string}
|
|
5
|
+
*/
|
|
6
|
+
export function minifyCss (css) {
|
|
7
|
+
return css.split('\n')
|
|
8
|
+
.map(s => s.trim()
|
|
9
|
+
.replace(/\s+{/g, '{')
|
|
10
|
+
.replace(/:\s/, ':'))
|
|
11
|
+
.filter(s => !!s)
|
|
12
|
+
.join('')
|
|
13
|
+
.replaceAll(/\/\*[\s\S]+?\*\//g, '')
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @param {string} html
|
|
18
|
+
* @returns {string}
|
|
19
|
+
*/
|
|
20
|
+
export function minifyHtml (html) {
|
|
21
|
+
return html.replace(/\s+(<(?!\/?style).+>?)/g, '$1')
|
|
22
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {string} condition
|
|
3
|
+
* @params {object} variables
|
|
4
|
+
* @return {bool}
|
|
5
|
+
*/
|
|
6
|
+
const ifConditionEvaluator = (condition, variables) => {
|
|
7
|
+
if (condition.includes('=')) {
|
|
8
|
+
const segmented = condition.match(/(?<left>[\S]+)\s(?<operator>!=|==)\s(?<right>[\S]+)/)
|
|
9
|
+
let {left, operator, right} = segmented.groups
|
|
10
|
+
if (variables.hasOwnProperty(left)) {
|
|
11
|
+
left = variables[left]
|
|
12
|
+
} else {
|
|
13
|
+
try {
|
|
14
|
+
left = eval(left)
|
|
15
|
+
} catch (e) {
|
|
16
|
+
left = undefined
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (variables.hasOwnProperty(right)) {
|
|
20
|
+
right = variables[right]
|
|
21
|
+
} else {
|
|
22
|
+
try {
|
|
23
|
+
right = eval(right)
|
|
24
|
+
} catch (e) {
|
|
25
|
+
right = undefined
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
switch (operator) {
|
|
29
|
+
case '==':
|
|
30
|
+
return left == right
|
|
31
|
+
case '!=':
|
|
32
|
+
return left != right
|
|
33
|
+
}
|
|
34
|
+
} else {
|
|
35
|
+
if (variables.hasOwnProperty(condition)) {
|
|
36
|
+
return !!variables[condition]
|
|
37
|
+
}
|
|
38
|
+
return false
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const ifRegexp =
|
|
43
|
+
new RegExp(/(\{|<)if\s(?<condition>[\s\S]+?)(}|>)(?<content>[\s\S]*?)((\{|<)else(\}|>)(?<elsecontent>[\s\S]*?))?(\{<)\/if(>|})/g)
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @param {string} text
|
|
47
|
+
* @param {object} variables
|
|
48
|
+
* @returns {string}
|
|
49
|
+
*/
|
|
50
|
+
const replaceIfFilter = (text, variables) => {
|
|
51
|
+
const matched = [...text.matchAll(ifRegexp)]
|
|
52
|
+
for (const item of matched) {
|
|
53
|
+
const target = item[0]
|
|
54
|
+
const content = item.groups.content
|
|
55
|
+
const elseContent = item.groups.elsecontent
|
|
56
|
+
if (ifConditionEvaluator(item.groups.condition, variables)) {
|
|
57
|
+
text = text.replace(target, content)
|
|
58
|
+
} else if (elseContent) {
|
|
59
|
+
text = text.replace(target, elseContent)
|
|
60
|
+
} else {
|
|
61
|
+
text = text.replace(target, '')
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return text
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export default replaceIfFilter
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import * as helper from '../helper/index.js'
|
|
2
|
+
import vm from 'vm'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 配列を再帰的に順不同リストに変換する
|
|
6
|
+
* @param {Array|string} arrayOrText
|
|
7
|
+
* @returns {mixed}
|
|
8
|
+
*/
|
|
9
|
+
const arrayToList = (arrayOrText) => {
|
|
10
|
+
if (typeof arrayOrText === 'string') {
|
|
11
|
+
return `<li>${arrayOrText}</li>`
|
|
12
|
+
}
|
|
13
|
+
if (Array.isArray(arrayOrText)) {
|
|
14
|
+
let resultListText = '<ul>'
|
|
15
|
+
for (const item of arrayOrText) {
|
|
16
|
+
if (Array.isArray(item)) {
|
|
17
|
+
resultListText += `<li>${arrayToList(item)}</li>`
|
|
18
|
+
} else {
|
|
19
|
+
resultListText += `<li>${item}</li>`
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
resultListText += '</ul>'
|
|
23
|
+
arrayOrText = resultListText
|
|
24
|
+
}
|
|
25
|
+
return arrayOrText
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const replaceScriptFilter = async (text, variables) => {
|
|
29
|
+
let replaced = text
|
|
30
|
+
const scriptRegexp = new RegExp(/({script}|\<script\s.*type="ssg".*>)(?<script>[\s\S]*?)(\{\/script}|\<\/script>)/g)
|
|
31
|
+
const scripts = [...text.matchAll(scriptRegexp)].map((matched) => {
|
|
32
|
+
return {
|
|
33
|
+
replace: matched[0],
|
|
34
|
+
script: matched.groups.script.trim("\n"),
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
const context = vm.createContext({
|
|
38
|
+
helper,
|
|
39
|
+
variables,
|
|
40
|
+
})
|
|
41
|
+
for (const script of scripts) {
|
|
42
|
+
const s = new vm.Script(script.script)
|
|
43
|
+
console.log(script.script)
|
|
44
|
+
console.log(s)
|
|
45
|
+
console.log('------------------------------------------------')
|
|
46
|
+
const func = vm.runInContext(script, context)
|
|
47
|
+
console.log(func)
|
|
48
|
+
let result = await func(variables, helper)
|
|
49
|
+
if (result instanceof Promise) {
|
|
50
|
+
result = await result
|
|
51
|
+
}
|
|
52
|
+
if (Array.isArray(result)) {
|
|
53
|
+
result = arrayToList(result)
|
|
54
|
+
} else if (typeof result === 'object') {
|
|
55
|
+
const resultText = []
|
|
56
|
+
for (const key in result) {
|
|
57
|
+
resultText.push(`<h3>${key}</h3>`)
|
|
58
|
+
if (Array.isArray(result[key])) {
|
|
59
|
+
resultText.push(arrayToList(result[key]))
|
|
60
|
+
} else {
|
|
61
|
+
resultText.push(`<p>${result[key]}</p>`)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
result = resultText.join('\n')
|
|
65
|
+
}
|
|
66
|
+
replaced = replaced.replace(script.replace, result)
|
|
67
|
+
}
|
|
68
|
+
return replaced
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export default replaceScriptFilter
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tenjuu99/blog",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "blog template",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "npm run generate && npm run serve",
|
|
8
|
+
"serve": "eval $(cat .env | tr \"\n\" \" \") node server.js",
|
|
9
|
+
"generate": "eval $(cat .env | tr \"\n\" \" \") node generate.js",
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
11
|
+
"watch": "npm-watch build"
|
|
12
|
+
},
|
|
13
|
+
"watch": {
|
|
14
|
+
"build": {
|
|
15
|
+
"patterns": [
|
|
16
|
+
"src/",
|
|
17
|
+
"src-sample/",
|
|
18
|
+
"../src/",
|
|
19
|
+
"helper/",
|
|
20
|
+
"lib/",
|
|
21
|
+
"generate.js"
|
|
22
|
+
],
|
|
23
|
+
"extensions": "html,md,css,js",
|
|
24
|
+
"quite": true
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"author": "AmashigeSeiji",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/amashigeseiji/blog_template.git"
|
|
31
|
+
},
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"marked": "^13.x"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"npm-watch": "^0.x"
|
|
38
|
+
},
|
|
39
|
+
"type": "module"
|
|
40
|
+
}
|
package/performance.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict"
|
|
2
|
+
import distribute from './lib/distribute.js'
|
|
3
|
+
import { indexing } from './lib/indexer.js'
|
|
4
|
+
import { srcDir, distDir } from './lib/dir.js'
|
|
5
|
+
|
|
6
|
+
process.env.FORCE_BUILD = true
|
|
7
|
+
const execute = async () => {
|
|
8
|
+
const start = performance.now()
|
|
9
|
+
const data = await indexing(srcDir)
|
|
10
|
+
await distribute(data, srcDir, distDir)
|
|
11
|
+
const end = performance.now()
|
|
12
|
+
return end - start
|
|
13
|
+
}
|
|
14
|
+
const times = 100
|
|
15
|
+
let executed = 0
|
|
16
|
+
//for (let i = 0; i < times; i++) {
|
|
17
|
+
//console.log(await executed())
|
|
18
|
+
const buildTime = await execute()
|
|
19
|
+
//executed += (buildTime)
|
|
20
|
+
console.log(buildTime)
|
|
21
|
+
//}
|
|
22
|
+
//console.log('build average: 100 times: ' + (executed/times) + "ms")
|
package/server.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import http from 'http'
|
|
2
|
+
import url from 'url'
|
|
3
|
+
import fs from 'node:fs/promises'
|
|
4
|
+
import { distDir } from './lib/dir.js'
|
|
5
|
+
|
|
6
|
+
const contentType = (ext) => {
|
|
7
|
+
switch (ext) {
|
|
8
|
+
case 'html':
|
|
9
|
+
return 'text/html'
|
|
10
|
+
case 'css':
|
|
11
|
+
return 'text/css'
|
|
12
|
+
case 'js':
|
|
13
|
+
case 'javascript':
|
|
14
|
+
return 'text/javascript'
|
|
15
|
+
case 'json':
|
|
16
|
+
return 'application/json'
|
|
17
|
+
case 'jpg':
|
|
18
|
+
case 'jpeg':
|
|
19
|
+
return 'image/jpeg'
|
|
20
|
+
case 'svg':
|
|
21
|
+
return 'image/svg+xml'
|
|
22
|
+
case 'xml':
|
|
23
|
+
return 'application/xml'
|
|
24
|
+
case 'rdf':
|
|
25
|
+
return 'application/rdf+xml.rdf'
|
|
26
|
+
default:
|
|
27
|
+
return 'application/octet-stream'
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
http.createServer((request, response) => {
|
|
32
|
+
console.log(request.method, request.url)
|
|
33
|
+
const url = new URL(`http://${request.headers.host}${request.url}`)
|
|
34
|
+
let path = url.pathname === '/' ? '/index.html' : decodeURIComponent(url.pathname)
|
|
35
|
+
if (!path.includes('.')) {
|
|
36
|
+
path += '.html'
|
|
37
|
+
}
|
|
38
|
+
fs.readFile(`${distDir}${path}`, 'binary').catch(async (error) => {
|
|
39
|
+
const errorContent = await fs.readFile(`${distDir}/404.html`)
|
|
40
|
+
console.log(error)
|
|
41
|
+
response.writeHead(404)
|
|
42
|
+
response.end(errorContent)
|
|
43
|
+
}).then(file => {
|
|
44
|
+
const ext = path.split('.')[1]
|
|
45
|
+
response.writeHead(200, { 'Content-Type': `${contentType(ext)}; charset=utf-8` })
|
|
46
|
+
response.end(file, 'binary')
|
|
47
|
+
})
|
|
48
|
+
}).listen(process.env.PORT || 8000)
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--headline-1: 28px;
|
|
3
|
+
--headline-2: 20px;
|
|
4
|
+
--headline-3: 16px;
|
|
5
|
+
--text-size: 16px;
|
|
6
|
+
--text-size-small: 12px;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
body {
|
|
10
|
+
display: flex;
|
|
11
|
+
flex-direction: column;
|
|
12
|
+
min-height: 100dvh !important;
|
|
13
|
+
text-align: justify;
|
|
14
|
+
font-feature-settings: "palt";
|
|
15
|
+
font-size: var(--text-size);
|
|
16
|
+
word-wrap: break-word;
|
|
17
|
+
word-break: break-all;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.container {
|
|
21
|
+
margin: 0 auto;
|
|
22
|
+
padding: 5px 30px;
|
|
23
|
+
max-width: 80%;
|
|
24
|
+
min-width: 600px;
|
|
25
|
+
width: 70vw;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@media screen and (max-width: 600px) {
|
|
29
|
+
.container {
|
|
30
|
+
max-width: 100%;
|
|
31
|
+
width: 100%;
|
|
32
|
+
min-width: 100%;
|
|
33
|
+
padding: 5px 15px;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* header, main, footer
|
|
39
|
+
*/
|
|
40
|
+
header {
|
|
41
|
+
padding: 10px 0;
|
|
42
|
+
border-bottom: 0.5px solid #ccc;
|
|
43
|
+
background: var(--header-bg);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
header h1, header p {
|
|
47
|
+
font: normal bold var(--headline-1)/1 monospace;
|
|
48
|
+
margin: 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
header a, header a:visited {
|
|
52
|
+
text-decoration: none;
|
|
53
|
+
color: var(--header-logo-color);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
main {
|
|
57
|
+
flex-grow: 1;
|
|
58
|
+
display: flex;
|
|
59
|
+
flex-direction: column;
|
|
60
|
+
justify-content: space-between;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
footer {
|
|
64
|
+
border-top: 1px solid #ccc;
|
|
65
|
+
padding: 15px 0 0;
|
|
66
|
+
font-size: var(--text-size-small);
|
|
67
|
+
background: var(--footer-bg);
|
|
68
|
+
}
|
|
69
|
+
footer ul {
|
|
70
|
+
display: flex;
|
|
71
|
+
justify-content: flex-start;
|
|
72
|
+
}
|
|
73
|
+
footer ul li {
|
|
74
|
+
list-style: none;
|
|
75
|
+
margin-left: 10px;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* headline
|
|
80
|
+
*/
|
|
81
|
+
h1 {
|
|
82
|
+
font-size: var(--headline-1);
|
|
83
|
+
}
|
|
84
|
+
h2 {
|
|
85
|
+
font-size: var(--headline-2);
|
|
86
|
+
}
|
|
87
|
+
h3 {
|
|
88
|
+
font-size: var(--headline-3);
|
|
89
|
+
}
|
|
90
|
+
main h1, main h2, main h3, main h4 {
|
|
91
|
+
margin: 15px 0 5px;
|
|
92
|
+
}
|
|
93
|
+
main h1, main h2 {
|
|
94
|
+
border-bottom: 0.5px solid #ccc;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* aside
|
|
98
|
+
*/
|
|
99
|
+
aside, aside * {
|
|
100
|
+
font-size: var(--text-size-small);
|
|
101
|
+
color: #503838;
|
|
102
|
+
line-height: 1.125rem;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* p
|
|
107
|
+
*/
|
|
108
|
+
p {
|
|
109
|
+
margin: 15px 0;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.prevNext {
|
|
113
|
+
list-style: none;
|
|
114
|
+
display: flex;
|
|
115
|
+
padding: 0;
|
|
116
|
+
}
|
|
117
|
+
.prevNext li {
|
|
118
|
+
display: flex;
|
|
119
|
+
align-items: center;
|
|
120
|
+
flex-basis: 50%;
|
|
121
|
+
}
|
|
122
|
+
.prevNext a {
|
|
123
|
+
padding: 10px;
|
|
124
|
+
display: contents;
|
|
125
|
+
}
|
|
126
|
+
.prevNext__prev {
|
|
127
|
+
padding-right: 5px;
|
|
128
|
+
justify-content: flex-start;
|
|
129
|
+
}
|
|
130
|
+
.prevNext__next {
|
|
131
|
+
padding-left: 5px;
|
|
132
|
+
justify-content: flex-end;
|
|
133
|
+
}
|
|
134
|
+
.prevNext__next a::after {
|
|
135
|
+
content: '>';
|
|
136
|
+
color: #ccc;
|
|
137
|
+
padding-left: 10px;
|
|
138
|
+
}
|
|
139
|
+
.prevNext__prev a::before {
|
|
140
|
+
content: '<';
|
|
141
|
+
color: #ccc;
|
|
142
|
+
padding-right: 10px;
|
|
143
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
blockquote {
|
|
2
|
+
padding: 3px 20px 3px 20px;
|
|
3
|
+
margin: 0 10px;
|
|
4
|
+
font-style: italic;
|
|
5
|
+
border-left: 2px solid #999;
|
|
6
|
+
background: #e3e8f57a;
|
|
7
|
+
}
|
|
8
|
+
blockquote p {
|
|
9
|
+
color: #5b6e5c;
|
|
10
|
+
line-height: 1.75;
|
|
11
|
+
}
|
|
12
|
+
table, td, th {
|
|
13
|
+
border: 0.5px solid #bbb;
|
|
14
|
+
border-spacing: 0;
|
|
15
|
+
border-collapse: collapse;
|
|
16
|
+
}
|
|
17
|
+
td, th {
|
|
18
|
+
padding: 3px 5px;
|
|
19
|
+
font-size: .7rem;
|
|
20
|
+
background: #eee;
|
|
21
|
+
}
|
|
22
|
+
th {
|
|
23
|
+
background: #767676;
|
|
24
|
+
color: #efefef;
|
|
25
|
+
}
|
|
26
|
+
td ul {
|
|
27
|
+
padding: 0 0 0 10px;
|
|
28
|
+
}
|
|
29
|
+
em {
|
|
30
|
+
font-style: italic;
|
|
31
|
+
font-weight: 600;
|
|
32
|
+
}
|
|
33
|
+
pre {
|
|
34
|
+
white-space: pre-wrap;
|
|
35
|
+
background: #333;
|
|
36
|
+
color: #fff;
|
|
37
|
+
padding: 10px;
|
|
38
|
+
}
|
|
39
|
+
pre > code {
|
|
40
|
+
background: #333;
|
|
41
|
+
color: #fff;
|
|
42
|
+
padding: 0;
|
|
43
|
+
}
|
|
44
|
+
code {
|
|
45
|
+
background: #eee;
|
|
46
|
+
color: #444;
|
|
47
|
+
padding: 0 5px;
|
|
48
|
+
border-radius: 5px;
|
|
49
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/* https://github.com/Andy-set-studio/modern-css-reset */
|
|
2
|
+
*,*::before,*::after{box-sizing:border-box}body,h1,h2,h3,h4,p,figure,blockquote,dl,dd{margin:0}ul[role="list"],ol[role="list"]{list-style:none}html:focus-within{scroll-behavior:smooth}body{min-height:100vh;text-rendering:optimizeSpeed;line-height:1.5}a:not([class]){text-decoration-skip-ink:auto}img,picture{max-width:100%;display:block}input,button,textarea,select{font:inherit}@media(prefers-reduced-motion:reduce){html:focus-within{scroll-behavior:auto}*,*::before,*::after{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition-duration:.01ms !important;scroll-behavior:auto !important}}
|
|
File without changes
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: INDEX
|
|
3
|
+
template: index.html
|
|
4
|
+
index: false
|
|
5
|
+
url: /
|
|
6
|
+
published: 2023-03-03 20:21
|
|
7
|
+
modified: 2023-03-03 20:21
|
|
8
|
+
---
|
|
9
|
+
## INDEX
|
|
10
|
+
|
|
11
|
+
{script}
|
|
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}
|