@kevinmarrec/create-app 0.7.0 → 0.9.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 (65) hide show
  1. package/README.md +3 -1
  2. package/dist/index.js +1 -3
  3. package/package.json +21 -19
  4. package/template/.docker/traefik/dynamic/tls.yml +4 -0
  5. package/template/.github/scripts/build-stats.md.ts +71 -0
  6. package/template/.github/workflows/ci.yml +19 -3
  7. package/template/{gitignore → .gitignore} +2 -0
  8. package/template/{npmrc → .npmrc} +0 -1
  9. package/template/.vscode/settings.json +1 -1
  10. package/template/api/.env.development +4 -0
  11. package/template/{backend → api}/package.json +9 -9
  12. package/template/{backend → api}/src/auth/index.ts +2 -0
  13. package/template/{backend → api}/src/database/index.ts +1 -1
  14. package/template/{backend → api}/src/env.d.ts +2 -2
  15. package/template/{backend → api}/src/main.ts +10 -20
  16. package/template/api/src/orpc/context.ts +11 -0
  17. package/template/api/src/orpc/index.ts +27 -0
  18. package/template/api/src/orpc/middlewares/auth.ts +24 -0
  19. package/template/api/src/orpc/plugins/error.ts +17 -0
  20. package/template/api/src/orpc/router/index.ts +31 -0
  21. package/template/api/src/utils/cors.ts +26 -0
  22. package/template/app/.env +1 -0
  23. package/template/{frontend → app}/package.json +13 -12
  24. package/template/{frontend → app}/src/App.vue +10 -2
  25. package/template/app/src/composables/auth.ts +39 -0
  26. package/template/app/src/composables/content.ts +13 -0
  27. package/template/{frontend → app}/src/composables/index.ts +1 -0
  28. package/template/{frontend → app}/src/lib/orpc.ts +3 -2
  29. package/template/{frontend → app}/src/main.ts +1 -1
  30. package/template/{frontend → app}/vite.config.ts +1 -1
  31. package/template/compose.yaml +65 -12
  32. package/template/docs/local-tls.md +152 -0
  33. package/template/knip.config.ts +5 -4
  34. package/template/package.json +14 -13
  35. package/template/tsconfig.json +2 -2
  36. package/template/backend/.env.development +0 -4
  37. package/template/backend/src/orpc/index.ts +0 -65
  38. package/template/backend/src/orpc/middlewares/auth.ts +0 -33
  39. package/template/backend/src/orpc/middlewares/index.ts +0 -1
  40. package/template/backend/src/orpc/router/auth.ts +0 -54
  41. package/template/backend/src/orpc/router/index.ts +0 -9
  42. package/template/frontend/.env.development +0 -1
  43. package/template/frontend/src/composables/auth.ts +0 -31
  44. package/template/scripts/dev.ts +0 -8
  45. /package/template/{backend → api}/src/database/drizzle/config.ts +0 -0
  46. /package/template/{backend → api}/src/database/migrations/0000_init.sql +0 -0
  47. /package/template/{backend → api}/src/database/migrations/meta/0000_snapshot.json +0 -0
  48. /package/template/{backend → api}/src/database/migrations/meta/_journal.json +0 -0
  49. /package/template/{backend → api}/src/database/schema/accounts.ts +0 -0
  50. /package/template/{backend → api}/src/database/schema/index.ts +0 -0
  51. /package/template/{backend → api}/src/database/schema/sessions.ts +0 -0
  52. /package/template/{backend → api}/src/database/schema/users.ts +0 -0
  53. /package/template/{backend → api}/src/database/schema/verifications.ts +0 -0
  54. /package/template/{backend → api}/src/utils/logger.ts +0 -0
  55. /package/template/{backend → api}/tsconfig.json +0 -0
  56. /package/template/{frontend → app}/index.html +0 -0
  57. /package/template/{frontend → app}/public/favicon.svg +0 -0
  58. /package/template/{frontend → app}/public/robots.txt +0 -0
  59. /package/template/{frontend → app}/src/components/.gitkeep +0 -0
  60. /package/template/{frontend → app}/src/env.d.ts +0 -0
  61. /package/template/{frontend → app}/src/locales/en.yml +0 -0
  62. /package/template/{frontend → app}/src/locales/fr.yml +0 -0
  63. /package/template/{frontend → app}/tsconfig.json +0 -0
  64. /package/template/{frontend → app}/uno.config.ts +0 -0
  65. /package/template/{frontend → app}/wrangler.json +0 -0
