pugkit 1.0.1 → 1.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/README.md +122 -93
- package/cli/develop.mjs +1 -2
- package/cli/logger.mjs +5 -9
- package/config/defaults.mjs +1 -2
- package/core/cache.mjs +6 -7
- package/core/context.mjs +1 -6
- package/core/server.mjs +7 -5
- package/core/watcher.mjs +184 -235
- package/generate/asset.mjs +2 -0
- package/generate/page.mjs +1 -1
- package/package.json +1 -1
- package/tasks/copy.mjs +15 -0
- package/tasks/pug.mjs +2 -4
- package/tasks/sass.mjs +1 -1
- package/tasks/script.mjs +3 -3
- package/tasks/svg-sprite.mjs +0 -4
- package/tasks/svg.mjs +1 -7
- package/utils/file.mjs +2 -7
package/README.md
CHANGED
|
@@ -1,55 +1,55 @@
|
|
|
1
1
|
# pugkit
|
|
2
2
|
|
|
3
|
+
<p>
|
|
4
|
+
<a aria-label="NPM version" href="https://www.npmjs.com/package/pugkit">
|
|
5
|
+
<img alt="" src="https://img.shields.io/npm/v/pugkit.svg?style=for-the-badge&labelColor=212121">
|
|
6
|
+
</a>
|
|
7
|
+
<a aria-label="License" href="https://github.com/mfxgu2i/pugkit/blob/main/LICENSE">
|
|
8
|
+
<img alt="" src="https://img.shields.io/npm/l/pugkit.svg?style=for-the-badge&labelColor=212121">
|
|
9
|
+
</a>
|
|
10
|
+
</p>
|
|
11
|
+
|
|
3
12
|
## About
|
|
4
13
|
|
|
5
14
|
pugkitは静的サイト制作に特化したビルドツールです。
|
|
6
15
|
納品向きの綺麗なHTMLと、ファイル構成に制約のないアセットファイルを出力可能です。
|
|
7
16
|
|
|
8
|
-
##
|
|
9
|
-
|
|
10
|
-
```bash
|
|
11
|
-
npm install pugkit
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
## Usage
|
|
15
|
-
|
|
16
|
-
### Development Mode
|
|
17
|
-
|
|
18
|
-
ファイルの変更を監視し、ブラウザの自動リロードを行います。
|
|
17
|
+
## How To Use
|
|
19
18
|
|
|
20
|
-
```
|
|
21
|
-
pugkit
|
|
22
|
-
|
|
23
|
-
pugkit dev
|
|
24
|
-
# or
|
|
25
|
-
pugkit watch
|
|
19
|
+
```sh
|
|
20
|
+
$ npm install --save-dev pugkit
|
|
21
|
+
$ touch ./src/index.pug
|
|
26
22
|
```
|
|
27
23
|
|
|
28
|
-
|
|
24
|
+
`package.json` にスクリプトを追加します。
|
|
29
25
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
pugkit build
|
|
26
|
+
```json
|
|
27
|
+
"scripts": {
|
|
28
|
+
"start": "pugkit",
|
|
29
|
+
"build": "pugkit build",
|
|
30
|
+
"sprite": "pugkit sprite"
|
|
31
|
+
}
|
|
34
32
|
```
|
|
35
33
|
|
|
36
|
-
|
|
34
|
+
## Commands
|
|
37
35
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
pugkit
|
|
42
|
-
|
|
36
|
+
| コマンド | 内容 |
|
|
37
|
+
| --------------- | ----------------------------- |
|
|
38
|
+
| `pugkit` | 開発モード(Ctrl + C で停止) |
|
|
39
|
+
| `pugkit build` | 本番ビルド |
|
|
40
|
+
| `pugkit sprite` | SVGスプライト生成 |
|
|
43
41
|
|
|
44
42
|
## Configuration
|
|
45
43
|
|
|
46
|
-
|
|
44
|
+
プロジェクトルートに`pugkit.config.mjs`を配置することで、ビルド設定をカスタマイズできます。
|
|
47
45
|
|
|
48
46
|
```js
|
|
49
47
|
// pugkit.config.mjs
|
|
50
|
-
|
|
48
|
+
import { defineConfig } from 'pugkit'
|
|
49
|
+
|
|
50
|
+
export default defineConfig({
|
|
51
51
|
siteUrl: 'https://example.com/',
|
|
52
|
-
subdir: '
|
|
52
|
+
subdir: '',
|
|
53
53
|
debug: false,
|
|
54
54
|
server: {
|
|
55
55
|
port: 5555,
|
|
@@ -57,42 +57,30 @@ export default {
|
|
|
57
57
|
startPath: '/'
|
|
58
58
|
},
|
|
59
59
|
build: {
|
|
60
|
-
imageOptimization: 'webp'
|
|
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
|
-
}
|
|
60
|
+
imageOptimization: 'webp'
|
|
75
61
|
}
|
|
76
|
-
}
|
|
62
|
+
})
|
|
77
63
|
```
|
|
78
64
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
|
82
|
-
|
|
|
83
|
-
| `debug` | デバッグモード(開発時のみ有効) | `false` |
|
|
84
|
-
| `server.port` | 開発サーバーのポート番号
|
|
85
|
-
| `server.host` | 開発サーバーのホスト
|
|
86
|
-
| `server.startPath` | サーバー起動時に開くパス
|
|
87
|
-
| `server.open` | サーバー起動時にブラウザを開く
|
|
88
|
-
| `build.imageOptimization` | 画像最適化の方式
|
|
89
|
-
| `build.imageOptions`
|
|
65
|
+
| Option | Description | Type / Values | Default |
|
|
66
|
+
| ------------------------- | ------------------------------------------------------------------------------------------- | ----------------------------------- | ------------- |
|
|
67
|
+
| `siteUrl` | サイトのベースURL(`Builder.url` に使用) | `string` | `''` |
|
|
68
|
+
| `subdir` | サブディレクトリのパス | `string` | `''` |
|
|
69
|
+
| `debug` | デバッグモード(開発時のみ有効) | `boolean` | `false` |
|
|
70
|
+
| `server.port` | 開発サーバーのポート番号 | `number` | `5555` |
|
|
71
|
+
| `server.host` | 開発サーバーのホスト | `string` | `'localhost'` |
|
|
72
|
+
| `server.startPath` | サーバー起動時に開くパス | `string` | `'/'` |
|
|
73
|
+
| `server.open` | サーバー起動時にブラウザを開く | `boolean` | `false` |
|
|
74
|
+
| `build.imageOptimization` | 画像最適化の方式 | `'webp'` \| `'compress'` \| `false` | `'webp'` |
|
|
75
|
+
| `build.imageOptions.webp` | WebP変換オプション([Sharp WebP options](https://sharp.pixelplumbing.com/api-output#webp)) | `object` | - |
|
|
76
|
+
| `build.imageOptions.jpeg` | JPEG圧縮オプション([Sharp JPEG options](https://sharp.pixelplumbing.com/api-output#jpeg)) | `object` | - |
|
|
77
|
+
| `build.imageOptions.png` | PNG圧縮オプション([Sharp PNG options](https://sharp.pixelplumbing.com/api-output#png)) | `object` | - |
|
|
90
78
|
|
|
91
79
|
## Features
|
|
92
80
|
|
|
93
81
|
### Pug Templates
|
|
94
82
|
|
|
95
|
-
Pug
|
|
83
|
+
Pugテンプレート内では `Builder` オブジェクトと `imageInfo()` 関数が使用できます。
|
|
96
84
|
|
|
97
85
|
#### Builder Object
|
|
98
86
|
|
|
@@ -122,8 +110,6 @@ meta(property='og:url', content=Builder.url.href)
|
|
|
122
110
|
img(src=info.src width=info.width height=info.height alt='')
|
|
123
111
|
```
|
|
124
112
|
|
|
125
|
-
**返り値**
|
|
126
|
-
|
|
127
113
|
| Property | Type | Description |
|
|
128
114
|
| -------- | -------------------------------- | ----------------------------------------------------- |
|
|
129
115
|
| `src` | `string` | 最適化設定に応じたパス(webpモード時は `.webp` パス) |
|
|
@@ -134,53 +120,102 @@ img(src=info.src width=info.width height=info.height alt='')
|
|
|
134
120
|
| `retina` | `{ src: string } \| null` | `@2x` 画像が存在する場合に自動検出 |
|
|
135
121
|
| `sp` | `{ src, width, height } \| null` | `_sp` 画像が存在する場合に自動検出 |
|
|
136
122
|
|
|
137
|
-
>
|
|
123
|
+
> `imageInfo()` は `src/` 配下の画像のみ対応しています。`public/` 配下の画像は非対応です。
|
|
138
124
|
|
|
139
|
-
###
|
|
125
|
+
### Sass
|
|
126
|
+
|
|
127
|
+
`src/` 配下の `.scss` ファイルをコンパイルして出力します。ベンダープレフィックスの自動付与と圧縮も行われます。
|
|
128
|
+
|
|
129
|
+
> ブラウザターゲットを指定する場合は、プロジェクトルートに `.browserslistrc` を配置してください。
|
|
140
130
|
|
|
141
|
-
|
|
131
|
+
### JavaScript / TypeScript
|
|
142
132
|
|
|
143
|
-
|
|
133
|
+
`src/` 配下の `.js` / `.ts` ファイルをバンドルして出力します。
|
|
134
|
+
|
|
135
|
+
esbuild がTypeScriptをネイティブ処理するため、`tsconfig.json` は不要です。ただし型チェックは行わずトランスパイルのみ行います。
|
|
136
|
+
|
|
137
|
+
#### TypeScript 型チェックを追加する(オプション)
|
|
138
|
+
|
|
139
|
+
型チェックが必要な場合は`typescript`を追加し、`tsc --noEmit`を組み合わせて使用します。
|
|
140
|
+
|
|
141
|
+
```sh
|
|
142
|
+
npm install --save-dev typescript
|
|
143
|
+
```
|
|
144
144
|
|
|
145
|
+
`tsconfig.json`をプロジェクトルートに作成します。
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
{
|
|
149
|
+
"compilerOptions": {
|
|
150
|
+
"target": "ES2022",
|
|
151
|
+
"module": "ESNext",
|
|
152
|
+
"moduleResolution": "bundler",
|
|
153
|
+
"strict": true,
|
|
154
|
+
"noEmit": true,
|
|
155
|
+
"skipLibCheck": true
|
|
156
|
+
},
|
|
157
|
+
"include": ["src/**/*.ts"]
|
|
158
|
+
}
|
|
145
159
|
```
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
160
|
+
|
|
161
|
+
`package.json` にスクリプトを追加します。
|
|
162
|
+
|
|
163
|
+
```json
|
|
164
|
+
"scripts": {
|
|
165
|
+
"start": "pugkit",
|
|
166
|
+
"build": "tsc --noEmit && pugkit build",
|
|
167
|
+
"sprite": "pugkit sprite",
|
|
168
|
+
}
|
|
151
169
|
```
|
|
152
170
|
|
|
153
|
-
|
|
171
|
+
### Image Optimization
|
|
172
|
+
|
|
173
|
+
ビルド時に `src/` 配下の画像(JPEG・PNG)を自動的に最適化します。
|
|
174
|
+
|
|
175
|
+
- `'webp'` - PNG/JPEGをWebPに変換
|
|
176
|
+
- `'compress'` - 元の形式を維持したまま圧縮
|
|
177
|
+
- `false` - 最適化を無効化
|
|
178
|
+
|
|
179
|
+
### SVG Sprite
|
|
180
|
+
|
|
181
|
+
`src/` 配下の `icons/` ディレクトリに配置したSVGを1つのスプライトファイルにまとめます。
|
|
154
182
|
|
|
155
183
|
```
|
|
156
|
-
dist/
|
|
157
|
-
└── assets/
|
|
158
|
-
└── icons.svg # 自動生成されるスプライト
|
|
184
|
+
src/assets/icons/arrow.svg → dist/assets/icons.svg#arrow
|
|
159
185
|
```
|
|
160
186
|
|
|
161
|
-
#### 使い方
|
|
162
|
-
|
|
163
187
|
```html
|
|
164
188
|
<svg><use href="assets/icons.svg#arrow"></use></svg>
|
|
165
189
|
```
|
|
166
190
|
|
|
167
|
-
- `icons/` ディレクトリは `src/` 配下の任意の場所に配置できます
|
|
168
191
|
- SVG ファイル名がそのまま `<symbol id>` になります
|
|
169
|
-
- `fill` / `stroke` は自動的に `currentColor`
|
|
192
|
+
- `fill` / `stroke` は自動的に `currentColor` に変換されます
|
|
170
193
|
|
|
171
|
-
###
|
|
194
|
+
### SVG Optimization
|
|
195
|
+
|
|
196
|
+
`icons/` 以外に配置した SVG ファイルは SVGO で自動最適化されて出力されます。
|
|
197
|
+
|
|
198
|
+
### Public Directory
|
|
199
|
+
|
|
200
|
+
`public/` に置いたファイルはそのまま `dist/` のルートにコピーされます。faviconやOGP画像など最適化不要なファイルの置き場として使用します。
|
|
201
|
+
|
|
202
|
+
### Debug Mode
|
|
172
203
|
|
|
173
|
-
|
|
204
|
+
`debug: true` のとき、開発モードでのみ以下の出力に切り替わります。
|
|
174
205
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
206
|
+
| 対象 | 通常 | debug: true |
|
|
207
|
+
| ---- | ---------------------------- | ------------------------------ |
|
|
208
|
+
| CSS | minify済み | expanded + ソースマップ |
|
|
209
|
+
| JS | minify済み・`console.*` 削除 | ソースマップ・`console.*` 保持 |
|
|
178
210
|
|
|
179
211
|
### File Naming Rules
|
|
180
212
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
213
|
+
`_`(アンダースコア)で始まるファイル・ディレクトリはビルド対象外です。それ以外のファイルは `src/` 配下のディレクトリ構成を維持したまま `dist/` に出力されます。
|
|
214
|
+
|
|
215
|
+
```
|
|
216
|
+
src/foo/style.scss → dist/foo/style.css
|
|
217
|
+
src/foo/bar/script.js → dist/foo/bar/script.js
|
|
218
|
+
```
|
|
184
219
|
|
|
185
220
|
## Directory Structure
|
|
186
221
|
|
|
@@ -210,10 +245,4 @@ project-root/
|
|
|
210
245
|
- [Sharp](https://sharp.pixelplumbing.com/) - 画像最適化
|
|
211
246
|
- [SVGO](https://svgo.dev/) - SVG最適化
|
|
212
247
|
- [Chokidar](https://github.com/paulmillr/chokidar) - ファイル監視
|
|
213
|
-
- [sirv](https://github.com/lukeed/sirv) - 開発サーバー
|
|
214
|
-
- SSE(Server-Sent Events) - ライブリロード
|
|
215
|
-
- [Prettier](https://prettier.io/) - HTML整形
|
|
216
|
-
|
|
217
|
-
## License
|
|
218
|
-
|
|
219
|
-
MIT
|
|
248
|
+
- [sirv](https://github.com/lukeed/sirv) + SSE(Server-Sent Events) - 開発サーバー
|
package/cli/develop.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { createBuilder } from '../index.mjs'
|
|
|
2
2
|
import { logger } from './logger.mjs'
|
|
3
3
|
|
|
4
4
|
export async function develop(options = {}) {
|
|
5
|
-
const { root = process.cwd(), port, host
|
|
5
|
+
const { root = process.cwd(), port, host } = options
|
|
6
6
|
|
|
7
7
|
logger.info('pugkit', 'starting dev server...')
|
|
8
8
|
|
|
@@ -10,7 +10,6 @@ export async function develop(options = {}) {
|
|
|
10
10
|
|
|
11
11
|
if (port) builder.context.config.server.port = port
|
|
12
12
|
if (host) builder.context.config.server.host = host
|
|
13
|
-
if (open) builder.context.config.server.open = open
|
|
14
13
|
|
|
15
14
|
await builder.watch()
|
|
16
15
|
}
|
package/cli/logger.mjs
CHANGED
|
@@ -6,26 +6,22 @@ import path from 'node:path'
|
|
|
6
6
|
const __filename = fileURLToPath(import.meta.url)
|
|
7
7
|
const __dirname = path.dirname(__filename)
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
const pkgPath = path.join(__dirname, '../package.json')
|
|
11
|
-
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
|
|
12
|
-
return pkg.version
|
|
13
|
-
}
|
|
9
|
+
const version = JSON.parse(readFileSync(path.join(__dirname, '../package.json'), 'utf8')).version
|
|
14
10
|
|
|
15
11
|
export const logger = {
|
|
16
12
|
info(name, message) {
|
|
17
|
-
console.log(`${pc.cyan(pc.bold(name.toUpperCase()))} ${pc.dim(`v${
|
|
13
|
+
console.log(`${pc.cyan(pc.bold(name.toUpperCase()))} ${pc.dim(`v${version}`)} ${message}`)
|
|
18
14
|
},
|
|
19
15
|
|
|
20
16
|
success(name, message) {
|
|
21
|
-
console.log(`${pc.cyan(pc.bold(name.toUpperCase()))} ${pc.dim(`v${
|
|
17
|
+
console.log(`${pc.cyan(pc.bold(name.toUpperCase()))} ${pc.dim(`v${version}`)} ${message}`)
|
|
22
18
|
},
|
|
23
19
|
|
|
24
20
|
warn(name, message) {
|
|
25
|
-
console.log(`${pc.cyan(pc.bold(name.toUpperCase()))} ${pc.dim(`v${
|
|
21
|
+
console.log(`${pc.cyan(pc.bold(name.toUpperCase()))} ${pc.dim(`v${version}`)} ${message}`)
|
|
26
22
|
},
|
|
27
23
|
|
|
28
24
|
error(name, message) {
|
|
29
|
-
console.error(`${pc.cyan(pc.bold(name.toUpperCase()))} ${pc.dim(`v${
|
|
25
|
+
console.error(`${pc.cyan(pc.bold(name.toUpperCase()))} ${pc.dim(`v${version}`)} ${message}`)
|
|
30
26
|
}
|
|
31
27
|
}
|
package/config/defaults.mjs
CHANGED
package/core/cache.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto'
|
|
2
|
-
import {
|
|
2
|
+
import { readFile } from 'node:fs/promises'
|
|
3
3
|
import { existsSync } from 'node:fs'
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -76,15 +76,14 @@ export class CacheManager {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
/**
|
|
79
|
-
*
|
|
79
|
+
* ハッシュ計算(ファイル内容のみ)
|
|
80
80
|
*/
|
|
81
81
|
async computeHash(filePath) {
|
|
82
82
|
try {
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return null
|
|
83
|
+
const content = await readFile(filePath)
|
|
84
|
+
return createHash('md5').update(content).digest('hex')
|
|
85
|
+
} catch {
|
|
86
|
+
return `error-${Math.random()}`
|
|
88
87
|
}
|
|
89
88
|
}
|
|
90
89
|
|
package/core/context.mjs
CHANGED
|
@@ -23,12 +23,7 @@ export class BuildContext {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
async runTask(taskName, taskFn, options = {}) {
|
|
26
|
-
|
|
27
|
-
await taskFn(this, options)
|
|
28
|
-
} catch (error) {
|
|
29
|
-
console.error(`Task failed: ${taskName}`)
|
|
30
|
-
throw error
|
|
31
|
-
}
|
|
26
|
+
await taskFn(this, options)
|
|
32
27
|
}
|
|
33
28
|
|
|
34
29
|
async runParallel(tasks) {
|
package/core/server.mjs
CHANGED
|
@@ -44,15 +44,17 @@ export async function serverTask(context, options = {}) {
|
|
|
44
44
|
await mkdir(paths.dist, { recursive: true })
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
const port = config.server?.port ??
|
|
47
|
+
const port = config.server?.port ?? 5555
|
|
48
48
|
const host = config.server?.host ?? 'localhost'
|
|
49
49
|
const subdir = config.subdir ? '/' + config.subdir.replace(/^\/|\/$/g, '') : ''
|
|
50
50
|
const startPath = (config.server?.startPath || '/').replace(/^\//, '')
|
|
51
51
|
const fullStartPath = subdir ? `${subdir}/${startPath}` : `/${startPath}`
|
|
52
52
|
|
|
53
|
+
const serveRoot = path.resolve(config.root, 'dist')
|
|
54
|
+
|
|
53
55
|
const clients = new Set()
|
|
54
56
|
|
|
55
|
-
const staticServe = sirv(
|
|
57
|
+
const staticServe = sirv(serveRoot, {
|
|
56
58
|
dev: true,
|
|
57
59
|
extensions: ['html'],
|
|
58
60
|
setHeaders(res, filePath) {
|
|
@@ -86,9 +88,9 @@ export async function serverTask(context, options = {}) {
|
|
|
86
88
|
// ── HTML へのライブリロードスクリプト注入 ───────────
|
|
87
89
|
const decoded = decodeURIComponent(urlPath)
|
|
88
90
|
const candidates = [
|
|
89
|
-
path.join(
|
|
90
|
-
path.join(
|
|
91
|
-
path.join(
|
|
91
|
+
path.join(serveRoot, decoded === '/' ? 'index.html' : decoded.replace(/\/$/, '') + '/index.html'),
|
|
92
|
+
path.join(serveRoot, decoded === '/' ? 'index.html' : decoded + '.html'),
|
|
93
|
+
path.join(serveRoot, decoded)
|
|
92
94
|
]
|
|
93
95
|
const htmlFile = candidates.find(p => p.endsWith('.html') && existsSync(p))
|
|
94
96
|
|
package/core/watcher.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import chokidar from 'chokidar'
|
|
2
|
-
import {
|
|
2
|
+
import { rm } from 'node:fs/promises'
|
|
3
|
+
import { relative, resolve, basename, extname } from 'node:path'
|
|
3
4
|
import { logger } from '../utils/logger.mjs'
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -16,7 +17,7 @@ export async function watcherTask(context, options = {}) {
|
|
|
16
17
|
class FileWatcher {
|
|
17
18
|
constructor(context) {
|
|
18
19
|
this.context = context
|
|
19
|
-
this.
|
|
20
|
+
this.watcher = null
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
async start() {
|
|
@@ -28,264 +29,208 @@ class FileWatcher {
|
|
|
28
29
|
await this.context.taskRegistry.pug(this.context)
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
this.watcher = chokidar
|
|
33
|
+
.watch([paths.src, paths.public], {
|
|
34
|
+
ignoreInitial: true,
|
|
35
|
+
ignored: [/(^|[\/\\])\./, /node_modules/, /\.git/],
|
|
36
|
+
persistent: true,
|
|
37
|
+
awaitWriteFinish: { stabilityThreshold: 100, pollInterval: 50 }
|
|
38
|
+
})
|
|
39
|
+
.on('change', filePath => this.handleChange(filePath))
|
|
40
|
+
.on('add', filePath => this.handleAdd(filePath))
|
|
41
|
+
.on('unlink', filePath => this.handleUnlink(filePath))
|
|
33
42
|
|
|
34
|
-
|
|
35
|
-
|
|
43
|
+
logger.info('watch', 'File watching started')
|
|
44
|
+
}
|
|
36
45
|
|
|
37
|
-
|
|
38
|
-
|
|
46
|
+
// ---- ルーティング ----
|
|
47
|
+
handleChange(filePath) {
|
|
48
|
+
if (this.isPublic(filePath)) return this.onPublicChange(filePath, 'change')
|
|
49
|
+
if (filePath.endsWith('.pug')) return this.onPugChange(filePath)
|
|
50
|
+
if (filePath.endsWith('.scss')) return this.onSassChange(filePath)
|
|
51
|
+
if (this.isScript(filePath)) return this.onScriptChange(filePath)
|
|
52
|
+
if (filePath.endsWith('.svg') && !this.isIcons(filePath)) return this.onSvgChange(filePath, 'change')
|
|
53
|
+
if (/\.(jpg|jpeg|png|gif)$/i.test(filePath)) return this.onImageChange(filePath, 'change')
|
|
54
|
+
}
|
|
39
55
|
|
|
40
|
-
|
|
41
|
-
this.
|
|
56
|
+
handleAdd(filePath) {
|
|
57
|
+
if (this.isPublic(filePath)) return this.onPublicChange(filePath, 'add')
|
|
58
|
+
if (filePath.endsWith('.svg') && !this.isIcons(filePath)) return this.onSvgChange(filePath, 'add')
|
|
59
|
+
if (/\.(jpg|jpeg|png|gif)$/i.test(filePath)) return this.onImageChange(filePath, 'add')
|
|
60
|
+
}
|
|
42
61
|
|
|
43
|
-
|
|
44
|
-
this.
|
|
62
|
+
handleUnlink(filePath) {
|
|
63
|
+
if (this.isPublic(filePath)) return this.onPublicUnlink(filePath)
|
|
64
|
+
if (filePath.endsWith('.pug')) return this.onPugUnlink(filePath)
|
|
65
|
+
if (filePath.endsWith('.scss')) return this.onSassUnlink(filePath)
|
|
66
|
+
if (this.isScript(filePath)) return this.onScriptUnlink(filePath)
|
|
67
|
+
if (filePath.endsWith('.svg') && !this.isIcons(filePath)) return this.onSvgUnlink(filePath)
|
|
68
|
+
if (/\.(jpg|jpeg|png|gif)$/i.test(filePath)) return this.onImageUnlink(filePath)
|
|
69
|
+
}
|
|
45
70
|
|
|
46
|
-
|
|
47
|
-
this.watchPublic(paths.public)
|
|
71
|
+
// ---- 判定ヘルパー ----
|
|
48
72
|
|
|
49
|
-
|
|
73
|
+
isPublic(filePath) {
|
|
74
|
+
return filePath.startsWith(this.context.paths.public)
|
|
50
75
|
}
|
|
51
76
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
watchPug(basePath) {
|
|
56
|
-
const watcher = chokidar.watch(basePath, {
|
|
57
|
-
ignoreInitial: true,
|
|
58
|
-
ignored: [
|
|
59
|
-
/(^|[\/\\])\../, // 隠しファイル
|
|
60
|
-
/node_modules/,
|
|
61
|
-
/\.git/
|
|
62
|
-
],
|
|
63
|
-
persistent: true,
|
|
64
|
-
awaitWriteFinish: {
|
|
65
|
-
stabilityThreshold: 100,
|
|
66
|
-
pollInterval: 50
|
|
67
|
-
}
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
watcher.on('change', async path => {
|
|
71
|
-
// .pugファイルのみ処理
|
|
72
|
-
if (!path.endsWith('.pug')) {
|
|
73
|
-
return
|
|
74
|
-
}
|
|
75
|
-
const relPath = relative(basePath, path)
|
|
76
|
-
logger.info('change', `pug: ${relPath}`)
|
|
77
|
-
|
|
78
|
-
try {
|
|
79
|
-
// パーシャルの場合、依存する親ファイルも再ビルド
|
|
80
|
-
const affectedFiles = this.context.graph.getAffectedParents(path)
|
|
81
|
-
|
|
82
|
-
if (affectedFiles.length > 0) {
|
|
83
|
-
logger.info('pug', `Rebuilding ${affectedFiles.length} affected file(s)`)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// キャッシュ無効化
|
|
87
|
-
this.context.cache.invalidatePugTemplate(path)
|
|
88
|
-
affectedFiles.forEach(f => this.context.cache.invalidatePugTemplate(f))
|
|
89
|
-
|
|
90
|
-
// Pugタスクを実行(変更されたファイルのみ)
|
|
91
|
-
if (this.context.taskRegistry?.pug) {
|
|
92
|
-
await this.context.taskRegistry.pug(this.context, {
|
|
93
|
-
files: [path, ...affectedFiles]
|
|
94
|
-
})
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
this.reload()
|
|
98
|
-
} catch (error) {
|
|
99
|
-
logger.error('watch', `Pug build failed: ${error.message}`)
|
|
100
|
-
}
|
|
101
|
-
})
|
|
77
|
+
isScript(filePath) {
|
|
78
|
+
return (filePath.endsWith('.ts') || filePath.endsWith('.js')) && !filePath.endsWith('.d.ts')
|
|
79
|
+
}
|
|
102
80
|
|
|
103
|
-
|
|
81
|
+
isIcons(filePath) {
|
|
82
|
+
// Windows 対応: セパレータを正規化
|
|
83
|
+
return filePath.replace(/\\/g, '/').includes('/icons/')
|
|
104
84
|
}
|
|
105
85
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (!path.endsWith('.scss')) {
|
|
120
|
-
return
|
|
86
|
+
// ---- Pug ----
|
|
87
|
+
|
|
88
|
+
async onPugChange(filePath) {
|
|
89
|
+
const { paths, graph, cache, taskRegistry } = this.context
|
|
90
|
+
const relPath = relative(paths.src, filePath)
|
|
91
|
+
logger.info('change', `pug: ${relPath}`)
|
|
92
|
+
try {
|
|
93
|
+
const affectedFiles = graph.getAffectedParents(filePath)
|
|
94
|
+
if (affectedFiles.length > 0) logger.info('pug', `Rebuilding ${affectedFiles.length} affected file(s)`)
|
|
95
|
+
cache.invalidatePugTemplate(filePath)
|
|
96
|
+
affectedFiles.forEach(f => cache.invalidatePugTemplate(f))
|
|
97
|
+
if (taskRegistry?.pug) {
|
|
98
|
+
await taskRegistry.pug(this.context, { files: [filePath, ...affectedFiles] })
|
|
121
99
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (this.context.taskRegistry?.sass) {
|
|
128
|
-
await this.context.taskRegistry.sass(this.context)
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// CSSはインジェクション
|
|
132
|
-
const cssUrlPath = '/' + relPath.replace(/\\/g, '/').replace(/\.scss$/, '.css')
|
|
133
|
-
this.injectCSS(cssUrlPath)
|
|
134
|
-
} catch (error) {
|
|
135
|
-
logger.error('watch', `Sass build failed: ${error.message}`)
|
|
136
|
-
}
|
|
137
|
-
})
|
|
100
|
+
this.reload()
|
|
101
|
+
} catch (error) {
|
|
102
|
+
logger.error('watch', `Pug build failed: ${error.message}`)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
138
105
|
|
|
139
|
-
|
|
106
|
+
async onPugUnlink(filePath) {
|
|
107
|
+
const { paths, cache, graph } = this.context
|
|
108
|
+
const relPath = relative(paths.src, filePath)
|
|
109
|
+
cache.invalidatePugTemplate(filePath)
|
|
110
|
+
graph.clearDependencies(filePath)
|
|
111
|
+
if (basename(filePath).startsWith('_')) {
|
|
112
|
+
logger.info('unlink', relPath)
|
|
113
|
+
return
|
|
114
|
+
}
|
|
115
|
+
const distPath = resolve(paths.dist, relPath.replace(/\.pug$/, '.html'))
|
|
116
|
+
await this.deleteDistFile(distPath, relPath)
|
|
140
117
|
}
|
|
141
118
|
|
|
142
|
-
|
|
143
|
-
* Script監視
|
|
144
|
-
*/
|
|
145
|
-
watchScript(basePath) {
|
|
146
|
-
const watcher = chokidar.watch(basePath, {
|
|
147
|
-
ignoreInitial: true,
|
|
148
|
-
ignored: [/(^|[\/\\])\../, /node_modules/, /\.git/, /\.d\.ts$/],
|
|
149
|
-
persistent: true,
|
|
150
|
-
awaitWriteFinish: { stabilityThreshold: 100, pollInterval: 50 }
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
watcher.on('change', async path => {
|
|
154
|
-
// .ts/.jsファイルのみ処理(.d.tsを除く)
|
|
155
|
-
if (!(path.endsWith('.ts') || path.endsWith('.js')) || path.endsWith('.d.ts')) {
|
|
156
|
-
return
|
|
157
|
-
}
|
|
158
|
-
const relPath = relative(basePath, path)
|
|
159
|
-
logger.info('change', `script: ${relPath}`)
|
|
160
|
-
|
|
161
|
-
try {
|
|
162
|
-
// Scriptタスクを実行
|
|
163
|
-
if (this.context.taskRegistry?.script) {
|
|
164
|
-
await this.context.taskRegistry.script(this.context)
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
this.reload()
|
|
168
|
-
} catch (error) {
|
|
169
|
-
logger.error('watch', `Script build failed: ${error.message}`)
|
|
170
|
-
}
|
|
171
|
-
})
|
|
119
|
+
// ---- Sass ----
|
|
172
120
|
|
|
173
|
-
|
|
121
|
+
async onSassChange(filePath) {
|
|
122
|
+
const relPath = relative(this.context.paths.src, filePath)
|
|
123
|
+
logger.info('change', `sass: ${relPath}`)
|
|
124
|
+
try {
|
|
125
|
+
if (this.context.taskRegistry?.sass) await this.context.taskRegistry.sass(this.context)
|
|
126
|
+
this.injectCSS()
|
|
127
|
+
} catch (error) {
|
|
128
|
+
logger.error('watch', `Sass build failed: ${error.message}`)
|
|
129
|
+
}
|
|
174
130
|
}
|
|
175
131
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
ignored: [/(^|[\/\\])\../, /node_modules/, /\.git/, /icons/], // iconsはスプライト用なので除外
|
|
183
|
-
persistent: true,
|
|
184
|
-
awaitWriteFinish: { stabilityThreshold: 100, pollInterval: 50 }
|
|
185
|
-
})
|
|
186
|
-
|
|
187
|
-
const handleSvgChange = async path => {
|
|
188
|
-
// .svgファイルのみ処理(iconsディレクトリは除外)
|
|
189
|
-
// Windows対応: パスセパレータを正規化
|
|
190
|
-
const normalizedPath = path.replace(/\\/g, '/')
|
|
191
|
-
if (!path.endsWith('.svg') || normalizedPath.includes('/icons/')) {
|
|
192
|
-
return
|
|
193
|
-
}
|
|
194
|
-
const relPath = relative(basePath, path)
|
|
195
|
-
logger.info('change', `svg: ${relPath}`)
|
|
196
|
-
|
|
197
|
-
try {
|
|
198
|
-
// SVGタスクを実行(変更されたファイルのみ)
|
|
199
|
-
if (this.context.taskRegistry?.svg) {
|
|
200
|
-
await this.context.taskRegistry.svg(this.context, {
|
|
201
|
-
files: [path]
|
|
202
|
-
})
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
this.reload()
|
|
206
|
-
} catch (error) {
|
|
207
|
-
logger.error('watch', `SVG processing failed: ${error.message}`)
|
|
208
|
-
}
|
|
132
|
+
async onSassUnlink(filePath) {
|
|
133
|
+
const { paths } = this.context
|
|
134
|
+
const relPath = relative(paths.src, filePath)
|
|
135
|
+
if (basename(filePath).startsWith('_')) {
|
|
136
|
+
logger.info('unlink', relPath)
|
|
137
|
+
return
|
|
209
138
|
}
|
|
139
|
+
const distPath = resolve(paths.dist, relPath.replace(/\.scss$/, '.css'))
|
|
140
|
+
await this.deleteDistFile(distPath, relPath)
|
|
141
|
+
}
|
|
210
142
|
|
|
211
|
-
|
|
212
|
-
watcher.on('add', handleSvgChange)
|
|
143
|
+
// ---- Script ----
|
|
213
144
|
|
|
214
|
-
|
|
145
|
+
async onScriptChange(filePath) {
|
|
146
|
+
const relPath = relative(this.context.paths.src, filePath)
|
|
147
|
+
logger.info('change', `script: ${relPath}`)
|
|
148
|
+
try {
|
|
149
|
+
if (this.context.taskRegistry?.script) await this.context.taskRegistry.script(this.context)
|
|
150
|
+
this.reload()
|
|
151
|
+
} catch (error) {
|
|
152
|
+
logger.error('watch', `Script build failed: ${error.message}`)
|
|
153
|
+
}
|
|
215
154
|
}
|
|
216
155
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
ignored: [/(^|[\/\\])\../, /node_modules/, /\.git/],
|
|
224
|
-
persistent: true,
|
|
225
|
-
awaitWriteFinish: { stabilityThreshold: 100, pollInterval: 50 }
|
|
226
|
-
})
|
|
227
|
-
|
|
228
|
-
const handleImageChange = async path => {
|
|
229
|
-
// 画像ファイルのみ処理
|
|
230
|
-
if (!/\.(jpg|jpeg|png|gif)$/i.test(path)) {
|
|
231
|
-
return
|
|
232
|
-
}
|
|
233
|
-
const relPath = relative(basePath, path)
|
|
234
|
-
logger.info('change', `image: ${relPath}`)
|
|
235
|
-
|
|
236
|
-
try {
|
|
237
|
-
// 追加・変更時: 画像を処理
|
|
238
|
-
if (this.context.taskRegistry?.image) {
|
|
239
|
-
await this.context.taskRegistry.image(this.context, {
|
|
240
|
-
files: [path]
|
|
241
|
-
})
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
this.reload()
|
|
245
|
-
} catch (error) {
|
|
246
|
-
logger.error('watch', `Image processing failed: ${error.message}`)
|
|
247
|
-
}
|
|
156
|
+
async onScriptUnlink(filePath) {
|
|
157
|
+
const { paths } = this.context
|
|
158
|
+
const relPath = relative(paths.src, filePath)
|
|
159
|
+
if (basename(filePath).startsWith('_')) {
|
|
160
|
+
logger.info('unlink', relPath)
|
|
161
|
+
return
|
|
248
162
|
}
|
|
163
|
+
const distPath = resolve(paths.dist, relPath.replace(/\.ts$/, '.js'))
|
|
164
|
+
await this.deleteDistFile(distPath, relPath)
|
|
165
|
+
}
|
|
249
166
|
|
|
250
|
-
|
|
251
|
-
watcher.on('add', handleImageChange)
|
|
167
|
+
// ---- SVG ----
|
|
252
168
|
|
|
253
|
-
|
|
169
|
+
async onSvgChange(filePath, event) {
|
|
170
|
+
const relPath = relative(this.context.paths.src, filePath)
|
|
171
|
+
logger.info(event, `svg: ${relPath}`)
|
|
172
|
+
try {
|
|
173
|
+
if (this.context.taskRegistry?.svg) {
|
|
174
|
+
await this.context.taskRegistry.svg(this.context, { files: [filePath] })
|
|
175
|
+
}
|
|
176
|
+
this.reload()
|
|
177
|
+
} catch (error) {
|
|
178
|
+
logger.error('watch', `SVG processing failed: ${error.message}`)
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async onSvgUnlink(filePath) {
|
|
183
|
+
const relPath = relative(this.context.paths.src, filePath)
|
|
184
|
+
const distPath = resolve(this.context.paths.dist, relPath)
|
|
185
|
+
await this.deleteDistFile(distPath, relPath)
|
|
254
186
|
}
|
|
255
187
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
awaitWriteFinish: { stabilityThreshold: 100, pollInterval: 50 }
|
|
265
|
-
})
|
|
266
|
-
|
|
267
|
-
watcher.on('change', async path => {
|
|
268
|
-
const relPath = relative(basePath, path)
|
|
269
|
-
logger.info('change', `public: ${relPath}`)
|
|
270
|
-
|
|
271
|
-
try {
|
|
272
|
-
// Copyタスクを実行
|
|
273
|
-
if (this.context.taskRegistry?.copy) {
|
|
274
|
-
await this.context.taskRegistry.copy(this.context)
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
this.reload()
|
|
278
|
-
} catch (error) {
|
|
279
|
-
logger.error('watch', `Copy failed: ${error.message}`)
|
|
188
|
+
// ---- Image ----
|
|
189
|
+
|
|
190
|
+
async onImageChange(filePath, event) {
|
|
191
|
+
const relPath = relative(this.context.paths.src, filePath)
|
|
192
|
+
logger.info(event, `image: ${relPath}`)
|
|
193
|
+
try {
|
|
194
|
+
if (this.context.taskRegistry?.image) {
|
|
195
|
+
await this.context.taskRegistry.image(this.context, { files: [filePath] })
|
|
280
196
|
}
|
|
281
|
-
|
|
197
|
+
this.reload()
|
|
198
|
+
} catch (error) {
|
|
199
|
+
logger.error('watch', `Image processing failed: ${error.message}`)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async onImageUnlink(filePath) {
|
|
204
|
+
const { paths, config } = this.context
|
|
205
|
+
const relPath = relative(paths.src, filePath)
|
|
206
|
+
const useWebp = config.build.imageOptimization === 'webp'
|
|
207
|
+
const ext = extname(filePath)
|
|
208
|
+
const destRelPath = useWebp ? relPath.replace(new RegExp(`\\${ext}$`, 'i'), '.webp') : relPath
|
|
209
|
+
const distPath = resolve(paths.dist, destRelPath)
|
|
210
|
+
await this.deleteDistFile(distPath, relPath)
|
|
211
|
+
}
|
|
282
212
|
|
|
283
|
-
|
|
213
|
+
// ---- Public ----
|
|
214
|
+
|
|
215
|
+
async onPublicChange(filePath, event) {
|
|
216
|
+
const relPath = relative(this.context.paths.public, filePath)
|
|
217
|
+
logger.info(event, `public: ${relPath}`)
|
|
218
|
+
try {
|
|
219
|
+
if (this.context.taskRegistry?.copy) await this.context.taskRegistry.copy(this.context, { files: [filePath] })
|
|
220
|
+
this.reload()
|
|
221
|
+
} catch (error) {
|
|
222
|
+
logger.error('watch', `Copy failed: ${error.message}`)
|
|
223
|
+
}
|
|
284
224
|
}
|
|
285
225
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
226
|
+
async onPublicUnlink(filePath) {
|
|
227
|
+
const relPath = relative(this.context.paths.public, filePath)
|
|
228
|
+
const distPath = resolve(this.context.paths.dist, relPath)
|
|
229
|
+
await this.deleteDistFile(distPath, relPath)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ---- 共通ヘルパー ----
|
|
233
|
+
|
|
289
234
|
reload() {
|
|
290
235
|
if (this.context.server) {
|
|
291
236
|
setTimeout(() => {
|
|
@@ -294,20 +239,24 @@ class FileWatcher {
|
|
|
294
239
|
}
|
|
295
240
|
}
|
|
296
241
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
242
|
+
async deleteDistFile(distPath, relPath) {
|
|
243
|
+
try {
|
|
244
|
+
await rm(distPath, { force: true })
|
|
245
|
+
logger.info('unlink', relPath)
|
|
246
|
+
this.reload()
|
|
247
|
+
} catch (error) {
|
|
248
|
+
logger.error('watch', `Failed to delete ${relPath}: ${error.message}`)
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
300
252
|
injectCSS() {
|
|
301
253
|
if (this.context.server) {
|
|
302
254
|
this.context.server.reloadCSS()
|
|
303
255
|
}
|
|
304
256
|
}
|
|
305
257
|
|
|
306
|
-
/**
|
|
307
|
-
* 監視停止
|
|
308
|
-
*/
|
|
309
258
|
async stop() {
|
|
310
|
-
|
|
259
|
+
if (this.watcher) await this.watcher.close()
|
|
311
260
|
logger.info('watch', 'File watching stopped')
|
|
312
261
|
}
|
|
313
262
|
}
|
package/generate/asset.mjs
CHANGED
|
@@ -7,6 +7,8 @@ export async function generateAsset(outputPath, data) {
|
|
|
7
7
|
|
|
8
8
|
if (typeof data === 'string' || Buffer.isBuffer(data)) {
|
|
9
9
|
await writeFile(outputPath, data)
|
|
10
|
+
} else {
|
|
11
|
+
throw new TypeError(`generateAsset: data must be a string or Buffer, got ${typeof data}`)
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
return outputPath
|
package/generate/page.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { ensureFileDir } from '../utils/file.mjs'
|
|
|
4
4
|
|
|
5
5
|
export async function generatePage(filePath, html, paths) {
|
|
6
6
|
const relativePath = relative(paths.src, filePath)
|
|
7
|
-
const outputPath = resolve(paths.dist, relativePath.replace(
|
|
7
|
+
const outputPath = resolve(paths.dist, relativePath.replace(/\.pug$/, '.html'))
|
|
8
8
|
|
|
9
9
|
await ensureFileDir(outputPath)
|
|
10
10
|
await writeFile(outputPath, html, 'utf8')
|
package/package.json
CHANGED
package/tasks/copy.mjs
CHANGED
|
@@ -10,6 +10,21 @@ import { ensureFileDir } from '../utils/file.mjs'
|
|
|
10
10
|
export async function copyTask(context, options = {}) {
|
|
11
11
|
const { paths } = context
|
|
12
12
|
|
|
13
|
+
// 特定のファイルが指定されている場合(watch時)
|
|
14
|
+
if (options.files && Array.isArray(options.files)) {
|
|
15
|
+
logger.info('copy', `Copying ${options.files.length} file(s)`)
|
|
16
|
+
await Promise.all(
|
|
17
|
+
options.files.map(async file => {
|
|
18
|
+
const relativePath = relative(paths.public, file)
|
|
19
|
+
const outputPath = resolve(paths.dist, relativePath)
|
|
20
|
+
await ensureFileDir(outputPath)
|
|
21
|
+
await copyFile(file, outputPath)
|
|
22
|
+
})
|
|
23
|
+
)
|
|
24
|
+
logger.success('copy', `Copied ${options.files.length} file(s)`)
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
|
|
13
28
|
const files = await glob('**/*', {
|
|
14
29
|
cwd: paths.public,
|
|
15
30
|
absolute: true,
|
package/tasks/pug.mjs
CHANGED
|
@@ -57,10 +57,8 @@ async function processFile(filePath, context) {
|
|
|
57
57
|
if (!template) {
|
|
58
58
|
const result = await compilePugFile(filePath, { basedir: paths.src })
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
result.dependencies.forEach(dep => graph.addDependency(filePath, dep))
|
|
63
|
-
}
|
|
60
|
+
graph.clearDependencies(filePath)
|
|
61
|
+
result.dependencies.forEach(dep => graph.addDependency(filePath, dep))
|
|
64
62
|
|
|
65
63
|
template = result.template
|
|
66
64
|
cache.setPugTemplate(filePath, template)
|
package/tasks/sass.mjs
CHANGED
|
@@ -78,7 +78,7 @@ async function compileSassFile(filePath, context, isDebugMode) {
|
|
|
78
78
|
)
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
const outputRelativePath = relative(paths.src, filePath).replace(
|
|
81
|
+
const outputRelativePath = relative(paths.src, filePath).replace(/\.scss$/, '.css')
|
|
82
82
|
const outputPath = resolve(paths.dist, outputRelativePath)
|
|
83
83
|
|
|
84
84
|
const postcssResult = await postcss(postcssPlugins).process(css, {
|
package/tasks/script.mjs
CHANGED
|
@@ -29,7 +29,7 @@ export async function scriptTask(context, options = {}) {
|
|
|
29
29
|
|
|
30
30
|
try {
|
|
31
31
|
// 2. esbuild設定
|
|
32
|
-
const
|
|
32
|
+
const esbuildConfig = {
|
|
33
33
|
entryPoints: scriptFiles,
|
|
34
34
|
outdir: paths.dist,
|
|
35
35
|
outbase: paths.src,
|
|
@@ -54,12 +54,12 @@ export async function scriptTask(context, options = {}) {
|
|
|
54
54
|
|
|
55
55
|
// debugモードでない場合はconsole/debuggerを削除
|
|
56
56
|
if (!isDebugMode) {
|
|
57
|
-
|
|
57
|
+
esbuildConfig.drop = ['console', 'debugger']
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
// 3. ビルド実行
|
|
61
61
|
await ensureDir(paths.dist)
|
|
62
|
-
const result = await esbuild.build(
|
|
62
|
+
const result = await esbuild.build(esbuildConfig)
|
|
63
63
|
|
|
64
64
|
if (result.errors && result.errors.length > 0) {
|
|
65
65
|
throw new Error(`esbuild errors: ${result.errors.length}`)
|
package/tasks/svg-sprite.mjs
CHANGED
package/tasks/svg.mjs
CHANGED
|
@@ -52,13 +52,7 @@ async function optimizeSvg(filePath, context) {
|
|
|
52
52
|
// SVGOで最適化
|
|
53
53
|
const result = optimize(content, {
|
|
54
54
|
path: filePath,
|
|
55
|
-
plugins: [
|
|
56
|
-
'preset-default',
|
|
57
|
-
{
|
|
58
|
-
name: 'removeViewBox',
|
|
59
|
-
active: false
|
|
60
|
-
}
|
|
61
|
-
]
|
|
55
|
+
plugins: ['preset-default']
|
|
62
56
|
})
|
|
63
57
|
|
|
64
58
|
// 出力
|
package/utils/file.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { mkdir, rm } from 'node:fs/promises'
|
|
2
|
-
import { existsSync } from 'node:fs'
|
|
3
2
|
import { dirname } from 'node:path'
|
|
4
3
|
|
|
5
4
|
/**
|
|
@@ -10,9 +9,7 @@ import { dirname } from 'node:path'
|
|
|
10
9
|
* ディレクトリを作成(再帰的)
|
|
11
10
|
*/
|
|
12
11
|
export async function ensureDir(dirPath) {
|
|
13
|
-
|
|
14
|
-
await mkdir(dirPath, { recursive: true })
|
|
15
|
-
}
|
|
12
|
+
await mkdir(dirPath, { recursive: true })
|
|
16
13
|
}
|
|
17
14
|
|
|
18
15
|
/**
|
|
@@ -26,9 +23,7 @@ export async function ensureFileDir(filePath) {
|
|
|
26
23
|
* ディレクトリをクリーンアップ
|
|
27
24
|
*/
|
|
28
25
|
export async function cleanDir(dirPath) {
|
|
29
|
-
|
|
30
|
-
await rm(dirPath, { recursive: true, force: true })
|
|
31
|
-
}
|
|
26
|
+
await rm(dirPath, { recursive: true, force: true })
|
|
32
27
|
await mkdir(dirPath, { recursive: true })
|
|
33
28
|
}
|
|
34
29
|
|