@inglorious/create-app 1.1.1 → 1.1.2
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 +11 -2
- package/bin/index.js +49 -366
- package/bin/lib/cli.js +18 -0
- package/bin/lib/constants.js +14 -0
- package/bin/lib/file.js +88 -0
- package/bin/lib/mobile.js +122 -0
- package/bin/lib/pwa.js +122 -0
- package/package.json +3 -3
- package/templates/js/package.json +8 -7
- package/templates/js-jsx/package.json +8 -7
- package/templates/js-vue/package.json +7 -6
- package/templates/ssx-js/package.json +2 -2
- package/templates/ssx-ts/package.json +2 -2
- package/templates/ts/package.json +7 -6
- package/templates/ts-jsx/package.json +7 -6
- package/templates/ts-vue/package.json +7 -6
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ npm create @inglorious/app@latest -- --mobile
|
|
|
31
31
|
npm create @inglorious/app@latest -- --pwa --mobile
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
- `--pwa` adds a web app manifest,
|
|
34
|
+
- `--pwa` adds `vite-plugin-pwa`, a basic web app manifest configuration, PWA icons, and browser-only service worker registration.
|
|
35
35
|
- `--mobile` adds Capacitor dependencies, config, and mobile scripts for iOS and Android.
|
|
36
36
|
|
|
37
37
|
## Templates
|
|
@@ -61,10 +61,19 @@ pnpm install
|
|
|
61
61
|
pnpm dev
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
When mobile support is enabled, build and sync
|
|
64
|
+
When mobile support is enabled, initialize the native platforms once, then build and sync them with:
|
|
65
65
|
|
|
66
66
|
```bash
|
|
67
|
+
# Run one or both commands once, depending on the platforms you need
|
|
68
|
+
pnpm mobile:init:android
|
|
69
|
+
pnpm mobile:init:ios
|
|
70
|
+
|
|
67
71
|
pnpm mobile:sync
|
|
72
|
+
|
|
73
|
+
# Build and open the native app
|
|
74
|
+
pnpm mobile:android
|
|
75
|
+
# or
|
|
76
|
+
pnpm mobile:ios
|
|
68
77
|
```
|
|
69
78
|
|
|
70
79
|
For Android and iOS builds, install Android Studio with the Android SDK and a Java runtime compatible with Capacitor. Capacitor currently works reliably with JDK 21, so prefer JDK 21 over newer Java versions if you hit Java compatibility issues. Set `JAVA_HOME` to your JDK 21 install and `ANDROID_HOME` to your Android SDK path before running the native app. See the official Capacitor setup guides for platform-specific instructions:
|
package/bin/index.js
CHANGED
|
@@ -8,15 +8,17 @@ import path from "path"
|
|
|
8
8
|
import prompts from "prompts"
|
|
9
9
|
import { fileURLToPath } from "url"
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
import { parseCliOptions } from "./lib/cli.js"
|
|
12
|
+
import {
|
|
13
|
+
EMPTY_LENGTH,
|
|
14
|
+
INDENTATION,
|
|
15
|
+
NPM_VIEW_TIMEOUT_MS,
|
|
16
|
+
USER_ARG_START_INDEX,
|
|
17
|
+
VITE_APP_TEMPLATES,
|
|
18
|
+
} from "./lib/constants.js"
|
|
19
|
+
import { copyDir } from "./lib/file.js"
|
|
20
|
+
import { addMobileSupport } from "./lib/mobile.js"
|
|
21
|
+
import { addPwaSupport } from "./lib/pwa.js"
|
|
20
22
|
|
|
21
23
|
async function main() {
|
|
22
24
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
@@ -83,7 +85,6 @@ async function main() {
|
|
|
83
85
|
let pwa = cliOptions.pwa === true
|
|
84
86
|
let mobile = cliOptions.mobile === true
|
|
85
87
|
|
|
86
|
-
// Ask about renderer for non-minimal templates
|
|
87
88
|
if (baseTemplate !== "minimal") {
|
|
88
89
|
const { selectedRenderer } = await prompts(
|
|
89
90
|
{
|
|
@@ -166,18 +167,14 @@ async function main() {
|
|
|
166
167
|
try {
|
|
167
168
|
const targetDir = path.join(process.cwd(), projectName)
|
|
168
169
|
|
|
169
|
-
// Determine the actual template directory based on base template and renderer
|
|
170
170
|
let templateName = baseTemplate
|
|
171
171
|
if (baseTemplate !== "minimal" && renderer !== "lit-html") {
|
|
172
172
|
templateName = `${baseTemplate}-${renderer}`
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
const templateDir = path.join(templatesRoot, templateName)
|
|
176
|
-
|
|
177
|
-
// Create project directory and copy template files
|
|
178
176
|
copyDir(templateDir, targetDir)
|
|
179
177
|
|
|
180
|
-
// Update README.md with project name
|
|
181
178
|
const readmePath = path.join(targetDir, "README.md")
|
|
182
179
|
if (fs.existsSync(readmePath)) {
|
|
183
180
|
let readme = fs.readFileSync(readmePath, "utf-8")
|
|
@@ -190,372 +187,58 @@ async function main() {
|
|
|
190
187
|
console.log(`\nNext steps:`)
|
|
191
188
|
console.log(` cd ${projectName}`)
|
|
192
189
|
console.log(` Open index.html in your browser`)
|
|
193
|
-
|
|
194
|
-
// Update package.json with project name
|
|
195
|
-
const pkgPath = path.join(targetDir, "package.json")
|
|
196
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"))
|
|
197
|
-
pkg.name = projectName
|
|
198
|
-
|
|
199
|
-
if (pwa) {
|
|
200
|
-
spinner.text = "Adding PWA support..."
|
|
201
|
-
addPwaSupport(targetDir, projectName)
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
if (mobile) {
|
|
205
|
-
spinner.text = "Adding Capacitor support..."
|
|
206
|
-
addMobileSupport(targetDir, pkg, projectName, templateName)
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
for (const depType of ["dependencies", "devDependencies"]) {
|
|
210
|
-
if (pkg[depType]) {
|
|
211
|
-
for (const [name, version] of Object.entries(pkg[depType])) {
|
|
212
|
-
if (version.startsWith("workspace:")) {
|
|
213
|
-
spinner.text = `Fetching latest version of ${name}...`
|
|
214
|
-
pkg[depType][name] = getLatestVersion(name)
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, INDENTATION) + EOL)
|
|
221
|
-
|
|
222
|
-
spinner.succeed(`Created ${projectName} at ${targetDir}`)
|
|
223
|
-
console.log(`\nNext steps:`)
|
|
224
|
-
console.log(` cd ${projectName}`)
|
|
225
|
-
console.log(` pnpm install`)
|
|
226
|
-
console.log(` pnpm dev`)
|
|
190
|
+
return
|
|
227
191
|
}
|
|
228
|
-
} catch (error) {
|
|
229
|
-
spinner.fail("Operation failed.")
|
|
230
|
-
console.error(error)
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
function copyDir(src, dest) {
|
|
234
|
-
fs.mkdirSync(dest, { recursive: true })
|
|
235
|
-
|
|
236
|
-
const entries = fs.readdirSync(src, { withFileTypes: true })
|
|
237
|
-
|
|
238
|
-
for (const entry of entries) {
|
|
239
|
-
const srcPath = path.join(src, entry.name)
|
|
240
|
-
const destPath = path.join(dest, entry.name)
|
|
241
192
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
continue
|
|
246
|
-
}
|
|
193
|
+
const pkgPath = path.join(targetDir, "package.json")
|
|
194
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"))
|
|
195
|
+
pkg.name = projectName
|
|
247
196
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
fs.copyFileSync(srcPath, destPath)
|
|
252
|
-
}
|
|
197
|
+
if (pwa) {
|
|
198
|
+
spinner.text = "Adding PWA support..."
|
|
199
|
+
addPwaSupport(targetDir, pkg, projectName)
|
|
253
200
|
}
|
|
254
|
-
}
|
|
255
201
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
encoding: "utf-8",
|
|
260
|
-
stdio: "pipe", // Prevent npm view output from cluttering the console
|
|
261
|
-
timeout: NPM_VIEW_TIMEOUT_MS,
|
|
262
|
-
}).trim()
|
|
263
|
-
return `^${version}`
|
|
264
|
-
} catch {
|
|
265
|
-
return "latest" // fallback
|
|
202
|
+
if (mobile) {
|
|
203
|
+
spinner.text = "Adding Capacitor support..."
|
|
204
|
+
addMobileSupport(targetDir, pkg, projectName)
|
|
266
205
|
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
function parseCliOptions(args) {
|
|
271
|
-
return {
|
|
272
|
-
mobile: readBooleanFlag(args, "mobile"),
|
|
273
|
-
pwa: readBooleanFlag(args, "pwa"),
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
function readBooleanFlag(args, name) {
|
|
278
|
-
if (args.includes(`--${name}`)) {
|
|
279
|
-
return true
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
if (args.includes(`--no-${name}`)) {
|
|
283
|
-
return false
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
return null
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
function addPwaSupport(targetDir, projectName) {
|
|
290
|
-
const publicDir = path.join(targetDir, "public")
|
|
291
|
-
fs.mkdirSync(publicDir, { recursive: true })
|
|
292
|
-
fs.writeFileSync(path.join(publicDir, "sw.js"), createServiceWorkerSource())
|
|
293
|
-
fs.writeFileSync(
|
|
294
|
-
path.join(publicDir, "manifest.webmanifest"),
|
|
295
|
-
JSON.stringify(createWebManifest(projectName), null, INDENTATION) + EOL,
|
|
296
|
-
)
|
|
297
|
-
|
|
298
|
-
injectPwaHeadTags(path.join(targetDir, "index.html"))
|
|
299
|
-
injectServiceWorkerRegistration(targetDir)
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
function addMobileSupport(targetDir, pkg, projectName, templateName) {
|
|
303
|
-
pkg.dependencies ??= {}
|
|
304
|
-
pkg.devDependencies ??= {}
|
|
305
|
-
pkg.scripts ??= {}
|
|
306
|
-
|
|
307
|
-
pkg.dependencies["@capacitor/core"] = CAPACITOR_VERSION
|
|
308
|
-
pkg.devDependencies["@capacitor/android"] = CAPACITOR_VERSION
|
|
309
|
-
pkg.devDependencies["@capacitor/cli"] = CAPACITOR_VERSION
|
|
310
|
-
pkg.devDependencies["@capacitor/ios"] = CAPACITOR_VERSION
|
|
311
|
-
|
|
312
|
-
pkg.scripts.cap = "cap"
|
|
313
|
-
pkg.scripts["mobile:sync"] = "pnpm build && cap sync"
|
|
314
|
-
pkg.scripts["mobile:ios"] = "pnpm build && cap run ios"
|
|
315
|
-
pkg.scripts["mobile:android"] = "pnpm build && cap run android"
|
|
316
|
-
|
|
317
|
-
const extension = templateName.startsWith("ts") ? "ts" : "js"
|
|
318
|
-
fs.writeFileSync(
|
|
319
|
-
path.join(targetDir, `capacitor.config.ts`), // HACK: apparently only ts config works!
|
|
320
|
-
createCapacitorConfigSource(projectName, extension),
|
|
321
|
-
)
|
|
322
|
-
|
|
323
|
-
appendMobileSetupToReadme(targetDir)
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
function appendMobileSetupToReadme(targetDir) {
|
|
327
|
-
const readmePath = path.join(targetDir, "README.md")
|
|
328
|
-
if (!fs.existsSync(readmePath)) {
|
|
329
|
-
return
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
const readme = fs.readFileSync(readmePath, "utf-8")
|
|
333
|
-
if (readme.includes("## Mobile Setup")) {
|
|
334
|
-
return
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
const mobileReadme = `
|
|
338
|
-
## Mobile Setup
|
|
339
|
-
|
|
340
|
-
This project includes Capacitor for Android and iOS support.
|
|
341
|
-
|
|
342
|
-
Please note that mobile development depends on external platform tooling that is not installed or managed by the CLI. If the app does not just work after scaffolding, it is usually because one or more native prerequisites are missing on the machine.
|
|
343
|
-
|
|
344
|
-
### Android
|
|
345
|
-
|
|
346
|
-
Before you run the native apps, make sure you have:
|
|
347
|
-
|
|
348
|
-
- Android Studio installed with the Android SDK: https://developer.android.com/studio
|
|
349
|
-
- A Java Development Kit compatible with Capacitor. Capacitor is currently known to work reliably with JDK 21, so prefer JDK 21 over newer versions if you run into Java compatibility errors.
|
|
350
|
-
- The SDK path available via ANDROID_HOME (for example, on macOS this is often $HOME/Library/Android/sdk)
|
|
351
|
-
|
|
352
|
-
On macOS, the following shell variables are commonly needed:
|
|
353
|
-
|
|
354
|
-
\`\`\`bash
|
|
355
|
-
export JAVA_HOME="/path/to/jdk-21"
|
|
356
|
-
export ANDROID_HOME="$HOME/Library/Android/sdk"
|
|
357
|
-
export PATH="$ANDROID_HOME/platform-tools:$PATH"
|
|
358
|
-
\`\`\`
|
|
359
|
-
|
|
360
|
-
### iOS
|
|
361
|
-
|
|
362
|
-
For iOS you also need the Apple toolchain and device setup on a Mac:
|
|
363
|
-
|
|
364
|
-
- Xcode from the App Store
|
|
365
|
-
- iOS development tools and simulator components (this can be very large, often multiple GB)
|
|
366
|
-
- A valid Apple Developer team / signing identity in Xcode
|
|
367
|
-
- Device trust setup for your iPhone in Settings > General > VPN & Device Management
|
|
368
|
-
|
|
369
|
-
The most common first-time setup commands are:
|
|
370
|
-
|
|
371
|
-
\`\`\`bash
|
|
372
|
-
sudo xcode-select -switch /Applications/Xcode.app/Contents/Developer
|
|
373
|
-
sudo xcodebuild -license accept
|
|
374
|
-
\`\`\`
|
|
375
|
-
|
|
376
|
-
If you are using a physical device, you may also need to approve the developer certificate on the device and ensure the app is signed with a valid team.
|
|
377
|
-
|
|
378
|
-
### Official resources
|
|
379
206
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
\`\`\`bash
|
|
388
|
-
pnpm mobile:sync
|
|
389
|
-
pnpm mobile:android
|
|
390
|
-
# or
|
|
391
|
-
pnpm mobile:ios
|
|
392
|
-
\`\`\`
|
|
393
|
-
`
|
|
394
|
-
|
|
395
|
-
fs.writeFileSync(
|
|
396
|
-
readmePath,
|
|
397
|
-
`${readme.trimEnd()}${EOL}${EOL}${mobileReadme.trim()}${EOL}`,
|
|
398
|
-
)
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
function createServiceWorkerSource() {
|
|
402
|
-
return `const CACHE_NAME = "inglorious-app-v1"
|
|
403
|
-
const APP_SHELL = ["/", "/index.html", "/logo.png", "/style.css"]
|
|
404
|
-
|
|
405
|
-
self.addEventListener("install", (event) => {
|
|
406
|
-
event.waitUntil(
|
|
407
|
-
caches.open(CACHE_NAME).then((cache) => cache.addAll(APP_SHELL)),
|
|
408
|
-
)
|
|
409
|
-
self.skipWaiting()
|
|
410
|
-
})
|
|
411
|
-
|
|
412
|
-
self.addEventListener("activate", (event) => {
|
|
413
|
-
event.waitUntil(
|
|
414
|
-
caches
|
|
415
|
-
.keys()
|
|
416
|
-
.then((keys) =>
|
|
417
|
-
Promise.all(keys.map((key) => key === CACHE_NAME ? null : caches.delete(key))),
|
|
418
|
-
),
|
|
419
|
-
)
|
|
420
|
-
self.clients.claim()
|
|
421
|
-
})
|
|
422
|
-
|
|
423
|
-
self.addEventListener("fetch", (event) => {
|
|
424
|
-
if (event.request.method !== "GET") {
|
|
425
|
-
return
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
event.respondWith(
|
|
429
|
-
caches.match(event.request).then((cachedResponse) => {
|
|
430
|
-
if (cachedResponse) {
|
|
431
|
-
return cachedResponse
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
return fetch(event.request).catch(() => {
|
|
435
|
-
if (event.request.mode === "navigate") {
|
|
436
|
-
return caches.match("/index.html")
|
|
207
|
+
for (const depType of ["dependencies", "devDependencies"]) {
|
|
208
|
+
if (pkg[depType]) {
|
|
209
|
+
for (const [name, version] of Object.entries(pkg[depType])) {
|
|
210
|
+
if (version.startsWith("workspace:")) {
|
|
211
|
+
spinner.text = `Fetching latest version of ${name}...`
|
|
212
|
+
pkg[depType][name] = getLatestVersion(name)
|
|
213
|
+
}
|
|
437
214
|
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
438
217
|
|
|
439
|
-
|
|
440
|
-
})
|
|
441
|
-
}),
|
|
442
|
-
)
|
|
443
|
-
})
|
|
444
|
-
`
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
function createWebManifest(projectName) {
|
|
448
|
-
return {
|
|
449
|
-
name: projectName,
|
|
450
|
-
short_name: projectName,
|
|
451
|
-
description: "An Inglorious Web app.",
|
|
452
|
-
start_url: "/",
|
|
453
|
-
display: "standalone",
|
|
454
|
-
background_color: "#ffffff",
|
|
455
|
-
theme_color: "#111827",
|
|
456
|
-
icons: [
|
|
457
|
-
{
|
|
458
|
-
src: "/logo.png",
|
|
459
|
-
sizes: "192x192",
|
|
460
|
-
type: "image/png",
|
|
461
|
-
},
|
|
462
|
-
{
|
|
463
|
-
src: "/logo.png",
|
|
464
|
-
sizes: "512x512",
|
|
465
|
-
type: "image/png",
|
|
466
|
-
},
|
|
467
|
-
],
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
function injectPwaHeadTags(indexPath) {
|
|
472
|
-
let index = fs.readFileSync(indexPath, "utf-8")
|
|
473
|
-
|
|
474
|
-
if (!index.includes('rel="manifest"')) {
|
|
475
|
-
index = index.replace(
|
|
476
|
-
/^([ \t]*<link rel="icon"[^>]*>\r?\n)/m,
|
|
477
|
-
`$1 <link rel="manifest" href="/manifest.webmanifest" />${EOL}`,
|
|
478
|
-
)
|
|
479
|
-
}
|
|
218
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, INDENTATION) + EOL)
|
|
480
219
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
)
|
|
220
|
+
spinner.succeed(`Created ${projectName} at ${targetDir}`)
|
|
221
|
+
console.log(`\nNext steps:`)
|
|
222
|
+
console.log(` cd ${projectName}`)
|
|
223
|
+
console.log(` pnpm install`)
|
|
224
|
+
console.log(` pnpm dev`)
|
|
225
|
+
} catch (error) {
|
|
226
|
+
spinner.fail("Operation failed.")
|
|
227
|
+
console.error(error)
|
|
486
228
|
}
|
|
487
|
-
|
|
488
|
-
fs.writeFileSync(indexPath, index)
|
|
489
229
|
}
|
|
490
230
|
|
|
491
|
-
function
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
main = main.replace(
|
|
502
|
-
/^([ \t]*import \{ mount \} from "@inglorious\/web"\r?\n)/m,
|
|
503
|
-
(match) =>
|
|
504
|
-
`${match}import { isNative } from "@inglorious/web/mobile"${EOL}`,
|
|
505
|
-
)
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
if (!main.includes('navigator.serviceWorker.register("/sw.js")')) {
|
|
509
|
-
main = `${main.trimEnd()}${EOL}${EOL}if (!isNative() && "serviceWorker" in navigator) {
|
|
510
|
-
navigator.serviceWorker.register("/sw.js")
|
|
511
|
-
}${EOL}`
|
|
231
|
+
function getLatestVersion(packageName) {
|
|
232
|
+
try {
|
|
233
|
+
const version = execSync(`npm view ${packageName} version`, {
|
|
234
|
+
encoding: "utf-8",
|
|
235
|
+
stdio: "pipe",
|
|
236
|
+
timeout: NPM_VIEW_TIMEOUT_MS,
|
|
237
|
+
}).trim()
|
|
238
|
+
return `^${version}`
|
|
239
|
+
} catch {
|
|
240
|
+
return "latest"
|
|
512
241
|
}
|
|
513
|
-
|
|
514
|
-
fs.writeFileSync(mainPath, main)
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
function findMainPath(targetDir) {
|
|
518
|
-
const candidates = [
|
|
519
|
-
path.join(targetDir, "src/main.ts"),
|
|
520
|
-
path.join(targetDir, "src/main.js"),
|
|
521
|
-
]
|
|
522
|
-
|
|
523
|
-
return candidates.find((candidate) => fs.existsSync(candidate)) ?? null
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
function createCapacitorConfigSource(projectName /* , extension */) {
|
|
527
|
-
const appId = `com.inglorious.${toJavaIdentifier(projectName)}`
|
|
528
|
-
|
|
529
|
-
//if (extension === "ts") { // HACK: apparently only ts config files work!
|
|
530
|
-
return `import type { CapacitorConfig } from "@capacitor/cli"
|
|
531
|
-
|
|
532
|
-
const config: CapacitorConfig = {
|
|
533
|
-
appId: "${appId}",
|
|
534
|
-
appName: "${projectName}",
|
|
535
|
-
webDir: "dist",
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
export default config
|
|
539
|
-
`
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
function toJavaIdentifier(value) {
|
|
543
|
-
const identifier = value
|
|
544
|
-
.toLowerCase()
|
|
545
|
-
.replace(/[^a-z0-9]+/g, " ")
|
|
546
|
-
.trim()
|
|
547
|
-
.split(" ")
|
|
548
|
-
.filter(Boolean)
|
|
549
|
-
.map((part, index) =>
|
|
550
|
-
index === FIRST_ITEM_INDEX
|
|
551
|
-
? part
|
|
552
|
-
: part.charAt(FIRST_CHARACTER_INDEX).toUpperCase() +
|
|
553
|
-
part.slice(AFTER_FIRST_CHARACTER_INDEX),
|
|
554
|
-
)
|
|
555
|
-
.join("")
|
|
556
|
-
.replace(/^[^a-z]+/, "")
|
|
557
|
-
|
|
558
|
-
return identifier || "app"
|
|
559
242
|
}
|
|
560
243
|
|
|
561
244
|
main().catch((e) => {
|
package/bin/lib/cli.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function parseCliOptions(args) {
|
|
2
|
+
return {
|
|
3
|
+
mobile: readBooleanFlag(args, "mobile"),
|
|
4
|
+
pwa: readBooleanFlag(args, "pwa"),
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function readBooleanFlag(args, name) {
|
|
9
|
+
if (args.includes(`--${name}`)) {
|
|
10
|
+
return true
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (args.includes(`--no-${name}`)) {
|
|
14
|
+
return false
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return null
|
|
18
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const CAPACITOR_VERSION = "^8.5.1"
|
|
2
|
+
export const VITE_PLUGIN_PWA_VERSION = "^1.3.0"
|
|
3
|
+
export const VITE_PWA_ASSETS_GENERATOR_VERSION = "^1.0.2"
|
|
4
|
+
export const TYPESCRIPT_VERSION = "^7.0.2"
|
|
5
|
+
export const WORKBOX_WINDOW_VERSION = "^7.4.1"
|
|
6
|
+
|
|
7
|
+
export const INDENTATION = 2
|
|
8
|
+
export const AFTER_FIRST_CHARACTER_INDEX = 1
|
|
9
|
+
export const EMPTY_LENGTH = 0
|
|
10
|
+
export const FIRST_CHARACTER_INDEX = 0
|
|
11
|
+
export const FIRST_ITEM_INDEX = 0
|
|
12
|
+
export const NPM_VIEW_TIMEOUT_MS = 10_000
|
|
13
|
+
export const USER_ARG_START_INDEX = 2
|
|
14
|
+
export const VITE_APP_TEMPLATES = new Set(["js", "ts"])
|
package/bin/lib/file.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import fs from "fs"
|
|
2
|
+
import { EOL } from "os"
|
|
3
|
+
import path from "path"
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
AFTER_FIRST_CHARACTER_INDEX,
|
|
7
|
+
FIRST_CHARACTER_INDEX,
|
|
8
|
+
FIRST_ITEM_INDEX,
|
|
9
|
+
} from "./constants.js"
|
|
10
|
+
|
|
11
|
+
export function copyDir(src, dest) {
|
|
12
|
+
fs.mkdirSync(dest, { recursive: true })
|
|
13
|
+
|
|
14
|
+
const entries = fs.readdirSync(src, { withFileTypes: true })
|
|
15
|
+
|
|
16
|
+
for (const entry of entries) {
|
|
17
|
+
const srcPath = path.join(src, entry.name)
|
|
18
|
+
const destPath = path.join(dest, entry.name)
|
|
19
|
+
|
|
20
|
+
if (entry.name === "gitignore") {
|
|
21
|
+
fs.copyFileSync(srcPath, path.join(dest, ".gitignore"))
|
|
22
|
+
continue
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (entry.isDirectory()) {
|
|
26
|
+
copyDir(srcPath, destPath)
|
|
27
|
+
} else {
|
|
28
|
+
fs.copyFileSync(srcPath, destPath)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function findMainPath(targetDir) {
|
|
34
|
+
const candidates = [
|
|
35
|
+
path.join(targetDir, "src/main.ts"),
|
|
36
|
+
path.join(targetDir, "src/main.js"),
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
return candidates.find((candidate) => fs.existsSync(candidate)) ?? null
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function findViteConfigPath(targetDir) {
|
|
43
|
+
const candidates = [
|
|
44
|
+
path.join(targetDir, "vite.config.ts"),
|
|
45
|
+
path.join(targetDir, "vite.config.js"),
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
return candidates.find((candidate) => fs.existsSync(candidate)) ?? null
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function injectAfterImport(source, importSource, newImport) {
|
|
52
|
+
const importLinePattern = new RegExp(
|
|
53
|
+
`^([ \\t]*import .+ from "${escapeRegExp(importSource)}"\\r?\\n)`,
|
|
54
|
+
"m",
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
if (!importLinePattern.test(source)) {
|
|
58
|
+
return `${newImport}${EOL}${source}`
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return source.replace(
|
|
62
|
+
importLinePattern,
|
|
63
|
+
(match) => `${match}${newImport}${EOL}`,
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function escapeRegExp(value) {
|
|
68
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function toJavaIdentifier(value) {
|
|
72
|
+
const identifier = value
|
|
73
|
+
.toLowerCase()
|
|
74
|
+
.replace(/[^a-z0-9]+/g, " ")
|
|
75
|
+
.trim()
|
|
76
|
+
.split(" ")
|
|
77
|
+
.filter(Boolean)
|
|
78
|
+
.map((part, index) =>
|
|
79
|
+
index === FIRST_ITEM_INDEX
|
|
80
|
+
? part
|
|
81
|
+
: part.charAt(FIRST_CHARACTER_INDEX).toUpperCase() +
|
|
82
|
+
part.slice(AFTER_FIRST_CHARACTER_INDEX),
|
|
83
|
+
)
|
|
84
|
+
.join("")
|
|
85
|
+
.replace(/^[^a-z]+/, "")
|
|
86
|
+
|
|
87
|
+
return identifier || "app"
|
|
88
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import fs from "fs"
|
|
2
|
+
import { EOL } from "os"
|
|
3
|
+
import path from "path"
|
|
4
|
+
|
|
5
|
+
import { CAPACITOR_VERSION, TYPESCRIPT_VERSION } from "./constants.js"
|
|
6
|
+
import { toJavaIdentifier } from "./file.js"
|
|
7
|
+
|
|
8
|
+
export function addMobileSupport(targetDir, pkg, projectName) {
|
|
9
|
+
pkg.dependencies ??= {}
|
|
10
|
+
pkg.devDependencies ??= {}
|
|
11
|
+
pkg.scripts ??= {}
|
|
12
|
+
|
|
13
|
+
pkg.dependencies["@capacitor/core"] = CAPACITOR_VERSION
|
|
14
|
+
pkg.devDependencies["@capacitor/android"] = CAPACITOR_VERSION
|
|
15
|
+
pkg.devDependencies["@capacitor/cli"] = CAPACITOR_VERSION
|
|
16
|
+
pkg.devDependencies["@capacitor/ios"] = CAPACITOR_VERSION
|
|
17
|
+
pkg.devDependencies["typescript"] = TYPESCRIPT_VERSION
|
|
18
|
+
|
|
19
|
+
pkg.scripts.cap = "cap"
|
|
20
|
+
pkg.scripts["mobile:init:ios"] = "cap add ios"
|
|
21
|
+
pkg.scripts["mobile:init:android"] = "cap add android"
|
|
22
|
+
pkg.scripts["mobile:sync"] = "pnpm build && cap sync"
|
|
23
|
+
pkg.scripts["mobile:ios"] = "pnpm build && cap run ios"
|
|
24
|
+
pkg.scripts["mobile:android"] = "pnpm build && cap run android"
|
|
25
|
+
|
|
26
|
+
fs.writeFileSync(
|
|
27
|
+
path.join(targetDir, "capacitor.config.ts"),
|
|
28
|
+
createCapacitorConfigSource(projectName),
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
appendMobileSetupToReadme(targetDir)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function appendMobileSetupToReadme(targetDir) {
|
|
35
|
+
const readmePath = path.join(targetDir, "README.md")
|
|
36
|
+
if (!fs.existsSync(readmePath)) {
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const readme = fs.readFileSync(readmePath, "utf-8")
|
|
41
|
+
if (readme.includes("## Mobile Setup")) {
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const mobileReadme = `
|
|
46
|
+
## Mobile Setup
|
|
47
|
+
|
|
48
|
+
This project includes Capacitor for Android and iOS support.
|
|
49
|
+
|
|
50
|
+
Please note that mobile development depends on external platform tooling that is not installed or managed by the CLI. If the app does not just work after scaffolding, it is usually because one or more native prerequisites are missing on the machine.
|
|
51
|
+
|
|
52
|
+
### Android
|
|
53
|
+
|
|
54
|
+
Before you run the native apps, make sure you have:
|
|
55
|
+
|
|
56
|
+
- Android Studio installed with the Android SDK: https://developer.android.com/studio
|
|
57
|
+
- A Java Development Kit compatible with Capacitor. Capacitor is currently known to work reliably with JDK 21, so prefer JDK 21 over newer versions if you run into Java compatibility errors.
|
|
58
|
+
- The SDK path available via ANDROID_HOME (for example, on macOS this is often $HOME/Library/Android/sdk)
|
|
59
|
+
|
|
60
|
+
On macOS, the following shell variables are commonly needed:
|
|
61
|
+
|
|
62
|
+
\`\`\`bash
|
|
63
|
+
export JAVA_HOME="/path/to/jdk-21"
|
|
64
|
+
export ANDROID_HOME="$HOME/Library/Android/sdk"
|
|
65
|
+
export PATH="$ANDROID_HOME/platform-tools:$PATH"
|
|
66
|
+
\`\`\`
|
|
67
|
+
|
|
68
|
+
### iOS
|
|
69
|
+
|
|
70
|
+
For iOS you also need the Apple toolchain and device setup on a Mac:
|
|
71
|
+
|
|
72
|
+
- Xcode from the App Store
|
|
73
|
+
- iOS development tools and simulator components (this can be very large, often multiple GB)
|
|
74
|
+
- A valid Apple Developer team / signing identity in Xcode
|
|
75
|
+
- Device trust setup for your iPhone in Settings > General > VPN & Device Management
|
|
76
|
+
|
|
77
|
+
The most common first-time setup commands are:
|
|
78
|
+
|
|
79
|
+
\`\`\`bash
|
|
80
|
+
sudo xcode-select -switch /Applications/Xcode.app/Contents/Developer
|
|
81
|
+
sudo xcodebuild -license accept
|
|
82
|
+
\`\`\`
|
|
83
|
+
|
|
84
|
+
If you are using a physical device, you may also need to approve the developer certificate on the device and ensure the app is signed with a valid team.
|
|
85
|
+
|
|
86
|
+
### Official resources
|
|
87
|
+
|
|
88
|
+
- Android: https://capacitorjs.com/docs/android
|
|
89
|
+
- iOS: https://capacitorjs.com/docs/ios
|
|
90
|
+
- Android Studio: https://developer.android.com/studio
|
|
91
|
+
- Xcode: https://developer.apple.com/xcode/
|
|
92
|
+
|
|
93
|
+
Then sync and open the native project:
|
|
94
|
+
|
|
95
|
+
\`\`\`bash
|
|
96
|
+
pnpm mobile:sync
|
|
97
|
+
pnpm mobile:android
|
|
98
|
+
# or
|
|
99
|
+
pnpm mobile:ios
|
|
100
|
+
\`\`\`
|
|
101
|
+
`
|
|
102
|
+
|
|
103
|
+
fs.writeFileSync(
|
|
104
|
+
readmePath,
|
|
105
|
+
`${readme.trimEnd()}${EOL}${EOL}${mobileReadme.trim()}${EOL}`,
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function createCapacitorConfigSource(projectName) {
|
|
110
|
+
const appId = `com.inglorious.${toJavaIdentifier(projectName)}`
|
|
111
|
+
|
|
112
|
+
return `import type { CapacitorConfig } from "@capacitor/cli"
|
|
113
|
+
|
|
114
|
+
const config: CapacitorConfig = {
|
|
115
|
+
appId: "${appId}",
|
|
116
|
+
appName: "${projectName}",
|
|
117
|
+
webDir: "dist",
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export default config
|
|
121
|
+
`
|
|
122
|
+
}
|
package/bin/lib/pwa.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import fs from "fs"
|
|
2
|
+
import { EOL } from "os"
|
|
3
|
+
import path from "path"
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
VITE_PLUGIN_PWA_VERSION,
|
|
7
|
+
VITE_PWA_ASSETS_GENERATOR_VERSION,
|
|
8
|
+
WORKBOX_WINDOW_VERSION,
|
|
9
|
+
} from "./constants.js"
|
|
10
|
+
import { findMainPath, findViteConfigPath, injectAfterImport } from "./file.js"
|
|
11
|
+
|
|
12
|
+
export function addPwaSupport(targetDir, pkg, projectName) {
|
|
13
|
+
pkg.devDependencies ??= {}
|
|
14
|
+
pkg.devDependencies["vite-plugin-pwa"] = VITE_PLUGIN_PWA_VERSION
|
|
15
|
+
pkg.devDependencies["@vite-pwa/assets-generator"] =
|
|
16
|
+
VITE_PWA_ASSETS_GENERATOR_VERSION
|
|
17
|
+
pkg.devDependencies["workbox-window"] = WORKBOX_WINDOW_VERSION
|
|
18
|
+
|
|
19
|
+
const publicDir = path.join(targetDir, "public")
|
|
20
|
+
fs.mkdirSync(publicDir, { recursive: true })
|
|
21
|
+
|
|
22
|
+
injectPwaPluginConfig(targetDir, projectName)
|
|
23
|
+
writePwaRegistration(targetDir)
|
|
24
|
+
injectServiceWorkerRegistration(targetDir)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function injectServiceWorkerRegistration(targetDir) {
|
|
28
|
+
const mainPath = findMainPath(targetDir)
|
|
29
|
+
|
|
30
|
+
if (!mainPath) {
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let main = fs.readFileSync(mainPath, "utf-8")
|
|
35
|
+
|
|
36
|
+
if (!main.includes("./pwa")) {
|
|
37
|
+
main = injectAfterImport(main, "@inglorious/web", `import "./pwa"`)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
fs.writeFileSync(mainPath, main)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function injectPwaPluginConfig(targetDir, projectName) {
|
|
44
|
+
const viteConfigPath = findViteConfigPath(targetDir)
|
|
45
|
+
|
|
46
|
+
if (!viteConfigPath) {
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let viteConfig = fs.readFileSync(viteConfigPath, "utf-8")
|
|
51
|
+
|
|
52
|
+
if (!viteConfig.includes("vite-plugin-pwa")) {
|
|
53
|
+
viteConfig = injectAfterImport(
|
|
54
|
+
viteConfig,
|
|
55
|
+
"vite",
|
|
56
|
+
`import { VitePWA } from "vite-plugin-pwa"`,
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!viteConfig.includes("VitePWA(")) {
|
|
61
|
+
viteConfig = viteConfig.replace(
|
|
62
|
+
/plugins: \[/,
|
|
63
|
+
`plugins: [${EOL} ${createVitePwaConfig(projectName)},`,
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
fs.writeFileSync(viteConfigPath, viteConfig)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function writePwaRegistration(targetDir) {
|
|
71
|
+
const mainPath = findMainPath(targetDir)
|
|
72
|
+
|
|
73
|
+
if (!mainPath) {
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const extension = path.extname(mainPath)
|
|
78
|
+
const pwaPath = path.join(targetDir, `src/pwa${extension}`)
|
|
79
|
+
|
|
80
|
+
fs.writeFileSync(pwaPath, createPwaRegistrationSource(extension))
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function createPwaRegistrationSource(extension) {
|
|
84
|
+
if (extension === ".ts") {
|
|
85
|
+
return `/// <reference types="vite-plugin-pwa/client" />
|
|
86
|
+
import { isNative } from "@inglorious/web/mobile"
|
|
87
|
+
import { registerSW } from "virtual:pwa-register"
|
|
88
|
+
|
|
89
|
+
if (!isNative()) {
|
|
90
|
+
registerSW()
|
|
91
|
+
}
|
|
92
|
+
`
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return `import { isNative } from "@inglorious/web/mobile"
|
|
96
|
+
import { registerSW } from "virtual:pwa-register"
|
|
97
|
+
|
|
98
|
+
if (!isNative()) {
|
|
99
|
+
registerSW()
|
|
100
|
+
}
|
|
101
|
+
`
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function createVitePwaConfig(projectName) {
|
|
105
|
+
return `VitePWA({
|
|
106
|
+
injectRegister: false,
|
|
107
|
+
registerType: "autoUpdate",
|
|
108
|
+
pwaAssets: {
|
|
109
|
+
image: "public/logo.png",
|
|
110
|
+
preset: "minimal-2023",
|
|
111
|
+
},
|
|
112
|
+
manifest: {
|
|
113
|
+
name: "${projectName}",
|
|
114
|
+
short_name: "${projectName}",
|
|
115
|
+
description: "An Inglorious Web app.",
|
|
116
|
+
theme_color: "#ffffff",
|
|
117
|
+
background_color: "#ffffff",
|
|
118
|
+
display: "standalone",
|
|
119
|
+
start_url: "/",
|
|
120
|
+
},
|
|
121
|
+
})`
|
|
122
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inglorious/create-app",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "A scaffolding tool to quickly create a new app with Inglorious Web.",
|
|
5
5
|
"author": "Matteo Antony Mistretta <antony.mistretta@gmail.com> (https://ingloriouscoderz.it)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"prompts": "^2.4.2"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"
|
|
41
|
-
"
|
|
40
|
+
"@inglorious/eslint-config": "1.1.2",
|
|
41
|
+
"prettier": "^3.6.2"
|
|
42
42
|
},
|
|
43
43
|
"scripts": {
|
|
44
44
|
"format": "prettier --write .",
|
|
@@ -19,13 +19,14 @@
|
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@inglorious/eslint-config": "workspace:*",
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"vite": "^
|
|
22
|
+
"@inglorious/vite-plugin-hmr": "workspace:*",
|
|
23
|
+
"eslint": "^10.9.1",
|
|
24
|
+
"prettier": "^3.9.6",
|
|
25
|
+
"rollup-plugin-minify-template-literals": "^2.2.0",
|
|
26
|
+
"sharp": "^0.35.4",
|
|
27
|
+
"svgo": "^4.1.0",
|
|
28
|
+
"vite": "^8.2.2",
|
|
29
|
+
"vite-plugin-image-optimizer": "^2.0.3"
|
|
29
30
|
},
|
|
30
31
|
"packageManager": "pnpm@10.15.0",
|
|
31
32
|
"engines": {
|
|
@@ -19,14 +19,15 @@
|
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@inglorious/eslint-config": "workspace:*",
|
|
22
|
+
"@inglorious/vite-plugin-hmr": "workspace:*",
|
|
22
23
|
"@inglorious/vite-plugin-jsx": "workspace:*",
|
|
23
|
-
"eslint": "^9.
|
|
24
|
-
"prettier": "^3.6
|
|
25
|
-
"rollup-plugin-minify-template-literals": "^
|
|
26
|
-
"sharp": "^0.
|
|
27
|
-
"svgo": "^4.
|
|
28
|
-
"vite
|
|
29
|
-
"vite": "^
|
|
24
|
+
"eslint": "^10.9.1",
|
|
25
|
+
"prettier": "^3.9.6",
|
|
26
|
+
"rollup-plugin-minify-template-literals": "^2.2.0",
|
|
27
|
+
"sharp": "^0.35.4",
|
|
28
|
+
"svgo": "^4.1.0",
|
|
29
|
+
"vite": "^8.2.2",
|
|
30
|
+
"vite-plugin-image-optimizer": "^2.0.3"
|
|
30
31
|
},
|
|
31
32
|
"packageManager": "pnpm@10.15.0",
|
|
32
33
|
"engines": {
|
|
@@ -19,13 +19,14 @@
|
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@inglorious/eslint-config": "workspace:*",
|
|
22
|
+
"@inglorious/vite-plugin-hmr": "workspace:*",
|
|
22
23
|
"@inglorious/vite-plugin-vue": "workspace:*",
|
|
23
|
-
"eslint": "^9.
|
|
24
|
-
"prettier": "^3.6
|
|
25
|
-
"rollup-plugin-minify-template-literals": "^
|
|
26
|
-
"sharp": "^0.
|
|
27
|
-
"svgo": "^4.
|
|
28
|
-
"vite": "^
|
|
24
|
+
"eslint": "^10.9.1",
|
|
25
|
+
"prettier": "^3.9.6",
|
|
26
|
+
"rollup-plugin-minify-template-literals": "^2.2.0",
|
|
27
|
+
"sharp": "^0.35.4",
|
|
28
|
+
"svgo": "^4.1.0",
|
|
29
|
+
"vite": "^8.2.2",
|
|
29
30
|
"vite-plugin-image-optimizer": "^2.0.3"
|
|
30
31
|
},
|
|
31
32
|
"packageManager": "pnpm@10.15.0",
|
|
@@ -19,14 +19,15 @@
|
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@inglorious/eslint-config": "workspace:*",
|
|
22
|
+
"@inglorious/vite-plugin-hmr": "workspace:*",
|
|
22
23
|
"@types/node": "^25.0.9",
|
|
23
|
-
"eslint": "^9.
|
|
24
|
-
"prettier": "^3.6
|
|
25
|
-
"rollup-plugin-minify-template-literals": "^
|
|
26
|
-
"sharp": "^0.
|
|
27
|
-
"svgo": "^4.
|
|
24
|
+
"eslint": "^10.9.1",
|
|
25
|
+
"prettier": "^3.9.6",
|
|
26
|
+
"rollup-plugin-minify-template-literals": "^2.2.0",
|
|
27
|
+
"sharp": "^0.35.4",
|
|
28
|
+
"svgo": "^4.1.0",
|
|
28
29
|
"typescript": "^5.9.2",
|
|
29
|
-
"vite": "^
|
|
30
|
+
"vite": "^8.2.2",
|
|
30
31
|
"vite-plugin-image-optimizer": "^2.0.3"
|
|
31
32
|
},
|
|
32
33
|
"packageManager": "pnpm@10.15.0",
|
|
@@ -20,15 +20,16 @@
|
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@inglorious/eslint-config": "workspace:*",
|
|
22
22
|
"@inglorious/jsx": "workspace:*",
|
|
23
|
+
"@inglorious/vite-plugin-hmr": "workspace:*",
|
|
23
24
|
"@inglorious/vite-plugin-jsx": "workspace:*",
|
|
24
25
|
"@types/node": "^25.0.9",
|
|
25
|
-
"eslint": "^9.
|
|
26
|
-
"prettier": "^3.6
|
|
27
|
-
"rollup-plugin-minify-template-literals": "^
|
|
28
|
-
"sharp": "^0.
|
|
29
|
-
"svgo": "^4.
|
|
26
|
+
"eslint": "^10.9.1",
|
|
27
|
+
"prettier": "^3.9.6",
|
|
28
|
+
"rollup-plugin-minify-template-literals": "^2.2.0",
|
|
29
|
+
"sharp": "^0.35.4",
|
|
30
|
+
"svgo": "^4.1.0",
|
|
30
31
|
"typescript": "^5.9.2",
|
|
31
|
-
"vite": "^
|
|
32
|
+
"vite": "^8.2.2",
|
|
32
33
|
"vite-plugin-image-optimizer": "^2.0.3"
|
|
33
34
|
},
|
|
34
35
|
"packageManager": "pnpm@10.15.0",
|
|
@@ -19,15 +19,16 @@
|
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@inglorious/eslint-config": "workspace:*",
|
|
22
|
+
"@inglorious/vite-plugin-hmr": "workspace:*",
|
|
22
23
|
"@inglorious/vite-plugin-vue": "workspace:*",
|
|
23
24
|
"@types/node": "^25.0.9",
|
|
24
|
-
"eslint": "^9.
|
|
25
|
-
"prettier": "^3.6
|
|
26
|
-
"rollup-plugin-minify-template-literals": "^
|
|
27
|
-
"sharp": "^0.
|
|
28
|
-
"svgo": "^4.
|
|
25
|
+
"eslint": "^10.9.1",
|
|
26
|
+
"prettier": "^3.9.6",
|
|
27
|
+
"rollup-plugin-minify-template-literals": "^2.2.0",
|
|
28
|
+
"sharp": "^0.35.4",
|
|
29
|
+
"svgo": "^4.1.0",
|
|
29
30
|
"typescript": "^5.9.2",
|
|
30
|
-
"vite": "^
|
|
31
|
+
"vite": "^8.2.2",
|
|
31
32
|
"vite-plugin-image-optimizer": "^2.0.3"
|
|
32
33
|
},
|
|
33
34
|
"packageManager": "pnpm@10.15.0",
|