@kevinmarrec/create-app 0.17.0 → 0.18.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
@@ -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,13 +3,12 @@ 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
-
9
+ import os from "node:os";
10
10
  //#region package.json
11
- var version = "0.17.0";
12
-
11
+ var version = "0.18.1";
13
12
  //#endregion
14
13
  //#region src/utils/fs.ts
15
14
  const IGNORED_FILES = new Set([".git"]);
@@ -30,14 +29,63 @@ var fs_default = {
30
29
  emptyCheck,
31
30
  exists
32
31
  };
33
-
32
+ //#endregion
33
+ //#region src/utils/template.ts
34
+ const GITHUB_SHORTHAND_RE = /^[^/\s]+\/[^/\s]+$/;
35
+ function resolveTemplate(template) {
36
+ const [repo, ...rest] = template.split("#");
37
+ const subdir = rest.join("#") || void 0;
38
+ return {
39
+ url: resolveTemplateUrl(repo),
40
+ subdir
41
+ };
42
+ }
43
+ function resolveTemplateUrl(repo) {
44
+ if (repo.startsWith("https://")) return repo;
45
+ if (repo.startsWith("git@")) return repo;
46
+ if (GITHUB_SHORTHAND_RE.test(repo)) return `https://github.com/${repo}.git`;
47
+ throw new Error(`Invalid template format: "${repo}"`);
48
+ }
49
+ async function cloneTemplate(url) {
50
+ const tempDir = await fs_default.mkdtemp(join(os.tmpdir(), "create-app-template-"));
51
+ try {
52
+ await x("git", [
53
+ "clone",
54
+ "--depth",
55
+ "1",
56
+ url,
57
+ tempDir
58
+ ]);
59
+ } catch (error) {
60
+ await fs_default.rm(tempDir, {
61
+ recursive: true,
62
+ force: true
63
+ });
64
+ throw new Error(`Failed to clone template from "${url}"`, { cause: error });
65
+ }
66
+ return tempDir;
67
+ }
34
68
  //#endregion
35
69
  //#region src/scaffold.ts
36
- async function scaffold(root) {
70
+ async function scaffold(root, template) {
37
71
  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 });
72
+ if (template) {
73
+ const { url, subdir } = resolveTemplate(template);
74
+ const tempDir = await cloneTemplate(url);
75
+ try {
76
+ const sourceDir = subdir ? join(tempDir, subdir) : tempDir;
77
+ await fs_default.cp(sourceDir, root, {
78
+ recursive: true,
79
+ filter: (src) => !ignorePredicate(basename(src))
80
+ });
81
+ } finally {
82
+ await fs_default.rm(tempDir, {
83
+ recursive: true,
84
+ force: true
85
+ });
86
+ }
87
+ } else await fs_default.cp(join(import.meta.dirname, "../template"), root, { recursive: true });
39
88
  }
40
-
41
89
  //#endregion
42
90
  //#region src/run.ts
43
91
  function maybeCancel(value, options) {
@@ -59,6 +107,10 @@ async function run() {
59
107
  type: "boolean",
60
108
  short: "h"
61
109
  },
110
+ template: {
111
+ type: "string",
112
+ short: "t"
113
+ },
62
114
  version: {
63
115
  type: "boolean",
64
116
  short: "v"
@@ -66,7 +118,7 @@ async function run() {
66
118
  }
67
119
  });
68
120
  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");
121
+ 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
122
  process.exit(0);
71
123
  }
72
124
  if (options.version) {
@@ -96,10 +148,10 @@ async function run() {
96
148
  }), { strict: true });
97
149
  }
