tssetup 1.0.0__tar.gz

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.
@@ -0,0 +1,29 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ build-and-publish:
9
+ name: Build and publish to PyPI
10
+ runs-on: ubuntu-latest
11
+ environment: pypi
12
+ permissions:
13
+ id-token: write
14
+
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - uses: actions/setup-python@v5
19
+ with:
20
+ python-version: "3.x"
21
+
22
+ - name: Install hatch
23
+ run: pip install hatch
24
+
25
+ - name: Build package
26
+ run: hatch build
27
+
28
+ - name: Publish to PyPI
29
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,5 @@
1
+
2
+ __pycache__/
3
+ *.pyc
4
+ dist/
5
+ *.egg-info/
tssetup-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,143 @@
1
+ Metadata-Version: 2.4
2
+ Name: tssetup
3
+ Version: 1.0.0
4
+ Summary: Bun + TypeScript フロントエンド環境を1コマンドで構築するツール
5
+ Project-URL: Homepage, https://github.com/Lapius7/tssetup
6
+ Project-URL: Issues, https://github.com/Lapius7/tssetup/issues
7
+ Author-email: Lapius7 <me@lapius7.com>
8
+ License: MIT
9
+ Keywords: bun,cli,frontend,hot-reload,scaffold,typescript
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: Microsoft :: Windows
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Topic :: Software Development :: Build Tools
16
+ Requires-Python: >=3.9
17
+ Description-Content-Type: text/markdown
18
+
19
+ # tssetup
20
+
21
+ PowerShellやCMDから1コマンドで、Bun + TypeScript の高速なフロントエンド開発環境(ホットリロードサーバー内蔵)を初期構築するツールです。
22
+
23
+ ## 🚀 特徴
24
+
25
+ - **瞬時にプロジェクト開始:** フォルダ作成、`bun init`、`tsconfig.json` の設定、ホットリロードサーバースクリプトの作成を自動で一括実行します。
26
+ - **多彩な初期テンプレート:**
27
+ - `default`: Destyle.css を内包したシンプルな構成(ダークモード対応)。
28
+ - `tailwind`: CDN版 Tailwind CSS が即座に使えるレイアウト構成。
29
+ - `router`: ライブラリを使わずに HTML5 History API を利用した、自作の超軽量 SPA ルーティング構成。
30
+ - `empty`: 最少構成(空の TypeScript ファイルとシンプルな HTML)。
31
+ - **エディタ連携:** `--code` オプションを付けるだけで、作成したプロジェクトフォルダを VS Code で即座に開きます。
32
+ - **自動バージョン確認:** 実行時に最新バージョンを確認し、更新がある場合は通知します。
33
+
34
+ ---
35
+
36
+ ## 🛠️ インストール方法
37
+
38
+ **pip(推奨):**
39
+
40
+ ```bash
41
+ pip install tssetup
42
+ ```
43
+
44
+ > [!NOTE]
45
+ >
46
+ > - PowerShell・CMD・Windows Terminal どれからでも使えます。
47
+ > - 動作には **Python 3.9+** と **Bun** が必要です。
48
+
49
+ ---
50
+
51
+ ## ⚙️ 動作に必要な環境(依存関係)
52
+
53
+ | ツール | 用途 | インストール |
54
+ | :--- | :--- | :--- |
55
+ | **Python 3.9+** | tssetup 本体の実行 | [python.org](https://www.python.org/) |
56
+ | **Bun** | プロジェクト初期化・TSコンパイル | `powershell -c "irm bun.sh/install.ps1 \| iex"` |
57
+ | **VS Code** (任意) | `--code` オプション使用時 | [code.visualstudio.com](https://code.visualstudio.com/) |
58
+
59
+ ---
60
+
61
+ ## ⚡ 使い方と全パラメータ一覧
62
+
63
+ ```bash
64
+ tssetup <プロジェクト名> [--mode <モード名>] [--title <タイトル名>] [--code]
65
+ ```
66
+
67
+ 引数なしで実行するとバージョン情報・開発者情報のインフォ画面が表示されます。
68
+
69
+ ### パラメータ(引数)詳細
70
+
71
+ | パラメータ | 短縮形 | 型 | 必須 | デフォルト値 | 説明 |
72
+ | :--- | :--- | :--- | :--- | :--- | :--- |
73
+ | `project_name` | - | string | **はい** | - | 作成するプロジェクトのフォルダ名 |
74
+ | `--mode` | `-m` | string | いいえ | `default` | テンプレートモード(`default` / `tailwind` / `router` / `empty`) |
75
+ | `--title` | `-t` | string | いいえ | `"Bun + TS App"` | `<title>` タグに埋め込むテキスト |
76
+ | `--code` | `-c` | flag | いいえ | - | セットアップ完了後に VS Code で開く |
77
+ | `--version` | `-v` | flag | いいえ | - | バージョンを表示 |
78
+ | `--help` | `-h` | flag | いいえ | - | ヘルプを表示 |
79
+
80
+ ---
81
+
82
+ ### コマンド実行例
83
+
84
+ ```bash
85
+ # 標準テンプレートで作成
86
+ tssetup my-app
87
+
88
+ # Tailwind CSS 組み込み + VS Code で開く
89
+ tssetup my-app --mode tailwind --code
90
+
91
+ # 自作 SPA ルーター構成
92
+ tssetup my-app --mode router --title "マイSPAサイト"
93
+
94
+ # 最小構成
95
+ tssetup my-app --mode empty
96
+ ```
97
+
98
+ ---
99
+
100
+ ## 📁 作成されるファイル構成
101
+
102
+ ```
103
+ my-app/
104
+ ├── src/
105
+ │ └── index.ts ← エントリポイント(--mode で内容が変わる)
106
+ ├── dist/ ← tsc が自動生成するJS出力先
107
+ ├── node_modules/ ← bun install が自動生成
108
+ ├── index.html ← フロントエンドのHTML
109
+ ├── server.ts ← ホットリロード対応の開発用Webサーバー
110
+ ├── tsconfig.json ← TypeScript コンパイラ設定
111
+ └── package.json ← Bun プロジェクト設定
112
+ ```
113
+
114
+ プロジェクト作成後は自動的にそのディレクトリへ移動します。そのまま `tsbuild` を実行すれば開発を始められます。
115
+
116
+ ---
117
+
118
+ ## 🔄 アップデート
119
+
120
+ ```bash
121
+ pip install --upgrade tssetup
122
+ ```
123
+
124
+ ---
125
+
126
+ ## ✉️ 問い合わせ先
127
+
128
+ - **X (旧Twitter):** [@Lapius7](https://x.com/Lapius7)
129
+ - **GitHub Issues:** [Lapius7/tssetup/issues](https://github.com/Lapius7/tssetup/issues)
130
+
131
+ ---
132
+
133
+ ## ⚠️ 免責事項
134
+
135
+ 本ソフトウェアの使用によって生じた直接的・間接的な損害について、作者は一切の責任を負いません。自己責任のもとでご使用ください。
136
+
137
+ ---
138
+
139
+ ## 📄 ライセンス & コピーライト
140
+
141
+ 本プロジェクトは [MIT License](https://opensource.org/licenses/MIT) のもとで公開されています。
142
+
143
+ Copyright (c) 2026 Lapius7
@@ -0,0 +1,125 @@
1
+ # tssetup
2
+
3
+ PowerShellやCMDから1コマンドで、Bun + TypeScript の高速なフロントエンド開発環境(ホットリロードサーバー内蔵)を初期構築するツールです。
4
+
5
+ ## 🚀 特徴
6
+
7
+ - **瞬時にプロジェクト開始:** フォルダ作成、`bun init`、`tsconfig.json` の設定、ホットリロードサーバースクリプトの作成を自動で一括実行します。
8
+ - **多彩な初期テンプレート:**
9
+ - `default`: Destyle.css を内包したシンプルな構成(ダークモード対応)。
10
+ - `tailwind`: CDN版 Tailwind CSS が即座に使えるレイアウト構成。
11
+ - `router`: ライブラリを使わずに HTML5 History API を利用した、自作の超軽量 SPA ルーティング構成。
12
+ - `empty`: 最少構成(空の TypeScript ファイルとシンプルな HTML)。
13
+ - **エディタ連携:** `--code` オプションを付けるだけで、作成したプロジェクトフォルダを VS Code で即座に開きます。
14
+ - **自動バージョン確認:** 実行時に最新バージョンを確認し、更新がある場合は通知します。
15
+
16
+ ---
17
+
18
+ ## 🛠️ インストール方法
19
+
20
+ **pip(推奨):**
21
+
22
+ ```bash
23
+ pip install tssetup
24
+ ```
25
+
26
+ > [!NOTE]
27
+ >
28
+ > - PowerShell・CMD・Windows Terminal どれからでも使えます。
29
+ > - 動作には **Python 3.9+** と **Bun** が必要です。
30
+
31
+ ---
32
+
33
+ ## ⚙️ 動作に必要な環境(依存関係)
34
+
35
+ | ツール | 用途 | インストール |
36
+ | :--- | :--- | :--- |
37
+ | **Python 3.9+** | tssetup 本体の実行 | [python.org](https://www.python.org/) |
38
+ | **Bun** | プロジェクト初期化・TSコンパイル | `powershell -c "irm bun.sh/install.ps1 \| iex"` |
39
+ | **VS Code** (任意) | `--code` オプション使用時 | [code.visualstudio.com](https://code.visualstudio.com/) |
40
+
41
+ ---
42
+
43
+ ## ⚡ 使い方と全パラメータ一覧
44
+
45
+ ```bash
46
+ tssetup <プロジェクト名> [--mode <モード名>] [--title <タイトル名>] [--code]
47
+ ```
48
+
49
+ 引数なしで実行するとバージョン情報・開発者情報のインフォ画面が表示されます。
50
+
51
+ ### パラメータ(引数)詳細
52
+
53
+ | パラメータ | 短縮形 | 型 | 必須 | デフォルト値 | 説明 |
54
+ | :--- | :--- | :--- | :--- | :--- | :--- |
55
+ | `project_name` | - | string | **はい** | - | 作成するプロジェクトのフォルダ名 |
56
+ | `--mode` | `-m` | string | いいえ | `default` | テンプレートモード(`default` / `tailwind` / `router` / `empty`) |
57
+ | `--title` | `-t` | string | いいえ | `"Bun + TS App"` | `<title>` タグに埋め込むテキスト |
58
+ | `--code` | `-c` | flag | いいえ | - | セットアップ完了後に VS Code で開く |
59
+ | `--version` | `-v` | flag | いいえ | - | バージョンを表示 |
60
+ | `--help` | `-h` | flag | いいえ | - | ヘルプを表示 |
61
+
62
+ ---
63
+
64
+ ### コマンド実行例
65
+
66
+ ```bash
67
+ # 標準テンプレートで作成
68
+ tssetup my-app
69
+
70
+ # Tailwind CSS 組み込み + VS Code で開く
71
+ tssetup my-app --mode tailwind --code
72
+
73
+ # 自作 SPA ルーター構成
74
+ tssetup my-app --mode router --title "マイSPAサイト"
75
+
76
+ # 最小構成
77
+ tssetup my-app --mode empty
78
+ ```
79
+
80
+ ---
81
+
82
+ ## 📁 作成されるファイル構成
83
+
84
+ ```
85
+ my-app/
86
+ ├── src/
87
+ │ └── index.ts ← エントリポイント(--mode で内容が変わる)
88
+ ├── dist/ ← tsc が自動生成するJS出力先
89
+ ├── node_modules/ ← bun install が自動生成
90
+ ├── index.html ← フロントエンドのHTML
91
+ ├── server.ts ← ホットリロード対応の開発用Webサーバー
92
+ ├── tsconfig.json ← TypeScript コンパイラ設定
93
+ └── package.json ← Bun プロジェクト設定
94
+ ```
95
+
96
+ プロジェクト作成後は自動的にそのディレクトリへ移動します。そのまま `tsbuild` を実行すれば開発を始められます。
97
+
98
+ ---
99
+
100
+ ## 🔄 アップデート
101
+
102
+ ```bash
103
+ pip install --upgrade tssetup
104
+ ```
105
+
106
+ ---
107
+
108
+ ## ✉️ 問い合わせ先
109
+
110
+ - **X (旧Twitter):** [@Lapius7](https://x.com/Lapius7)
111
+ - **GitHub Issues:** [Lapius7/tssetup/issues](https://github.com/Lapius7/tssetup/issues)
112
+
113
+ ---
114
+
115
+ ## ⚠️ 免責事項
116
+
117
+ 本ソフトウェアの使用によって生じた直接的・間接的な損害について、作者は一切の責任を負いません。自己責任のもとでご使用ください。
118
+
119
+ ---
120
+
121
+ ## 📄 ライセンス & コピーライト
122
+
123
+ 本プロジェクトは [MIT License](https://opensource.org/licenses/MIT) のもとで公開されています。
124
+
125
+ Copyright (c) 2026 Lapius7
@@ -0,0 +1,373 @@
1
+ # 💡 インストールを実行するPowerShellスクリプト
2
+ $functionName = "tssetup"
3
+ $functionCode = @'
4
+ function tssetup {
5
+ [CmdletBinding()]
6
+ param (
7
+ [Parameter(Mandatory = $false, Position = 0)][string]$ProjectName,
8
+ [Parameter(Mandatory = $false)][string]$Title = "Bun + TS App",
9
+ [Parameter(Mandatory = $false)]
10
+ [ValidateSet("default", "tailwind", "router", "empty")]
11
+ [string]$Mode = "default",
12
+ [Parameter(Mandatory = $false)][switch]$Help,
13
+ [Parameter(Mandatory = $false)][switch]$Code,
14
+ [Parameter(Mandatory = $false)][switch]$Uninstall
15
+ )
16
+
17
+ if ($Uninstall) {
18
+ Write-Host ""
19
+ Write-Host "⚠ tssetup をアンインストールします。本当によろしいですか? (y/N): " -NoNewline -ForegroundColor Yellow
20
+ $confirm = Read-Host
21
+ if ($confirm -ne "y" -and $confirm -ne "Y") {
22
+ Write-Host "キャンセルしました。" -ForegroundColor DarkGray
23
+ return
24
+ }
25
+ $profileContent = Get-Content $PROFILE -Raw -ErrorAction SilentlyContinue
26
+ $n = "tssetup"
27
+ if ($profileContent -match "# <<BEGIN:${n}>>") {
28
+ $profileContent = $profileContent -replace "(?s)`n?# <<BEGIN:${n}>>.*?# <<END:${n}>>", ""
29
+ [System.IO.File]::WriteAllText($PROFILE, $profileContent.Trim(), [System.Text.UTF8Encoding]::new($true))
30
+ Remove-Item Function:tssetup -ErrorAction SilentlyContinue
31
+ Write-Host "✅ tssetup をアンインストールしました。" -ForegroundColor Green
32
+ } else {
33
+ Write-Host "⚠ tssetup はインストールされていません。" -ForegroundColor Yellow
34
+ }
35
+ return
36
+ }
37
+
38
+ $localVersion = "1.0.1"
39
+ $remoteVersion = $null
40
+ try {
41
+ $remoteVersion = (irm https://raw.githubusercontent.com/Lapius7/tssetup/main/version.txt -TimeoutSec 3 -ErrorAction Stop).Trim()
42
+ if ($remoteVersion -ne $localVersion) {
43
+ Write-Host "🔄 新しいバージョン ($remoteVersion) があります。自動更新しています..." -ForegroundColor Yellow
44
+ irm https://raw.githubusercontent.com/Lapius7/tssetup/main/install.ps1 | iex
45
+ Write-Host "✅ 更新完了!もう一度コマンドを実行してください。" -ForegroundColor Green
46
+ return
47
+ }
48
+ } catch {}
49
+
50
+ if ([string]::IsNullOrEmpty($ProjectName) -and -not $Help) {
51
+ Write-Host ""
52
+ Write-Host "🎨 tssetup" -ForegroundColor Cyan -NoNewline; Write-Host " v$localVersion" -ForegroundColor DarkGray
53
+ Write-Host "Bun + TypeScript フロントエンド環境を1コマンドで構築するツール"
54
+ Write-Host ""
55
+ Write-Host "開発者 : " -NoNewline -ForegroundColor Yellow; Write-Host "Lapius7"
56
+ Write-Host "X : " -NoNewline -ForegroundColor Yellow; Write-Host "https://x.com/Lapius7"
57
+ Write-Host "GitHub : " -NoNewline -ForegroundColor Yellow; Write-Host "https://github.com/Lapius7/tssetup"
58
+ Write-Host ""
59
+ if ($null -ne $remoteVersion) {
60
+ if ($remoteVersion -eq $localVersion) {
61
+ Write-Host "バージョン : $localVersion " -NoNewline; Write-Host "✅ 最新です" -ForegroundColor Green
62
+ } else {
63
+ Write-Host "バージョン : $localVersion " -NoNewline; Write-Host "⬆ 最新: $remoteVersion" -ForegroundColor Yellow
64
+ }
65
+ } else {
66
+ Write-Host "バージョン : $localVersion " -NoNewline; Write-Host "(バージョン確認失敗)" -ForegroundColor DarkGray
67
+ }
68
+ Write-Host ""
69
+ Write-Host "使い方 : tssetup <プロジェクト名> [-Mode <モード>]" -ForegroundColor DarkGray
70
+ Write-Host "ヘルプ : tssetup -Help" -ForegroundColor DarkGray
71
+ Write-Host ""
72
+ return
73
+ }
74
+
75
+ if ($Help) {
76
+ Write-Host "`n🎨 [tssetup] コマンドヘルプ" -ForegroundColor Cyan
77
+ Write-Host "==================================================" -ForegroundColor DarkGray
78
+ Write-Host "簡単にBun + TypeScriptのフロントエンド開発環境を構築します。"
79
+ Write-Host "`n使い方:" -ForegroundColor Yellow
80
+ Write-Host " tssetup <プロジェクト名> [-Mode <モード名>] [-Title <タイトル>] [-Code]"
81
+ Write-Host "`nオプション:" -ForegroundColor Yellow
82
+ Write-Host " -Mode [default] : 標準テンプレート (Destyle.css内包・ダーク系)"
83
+ Write-Host " -Mode [tailwind] : Tailwind CSS 組み込み (即開発可能レイアウト)"
84
+ Write-Host " -Mode [router] : ⚡ライブラリ不使用・自作SPAルーティング構成"
85
+ Write-Host " -Mode [empty] : 最少構成(空のTSファイルとシンプルなHTML)"
86
+ Write-Host " -Title : HTMLの <title> タグに設定する文字列"
87
+ Write-Host " -Code : セットアップ完了後、新しいVS Codeのウィンドウで開く"
88
+ Write-Host " -Help : このヘルプを表示します"
89
+ Write-Host "`n📁 作成されるファイル構成:" -ForegroundColor Yellow
90
+ Write-Host " <プロジェクト名>/"
91
+ Write-Host " ├── src/"
92
+ Write-Host " │ └── index.ts " -NoNewline; Write-Host "← エントリポイント(モード別で内容が異なる)" -ForegroundColor DarkGray
93
+ Write-Host " ├── dist/ " -NoNewline; Write-Host "← コンパイル後JS(tsc が自動生成)" -ForegroundColor DarkGray
94
+ Write-Host " ├── node_modules/ " -NoNewline; Write-Host "← bun install が自動生成" -ForegroundColor DarkGray
95
+ Write-Host " ├── index.html " -NoNewline; Write-Host "← フロントエンドHTML" -ForegroundColor DarkGray
96
+ Write-Host " ├── server.ts " -NoNewline; Write-Host "← 開発サーバー(ホットリロード対応)" -ForegroundColor DarkGray
97
+ Write-Host " ├── tsconfig.json " -NoNewline; Write-Host "← TypeScript設定" -ForegroundColor DarkGray
98
+ Write-Host " └── package.json " -NoNewline; Write-Host "← Bunプロジェクト設定" -ForegroundColor DarkGray
99
+ Write-Host "==================================================`n" -ForegroundColor DarkGray
100
+ return
101
+ }
102
+
103
+ New-Item -ItemType Directory -Path "$ProjectName/src" -Force > $null
104
+ New-Item -ItemType Directory -Path "$ProjectName/dist" -Force > $null
105
+ Set-Location $ProjectName
106
+
107
+ Write-Host ""
108
+ Write-Host "🔨 " -NoNewline -ForegroundColor Cyan
109
+ Write-Host $ProjectName -NoNewline -ForegroundColor White
110
+ Write-Host " を構築中..." -ForegroundColor Cyan
111
+ Write-Host ""
112
+ Write-Host " 📁 src/ dist/ フォルダを作成しました" -ForegroundColor DarkGray
113
+
114
+ bun init -y > $null
115
+ if (Test-Path "index.ts") { Remove-Item "index.ts" -Force }
116
+ Write-Host " 📦 package.json" -NoNewline; Write-Host " (bun init)" -ForegroundColor DarkGray
117
+
118
+ $tsconfig = '{
119
+ "compilerOptions": {
120
+ "rootDir": "./src",
121
+ "outDir": "./dist",
122
+ "target": "es2020",
123
+ "module": "es2020",
124
+ "moduleResolution": "node",
125
+ "lib": ["es2020", "dom"],
126
+ "strict": true,
127
+ "skipLibCheck": true,
128
+ "isolatedModules": true
129
+ },
130
+ "include": ["src"]
131
+ }'
132
+ [System.IO.File]::WriteAllText((Join-Path (Get-Location) "tsconfig.json"), $tsconfig, [System.Text.UTF8Encoding]::new($false))
133
+ Write-Host " ✅ tsconfig.json" -ForegroundColor Green
134
+
135
+ $serverTs = 'import { watch, mkdirSync, existsSync } from "fs";
136
+ const DEFAULT_PORT = 53000;
137
+
138
+ async function findAvailablePort(startPort: number): Promise<number> {
139
+ let port = startPort;
140
+ while (port <= 65535) {
141
+ try {
142
+ const server = Bun.serve({ port, fetch() { return new Response(); } });
143
+ server.stop();
144
+ return port;
145
+ } catch { port++; }
146
+ }
147
+ return startPort;
148
+ }
149
+
150
+ const PORT = await findAvailablePort(DEFAULT_PORT);
151
+ const sockets = new Set();
152
+
153
+ const notifyReload = () => {
154
+ console.log("🔄 File changed! Reloading browser...");
155
+ for (const socket of sockets) { socket.send("reload"); }
156
+ };
157
+ if (!existsSync("./dist")) mkdirSync("./dist", { recursive: true });
158
+ watch("./dist", { recursive: true }, notifyReload);
159
+ watch("./index.html", notifyReload);
160
+
161
+ Bun.serve({
162
+ port: PORT,
163
+ fetch(req, server) {
164
+ const url = new URL(req.url);
165
+ if (url.pathname === "/ws") {
166
+ if (server.upgrade(req)) return;
167
+ return new Response("Upgrade failed", { status: 400 });
168
+ }
169
+ let filePath = "." + url.pathname;
170
+ if (filePath === "./") filePath = "./index.html";
171
+ try {
172
+ const file = Bun.file(filePath);
173
+ return new Response(file);
174
+ } catch {
175
+ try {
176
+ return new Response(Bun.file("./index.html"));
177
+ } catch {
178
+ return new Response("404 Not Found", { status: 404 });
179
+ }
180
+ }
181
+ },
182
+ websocket: {
183
+ open(ws) { sockets.add(ws); },
184
+ close(ws) { sockets.delete(ws); }
185
+ }
186
+ });
187
+ console.log(`🌍 Bun Live Server running at http://localhost:${PORT}`);
188
+ '
189
+ [System.IO.File]::WriteAllText((Join-Path (Get-Location) "server.ts"), $serverTs, [System.Text.UTF8Encoding]::new($false))
190
+ Write-Host " ✅ server.ts" -ForegroundColor Green
191
+
192
+ $cdn ="<link rel=""stylesheet"" href=""https://cdn.jsdelivr.net/npm/destyle.css@3.0.2/destyle.min.css"">"
193
+ $styles = " <style>`n body { font-family: 'Helvetica Neue', Arial, sans-serif; background-color: #0f172a; color: #f8fafc; display: grid; place-items: center; min-height: 100vh; margin: 0; }`n #app { text-align: center; }`n h1 { font-size: 2.5rem; font-weight: bold; margin-bottom: 1rem; background: linear-gradient(to right, #38bdf8, #818cf8); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }`n p { color: #94a3b8; font-size: 1.1rem; }`n code { background-color: #1e293b; padding: 0.2rem 0.4rem; border-radius: 0.25rem; color: #f43f5e; font-family: monospace; }`n </style>"
194
+ $bodyClass = ""
195
+ $bodyHtml = " <div id=""app""></div>"
196
+ $bq = [char]96
197
+
198
+ switch ($Mode) {
199
+ "empty" {
200
+ $cdn = ""; $styles = ""
201
+ $tsLines = @("console.log('Hello TypeScript!');")
202
+ }
203
+ "tailwind" {
204
+ $cdn = "<script src=""https://cdn.tailwindcss.com""></script>"
205
+ $styles = ""
206
+ $bodyClass = " class=""bg-slate-900 text-slate-100 min-h-screen grid place-items-center font-sans"""
207
+ $tsLines = @(
208
+ "const app = document.getElementById('app');"
209
+ "if (app) {"
210
+ " app.innerHTML = ${bq}"
211
+ " <div class='max-w-md p-8 bg-slate-800 rounded-2xl border border-slate-700 shadow-xl text-center'>"
212
+ " <h1 class='text-3xl font-black mb-3 bg-gradient-to-r from-sky-400 to-blue-500 bg-clip-text text-transparent'>Tailwind Ready</h1>"
213
+ " <p class='text-slate-400'>Edit <code class='bg-slate-950 text-rose-400 px-1.5 py-0.5 rounded text-sm font-mono'>src/index.ts</code> to start building your UI.</p>"
214
+ " </div>"
215
+ " ${bq};"
216
+ "}"
217
+ )
218
+ }
219
+ "router" {
220
+ $cdn = "<script src=""https://cdn.tailwindcss.com""></script>"
221
+ $styles = ""
222
+ $bodyClass = " class=""bg-slate-900 text-slate-100 min-h-screen font-sans flex flex-col"""
223
+ $bodyHtml = " `n <nav class='bg-slate-950 border-b border-slate-800 px-6 py-4 flex gap-6'>`n <a href='/' class='nav-link text-sky-400 font-bold hover:text-sky-300'>🏠 Home</a>`n <a href='/about' class='nav-link text-slate-400 font-bold hover:text-slate-200'>📄 About</a>`n <a href='/setting' class='nav-link text-slate-400 font-bold hover:text-slate-200'>⚙️ Setting</a>`n </nav>`n `n <main id='app' class='flex-1 grid place-items-center p-6'></main>"
224
+
225
+ $tsLines = @(
226
+ "// ⚡ 自作SPAルーターの基本実装"
227
+ "const routes: Record<string, string> = {"
228
+ " '/': '<div class=""text-center""><h1 class=""text-3xl font-bold text-sky-400 mb-2"">Home Page</h1><p class=""text-slate-400"">Welcome to the lightweight SPA initial template!</p></div>',"
229
+ " '/about': '<div class=""text-center""><h1 class=""text-3xl font-bold text-indigo-400 mb-2"">About Page</h1><p class=""text-slate-400"">This router operates using pure HTML5 History API.</p></div>',"
230
+ " '/setting': '<div class=""text-center""><h1 class=""text-3xl font-bold text-emerald-400 mb-2"">Setting Page</h1><p class=""text-slate-400"">Configure your system parameters here.</p></div>'"
231
+ "};"
232
+ ""
233
+ "const render = (path: string) => {"
234
+ " const app = document.getElementById('app');"
235
+ " if (app) app.innerHTML = routes[path] || '<h1 class=""text-2xl font-bold text-rose-500"">404 Not Found</h1>';"
236
+ " "
237
+ " document.querySelectorAll('.nav-link').forEach(link => {"
238
+ " const href = link.getAttribute('href');"
239
+ " if (href === path) {"
240
+ " link.classList.replace('text-slate-400', 'text-sky-400');"
241
+ " } else {"
242
+ " link.classList.replace('text-sky-400', 'text-slate-400');"
243
+ " }"
244
+ " });"
245
+ "};"
246
+ ""
247
+ "const navigate = (path: string) => {"
248
+ " window.history.pushState({}, '', path);"
249
+ " render(path);"
250
+ "};"
251
+ ""
252
+ "window.addEventListener('popstate', () => render(window.location.pathname));"
253
+ ""
254
+ "document.addEventListener('click', (e) => {"
255
+ " const target = e.target as HTMLElement;"
256
+ " if (target.matches('.nav-link')) {"
257
+ " e.preventDefault();"
258
+ " const href = target.getAttribute('href');"
259
+ " if (href) navigate(href);"
260
+ " }"
261
+ "});"
262
+ ""
263
+ "render(window.location.pathname);"
264
+ )
265
+ }
266
+ default {
267
+ $tsLines = @(
268
+ "const app = document.getElementById('app');"
269
+ "if (app) {"
270
+ " app.innerHTML = ${bq}"
271
+ " <h1>✨ Bun + TypeScript Environment</h1>"
272
+ " <p>Ready to develop! Edit <code>src/index.ts</code> to get started.</p>"
273
+ " ${bq};"
274
+ "}"
275
+ )
276
+ }
277
+ }
278
+
279
+ $utf8NoBom = [System.Text.UTF8Encoding]::new($false)
280
+ [System.IO.File]::WriteAllText((Join-Path (Get-Location) "src/index.ts"), ($tsLines -join "`n"), $utf8NoBom)
281
+ Write-Host " ✅ src/index.ts" -ForegroundColor Green
282
+ $html ="<!DOCTYPE html>`n<html lang=""ja"">`n<head>`n <meta charset=""UTF-8"">`n <meta name=""viewport"" content=""width=device-width, initial-scale=1.0"">`n <title>$Title</title>`n $cdn`n$styles`n</head>`n<body$bodyClass>`n$bodyHtml`n <script type=""module"" src=""./dist/index.js""></script>`n <script>`n const ws = new WebSocket('ws://' + location.host + '/ws');`n ws.onmessage = (e) => { if(e.data === 'reload') location.reload(); };`n </script>`n</body>`n</html>"
283
+ [System.IO.File]::WriteAllText((Join-Path (Get-Location) "index.html"), $html, $utf8NoBom)
284
+ Write-Host " ✅ index.html" -ForegroundColor Green
285
+
286
+ Write-Host ""
287
+ Write-Host " ──────────────────────────────────────────" -ForegroundColor DarkGray
288
+ Write-Host " ✨ " -NoNewline
289
+ Write-Host $ProjectName -NoNewline -ForegroundColor Cyan
290
+ Write-Host " セットアップ完了!" -ForegroundColor Green
291
+ Write-Host " ──────────────────────────────────────────" -ForegroundColor DarkGray
292
+ Write-Host ""
293
+ Write-Host " 📁 $ProjectName/" -ForegroundColor Yellow
294
+ Write-Host " ├── src/"
295
+ Write-Host " │ └── index.ts"
296
+ Write-Host " ├── dist/ " -NoNewline; Write-Host "← tsc が自動生成" -ForegroundColor DarkGray
297
+ Write-Host " ├── index.html"
298
+ Write-Host " ├── server.ts"
299
+ Write-Host " ├── tsconfig.json"
300
+ Write-Host " └── package.json"
301
+ Write-Host ""
302
+ Write-Host " 📂 $(Get-Location)" -ForegroundColor DarkGray
303
+ Write-Host ""
304
+ Write-Host " 🚀 次のステップ:" -ForegroundColor Cyan
305
+ Write-Host " tsbuild" -NoNewline -ForegroundColor White
306
+ Write-Host " 開発サーバーを起動" -ForegroundColor DarkGray
307
+ Write-Host ""
308
+ if ($Code -and (Get-Command code -ErrorAction SilentlyContinue)) { code . }
309
+ }
310
+ '@
311
+
312
+ if (!(Test-Path $PROFILE)) {
313
+ New-Item -Type File -Path $PROFILE -Force > $null
314
+ }
315
+
316
+ $profileContent = Get-Content $PROFILE -Raw -ErrorAction SilentlyContinue
317
+ if ([string]::IsNullOrEmpty($profileContent)) { $profileContent = "" }
318
+
319
+ $beginMarker = "# <<BEGIN:$functionName>>"
320
+ $endMarker = "# <<END:$functionName>>"
321
+ $markerPattern = "(?s)# <<BEGIN:$functionName>>.*# <<END:$functionName>>"
322
+
323
+ if ($profileContent -match $markerPattern) {
324
+ $profileContent = $profileContent -replace $markerPattern, ""
325
+ }
326
+ $profileContent = $profileContent.Replace("# <<END:$functionName>>", "").Trim()
327
+
328
+ if (-not ($profileContent -match "# <<BEGIN:$functionName>>") -and $profileContent -match "function\s+$functionName\b") {
329
+ Write-Host ""
330
+ Write-Host "⚠ 警告: マーカーのない旧バージョンの $functionName がプロファイルに存在します。" -ForegroundColor Yellow
331
+ Write-Host " 自動削除は行いません。以下を手動で実行してください:" -ForegroundColor Yellow
332
+ Write-Host ""
333
+ Write-Host " 1. メモ帳でプロファイルを開く:" -ForegroundColor Cyan
334
+ Write-Host " notepad `$PROFILE" -ForegroundColor White
335
+ Write-Host " 2. 'function $functionName' から始まるブロックを手動で削除する" -ForegroundColor Cyan
336
+ Write-Host " 3. 保存後にこのインストールコマンドを再実行する" -ForegroundColor Cyan
337
+ Write-Host ""
338
+ return
339
+ }
340
+
341
+ $block = "$beginMarker`n$functionCode`n$endMarker"
342
+ $newProfileContent = $profileContent.Trim() + "`n`n" + $block
343
+ [System.IO.File]::WriteAllText($PROFILE, $newProfileContent.Trim(), [System.Text.UTF8Encoding]::new($true))
344
+
345
+ # 即時反映のためにメモリ上の関数も更新する
346
+ Invoke-Expression $functionCode
347
+
348
+ Write-Host "✨ tssetup コマンドのインストール/更新が完了しました!" -ForegroundColor Green
349
+ Write-Host ""
350
+ Write-Host "📦 インストールコマンド(再インストール・更新時):" -ForegroundColor Cyan
351
+ Write-Host " irm https://raw.githubusercontent.com/Lapius7/tssetup/main/install.ps1 | iex"
352
+ Write-Host ""
353
+ Write-Host "📋 使い方:" -ForegroundColor Cyan
354
+ Write-Host " tssetup <プロジェクト名> [-Mode <モード>] [-Title <タイトル>] [-Code]"
355
+ Write-Host ""
356
+ Write-Host " tssetup myapp " -NoNewline; Write-Host "# 標準テンプレートで作成" -ForegroundColor DarkGray
357
+ Write-Host " tssetup myapp -Mode tailwind " -NoNewline; Write-Host "# Tailwind CSS 組み込み" -ForegroundColor DarkGray
358
+ Write-Host " tssetup myapp -Mode router " -NoNewline; Write-Host "# 自作SPA ルーター構成" -ForegroundColor DarkGray
359
+ Write-Host " tssetup myapp -Mode empty " -NoNewline; Write-Host "# 最小構成" -ForegroundColor DarkGray
360
+ Write-Host " tssetup myapp -Title ""My App"" -Code " -NoNewline; Write-Host "# タイトル指定 + VS Code 起動" -ForegroundColor DarkGray
361
+ Write-Host ""
362
+ Write-Host "📁 作成されるファイル構成:" -ForegroundColor Cyan
363
+ Write-Host " <プロジェクト名>/"
364
+ Write-Host " ├── src/"
365
+ Write-Host " │ └── index.ts " -NoNewline; Write-Host "← エントリポイント(モード別で内容が異なる)" -ForegroundColor DarkGray
366
+ Write-Host " ├── dist/ " -NoNewline; Write-Host "← コンパイル後JS(tsc が自動生成)" -ForegroundColor DarkGray
367
+ Write-Host " ├── node_modules/ " -NoNewline; Write-Host "← bun install が自動生成" -ForegroundColor DarkGray
368
+ Write-Host " ├── index.html " -NoNewline; Write-Host "← フロントエンドHTML" -ForegroundColor DarkGray
369
+ Write-Host " ├── server.ts " -NoNewline; Write-Host "← 開発サーバー(ホットリロード対応)" -ForegroundColor DarkGray
370
+ Write-Host " ├── tsconfig.json " -NoNewline; Write-Host "← TypeScript設定" -ForegroundColor DarkGray
371
+ Write-Host " └── package.json " -NoNewline; Write-Host "← Bunプロジェクト設定" -ForegroundColor DarkGray
372
+ Write-Host ""
373
+ Write-Host "新しいPowerShellウィンドウを開くか、 '. `$PROFILE' を実行して即時反映させてください。" -ForegroundColor Yellow
@@ -0,0 +1,28 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "tssetup"
7
+ version = "1.0.0"
8
+ description = "Bun + TypeScript フロントエンド環境を1コマンドで構築するツール"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Lapius7", email = "me@lapius7.com" }]
13
+ keywords = ["bun", "typescript", "frontend", "scaffold", "cli", "hot-reload"]
14
+ classifiers = [
15
+ "Environment :: Console",
16
+ "Intended Audience :: Developers",
17
+ "Programming Language :: Python :: 3",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Operating System :: Microsoft :: Windows",
20
+ "Topic :: Software Development :: Build Tools",
21
+ ]
22
+
23
+ [project.scripts]
24
+ tssetup = "tssetup.__main__:main"
25
+
26
+ [project.urls]
27
+ Homepage = "https://github.com/Lapius7/tssetup"
28
+ Issues = "https://github.com/Lapius7/tssetup/issues"
@@ -0,0 +1 @@
1
+ __version__ = "1.0.0"
@@ -0,0 +1,174 @@
1
+ import argparse
2
+ import os
3
+ import sys
4
+ import subprocess
5
+ import shutil
6
+ import urllib.request
7
+ from pathlib import Path
8
+ from typing import Optional
9
+ from . import __version__
10
+ from .templates import TSCONFIG, SERVER_TS, INDEX_TS, get_html
11
+
12
+ # --- Console setup (Windows: enable ANSI + force UTF-8) ---
13
+
14
+ def _setup_console():
15
+ if sys.platform == "win32":
16
+ try:
17
+ import ctypes
18
+ ctypes.windll.kernel32.SetConsoleMode(
19
+ ctypes.windll.kernel32.GetStdHandle(-11), 7)
20
+ except Exception:
21
+ pass
22
+ if hasattr(sys.stdout, "reconfigure"):
23
+ sys.stdout.reconfigure(encoding="utf-8", errors="replace")
24
+ sys.stderr.reconfigure(encoding="utf-8", errors="replace")
25
+
26
+ _setup_console()
27
+
28
+ R = "\033[0m"
29
+ CYAN = "\033[36m"
30
+ GREEN = "\033[32m"
31
+ YELLOW = "\033[33m"
32
+ WHITE = "\033[97m"
33
+ GRAY = "\033[90m"
34
+
35
+ def c(text, color): return f"{color}{text}{R}"
36
+
37
+
38
+ # --- Version check ---
39
+
40
+ def fetch_remote_version() -> Optional[str]:
41
+ try:
42
+ url = "https://raw.githubusercontent.com/Lapius7/tssetup/main/version.txt"
43
+ with urllib.request.urlopen(url, timeout=3) as r:
44
+ return r.read().decode().strip()
45
+ except Exception:
46
+ return None
47
+
48
+
49
+ # --- Info screen ---
50
+
51
+ def show_info(remote: Optional[str]):
52
+ print()
53
+ print(c("🎨 tssetup", CYAN) + c(f" v{__version__}", GRAY))
54
+ print("Bun + TypeScript フロントエンド環境を1コマンドで構築するツール")
55
+ print()
56
+ print(c("開発者 : ", YELLOW) + "Lapius7")
57
+ print(c("X : ", YELLOW) + "https://x.com/Lapius7")
58
+ print(c("GitHub : ", YELLOW) + "https://github.com/Lapius7/tssetup")
59
+ print()
60
+ if remote is None:
61
+ print(f"バージョン : {__version__} " + c("(バージョン確認失敗)", GRAY))
62
+ elif remote == __version__:
63
+ print(f"バージョン : {__version__} " + c("✅ 最新です", GREEN))
64
+ else:
65
+ print(f"バージョン : {__version__} " + c(f"⬆ 最新: {remote}", YELLOW))
66
+ print()
67
+ print(c("使い方 : tssetup <プロジェクト名> [--mode <モード>]", GRAY))
68
+ print(c("ヘルプ : tssetup --help", GRAY))
69
+ print()
70
+
71
+
72
+ # --- Project creation ---
73
+
74
+ def create_project(name: str, mode: str, title: str, code: bool):
75
+ project_dir = Path(name)
76
+
77
+ print()
78
+ print(c("🔨 ", CYAN) + c(name, WHITE) + c(" を構築中...", CYAN))
79
+ print()
80
+
81
+ (project_dir / "src").mkdir(parents=True, exist_ok=True)
82
+ (project_dir / "dist").mkdir(parents=True, exist_ok=True)
83
+ print(c(" 📁 src/ dist/ フォルダを作成しました", GRAY))
84
+
85
+ os.chdir(project_dir)
86
+
87
+ subprocess.run(["bun", "init", "-y"], capture_output=True)
88
+ stale = Path("index.ts")
89
+ if stale.exists():
90
+ stale.unlink()
91
+ print(" 📦 package.json" + c(" (bun init)", GRAY))
92
+
93
+ Path("tsconfig.json").write_text(TSCONFIG, encoding="utf-8")
94
+ print(c(" ✅ tsconfig.json", GREEN))
95
+
96
+ Path("server.ts").write_text(SERVER_TS, encoding="utf-8")
97
+ print(c(" ✅ server.ts", GREEN))
98
+
99
+ Path("src/index.ts").write_text(INDEX_TS[mode], encoding="utf-8")
100
+ print(c(" ✅ src/index.ts", GREEN))
101
+
102
+ Path("index.html").write_text(get_html(mode, title), encoding="utf-8")
103
+ print(c(" ✅ index.html", GREEN))
104
+
105
+ print()
106
+ print(c(" ──────────────────────────────────────────", GRAY))
107
+ print(" ✨ " + c(name, CYAN) + c(" セットアップ完了!", GREEN))
108
+ print(c(" ──────────────────────────────────────────", GRAY))
109
+ print()
110
+ print(c(f" 📁 {name}/", YELLOW))
111
+ print(" ├── src/")
112
+ print(" │ └── index.ts")
113
+ print(" ├── dist/ " + c("← tsc が自動生成", GRAY))
114
+ print(" ├── index.html")
115
+ print(" ├── server.ts")
116
+ print(" ├── tsconfig.json")
117
+ print(" └── package.json")
118
+ print()
119
+ print(c(f" 📂 {Path.cwd()}", GRAY))
120
+ print()
121
+ print(c(" 🚀 次のステップ:", CYAN))
122
+ print(" " + c("tsbuild", WHITE) + c(" 開発サーバーを起動", GRAY))
123
+ print()
124
+
125
+ if code and shutil.which("code"):
126
+ subprocess.run(["code", "."])
127
+
128
+
129
+ # --- Entry point ---
130
+
131
+ def main():
132
+ parser = argparse.ArgumentParser(
133
+ prog="tssetup",
134
+ description="Bun + TypeScript フロントエンド環境を1コマンドで構築するツール",
135
+ add_help=False,
136
+ )
137
+ parser.add_argument("project_name", nargs="?", help="作成するプロジェクトのフォルダ名")
138
+ parser.add_argument("--mode", "-m",
139
+ choices=["default", "tailwind", "router", "empty"],
140
+ default="default",
141
+ help="テンプレートモード (default/tailwind/router/empty)")
142
+ parser.add_argument("--title", "-t", default="Bun + TS App",
143
+ help="HTMLの <title> タグに埋め込む文字列")
144
+ parser.add_argument("--code", "-c", action="store_true",
145
+ help="セットアップ完了後に VS Code で開く")
146
+ parser.add_argument("--version", "-v", action="store_true",
147
+ help="バージョンを表示")
148
+ parser.add_argument("--help", "-h", action="store_true",
149
+ help="ヘルプを表示")
150
+
151
+ args = parser.parse_args()
152
+
153
+ if args.version:
154
+ print(f"tssetup v{__version__}")
155
+ return
156
+
157
+ remote = fetch_remote_version()
158
+
159
+ if remote and remote != __version__:
160
+ print()
161
+ print(c(f"🔄 新しいバージョン ({remote}) があります。pip install --upgrade tssetup で更新できます。", YELLOW))
162
+
163
+ if args.help or not args.project_name:
164
+ if not args.project_name:
165
+ show_info(remote)
166
+ if args.help or not args.project_name:
167
+ parser.print_help()
168
+ return
169
+
170
+ create_project(args.project_name, args.mode, args.title, args.code)
171
+
172
+
173
+ if __name__ == "__main__":
174
+ main()
@@ -0,0 +1,180 @@
1
+ TSCONFIG = '''{
2
+ "compilerOptions": {
3
+ "rootDir": "./src",
4
+ "outDir": "./dist",
5
+ "target": "es2020",
6
+ "module": "es2020",
7
+ "moduleResolution": "node",
8
+ "lib": ["es2020", "dom"],
9
+ "strict": true,
10
+ "skipLibCheck": true,
11
+ "isolatedModules": true
12
+ },
13
+ "include": ["src"]
14
+ }'''
15
+
16
+ SERVER_TS = '''import { watch, mkdirSync, existsSync } from "fs";
17
+ const DEFAULT_PORT = 53000;
18
+
19
+ async function findAvailablePort(startPort: number): Promise<number> {
20
+ let port = startPort;
21
+ while (port <= 65535) {
22
+ try {
23
+ const server = Bun.serve({ port, fetch() { return new Response(); } });
24
+ server.stop();
25
+ return port;
26
+ } catch { port++; }
27
+ }
28
+ return startPort;
29
+ }
30
+
31
+ const PORT = await findAvailablePort(DEFAULT_PORT);
32
+ const sockets = new Set<any>();
33
+
34
+ const notifyReload = () => {
35
+ console.log("\\u{1F504} File changed! Reloading browser...");
36
+ for (const socket of sockets) { socket.send("reload"); }
37
+ };
38
+
39
+ if (!existsSync("./dist")) mkdirSync("./dist", { recursive: true });
40
+ watch("./dist", { recursive: true }, notifyReload);
41
+ watch("./index.html", notifyReload);
42
+
43
+ Bun.serve({
44
+ port: PORT,
45
+ fetch(req: Request, server: any) {
46
+ const url = new URL(req.url);
47
+ if (url.pathname === "/ws") {
48
+ if (server.upgrade(req)) return;
49
+ return new Response("Upgrade failed", { status: 400 });
50
+ }
51
+ let filePath = "." + url.pathname;
52
+ if (filePath === "./") filePath = "./index.html";
53
+ try {
54
+ return new Response(Bun.file(filePath));
55
+ } catch {
56
+ try { return new Response(Bun.file("./index.html")); }
57
+ catch { return new Response("404 Not Found", { status: 404 }); }
58
+ }
59
+ },
60
+ websocket: {
61
+ open(ws: any) { sockets.add(ws); },
62
+ close(ws: any) { sockets.delete(ws); }
63
+ }
64
+ });
65
+ console.log(`\\u{1F30D} Bun Live Server running at http://localhost:${PORT}`);
66
+ '''
67
+
68
+ INDEX_TS = {
69
+ "default": '''const app = document.getElementById('app');
70
+ if (app) {
71
+ app.innerHTML = `
72
+ <h1>✨ Bun + TypeScript Environment</h1>
73
+ <p>Ready to develop! Edit <code>src/index.ts</code> to get started.</p>
74
+ `;
75
+ }''',
76
+
77
+ "tailwind": '''const app = document.getElementById('app');
78
+ if (app) {
79
+ app.innerHTML = `
80
+ <div class='max-w-md p-8 bg-slate-800 rounded-2xl border border-slate-700 shadow-xl text-center'>
81
+ <h1 class='text-3xl font-black mb-3 bg-gradient-to-r from-sky-400 to-blue-500 bg-clip-text text-transparent'>Tailwind Ready</h1>
82
+ <p class='text-slate-400'>Edit <code class='bg-slate-950 text-rose-400 px-1.5 py-0.5 rounded text-sm font-mono'>src/index.ts</code> to start building your UI.</p>
83
+ </div>
84
+ `;
85
+ }''',
86
+
87
+ "router": '''// ⚡ 自作SPAルーターの基本実装
88
+ const routes: Record<string, string> = {
89
+ '/': '<div class="text-center"><h1 class="text-3xl font-bold text-sky-400 mb-2">Home Page</h1><p class="text-slate-400">Welcome to the lightweight SPA initial template!</p></div>',
90
+ '/about': '<div class="text-center"><h1 class="text-3xl font-bold text-indigo-400 mb-2">About Page</h1><p class="text-slate-400">This router operates using pure HTML5 History API.</p></div>',
91
+ '/setting': '<div class="text-center"><h1 class="text-3xl font-bold text-emerald-400 mb-2">Setting Page</h1><p class="text-slate-400">Configure your system parameters here.</p></div>'
92
+ };
93
+
94
+ const render = (path: string) => {
95
+ const app = document.getElementById('app');
96
+ if (app) app.innerHTML = routes[path] || '<h1 class="text-2xl font-bold text-rose-500">404 Not Found</h1>';
97
+ document.querySelectorAll('.nav-link').forEach(link => {
98
+ const href = link.getAttribute('href');
99
+ link.classList.toggle('text-sky-400', href === path);
100
+ link.classList.toggle('text-slate-400', href !== path);
101
+ });
102
+ };
103
+
104
+ const navigate = (path: string) => { window.history.pushState({}, '', path); render(path); };
105
+
106
+ window.addEventListener('popstate', () => render(window.location.pathname));
107
+ document.addEventListener('click', (e) => {
108
+ const target = e.target as HTMLElement;
109
+ if (target.matches('.nav-link')) {
110
+ e.preventDefault();
111
+ const href = target.getAttribute('href');
112
+ if (href) navigate(href);
113
+ }
114
+ });
115
+
116
+ render(window.location.pathname);''',
117
+
118
+ "empty": "console.log('Hello TypeScript!');",
119
+ }
120
+
121
+ DESTYLE_CDN = '<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/destyle.css@3.0.2/destyle.min.css">'
122
+ TAILWIND_CDN = '<script src="https://cdn.tailwindcss.com"></script>'
123
+
124
+ DEFAULT_STYLES = """ <style>
125
+ body { font-family: 'Helvetica Neue', Arial, sans-serif; background-color: #0f172a; color: #f8fafc; display: grid; place-items: center; min-height: 100vh; margin: 0; }
126
+ #app { text-align: center; }
127
+ h1 { font-size: 2.5rem; font-weight: bold; margin-bottom: 1rem; background: linear-gradient(to right, #38bdf8, #818cf8); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
128
+ p { color: #94a3b8; font-size: 1.1rem; }
129
+ code { background-color: #1e293b; padding: 0.2rem 0.4rem; border-radius: 0.25rem; color: #f43f5e; font-family: monospace; }
130
+ </style>"""
131
+
132
+ HOTRELOAD_SCRIPT = """ <script>
133
+ const ws = new WebSocket('ws://' + location.host + '/ws');
134
+ ws.onmessage = (e) => { if(e.data === 'reload') location.reload(); };
135
+ </script>"""
136
+
137
+
138
+ def get_html(mode: str, title: str) -> str:
139
+ if mode == "tailwind":
140
+ cdn = TAILWIND_CDN
141
+ styles = ""
142
+ body_class = ' class="bg-slate-900 text-slate-100 min-h-screen grid place-items-center font-sans"'
143
+ body_content = ' <div id="app"></div>'
144
+ elif mode == "router":
145
+ cdn = TAILWIND_CDN
146
+ styles = ""
147
+ body_class = ' class="bg-slate-900 text-slate-100 min-h-screen font-sans flex flex-col"'
148
+ body_content = """ <nav class="bg-slate-950 border-b border-slate-800 px-6 py-4 flex gap-6">
149
+ <a href="/" class="nav-link text-sky-400 font-bold hover:text-sky-300">\U0001f3e0 Home</a>
150
+ <a href="/about" class="nav-link text-slate-400 font-bold hover:text-slate-200">\U0001f4c4 About</a>
151
+ <a href="/setting" class="nav-link text-slate-400 font-bold hover:text-slate-200">⚙️ Setting</a>
152
+ </nav>
153
+ <main id="app" class="flex-1 grid place-items-center p-6"></main>"""
154
+ elif mode == "empty":
155
+ cdn = ""
156
+ styles = ""
157
+ body_class = ""
158
+ body_content = ' <div id="app"></div>'
159
+ else: # default
160
+ cdn = DESTYLE_CDN
161
+ styles = DEFAULT_STYLES
162
+ body_class = ""
163
+ body_content = ' <div id="app"></div>'
164
+
165
+ cdn_line = f"\n {cdn}" if cdn else ""
166
+ styles_line = f"\n{styles}" if styles else ""
167
+
168
+ return f"""<!DOCTYPE html>
169
+ <html lang="ja">
170
+ <head>
171
+ <meta charset="UTF-8">
172
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
173
+ <title>{title}</title>{cdn_line}{styles_line}
174
+ </head>
175
+ <body{body_class}>
176
+ {body_content}
177
+ <script type="module" src="./dist/index.js"></script>
178
+ {HOTRELOAD_SCRIPT}
179
+ </body>
180
+ </html>"""
@@ -0,0 +1 @@
1
+ 1.0.0