@inglorious/create-app 1.1.1 → 1.2.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.
Files changed (46) hide show
  1. package/README.md +25 -8
  2. package/bin/index.js +79 -401
  3. package/bin/lib/app-jsx.js +280 -0
  4. package/bin/lib/app-lit.js +275 -0
  5. package/bin/lib/app-vue.js +239 -0
  6. package/bin/lib/app.js +402 -0
  7. package/bin/lib/cli.js +49 -0
  8. package/bin/lib/constants.js +14 -0
  9. package/bin/lib/features.js +142 -0
  10. package/bin/lib/file.js +88 -0
  11. package/bin/lib/imports.js +64 -0
  12. package/bin/lib/mobile.js +140 -0
  13. package/bin/lib/pwa.js +168 -0
  14. package/bin/lib/questions.js +184 -0
  15. package/bin/lib/source.js +58 -0
  16. package/package.json +4 -4
  17. package/templates/js/package.json +8 -7
  18. package/templates/js/vite.config.js +2 -1
  19. package/templates/js-jsx/package.json +8 -7
  20. package/templates/js-jsx/vite.config.js +2 -1
  21. package/templates/js-vue/package.json +7 -6
  22. package/templates/js-vue/vite.config.js +2 -1
  23. package/templates/ssx-js/package.json +8 -7
  24. package/templates/ssx-js/src/site.config.js +2 -1
  25. package/templates/ssx-ts/package.json +9 -7
  26. package/templates/ssx-ts/src/site.config.ts +1 -0
  27. package/templates/ts/package.json +10 -8
  28. package/templates/ts/src/app.ts +4 -2
  29. package/templates/ts/tsconfig.app.json +2 -0
  30. package/templates/ts/tsconfig.node.json +2 -0
  31. package/templates/ts/types/index.d.ts +4 -1
  32. package/templates/ts/vite.config.ts +2 -1
  33. package/templates/ts-jsx/package.json +10 -8
  34. package/templates/ts-jsx/src/app.tsx +2 -2
  35. package/templates/ts-jsx/tsconfig.app.json +2 -0
  36. package/templates/ts-jsx/tsconfig.node.json +2 -0
  37. package/templates/ts-jsx/types/index.d.ts +3 -1
  38. package/templates/ts-jsx/types/vite-end.d.ts +1 -0
  39. package/templates/ts-jsx/vite.config.ts +2 -1
  40. package/templates/ts-vue/eslint.config.ts +1 -1
  41. package/templates/ts-vue/package.json +10 -8
  42. package/templates/ts-vue/src/store/middlewares.ts +5 -2
  43. package/templates/ts-vue/tsconfig.app.json +2 -0
  44. package/templates/ts-vue/tsconfig.node.json +2 -0
  45. package/templates/ts-vue/types/index.d.ts +3 -1
  46. package/templates/ts-vue/vite.config.ts +2 -1
package/README.md CHANGED
@@ -23,17 +23,25 @@ pnpm create @inglorious/app
23
23
 
24
24
  The CLI will guide you through the setup process, asking for a project name and which template you'd like to use.
25
25
 
26
- For JavaScript and TypeScript Vite app templates, the CLI can also add optional app targets:
26
+ For the JavaScript and TypeScript Vite templates, the CLI then asks about the app's features:
27
+
28
+ | Question | Flag | Adds |
29
+ | ------------------------- | ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
30
+ | Use Inglorious UI? | `--ui` / `--no-ui` | `@inglorious/ui`, its base styles and an app bar |
31
+ | Which theme? | `--theme=inglorious\|material\|bootstrap` | The theme's stylesheet and class |
32
+ | Is it a mobile app? | `--mobile` / `--no-mobile` | Capacitor for Android and iOS; with Inglorious UI, a bottom tab bar and safe-area spacing |
33
+ | Add routing? | `--routing` / `--no-routing` | The Router, a home and an about page, and navigation between them |
34
+ | Animate page transitions? | `--transitions` / `--no-transitions` | View transitions between pages (with routing and Inglorious UI) |
35
+ | Which icon packs? | `--icons=material,fontawesome,lucide` / `--no-icons` | Iconify icon sets, drawn by Inglorious UI's `Icon` (with Inglorious UI) |
36
+ | Translate the app? | `--i18n` / `--no-i18n`, `--languages=en,it` | `@inglorious/i18n`, a catalogue per language and a language picker; the first language is the default |
37
+ | Enable PWA support? | `--pwa` / `--no-pwa` | `vite-plugin-pwa`, a manifest, icons and service worker registration |
38
+
39
+ The app is generated in the syntax you chose: lit-html, JSX or Vue. Every question answered by a flag is skipped:
27
40
 
28
41
  ```bash
29
- npm create @inglorious/app@latest -- --pwa
30
- npm create @inglorious/app@latest -- --mobile
31
- npm create @inglorious/app@latest -- --pwa --mobile
42
+ npm create @inglorious/app@latest -- --ui --theme=material --routing --transitions --icons=material --languages=en,it --mobile
32
43
  ```
33
44
 
