@inglorious/create-app 1.8.1 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,16 +8,9 @@ This package allows you to generate a new project from a set of pre-configured t
8
8
 
9
9
  ## Usage
10
10
 
11
- To create a new app, run one of the following commands in your terminal. You will be prompted to choose a project name and select a template.
11
+ To create a new app, run this command in your terminal. You will be prompted to choose a project name and select a template.
12
12
 
13
13
  ```bash
14
- # with npm
15
- npm create @inglorious/app@latest
16
-
17
- # with yarn
18
- yarn create @inglorious/app
19
-
20
- # with pnpm
21
14
  pnpm create @inglorious/app
22
15
  ```
23
16
 
@@ -49,7 +42,7 @@ The app is generated in the syntax you chose: lit-html, JSX or Vue. Every questi
49
42
  | Select a rendering syntax | `--syntax=lit-html\|jsx\|vue` |
50
43
 
51
44
  ```bash
52
- npm create @inglorious/app@latest -- my-app --template=ts --syntax=jsx --ui --theme=material-3 --routing --transitions --icons=material --languages=en,it --no-mobile --no-analytics --no-push --no-pwa --skills
45
+ pnpm create @inglorious/app my-app --template=ts --syntax=jsx --ui --theme=material-3 --routing --transitions --icons=material --languages=en,it --no-mobile --no-analytics --no-push --no-pwa --skills
53
46
  ```
54
47
 
55
48
  Analytics and push generate their setup steps into the new project's README, and a `.env.example`. Push also generates a Supabase migration, the `push-notify` edge function and a service worker: a push is sent by inserting a row in `notifications`. It needs a Firebase project, a Supabase project and a sign-in screen, since the permission prompt appears once someone is signed in with Supabase Auth.
@@ -102,6 +95,12 @@ For Android and iOS builds, install Android Studio with the Android SDK and a Ja
102
95
  - iOS: https://capacitorjs.com/docs/ios
103
96
  - Android Studio: https://developer.android.com/studio
104
97
 
98
+ ### Versions and releases
99
+
100
+ Every Vite app is versioned with [Changesets](https://github.com/changesets/changesets): describe each change with `pnpm changeset`, and `pnpm release:version` raises the version in `package.json` and writes `CHANGELOG.md`. The app reads that version as `import.meta.env.VITE_APP_VERSION`.
101
+
102
+ Mobile apps also get `pnpm mobile:release`, which copies the version into the iOS and Android projects, raises the build number they share (every store upload needs a new one), builds with the values in `.env` rather than `.env.local`, and syncs. The generated README describes the whole release, upload included.
103
+
105
104
  ## License
106
105
 
107
106
  **MIT License - Free and open source**
package/bin/index.js CHANGED
@@ -22,6 +22,7 @@ import { addFirebaseSupport } from "./lib/firebase.js"
22
22
  import { addMobileSupport } from "./lib/mobile.js"
23
23
  import { addPwaSupport } from "./lib/pwa.js"
24
24
  import { askAppFeatures } from "./lib/questions.js"
25
+ import { addReleaseSupport } from "./lib/release.js"
25
26
  import { installSkills, skillsCommand, skillsFor } from "./lib/skills.js"
26
27
 
27
28
  async function main() {
@@ -189,6 +190,8 @@ async function main() {
189
190
  })
190
191
  }
191
192
 
