pugkit 1.0.0-beta.2 → 1.0.0-beta.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/README.md +2 -1
- package/core/server.mjs +125 -60
- package/core/watcher.mjs +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -164,7 +164,8 @@ project-root/
|
|
|
164
164
|
- [Sharp](https://sharp.pixelplumbing.com/) - 画像最適化
|
|
165
165
|
- [SVGO](https://svgo.dev/) - SVG最適化
|
|
166
166
|
- [Chokidar](https://github.com/paulmillr/chokidar) - ファイル監視
|
|
167
|
-
- [
|
|
167
|
+
- [sirv](https://github.com/lukeed/sirv) - 開発サーバー
|
|
168
|
+
- SSE(Server-Sent Events) - ライブリロード
|
|
168
169
|
- [Prettier](https://prettier.io/) - HTML整形
|
|
169
170
|
|
|
170
171
|
## License
|
package/core/server.mjs
CHANGED
|
@@ -1,77 +1,142 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import { existsSync } from 'node:fs'
|
|
1
|
+
import http from 'node:http'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
4
4
|
import { mkdir } from 'node:fs/promises'
|
|
5
|
+
import sirv from 'sirv'
|
|
5
6
|
import { logger } from '../utils/logger.mjs'
|
|
6
7
|
|
|
8
|
+
const SSE_PATH = '/__pugkit_sse'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* HTMLに挿入するライブリロードクライアントスクリプト。
|
|
12
|
+
*/
|
|
13
|
+
const liveReloadScript = `<script>
|
|
14
|
+
(function() {
|
|
15
|
+
var es = new EventSource('${SSE_PATH}');
|
|
16
|
+
es.addEventListener('reload', function() {
|
|
17
|
+
location.reload();
|
|
18
|
+
});
|
|
19
|
+
es.addEventListener('css-update', function() {
|
|
20
|
+
document.querySelectorAll('link[rel="stylesheet"]').forEach(function(link) {
|
|
21
|
+
var url = new URL(link.href);
|
|
22
|
+
url.searchParams.set('t', Date.now());
|
|
23
|
+
link.href = url.toString();
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
es.onerror = function() {
|
|
27
|
+
es.close();
|
|
28
|
+
setTimeout(function() { location.reload(); }, 1000);
|
|
29
|
+
};
|
|
30
|
+
})();
|
|
31
|
+
</script>`
|
|
32
|
+
|
|
7
33
|
/**
|
|
8
|
-
*
|
|
34
|
+
* 開発サーバータスク(SSE + sirv)
|
|
9
35
|
*/
|
|
10
36
|
export async function serverTask(context, options = {}) {
|
|
11
37
|
const { paths, config } = context
|
|
12
|
-
const distRoot = paths.dist
|
|
13
38
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
await mkdir(distRoot, { recursive: true })
|
|
39
|
+
if (!existsSync(paths.dist)) {
|
|
40
|
+
await mkdir(paths.dist, { recursive: true })
|
|
17
41
|
}
|
|
18
42
|
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
context.server = bs
|
|
22
|
-
|
|
43
|
+
const port = config.server?.port ?? 3000
|
|
44
|
+
const host = config.server?.host ?? 'localhost'
|
|
23
45
|
const subdir = config.subdir ? '/' + config.subdir.replace(/^\/|\/$/g, '') : ''
|
|
24
|
-
const startPath = (config.server
|
|
46
|
+
const startPath = (config.server?.startPath || '/').replace(/^\//, '')
|
|
25
47
|
const fullStartPath = subdir ? `${subdir}/${startPath}` : `/${startPath}`
|
|
26
48
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
setHeaders: (res, path) => {
|
|
36
|
-
if (path.endsWith('.css') || path.endsWith('.js')) {
|
|
37
|
-
res.setHeader('Cache-Control', 'no-cache')
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
},
|
|
42
|
-
open: false,
|
|
43
|
-
scrollProportionally: false,
|
|
44
|
-
ghostMode: false,
|
|
45
|
-
ui: false,
|
|
46
|
-
startPath: fullStartPath,
|
|
47
|
-
port: config.server.port,
|
|
48
|
-
host: config.server.host,
|
|
49
|
-
socket: {
|
|
50
|
-
namespace: '/browser-sync'
|
|
51
|
-
},
|
|
52
|
-
logLevel: 'silent',
|
|
53
|
-
logFileChanges: false,
|
|
54
|
-
logConnections: false,
|
|
55
|
-
minify: false,
|
|
56
|
-
timestamps: false,
|
|
57
|
-
codeSync: true,
|
|
58
|
-
online: false,
|
|
59
|
-
files: false, // 手動でリロード制御
|
|
60
|
-
injectChanges: true,
|
|
61
|
-
reloadDelay: 0,
|
|
62
|
-
reloadDebounce: 50
|
|
63
|
-
},
|
|
64
|
-
err => {
|
|
65
|
-
if (err) {
|
|
66
|
-
logger.error('server', err.message)
|
|
67
|
-
reject(err)
|
|
68
|
-
} else {
|
|
69
|
-
const urls = bs.getOption('urls')
|
|
70
|
-
logger.success('server', `Running at ${urls.get('local')}`)
|
|
71
|
-
resolve()
|
|
72
|
-
}
|
|
49
|
+
const clients = new Set()
|
|
50
|
+
|
|
51
|
+
const staticServe = sirv(paths.dist, {
|
|
52
|
+
dev: true,
|
|
53
|
+
extensions: ['html'],
|
|
54
|
+
setHeaders(res, filePath) {
|
|
55
|
+
if (filePath.endsWith('.css') || filePath.endsWith('.js')) {
|
|
56
|
+
res.setHeader('Cache-Control', 'no-cache')
|
|
73
57
|
}
|
|
74
|
-
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
const httpServer = http.createServer((req, res) => {
|
|
62
|
+
const urlPath = req.url?.split('?')[0] ?? '/'
|
|
63
|
+
|
|
64
|
+
// ── SSE エンドポイント ──────────────────────────────
|
|
65
|
+
if (urlPath === SSE_PATH) {
|
|
66
|
+
res.writeHead(200, {
|
|
67
|
+
'Content-Type': 'text/event-stream',
|
|
68
|
+
'Cache-Control': 'no-cache',
|
|
69
|
+
Connection: 'keep-alive',
|
|
70
|
+
'X-Accel-Buffering': 'no'
|
|
71
|
+
})
|
|
72
|
+
res.write('retry: 1000\n\n')
|
|
73
|
+
clients.add(res)
|
|
74
|
+
req.on('close', () => clients.delete(res))
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ── HTML へのライブリロードスクリプト注入 ───────────
|
|
79
|
+
const decoded = decodeURIComponent(urlPath)
|
|
80
|
+
const candidates = [
|
|
81
|
+
path.join(paths.dist, decoded === '/' ? 'index.html' : decoded.replace(/\/$/, '') + '/index.html'),
|
|
82
|
+
path.join(paths.dist, decoded === '/' ? 'index.html' : decoded + '.html'),
|
|
83
|
+
path.join(paths.dist, decoded)
|
|
84
|
+
]
|
|
85
|
+
const htmlFile = candidates.find(p => p.endsWith('.html') && existsSync(p))
|
|
86
|
+
|
|
87
|
+
if (htmlFile) {
|
|
88
|
+
try {
|
|
89
|
+
let html = readFileSync(htmlFile, 'utf-8')
|
|
90
|
+
html = html.includes('</body>')
|
|
91
|
+
? html.replace('</body>', liveReloadScript + '</body>')
|
|
92
|
+
: html + liveReloadScript
|
|
93
|
+
const buf = Buffer.from(html, 'utf-8')
|
|
94
|
+
res.writeHead(200, {
|
|
95
|
+
'Content-Type': 'text/html; charset=utf-8',
|
|
96
|
+
'Content-Length': buf.length,
|
|
97
|
+
'Cache-Control': 'no-cache'
|
|
98
|
+
})
|
|
99
|
+
res.end(buf)
|
|
100
|
+
return
|
|
101
|
+
} catch {
|
|
102
|
+
// 読み込み失敗時は sirv にフォールスルー
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ── sirv で静的ファイルを配信 ───────────────────────
|
|
107
|
+
staticServe(req, res, () => {
|
|
108
|
+
res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' })
|
|
109
|
+
res.end('404 Not Found')
|
|
110
|
+
})
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
function broadcast(event, data = '') {
|
|
114
|
+
const msg = `event: ${event}\ndata: ${data}\n\n`
|
|
115
|
+
for (const res of clients) {
|
|
116
|
+
res.write(msg)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
context.server = {
|
|
121
|
+
reload() {
|
|
122
|
+
broadcast('reload')
|
|
123
|
+
},
|
|
124
|
+
reloadCSS() {
|
|
125
|
+
broadcast('css-update')
|
|
126
|
+
},
|
|
127
|
+
close() {
|
|
128
|
+
for (const res of clients) res.end()
|
|
129
|
+
clients.clear()
|
|
130
|
+
httpServer.close()
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return new Promise((resolve, reject) => {
|
|
135
|
+
httpServer.listen(port, host, () => {
|
|
136
|
+
logger.success('server', `Running at http://${host}:${port}${fullStartPath}`)
|
|
137
|
+
resolve()
|
|
138
|
+
})
|
|
139
|
+
httpServer.on('error', reject)
|
|
75
140
|
})
|
|
76
141
|
}
|
|
77
142
|
|
package/core/watcher.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pugkit",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.3",
|
|
4
4
|
"description": "A build tool for Pug-based projects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "mfxgu2i <mfxgu2i@gmail.com>",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
|
-
"url": "https://github.com/mfxgu2i/pugkit.git",
|
|
10
|
+
"url": "git+https://github.com/mfxgu2i/pugkit.git",
|
|
11
11
|
"directory": "packages/pugkit"
|
|
12
12
|
},
|
|
13
13
|
"bugs": "https://github.com/mfxgu2i/pugkit/issues",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"access": "public"
|
|
20
20
|
},
|
|
21
21
|
"bin": {
|
|
22
|
-
"pugkit": "
|
|
22
|
+
"pugkit": "cli/index.mjs"
|
|
23
23
|
},
|
|
24
24
|
"keywords": [
|
|
25
25
|
"pug",
|
|
@@ -43,12 +43,11 @@
|
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"autoprefixer": "^10.4.21",
|
|
46
|
-
"browser-sync": "^3.0.4",
|
|
47
46
|
"cac": "^6.7.14",
|
|
48
47
|
"chokidar": "^4.0.3",
|
|
49
48
|
"cssnano": "^7.1.1",
|
|
50
49
|
"esbuild": "^0.25.5",
|
|
51
|
-
"glob": "^
|
|
50
|
+
"glob": "^13.0.6",
|
|
52
51
|
"image-size": "^2.0.2",
|
|
53
52
|
"picocolors": "^1.1.1",
|
|
54
53
|
"postcss": "^8.4.49",
|
|
@@ -56,6 +55,7 @@
|
|
|
56
55
|
"pug": "^3.0.3",
|
|
57
56
|
"sass": "^1.89.2",
|
|
58
57
|
"sharp": "^0.34.2",
|
|
58
|
+
"sirv": "^3.0.2",
|
|
59
59
|
"svgo": "^4.0.0"
|
|
60
60
|
}
|
|
61
61
|
}
|