@kevinmarrec/create-app 0.10.0 → 0.11.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/dist/index.js CHANGED
@@ -8,7 +8,7 @@ import { x } from "tinyexec";
8
8
  import fs from "node:fs/promises";
9
9
 
10
10
  //#region package.json
11
- var version = "0.10.0";
11
+ var version = "0.11.0";
12
12
 
13
13
  //#endregion
14
14
  //#region src/utils/fs.ts
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@kevinmarrec/create-app",
3
3
  "type": "module",
4
- "version": "0.10.0",
5
- "packageManager": "bun@1.3.2",
4
+ "version": "0.11.0",
5
+ "packageManager": "bun@1.3.3",
6
6
  "description": "CLI that scaffolds an opinionated Bun & Vue fullstack application.",
7
7
  "author": "Kevin Marrec <kevin@marrec.io>",
8
8
  "license": "MIT",
@@ -53,18 +53,18 @@
53
53
  },
54
54
  "devDependencies": {
55
55
  "@faker-js/faker": "^10.1.0",
56
- "@kevinmarrec/eslint-config": "^1.5.6",
57
- "@kevinmarrec/stylelint-config": "^1.5.6",
58
- "@kevinmarrec/tsconfig": "^1.5.6",
59
- "@types/bun": "^1.3.2",
60
- "@vitest/coverage-v8": "^4.0.9",
56
+ "@kevinmarrec/eslint-config": "^1.5.7",
57
+ "@kevinmarrec/stylelint-config": "^1.5.7",
58
+ "@kevinmarrec/tsconfig": "^1.5.7",
59
+ "@types/bun": "^1.3.3",
60
+ "@vitest/coverage-v8": "^4.0.13",
61
61
  "bumpp": "^10.3.1",
62
62
  "eslint": "^9.39.1",
63
- "knip": "^5.69.1",
64
- "stylelint": "^16.25.0",
65
- "tsdown": "^0.16.4",
63
+ "knip": "^5.70.1",
64
+ "stylelint": "^16.26.0",
65
+ "tsdown": "^0.16.6",
66
66
  "typescript": "^5.9.3",
67
- "vitest": "^4.0.9",
68
- "vue-tsc": "^3.1.4"
67
+ "vitest": "^4.0.13",
68
+ "vue-tsc": "^3.1.5"
69
69
  }
70
70
  }
@@ -0,0 +1,5 @@
1
+ -- Main database
2
+ CREATE DATABASE app;
3
+
4
+ -- Analytics database
5
+ CREATE DATABASE analytics;
@@ -7,47 +7,23 @@ import { filesize } from 'filesize'
7
7
  import { x } from 'tinyexec'
8
8
  import { glob } from 'tinyglobby'
9
9
 
10
- interface FileStats {
11
- file: string
12
- size: number
13
- }
14
-
15
10
  async function getFileStats(directory: string) {
16
- const fileStats: FileStats[] = []
17
-
18
- for (const file of await glob(['**/*'], { cwd: directory })) {
19
- const size = fs.statSync(path.join(directory, file)).size
20
- fileStats.push({
11
+ const fileStats = await Promise.all(
12
+ (await glob(['**/*'], { cwd: directory })).map(async file => ({
21
13
  file,
22
- size,
23
- })
24
- }
14
+ size: fs.statSync(path.join(directory, file)).size,
15
+ })),
16
+ )
25
17
 
26
18
  fileStats.sort((a, b) => {
27
- // Sort so that files starting with 'assets/' come first
28
- if (a.file.startsWith('assets/') && !b.file.startsWith('assets/')) return -1
29
- if (!a.file.startsWith('assets/') && b.file.startsWith('assets/')) return 1
30
-
31
- // Within that, files ending with '.js' come first
32
- if (a.file.endsWith('.js') && !b.file.endsWith('.js')) return -1
33
- if (!a.file.endsWith('.js') && b.file.endsWith('.js')) return 1
34
-
35
- // Otherwise sort alphabetically
36
- return a.file.localeCompare(b.file)
19
+ const scoreA = (a.file.startsWith('assets/') ? 2 : 0) + (a.file.endsWith('.js') ? 1 : 0)
20
+ const scoreB = (b.file.startsWith('assets/') ? 2 : 0) + (b.file.endsWith('.js') ? 1 : 0)
21
+ return scoreB - scoreA || a.file.localeCompare(b.file)
37
22
  })
38
23
 
39
24
  return fileStats
40
25
  }
41
26
 
42
- async function generateFileStatsMarkdown(directory: string) {
43
- const fileStats = await getFileStats(directory)
44
- return [
45
- '| File | Size |',
46
- '| :--- | ---: |',
47
- ...fileStats.map(file => `| ${file.file} | ${filesize(file.size)} |`),
48
- ].join('\n')
49
- }
50
-
51
27
  async function main() {
52
28
  const { positionals: [directory] } = parseArgs({
53
29
  args: process.argv.slice(2),
@@ -61,7 +37,12 @@ async function main() {
61
37
 
62
38
  await x('bun', ['run', 'build'], { nodeOptions: { cwd: directory } })
63
39
 
64
- const markdownTable = await generateFileStatsMarkdown(path.join(directory, 'dist'))
40
+ const fileStats = await getFileStats(path.join(directory, 'dist'))
41
+ const markdownTable = [
42
+ '| File | Size |',
43
+ '| :--- | ---: |',
44
+ ...fileStats.map(file => `| ${file.file} | ${filesize(file.size)} |`),
45
+ ].join('\n')
65
46
  process.stdout.write(`${markdownTable}\n`)
66
47
  }
67
48
 
@@ -199,6 +199,7 @@ bun run db:migrate
199
199
 
200
200
  ### Root Level Scripts
201
201
 
202
+ - `bun run dev` - Alias for `docker compose`
202
203
  - `bun run check` - Run all checks (ESLint, Stylelint, unused dependencies, TypeScript)
203
204
  - `bun run lint` - Run linting (ESLint and Stylelint)
204
205
  - `bun run lint:inspect` - Open ESLint config inspector
@@ -10,14 +10,14 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "@libsql/client": "^0.15.15",
13
- "@orpc/server": "^1.11.2",
14
- "better-auth": "^1.3.34",
13
+ "@orpc/server": "^1.11.3",
14
+ "better-auth": "^1.4.1",
15
15
  "drizzle-orm": "^0.44.7",
16
16
  "pino": "^10.1.0",
17
17
  "valibot": "^1.1.0"
18
18
  },
19
19
  "devDependencies": {
20
- "@types/bun": "^1.3.2",
20
+ "@types/bun": "^1.3.3",
21
21
  "drizzle-kit": "^0.31.7",
22
22
  "pg": "^8.16.3",
23
23
  "pino-pretty": "^13.1.2"
@@ -13,7 +13,7 @@ export function cors(handler: (req: Request) => Promise<Response>) {
13
13
  : await handler(req)
14
14
 
15
15
  if (req.method === 'OPTIONS') {
16
- response.headers.append('Access-Control-Allow-Headers', 'Content-Type')
16
+ response.headers.append('Access-Control-Allow-Headers', 'Content-Type, Authorization, User-Agent')
17
17
  response.headers.append('Access-Control-Allow-Methods', 'GET, HEAD, PUT, POST, DELETE, PATCH')
18
18
  response.headers.append('Access-Control-Max-Age', '7200') // 2 hours https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Max-Age
19
19
  }
package/template/app/.env CHANGED
@@ -1 +1,3 @@
1
1
  VITE_API_URL=https://api.dev.localhost
2
+ VITE_ANALYTICS_URL=https://analytics.dev.localhost
3
+ VITE_ANALYTICS_WEBSITE_ID=bcb4bcfc-9f60-4dc1-8fd3-e522e3089c6f
@@ -6,6 +6,9 @@
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <meta name="color-scheme" content="dark light" />
8
8
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
9
+ <link rel="preconnect" href="%VITE_API_URL%" crossorigin />
10
+ <link rel="dns-prefetch" href="%VITE_ANALYTICS_URL%" />
11
+ <script defer src="%VITE_ANALYTICS_URL%/script.js" data-website-id="%VITE_ANALYTICS_WEBSITE_ID%"></script>
9
12
  <title>Title</title>
10
13
  </head>
11
14
  <body class="font-sans">
@@ -10,26 +10,26 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "@kevinmarrec/vue-i18n": "^1.1.3",
13
- "@orpc/client": "^1.11.2",
14
- "@orpc/tanstack-query": "1.11.2",
13
+ "@orpc/client": "^1.11.3",
14
+ "@orpc/tanstack-query": "^1.11.3",
15
15
  "@tanstack/query-core": "^5.90.10",
16
16
  "@tanstack/vue-query": "^5.91.2",
17
17
  "@unhead/vue": "^2.0.19",
18
18
  "@vueuse/core": "^14.0.0",
19
- "better-auth": "^1.3.34",
20
- "unocss": "^66.5.6",
19
+ "better-auth": "^1.4.1",
20
+ "unocss": "^66.5.9",
21
21
  "vue": "^3.5.24"
22
22
  },
23
23
  "devDependencies": {
24
- "@kevinmarrec/unocss-config": "^1.5.6",
24
+ "@kevinmarrec/unocss-config": "^1.5.7",
25
25
  "@kevinmarrec/vite-plugin-dark-mode": "^1.1.2",
26
26
  "@modyfi/vite-plugin-yaml": "^1.1.1",
27
27
  "@unhead/addons": "^2.0.19",
28
- "@vitejs/plugin-vue": "^6.0.1",
28
+ "@vitejs/plugin-vue": "^6.0.2",
29
29
  "beasties": "^0.3.5",
30
30
  "rollup-plugin-visualizer": "^6.0.5",
31
- "vite": "^7.2.2",
32
- "vite-plugin-vue-devtools": "^8.0.3",
31
+ "vite": "^7.2.4",
32
+ "vite-plugin-vue-devtools": "^8.0.5",
33
33
  "vite-ssg": "^28.2.2",
34
34
  "vite-tsconfig-paths": "^5.1.4"
35
35
  }
@@ -6,11 +6,6 @@ import { useAuth, useContent, useHead, useI18n } from '~/app/composables'
6
6
  const { t } = useI18n()
7
7
  useHead({
8
8
  title: () => t('title'),
9
- link: [{
10
- rel: 'preconnect',
11
- href: new URL(import.meta.env.VITE_API_URL).origin,
12
- crossorigin: '',
13
- }],
14
9
  })
15
10
 
16
11
  const { user, signIn, signUp, signOut } = useAuth()
@@ -2,6 +2,8 @@
2
2
 
3
3
  interface ImportMetaEnv {
4
4
  readonly VITE_API_URL: string
5
+ readonly VITE_ANALYTICS_URL: string
6
+ readonly VITE_ANALYTICS_WEBSITE_ID: string
5
7
  }
6
8
 
7
9
  interface ImportMeta {
@@ -38,7 +38,7 @@ services:
38
38
  test: [CMD-SHELL, traefik healthcheck --ping]
39
39
  interval: 1s
40
40
  timeout: 1s
41
- retries: 10
41
+ retries: 20
42
42
  labels:
43
43
  traefik.enable: 'true'
44
44
  traefik.http.routers.traefik.rule: Host(`traefik.dev.localhost`)
@@ -52,16 +52,16 @@ services:
52
52
  environment:
53
53
  - POSTGRES_USER=user
54
54
  - POSTGRES_PASSWORD=password
55
- - POSTGRES_DB=app
56
55
  ports:
57
56
  - 5432:5432
58
57
  volumes:
59
58
  - postgres_data:/var/lib/postgresql/data
59
+ - ./.docker/postgres/init.sql:/docker-entrypoint-initdb.d/init.sql
60
60
  healthcheck:
61
61
  test: [CMD-SHELL, "psql postgresql://user:password@postgres:5432/app -c 'SELECT 1' 2> /dev/null"]
62
62
  interval: 1s
63
63
  timeout: 1s
64
- retries: 10
64
+ retries: 20
65
65
 
66
66
  metabase:
67
67
  image: metabase/metabase:v0.57.x
@@ -121,6 +121,47 @@ services:
121
121
  traefik.http.routers.app.tls: 'true'
122
122
  traefik.http.services.app.loadbalancer.server.port: '5173'
123
123
 
124
+ studio:
125
+ depends_on:
126
+ traefik:
127
+ condition: service_healthy
128
+ postgres:
129
+ condition: service_healthy
130
+ container_name: studio
131
+ image: ghcr.io/drizzle-team/gateway:latest
132
+ environment:
133
+ - MASTERPASS=foobar
134
+ volumes:
135
+ - studio_data:/app
136
+ labels:
137
+ traefik.enable: 'true'
138
+ traefik.http.routers.studio.rule: Host(`studio.dev.localhost`)
139
+ traefik.http.routers.studio.entrypoints: websecure
140
+ traefik.http.routers.studio.tls: 'true'
141
+ traefik.http.services.studio.loadbalancer.server.port: '4983'
142
+
143
+ analytics:
144
+ depends_on:
145
+ traefik:
146
+ condition: service_healthy
147
+ postgres:
148
+ condition: service_healthy
149
+ container_name: analytics
150
+ image: umamisoftware/umami:3
151
+ environment:
152
+ - DATABASE_URL=postgresql://user:password@postgres:5432/analytics
153
+ healthcheck:
154
+ test: [CMD-SHELL, curl -f http://localhost:3000]
155
+ interval: 1s
156
+ timeout: 1s
157
+ retries: 20
158
+ labels:
159
+ traefik.enable: 'true'
160
+ traefik.http.routers.analytics.rule: Host(`analytics.dev.localhost`)
161
+ traefik.http.routers.analytics.entrypoints: websecure
162
+ traefik.http.routers.analytics.tls: 'true'
163
+ traefik.http.services.analytics.loadbalancer.server.port: '3000'
164
+
124
165
  networks:
125
166
  default:
126
167
  name: dev
@@ -128,3 +169,4 @@ networks:
128
169
  volumes:
129
170
  postgres_data:
130
171
  metabase_data:
172
+ studio_data:
@@ -2,7 +2,7 @@
2
2
  "name": "project",
3
3
  "type": "module",
4
4
  "private": true,
5
- "packageManager": "bun@1.3.2",
5
+ "packageManager": "bun@1.3.3",
6
6
  "engines": {
7
7
  "node": "lts/*"
8
8
  },
@@ -16,21 +16,22 @@
16
16
  "check:stylelint": "stylelint '**/*.{css,scss,vue}' --ignorePath .gitignore --cache",
17
17
  "check:types": "vue-tsc --noEmit",
18
18
  "check:unused": "knip -n",
19
+ "dev": "docker compose",
19
20
  "lint": "bun run check:eslint && bun run check:stylelint",
20
21
  "lint:inspect": "bunx @eslint/config-inspector",
21
22
  "update": "bunx taze -I -rwi"
22
23
  },
23
24
  "devDependencies": {
24
- "@kevinmarrec/eslint-config": "^1.5.6",
25
- "@kevinmarrec/stylelint-config": "^1.5.6",
26
- "@kevinmarrec/tsconfig": "^1.5.6",
25
+ "@kevinmarrec/eslint-config": "^1.5.7",
26
+ "@kevinmarrec/stylelint-config": "^1.5.7",
27
+ "@kevinmarrec/tsconfig": "^1.5.7",
27
28
  "eslint": "^9.39.1",
28
29
  "filesize": "^11.0.13",
29
- "knip": "^5.69.1",
30
- "stylelint": "^16.25.0",
30
+ "knip": "^5.70.1",
31
+ "stylelint": "^16.26.0",
31
32
  "tinyexec": "^1.0.2",
32
33
  "tinyglobby": "^0.2.15",
33
34
  "typescript": "~5.9.3",
34
- "vue-tsc": "^3.1.4"
35
+ "vue-tsc": "^3.1.5"
35
36
  }
36
37
  }