@@ -4,7 +4,7 @@ import { ViteSSG } from 'vite-ssg/single-page'
4
4
 
5
5
  import App from './App.vue'
6
6
 
7
- import 'uno.css'
7
+ import 'virtual:uno.css'
8
8
 
9
9
  export const createApp = ViteSSG(App, async ({ app }) => {
10
10
  const i18n = await createI18n({
@@ -3,9 +3,9 @@ import process from 'node:process'
3
3
  import darkMode from '@kevinmarrec/vite-plugin-dark-mode'
4
4
  import yaml from '@modyfi/vite-plugin-yaml'
5
5
  import unhead from '@unhead/addons/vite'
6
- import unocss from '@unocss/vite'
7
6
  import vue from '@vitejs/plugin-vue'
8
7
  import { visualizer } from 'rollup-plugin-visualizer'
8
+ import unocss from 'unocss/vite'
9
9
  import { defineConfig } from 'vite'
10
10
  import devtools from 'vite-plugin-vue-devtools'
11
11
  import tsconfigPaths from 'vite-tsconfig-paths'
@@ -1,22 +1,75 @@
1
1
  x-common: &common
2
- working_dir: /app
3
2
  volumes:
4
- - ./:/app
3
+ - ./:/code
4
+ working_dir: /code
5
5
  user: 1000:1000
6
6
 
7
7
  services:
8
- backend:
8
+ traefik:
9
+ image: traefik:v3.6
10
+ container_name: traefik
11
+ restart: unless-stopped
12
+ security_opt:
13
+ - no-new-privileges:true
14
+ command:
15
+ # General configuration
16
+ - --log.level=INFO
17
+ - --api.dashboard=true
18
+ # Entrypoints for HTTP and HTTPS
19
+ - --entrypoints.websecure.address=:443
20
+ - --entrypoints.web.address=:80
21
+ # Redirect HTTP to HTTPS
22
+ - --entrypoints.web.http.redirections.entrypoint.to=websecure
23
+ - --entrypoints.web.http.redirections.entrypoint.scheme=https
24
+ # Providers (Docker & File for dynamic config)
25
+ - --providers.docker=true
26
+ - --providers.docker.exposedbydefault=false
27
+ - --providers.file.directory=/etc/traefik/dynamic
28
+ - --providers.file.watch=true
29
+ labels:
30
+ traefik.enable: 'true'
31
+ traefik.http.routers.traefik.rule: Host(`traefik.dev.localhost`)
32
+ traefik.http.routers.traefik.entrypoints: websecure
33
+ traefik.http.routers.traefik.tls: 'true'
34
+ traefik.http.routers.traefik.service: api@internal
35
+ ports:
36
+ - 80:80
37
+ - 443:443
38
+ volumes:
39
+ - /var/run/docker.sock:/var/run/docker.sock:ro
40
+ - ./.docker/traefik/dynamic:/etc/traefik/dynamic:ro # Mount the dynamic config directory
41
+ - ./.docker/traefik/certs:/certs:ro # Mount the certs directory
42
+
43
+ api:
9
44
  <<: *common
45
+ depends_on:
46
+ - traefik
47
+ container_name: api
10
48
  image: oven/bun:1-alpine
11
- command: [bun, --cwd, backend, dev]
12
- ports:
13
- - 4000:4000
49
+ init: true
50
+ command: [bun, --cwd, api, dev]
51
+ environment:
52
+ - FORCE_COLOR=1
53
+ labels:
54
+ traefik.enable: 'true'
55
+ traefik.http.routers.api.rule: Host(`api.dev.localhost`)
56
+ traefik.http.routers.api.entrypoints: websecure
57
+ traefik.http.routers.api.tls: 'true'
58
+ traefik.http.services.api.loadbalancer.server.port: '4000'
14
59
 
15
- frontend:
60
+ app:
16
61
  <<: *common
17
62
  depends_on:
18
- - backend
19
- image: imbios/bun-node:22-alpine
20
- command: [bun, --cwd, frontend, dev, --host, 0.0.0.0]
21
- ports:
22
- - 5173:5173
63
+ - api
64
+ container_name: app
65
+ image: imbios/bun-node:24-alpine
66
+ init: true
67
+ command: [bun, --cwd, app, dev, --host, 0.0.0.0]
68
+ environment:
69
+ - FORCE_COLOR=1
70
+ labels:
71
+ traefik.enable: 'true'
72
+ traefik.http.routers.app.rule: Host(`app.dev.localhost`)
73
+ traefik.http.routers.app.entrypoints: websecure
74
+ traefik.http.routers.app.tls: 'true'
75
+ traefik.http.services.app.loadbalancer.server.port: '5173'
@@ -0,0 +1,152 @@
1
+ # **Local TLS Setup Guide**
2
+
3
+ This guide provides the essential steps to install mkcert and generate trusted TLS certificates for your Traefik-secured local development environment.
4
+
5
+ Certificates are expected in `.docker/traefik/certs/`.
6
+
7
+ ## **Prerequisites**
8
+
9
+ - [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/) installed
10
+ - Ports 80 and 443 available on your system
11
+ - Administrator/sudo access for installing mkcert
12
+
13
+ ## **1. Install mkcert (Ubuntu/WSL)**
14
+
15
+ Download, install, and clean up the executable:
16
+
17
+ ```sh
18
+ curl -JLO "https://dl.filippo.io/mkcert/latest?for=linux/amd64"
19
+ chmod +x mkcert-v*-linux-amd64
20
+ sudo cp mkcert-v*-linux-amd64 /usr/local/bin/mkcert
21
+ rm mkcert-v*-linux-amd64
22
+ ```
23
+
24
+ Verify the installation:
25
+
26
+ ```sh
27
+ mkcert -version
28
+ ```
29
+
30
+ ## **2. Install the mkcert Root CA**
31
+
32
+ Install the local root CA to trust generated certificates:
33
+
34
+ ```sh
35
+ mkcert -install
36
+ ```
37
+
38
+ ### **Firefox and Zen Browser (Manual Import)**
39
+
40
+ Browsers like Firefox and Firefox-based Zen Browser require manual import into their internal trust stores.
41
+
42
+ 1. **Find Root CA File Path:**
43
+ Run the appropriate command below to get the full path to the rootCA.pem file:
44
+ - **Linux Native (or WSL for Linux Apps):**
45
+
46
+ ```sh
47
+ echo "$(mkcert -CAROOT)/rootCA.pem"
48
+ # /home/user/.local/share/mkcert/rootCA.pem
49
+ ```
50
+
51
+ - **WSL for Windows Host Applications:** Use wslpath to get the Windows-formatted path.
52
+ ```sh
53
+ wslpath -w "$(mkcert -CAROOT)/rootCA.pem"
54
+ # \\wsl.localhost\Ubuntu\home\user\.local\share\mkcert\rootCA.pem
55
+ ```
56
+
57
+ 2. **Import:** In the browser's settings:
58
+ - **Settings** -> **Privacy & Security**.
59
+ - **Certificates** -> **View Certificates...**
60
+ - **Authorities** tab -> **Import...**
61
+ - Select the rootCA.pem file (using the path found above)
62
+ - Check **"Trust this CA to identify websites"**
63
+ - Click **OK**
64
+
65
+ ## **3. Generate the `dev.localhost` Certificate**
66
+
67
+ Generate the wildcard certificate for `*.dev.localhost` and place the files where Traefik is configured to look (See [`tls.yml`](../.docker/traefik/dynamic/tls.yml)).
68
+
69
+ 1. **Create Directory:**
70
+
71
+ ```sh
72
+ mkdir -p .docker/traefik/certs
73
+ ```
74
+
75
+ 2. **Generate Certificate:**
76
+
77
+ ```sh
78
+ mkcert \
79
+ -cert-file .docker/traefik/certs/dev.localhost.crt \
80
+ -key-file .docker/traefik/certs/dev.localhost.key \
81
+ "dev.localhost" "*.dev.localhost"
82
+ ```
83
+
84
+ 3. **Verify Certificate:**
85
+
86
+ ```sh
87
+ openssl x509 -in .docker/traefik/certs/dev.localhost.crt -text -noout | grep -A 2 "Subject Alternative Name"
88
+ ```
89
+
90
+ ## **4. Validate Setup**
91
+
92
+ 1. **Start Services:**
93
+
94
+ ```sh
95
+ docker compose up -d
96
+ ```
97
+
98
+ 2. **Test in Browser:**
99
+
100
+ - Visit **https://traefik.dev.localhost** — you should see the Traefik dashboard
101
+ - Visit **https://app.dev.localhost** — you should see your app
102
+ - Both should load without SSL warnings
103
+
104
+ ## **5. Troubleshooting**
105
+
106
+ ### **Certificate Files Missing**
107
+
108
+ If Traefik fails to start or shows TLS errors, verify the certificate files exist:
109
+
110
+ ```sh
111
+ ls -la .docker/traefik/certs/
112
+ ```
113
+
114
+ ### **Browser Shows SSL Warning**
115
+
116
+ - Ensure mkcert root CA is installed (`mkcert -install`)
117
+ - For Firefox/Zen Browser, manually import the root CA (see section 2)
118
+ - Clear browser cache and restart the browser
119
+
120
+ ### **Certificate Expiration**
121
+
122
+ mkcert certificates are valid for a long time, but if you need to regenerate:
123
+
124
+ 1. **Remove old certificates:**
125
+
126
+ ```sh
127
+ rm .docker/traefik/certs/dev.localhost.*
128
+ ```
129
+
130
+ 2. **Regenerate (see section 3)**
131
+
132
+ ## **6. Clean Up**
133
+
134
+ 1. **Stop Services:**
135
+
136
+ ```sh
137
+ docker compose down
138
+ ```
139
+
140
+ 2. **Remove Certificates (Optional):**
141
+
142
+ ```sh
143
+ rm -rf .docker/traefik/certs
144
+ ```
145
+
146
+ 3. **Uninstall mkcert Root CA (Optional):**
147
+
148
+ ```sh
149
+ mkcert -uninstall
150
+ ```
151
+
152
+ > **Note:** Removing the root CA will cause SSL warnings in browsers until you reinstall it or regenerate certificates.
@@ -10,15 +10,16 @@ export default {
10
10
  '.': {
11
11
  entry: ['*.config.ts'],
12
12
  },
13
- 'backend': {
13
+ 'api': {
14
14
  drizzle: {
15
15
  config: 'src/database/drizzle/config.ts',
16
16
  },
17
- ignoreDependencies: ['pino-pretty'],
18
17
  },
19
- 'frontend': {
18
+ 'app': {
20
19
  entry: ['src/main.ts'],
21
- ignoreDependencies: ['uno.css'],
20
+ ignoreDependencies: [
21
+ '@vueuse/core',
22
+ ],
22
23
  },
23
24
  },
24
25
  } satisfies KnipConfig
@@ -1,35 +1,36 @@
1
1
  {
2
- "name": "app",
2
+ "name": "project",
3
3
  "type": "module",
4
4
  "private": true,
5
- "packageManager": "bun@1.3.1",
5
+ "packageManager": "bun@1.3.2",
6
6
  "engines": {
7
7
  "node": "lts/*"
8
8
  },
9
9
  "workspaces": [
10
- "backend",
11
- "frontend"
10
+ "api",
11
+ "app"
12
12
  ],
13
13
  "scripts": {
14
- "check": "bun run check:unused && bun run check:eslint && bun run check:stylelint && bun run check:types",
14
+ "check": "bun run check:eslint && bun run check:stylelint && bun run check:unused && bun run check:types",
15
15
  "check:eslint": "eslint . --cache",
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": "bun scripts/dev.ts",
20
19
  "lint": "bun run check:eslint && bun run check:stylelint",
21
20
  "lint:inspect": "bunx @eslint/config-inspector",
22
21
  "update": "bunx taze -I -rwi"
23
22
  },
24
23
  "devDependencies": {
25
- "@kevinmarrec/eslint-config": "^1.5.3",
26
- "@kevinmarrec/stylelint-config": "^1.2.2",
27
- "@kevinmarrec/tsconfig": "^1.1.0",
28
- "concurrently": "^9.2.1",
29
- "eslint": "^9.38.0",
30
- "knip": "^5.66.0",
24
+ "@kevinmarrec/eslint-config": "^1.5.6",
25
+ "@kevinmarrec/stylelint-config": "^1.5.6",
26
+ "@kevinmarrec/tsconfig": "^1.5.6",
27
+ "eslint": "^9.39.1",
28
+ "filesize": "^11.0.13",
29
+ "knip": "^5.69.1",
31
30
  "stylelint": "^16.25.0",
31
+ "tinyexec": "^1.0.2",
32
+ "tinyglobby": "^0.2.15",
32
33
  "typescript": "~5.9.3",
33
- "vue-tsc": "^3.1.1"
34
+ "vue-tsc": "^3.1.3"
34
35
  }
35
36
  }
@@ -2,8 +2,8 @@
2
2
  "extends": "@kevinmarrec/tsconfig",
3
3
  "compilerOptions": {
4
4
  "paths": {
5
- "@backend/*": ["./backend/src/*"],
6
- "@frontend/*": ["./frontend/src/*"]
5
+ "~/api/*": ["./api/src/*"],
6
+ "~/app/*": ["./app/src/*"]
7
7
  }
8
8
  },
9
9
  "exclude": ["**/dist/**"]
@@ -1,4 +0,0 @@
1
- BETTER_AUTH_SECRET=foo
2
- BETTER_AUTH_URL=http://localhost:5137
3
- DATABASE_URL=.db
4
- NODE_ENV=development
@@ -1,65 +0,0 @@
1
- import type { Auth } from '@backend/auth'
2
- import type { Database } from '@backend/database'
3
- import { requiredAuthMiddleware } from '@backend/orpc/middlewares'
4
- import type { Logger } from '@backend/utils/logger'
5
- import { onError, ORPCError, os, type Router } from '@orpc/server'
6
- import { RPCHandler } from '@orpc/server/fetch'
7
- import {
8
- CORSPlugin,
9
- RequestHeadersPlugin,
10
- type RequestHeadersPluginContext,
11
- ResponseHeadersPlugin,
12
- type ResponseHeadersPluginContext,
13
- } from '@orpc/server/plugins'
14
- import { APIError } from 'better-auth/api'
15
-
16
- /* Context */
17
-
18
- export interface Context extends RequestHeadersPluginContext, ResponseHeadersPluginContext {
19
- auth: Auth
20
- db: Database
21
- logger: Logger
22
- }
23
-
24
- /* RPC Handler */
25
-
26
- export function createRpcHandler<T extends Context>(router: Router<any, T>) {
27
- return new RPCHandler<T>(router, {
28
- plugins: [
29
- new CORSPlugin({
30
- credentials: true,
31
- maxAge: 7200, // 2 hours https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Max-Age
32
- }),
33
- new RequestHeadersPlugin(),
34
- new ResponseHeadersPlugin(),
35
- ],
36
- clientInterceptors: [
37
- onError((error, { context }) => {
38
- if (error instanceof APIError) {
39
- throw new ORPCError(error.body?.code ?? 'INTERNAL_SERVER_ERROR', {
40
- status: error.statusCode,
41
- message: error.body?.message,
42
- })
43
- }
44
-
45
- if (error instanceof ORPCError) {
46
- throw error
47
- }
48
-
49
- context.logger.error(error)
50
- }),
51
- ],
52
- })
53
- }
54
-
55
- /* Builder */
56
-
57
- export const pub = os
58
- .$context<Context>()
59
- .errors({
60
- UNAUTHORIZED: { status: 401 },
61
- })
62
-
63
- /** @beta */
64
- export const authed = pub
65
- .use(requiredAuthMiddleware)
@@ -1,33 +0,0 @@
1
- import type { Context } from '@backend/orpc'
2
- import { ORPCError, os } from '@orpc/server'
3
-
4
- export const authMiddleware = os
5
- .$context<Context>()
6
- .middleware(async ({ context, next }) => {
7
- const { headers, response: session } = await context.auth.api.getSession({
8
- headers: context.reqHeaders ?? new Headers(),
9
- returnHeaders: true,
10
- })
11
-
12
- headers.forEach((v, k) => context.resHeaders?.append(k, v))
13
-
14
- return next({
15
- context: {
16
- user: session ? session.user : null,
17
- },
18
- })
19
- })
20
-
21
- export const requiredAuthMiddleware = authMiddleware
22
- .concat(async ({ context, next }) => {
23
- if (!context.user) {
24
- throw new ORPCError('UNAUTHORIZED')
25
- }
26
-
27
- return next({
28
- context: {
29
- user: context.user,
30
- },
31
- })
32
- },
33
- )
@@ -1 +0,0 @@
1
- export * from './auth'
@@ -1,54 +0,0 @@
1
- import { pub } from '@backend/orpc'
2
- import { authMiddleware } from '@backend/orpc/middlewares'
3
- import * as v from 'valibot'
4
-
5
- export const getCurrentUser = pub
6
- .use(authMiddleware)
7
- .handler(async ({ context }) => context.user)
8
-
9
- export const signUp = pub
10
- .input(v.object({
11
- name: v.string(),
12
- email: v.pipe(v.string(), v.email()),
13
- password: v.string(),
14
- rememberMe: v.optional(v.boolean(), true),
15
- }))
16
- .handler(async ({ input, context: { resHeaders, auth } }) => {
17
- const { headers, response } = await auth.api.signUpEmail({
18
- body: input,
19
- returnHeaders: true,
20
- })
21
-
22
- headers.forEach((v, k) => resHeaders?.append(k, v))
23
-
24
- return response.user
25
- })
26
-
27
- export const signIn = pub
28
- .input(v.object({
29
- email: v.pipe(v.string(), v.email()),
30
- password: v.string(),
31
- rememberMe: v.optional(v.boolean(), true),
32
- }))
33
- .handler(async ({ input, context: { resHeaders, auth } }) => {
34
- const { headers, response } = await auth.api.signInEmail({
35
- body: input,
36
- returnHeaders: true,
37
- })
38
-
39
- headers.forEach((v, k) => resHeaders?.append(k, v))
40
-
41
- return response.user
42
- })
43
-
44
- export const signOut = pub
45
- .handler(async ({ context: { auth, reqHeaders, resHeaders } }) => {
46
- const { headers, response } = await auth.api.signOut({
47
- headers: reqHeaders ?? new Headers(),
48
- returnHeaders: true,
49
- })
50
-
51
- headers.forEach((v, k) => resHeaders?.append(k, v))
52
-
53
- return response
54
- })
@@ -1,9 +0,0 @@
1
- import * as auth from './auth'
2
-
3
- export type { RouterClient } from '@orpc/server'
4
-
5
- export const router = {
6
- auth,
7
- }
8
-
9
- export type Router = typeof router
@@ -1 +0,0 @@
1
- VITE_API_URL=http://localhost:4000/rpc
@@ -1,31 +0,0 @@
1
- import { orpc } from '@frontend/lib/orpc'
2
- import { useMutation, useQuery, useQueryClient } from '@tanstack/vue-query'
3
-
4
- export function useAuth() {
5
- const qc = useQueryClient()
6
-
7
- const { data: user } = useQuery(orpc.auth.getCurrentUser.queryOptions({
8
- retry: false,
9
- }))
10
-
11
- const signUp = useMutation(orpc.auth.signUp.mutationOptions({
12
- onSuccess: () => qc.invalidateQueries({ queryKey: orpc.auth.getCurrentUser.queryKey() }),
13
- }))
14
-
15
- const signIn = useMutation(orpc.auth.signIn.mutationOptions({
16
- onSuccess: () => qc.invalidateQueries({ queryKey: orpc.auth.getCurrentUser.queryKey() }),
17
- }))
18
-
19
- const signOut = useMutation(orpc.auth.signOut.mutationOptions({
20
- onSuccess: () => {
21
- qc.setQueryData<null>(orpc.auth.getCurrentUser.queryKey(), null)
22
- },
23
- }))
24
-
25
- return {
26
- user,
27
- signUp,
28
- signIn,
29
- signOut,
30
- }
31
- }
@@ -1,8 +0,0 @@
1
- import concurrently, { type ConcurrentlyCommandInput } from 'concurrently'
2
-
3
- const commandInputs: ConcurrentlyCommandInput[] = [
4
- { name: 'backend', command: `bun --cwd backend dev | pino-pretty`, prefixColor: 'blue' },
5
- { name: 'frontend', command: `bun --cwd frontend dev`, prefixColor: 'green' },
6
- ]
7
-
8
- concurrently(commandInputs)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes