@refai.code/vite-boost 0.1.0 → 0.1.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/LICENSE +21 -0
- package/README.md +66 -0
- package/bin/vite-boost +456 -93
- package/package.json +21 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Refai.Code
|
|
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,66 @@
|
|
|
1
|
+
# @refai.code/vite-boost
|
|
2
|
+
|
|
3
|
+
CLI tool to supercharge **Vite Vanilla JavaScript** projects.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 🚀 What it does
|
|
8
|
+
`vite-boost` helps you quickly scaffold and enhance a **Vite Vanilla JS** project with a clean architecture and optional features.
|
|
9
|
+
|
|
10
|
+
### It can:
|
|
11
|
+
- Create a clean project structure:
|
|
12
|
+
- `router/`, `pages/`, `components/`, `services/`, `utils/`, `styles/`
|
|
13
|
+
- Optionally add:
|
|
14
|
+
- 🎨 TailwindCSS
|
|
15
|
+
- 📡 Axios (Fetch is default)
|
|
16
|
+
- 📱 PWA support
|
|
17
|
+
- 🗄️ JSON Server (`db.json` + dev scripts)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 📦 Requirements
|
|
22
|
+
- Node.js **>= 18**
|
|
23
|
+
- A **Vite Vanilla JavaScript** project
|
|
24
|
+
(must contain: `package.json`, `index.html`, `src/`)
|
|
25
|
+
|
|
26
|
+
> ❌ Not intended for React or TypeScript templates.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## ▶️ Usage (recommended)
|
|
31
|
+
|
|
32
|
+
Inside your Vite project folder:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm i -D @refai.code/vite-boost
|
|
36
|
+
npx vite-boost
|
|
37
|
+
```
|
|
38
|
+
## ▶️ Run without installing
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx @refai.code/vite-boost
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## 🏃 After running
|
|
45
|
+
|
|
46
|
+
### Start dev server:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm run dev
|
|
50
|
+
```
|
|
51
|
+
### If you enabled JSON Server:
|
|
52
|
+
```bash
|
|
53
|
+
npm run dev:full
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## ⚠️ Important Notes
|
|
57
|
+
|
|
58
|
+
- This tool targets Vanilla JavaScript only
|
|
59
|
+
- Some files under src/ may be overwritten
|
|
60
|
+
- Existing files are backed up before modification when possible
|
|
61
|
+
- Safe to re-run (will not blindly duplicate router code)
|
|
62
|
+
|
|
63
|
+
## 🧑💻 Author
|
|
64
|
+
|
|
65
|
+
Created by **Refai.Code**
|
|
66
|
+
CLI & tooling for modern Vite projects
|
package/bin/vite-boost
CHANGED
|
@@ -11,54 +11,216 @@ set -u
|
|
|
11
11
|
# - Real JSON Server Auth (Users save to db.json)
|
|
12
12
|
# - Concurrent Run (Vite + JSON Server)
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
# Handle exit vs return safely
|
|
15
|
+
terminate() {
|
|
16
|
+
local code="${1:-0}"
|
|
17
|
+
# Check if we are being sourced
|
|
18
|
+
if [[ "$ZSH_EVAL_CONTEXT" == "toplevel" || "$ZSH_EVAL_CONTEXT" == "shfunc" ]]; then
|
|
19
|
+
return "$code" 2>/dev/null || exit "$code"
|
|
20
|
+
else
|
|
21
|
+
exit "$code"
|
|
22
|
+
fi
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
die() { print -P "%F{red}❌ $*%f" >&2; terminate 1; }
|
|
26
|
+
warn() { print -P "%F{yellow}⚠️ $*%f" >&2; }
|
|
27
|
+
ok() { print -P "%F{green}✅ $*%f"; }
|
|
28
|
+
|
|
29
|
+
VERSION="0.1.0"
|
|
30
|
+
|
|
31
|
+
# -----------------------------
|
|
32
|
+
# Help / Version
|
|
33
|
+
# -----------------------------
|
|
34
|
+
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
|
|
35
|
+
cat <<'EOF'
|
|
36
|
+
vite-boost (Refai.Code)
|
|
37
|
+
|
|
38
|
+
Usage:
|
|
39
|
+
npx vite-boost
|
|
40
|
+
npx vite-boost --help
|
|
41
|
+
npx vite-boost --version
|
|
42
|
+
|
|
43
|
+
Notes:
|
|
44
|
+
- Run inside a Vite Vanilla JS project folder (package.json, index.html, src/)
|
|
45
|
+
EOF
|
|
46
|
+
terminate 0
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
if [[ "${1:-}" == "--version" || "${1:-}" == "-v" ]]; then
|
|
50
|
+
print "vite-boost v${VERSION}"
|
|
51
|
+
terminate 0
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
print ""
|
|
55
|
+
print "🚀 \033[1;36mVite Boost: Refai.Code Final Edition\033[0m"
|
|
56
|
+
print ""
|
|
15
57
|
|
|
58
|
+
# -----------------------------
|
|
59
|
+
# Validation: Must be in a Vite project folder
|
|
60
|
+
# -----------------------------
|
|
16
61
|
# --- Validation: Check if it's a Vite project ---
|
|
17
|
-
if [ ! -f
|
|
18
|
-
|
|
19
|
-
|
|
62
|
+
if [[ ! -f package.json ]]; then
|
|
63
|
+
print -u2 "❌ Error: package.json not found. Are you in the right directory?"
|
|
64
|
+
terminate 1
|
|
20
65
|
fi
|
|
21
66
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if read -q "choice?This doesn't look like a Vite project. Continue anyway? (y/N) "; then
|
|
67
|
+
if ! grep -q '"vite"' package.json 2>/dev/null; then
|
|
68
|
+
print -u2 "⚠️ Warning: Vite not found in package.json."
|
|
69
|
+
if ! read -k 1 "choice?This doesn't look like a Vite project. Continue anyway? (y/N) "; then
|
|
26
70
|
echo ""
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return 1
|
|
71
|
+
print -u2 "❌ Setup cancelled."
|
|
72
|
+
terminate 1
|
|
30
73
|
fi
|
|
74
|
+
echo ""
|
|
31
75
|
fi
|
|
32
|
-
|
|
33
|
-
|
|
76
|
+
ok "Proceeding..."
|
|
77
|
+
print ""
|
|
34
78
|
|
|
35
79
|
# --- 1. Interactive Prompts ---
|
|
36
80
|
ask() {
|
|
37
|
-
|
|
38
|
-
|
|
81
|
+
print -n "📦 Install $1? (y/N) "
|
|
82
|
+
read -k 1 choice
|
|
83
|
+
echo ""
|
|
84
|
+
if [[ "$choice" == [Yy] ]]; then
|
|
85
|
+
print " ✅ Selected $1"
|
|
39
86
|
eval "$2=true"
|
|
40
87
|
else
|
|
41
|
-
|
|
88
|
+
print " ❌ Skipped $1"
|
|
42
89
|
eval "$2=false"
|
|
43
90
|
fi
|
|
44
|
-
|
|
91
|
+
print ""
|
|
45
92
|
}
|
|
46
93
|
|
|
47
94
|
# Helper: Check file overwrite
|
|
95
|
+
# returns: 0 always (never trips set -e)
|
|
96
|
+
# sets global var: OVERWRITE_OK=true/false
|
|
48
97
|
check_overwrite() {
|
|
49
|
-
local file
|
|
50
|
-
if [ -f "$file" ]; then
|
|
51
|
-
|
|
52
|
-
|
|
98
|
+
local file="$1"
|
|
99
|
+
if [[ -f "$file" ]]; then
|
|
100
|
+
print -n "⚠️ $file already exists. Overwrite? (y/N) "
|
|
101
|
+
read -k 1 choice
|
|
102
|
+
echo ""
|
|
103
|
+
if [[ "$choice" == [Yy] ]]; then
|
|
53
104
|
return 0 # Yes, overwrite
|
|
54
105
|
else
|
|
55
|
-
|
|
106
|
+
print " ⏭️ Skipped $file"
|
|
56
107
|
return 1 # No, skip
|
|
57
108
|
fi
|
|
58
109
|
fi
|
|
59
|
-
return 0
|
|
110
|
+
return 0
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
# write file with preview diff + choices
|
|
114
|
+
write_file() {
|
|
115
|
+
local file="$1"
|
|
116
|
+
local tmp dir
|
|
117
|
+
tmp="$(mktemp)"
|
|
118
|
+
|
|
119
|
+
cat > "$tmp"
|
|
120
|
+
|
|
121
|
+
dir="$(dirname -- "$file")"
|
|
122
|
+
mkdir -p "$dir"
|
|
123
|
+
|
|
124
|
+
# if file doesn't exist, optionally still ask
|
|
125
|
+
if [[ ! -f "$file" ]]; then
|
|
126
|
+
if [[ "${ALWAYS_CONFIRM_OVERWRITE:-false}" == "true" ]]; then
|
|
127
|
+
if [[ "$choice" == [Nn] ]]; then
|
|
128
|
+
rm -f "$tmp"
|
|
129
|
+
warn "Skipped $file"
|
|
130
|
+
return 0 # Continue script
|
|
131
|
+
fi
|
|
132
|
+
fi
|
|
133
|
+
mv "$tmp" "$file"
|
|
134
|
+
ok "Created $file"
|
|
135
|
+
return 0
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
# same content => do nothing
|
|
139
|
+
if cmp -s "$tmp" "$file" 2>/dev/null; then
|
|
140
|
+
rm -f "$tmp"
|
|
141
|
+
ok "Unchanged $file"
|
|
142
|
+
return 0
|
|
143
|
+
fi
|
|
144
|
+
|
|
145
|
+
while true; do
|
|
146
|
+
print -n "⚠️ ${file} exists. [y] overwrite / [n] skip / [d] diff (n): "
|
|
147
|
+
local ans; IFS= read -r ans
|
|
148
|
+
ans="${ans:l}"
|
|
149
|
+
case "$ans" in
|
|
150
|
+
y|yes)
|
|
151
|
+
mv "$tmp" "$file"
|
|
152
|
+
ok "Overwritten $file"
|
|
153
|
+
return 0
|
|
154
|
+
;;
|
|
155
|
+
d|diff)
|
|
156
|
+
if command -v git >/dev/null 2>&1; then
|
|
157
|
+
git --no-pager diff --no-index -- "$file" "$tmp" || true
|
|
158
|
+
else
|
|
159
|
+
diff -u -- "$file" "$tmp" || true
|
|
160
|
+
fi
|
|
161
|
+
;;
|
|
162
|
+
""|n|no)
|
|
163
|
+
rm -f "$tmp"
|
|
164
|
+
warn "Skipped $file"
|
|
165
|
+
return 0 # Continue script
|
|
166
|
+
;;
|
|
167
|
+
*)
|
|
168
|
+
warn "Choose y / n / d"
|
|
169
|
+
;;
|
|
170
|
+
esac
|
|
171
|
+
done
|
|
60
172
|
}
|
|
173
|
+
upsert_block() {
|
|
174
|
+
local file="$1"
|
|
175
|
+
local start="$2"
|
|
176
|
+
local end="$3"
|
|
177
|
+
local tmp
|
|
178
|
+
tmp="$(mktemp)"
|
|
179
|
+
|
|
180
|
+
# Read stdin into tmp (the new block content)
|
|
181
|
+
cat > "$tmp"
|
|
182
|
+
|
|
183
|
+
# If file doesn't exist, just write it
|
|
184
|
+
if [[ ! -f "$file" ]]; then
|
|
185
|
+
mkdir -p "${file:h}"
|
|
186
|
+
cat "$tmp" > "$file"
|
|
187
|
+
rm -f "$tmp"
|
|
188
|
+
return 0
|
|
189
|
+
fi
|
|
190
|
+
|
|
191
|
+
# If markers exist, replace block
|
|
192
|
+
if grep -qF "$start" "$file" && grep -qF "$end" "$file"; then
|
|
193
|
+
# keep everything before start + new block + everything after end
|
|
194
|
+
awk -v s="$start" -v e="$end" '
|
|
195
|
+
BEGIN{in=0}
|
|
196
|
+
index($0,s){print; in=1; next}
|
|
197
|
+
index($0,e){in=0; print; next}
|
|
198
|
+
in==0{print}
|
|
199
|
+
' "$file" > "${file}.tmp"
|
|
200
|
+
|
|
201
|
+
# Insert new content between markers
|
|
202
|
+
awk -v s="$start" -v e="$end" '
|
|
203
|
+
{print}
|
|
204
|
+
index($0,s){
|
|
205
|
+
while ((getline line < "'"$tmp"'") > 0) print line
|
|
206
|
+
close("'"$tmp"'")
|
|
207
|
+
}
|
|
208
|
+
' "${file}.tmp" > "$file"
|
|
209
|
+
|
|
210
|
+
rm -f "${file}.tmp" "$tmp"
|
|
211
|
+
else
|
|
212
|
+
# markers not found => append safely once
|
|
213
|
+
cat >> "$file" <<EOF
|
|
61
214
|
|
|
215
|
+
$start
|
|
216
|
+
EOF
|
|
217
|
+
cat "$tmp" >> "$file"
|
|
218
|
+
cat >> "$file" <<EOF
|
|
219
|
+
$end
|
|
220
|
+
EOF
|
|
221
|
+
rm -f "$tmp"
|
|
222
|
+
fi
|
|
223
|
+
}
|
|
62
224
|
# Defaults
|
|
63
225
|
tail=false
|
|
64
226
|
expr=false
|
|
@@ -77,13 +239,14 @@ ask "JSON Server (Real Database & Auth)" json
|
|
|
77
239
|
# ==========================================
|
|
78
240
|
# 🛠️ Architecture Setup
|
|
79
241
|
# ==========================================
|
|
80
|
-
|
|
242
|
+
print ""
|
|
243
|
+
print "📁 Creating project structure..."
|
|
81
244
|
mkdir -p src/{components,pages,router,styles,services,utils,assets}
|
|
82
245
|
|
|
83
246
|
# -----------------------------
|
|
84
247
|
# 1. Utilities
|
|
85
248
|
# -----------------------------
|
|
86
|
-
|
|
249
|
+
print "\n🔧 Generating utility files..."
|
|
87
250
|
if check_overwrite "src/utils/helpers.js"; then
|
|
88
251
|
cat > src/utils/helpers.js <<'EOF'
|
|
89
252
|
export const sanitize = (str) => {
|
|
@@ -379,7 +542,7 @@ EOF
|
|
|
379
542
|
# -----------------------------
|
|
380
543
|
# 4. Styles (Glass, Spinner, Cards)
|
|
381
544
|
# -----------------------------
|
|
382
|
-
|
|
545
|
+
write_file "src/styles/main.css" <<'EOF'
|
|
383
546
|
:root {
|
|
384
547
|
--primary: #4f46e5;
|
|
385
548
|
--bg: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
|
@@ -1097,20 +1260,20 @@ EOF
|
|
|
1097
1260
|
# -----------------------------
|
|
1098
1261
|
# 7. Main Entry Point
|
|
1099
1262
|
# -----------------------------
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
# Append router imports and setup
|
|
1111
|
-
cat >> src/main.js <<'EOF'
|
|
1263
|
+
START_MARK='// ===== Refai.Code Router Setup (Auto-generated) ====='
|
|
1264
|
+
END_MARK='// ===== End Refai.Code Router Setup ====='
|
|
1265
|
+
|
|
1266
|
+
router_block() {
|
|
1267
|
+
local pwa_import=""
|
|
1268
|
+
local pwa_init=""
|
|
1269
|
+
if [[ "$pwa" == "true" ]]; then
|
|
1270
|
+
pwa_import="import { registerPWA } from './pwa/register.js';"
|
|
1271
|
+
pwa_init="registerPWA();"
|
|
1272
|
+
fi
|
|
1112
1273
|
|
|
1113
|
-
|
|
1274
|
+
cat <<EOF
|
|
1275
|
+
import './styles/main.css';
|
|
1276
|
+
$pwa_import
|
|
1114
1277
|
import { LiteRouter } from './router/LiteRouter.js';
|
|
1115
1278
|
import { Layout } from './components/Layout.js';
|
|
1116
1279
|
import { Navbar } from './components/Navbar.js';
|
|
@@ -1137,47 +1300,76 @@ authState.subscribe(() => {
|
|
|
1137
1300
|
if (el) el.innerHTML = Navbar();
|
|
1138
1301
|
});
|
|
1139
1302
|
|
|
1140
|
-
|
|
1141
|
-
app.init();
|
|
1303
|
+
$pwa_init
|
|
1304
|
+
new LiteRouter(routes, '#app').init();
|
|
1142
1305
|
EOF
|
|
1143
|
-
|
|
1144
|
-
echo "\n⏭️ Skipped modifying main.js"
|
|
1145
|
-
fi
|
|
1146
|
-
else
|
|
1147
|
-
# File doesn't exist, create new one
|
|
1148
|
-
cat > src/main.js <<'EOF'
|
|
1149
|
-
import './styles/main.css';
|
|
1150
|
-
import { LiteRouter } from './router/LiteRouter.js';
|
|
1151
|
-
import { Layout } from './components/Layout.js';
|
|
1152
|
-
import { Navbar } from './components/Navbar.js';
|
|
1153
|
-
import { authState } from './services/auth.js';
|
|
1306
|
+
}
|
|
1154
1307
|
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1308
|
+
ensure_main_js() {
|
|
1309
|
+
mkdir -p src
|
|
1310
|
+
|
|
1311
|
+
local block_content
|
|
1312
|
+
block_content=$(router_block)
|
|
1313
|
+
|
|
1314
|
+
if [[ -f "src/main.js" ]]; then
|
|
1315
|
+
# Case 1: Markers already exist => Replace content between them
|
|
1316
|
+
if grep -qF "$START_MARK" src/main.js && grep -qF "$END_MARK" src/main.js; then
|
|
1317
|
+
print -n "⚠️ Refai router block found in main.js. Update it? (y/N) "
|
|
1318
|
+
read -k 1 choice; echo ""
|
|
1319
|
+
if [[ "$choice" == [Yy] ]]; then
|
|
1320
|
+
# Use awk to replace block
|
|
1321
|
+
awk -v s="$START_MARK" -v e="$END_MARK" -v r="$block_content" '
|
|
1322
|
+
BEGIN {p=1}
|
|
1323
|
+
$0 == s {print s; print r; p=0}
|
|
1324
|
+
$0 == e {p=1; print e; next}
|
|
1325
|
+
p {print}
|
|
1326
|
+
' src/main.js > src/main.js.tmp && mv src/main.js.tmp src/main.js
|
|
1327
|
+
ok "Updated router block in main.js"
|
|
1328
|
+
fi
|
|
1329
|
+
return 0
|
|
1330
|
+
fi
|
|
1160
1331
|
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1332
|
+
# Case 2: No markers, but Refai code (LiteRouter) detected => Wrap it with markers or replace
|
|
1333
|
+
if grep -q "LiteRouter" src/main.js; then
|
|
1334
|
+
print -n "⚠️ LiteRouter detected but no markers found. Replace entire main.js to fix structure? (y/N) "
|
|
1335
|
+
read -k 1 choice; echo ""
|
|
1336
|
+
if [[ "$choice" == [Yy] ]]; then
|
|
1337
|
+
cp src/main.js "src/main.js.backup.$(date +%Y%m%d-%H%M%S)"
|
|
1338
|
+
{
|
|
1339
|
+
echo "$START_MARK"
|
|
1340
|
+
echo "$block_content"
|
|
1341
|
+
echo "$END_MARK"
|
|
1342
|
+
} > src/main.js
|
|
1343
|
+
ok "Replaced main.js with structured Refai block."
|
|
1344
|
+
fi
|
|
1345
|
+
return 0
|
|
1346
|
+
fi
|
|
1170
1347
|
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1348
|
+
# Case 3: Completely foreign file => Append block with markers
|
|
1349
|
+
print -n "⚠️ src/main.js exists. Append Refai router block at the end? (y/N) "
|
|
1350
|
+
read -k 1 choice; echo ""
|
|
1351
|
+
if [[ "$choice" == [Yy] ]]; then
|
|
1352
|
+
cp src/main.js "src/main.js.backup.$(date +%Y%m%d-%H%M%S)"
|
|
1353
|
+
{
|
|
1354
|
+
echo -e "\n$START_MARK"
|
|
1355
|
+
echo "$block_content"
|
|
1356
|
+
echo "$END_MARK"
|
|
1357
|
+
} >> src/main.js
|
|
1358
|
+
ok "Appended Refai block to main.js"
|
|
1359
|
+
fi
|
|
1360
|
+
else
|
|
1361
|
+
# File doesn't exist => Create new one WITH markers
|
|
1362
|
+
echo "📝 Creating new src/main.js..."
|
|
1363
|
+
{
|
|
1364
|
+
echo "$START_MARK"
|
|
1365
|
+
echo "$block_content"
|
|
1366
|
+
echo "$END_MARK"
|
|
1367
|
+
} > src/main.js
|
|
1368
|
+
ok "Created src/main.js with markers."
|
|
1369
|
+
fi
|
|
1370
|
+
}
|
|
1176
1371
|
|
|
1177
|
-
|
|
1178
|
-
app.init();
|
|
1179
|
-
EOF
|
|
1180
|
-
fi
|
|
1372
|
+
ensure_main_js
|
|
1181
1373
|
|
|
1182
1374
|
# -----------------------------
|
|
1183
1375
|
# 8. Dependency & Concurrent
|
|
@@ -1230,12 +1422,10 @@ if [ "$tail" = true ]; then
|
|
|
1230
1422
|
$PM install -D tailwindcss postcss autoprefixer
|
|
1231
1423
|
|
|
1232
1424
|
# Initialize tailwind config
|
|
1233
|
-
if [
|
|
1234
|
-
|
|
1235
|
-
elif [ "$PM" = "yarn" ]; then
|
|
1236
|
-
yarn tailwindcss init -p
|
|
1425
|
+
if [[ -f "node_modules/.bin/tailwindcss" ]]; then
|
|
1426
|
+
./node_modules/.bin/tailwindcss init -p
|
|
1237
1427
|
else
|
|
1238
|
-
npx -y tailwindcss init -p
|
|
1428
|
+
npx -y --package tailwindcss tailwindcss init -p
|
|
1239
1429
|
fi
|
|
1240
1430
|
|
|
1241
1431
|
cat > src/styles/tailwind.css <<EOF
|
|
@@ -1263,11 +1453,132 @@ if [ "$json" = true ]; then
|
|
|
1263
1453
|
fi
|
|
1264
1454
|
|
|
1265
1455
|
# PWA Setup
|
|
1456
|
+
# -----------------------------
|
|
1457
|
+
# PWA Setup (FULL)
|
|
1458
|
+
# -----------------------------
|
|
1266
1459
|
if [ "$pwa" = true ]; then
|
|
1267
1460
|
echo "📱 Setting up PWA..."
|
|
1461
|
+
|
|
1462
|
+
# Install plugin
|
|
1268
1463
|
$PM install -D vite-plugin-pwa
|
|
1269
1464
|
installed_packages+=("Vite PWA Plugin")
|
|
1270
|
-
|
|
1465
|
+
|
|
1466
|
+
# Ensure public/ exists
|
|
1467
|
+
mkdir -p public
|
|
1468
|
+
|
|
1469
|
+
# 1) Generate real PNG icons (no external deps)
|
|
1470
|
+
# Creates:
|
|
1471
|
+
# - public/pwa-192x192.png
|
|
1472
|
+
# - public/pwa-512x512.png
|
|
1473
|
+
# - public/apple-touch-icon.png
|
|
1474
|
+
node - <<'NODE'
|
|
1475
|
+
const fs = require('fs');
|
|
1476
|
+
const zlib = require('zlib');
|
|
1477
|
+
|
|
1478
|
+
function crc32(buf) {
|
|
1479
|
+
let c = ~0;
|
|
1480
|
+
for (let i = 0; i < buf.length; i++) {
|
|
1481
|
+
c ^= buf[i];
|
|
1482
|
+
for (let k = 0; k < 8; k++) c = (c >>> 1) ^ (0xEDB88320 & (-(c & 1)));
|
|
1483
|
+
}
|
|
1484
|
+
return (~c) >>> 0;
|
|
1485
|
+
}
|
|
1486
|
+
function chunk(type, data) {
|
|
1487
|
+
const t = Buffer.from(type, 'ascii');
|
|
1488
|
+
const len = Buffer.alloc(4);
|
|
1489
|
+
len.writeUInt32BE(data.length, 0);
|
|
1490
|
+
const crc = Buffer.alloc(4);
|
|
1491
|
+
crc.writeUInt32BE(crc32(Buffer.concat([t, data])), 0);
|
|
1492
|
+
return Buffer.concat([len, t, data, crc]);
|
|
1493
|
+
}
|
|
1494
|
+
function makePng(size, outPath) {
|
|
1495
|
+
const width = size, height = size;
|
|
1496
|
+
const raw = Buffer.alloc(width * height * 4);
|
|
1497
|
+
|
|
1498
|
+
const bg = { r: 0x4f, g: 0x46, b: 0xe5, a: 0xff }; // #4f46e5
|
|
1499
|
+
const fg = { r: 0xff, g: 0xff, b: 0xff, a: 0xff }; // white
|
|
1500
|
+
|
|
1501
|
+
const badgeSize = Math.floor(size * 0.46);
|
|
1502
|
+
const badgeStart = Math.floor((size - badgeSize) / 2);
|
|
1503
|
+
const badgeEnd = badgeStart + badgeSize;
|
|
1504
|
+
|
|
1505
|
+
for (let y = 0; y < height; y++) {
|
|
1506
|
+
for (let x = 0; x < width; x++) {
|
|
1507
|
+
const i = (y * width + x) * 4;
|
|
1508
|
+
let c = bg;
|
|
1509
|
+
if (x >= badgeStart && x < badgeEnd && y >= badgeStart && y < badgeEnd) c = fg;
|
|
1510
|
+
raw[i+0] = c.r; raw[i+1] = c.g; raw[i+2] = c.b; raw[i+3] = c.a;
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
const stride = width * 4;
|
|
1515
|
+
const scan = Buffer.alloc((stride + 1) * height);
|
|
1516
|
+
for (let y = 0; y < height; y++) {
|
|
1517
|
+
scan[y * (stride + 1)] = 0; // filter 0
|
|
1518
|
+
raw.copy(scan, y * (stride + 1) + 1, y * stride, (y + 1) * stride);
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
const signature = Buffer.from([0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A]);
|
|
1522
|
+
const ihdr = Buffer.alloc(13);
|
|
1523
|
+
ihdr.writeUInt32BE(width, 0);
|
|
1524
|
+
ihdr.writeUInt32BE(height, 4);
|
|
1525
|
+
ihdr[8] = 8; // bit depth
|
|
1526
|
+
ihdr[9] = 6; // color type RGBA
|
|
1527
|
+
ihdr[10] = 0; // compression
|
|
1528
|
+
ihdr[11] = 0; // filter
|
|
1529
|
+
ihdr[12] = 0; // interlace
|
|
1530
|
+
|
|
1531
|
+
const idatData = zlib.deflateSync(scan, { level: 9 });
|
|
1532
|
+
const png = Buffer.concat([
|
|
1533
|
+
signature,
|
|
1534
|
+
chunk('IHDR', ihdr),
|
|
1535
|
+
chunk('IDAT', idatData),
|
|
1536
|
+
chunk('IEND', Buffer.alloc(0)),
|
|
1537
|
+
]);
|
|
1538
|
+
|
|
1539
|
+
fs.writeFileSync(outPath, png);
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
fs.mkdirSync('public', { recursive: true });
|
|
1543
|
+
makePng(192, 'public/pwa-192x192.png');
|
|
1544
|
+
makePng(512, 'public/pwa-512x512.png');
|
|
1545
|
+
makePng(180, 'public/apple-touch-icon.png');
|
|
1546
|
+
console.log('✅ PWA icons generated: 192, 512, 180');
|
|
1547
|
+
NODE
|
|
1548
|
+
|
|
1549
|
+
# 2) Write manifest.webmanifest
|
|
1550
|
+
cat > public/manifest.webmanifest <<'EOF'
|
|
1551
|
+
{
|
|
1552
|
+
"name": "Refai App",
|
|
1553
|
+
"short_name": "Refai",
|
|
1554
|
+
"description": "Vite PWA powered by Refai.Code",
|
|
1555
|
+
"start_url": "/",
|
|
1556
|
+
"scope": "/",
|
|
1557
|
+
"display": "standalone",
|
|
1558
|
+
"background_color": "#ffffff",
|
|
1559
|
+
"theme_color": "#4f46e5",
|
|
1560
|
+
"icons": [
|
|
1561
|
+
{ "src": "/pwa-192x192.png", "sizes": "192x192", "type": "image/png" },
|
|
1562
|
+
{ "src": "/pwa-512x512.png", "sizes": "512x512", "type": "image/png" },
|
|
1563
|
+
{ "src": "/pwa-512x512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable" }
|
|
1564
|
+
]
|
|
1565
|
+
}
|
|
1566
|
+
EOF
|
|
1567
|
+
|
|
1568
|
+
# 3) Ensure index.html links the manifest + theme color + apple touch icon
|
|
1569
|
+
if [ -f index.html ]; then
|
|
1570
|
+
# Add manifest link if missing
|
|
1571
|
+
if ! grep -q 'manifest\.webmanifest' index.html; then
|
|
1572
|
+
# insert before </head> using | as delimiter to avoid hex code # conflict
|
|
1573
|
+
perl -0777 -i -pe 's|</head>| <link rel="manifest" href="/manifest.webmanifest">\n <meta name="theme-color" content="#4f46e5">\n <link rel="apple-touch-icon" href="/apple-touch-icon.png">\n</head>|s' index.html
|
|
1574
|
+
echo "✅ Updated index.html (manifest + theme-color + apple-touch-icon)"
|
|
1575
|
+
else
|
|
1576
|
+
echo "✅ index.html already has manifest link"
|
|
1577
|
+
fi
|
|
1578
|
+
fi
|
|
1579
|
+
|
|
1580
|
+
# 4) Create/overwrite vite.config.js with correct PWA config
|
|
1581
|
+
cat > vite.config.js <<'EOF'
|
|
1271
1582
|
import { defineConfig } from 'vite';
|
|
1272
1583
|
import path from 'path';
|
|
1273
1584
|
import { VitePWA } from 'vite-plugin-pwa';
|
|
@@ -1275,11 +1586,64 @@ import { VitePWA } from 'vite-plugin-pwa';
|
|
|
1275
1586
|
export default defineConfig({
|
|
1276
1587
|
resolve: { alias: { '@': path.resolve(__dirname, './src') } },
|
|
1277
1588
|
plugins: [
|
|
1278
|
-
VitePWA({
|
|
1589
|
+
VitePWA({
|
|
1590
|
+
registerType: 'autoUpdate',
|
|
1591
|
+
injectRegister: 'auto',
|
|
1592
|
+
devOptions: {
|
|
1593
|
+
enabled: true,
|
|
1594
|
+
type: 'module'
|
|
1595
|
+
},
|
|
1596
|
+
workbox: {
|
|
1597
|
+
globPatterns: ['**/*.{js,css,html,ico,png,svg,webmanifest}']
|
|
1598
|
+
},
|
|
1599
|
+
includeAssets: [
|
|
1600
|
+
'apple-touch-icon.png',
|
|
1601
|
+
'pwa-192x192.png',
|
|
1602
|
+
'pwa-512x512.png'
|
|
1603
|
+
],
|
|
1604
|
+
manifest: {
|
|
1605
|
+
name: 'Refai App',
|
|
1606
|
+
short_name: 'Refai',
|
|
1607
|
+
description: 'Vite PWA powered by Refai.Code',
|
|
1608
|
+
start_url: '/',
|
|
1609
|
+
scope: '/',
|
|
1610
|
+
display: 'standalone',
|
|
1611
|
+
background_color: '#ffffff',
|
|
1612
|
+
theme_color: '#4f46e5',
|
|
1613
|
+
icons: [
|
|
1614
|
+
{ src: 'pwa-192x192.png', sizes: '192x192', type: 'image/png' },
|
|
1615
|
+
{ src: 'pwa-512x512.png', sizes: '512x512', type: 'image/png' },
|
|
1616
|
+
{ src: 'pwa-512x512.png', sizes: '512x512', type: 'image/png', purpose: 'any maskable' }
|
|
1617
|
+
]
|
|
1618
|
+
}
|
|
1619
|
+
})
|
|
1279
1620
|
]
|
|
1280
1621
|
});
|
|
1281
1622
|
EOF
|
|
1623
|
+
|
|
1624
|
+
# 5) Add a small SW hook file (optional but useful) + include it from main.js
|
|
1625
|
+
# We'll create src/pwa/register.js and then import it from src/main.js (if main.js exists)
|
|
1626
|
+
mkdir -p src/pwa
|
|
1627
|
+
|
|
1628
|
+
cat > src/pwa/register.js <<'EOF'
|
|
1629
|
+
export function registerPWA() {
|
|
1630
|
+
// vite-plugin-pwa with injectRegister:'auto' registers automatically.
|
|
1631
|
+
// This file is just a safe place to add UX hooks (optional).
|
|
1632
|
+
if ('serviceWorker' in navigator) {
|
|
1633
|
+
// Optional: listen for SW updates
|
|
1634
|
+
navigator.serviceWorker.addEventListener('controllerchange', () => {
|
|
1635
|
+
// SW updated and controlling the page
|
|
1636
|
+
console.log('[PWA] controller changed');
|
|
1637
|
+
});
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
EOF
|
|
1641
|
+
|
|
1642
|
+
# Note: PWA registration is now handled inside the main.js router block for consistency
|
|
1643
|
+
installed_packages+=("PWA (manifest + icons + SW)")
|
|
1644
|
+
|
|
1282
1645
|
else
|
|
1646
|
+
# If PWA not selected, keep normal vite.config.js
|
|
1283
1647
|
cat > vite.config.js <<EOF
|
|
1284
1648
|
import { defineConfig } from 'vite';
|
|
1285
1649
|
import path from 'path';
|
|
@@ -1289,25 +1653,24 @@ export default defineConfig({
|
|
|
1289
1653
|
});
|
|
1290
1654
|
EOF
|
|
1291
1655
|
fi
|
|
1292
|
-
|
|
1293
1656
|
# -----------------------------
|
|
1294
1657
|
# Final Summary
|
|
1295
1658
|
# -----------------------------
|
|
1296
|
-
|
|
1297
|
-
|
|
1659
|
+
print "\n✅ \033[1;32m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\033[0m"
|
|
1660
|
+
print "\n🎉 \033[1;32mSetup Complete!\033[0m Created by Refai.Code\n"
|
|
1298
1661
|
|
|
1299
1662
|
if [ ${#installed_packages[@]} -gt 0 ]; then
|
|
1300
|
-
|
|
1663
|
+
print "📦 \033[1;36mInstalled Packages:\033[0m"
|
|
1301
1664
|
for pkg in "${installed_packages[@]}"; do
|
|
1302
|
-
|
|
1665
|
+
print " ✓ $pkg"
|
|
1303
1666
|
done
|
|
1304
|
-
|
|
1667
|
+
print ""
|
|
1305
1668
|
fi
|
|
1306
1669
|
|
|
1307
|
-
|
|
1308
|
-
|
|
1670
|
+
print "🚀 \033[1;33mNext Steps:\033[0m"
|
|
1671
|
+
print " 1. Run \033[1m$PM run dev\033[0m to start Vite dev server"
|
|
1309
1672
|
if [ "$json" = true ]; then
|
|
1310
|
-
|
|
1311
|
-
|
|
1673
|
+
print " 2. Run \033[1m$PM run dev:full\033[0m to start Vite + JSON Server together"
|
|
1674
|
+
print " 3. Or run \033[1mnpx json-server db.json --port 3000\033[0m in a separate terminal"
|
|
1312
1675
|
fi
|
|
1313
|
-
|
|
1676
|
+
print "\n✅ \033[1;32m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\033[0m\n"
|
package/package.json
CHANGED
|
@@ -1,16 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@refai.code/vite-boost",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Vite Vanilla JS booster script (optional Tailwind/Axios/PWA/JSON-server)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|
|
7
|
+
"homepage": "https://github.com/David-refai/vite-boost",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/David-refai/vite-boost.git"
|
|
11
|
+
},
|
|
7
12
|
"bin": {
|
|
8
13
|
"vite-boost": "bin/vite-boost"
|
|
9
14
|
},
|
|
10
15
|
"files": [
|
|
11
|
-
"bin"
|
|
16
|
+
"bin",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE"
|
|
12
19
|
],
|
|
13
20
|
"engines": {
|
|
14
21
|
"node": ">=18"
|
|
15
|
-
}
|
|
22
|
+
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"vite",
|
|
28
|
+
"cli",
|
|
29
|
+
"pwa",
|
|
30
|
+
"tailwind",
|
|
31
|
+
"json-server",
|
|
32
|
+
"scaffold"
|
|
33
|
+
]
|
|
16
34
|
}
|