193
+ if (VITE_APP_TEMPLATES.has(baseTemplate)) addReleaseSupport(targetDir, pkg)
194
+
192
195
  if (features.pwa) {
193
196
  spinner.text = "Adding PWA support..."
194
197
  addPwaSupport(targetDir, pkg, projectName, { push: features.push })
@@ -196,7 +199,7 @@ async function main() {
196
199
 
197
200
  if (features.mobile) {
198
201
  spinner.text = "Adding Capacitor support..."
199
- addMobileSupport(targetDir, pkg, projectName)
202
+ addMobileSupport(targetDir, pkg, projectName, templatesRoot)
200
203
  }
201
204
 
202
205
  if (features.analytics || features.push) {
@@ -1,4 +1,5 @@
1
1
  export const CAPACITOR_VERSION = "^8.5.3"
2
+ export const CHANGESETS_VERSION = "^3.0.3"
2
3
  export const CAPACITOR_FIREBASE_VERSION = "^8.5.2"
3
4
  export const FIREBASE_VERSION = "^12.19.0"
4
5
  export const SUPABASE_VERSION = "^2.118.0"
package/bin/lib/mobile.js CHANGED
@@ -3,9 +3,9 @@ import { EOL } from "os"
3
3
  import path from "path"
4
4
 
5
5
  import { CAPACITOR_VERSION, TYPESCRIPT_VERSION } from "./constants.js"
6
- import { toJavaIdentifier } from "./file.js"
6
+ import { copyDir, toJavaIdentifier } from "./file.js"
7
7
 
8
- export function addMobileSupport(targetDir, pkg, projectName) {
8
+ export function addMobileSupport(targetDir, pkg, projectName, templatesRoot) {
9
9
  pkg.dependencies ??= {}
10
10
  pkg.devDependencies ??= {}
11
11
  pkg.scripts ??= {}
@@ -22,6 +22,13 @@ export function addMobileSupport(targetDir, pkg, projectName) {
22
22
  pkg.scripts["mobile:sync"] = "pnpm build && cap sync"
23
23
  pkg.scripts["mobile:ios"] = "pnpm build && cap run ios"
24
24
  pkg.scripts["mobile:android"] = "pnpm build && cap run android"
25
+ // Vite never overrides a variable already set, so .env beats .env.local.
26
+ pkg.scripts["build:release"] =
27
+ "node --env-file-if-exists=.env node_modules/vite/bin/vite.js build"
28
+ pkg.scripts["mobile:release"] =
29
+ "node scripts/native-version.mjs && pnpm build:release && cap sync"
30
+
31
+ copyDir(path.join(templatesRoot, "features/mobile"), targetDir)
25
32
 
26
33
  fs.writeFileSync(
27
34
  path.join(targetDir, "capacitor.config.ts"),
@@ -99,6 +106,14 @@ pnpm mobile:android
99
106
  # or
100
107
  pnpm mobile:ios
101
108
  \`\`\`
109
+
110
+ ### Releasing
111
+
112
+ The version users see comes from \`package.json\`, the same on the web, Android and iOS. The build number is shared by both native apps and must go up with every store upload.
113
+
114
+ 1. Describe each change as you make it with \`pnpm changeset\`. When a release is due, \`pnpm release:version\` raises the version and writes \`CHANGELOG.md\`. Skip this to upload another build of the same version, for example to TestFlight.
115
+ 2. Run \`pnpm mobile:release\`. It copies the version into both native projects, raises the build number, builds with the production values in \`.env\` (ignoring \`.env.local\`), and syncs. Commit what it changed.
116
+ 3. Upload. In Xcode, use **Product → Archive**, then **Distribute App → Custom → App Store Connect → Upload**, and untick **Manage Version and Build Number** so the store gets the project's build number. In Android Studio, use **Build → Generate Signed App Bundle**.
102
117
  `
103
118
 
104
119
  fs.writeFileSync(
@@ -0,0 +1,48 @@
1
+ import fs from "fs"
2
+ import { EOL } from "os"
3
+ import path from "path"
4
+
5
+ import { CHANGESETS_VERSION, INDENTATION } from "./constants.js"
6
+
7
+ const CHANGESET_CONFIG = {
8
+ $schema: "https://unpkg.com/@changesets/config@3.1.1/schema.json",
9
+ changelog: "@changesets/cli/changelog",
10
+ commit: false,
11
+ fixed: [],
12
+ linked: [],
13
+ access: "restricted",
14
+ baseBranch: "main",
15
+ updateInternalDependencies: "patch",
16
+ ignore: [],
17
+ }
18
+
19
+ const CHANGESET_README = `# Changesets
20
+
21
+ Each file here describes a change for the next release. Add one with
22
+ \`pnpm changeset\`; \`pnpm release:version\` turns them into a new version in
23
+ \`package.json\` and an entry in \`CHANGELOG.md\`.
24
+ `
25
+
26
+ /**
27
+ * Versions the app with Changesets: \`package.json\` holds the version, which
28
+ * the Vite config hands to the app as \`import.meta.env.VITE_APP_VERSION\`.
29
+ *
30
+ * @param {string} targetDir
31
+ * @param {object} pkg - The package.json being built.
32
+ */
33
+ export function addReleaseSupport(targetDir, pkg) {
34
+ pkg.devDependencies ??= {}
35
+ pkg.scripts ??= {}
36
+
37
+ pkg.devDependencies["@changesets/cli"] = CHANGESETS_VERSION
38
+ pkg.scripts.changeset = "changeset"
39
+ pkg.scripts["release:version"] = "changeset version"
40
+
41
+ const dir = path.join(targetDir, ".changeset")
42
+ fs.mkdirSync(dir, { recursive: true })
43
+ fs.writeFileSync(
44
+ path.join(dir, "config.json"),
45
+ JSON.stringify(CHANGESET_CONFIG, null, INDENTATION) + EOL,
46
+ )
47
+ fs.writeFileSync(path.join(dir, "README.md"), CHANGESET_README)
48
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inglorious/create-app",
3
- "version": "1.8.1",
3
+ "version": "1.9.1",
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",
@@ -0,0 +1,57 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs"
2
+ import process from "node:process"
3
+
4
+ const PLATFORMS = [
5
+ {
6
+ file: "ios/App/App.xcodeproj/project.pbxproj",
7
+ builds: (source) =>
8
+ [...source.matchAll(/CURRENT_PROJECT_VERSION = (\d+);/g)].map(
9
+ ([, build]) => build,
10
+ ),
11
+ update: (source, version, build) =>
12
+ source
13
+ .replaceAll(
14
+ /CURRENT_PROJECT_VERSION = \d+;/g,
15
+ `CURRENT_PROJECT_VERSION = ${build};`,
16
+ )
17
+ .replaceAll(
18
+ /MARKETING_VERSION = [^;]+;/g,
19
+ `MARKETING_VERSION = ${version};`,
20
+ ),
21
+ },
22
+ {
23
+ file: "android/app/build.gradle",
24
+ builds: (source) =>
25
+ [...source.matchAll(/versionCode (\d+)/g)].map(([, build]) => build),
26
+ update: (source, version, build) =>
27
+ source
28
+ .replace(/versionCode \d+/, `versionCode ${build}`)
29
+ .replace(/versionName "[^"]*"/, `versionName "${version}"`),
30
+ },
31
+ ]
32
+
33
+ const NO_BUILD = 0
34
+ const NEXT = 1
35
+
36
+ const { name, version } = JSON.parse(readFileSync("package.json", "utf8"))
37
+
38
+ // The App Store only takes up to three numbers, with no pre-release tag.
39
+ if (!/^\d+\.\d+\.\d+$/.test(version)) {
40
+ throw new Error(`Version ${version} must be MAJOR.MINOR.PATCH for the stores`)
41
+ }
42
+
43
+ const platforms = PLATFORMS.filter(({ file }) => existsSync(file)).map(
44
+ (platform) => ({ ...platform, source: readFileSync(platform.file, "utf8") }),
45
+ )
46
+ if (!platforms.length) {
47
+ throw new Error("No native project found: run mobile:init:ios or :android")
48
+ }
49
+
50
+ const builds = platforms.flatMap(({ source, builds }) => builds(source))
51
+ const build = Math.max(NO_BUILD, ...builds.map(Number)) + NEXT
52
+
53
+ for (const { file, source, update } of platforms) {
54
+ writeFileSync(file, update(source, version, build))
55
+ }
56
+
57
+ process.stdout.write(`${name} ${version}, build ${build}\n`)
@@ -47,3 +47,6 @@ lerna-debug.log*
47
47
  *.sln
48
48
  *.sw?
49
49
  .qodo
50
+
51
+ # Agent skills
52
+ /.*/skills/
@@ -1,3 +1,4 @@
1
+ import { readFileSync } from "node:fs"
1
2
  import path from "node:path"
2
3
  import { fileURLToPath } from "node:url"
3
4
 
@@ -8,8 +9,15 @@ import { ViteImageOptimizer } from "vite-plugin-image-optimizer"
8
9
 
9
10
  const __filename = fileURLToPath(import.meta.url)
10
11
  const __dirname = path.dirname(__filename)
12
+ const { version } = JSON.parse(
13
+ readFileSync(path.resolve(__dirname, "package.json"), "utf8"),
14
+ )
11
15
 
12
16
  export default defineConfig({
17
+ define: {
18
+ "import.meta.env.VITE_APP_VERSION": JSON.stringify(version),
19
+ },
20
+
13
21
  plugins: [ViteImageOptimizer(), hmr()],
14
22
 
15
23
  build: {
@@ -47,3 +47,6 @@ lerna-debug.log*
47
47
  *.sln
48
48
  *.sw?
49
49
  .qodo
50
+
51
+ # Agent skills
52
+ /.*/skills/
@@ -1,3 +1,4 @@
1
+ import { readFileSync } from "node:fs"
1
2
  import path from "node:path"
2
3
  import { fileURLToPath } from "node:url"
3
4
 
@@ -9,8 +10,15 @@ import { ViteImageOptimizer } from "vite-plugin-image-optimizer"
9
10
 
10
11
  const __filename = fileURLToPath(import.meta.url)
11
12
  const __dirname = path.dirname(__filename)
13
+ const { version } = JSON.parse(
14
+ readFileSync(path.resolve(__dirname, "package.json"), "utf8"),
15
+ )
12
16
 
13
17
  export default defineConfig({
18
+ define: {
19
+ "import.meta.env.VITE_APP_VERSION": JSON.stringify(version),
20
+ },
21
+
14
22
  plugins: [ViteImageOptimizer(), jsx(), hmr()],
15
23
 
16
24
  build: {
@@ -47,3 +47,6 @@ lerna-debug.log*
47
47
  *.sln
48
48
  *.sw?
49
49
  .qodo
50
+
51
+ # Agent skills
52
+ /.*/skills/
@@ -1,3 +1,4 @@
1
+ import { readFileSync } from "node:fs"
1
2
  import path from "node:path"
2
3
  import { fileURLToPath } from "node:url"
3
4
 
@@ -9,8 +10,15 @@ import { ViteImageOptimizer } from "vite-plugin-image-optimizer"
9
10
 
10
11
  const __filename = fileURLToPath(import.meta.url)
11
12
  const __dirname = path.dirname(__filename)
13
+ const { version } = JSON.parse(
14
+ readFileSync(path.resolve(__dirname, "package.json"), "utf8"),
15
+ )
12
16
 
13
17
  export default defineConfig({
18
+ define: {
19
+ "import.meta.env.VITE_APP_VERSION": JSON.stringify(version),
20
+ },
21
+
14
22
  plugins: [ViteImageOptimizer(), vue(), hmr()],
15
23
 
16
24
  build: {
@@ -1,3 +1,6 @@
1
1
  node_modules
2
2
  dist
3
3
  .DS_Store
4
+
5
+ # Agent skills
6
+ /.*/skills/
@@ -1,3 +1,6 @@
1
1
  node_modules
2
2
  dist
3
3
  .DS_Store
4
+
5
+ # Agent skills
6
+ /.*/skills/
@@ -1,3 +1,6 @@
1
1
  node_modules
2
2
  dist
3
3
  .DS_Store
4
+
5
+ # Agent skills
6
+ /.*/skills/
@@ -1,3 +1,6 @@
1
1
  node_modules
2
2
  dist
3
3
  .DS_Store
4
+
5
+ # Agent skills
6
+ /.*/skills/
@@ -1,3 +1,6 @@
1
1
  node_modules
2
2
  dist
3
3
  .DS_Store
4
+
5
+ # Agent skills
6
+ /.*/skills/
@@ -1,3 +1,6 @@
1
1
  node_modules
2
2
  dist
3
3
  .DS_Store
4
+
5
+ # Agent skills
6
+ /.*/skills/
@@ -47,3 +47,6 @@ lerna-debug.log*
47
47
  *.sln
48
48
  *.sw?
49
49
  .qodo
50
+
51
+ # Agent skills
52
+ /.*/skills/
@@ -1,3 +1,4 @@
1
+ import { readFileSync } from "node:fs"
1
2
  import path from "node:path"
2
3
  import { fileURLToPath } from "node:url"
3
4
 
@@ -8,9 +9,15 @@ import { ViteImageOptimizer } from "vite-plugin-image-optimizer"
8
9
 
9
10
  const __filename = fileURLToPath(import.meta.url)
10
11
  const __dirname = path.dirname(__filename)
12
+ const { version } = JSON.parse(
13
+ readFileSync(path.resolve(__dirname, "package.json"), "utf8"),
14
+ )
11
15
 
12
16
  export default defineConfig({
13
- define: { "process.env": {} },
17
+ define: {
18
+ "process.env": {},
19
+ "import.meta.env.VITE_APP_VERSION": JSON.stringify(version),
20
+ },
14
21
 
15
22
  plugins: [ViteImageOptimizer(), hmr()],
16
23
 
@@ -47,3 +47,6 @@ lerna-debug.log*
47
47
  *.sln
48
48
  *.sw?
49
49
  .qodo
50
+
51
+ # Agent skills
52
+ /.*/skills/
@@ -1,3 +1,4 @@
1
+ import { readFileSync } from "node:fs"
1
2
  import path from "node:path"
2
3
  import { fileURLToPath } from "node:url"
3
4
 
@@ -9,9 +10,15 @@ import { ViteImageOptimizer } from "vite-plugin-image-optimizer"
9
10
 
10
11
  const __filename = fileURLToPath(import.meta.url)
11
12
  const __dirname = path.dirname(__filename)
13
+ const { version } = JSON.parse(
14
+ readFileSync(path.resolve(__dirname, "package.json"), "utf8"),
15
+ )
12
16
 
13
17
  export default defineConfig({
14
- define: { "process.env": {} },
18
+ define: {
19
+ "process.env": {},
20
+ "import.meta.env.VITE_APP_VERSION": JSON.stringify(version),
21
+ },
15
22
 
16
23
  plugins: [ViteImageOptimizer(), jsx(), hmr()],
17
24
 
@@ -47,3 +47,6 @@ lerna-debug.log*
47
47
  *.sln
48
48
  *.sw?
49
49
  .qodo
50
+
51
+ # Agent skills
52
+ /.*/skills/
@@ -1,3 +1,4 @@
1
+ import { readFileSync } from "node:fs"
1
2
  import path from "node:path"
2
3
  import { fileURLToPath } from "node:url"
3
4
 
@@ -9,9 +10,15 @@ import { ViteImageOptimizer } from "vite-plugin-image-optimizer"
9
10
 
10
11
  const __filename = fileURLToPath(import.meta.url)
11
12
  const __dirname = path.dirname(__filename)
13
+ const { version } = JSON.parse(
14
+ readFileSync(path.resolve(__dirname, "package.json"), "utf8"),
15
+ )
12
16
 
13
17
  export default defineConfig({
14
- define: { "process.env": {} },
18
+ define: {
19
+ "process.env": {},
20
+ "import.meta.env.VITE_APP_VERSION": JSON.stringify(version),
21
+ },
15
22
 
16
23
  plugins: [ViteImageOptimizer(), vue(), hmr()],
17
24