@kevinmarrec/create-app 0.17.0 → 0.18.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 CHANGED
@@ -38,6 +38,8 @@ CLI that scaffolds an opinionated [Bun](https://bun.sh/) & [Vue](https://vuejs.o
38
38
 
39
39
  > Requires [Bun](https://bun.sh/) v1.3 _or later_.
40
40
 
41
+ By default, the CLI scaffolds from its built-in opinionated template:
42
+
41
43
  ```sh
42
44
  bun create @kevinmarrec/app
43
45
  # OR
@@ -45,3 +47,25 @@ bunx @kevinmarrec/create-app
45
47
  ```
46
48
 
47
49
  After scaffolding, see the generated `README.md` in your project root for detailed setup instructions, including environment configuration, Docker setup, and development workflows.
50
+
51
+ ### Custom Template
52
+
53
+ You can use the `--template` (`-t`) option to scaffold from any external git repository instead of the default template:
54
+
55
+ ```sh
56
+ # GitHub shorthand
57
+ bun create @kevinmarrec/app -t user/repo
58
+
59
+ # HTTPS URL
60
+ bun create @kevinmarrec/app -t https://github.com/user/repo.git
61
+
62
+ # SSH URL
63
+ bun create @kevinmarrec/app -t git@github.com:user/repo.git
64
+
65
+ # Subdirectory of a repository
66
+ bun create @kevinmarrec/app -t user/repo#path/to/template
67
+ ```
68
+
69
+ The repository is cloned with `--depth 1` (shallow clone) and its contents are copied into the target directory.
70
+
71
+ Use `#subdir` to scaffold from a specific subdirectory within the repository.
package/dist/index.mjs CHANGED
@@ -3,12 +3,13 @@ import process from "node:process";
3
3
  import { parseArgs } from "node:util";
4
4
  import { cancel, confirm, intro, isCancel, log, note, outro, tasks, text } from "@clack/prompts";
5
5
  import c from "ansis";
6
- import { join, resolve } from "pathe";
6
+ import { basename, join, resolve } from "pathe";
7
7
  import { x } from "tinyexec";
8
8
  import fs from "node:fs/promises";
9
+ import os from "node:os";
9
10
 
10
11
  //#region package.json
11
- var version = "0.17.0";
12
+ var version = "0.18.0";
12
13
 
13
14
  //#endregion
14
15
  //#region src/utils/fs.ts
@@ -31,11 +32,62 @@ var fs_default = {
31
32
  exists
32
33
  };
33
34
 
35
+ //#endregion
36
+ //#region src/utils/template.ts
37
+ function resolveTemplate(template) {
38
+ const [repo, ...rest] = template.split("#");
39
+ const subdir = rest.join("#") || void 0;
40
+ return {
41
+ url: resolveTemplateUrl(repo),
42
+ subdir
43
+ };
44
+ }
45
+ function resolveTemplateUrl(repo) {
46
+ if (repo.startsWith("https://")) return repo;
47
+ if (repo.startsWith("git@")) return repo;
48
+ if (/^[^/\s]+\/[^/\s]+$/.test(repo)) return `https://github.com/${repo}.git`;
49
+ throw new Error(`Invalid template format: "${repo}"`);
50
+ }
51
+ async function cloneTemplate(url) {
52
+ const tempDir = await fs_default.mkdtemp(join(os.tmpdir(), "create-app-template-"));
53
+ try {
54
+ await x("git", [
55
+ "clone",
56
+ "--depth",
57
+ "1",
58
+ url,
59
+ tempDir
60
+ ]);
61
+ } catch (error) {
62
+ await fs_default.rm(tempDir, {
63
+ recursive: true,
64
+ force: true
65
+ });
66
+ throw new Error(`Failed to clone template from "${url}"`, { cause: error });
67
+ }
68
+ return tempDir;
69
+ }
70
+
34
71
  //#endregion
35
72
  //#region src/scaffold.ts
36
- async function scaffold(root) {
73
+ async function scaffold(root, template) {
37
74
  await fs_default.exists(root) ? await fs_default.empty(root) : await fs_default.mkdir(root, { recursive: true });
38
- await fs_default.cp(join(import.meta.dirname, "../template"), root, { recursive: true });
75
+ if (template) {
76
+ const { url, subdir } = resolveTemplate(template);
77
+ const tempDir = await cloneTemplate(url);
78
+ try {
79
+ const sourceDir = subdir ? join(tempDir, subdir) : tempDir;
80
+ await fs_default.cp(sourceDir, root, {
81
+ recursive: true,
82
+ filter: (src) => !ignorePredicate(basename(src))
83
+ });
84
+ } finally {
85
+ await fs_default.rm(tempDir, {
86
+ recursive: true,
87
+ force: true
88
+ });
89
+ }
90
+ } else await fs_default.cp(join(import.meta.dirname, "../template"), root, { recursive: true });
39
91
  }
40
92
 
41
93
  //#endregion
@@ -59,6 +111,10 @@ async function run() {
59
111
  type: "boolean",
60
112
  short: "h"
61
113
  },
114
+ template: {
115
+ type: "string",
116
+ short: "t"
117
+ },
62
118
  version: {
63
119
  type: "boolean",
64
120
  short: "v"
@@ -66,7 +122,7 @@ async function run() {
66
122
  }
67
123
  });
68
124
  if (options.help) {
69
- process.stdout.write("Usage: create-app [OPTIONS...] [DIRECTORY]\n\nOptions:\n -f, --force Create the project even if the directory is not empty.\n -h, --help Display this help message.\n -v, --version Display the version number of this CLI.\n");
125
+ process.stdout.write("Usage: create-app [OPTIONS...] [DIRECTORY]\n\nOptions:\n -f, --force Create the project even if the directory is not empty.\n -h, --help Display this help message.\n -t, --template <repo> Use a custom template from a git repository (supports #subdir).\n -v, --version Display the version number of this CLI.\n");
70
126
  process.exit(0);
71
127
  }
72
128
  if (options.version) {
@@ -96,10 +152,10 @@ async function run() {
96
152
  }), { strict: true });
97
153
  }
