easy-starter 2.0.0 → 2.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 CHANGED
@@ -35,6 +35,7 @@ The generated boilerplate can be configured to contain:
35
35
  * **Database:** [`easy-db-node`](https://www.npmjs.com/package/easy-db-node) - A simple, local JSON-based database perfect for small to medium projects where setting up Postgres/MongoDB is overkill.
36
36
  * **Routing:** [`easy-page-router`](https://www.npmjs.com/package/easy-page-router) - A lightweight, programmatic routing solution.
37
37
  * **Analytics:** [`easy-analytics`](https://www.npmjs.com/package/easy-analytics) - Privacy-friendly, local analytics tracking.
38
+ * **Image Resizing:** [`easy-image-resizer`](https://www.npmjs.com/package/easy-image-resizer) - Express middleware for dynamic image resizing and WebP/JPEG conversion with automatic mtime disk caching.
38
39
  * **AI-Friendly (.cursorrules):** Pre-configured, LLM-optimized guidelines in the project root so AI coding assistants (Cursor, Windsurf, Copilot, etc.) immediately understand the codebase conventions, API restrictions, routing patterns, and database operations.
39
40
  * **Deployment:** Integrated Github Actions workflow and PM2 deployment script for seamless, automated deployment straight to your own VPS.
40
41
 
@@ -46,11 +47,11 @@ When you run `npx easy-starter`, the CLI will prompt you with the following conf
46
47
 
47
48
  1. **Project name:** Choose the name of the folder and the app.
48
49
  2. **User Authentication:** Do you want to add user login/registration widgets? (Installs `easy-user-auth` and nodemailer, generates JWT secrets in `.env`).
49
- 3. **Viewport Width Fix:** Do you want to fix iOS mobile viewport issues by ensuring a minimum width of 600px? (Adds JS viewport resizing listener and sets CSS `min-width`).
50
+ 3. **Analytics:** Do you want local visitor analytics? (Installs `easy-analytics`, adds dashboard visualization).
50
51
  4. **Admin Dashboard:** Do you want to include `/admin` page? (If Auth is enabled, protects it using user roles. If Auth is disabled, protects it using a static password).
51
- 5. **Analytics:** Do you want local visitor analytics? (Installs `easy-analytics`, adds dashboard visualization).
52
- 6. **SSR Rendering:** Do you want Server-Side Rendering? (Installs `ssr-hook`, configures hydration).
53
- 7. **og:image Generation:** Do you want dynamic `og:image` generation? (Installs `puppeteer` to automatically screenshot URLs for social cards, cached via easy-db).
52
+ 5. **SSR Rendering:** Do you want Server-Side Rendering? (Installs `ssr-hook`, configures hydration).
53
+ 6. **og:image Generation:** Do you want dynamic `og:image` generation? (Installs `puppeteer` to automatically screenshot URLs for social cards, cached via easy-db).
54
+ 7. **Image Resizing:** Do you want dynamic image resizing? (Installs `easy-image-resizer` Express middleware for on-the-fly image resizing and WebP conversion with automatic caching).
54
55
  8. **Deployment Script:** Do you want to deploy to your VPS using GitHub Actions? (Installs `node-ssh`, adds SSH deploy script).
55
56
 
56
57
  ---
package/bin/index.js CHANGED
@@ -14,11 +14,17 @@ const rl = readline.createInterface({
14
14
  const askQuestion = (query) => new Promise(resolve => rl.question(query, resolve));
15
15
 
16
16
  async function main() {
17
- console.log('šŸš€ Welcome to easy-starter v2.0.0!');
17
+ console.log('šŸš€ Welcome to easy-starter v2.1.0!');
18
18
 
19
- let projectName = process.argv[2];
19
+ const isYes = process.argv.includes('-y') || process.argv.includes('--yes') || process.argv.includes('--default');
20
+ const hasFlag = (flag) => process.argv.includes(`--${flag}`);
21
+ const hasNoFlag = (flag) => process.argv.includes(`--no-${flag}`);
22
+
23
+ let args = process.argv.slice(2).filter(arg => !arg.startsWith('-'));
24
+
25
+ let projectName = args[0];
20
26
  if (!projectName) {
21
- projectName = await askQuestion('Enter the project name (e.g., my-awesome-app): ');
27
+ projectName = isYes ? 'my-app' : await askQuestion('Enter the project name (e.g., my-awesome-app): ');
22
28
  }
23
29
  projectName = projectName.trim();
24
30
 
@@ -28,24 +34,28 @@ async function main() {
28
34
  process.exit(1);
29
35
  }
30
36
 
37
+ const getAnswer = async (question, flagName) => {
38
+ if (hasNoFlag(flagName)) return false;
39
+ if (hasFlag(flagName) || isYes) return true;
40
+ const ans = await askQuestion(question);
41
+ return ans.trim().toLowerCase() !== 'n';
42
+ };
43
+
31
44
  console.log('\nšŸ”’ 1. User Authentication & Login (easy-user-auth)');
32
45
  console.log(' Provides out-of-the-box user registration, login dialogs, secure cookie-based');
33
46
  console.log(' sessions, and forgotten password recovery emails.');
34
- const ansAuth = await askQuestion(' Do you want to include user authentication? (y/n) [y]: ');
35
- const useAuth = ansAuth.trim().toLowerCase() !== 'n';
47
+ const useAuth = await getAnswer(' Do you want to include user authentication? (y/n) [y]: ', 'auth');
36
48
 
37
49
  console.log('\nšŸ“Š 2. Analytics Tracking (easy-analytics)');
38
50
  console.log(' Tracks page visits, referrers, resolutions, and browsers locally without cookies.');
39
51
  console.log(' Also captures and records client-side/frontend errors encountered by users.');
40
52
  console.log(' If the Admin Dashboard is enabled, it generates interactive visual graphs inside /admin.');
41
- const ansAnalytics = await askQuestion(' Do you want to include privacy-friendly analytics? (y/n) [y]: ');
42
- const useAnalytics = ansAnalytics.trim().toLowerCase() !== 'n';
53
+ const useAnalytics = await getAnswer(' Do you want to include privacy-friendly analytics? (y/n) [y]: ', 'analytics');
43
54
 
44
55
  console.log('\nšŸ› ļø 3. Admin Dashboard (/admin)');
45
56
  console.log(' A secure section to manage your app. Protected by user authentication roles');
46
57
  console.log(' (admin/user) if auth is enabled, or a static environment password if disabled.');
47
- const ansAdmin = await askQuestion(' Do you want to include the admin dashboard? (y/n) [y]: ');
48
- const useAdmin = ansAdmin.trim().toLowerCase() !== 'n';
58
+ const useAdmin = await getAnswer(' Do you want to include the admin dashboard? (y/n) [y]: ', 'admin');
49
59
 
50
60
  let useAdminUsers = false;
51
61
  let useAdminErrors = false;
@@ -55,8 +65,7 @@ async function main() {
55
65
  console.log('\nšŸ‘„ 3a. Admin User Management');
56
66
  console.log(' Allows administrators to list registered users and change their roles');
57
67
  console.log(' directly from the admin dashboard.');
58
- const ansAdminUsers = await askQuestion(' Do you want to include User Management in the admin panel? (y/n) [y]: ');
59
- useAdminUsers = ansAdminUsers.trim().toLowerCase() !== 'n';
68
+ useAdminUsers = await getAnswer(' Do you want to include User Management in the admin panel? (y/n) [y]: ', 'admin-users');
60
69
  } else {
61
70
  console.log('\nā„¹ļø Note: User Management is disabled in the admin panel because it requires User Authentication.');
62
71
  }
@@ -65,8 +74,7 @@ async function main() {
65
74
  console.log('\n🚨 3b. Admin Error Logging');
66
75
  console.log(' Captures frontend/client-side errors (requires easy-analytics) and lists them');
67
76
  console.log(' inside the admin panel, including descriptions and collapsible stack traces.');
68
- const ansAdminErrors = await askQuestion(' Do you want to include Error Logging in the admin panel? (y/n) [y]: ');
69
- useAdminErrors = ansAdminErrors.trim().toLowerCase() !== 'n';
77
+ useAdminErrors = await getAnswer(' Do you want to include Error Logging in the admin panel? (y/n) [y]: ', 'admin-errors');
70
78
  } else {
71
79
  console.log('\nā„¹ļø Note: Error Logging is disabled in the admin panel because it requires');
72
80
  console.log(' Analytics Tracking to be enabled.');
@@ -76,26 +84,27 @@ async function main() {
76
84
  console.log('\n⚔ 4. Server-Side Rendering (ssr-hook)');
77
85
  console.log(' Enables server-side pre-rendering for React. Enhances initial load speeds,');
78
86
  console.log(' prevents content flashes, and optimizes your site for search engine crawlers (SEO).');
79
- const ansSSR = await askQuestion(' Do you want to enable Server-Side Rendering (SSR)? (y/n) [y]: ');
80
- const useSSR = ansSSR.trim().toLowerCase() !== 'n';
87
+ const useSSR = await getAnswer(' Do you want to enable Server-Side Rendering (SSR)? (y/n) [y]: ', 'ssr');
81
88
 
82
89
  console.log('\nšŸ–¼ļø 5. Social Preview og:image');
83
90
  console.log(' Automatically screenshots and generates social preview card og:images for your pages');
84
91
  console.log(' using a headless browser rendering engine (puppeteer) and caches them in the database.');
85
- const ansOgImage = await askQuestion(' Do you want to include dynamic og:image generation? (y/n) [y]: ');
86
- const useOgImage = ansOgImage.trim().toLowerCase() !== 'n';
92
+ const useOgImage = await getAnswer(' Do you want to include dynamic og:image generation? (y/n) [y]: ', 'ogimage');
87
93
 
88
94
  console.log('\nšŸ“± 6. Viewport Tuning');
89
95
  console.log(' Injects a viewport event listener that forces a minimum width layout (600px)');
90
96
  console.log(' for mobile devices, resolving scaling/rendering issues on iOS Safari.');
91
- const ansViewport = await askQuestion(' Do you want to fix iOS Safari viewport width issues? (y/n) [y]: ');
92
- const useViewport = ansViewport.trim().toLowerCase() !== 'n';
97
+ const useViewport = await getAnswer(' Do you want to fix iOS Safari viewport width issues? (y/n) [y]: ', 'viewport');
93
98
 
94
- console.log('\n🚢 7. Deployment Integration (node-ssh)');
99
+ console.log('\nšŸ–¼ļø 7. Express Middleware Image Resizing (easy-image-resizer)');
100
+ console.log(' Enables dynamic image resizing and format conversion on the fly');
101
+ console.log(' via URL query parameters (e.g. /images/icon.png?w=192&format=webp) with automatic mtime caching.');
102
+ const useResizer = await getAnswer(' Do you want to include dynamic image resizing? (y/n) [y]: ', 'resizer');
103
+
104
+ console.log('\n🚢 8. Deployment Integration (node-ssh)');
95
105
  console.log(' Configures deploy scripts and GitHub Actions workflows to build and transfer your code');
96
106
  console.log(' via SSH straight to your own VPS (Virtual Private Server), where it runs managed by PM2.');
97
- const ansDeploy = await askQuestion(' Do you want to include VPS deploy scripts? (y/n) [y]: ');
98
- const useDeploy = ansDeploy.trim().toLowerCase() !== 'n';
107
+ const useDeploy = await getAnswer(' Do you want to include VPS deploy scripts? (y/n) [y]: ', 'deploy');
99
108
 
100
109
  rl.close();
101
110
 
@@ -112,7 +121,18 @@ async function main() {
112
121
  const templatePath = path.join(__dirname, '..', 'template');
113
122
 
114
123
  // Ignored files based on configuration
115
- const ignoreList = ['env'];
124
+ const ignoreList = [
125
+ 'env',
126
+ 'node_modules',
127
+ 'dist',
128
+ 'cache',
129
+ 'easy-db',
130
+ 'easy-db-files',
131
+ 'easy-db-backup',
132
+ '.vite',
133
+ 'package-lock.json',
134
+ '.DS_Store'
135
+ ];
116
136
  if (!useDeploy) {
117
137
  ignoreList.push('scripts');
118
138
  ignoreList.push('.github');
@@ -124,6 +144,9 @@ async function main() {
124
144
  if (!useAdmin) {
125
145
  ignoreList.push('admin');
126
146
  }
147
+ if (!useResizer) {
148
+ ignoreList.push('Img.tsx');
149
+ }
127
150
 
128
151
  function copyFolderSync(from, to, ignoreFiles = []) {
129
152
  fs.mkdirSync(to, { recursive: true });
@@ -210,6 +233,10 @@ async function main() {
210
233
  content = handleBlock(content, 'NO_SSR', !useSSR);
211
234
 
212
235
  content = handleBlock(content, 'OGIMAGE', useOgImage);
236
+ content = handleBlock(content, 'NO_OGIMAGE', !useOgImage);
237
+
238
+ content = handleBlock(content, 'RESIZER', useResizer);
239
+ content = handleBlock(content, 'NO_RESIZER', !useResizer);
213
240
 
214
241
  return content;
215
242
  }
@@ -241,25 +268,25 @@ async function main() {
241
268
  const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
242
269
  pkg.name = projectName;
243
270
 
244
- if (useAnalytics) {
245
- pkg.dependencies['easy-analytics'] = '^1.0.1';
271
+ if (!useAnalytics) {
272
+ delete pkg.dependencies['easy-analytics'];
246
273
  }
247
- if (useAuth) {
248
- pkg.dependencies['easy-user-auth'] = '^1.0.6';
249
- pkg.dependencies['nodemailer'] = '^7.0.9';
250
- pkg.devDependencies['@types/nodemailer'] = '^6.4.15';
274
+ if (!useAuth) {
275
+ delete pkg.dependencies['easy-user-auth'];
276
+ delete pkg.dependencies['nodemailer'];
277
+ if (pkg.devDependencies) delete pkg.devDependencies['@types/nodemailer'];
251
278
  }
252
- if (useSSR) {
253
- pkg.dependencies['ssr-hook'] = '^0.2.0';
279
+ if (!useSSR) {
280
+ delete pkg.dependencies['ssr-hook'];
254
281
  }
255
- if (useOgImage) {
256
- pkg.dependencies['puppeteer'] = '^25.3.0';
282
+ if (!useOgImage) {
283
+ delete pkg.dependencies['puppeteer'];
257
284
  }
258
- if (useDeploy) {
259
- pkg.devDependencies['node-ssh'] = '^13.2.1';
260
- pkg.scripts = pkg.scripts || {};
261
- pkg.scripts['deploy'] = 'tsx scripts/deploy.ts';
262
- } else {
285
+ if (!useResizer) {
286
+ delete pkg.dependencies['easy-image-resizer'];
287
+ }
288
+ if (!useDeploy) {
289
+ if (pkg.devDependencies) delete pkg.devDependencies['node-ssh'];
263
290
  if (pkg.scripts && pkg.scripts.deploy) {
264
291
  delete pkg.scripts.deploy;
265
292
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "easy-starter",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "A lightweight, production-ready fullstack React boilerplate generator with Vite v8, TypeScript 7, server-side rendering (SSR), local analytics, SSH deploy script, PM2, and secure user authentication.",
5
5
  "bin": {
6
6
  "easy-starter": "./bin/index.js"
@@ -99,17 +99,42 @@ export type Database = {
99
99
  * **Centralized & Generic**: Implement as many styles as possible in `src/styles.css` using generic, reusable selectors.
100
100
  * **Minimize Inline Styles**: Avoid inline styles (e.g., `style={{ ... }}`) in React components. Keep style declarations in the central CSS file to maintain design consistency and code quality.
101
101
 
102
- ### Image Asset Handling
103
- * **Use `vite-imagetools` Exclusively in CSS:**
104
- - When loading local images for styling and background layouts, reference them exclusively inside CSS `url()` declarations, appending `vite-imagetools` query parameters (such as `format=webp` and dimensions `w=`) to automatically optimize them.
102
+ /* --- RESIZER START --- */
103
+ ### Image Asset Handling (`easy-image-resizer` & `<Img />` Component)
104
+ * **Dynamic Image Resizing via Express Middleware (`easy-image-resizer`):**
105
+ - All image assets (logos, icons, banners) must be placed inside `/public` (e.g. `/public/images/icon.png`).
106
+ - Request dynamically resized, cropped, or format-converted images on the fly via URL query parameters in CSS `url()` or via `<Img />` component props.
105
107
  - **CORRECT (CSS):**
106
108
  ```css
107
109
  .hero-banner {
108
- background-image: url("./assets/hero.jpg?format=webp&w=1200");
110
+ background-image: url("/images/hero.png?w=1200&format=webp&q=85");
109
111
  }
110
112
  ```
111
113
  * **JavaScript Imports for Images are Forbidden:**
112
114
  - Do **NOT** import image assets inside `.js`, `.ts`, or `.tsx` files (e.g. do NOT write `import hero from "./hero.jpg"`).
113
- * **Referencing Images in TSX/React (Only via `/public`):**
114
- - If you need to reference or display an image directly inside React TSX components (e.g., standard `<img>` tags or SEO metadata), **you must place the pre-optimized WebP image inside the `/public` directory** and reference it using a standard absolute URL path.
115
- - **CORRECT (TSX):** `<img src="/images/logo.webp" alt="Logo" />` (with the file stored inside `/public/images/logo.webp`).
115
+ * **Use `<Img />` Component for Images in React TSX (Do NOT use standard `<img>` tags):**
116
+ - When displaying images inside React TSX components, **always use the `<Img />` component** imported from `../components/Img` (or `./components/Img`) instead of native HTML `<img>` tags.
117
+ - The `<Img />` component automatically sets default format to `"webp"` and constructs resizer query parameters.
118
+ - Supported props on `<Img />`:
119
+ - `src`: Original path to image inside `/public` (e.g. `"/images/icon.png"`)
120
+ - `w` or `width`: Target width (px)
121
+ - `h` or `height`: Target height (px)
122
+ - `mw` or `maxWidth`: Maximum width (px)
123
+ - `mh` or `maxHeight`: Maximum height (px)
124
+ - `format` or `type`: `"webp"` (default), `"jpeg"`, or `"png"`
125
+ - `q` or `quality`: 1 to 100
126
+ - **CORRECT (TSX):**
127
+ ```tsx
128
+ import { Img } from "../components/Img";
129
+
130
+ <Img src="/images/logo.png" w={300} h={100} alt="Application Logo" />
131
+ ```
132
+ /* --- RESIZER END --- */
133
+ /* --- NO_RESIZER START --- */
134
+ ### Image Asset Handling
135
+ * **JavaScript Imports for Images are Forbidden:**
136
+ - Do **NOT** import image assets inside `.js`, `.ts`, or `.tsx` files (e.g. do NOT write `import hero from "./hero.jpg"`).
137
+ * **Referencing Images in TSX/React (via `/public`):**
138
+ - Place original images in `/public` and reference them using standard absolute URL paths.
139
+ - **CORRECT (TSX):** `<img src="/images/logo.png" alt="Logo" />`
140
+ /* --- NO_RESIZER END --- */
@@ -22,6 +22,9 @@ This project was bootstrapped with `easy-starter`, giving you maximum freedom wi
22
22
  <!-- --- OGIMAGE START --- -->
23
23
  * **og:image Generation**: Dynamic page screenshotting for social previews using headless `puppeteer`.
24
24
  <!-- --- OGIMAGE END --- -->
25
+ <!-- --- RESIZER START --- -->
26
+ * **Image Resizing**: Dynamic image resizing and WebP/JPEG conversion on the fly via [`easy-image-resizer`](https://www.npmjs.com/package/easy-image-resizer) with automatic disk caching.
27
+ <!-- --- RESIZER END --- -->
25
28
 
26
29
  ---
27
30
 
@@ -135,7 +138,51 @@ export default function Index() {
135
138
  ```
136
139
  <!-- --- NO_SSR END --- -->
137
140
 
138
- ### 3. AI Integration (`.cursorrules`)
141
+ <!-- --- RESIZER START --- -->
142
+ ### 3. Dynamic Image Resizing (`easy-image-resizer`)
143
+
144
+ All images stored in `/public` (e.g., `/public/images/logo.png`) can be dynamically resized, cropped, and converted on the fly using `easy-image-resizer` Express middleware.
145
+
146
+ #### Using `<Img />` Component in React (TSX)
147
+
148
+ Use the `<Img />` component imported from `../components/Img` instead of standard `<img>` tags. It automatically appends query parameters and defaults to `"webp"` format for optimal performance:
149
+
150
+ ```tsx
151
+ import { Img } from "../components/Img";
152
+
153
+ export default function MyComponent() {
154
+ return (
155
+ <Img
156
+ src="/images/logo.png"
157
+ w={300}
158
+ h={150}
159
+ format="webp"
160
+ quality={85}
161
+ alt="App Logo"
162
+ />
163
+ );
164
+ }
165
+ ```
166
+
167
+ Available props on `<Img />`:
168
+ * `w` or `width`: Target width in pixels
169
+ * `h` or `height`: Target height in pixels
170
+ * `mw` or `maxWidth`, `mh` or `maxHeight`: Max dimension constraints
171
+ * `format` or `type`: `"webp"` (default), `"jpeg"`, or `"png"`
172
+ * `q` or `quality`: Image quality (1-100)
173
+
174
+ #### Using Image Resizing in CSS
175
+
176
+ You can also use URL query parameters directly inside CSS `url()` declarations:
177
+
178
+ ```css
179
+ .hero-banner {
180
+ background-image: url("/images/hero.png?w=1200&format=webp&q=85");
181
+ }
182
+ ```
183
+ <!-- --- RESIZER END --- -->
184
+
185
+ ### 4. AI Integration (`.cursorrules`)
139
186
 
140
187
  This template includes a pre-configured `.cursorrules` file in the root. If you develop using AI assistants such as Cursor, Windsurf, or GitHub Copilot, they will automatically read this file to understand the specific conventions of `easy-starter`.
141
188
 
@@ -1,6 +1,7 @@
1
1
  .vite/
2
2
  node_modules/
3
3
  dist/
4
+ cache/
4
5
  easy-db/
5
6
  easy-db-files/
6
7
  easy-db-backup/
@@ -3,9 +3,9 @@
3
3
  <head>
4
4
  <meta charset="utf-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
- <link rel="apple-touch-icon" sizes="180x180" href="/images/icon-180.png" />
7
- <link rel="icon" type="image/png" sizes="32x32" href="/images/icon-32.png" />
8
- <link rel="icon" type="image/png" sizes="16x16" href="/images/icon-16.png" />
6
+ <link rel="apple-touch-icon" sizes="180x180" href="/images/icon.png?w=180&h=180&format=webp" />
7
+ <link rel="icon" type="image/png" sizes="32x32" href="/images/icon.png?w=32&h=32" />
8
+ <link rel="icon" type="image/png" sizes="16x16" href="/images/icon.png?w=16&h=16" />
9
9
  <link rel="manifest" href="/site.webmanifest" />
10
10
  </head>
11
11
  <body>
@@ -11,24 +11,31 @@
11
11
  },
12
12
  "dependencies": {
13
13
  "cors": "^2.8.6",
14
+ "easy-analytics": "^1.0.1",
14
15
  "easy-db-node": "^3.0.1",
16
+ "easy-image-resizer": "^2.0.1",
15
17
  "easy-page-router": "^0.2.1",
18
+ "easy-user-auth": "^1.1.0",
16
19
  "express": "^5.2.1",
20
+ "nodemailer": "^7.0.9",
21
+ "puppeteer": "^25.3.0",
17
22
  "react": "^19.2.6",
18
23
  "react-dom": "^19.2.6",
24
+ "ssr-hook": "^0.2.0",
19
25
  "typed-client-server-api": "^1.1.0"
20
26
  },
21
27
  "devDependencies": {
22
28
  "@types/cors": "^2.8.19",
23
29
  "@types/express": "^5.0.6",
24
30
  "@types/node": "^24.0.0",
31
+ "@types/nodemailer": "^6.4.15",
25
32
  "@types/react": "^19.0.0",
26
33
  "@types/react-dom": "^19.0.0",
34
+ "@vitejs/plugin-react": "^6.0.3",
27
35
  "concurrently": "^9.2.1",
36
+ "node-ssh": "^13.2.1",
28
37
  "tsx": "^4.21.0",
29
38
  "typescript": "^7.0.0",
30
- "vite": "^8.1.5",
31
- "@vitejs/plugin-react": "^6.0.3",
32
- "vite-imagetools": "^10.0.1"
39
+ "vite": "^8.1.5"
33
40
  }
34
- }
41
+ }
@@ -8,33 +8,33 @@
8
8
  "background_color": "#09090b",
9
9
  "icons": [
10
10
  {
11
- "src": "/images/icon-16.png",
11
+ "src": "/images/icon.png?w=16&h=16",
12
12
  "sizes": "16x16",
13
13
  "type": "image/png",
14
14
  "purpose": "maskable"
15
15
  },
16
16
  {
17
- "src": "/images/icon-32.png",
17
+ "src": "/images/icon.png?w=32&h=32",
18
18
  "sizes": "32x32",
19
19
  "type": "image/png",
20
20
  "purpose": "maskable"
21
21
  },
22
22
  {
23
- "src": "/images/icon-180.png",
23
+ "src": "/images/icon.png?w=180&h=180&format=webp",
24
24
  "sizes": "180x180",
25
- "type": "image/png",
25
+ "type": "image/webp",
26
26
  "purpose": "maskable"
27
27
  },
28
28
  {
29
- "src": "/images/icon-192.png",
29
+ "src": "/images/icon.png?w=192&h=192&format=webp",
30
30
  "sizes": "192x192",
31
- "type": "image/png",
31
+ "type": "image/webp",
32
32
  "purpose": "maskable"
33
33
  },
34
34
  {
35
- "src": "/images/icon-512.png",
35
+ "src": "/images/icon.png?w=512&h=512&format=webp",
36
36
  "sizes": "512x512",
37
- "type": "image/png",
37
+ "type": "image/webp",
38
38
  "purpose": "maskable"
39
39
  }
40
40
  ]
@@ -17,7 +17,8 @@ const path = resolve(process.cwd()); // Local project path
17
17
  const ignoreFiles = [
18
18
  ".git",
19
19
  ".github",
20
- ".parcel-cache",
20
+ ".vite",
21
+ "dist",
21
22
  "node_modules",
22
23
  "scripts",
23
24
  "easy-db",
@@ -70,7 +71,7 @@ async function deploy() {
70
71
  const timeServerOff = new Date().getTime();
71
72
 
72
73
  console.log(`Removing old build...`);
73
- await sshExec(ssh, "rm -rf .parcel-cache", "Failed to remove .parcel-cache folder.");
74
+ await sshExec(ssh, "rm -rf .vite", "Failed to remove .vite folder.");
74
75
  await sshExec(ssh, "rm -rf dist", "Failed to remove dist folder.");
75
76
  console.log(`āœ” Old build removed.\n`);
76
77