98
150
  await tasks([{
99
- title: `Scaffolding project in ${c.blue(targetDir)}`,
151
+ title: options.template ? `Cloning ${c.blue(options.template)} into ${c.blue(targetDir)}` : `Scaffolding project in ${c.blue(targetDir)}`,
100
152
  task: async () => {
101
- await scaffold(targetDir);
102
- return `Scaffolded project in ${c.blue(targetDir)}`;
153
+ await scaffold(targetDir, options.template);
154
+ return options.template ? `Cloned ${c.blue(options.template)} and scaffolded project in ${c.blue(targetDir)}` : `Scaffolded project in ${c.blue(targetDir)}`;
103
155
  }
104
156
  }]);
105
157
  const shouldInstall = await confirm({
@@ -110,7 +162,7 @@ async function run() {
110
162
  });
111
163
  maybeCancel(shouldInstall);
112
164
  await tasks([{
113
- title: "Installing with bun",
165
+ title: "Installing dependencies",
114
166
  enabled: shouldInstall,
115
167
  task: async () => {
116
168
  await x("bun", [
@@ -119,19 +171,17 @@ async function run() {
119
171
  targetDir,
120
172
  "--force"
121
173
  ]);
122
- return "Installed with bun";
174
+ return "Installed dependencies";
123
175
  }
124
176
  }]);
125
177
  await note([targetDir !== cwd && `cd ${c.reset.blue(projectName)}`, `bun run dev`].filter(Boolean).join("\n"), "Next steps");
126
178
  await outro(`Problems? ${c.cyan("https://github.com/kevinmarrec/create-app/issues")}`);
127
179
  }
128
-
129
180
  //#endregion
130
181
  //#region src/index.ts
131
182
  run().catch((error) => {
132
183
  console.error(error);
133
184
  process.exitCode = 1;
134
185
  });
135
-
136
186
  //#endregion
137
- export { };
187
+ export {};
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@kevinmarrec/create-app",
3
3
  "type": "module",
4
- "version": "0.17.0",
5
- "packageManager": "bun@1.3.9",
4
+ "version": "0.18.1",
5
+ "packageManager": "bun@1.3.10",
6
6
  "description": "CLI that scaffolds an opinionated Bun & Vue fullstack application.",
7
7
  "author": "Kevin Marrec <kevin@marrec.io>",
8
8
  "license": "MIT",
@@ -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.13.0",
55
+ "@kevinmarrec/stylelint-config": "^1.13.0",
56
+ "@kevinmarrec/tsconfig": "^1.13.0",
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",
63
- "tsdown": "^0.20.3",
60
+ "eslint": "^9.39.4",
61
+ "knip": "^5.86.0",
62
+ "stylelint": "^17.4.0",
63
+ "tsdown": "^0.21.0",
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.10
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.4",
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.4",
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.13.0",
22
+ "@types/bun": "^1.3.10",
22
23
  "drizzle-kit": "^0.31.9",
23
- "pg": "^8.18.0",
24
+ "pg": "^8.20.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.3",
13
+ "@orpc/client": "^1.13.6",
14
+ "@orpc/vue-colada": "^1.13.6",
15
+ "@pinia/colada": "^1.0.0",
16
+ "@unhead/vue": "^2.1.10",
17
17
  "@vueuse/core": "^14.2.1",
18
- "better-auth": "^1.4.18",
18
+ "better-auth": "^1.5.4",
19
19
  "pinia": "^3.0.4",
20
- "unocss": "^66.6.0",
21
- "vue": "^3.5.28"
20
+ "unocss": "^66.6.6",
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.13.0",
25
+ "@kevinmarrec/unocss-config": "^1.13.0",
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:
@@ -2,9 +2,9 @@
2
2
  "name": "project",
3
3
  "type": "module",
4
4
  "private": true,
5
- "packageManager": "bun@1.3.9",
5
+ "packageManager": "bun@1.3.10",
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.13.0",
27
+ "@kevinmarrec/stylelint-config": "^1.13.0",
28
+ "@kevinmarrec/tsconfig": "^1.13.0",
29
+ "eslint": "^9.39.4",
30
+ "knip": "^5.86.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
  }