@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.
- package/README.md +25 -8
- package/bin/index.js +79 -401
- package/bin/lib/app-jsx.js +280 -0
- package/bin/lib/app-lit.js +275 -0
- package/bin/lib/app-vue.js +239 -0
- package/bin/lib/app.js +402 -0
- package/bin/lib/cli.js +49 -0
- package/bin/lib/constants.js +14 -0
- package/bin/lib/features.js +142 -0
- package/bin/lib/file.js +88 -0
- package/bin/lib/imports.js +64 -0
- package/bin/lib/mobile.js +140 -0
- package/bin/lib/pwa.js +168 -0
- package/bin/lib/questions.js +184 -0
- package/bin/lib/source.js +58 -0
- package/package.json +4 -4
- package/templates/js/package.json +8 -7
- package/templates/js/vite.config.js +2 -1
- package/templates/js-jsx/package.json +8 -7
- package/templates/js-jsx/vite.config.js +2 -1
- package/templates/js-vue/package.json +7 -6
- package/templates/js-vue/vite.config.js +2 -1
- package/templates/ssx-js/package.json +8 -7
- package/templates/ssx-js/src/site.config.js +2 -1
- package/templates/ssx-ts/package.json +9 -7
- package/templates/ssx-ts/src/site.config.ts +1 -0
- package/templates/ts/package.json +10 -8
- package/templates/ts/src/app.ts +4 -2
- package/templates/ts/tsconfig.app.json +2 -0
- package/templates/ts/tsconfig.node.json +2 -0
- package/templates/ts/types/index.d.ts +4 -1
- package/templates/ts/vite.config.ts +2 -1
- package/templates/ts-jsx/package.json +10 -8
- package/templates/ts-jsx/src/app.tsx +2 -2
- package/templates/ts-jsx/tsconfig.app.json +2 -0
- package/templates/ts-jsx/tsconfig.node.json +2 -0
- package/templates/ts-jsx/types/index.d.ts +3 -1
- package/templates/ts-jsx/types/vite-end.d.ts +1 -0
- package/templates/ts-jsx/vite.config.ts +2 -1
- package/templates/ts-vue/eslint.config.ts +1 -1
- package/templates/ts-vue/package.json +10 -8
- package/templates/ts-vue/src/store/middlewares.ts +5 -2
- package/templates/ts-vue/tsconfig.app.json +2 -0
- package/templates/ts-vue/tsconfig.node.json +2 -0
- package/templates/ts-vue/types/index.d.ts +3 -1
- 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
|
|
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 -- --
|
|
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
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
return
|
|
151
|
-
}
|
|
131
|
+
if (VITE_APP_TEMPLATES.has(baseTemplate)) {
|
|
132
|
+
features = await askAppFeatures(cliOptions)
|
|
152
133
|
|
|
153
|
-
|
|
154
|
-
|
|
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
|
-
|
|
194
|
-
|
|
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
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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
|
-
|
|
213
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, INDENTATION) + EOL)
|
|
221
214
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
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) => {
|