98
154
  await tasks([{
99
- title: `Scaffolding project in ${c.blue(targetDir)}`,
155
+ title: options.template ? `Cloning ${c.blue(options.template)} into ${c.blue(targetDir)}` : `Scaffolding project in ${c.blue(targetDir)}`,
100
156
  task: async () => {
101
- await scaffold(targetDir);
102
- return `Scaffolded project in ${c.blue(targetDir)}`;
157
+ await scaffold(targetDir, options.template);
158
+ return options.template ? `Cloned ${c.blue(options.template)} and scaffolded project in ${c.blue(targetDir)}` : `Scaffolded project in ${c.blue(targetDir)}`;
103
159
  }
104
160
  }]);
105
161
  const shouldInstall = await confirm({
@@ -110,7 +166,7 @@ async function run() {
110
166
  });
111
167
  maybeCancel(shouldInstall);
112
168
  await tasks([{
113
- title: "Installing with bun",
169
+ title: "Installing dependencies",
114
170
  enabled: shouldInstall,
115
171
  task: async () => {
116
172
  await x("bun", [
@@ -119,7 +175,7 @@ async function run() {
119
175
  targetDir,
120
176
  "--force"
121
177
  ]);
122
- return "Installed with bun";
178
+ return "Installed dependencies";
123
179
  }
124
180
  }]);
125
181
  await note([targetDir !== cwd && `cd ${c.reset.blue(projectName)}`, `bun run dev`].filter(Boolean).join("\n"), "Next steps");
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kevinmarrec/create-app",
3
3
  "type": "module",
4
- "version": "0.17.0",
4
+ "version": "0.18.0",
5
5
  "packageManager": "bun@1.3.9",
6
6
  "description": "CLI that scaffolds an opinionated Bun & Vue fullstack application.",
7
7
  "author": "Kevin Marrec <kevin@marrec.io>",
@@ -44,25 +44,25 @@
44
44
  "test:coverage": "vitest --coverage"
45
45
  },
46
46
  "dependencies": {
47
- "@clack/prompts": "^1.0.1",
47
+ "@clack/prompts": "^1.1.0",
48
48
  "ansis": "^4.2.0",
49
49
  "pathe": "^2.0.3",
50
50
  "tinyexec": "^1.0.2"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@faker-js/faker": "^10.3.0",
54
- "@kevinmarrec/eslint-config": "^1.11.0",
55
- "@kevinmarrec/stylelint-config": "^1.11.0",
56
- "@kevinmarrec/tsconfig": "^1.11.0",
57
- "@types/bun": "^1.3.9",
54
+ "@kevinmarrec/eslint-config": "^1.12.1",
55
+ "@kevinmarrec/stylelint-config": "^1.12.1",
56
+ "@kevinmarrec/tsconfig": "^1.12.1",
57
+ "@types/bun": "^1.3.10",
58
58
  "@vitest/coverage-v8": "^4.0.18",
59
59
  "bumpp": "^10.4.1",
60
- "eslint": "^9.39.2",
61
- "knip": "^5.84.1",
62
- "stylelint": "^17.3.0",
60
+ "eslint": "^9.39.3",
61
+ "knip": "^5.85.0",
62
+ "stylelint": "^17.4.0",
63
63
  "tsdown": "^0.20.3",
64
64
  "typescript": "^5.9.3",
65
65
  "vitest": "^4.0.18",
66
- "vue-tsc": "^3.2.4"
66
+ "vue-tsc": "^3.2.5"
67
67
  }
68
68
  }
@@ -1,7 +1,7 @@
1
1
  services:
2
2
  mailpit:
3
3
  profiles: [mail]
4
- image: axllent/mailpit:v1.29.1
4
+ image: axllent/mailpit:v1.29.2
5
5
  container_name: mailpit
6
6
  depends_on:
7
7
  traefik:
@@ -1,7 +1,7 @@
1
1
  services:
2
2
  metabase:
3
3
  profiles: [metabase]
4
- image: metabase/metabase:v0.58.7
4
+ image: metabase/metabase:v0.59.1
5
5
  container_name: metabase
6
6
  depends_on:
7
7
  traefik:
@@ -1,6 +1,6 @@
1
1
  services:
2
2
  postgres:
3
- image: postgres:18.2-alpine
3
+ image: postgres:18.3-alpine
4
4
  container_name: postgres
5
5
  environment:
6
6
  - POSTGRES_USER=user
@@ -1,6 +1,6 @@
1
1
  services:
2
2
  traefik:
3
- image: traefik:v3.6.8
3
+ image: traefik:v3.6.9
4
4
  container_name: traefik
5
5
  restart: unless-stopped
6
6
  security_opt:
@@ -1,9 +1,15 @@
1
1
  {
2
- "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3
- "extends": ["github>kevinmarrec/renovate-config"],
2
+ "extends": ["github>kevinmarrec/config//packages/renovate/default"],
4
3
  "docker-compose": {
5
4
  "managerFilePatterns": [
6
5
  "/^\\.docker\\/[^/]*\\/service\\.ya?ml$/"
7
6
  ]
8
- }
7
+ },
8
+ "packageRules": [
9
+ {
10
+ "matchDatasources": ["docker"],
11
+ "matchPackageNames": ["imbios/bun-node"],
12
+ "versioning": "regex:^(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<patch>\\d+)-(?<compatibility>\\d+)\\.(?<build>\\d+\\.\\d+)-alpine$"
13
+ }
14
+ ]
9
15
  }
@@ -14,7 +14,7 @@ jobs:
14
14
  runs-on: ubuntu-latest
15
15
  steps:
16
16
  - name: Setup Bun
17
- uses: kevinmarrec/workflows/setup-bun@9d2e32941c6fbf76894926019c66ef428ddcc4d2 # main
17
+ uses: kevinmarrec/workflows/setup-bun@e2dafbacb91d9e74b79980f8f5a63dd20bf96619 # main
18
18
 
19
19
  - name: Lint
20
20
  run: bun run lint
@@ -32,12 +32,12 @@ jobs:
32
32
  pull-requests: write
33
33
  steps:
34
34
  - name: Setup Bun
35
- uses: kevinmarrec/workflows/setup-bun@9d2e32941c6fbf76894926019c66ef428ddcc4d2 # main
35
+ uses: kevinmarrec/workflows/setup-bun@e2dafbacb91d9e74b79980f8f5a63dd20bf96619 # main
36
36
 
37
37
  - name: Build project
38
38
  run: bun run build
39
39
 
40
40
  - name: Analyze project build size differences
41
- uses: kevinmarrec/workflows/filesize-diff@9d2e32941c6fbf76894926019c66ef428ddcc4d2 # main
41
+ uses: kevinmarrec/workflows/filesize-diff@e2dafbacb91d9e74b79980f8f5a63dd20bf96619 # main
42
42
  with:
43
43
  directories: app/dist,api/dist
@@ -1 +1 @@
1
- 24.13.1
1
+ 24.14.0
@@ -69,6 +69,13 @@
69
69
  "orpc": "context"
70
70
  },
71
71
 
72
+ "json.schemas": [
73
+ {
74
+ "fileMatch": ["renovate.json"],
75
+ "url": "https://docs.renovatebot.com/renovate-schema.json"
76
+ }
77
+ ],
78
+
72
79
  // i18n Ally
73
80
  "i18n-ally.enabledFrameworks": ["vue"],
74
81
  "i18n-ally.keystyle": "nested",
@@ -41,7 +41,7 @@ A full-stack application template with Vue.js frontend, Bun-based API backend, a
41
41
 
42
42
  ## Project Structure
43
43
 
44
- ```
44
+ ```sh
45
45
  project/
46
46
  ├── api/ # Backend API server
47
47
  │ ├── src/
@@ -1,4 +1,4 @@
1
- FROM --platform=$BUILDPLATFORM oven/bun:1.3.9-slim AS build
1
+ FROM --platform=$BUILDPLATFORM oven/bun:1.3.10-slim AS build
2
2
 
3
3
  ARG TARGETARCH
4
4
 
@@ -9,18 +9,19 @@
9
9
  "db:migrate": "bun --bun run drizzle-kit migrate --config src/database/drizzle/config.ts"
10
10
  },
11
11
  "dependencies": {
12
+ "@better-auth/drizzle-adapter": "^1.5.2",
12
13
  "@libsql/client": "^0.17.0",
13
- "@orpc/server": "^1.13.5",
14
- "better-auth": "^1.4.18",
14
+ "@orpc/server": "^1.13.6",
15
+ "better-auth": "^1.5.2",
15
16
  "drizzle-orm": "^0.45.1",
16
17
  "pino": "^10.3.1",
17
18
  "valibot": "^1.2.0"
18
19
  },
19
20
  "devDependencies": {
20
- "@kevinmarrec/tsconfig": "^1.11.0",
21
- "@types/bun": "^1.3.9",
21
+ "@kevinmarrec/tsconfig": "^1.12.1",
22
+ "@types/bun": "^1.3.10",
22
23
  "drizzle-kit": "^0.31.9",
23
- "pg": "^8.18.0",
24
+ "pg": "^8.19.0",
24
25
  "pino-pretty": "^13.1.3",
25
26
  "typescript": "~5.9.3"
26
27
  }
@@ -1,5 +1,5 @@
1
- import { betterAuth } from 'better-auth'
2
- import { type DB, drizzleAdapter } from 'better-auth/adapters/drizzle'
1
+ import { type DB, drizzleAdapter } from '@better-auth/drizzle-adapter'
2
+ import { betterAuth } from 'better-auth/minimal'
3
3
  import type { Logger } from 'pino'
4
4
 
5
5
  import { env } from '../env'
@@ -1,4 +1,4 @@
1
- FROM --platform=$BUILDPLATFORM imbios/bun-node:1.3.9-24.13.1-alpine AS build
1
+ FROM --platform=$BUILDPLATFORM imbios/bun-node:1.3.10-24.14.0-alpine AS build
2
2
 
3
3
  ARG TARGETARCH
4
4
 
@@ -9,31 +9,31 @@
9
9
  "preview": "vite preview --open"
10
10
  },
11
11
  "dependencies": {
12
- "@kevinmarrec/vue-i18n": "^1.2.1",
13
- "@orpc/client": "^1.13.5",
14
- "@orpc/vue-colada": "^1.13.5",
15
- "@pinia/colada": "^0.21.4",
16
- "@unhead/vue": "^2.1.4",
12
+ "@kevinmarrec/vue-i18n": "^1.2.2",
13
+ "@orpc/client": "^1.13.6",
14
+ "@orpc/vue-colada": "^1.13.6",
15
+ "@pinia/colada": "^0.21.7",
16
+ "@unhead/vue": "^2.1.10",
17
17
  "@vueuse/core": "^14.2.1",
18
- "better-auth": "^1.4.18",
18
+ "better-auth": "^1.5.2",
19
19
  "pinia": "^3.0.4",
20
- "unocss": "^66.6.0",
21
- "vue": "^3.5.28"
20
+ "unocss": "^66.6.4",
21
+ "vue": "^3.5.29"
22
22
  },
23
23
  "devDependencies": {
24
- "@kevinmarrec/tsconfig": "^1.11.0",
25
- "@kevinmarrec/unocss-config": "^1.11.0",
26
- "@kevinmarrec/vite-plugin-dark-mode": "^1.2.1",
24
+ "@kevinmarrec/tsconfig": "^1.12.1",
25
+ "@kevinmarrec/unocss-config": "^1.12.1",
26
+ "@kevinmarrec/vite-plugin-dark-mode": "^1.2.2",
27
27
  "@modyfi/vite-plugin-yaml": "^1.1.1",
28
- "@unhead/addons": "^2.1.4",
28
+ "@unhead/addons": "^2.1.10",
29
29
  "@vitejs/plugin-vue": "^6.0.4",
30
30
  "beasties": "^0.4.1",
31
- "rollup-plugin-visualizer": "^6.0.5",
31
+ "rollup-plugin-visualizer": "^7.0.1",
32
32
  "typescript": "~5.9.3",
33
33
  "valibot": "^1.2.0",
34
34
  "vite": "^7.3.1",
35
35
  "vite-plugin-valibot-env": "^1.0.1",
36
- "vite-plugin-vue-devtools": "^8.0.6",
36
+ "vite-plugin-vue-devtools": "^8.0.7",
37
37
  "vite-ssg": "^28.3.0",
38
38
  "vite-tsconfig-paths": "^6.1.1"
39
39
  }
@@ -32,7 +32,7 @@ export default defineConfig(({ command, mode }) => {
32
32
  }),
33
33
  tsconfigPaths(),
34
34
  unhead(),
35
- unocss(),
35
+ unocss(`${import.meta.dirname}/uno.config.ts`),
36
36
  valibot(envSchema),
37
37
  vue({
38
38
  features: {
@@ -16,7 +16,7 @@ services:
16
16
 
17
17
  api:
18
18
  <<: *common
19
- image: oven/bun:1.3.9-alpine
19
+ image: oven/bun:1.3.10-alpine
20
20
  container_name: api
21
21
  init: true
22
22
  depends_on:
@@ -36,7 +36,7 @@ services:
36
36
 
37
37
  app:
38
38
  <<: *common
39
- image: imbios/bun-node:1.3.9-24.13.1-alpine
39
+ image: imbios/bun-node:1.3.10-24.14.0-alpine
40
40
  container_name: app
41
41
  init: true
42
42
  depends_on:
@@ -4,7 +4,7 @@
4
4
  "private": true,
5
5
  "packageManager": "bun@1.3.9",
6
6
  "engines": {
7
- "node": ">=24.13.1"
7
+ "node": ">=24.14.0"
8
8
  },
9
9
  "workspaces": [
10
10
  "api",
@@ -23,13 +23,13 @@
23
23
  "update": "bunx taze -I -rwi"
24
24
  },
25
25
  "devDependencies": {
26
- "@kevinmarrec/eslint-config": "^1.11.0",
27
- "@kevinmarrec/stylelint-config": "^1.11.0",
28
- "@kevinmarrec/tsconfig": "^1.11.0",
29
- "eslint": "^9.39.2",
30
- "knip": "^5.84.1",
31
- "stylelint": "^17.3.0",
26
+ "@kevinmarrec/eslint-config": "^1.12.1",
27
+ "@kevinmarrec/stylelint-config": "^1.12.1",
28
+ "@kevinmarrec/tsconfig": "^1.12.1",
29
+ "eslint": "^9.39.3",
30
+ "knip": "^5.85.0",
31
+ "stylelint": "^17.4.0",
32
32
  "typescript": "~5.9.3",
33
- "vue-tsc": "^3.2.4"
33
+ "vue-tsc": "^3.2.5"
34
34
  }
35
35
  }
@@ -1,3 +1,9 @@
1
1
  {
2
- "extends": "@kevinmarrec/tsconfig"
2
+ "extends": "@kevinmarrec/tsconfig",
3
+ "compilerOptions": {
4
+ "paths": {
5
+ "~/api/*": ["./api/src/*"],
6
+ "~/app/*": ["./app/src/*"]
7
+ }
8
+ }
3
9
  }