34
- - `--pwa` adds a web app manifest, service worker, and browser-only service worker registration.
35
- - `--mobile` adds Capacitor dependencies, config, and mobile scripts for iOS and Android.
36
-
37
45
  ## Templates
38
46
 
39
47
  The following templates are available to get you started:
@@ -61,10 +69,19 @@ pnpm install
61
69
  pnpm dev
62
70
  ```
63
71
 
64
- When mobile support is enabled, build and sync native projects with:
72
+ When mobile support is enabled, initialize the native platforms once, then build and sync them with:
65
73
 
66
74
  ```bash
75
+ # Run one or both commands once, depending on the platforms you need
76
+ pnpm mobile:init:android
77
+ pnpm mobile:init:ios
78
+
67
79
  pnpm mobile:sync
80
+
81
+ # Build and open the native app
82
+ pnpm mobile:android
83
+ # or
84
+ pnpm mobile:ios
68
85
  ```
69
86
 
70
87
  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,18 @@ import path from "path"
8
8
  import prompts from "prompts"
9
9
  import { fileURLToPath } from "url"
10
10
 
11
- const INDENTATION = 2
12
- const CAPACITOR_VERSION = "^8.5.0"
13
- const AFTER_FIRST_CHARACTER_INDEX = 1
14
- const EMPTY_LENGTH = 0
15
- const FIRST_CHARACTER_INDEX = 0
16
- const FIRST_ITEM_INDEX = 0
17
- const NPM_VIEW_TIMEOUT_MS = 10_000
18
- const USER_ARG_START_INDEX = 2
19
- const VITE_APP_TEMPLATES = new Set(["js", "ts"])
11
+ import { addAppFeatures } from "./lib/app.js"
12
+ import { parseCliOptions } from "./lib/cli.js"
13
+ import {
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"
22
+ import { askAppFeatures } from "./lib/questions.js"
20
23
 
21
24
  async function main() {
22
25
  const __dirname = path.dirname(fileURLToPath(import.meta.url))
@@ -80,10 +83,7 @@ async function main() {
80
83
  }
81
84
 
82
85
  let renderer = "lit-html"
83
- let pwa = cliOptions.pwa === true
84
- let mobile = cliOptions.mobile === true
85
86
 
86
- // Ask about renderer for non-minimal templates
87
87
  if (baseTemplate !== "minimal") {
88
88
  const { selectedRenderer } = await prompts(
89
89
  {
@@ -119,46 +119,26 @@ async function main() {
119
119
  renderer = selectedRenderer
120
120
  }
121
121
 
122
- const supportsAppFeatures = VITE_APP_TEMPLATES.has(baseTemplate)
123
-
124
- if (supportsAppFeatures) {
125
- const featureQuestions = []
126
-
127
- if (cliOptions.pwa === null) {
128
- featureQuestions.push({
129
- type: "confirm",
130
- name: "pwa",
131
- message: "Enable PWA support?",
132
- initial: false,
133
- })
134
- }
135
-
136
- if (cliOptions.mobile === null) {
137
- featureQuestions.push({
138
- type: "confirm",
139
- name: "mobile",
140
- message: "Add hybrid mobile support with Capacitor?",
141
- initial: false,
142
- })
143
- }
144
-
145
- if (featureQuestions.length > EMPTY_LENGTH) {
146
- const answers = await prompts(featureQuestions, { onCancel })
122
+ let features = {
123
+ ui: false,
124
+ routing: false,
125
+ icons: [],
126
+ languages: [],
127
+ mobile: false,
128
+ pwa: false,
129
+ }
147
130
 
148
- if (canceled) {
149
- console.log("Operation canceled.")
150
- return
151
- }
131
+ if (VITE_APP_TEMPLATES.has(baseTemplate)) {
132
+ features = await askAppFeatures(cliOptions)
152
133
 
153
- pwa = cliOptions.pwa === true ? true : answers.pwa === true
154
- mobile = cliOptions.mobile === true ? true : answers.mobile === true
134
+ if (!features) {
135
+ console.log("Operation canceled.")
136
+ return
155
137
  }
156
- } else if (pwa || mobile) {
138
+ } else if (cliOptions.pwa || cliOptions.mobile) {
157
139
  console.warn(
158
140
  "PWA and mobile options are only available for JavaScript and TypeScript Vite app templates.",
159
141
  )
160
- pwa = false
161
- mobile = false
162
142
  }
163
143
 
164
144
  const spinner = ora(`Creating project "${projectName}"...`).start()
@@ -166,18 +146,14 @@ async function main() {
166
146
  try {
167
147
  const targetDir = path.join(process.cwd(), projectName)
168
148
 
169
- // Determine the actual template directory based on base template and renderer
170
149
  let templateName = baseTemplate
171
150
  if (baseTemplate !== "minimal" && renderer !== "lit-html") {
172
151
  templateName = `${baseTemplate}-${renderer}`
173
152
  }
174
153
 
175
154
  const templateDir = path.join(templatesRoot, templateName)
176
-
177
- // Create project directory and copy template files
178
155
  copyDir(templateDir, targetDir)
179
156
 
180
- // Update README.md with project name
181
157
  const readmePath = path.join(targetDir, "README.md")
182
158
  if (fs.existsSync(readmePath)) {
183
159
  let readme = fs.readFileSync(readmePath, "utf-8")
@@ -190,372 +166,74 @@ async function main() {
190
166
  console.log(`\nNext steps:`)
191
167
  console.log(` cd ${projectName}`)
192
168
  console.log(` Open index.html in your browser`)
193
- } else {
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
169
+ return
170
+ }
198
171
 
199
- if (pwa) {
200
- spinner.text = "Adding PWA support..."
201
- addPwaSupport(targetDir, projectName)
202
- }
172
+ const pkgPath = path.join(targetDir, "package.json")
173
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"))
174
+ pkg.name = projectName
175
+
176
+ const hasAppFeatures =
177
+ features.ui ||
178
+ features.routing ||
179
+ Boolean(features.icons.length) ||
180
+ Boolean(features.languages.length)
181
+
182
+ if (hasAppFeatures) {
183
+ spinner.text = "Adding app features..."
184
+ addAppFeatures(targetDir, pkg, {
185
+ ...features,
186
+ projectName,
187
+ isTypeScript: baseTemplate === "ts",
188
+ renderer,
189
+ })
190
+ }
203
191
 
204
- if (mobile) {
205
- spinner.text = "Adding Capacitor support..."
206
- addMobileSupport(targetDir, pkg, projectName, templateName)
207
- }
192
+ if (features.pwa) {
193
+ spinner.text = "Adding PWA support..."
194
+ addPwaSupport(targetDir, pkg, projectName)
195
+ }
196
+
197
+ if (features.mobile) {
198
+ spinner.text = "Adding Capacitor support..."
199
+ addMobileSupport(targetDir, pkg, projectName)
200
+ }
208
201
 
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
- }
202
+ for (const depType of ["dependencies", "devDependencies"]) {
203
+ if (pkg[depType]) {
204
+ for (const [name, version] of Object.entries(pkg[depType])) {
205
+ if (version.startsWith("workspace:")) {
206
+ spinner.text = `Fetching latest version of ${name}...`
207
+ pkg[depType][name] = getLatestVersion(name)
216
208
  }
217
209
  }
218
210
  }
211
+ }
219
212
 
220
- fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, INDENTATION) + EOL)
213
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, INDENTATION) + EOL)
221
214
 
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`)
227
- }
215
+ spinner.succeed(`Created ${projectName} at ${targetDir}`)
216
+ console.log(`\nNext steps:`)
217
+ console.log(` cd ${projectName}`)
218
+ console.log(` pnpm install`)
219
+ console.log(` pnpm dev`)
228
220
  } catch (error) {
229
221
  spinner.fail("Operation failed.")
230
222
  console.error(error)
231
223
  }
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
-
242
- // Rename gitignore to .gitignore
243
- if (entry.name === "gitignore") {
244
- fs.copyFileSync(srcPath, path.join(dest, ".gitignore"))
245
- continue
246
- }
247
-
248
- if (entry.isDirectory()) {
249
- copyDir(srcPath, destPath)
250
- } else {
251
- fs.copyFileSync(srcPath, destPath)
252
- }
253
- }
254
- }
255
-
256
- function getLatestVersion(packageName) {
257
- try {
258
- const version = execSync(`npm view ${packageName} version`, {
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
266
- }
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
-
380
- - Android: https://capacitorjs.com/docs/android
381
- - iOS: https://capacitorjs.com/docs/ios
382
- - Android Studio: https://developer.android.com/studio
383
- - Xcode: https://developer.apple.com/xcode/
384
-
385
- Then sync and open the native project:
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")
437
- }
438
-
439
- throw new Error("Network request failed")
440
- })
441
- }),
442
- )
443
- })
444
- `
445
224
  }
446
225
 
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
- }
480
-
481
- if (!index.includes('name="theme-color"')) {
482
- index = index.replace(
483
- /^([ \t]*<meta name="viewport"[^>]*>\r?\n)/m,
484
- `$1 <meta name="theme-color" content="#111827" />${EOL}`,
485
- )
486
- }
487
-
488
- fs.writeFileSync(indexPath, index)
489
- }
490
-
491
- function injectServiceWorkerRegistration(targetDir) {
492
- const mainPath = findMainPath(targetDir)
493
-
494
- if (!mainPath) {
495
- return
496
- }
497
-
498
- let main = fs.readFileSync(mainPath, "utf-8")
499
-
500
- if (!main.includes("@inglorious/web/mobile")) {
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}`
226
+ function getLatestVersion(packageName) {
227
+ try {
228
+ const version = execSync(`npm view ${packageName} version`, {
229
+ encoding: "utf-8",
230
+ stdio: "pipe",
231
+ timeout: NPM_VIEW_TIMEOUT_MS,
232
+ }).trim()
233
+ return `^${version}`
234
+ } catch {
235
+ return "latest"
512
236
  }
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
237
  }
560
238
 
561
239
  main().catch((e) => {