@ywal123456/jskim 0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jeongsub Kim
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,336 @@
1
+ # JSKim
2
+
3
+ Nunjucks を使った汎用の静的 HTML ビルド環境です。
4
+
5
+ 特定のアプリ構成、業務ロジック、CSS/JS 設計、Git 運用、デプロイ基盤を強制しません。
6
+ Nunjucks ソースを設定に従って静的 HTML にレンダリングし、assets をコピーして配布可能な結果を作ります。
7
+
8
+ ## 現在の対応範囲
9
+
10
+ - `jskim.config.js` による設定
11
+ - defaults とプロジェクト設定のマージ
12
+ - Nunjucks ページのレンダリング
13
+ - 静的ファイルのコピー
14
+ - outputDir の clean
15
+ - `rootPath` の自動注入
16
+ - プロジェクトごとの出力分離
17
+ - ファイル監視(`watch`)後の全体再ビルド
18
+ - ビルド結果のローカル静的サーバー(`serve`)
19
+ - 開発サーバー(`dev` = build + watch + serve)
20
+ - 成功した再ビルド後のページ全体ライブリロード(SSE)
21
+ - CLI binary `jskim`
22
+ - プロジェクト生成 CLI `create-jskim`
23
+
24
+ ## 現在の非対応範囲
25
+
26
+ - ブラウザ自動起動
27
+ - HMR / CSS だけのホット更新
28
+ - HTML 移行
29
+ - JSON データの自動読み込み
30
+ - API / Mock API
31
+ - 実際の npm registry publish(手順は準備済み)
32
+ - config の hot reload
33
+ - 増分ビルド
34
+ - SPA fallback / proxy
35
+
36
+ ## パッケージの役割
37
+
38
+ | 名称 | 種類 | 役割 |
39
+ |------|------|------|
40
+ | `@ywal123456/jskim` | npm engine package | `jskim build` / `watch` / `serve` / `dev` |
41
+ | `create-jskim` | npm creator package | 新しい JSKim 作業空間の生成 |
42
+ | `jskim` | CLI binary | インストール後に実行するコマンド名 |
43
+
44
+ 製品名は **JSKim**、CLI binary は **`jskim`**、npm engine package は **`@ywal123456/jskim`** です。package 名と binary 名を混同しないでください。
45
+
46
+ Package 名・CLI 構造は公開名に合わせてあります。**このリポジトリ作業では registry への publish は行っていません**。registry からのインストールは publish 後に可能です。scoped engine は公開 MIT package として配布する予定です。
47
+
48
+ 詳細:
49
+
50
+ - [docs/create-jskim.md](docs/create-jskim.md)
51
+ - [docs/publishing.md](docs/publishing.md)
52
+
53
+ ## パッケージとしての利用
54
+
55
+ 公開後のインストール例:
56
+
57
+ ```bash
58
+ npm install --save-dev @ywal123456/jskim
59
+ ```
60
+
61
+ ```bash
62
+ jskim build sample
63
+ jskim watch sample
64
+ jskim serve sample
65
+ jskim dev sample
66
+
67
+ npx jskim build sample
68
+ ```
69
+
70
+ `package.json` の scripts 例:
71
+
72
+ ```json
73
+ {
74
+ "scripts": {
75
+ "build": "jskim build sample",
76
+ "watch": "jskim watch sample",
77
+ "serve": "jskim serve sample",
78
+ "dev": "jskim dev sample"
79
+ }
80
+ }
81
+ ```
82
+
83
+ 要点:
84
+
85
+ - npm package 名は `@ywal123456/jskim`(scoped)
86
+ - インストール後の CLI binary 名は `jskim`(変更なし)
87
+ - コマンドは実行したディレクトリの `process.cwd()` をプロジェクトルートとして扱う
88
+ - パッケージのインストール先(`node_modules/@ywal123456/jskim`)を作業空間とはみなさない
89
+ - プロジェクトルートに `jskim.config.js` が必要
90
+ - `create-jskim` package と `npm create jskim@latest` コマンドは維持します
91
+ ヘルプ / バージョン:
92
+
93
+ ```bash
94
+ jskim --help
95
+ jskim --version
96
+ ```
97
+
98
+ ## プロジェクト生成(create-jskim)
99
+
100
+ 公開後の例:
101
+
102
+ ```bash
103
+ npm create jskim@latest
104
+ # または
105
+ npx create-jskim my-project
106
+ ```
107
+
108
+ ローカル / 開発時:
109
+
110
+ ```bash
111
+ create-jskim my-project
112
+ cd my-project
113
+ npm install
114
+ npm run dev
115
+ ```
116
+
117
+ - 自動で `npm install` / `git init` は実行しません
118
+ - 空ではない既存ディレクトリは上書きしません
119
+ - 実際の registry インストールは publish 後に利用できます
120
+
121
+ ## このリポジトリでの開発コマンド
122
+
123
+ 開発リポジトリでは従来どおり `npm run` も使えます。
124
+
125
+ ## インストール(開発リポジトリ)
126
+
127
+ ```bash
128
+ npm install
129
+ ```
130
+
131
+ ## ビルド
132
+
133
+ ```bash
134
+ npm run build -- sample
135
+ # または(インストール済み binary)
136
+ jskim build sample
137
+ ```
138
+
139
+ `sample` は `jskim.config.js` の `projects` に登録されたプロジェクト名です。
140
+
141
+ ## ウォッチ
142
+
143
+ ```bash
144
+ npm run watch -- sample
145
+ # または
146
+ jskim watch sample
147
+ ```
148
+
149
+ 動作:
150
+
151
+ 1. 初回に全体ビルドを実行
152
+ 2. プロジェクト関連パスの監視を開始
153
+ 3. 変更検知後、debounce して全体ビルドを再実行
154
+ 4. ビルドエラーがあってもウォッチャーは維持し、次の保存で再試行
155
+
156
+ ### 監視対象
157
+
158
+ マージ済みプロジェクト設定から計算します。
159
+
160
+ - `sourceDir` + `render[].from`(例: `src/sample/pages`)
161
+ - `sourceDir` + `templates[]`(例: `src/sample/layouts`, `src/sample/components`)
162
+ - `sourceDir` + `copy[].from`(例: `src/sample/assets`)
163
+
164
+ `dist/`、`node_modules/`、`outputDir` は監視しません。
165
+
166
+ ### 現在の方針
167
+
168
+ - 関連する変更はすべて **全体ビルド** です(増分 render / 単一 asset copy なし)。
169
+ - `build.clean: true` のとき、再ビルドで outputDir を消して作り直すため、ソース削除も結果に反映されます。
170
+ - `jskim.config.js` を変更した場合は **watch / dev を再起動** してください(config hot reload なし)。
171
+ - 終了: `Ctrl+C`(SIGINT)または SIGTERM → `[JSKim] ウォッチを停止しました。`
172
+
173
+ debounce の既定値は `watch.debounce: 150`(ms)で、プロジェクトごとに上書きできます。詳細は [docs/configuration.md](docs/configuration.md) を参照してください。
174
+
175
+ ## 静的サーバー(serve)
176
+
177
+ ```bash
178
+ npm run build -- sample
179
+ npm run serve -- sample
180
+ # または
181
+ jskim build sample
182
+ jskim serve sample
183
+ ```
184
+
185
+ `serve` は **すでにビルドされた** `outputDir` をローカルで確認するためのコマンドです。
186
+
187
+ - 自動で build / watch は実行しません
188
+ - サーバールートは `outputDir`(例: `dist/sample`)
189
+ - 対応 HTTP メソッドは `GET` と `HEAD` のみ
190
+ - ライブリロードはありません(HTML を変換しません)
191
+ - ローカル確認用です(本番サーバー機能ではありません)
192
+
193
+ 既定の接続先:
194
+
195
+ ```text
196
+ http://127.0.0.1:3000/
197
+ ```
198
+
199
+ `host` / `port` は `jskim.config.js` の `serve` で変更できます。
200
+
201
+ `outputDir` が無い場合は、先に build するよう案内して終了します。
202
+
203
+ 終了: `Ctrl+C` → `[JSKim] 静的サーバーを停止しました。`
204
+
205
+ ## 開発サーバー(dev)
206
+
207
+ ```bash
208
+ npm run dev -- sample
209
+ # または
210
+ jskim dev sample
211
+ ```
212
+
213
+ `dev` は次を統合します。
214
+
215
+ 1. 初回に全体ビルド
216
+ 2. 実際の `outputDir`(`dist`)を静的サーバーで提供
217
+ 3. ファイル監視と全体再ビルド(`watch` と同じ debounce / 直列化)
218
+ 4. **成功した再ビルドのあとだけ** ブラウザを全体リロード(SSE)
219
+
220
+ ポイント:
221
+
222
+ - メモリ専用レンダリングではなく、常に実際の `dist` を提供します
223
+ - ライブリロードは HMR ではなく **ページ全体の reload** です
224
+ - `dist` ファイルへ script を書き込みません。dev の HTML レスポンスにだけ一時注入します
225
+ - ビルドエラー時も watcher / server は維持し、reload は送りません
226
+ - ブラウザ自動起動はサポートしません
227
+ - `watch.debounce` / `serve.host` / `serve.port` / `dev.liveReload` を使います
228
+
229
+ 既定 URL:
230
+
231
+ ```text
232
+ http://127.0.0.1:3000/
233
+ ```
234
+
235
+ `dev.liveReload: false` にすると SSE endpoint と script 注入を無効化できます。
236
+
237
+ 設定を変えた場合は `dev` を再起動してください。
238
+
239
+ 終了: `Ctrl+C` → `[JSKim] 開発サーバーを停止しました。`
240
+
241
+ ### 予約パス `/_jskim/live-reload`
242
+
243
+ `dev.liveReload: true` のとき、JSKim が内部で使う予約パスです。
244
+
245
+ - 実際の静的ファイルパスとして使わないことを推奨します
246
+ - `serve` ではこの endpoint は有効になりません
247
+ - `dev.liveReload: false` のときも有効になりません
248
+
249
+ ## テスト
250
+
251
+ ```bash
252
+ npm test
253
+ ```
254
+
255
+ Node 標準の `node:test` / `node:assert` を使います。外部テスト framework は使いません。
256
+
257
+ - 実際の `src/sample` と `dist/sample` は変更しません
258
+ - 各テストは一時ワークスペース(`os.tmpdir`)で実行します
259
+ - build / watch / serve / dev / 言語ポリシーの回帰を検証します
260
+
261
+ ## 基本ディレクトリ構成
262
+
263
+ ```text
264
+ jskim/
265
+ ├─ bin/
266
+ │ └─ jskim.js
267
+ ├─ jskim.config.js
268
+ ├─ scripts/
269
+ │ ├─ build.js
270
+ │ ├─ watch.js
271
+ │ ├─ serve.js
272
+ │ ├─ dev.js
273
+ │ └─ commands/
274
+ ├─ src/
275
+ │ └─ sample/
276
+ │ ├─ pages/
277
+ │ ├─ layouts/
278
+ │ ├─ components/
279
+ │ └─ assets/
280
+ └─ dist/
281
+ └─ sample/
282
+ ```
283
+
284
+ ## jskim.config.js の役割
285
+
286
+ コマンドを実行したディレクトリ(`process.cwd()`)をワークスペースルートとし、そこに置いた `jskim.config.js` がビルド動作の基準です。
287
+
288
+ - `defaults`: 全プロジェクト共通の render / templates / copy / build / watch / serve / dev
289
+ - `projects`: プロジェクトごとの `sourceDir`、`outputDir`、および任意の上書き
290
+
291
+ 詳細は [docs/configuration.md](docs/configuration.md) を参照してください。
292
+
293
+ ## src と dist の関係
294
+
295
+ - `sourceDir`(例: `src/sample`)に Nunjucks ソースと assets を置きます
296
+ - `outputDir`(例: `dist/sample`)にレンダリング済み HTML とコピーされた静的ファイルが生成されます
297
+ - `dist/` はビルド成果物なので手編集しないでください
298
+
299
+ ## rootPath の使い方
300
+
301
+ 各ページの最終出力位置を基準に `rootPath` が自動計算され、Nunjucks context に注入されます。
302
+
303
+ ```njk
304
+ <link rel="stylesheet" href="{{ rootPath }}assets/css/style.css">
305
+ <a href="{{ rootPath }}index.html">Home</a>
306
+ ```
307
+
308
+ | 出力ファイル | rootPath |
309
+ |-----------|----------|
310
+ | `dist/sample/index.html` | `./` |
311
+ | `dist/sample/guide/basic.html` | `../` |
312
+ | `dist/sample/guide/syntax/loop.html` | `../../` |
313
+
314
+ ## sample
315
+
316
+ `src/sample` は JSKim の紹介と Nunjucks 構文例を含む技術ドキュメント型の単一ページです。
317
+
318
+ ```bash
319
+ npm run build -- sample
320
+ npm run serve -- sample
321
+ # または
322
+ npm run dev -- sample
323
+ ```
324
+
325
+ ビルド後、`dist/sample/index.html` または `http://127.0.0.1:3000/` で確認できます。
326
+
327
+ ## リポジトリ
328
+
329
+ - Repository: https://github.com/ywal123456-collab/jskim
330
+ - Issues: https://github.com/ywal123456-collab/jskim/issues
331
+
332
+ ## ライセンス
333
+
334
+ JSKim は MIT License のもとで提供されます。
335
+ 自由に使用・修正・再配布・商用利用できます。著作権表示と License 文言の保持が必要であり、無保証です。
336
+ 詳細は [`LICENSE`](./LICENSE) を確認してください。
package/bin/jskim.js ADDED
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const path = require('node:path');
5
+ const { getHelpText, getUnknownCommandText } = require('../scripts/commands/help-text');
6
+ const { runBuildCommand } = require('../scripts/commands/build-command');
7
+ const { runWatchCommand } = require('../scripts/commands/watch-command');
8
+ const { runServeCommand } = require('../scripts/commands/serve-command');
9
+ const { runDevCommand } = require('../scripts/commands/dev-command');
10
+
11
+ const COMMANDS = new Set(['build', 'watch', 'serve', 'dev']);
12
+
13
+ function readPackageVersion() {
14
+ // package インストール先の package.json を読む(作業空間ではない)
15
+ const pkg = require(path.join(__dirname, '..', 'package.json'));
16
+ return pkg.version;
17
+ }
18
+
19
+ function printHelp() {
20
+ console.log(getHelpText());
21
+ }
22
+
23
+ function printVersion() {
24
+ console.log(readPackageVersion());
25
+ }
26
+
27
+ /**
28
+ * @param {string[]} argv process.argv 相当(node と script を除く)
29
+ */
30
+ async function dispatch(argv) {
31
+ const first = argv[0];
32
+
33
+ if (!first) {
34
+ printHelp();
35
+ process.exitCode = 0;
36
+ return;
37
+ }
38
+
39
+ if (first === '--help' || first === '-h' || first === 'help') {
40
+ printHelp();
41
+ process.exitCode = 0;
42
+ return;
43
+ }
44
+
45
+ if (first === '--version' || first === '-v') {
46
+ printVersion();
47
+ process.exitCode = 0;
48
+ return;
49
+ }
50
+
51
+ if (!COMMANDS.has(first)) {
52
+ console.error(getUnknownCommandText(first));
53
+ process.exitCode = 1;
54
+ return;
55
+ }
56
+
57
+ const projectName = argv[1];
58
+ const workspaceRoot = process.cwd();
59
+ const usageLine = `jskim ${first} <project>`;
60
+ const buildHint = projectName
61
+ ? `jskim build ${projectName}`
62
+ : 'jskim build <project>';
63
+
64
+ if (first === 'build') {
65
+ await runBuildCommand({
66
+ projectName,
67
+ workspaceRoot,
68
+ usageLine,
69
+ });
70
+ return;
71
+ }
72
+
73
+ if (first === 'watch') {
74
+ await runWatchCommand({
75
+ projectName,
76
+ workspaceRoot,
77
+ usageLine,
78
+ });
79
+ return;
80
+ }
81
+
82
+ if (first === 'serve') {
83
+ await runServeCommand({
84
+ projectName,
85
+ workspaceRoot,
86
+ usageLine,
87
+ buildHint,
88
+ });
89
+ return;
90
+ }
91
+
92
+ if (first === 'dev') {
93
+ await runDevCommand({
94
+ projectName,
95
+ workspaceRoot,
96
+ usageLine,
97
+ });
98
+ }
99
+ }
100
+
101
+ dispatch(process.argv.slice(2)).catch((err) => {
102
+ const message = err && err.message ? err.message : String(err);
103
+ console.error(message);
104
+ process.exitCode = 1;
105
+ });