@tenjuu99/blog 0.2.13 → 0.2.14

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/lib/dir.js CHANGED
@@ -2,6 +2,8 @@ import config from './config.js'
2
2
  import { watchers } from './watcher.js'
3
3
  import fs from 'node:fs'
4
4
  import { styleText } from 'node:util'
5
+ import path from 'path'
6
+ import { fileURLToPath } from 'url';
5
7
 
6
8
  const rootDir = process.cwd()
7
9
  const srcDir = `${rootDir}/${config.src_dir}`
@@ -21,6 +23,8 @@ const watch = {
21
23
  helperDir: `${srcDir}/helper`,
22
24
  jsDir: `${srcDir}/js`,
23
25
  }
26
+ const __filename = fileURLToPath(import.meta.url);
27
+ const packageDirCore = path.dirname(__filename) + '/../packages'
24
28
 
25
29
  let alreadyCached = false
26
30
 
@@ -31,11 +35,14 @@ const cache = () => {
31
35
  if (config.packages) {
32
36
  const packages = config.packages.split(',')
33
37
  packages.forEach(dir => {
34
- if (!fs.existsSync(`${packageDir}/${dir}`)) {
35
- throw new Error(`"${dir}" package does not exist.`)
38
+ if (fs.existsSync(`${packageDirCore}/${dir}`)) {
39
+ console.log(styleText('blue', `[cache] enable core package: ${dir}`))
40
+ fs.cpSync(`${packageDirCore}/${dir}`, cacheDir, { recursive: true })
41
+ }
42
+ if (fs.existsSync(`${packageDir}/${dir}`)) {
43
+ console.log(styleText('blue', `[cache] enable package: ${dir}`))
44
+ fs.cpSync(`${packageDir}/${dir}`, cacheDir, { recursive: true })
36
45
  }
37
- console.log(styleText('blue', `[cache] enable package: ${dir}`))
38
- fs.cpSync(`${packageDir}/${dir}`, cacheDir, { recursive: true })
39
46
  })
40
47
  }
41
48
  fs.cpSync(srcDir, cacheDir, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenjuu99/blog",
3
- "version": "0.2.13",
3
+ "version": "0.2.14",
4
4
  "description": "blog template",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -1,252 +0,0 @@
1
- body {
2
- background: #fafafa;
3
- height: 100vh;
4
- display: flex;
5
- flex-direction: column;
6
- }
7
- header {
8
- text-align: center;
9
- border-bottom: .1px solid #eee;
10
- box-shadow: 1px 1px 3px #999;
11
- }
12
- main {
13
- height: 100%;
14
- /*margin: 0 auto;*/
15
- display: flex;
16
- flex-direction: row;
17
- justify-content: flex-start;
18
- }
19
- main.container {
20
- padding: 0;
21
- }
22
- .textareaAndPreview {
23
- display: flex;
24
- margin: 0;
25
- border: 1px solid #666;
26
- border-radius: 5px;
27
- height: 100%;
28
- }
29
- .textareaAndPreview>* {
30
- flex-basis: 50%;
31
- }
32
- .editor {
33
- display: flex;
34
- flex-direction: column;
35
- padding: 5px;
36
- justify-content:center;
37
- height: 100%;
38
- /* width: 100%; */
39
- margin: 0;
40
- position: relative;
41
- width: 100%;
42
- left: 0;
43
- flex-basis: 100%;
44
- }
45
- .sidebar-close .editor {
46
- /*flex-basis: 90%;*/
47
- margin-left: 20px;
48
- }
49
- .editor-options {
50
- display: flex;
51
- justify-content: space-between;
52
- }
53
- #editorTextArea {
54
- resize: none;
55
- background: #cccccc;
56
- color: #333;
57
- padding: 5px;
58
- border-radius: 5px 0 0 5px;
59
- border: 0;
60
- transition: background 0.2s;
61
- }
62
- #editorTextArea:focus {
63
- background: #eee;
64
- transition: background 0.2s;
65
- outline: none;
66
- }
67
- form select {
68
- padding: 5px;
69
- border-radius: 5px;
70
- }
71
- #previewContent {
72
- flex-basis: 100%;
73
- height: 100%;
74
- min-height: 90%;
75
- display: block;
76
- width: 100%;
77
- }
78
- #previewContent iframe {
79
- width: 100%;
80
- height: 100%;
81
- border: none;
82
- border-radius: 0 5px 5px 0;
83
- }
84
- .sidebar {
85
- display: flex;
86
- justify-content: space-between;
87
- width: 300px;
88
- overflow: hidden;
89
- left: 0;
90
- background: rgb(192, 198, 217);
91
- height: 100%;
92
- padding: 0;
93
- overflow-y: hidden;
94
- flex-basis: 300px;
95
- position: relative;
96
- z-index: 3;
97
- word-break: break-all;
98
- }
99
- .sidebar-close .sidebar {
100
- left: -290px;
101
- flex-basis: 0;
102
- }
103
- .sidebar ul {
104
- padding: 0;
105
- margin: 0;
106
- }
107
- .sidebar-close .sidebar ul {
108
- position: absolute;
109
- left: -290px;
110
- transition: left 0.3s;
111
- }
112
- .sidebar li {
113
- list-style: none;
114
- padding-left: 20px;
115
- }
116
- .sidebar a {
117
- color: #666;
118
- text-decoration: none;
119
- }
120
- .sidebar li:hover {
121
- background: rgba(104, 117, 154, .3);
122
- }
123
- .sidebar-toggle {
124
- cursor: pointer;
125
- width: 20px;
126
- height: 100%;
127
- position: absolute;
128
- right: 0;
129
- }
130
- .sidebar-close .sidebar-toggle {
131
- position: fixed;
132
- left: 0;
133
- background: rgb(192, 198, 217);
134
- }
135
- .sidebar-toggle:hover:after {
136
- position: absolute;
137
- right: 0;
138
- content: '';
139
- height: 100%;
140
- width: 20px;
141
- background: rgba(255, 255,255, 0.3);
142
- }
143
- @media screen and (max-width: 600px) {
144
- main {
145
- width: 100%;
146
- display: block;
147
- }
148
- main.container {
149
- padding: 0 5px;
150
- }
151
- .sidebar {
152
- position: fixed;
153
- width: 80vw;
154
- z-index: 3;
155
- right: 0;
156
- left: unset;
157
- transition: right .3s, width .3s;
158
- }
159
- .sidebar-close .sidebar {
160
- width: 0;
161
- position: fixed;
162
- right: -300px;
163
- left: unset;
164
- transition: right .3s, width .3s;
165
- }
166
- .editor {
167
- padding: 15px 0;
168
- }
169
- .preview {
170
- display: none;
171
- }
172
- #editorTextArea {
173
- flex-basis: 100%;
174
- }
175
- .sidebar-toggle {
176
- display: none;
177
- }
178
- .sidebar {
179
- flex-basis: 80%;
180
- }
181
- .sidebar.close {
182
- flex-basis: 5%;
183
- }
184
- }
185
- /**
186
- * hamburger
187
- */
188
- .hamburger-menu {
189
- display: none;
190
- }
191
- @media screen and (max-width: 600px) {
192
- .sidebar-close .editor {
193
- margin-left: 0;
194
- margin: 0;
195
- }
196
- .editor-options {
197
- margin-right: 50px;
198
- }
199
- .editor-options * {
200
- display: flex;
201
- flex-direction: column;
202
- }
203
- .hamburger-menu {
204
- display: block;
205
- }
206
- .menu-btn {
207
- position: fixed;
208
- top: 10px;
209
- right: 10px;
210
- display: flex;
211
- height: 40px;
212
- width: 40px;
213
- justify-content: center;
214
- align-items: center;
215
- z-index: 90;
216
- /* background-color: #3584bb; */
217
- }
218
- .menu-btn span,
219
- .menu-btn span:before,
220
- .menu-btn span:after {
221
- content: '';
222
- display: block;
223
- height: 3px;
224
- width: 25px;
225
- border-radius: 3px;
226
- background-color: #666666;
227
- position: absolute;
228
- transition: transform 0.3s, background-color 0.3s;
229
- }
230
- .menu-btn span:before {
231
- bottom: 8px;
232
- }
233
- .menu-btn span:after {
234
- top: 8px;
235
- }
236
- #menu-btn-check:checked ~ .menu-btn span {
237
- background-color: rgba(255, 255, 255, 0);/*メニューオープン時は真ん中の線を透明にする*/
238
- }
239
- #menu-btn-check:checked ~ .menu-btn span::before {
240
- bottom: 0;
241
- transform: rotate(45deg);
242
- transition: transform 0.3s, background-color 0.3s;
243
- }
244
- #menu-btn-check:checked ~ .menu-btn span::after {
245
- top: 0;
246
- transform: rotate(-45deg);
247
- transition: transform 0.3s, background-color 0.3s;
248
- }
249
- #menu-btn-check {
250
- display: none;
251
- }
252
- }
@@ -1,123 +0,0 @@
1
- const sleep = waitTime => new Promise( resolve => setTimeout(resolve, waitTime) );
2
-
3
- const fetchData = (target) => {
4
- return fetch(`/get_editor_target?md=${target}`)
5
- .then(async res => {
6
- if (!res.ok) {
7
- document.querySelector('#inputFileName').value = target
8
- document.querySelector('#editorTextArea').value = `---
9
- title: ${target.split('.')[0].split('/').pop()}
10
- ---
11
- ${target.split('.')[0].split('/').pop()} についての記事を作成しましょう`
12
- // submit('/preview', form)
13
- throw new Error(`${target} does not exist.`)
14
- } else {
15
- const json = await res.json()
16
- return json
17
- }
18
- })
19
- }
20
- const onloadFunction = async (e) => {
21
- const form = document.querySelector('#editor')
22
- const textarea = form.querySelector('#editorTextArea')
23
- const select = form.querySelector('#selectDataFile')
24
- const inputFileName = form.querySelector('#inputFileName')
25
- const preview = document.querySelector('#previewContent')
26
- const url = new URL(location)
27
- const target = url.searchParams.get('md')
28
- if (target) {
29
- fetchData(target).then(json => {
30
- textarea.value = json.content
31
- select.value = json.filename
32
- inputFileName.value = json.filename
33
- inputFileName.setAttribute('disabled', true)
34
- submit('/preview', form)
35
- }).catch(e => {
36
- console.log('error!!!')
37
- console.log(e)
38
- })
39
- }
40
- select.addEventListener('change', async (event) => {
41
- if (select.value) {
42
- const json = await fetchData(select.value)
43
- textarea.value = json.content
44
- inputFileName.value = json.filename
45
- inputFileName.setAttribute('disabled', true)
46
- url.searchParams.set('md', select.value)
47
- submit('/preview', form)
48
- } else {
49
- inputFileName.value = ""
50
- inputFileName.removeAttribute('disabled')
51
- textarea.value = ''
52
- url.searchParams.set('md', "")
53
- const iframe = preview.querySelector('iframe')
54
- if (iframe) {
55
- iframe.srcdoc = ''
56
- }
57
- }
58
- history.pushState({}, "", url)
59
- })
60
-
61
- const submit = (fetchUrl, form) => {
62
- const formData = new FormData(form)
63
- const obj = {}
64
- formData.forEach((v, k) => {
65
- obj[k] = v
66
- })
67
- return fetch(fetchUrl, {
68
- method: 'post',
69
- body: JSON.stringify(obj)
70
- }).then(async response => {
71
- const json = await response.json()
72
- if (!response.ok) {
73
- alert(json.message)
74
- return
75
- }
76
- if (json.href) {
77
- await sleep(300)
78
- location.href = json.href
79
- }
80
- if (json.preview) {
81
- const iframe = document.createElement('iframe')
82
- iframe.setAttribute('srcdoc', json.preview)
83
- iframe.setAttribute('sandbox', 'allow-same-origin allow-scripts')
84
- const old = preview.querySelector('iframe')
85
- if (!old) {
86
- preview.appendChild(iframe)
87
- }
88
- old.setAttribute('srcdoc', json.preview)
89
- }
90
- }).catch(e => {
91
- console.log(e.message)
92
- })
93
- }
94
- form.addEventListener('submit', (event) => {
95
- event.preventDefault()
96
- const fetchUrl = event.submitter.dataset.url
97
- submit(fetchUrl, event.target)
98
- })
99
- }
100
-
101
- const sidebarToggle = (e) => {
102
- const sidebar = document.querySelector('.sidebar')
103
- const main = document.querySelector('main')
104
- const toggle = sidebar.querySelector('.sidebar-toggle')
105
- toggle.addEventListener('click', (e) => {
106
- e.preventDefault()
107
- main.classList.toggle('sidebar-close')
108
- localStorage.setItem('sidebar-is-open', !main.classList.contains('sidebar-close'))
109
- })
110
- if (localStorage.getItem('sidebar-is-open') === 'true') {
111
- main.classList.remove('sidebar-close')
112
- } else {
113
- main.classList.add('sidebar-close')
114
- }
115
- const hamburger = document.querySelector('.hamburger-menu input[type="checkbox"]')
116
- hamburger.addEventListener('change', (e) => {
117
- main.classList.toggle('sidebar-close')
118
- })
119
- }
120
- document.addEventListener('DOMContentLoaded', (event) => {
121
- onloadFunction(event)
122
- sidebarToggle(event)
123
- })
@@ -1,8 +0,0 @@
1
- document.addEventListener('DOMContentLoaded', (e) => {
2
- })
3
- const url = new URL(location)
4
- if (url.pathname !== '/editor') {
5
- const link = document.querySelector('.editor_link')
6
- link.href = `/editor?md=${url.pathname.replace('/', '')}.md`
7
- link.innerHTML = `[${decodeURI(url.pathname).replace('/', '')}]のページを作成する`
8
- }
@@ -1,7 +0,0 @@
1
- ---
2
- template: editor.html
3
- index: false
4
- distribute: config.editor_enable
5
- ---
6
-
7
- 新規に記事を作成しましょう。
@@ -1,37 +0,0 @@
1
- import { IncomingMessage, ServerResponse } from 'http'
2
- import fs from 'node:fs/promises'
3
- import { styleText } from 'node:util'
4
- import config from '@tenjuu99/blog/lib/config.js'
5
- import { watch } from '@tenjuu99/blog/lib/dir.js'
6
-
7
- export const path = '/editor'
8
-
9
- /**
10
- * @param {IncomingMessage} req
11
- * @param {ServerResponse} res
12
- */
13
- export const post = async (req, res) => {
14
- const chunks = []
15
- req
16
- .on('data', (chunk) => chunks.push(chunk))
17
- .on('end', async () => {
18
- const json = JSON.parse(chunks.join())
19
- const file = json.inputFileName ? json.inputFileName : json.selectDataFile
20
- if (!file) {
21
- res.writeHead(400, { 'content-type': 'application/json' })
22
- res.end(JSON.stringify({
23
- 'message': 'ファイル名がありません'
24
- }))
25
- return
26
- }
27
- await fs.writeFile(`${watch.pageDir}/${file}`, json.content)
28
- console.log(styleText('blue', '[editor/post] finished'))
29
-
30
- const href = file.split('.')[0]
31
- res.writeHead(200, { 'content-type': 'application/json' })
32
- res.end(JSON.stringify({
33
- 'href': `/${href}`
34
- }))
35
- })
36
- return true
37
- }
@@ -1,32 +0,0 @@
1
- import { IncomingMessage, ServerResponse } from 'http'
2
- import fs from 'node:fs'
3
- import config from '@tenjuu99/blog/lib/config.js'
4
- import { pageDir } from '@tenjuu99/blog/lib/dir.js'
5
-
6
- export const path = '/get_editor_target'
7
-
8
- /**
9
- * @param {IncomingMessage} req
10
- * @param {ServerResponse} res
11
- */
12
- export const get = async (req, res) => {
13
- const url = new URL(`${config.url_base}${req.url}`)
14
- const target = url.searchParams.get('md')
15
- if (!target) {
16
- return
17
- }
18
- const file = `${pageDir}/${target}`
19
- if (!fs.existsSync(`${file}`)) {
20
- return {
21
- status: 404,
22
- contentType: 'application/json',
23
- body: JSON.stringify({ content: '', filename: target })
24
- }
25
- }
26
- const f = fs.readFileSync(`${file}`, 'utf8')
27
- return {
28
- status: 200,
29
- contentType: 'application/json',
30
- body: JSON.stringify({ content: f, filename: target }),
31
- }
32
- }
@@ -1,33 +0,0 @@
1
- import { IncomingMessage, ServerResponse } from 'http'
2
- import { styleText } from 'node:util'
3
- import render from '@tenjuu99/blog/lib/render.js'
4
- import makePageData from '@tenjuu99/blog/lib/pageData.js'
5
-
6
- export const path = '/preview'
7
-
8
- /**
9
- * @param {IncomingMessage} req
10
- * @param {ServerResponse} res
11
- */
12
- export const post = async (req, res) => {
13
- const chunks = []
14
- req
15
- .on('data', (chunk) => chunks.push(chunk))
16
- .on('end', async () => {
17
- const json = JSON.parse(chunks.join())
18
- const filename = json.inputFileName ? json.inputFileName : json.selectDataFile
19
- if (!filename) {
20
- res.writeHead(400, { 'content-type': 'application/json' })
21
- return res.end(JSON.stringify({
22
- message: 'filename is requried.'
23
- }))
24
- }
25
- const pageData = makePageData(filename, json.content)
26
- const rendered = await render(pageData.template, pageData)
27
- res.writeHead(200, { 'content-type': 'application/json' })
28
- res.end(JSON.stringify({
29
- 'preview': rendered
30
- }))
31
- })
32
- return true
33
- }
@@ -1,58 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="ja">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>{{SITE_NAME}}</title>
7
- <link rel="stylesheet" href="${/css/editor.css<<reset.css,editor.css}">
8
- <script src="/js/editor.js" defer></script>
9
-
10
- </head>
11
- <body>
12
- <main class="container">
13
- <div class="sidebar">
14
- <ul>
15
- <script type="ssg">
16
- return helper.readIndex().map(p => {
17
- return `<li><a href="/editor?md=${p.name}.${p.__filetype}">${p.url}.${p.__filetype}</a></li>`
18
- }).join("\n")
19
- </script>
20
- </ul>
21
- <div class="sidebar-toggle">&nbsp;</div>
22
- </div>
23
- <form action="/editor" class="editor" method="post" id="editor">
24
- <div class="editor-options">
25
- <div>
26
- <input id="inputFileName" name="inputFileName" type="text" value="" placeholder="sample.md">
27
- <select id="selectDataFile" name="selectDataFile">
28
- <option value="">新規作成</option>
29
- <script type="ssg">
30
- return helper.readIndex().map(p => {
31
- return `<option value="${p.name}.${p.__filetype}">${p.url}.${p.__filetype}</option>`
32
- }).join("\n")
33
- </script>
34
- </select>
35
- </div>
36
- <div>
37
- <input type="submit" value="preview" data-url="/preview">
38
- <input type="submit" value="save" data-url="/editor">
39
- <a href="/">戻る</a>
40
- </div>
41
- </div>
42
- <div class="textareaAndPreview">
43
- <textarea id="editorTextArea" name="content" cols="30" rows="10">
44
- {{ MARKDOWN_NOT_PARSED }}
45
- </textarea>
46
- <div class="preview">
47
- <div id="previewContent"></div>
48
- </div>
49
- </div>
50
- <input type="hidden" name="token" value="{{TOKEN}}">
51
- </form>
52
- <div class="hamburger-menu">
53
- <input type="checkbox" id="menu-btn-check">
54
- <label for="menu-btn-check" class="menu-btn"><span></span></label>
55
- </div>
56
- </main>
57
- </body>
58
- </html>