@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 CHANGED
@@ -1,5 +1,7 @@
1
1
  # @tenjuu99/blog
2
2
 
3
+ 静的なブログを作るのにどうして React を必要としてしまったんですか
4
+
3
5
  ## setup
4
6
 
5
7
  ```
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
+ }
@@ -1,8 +1,8 @@
1
1
  "use strict"
2
- import fs from "node:fs/promises";
3
2
  import applyCss from './cssGenerator.js'
4
- import { includeFilter } from './filter.js'
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 fs.readFile(`${templateDir}/${name}`, 'utf8')
15
- templateContent = await includeFilter(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 default applyTemplate
34
+ export { applyTemplate, warmUpTemplate }
@@ -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 as distRoot, cssDir } from './dir.js'
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 += await fs.readFile(`${cssDir}/${cssFile}`)
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(`${distRoot}${path.dirname(dist)}`, { recursive: true }).then(() => {
35
- fs.writeFile(`${distRoot}${dist}`, css)
36
- console.log(styleText('green', '[generate]'), `${src} => ${distRoot}${dist}`)
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 distribute = async (data, deleted, srcDir, distDir) => {
10
- if (deleted) {
11
- for (const obj of deleted) {
12
- console.log(styleText('red', '[unlink]'), `${distDir}${obj.__output}`)
13
- fs.unlink(`${distDir}${obj.__output}`)
14
- }
15
- delete data['__deleted']
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
- const template = data[name].template
19
- const rendered = await render(template, data[name])
20
- let writeTo = `${distDir}${data[name].__output}`
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
- if (typeof result === 'undefined') {
140
- result = null
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, deleted } from './indexer.js'
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
- const start = performance.now()
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
- await distribute(allData, deleted, srcDir, distDir)
12
- const end = performance.now()
13
- console.log(styleText('blue', '[build: ' + (end - start) + "ms]"))
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
@@ -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 includeFilter = async (text) => {
7
+ const includeRegexp = new RegExp(/\{\s*include\('(template|css)\/([\w\./]+)'\)\s*\}/g)
8
+
9
+ const includeFilter = (text) => {
8
10
  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
+ 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 [toBeReplace, type, filename] = [...include[index]]
18
+ const {toBeReplace, type, filename} = include[index]
13
19
  let content
14
20
  const cacheKey = `${type}/${filename}`
15
21
  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`.');
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 = await includeFilter(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 { writeFile, readFile } from "node:fs/promises";
3
- import { readdirSync, existsSync, mkdirSync } from "node:fs";
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
- const collect = (dir, files = {}, namePrefix = '') => {
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}`, files, namePrefix + 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 pageData = makePageData(`${namePrefix}${dirent.name}`)
21
- allData[pageData.name] = pageData
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
- deleted = oldIndex.filter(oi => !newIndex.map(ni => ni.__output).includes(oi.__output))
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, deleted }
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 load = (path) => {
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 = await includeFilter(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
- let path = url.pathname === '/' ? '/index.html' : decodeURIComponent(url.pathname)
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenjuu99/blog",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "blog template",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -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
- {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}
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
@@ -0,0 +1,7 @@
1
+ ---
2
+ title: 投稿一覧
3
+ ---
4
+ <script type="ssg">
5
+ variables.postPages = helper.readIndex().filter(v => v.url.indexOf('/post/') === 0)
6
+ </script>
7
+ {{ renderIndex(postPages) }}
@@ -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>投稿: {script}return helper.dateFormat(variables.published){/script}
37
+ <p>投稿: {{ dateFormat(published) }}
41
38
  {/if}
42
- {if modified} / 更新: {script}return helper.dateFormat(variables.modified){/script}{/if}
39
+ {if modified} / 更新: {{ dateFormat(modified) }}{/if}
43
40
  {if published}
44
41
  </p>
45
42
  </aside>
@@ -4,9 +4,6 @@
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>{{SITE_NAME}}</title>
7
- {if gtag_id}
8
- {include('template/gtag.html')}
9
- {/if}
10
7
  {include('template/css.html')}
11
8
  </head>
12
9
  <body>