pugkit 1.0.0-beta.1
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 +172 -0
- package/cli/build.mjs +15 -0
- package/cli/develop.mjs +16 -0
- package/cli/index.mjs +55 -0
- package/cli/logger.mjs +31 -0
- package/cli/sprite.mjs +13 -0
- package/config/defaults.mjs +36 -0
- package/config/define.mjs +3 -0
- package/config/index.mjs +4 -0
- package/config/main.mjs +53 -0
- package/config/patterns.mjs +30 -0
- package/core/builder.mjs +139 -0
- package/core/cache.mjs +98 -0
- package/core/context.mjs +51 -0
- package/core/graph.mjs +97 -0
- package/core/server.mjs +78 -0
- package/core/watcher.mjs +314 -0
- package/generate/asset.mjs +19 -0
- package/generate/page.mjs +13 -0
- package/index.mjs +60 -0
- package/package.json +61 -0
- package/tasks/copy.mjs +40 -0
- package/tasks/image.mjs +93 -0
- package/tasks/pug.mjs +81 -0
- package/tasks/sass.mjs +107 -0
- package/tasks/script.mjs +75 -0
- package/tasks/svg-sprite.mjs +125 -0
- package/tasks/svg.mjs +73 -0
- package/transform/builder-vars.mjs +33 -0
- package/transform/html.mjs +14 -0
- package/transform/image-size.mjs +38 -0
- package/transform/pug.mjs +26 -0
- package/utils/file.mjs +44 -0
- package/utils/logger.mjs +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# pugkit
|
|
2
|
+
|
|
3
|
+
## About
|
|
4
|
+
|
|
5
|
+
pugkitは静的サイト制作に特化したビルドツールです。
|
|
6
|
+
納品向きの綺麗なHTMLと自由度の高いアセットを出力します。
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install pugkit
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
### Development Mode
|
|
17
|
+
|
|
18
|
+
ファイルの変更を監視し、ブラウザの自動リロードを行います。
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pugkit
|
|
22
|
+
# or
|
|
23
|
+
pugkit dev
|
|
24
|
+
# or
|
|
25
|
+
pugkit watch
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Production Build
|
|
29
|
+
|
|
30
|
+
最適化されたファイルを生成します。
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pugkit build
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### SVG Sprite Generation
|
|
37
|
+
|
|
38
|
+
アイコン用のSVGスプライトを生成します。
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pugkit sprite
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Configuration
|
|
45
|
+
|
|
46
|
+
プロジェクトのルートに `pugkit.config.mjs` を配置することで、ビルド設定をカスタマイズできます。
|
|
47
|
+
|
|
48
|
+
```js
|
|
49
|
+
// pugkit.config.mjs
|
|
50
|
+
export default {
|
|
51
|
+
siteUrl: 'https://example.com/',
|
|
52
|
+
subdir: 'subdirectory',
|
|
53
|
+
debug: false,
|
|
54
|
+
server: {
|
|
55
|
+
port: 5555,
|
|
56
|
+
host: 'localhost',
|
|
57
|
+
startPath: '/'
|
|
58
|
+
},
|
|
59
|
+
build: {
|
|
60
|
+
imageOptimization: 'webp', // 'webp' | 'compress' | false
|
|
61
|
+
imageOptions: {
|
|
62
|
+
webp: {
|
|
63
|
+
quality: 90,
|
|
64
|
+
effort: 6
|
|
65
|
+
},
|
|
66
|
+
jpeg: {
|
|
67
|
+
quality: 75,
|
|
68
|
+
progressive: true
|
|
69
|
+
},
|
|
70
|
+
png: {
|
|
71
|
+
quality: 85,
|
|
72
|
+
compressionLevel: 6
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Configuration Options
|
|
80
|
+
|
|
81
|
+
| Option | Description | Default |
|
|
82
|
+
| ------------------------- | -------------------------------- | ------------- |
|
|
83
|
+
| `debug` | デバッグモード(開発時のみ有効) | `false` |
|
|
84
|
+
| `server.port` | 開発サーバーのポート番号 | `5555` |
|
|
85
|
+
| `server.host` | 開発サーバーのホスト | `'localhost'` |
|
|
86
|
+
| `server.startPath` | サーバー起動時に開くパス | `'/'` |
|
|
87
|
+
| `server.open` | サーバー起動時にブラウザを開く | `false` |
|
|
88
|
+
| `build.imageOptimization` | 画像最適化の方式 | `'webp'` |
|
|
89
|
+
| `build.imageOptions` | 画像最適化の詳細設定 | - |
|
|
90
|
+
|
|
91
|
+
## Features
|
|
92
|
+
|
|
93
|
+
### Pug Templates
|
|
94
|
+
|
|
95
|
+
Pugテンプレート内では、`Builder`オブジェクトと`imageSize()`関数が使用できます。
|
|
96
|
+
|
|
97
|
+
#### Builder Object
|
|
98
|
+
|
|
99
|
+
```pug
|
|
100
|
+
//- 相対パスでリンク
|
|
101
|
+
a(href=`${Builder.dir}about/`)
|
|
102
|
+
|
|
103
|
+
//- 完全なURL
|
|
104
|
+
meta(property='og:url', content=Builder.url.href)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
| Property | Description | Example |
|
|
108
|
+
| ---------------------- | ---------------------------------- | ----------------------------------------- |
|
|
109
|
+
| `Builder.dir` | 現在のページからルートへの相対パス | `./` or `../` |
|
|
110
|
+
| `Builder.subdir` | サブディレクトリのパス | `/subdirectory` |
|
|
111
|
+
| `Builder.url.origin` | サイトのオリジン | `https://example.com` |
|
|
112
|
+
| `Builder.url.base` | サイトのベースURL | `https://example.com/subdirectory` |
|
|
113
|
+
| `Builder.url.pathname` | 現在のページのパス | `/about/` |
|
|
114
|
+
| `Builder.url.href` | 完全なURL | `https://example.com/subdirectory/about/` |
|
|
115
|
+
|
|
116
|
+
#### imageSize() Function
|
|
117
|
+
|
|
118
|
+
画像ファイルのサイズを自動取得し、CLSを防ぎます。
|
|
119
|
+
|
|
120
|
+
```pug
|
|
121
|
+
- const size = imageSize('/assets/img/photo.jpg')
|
|
122
|
+
img(src='/assets/img/photo.jpg', width=size.width, height=size.height, alt='')
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Image Optimization
|
|
126
|
+
|
|
127
|
+
ビルド時に自動的に画像を最適化します。
|
|
128
|
+
|
|
129
|
+
- `imageOptimization: 'webp'` - PNG/JPEGをWebP形式に変換
|
|
130
|
+
- `imageOptimization: 'compress'` - 元の形式を維持したまま圧縮
|
|
131
|
+
- `imageOptimization: false` - 最適化を無効化
|
|
132
|
+
|
|
133
|
+
### File Naming Rules
|
|
134
|
+
|
|
135
|
+
- `_`(アンダースコア)で始まるファイルは部分テンプレートとして扱われます
|
|
136
|
+
- `_`で始まるディレクトリ内のファイルもビルド対象外です
|
|
137
|
+
- 通常のファイル名のみがビルドされます
|
|
138
|
+
|
|
139
|
+
## Directory Structure
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
project-root/
|
|
143
|
+
├── src/ # ソースファイル
|
|
144
|
+
│ ├── *.pug
|
|
145
|
+
│ ├── *.scss
|
|
146
|
+
│ ├── *.ts
|
|
147
|
+
│ ├── *.js
|
|
148
|
+
│ ├── *.jpg
|
|
149
|
+
│ ├── *.png
|
|
150
|
+
│ └── *.svg
|
|
151
|
+
├── public/ # 静的ファイル
|
|
152
|
+
│ ├── ogp.jpg
|
|
153
|
+
│ └── favicon.ico
|
|
154
|
+
├── dist/ # ビルド出力先
|
|
155
|
+
└── pugkit.config.mjs # ビルド設定ファイル
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Tech Stack
|
|
159
|
+
|
|
160
|
+
- [Pug](https://pugjs.org/) - HTMLテンプレートエンジン
|
|
161
|
+
- [Sass](https://sass-lang.com/) - CSSプリプロセッサー
|
|
162
|
+
- [esbuild](https://esbuild.github.io/) - TypeScript/JavaScriptバンドラー
|
|
163
|
+
- [PostCSS](https://postcss.org/) - CSS後処理(Autoprefixer、cssnano)
|
|
164
|
+
- [Sharp](https://sharp.pixelplumbing.com/) - 画像最適化
|
|
165
|
+
- [SVGO](https://svgo.dev/) - SVG最適化
|
|
166
|
+
- [Chokidar](https://github.com/paulmillr/chokidar) - ファイル監視
|
|
167
|
+
- [BrowserSync](https://browsersync.io/) - 開発サーバー
|
|
168
|
+
- [Prettier](https://prettier.io/) - HTML整形
|
|
169
|
+
|
|
170
|
+
## License
|
|
171
|
+
|
|
172
|
+
MIT
|
package/cli/build.mjs
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createBuilder } from '../index.mjs'
|
|
2
|
+
import { logger } from './logger.mjs'
|
|
3
|
+
|
|
4
|
+
export async function build(options = {}) {
|
|
5
|
+
const { root = process.cwd() } = options
|
|
6
|
+
|
|
7
|
+
logger.info('pugkit', 'building...')
|
|
8
|
+
|
|
9
|
+
const startTime = Date.now()
|
|
10
|
+
const builder = await createBuilder(root, 'production')
|
|
11
|
+
await builder.build()
|
|
12
|
+
|
|
13
|
+
const elapsed = Date.now() - startTime
|
|
14
|
+
logger.success('pugkit', `built in ${elapsed}ms`)
|
|
15
|
+
}
|
package/cli/develop.mjs
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createBuilder } from '../index.mjs'
|
|
2
|
+
import { logger } from './logger.mjs'
|
|
3
|
+
|
|
4
|
+
export async function develop(options = {}) {
|
|
5
|
+
const { root = process.cwd(), port, host, open } = options
|
|
6
|
+
|
|
7
|
+
logger.info('pugkit', 'starting dev server...')
|
|
8
|
+
|
|
9
|
+
const builder = await createBuilder(root, 'development')
|
|
10
|
+
|
|
11
|
+
if (port) builder.context.config.server.port = port
|
|
12
|
+
if (host) builder.context.config.server.host = host
|
|
13
|
+
if (open) builder.context.config.server.open = open
|
|
14
|
+
|
|
15
|
+
await builder.watch()
|
|
16
|
+
}
|
package/cli/index.mjs
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readFileSync } from 'node:fs'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
5
|
+
import path from 'node:path'
|
|
6
|
+
import { cac } from 'cac'
|
|
7
|
+
import { develop } from './develop.mjs'
|
|
8
|
+
import { build } from './build.mjs'
|
|
9
|
+
import { sprite } from './sprite.mjs'
|
|
10
|
+
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
12
|
+
const __dirname = path.dirname(__filename)
|
|
13
|
+
|
|
14
|
+
function pkgVersion() {
|
|
15
|
+
const pkgPath = path.join(__dirname, '../package.json')
|
|
16
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
|
|
17
|
+
return pkg.version
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const cli = cac('pugkit')
|
|
21
|
+
|
|
22
|
+
cli
|
|
23
|
+
.command('[root]', 'Start development mode with file watching')
|
|
24
|
+
.alias('dev')
|
|
25
|
+
.alias('watch')
|
|
26
|
+
.action(async (root, options) => {
|
|
27
|
+
try {
|
|
28
|
+
await develop({ root: root || process.cwd() })
|
|
29
|
+
} catch (err) {
|
|
30
|
+
console.error(err)
|
|
31
|
+
process.exit(1)
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
cli.command('build [root]', 'Production build').action(async root => {
|
|
36
|
+
try {
|
|
37
|
+
await build({ root: root || process.cwd() })
|
|
38
|
+
} catch (err) {
|
|
39
|
+
console.error(err)
|
|
40
|
+
process.exit(1)
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
cli.command('sprite [root]', 'Generate SVG sprite').action(async root => {
|
|
45
|
+
try {
|
|
46
|
+
await sprite({ root: root || process.cwd() })
|
|
47
|
+
} catch (err) {
|
|
48
|
+
console.error(err)
|
|
49
|
+
process.exit(1)
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
cli.help()
|
|
54
|
+
cli.version(pkgVersion())
|
|
55
|
+
cli.parse()
|
package/cli/logger.mjs
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import pc from 'picocolors'
|
|
2
|
+
import { readFileSync } from 'node:fs'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
7
|
+
const __dirname = path.dirname(__filename)
|
|
8
|
+
|
|
9
|
+
function getVersion() {
|
|
10
|
+
const pkgPath = path.join(__dirname, '../package.json')
|
|
11
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
|
|
12
|
+
return pkg.version
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const logger = {
|
|
16
|
+
info(name, message) {
|
|
17
|
+
console.log(`${pc.cyan(pc.bold(name.toUpperCase()))} ${pc.dim(`v${getVersion()}`)} ${message}`)
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
success(name, message) {
|
|
21
|
+
console.log(`${pc.cyan(pc.bold(name.toUpperCase()))} ${pc.dim(`v${getVersion()}`)} ${message}`)
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
warn(name, message) {
|
|
25
|
+
console.log(`${pc.cyan(pc.bold(name.toUpperCase()))} ${pc.dim(`v${getVersion()}`)} ${message}`)
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
error(name, message) {
|
|
29
|
+
console.error(`${pc.cyan(pc.bold(name.toUpperCase()))} ${pc.dim(`v${getVersion()}`)} ${message}`)
|
|
30
|
+
}
|
|
31
|
+
}
|
package/cli/sprite.mjs
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createBuilder } from '../index.mjs'
|
|
2
|
+
import { logger } from './logger.mjs'
|
|
3
|
+
|
|
4
|
+
export async function sprite(options = {}) {
|
|
5
|
+
const { root = process.cwd() } = options
|
|
6
|
+
|
|
7
|
+
logger.info('pugkit', 'generating sprite...')
|
|
8
|
+
|
|
9
|
+
const builder = await createBuilder(root, 'production')
|
|
10
|
+
await builder.runTask('sprite')
|
|
11
|
+
|
|
12
|
+
logger.success('pugkit', 'sprite generated')
|
|
13
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export const defaultConfig = {
|
|
2
|
+
siteUrl: '',
|
|
3
|
+
subdir: '',
|
|
4
|
+
debug: false,
|
|
5
|
+
server: {
|
|
6
|
+
port: 5555,
|
|
7
|
+
host: 'localhost',
|
|
8
|
+
startPath: '/',
|
|
9
|
+
open: false
|
|
10
|
+
},
|
|
11
|
+
build: {
|
|
12
|
+
imageOptimization: 'webp',
|
|
13
|
+
imageOptions: {
|
|
14
|
+
webp: {
|
|
15
|
+
quality: 90,
|
|
16
|
+
effort: 6,
|
|
17
|
+
smartSubsample: true,
|
|
18
|
+
method: 6,
|
|
19
|
+
reductionEffort: 6,
|
|
20
|
+
alphaQuality: 100,
|
|
21
|
+
lossless: false
|
|
22
|
+
},
|
|
23
|
+
jpeg: {
|
|
24
|
+
quality: 75,
|
|
25
|
+
progressive: true,
|
|
26
|
+
mozjpeg: false
|
|
27
|
+
},
|
|
28
|
+
png: {
|
|
29
|
+
quality: 85,
|
|
30
|
+
compressionLevel: 6,
|
|
31
|
+
adaptiveFiltering: true,
|
|
32
|
+
palette: true
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
package/config/index.mjs
ADDED
package/config/main.mjs
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { resolve } from 'node:path'
|
|
2
|
+
import { existsSync } from 'node:fs'
|
|
3
|
+
import { defaultConfig } from './defaults.mjs'
|
|
4
|
+
|
|
5
|
+
async function loadUserConfig(root) {
|
|
6
|
+
const configPath = resolve(root, 'pugkit.config.mjs')
|
|
7
|
+
|
|
8
|
+
if (!existsSync(configPath)) return {}
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
const module = await import(configPath)
|
|
12
|
+
return module.default || {}
|
|
13
|
+
} catch (error) {
|
|
14
|
+
console.warn(`Failed to load pugkit.config.mjs: ${error.message}`)
|
|
15
|
+
return {}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function mergeConfig(defaults, user) {
|
|
20
|
+
return {
|
|
21
|
+
siteUrl: user.siteUrl || defaults.siteUrl,
|
|
22
|
+
subdir: user.subdir || defaults.subdir,
|
|
23
|
+
debug: user.debug !== undefined ? user.debug : defaults.debug,
|
|
24
|
+
server: { ...defaults.server, ...(user.server || {}) },
|
|
25
|
+
build: {
|
|
26
|
+
...defaults.build,
|
|
27
|
+
...(user.build || {}),
|
|
28
|
+
imageOptions: {
|
|
29
|
+
webp: { ...defaults.build.imageOptions.webp, ...(user.build?.imageOptions?.webp || {}) },
|
|
30
|
+
jpeg: { ...defaults.build.imageOptions.jpeg, ...(user.build?.imageOptions?.jpeg || {}) },
|
|
31
|
+
png: { ...defaults.build.imageOptions.png, ...(user.build?.imageOptions?.png || {}) }
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function loadConfig(root = process.cwd()) {
|
|
38
|
+
const userConfig = await loadUserConfig(root)
|
|
39
|
+
const config = mergeConfig(defaultConfig, userConfig)
|
|
40
|
+
config.root = root
|
|
41
|
+
return config
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function resolveConfig(inlineConfig = {}) {
|
|
45
|
+
const root = inlineConfig.root || process.cwd()
|
|
46
|
+
const config = await loadConfig(root)
|
|
47
|
+
|
|
48
|
+
if (inlineConfig.server) {
|
|
49
|
+
Object.assign(config.server, inlineConfig.server)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return config
|
|
53
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export function createGlobPatterns(srcPath) {
|
|
2
|
+
return {
|
|
3
|
+
pug: {
|
|
4
|
+
src: [`${srcPath}/**/[^_]*.pug`, `!${srcPath}/_*/**/*.pug`]
|
|
5
|
+
},
|
|
6
|
+
sass: {
|
|
7
|
+
src: [`${srcPath}/**/[^_]*.scss`, `!${srcPath}/**/_*.scss`]
|
|
8
|
+
},
|
|
9
|
+
script: {
|
|
10
|
+
src: './**/[^_]*.{ts,js}',
|
|
11
|
+
ignore: ['**/*.d.ts', '**/node_modules/**']
|
|
12
|
+
},
|
|
13
|
+
images: {
|
|
14
|
+
optimize: [
|
|
15
|
+
`${srcPath}/**/*.{png,jpg,jpeg}`,
|
|
16
|
+
`!${srcPath}/**/sprites_*/*`,
|
|
17
|
+
`!${srcPath}/**/_inline*/*`,
|
|
18
|
+
`!${srcPath}/**/icons*/*`
|
|
19
|
+
],
|
|
20
|
+
webp: {
|
|
21
|
+
src: [`${srcPath}/**/*.{png,jpg,jpeg,gif}`],
|
|
22
|
+
ignore: [`!${srcPath}/**/favicons/*`, `!${srcPath}/**/ogp.{png,jpg}`]
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
svg: {
|
|
26
|
+
src: [`${srcPath}/**/*.svg`],
|
|
27
|
+
ignore: [`!${srcPath}/**/sprites_*/*`, `!${srcPath}/**/_inline*/*`, `!${srcPath}/**/icons*/*`]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
package/core/builder.mjs
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { BuildContext } from './context.mjs'
|
|
2
|
+
import { logger } from '../utils/logger.mjs'
|
|
3
|
+
import { cleanDir } from '../utils/file.mjs'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* メインビルダー
|
|
7
|
+
*/
|
|
8
|
+
export class Builder {
|
|
9
|
+
constructor(config, mode = 'development') {
|
|
10
|
+
this.context = new BuildContext(config, mode)
|
|
11
|
+
this.tasks = {}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* タスクを登録
|
|
16
|
+
*/
|
|
17
|
+
registerTask(name, fn) {
|
|
18
|
+
this.tasks[name] = fn
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 複数タスクを登録
|
|
23
|
+
*/
|
|
24
|
+
registerTasks(tasks) {
|
|
25
|
+
Object.entries(tasks).forEach(([name, fn]) => {
|
|
26
|
+
this.registerTask(name, fn)
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 本番ビルド
|
|
32
|
+
*/
|
|
33
|
+
async build() {
|
|
34
|
+
const { context } = this
|
|
35
|
+
const startTime = Date.now()
|
|
36
|
+
|
|
37
|
+
logger.info('build', `Building in ${context.mode} mode`)
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
// 1. クリーンアップ
|
|
41
|
+
await this.clean()
|
|
42
|
+
|
|
43
|
+
// 2. 並列ビルド(軽量タスク + スプライト)
|
|
44
|
+
const parallelTasks = []
|
|
45
|
+
|
|
46
|
+
if (this.tasks.sass) {
|
|
47
|
+
parallelTasks.push({ name: 'sass', fn: this.tasks.sass })
|
|
48
|
+
}
|
|
49
|
+
if (this.tasks.script) {
|
|
50
|
+
parallelTasks.push({ name: 'script', fn: this.tasks.script })
|
|
51
|
+
}
|
|
52
|
+
if (this.tasks.sprite) {
|
|
53
|
+
parallelTasks.push({ name: 'sprite', fn: this.tasks.sprite })
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (parallelTasks.length > 0) {
|
|
57
|
+
await context.runParallel(parallelTasks)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 3. Pug(Sass/Scriptの出力を参照するため後)
|
|
61
|
+
if (this.tasks.pug) {
|
|
62
|
+
await context.runTask('pug', this.tasks.pug)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// 4. 最終処理(並列)
|
|
66
|
+
const finalTasks = []
|
|
67
|
+
|
|
68
|
+
if (this.tasks.image) {
|
|
69
|
+
finalTasks.push({ name: 'image', fn: this.tasks.image })
|
|
70
|
+
}
|
|
71
|
+
if (this.tasks.svg) {
|
|
72
|
+
finalTasks.push({ name: 'svg', fn: this.tasks.svg })
|
|
73
|
+
}
|
|
74
|
+
if (this.tasks.copy) {
|
|
75
|
+
finalTasks.push({ name: 'copy', fn: this.tasks.copy })
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (finalTasks.length > 0) {
|
|
79
|
+
await context.runParallel(finalTasks)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const elapsed = Date.now() - startTime
|
|
83
|
+
logger.success('build', `Completed in ${elapsed}ms`)
|
|
84
|
+
} catch (error) {
|
|
85
|
+
logger.error('build', error.message)
|
|
86
|
+
throw error
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* 監視モード(開発)
|
|
92
|
+
*/
|
|
93
|
+
async watch() {
|
|
94
|
+
const { context } = this
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
// ファイル監視開始
|
|
98
|
+
if (this.tasks.watch) {
|
|
99
|
+
await context.runTask('watch', this.tasks.watch)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 開発サーバー起動
|
|
103
|
+
if (this.tasks.server) {
|
|
104
|
+
await context.runTask('server', this.tasks.server)
|
|
105
|
+
}
|
|
106
|
+
} catch (error) {
|
|
107
|
+
logger.error('watch', error.message)
|
|
108
|
+
throw error
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* 個別タスク実行
|
|
114
|
+
*/
|
|
115
|
+
async runTask(taskName, options = {}) {
|
|
116
|
+
const task = this.tasks[taskName]
|
|
117
|
+
|
|
118
|
+
if (!task) {
|
|
119
|
+
throw new Error(`Task not found: ${taskName}`)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
await this.context.runTask(taskName, task, options)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* クリーンアップ
|
|
127
|
+
*/
|
|
128
|
+
async clean() {
|
|
129
|
+
const distPath = this.context.paths.dist
|
|
130
|
+
logger.info('clean', 'Cleaning dist directory')
|
|
131
|
+
|
|
132
|
+
await cleanDir(distPath)
|
|
133
|
+
|
|
134
|
+
this.context.cache.clear()
|
|
135
|
+
this.context.graph.clear()
|
|
136
|
+
|
|
137
|
+
logger.success('clean', 'Completed')
|
|
138
|
+
}
|
|
139
|
+
}
|
package/core/cache.mjs
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
import { stat, readFile } from 'node:fs/promises'
|
|
3
|
+
import { existsSync } from 'node:fs'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 統合キャッシュマネージャー
|
|
7
|
+
*/
|
|
8
|
+
export class CacheManager {
|
|
9
|
+
constructor(mode) {
|
|
10
|
+
this.mode = mode
|
|
11
|
+
this.fileHashes = new Map() // ファイルパス -> ハッシュ
|
|
12
|
+
this.compiledCache = new Map() // Pugコンパイル済みテンプレート
|
|
13
|
+
this.isDevelopment = mode === 'development'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* ファイルが変更されたかチェック
|
|
18
|
+
*/
|
|
19
|
+
async isFileChanged(filePath) {
|
|
20
|
+
if (!existsSync(filePath)) {
|
|
21
|
+
return false
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const currentHash = await this.computeHash(filePath)
|
|
25
|
+
const cachedHash = this.fileHashes.get(filePath)
|
|
26
|
+
|
|
27
|
+
// キャッシュが存在しない場合は変更ありとして扱う
|
|
28
|
+
if (cachedHash === undefined) {
|
|
29
|
+
this.fileHashes.set(filePath, currentHash)
|
|
30
|
+
return true
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (cachedHash === currentHash) {
|
|
34
|
+
return false
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
this.fileHashes.set(filePath, currentHash)
|
|
38
|
+
return true
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 複数ファイルの変更をバッチチェック
|
|
43
|
+
*/
|
|
44
|
+
async getChangedFiles(filePaths) {
|
|
45
|
+
const checks = await Promise.all(
|
|
46
|
+
filePaths.map(async path => ({
|
|
47
|
+
path,
|
|
48
|
+
changed: await this.isFileChanged(path)
|
|
49
|
+
}))
|
|
50
|
+
)
|
|
51
|
+
return checks.filter(c => c.changed).map(c => c.path)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Pugテンプレートのキャッシュ取得
|
|
56
|
+
*/
|
|
57
|
+
getPugTemplate(filePath) {
|
|
58
|
+
return this.compiledCache.get(filePath)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Pugテンプレートをキャッシュに保存
|
|
63
|
+
*/
|
|
64
|
+
setPugTemplate(filePath, template) {
|
|
65
|
+
if (this.isDevelopment) {
|
|
66
|
+
this.compiledCache.set(filePath, template)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Pugテンプレートのキャッシュを無効化
|
|
72
|
+
*/
|
|
73
|
+
invalidatePugTemplate(filePath) {
|
|
74
|
+
this.compiledCache.delete(filePath)
|
|
75
|
+
this.fileHashes.delete(filePath)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* ハッシュ計算(ファイル内容 + 更新日時)
|
|
80
|
+
*/
|
|
81
|
+
async computeHash(filePath) {
|
|
82
|
+
try {
|
|
83
|
+
const [stats, content] = await Promise.all([stat(filePath), readFile(filePath)])
|
|
84
|
+
|
|
85
|
+
return createHash('md5').update(content).update(stats.mtime.toISOString()).digest('hex')
|
|
86
|
+
} catch (error) {
|
|
87
|
+
return null
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* すべてのキャッシュをクリア
|
|
93
|
+
*/
|
|
94
|
+
clear() {
|
|
95
|
+
this.fileHashes.clear()
|
|
96
|
+
this.compiledCache.clear()
|
|
97
|
+
}
|
|
98
|
+
}
|