create-disposabl-app 0.1.3 → 0.2.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
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // index.ts
4
- import { spawn, spawnSync } from "node:child_process";
5
- import { existsSync as existsSync2 } from "node:fs";
4
+ import { execFileSync, spawn, spawnSync } from "node:child_process";
5
+ import { lstatSync } from "node:fs";
6
6
  import { resolve } from "node:path";
7
7
  import { fileURLToPath } from "node:url";
8
8
  import { Command, CommanderError } from "commander";
9
9
  import * as p2 from "@clack/prompts";
10
10
  // package.json
11
- var version = "0.1.3";
11
+ var version = "0.2.0";
12
12
 
13
13
  // options.ts
14
14
  var platforms = [
@@ -302,7 +302,7 @@ import {
302
302
  } from "node:fs/promises";
303
303
  import { existsSync } from "node:fs";
304
304
  import { join } from "node:path";
305
- async function createProject(template, destination, name, config) {
305
+ async function createApp(template, destination, name, config, overwrite = false) {
306
306
  config = {
307
307
  ...config,
308
308
  features: selectFeatures(config.features.join(",") || "none")
@@ -313,6 +313,8 @@ async function createProject(template, destination, name, config) {
313
313
  throw new Error("The bundled starter is missing " + folder + "/package.json. Rebuild the CLI.");
314
314
  }
315
315
  }
316
+ if (overwrite)
317
+ await rm(destination, { recursive: true, force: true });
316
318
  await mkdir(destination);
317
319
  try {
318
320
  for (const entry of await readdir(template)) {
@@ -373,6 +375,7 @@ The starter includes web and backend authentication code. No appId or Convex dep
373
375
 
374
376
  class Cancelled extends Error {
375
377
  }
378
+ var pathExists = (path) => lstatSync(path, { throwIfNoEntry: false }) !== undefined;
376
379
  function answer(value) {
377
380
  if (p2.isCancel(value))
378
381
  throw new Cancelled("Cancelled. No files were created.");
@@ -450,7 +453,7 @@ async function main() {
450
453
  message: "App name",
451
454
  placeholder: "my-app",
452
455
  defaultValue: "my-app",
453
- validate: (value) => validateName(value ?? "") || (existsSync2(resolve(value ?? "")) ? "That directory already exists. Choose a new name." : undefined)
456
+ validate: (value) => validateName(value ?? "")
454
457
  }));
455
458
  }
456
459
  if (!name)
@@ -458,9 +461,25 @@ async function main() {
458
461
  const invalidName = validateName(name);
459
462
  if (invalidName)
460
463
  throw new Error(invalidName);
461
- const directory = resolve(name);
462
- if (existsSync2(directory))
463
- throw new Error("Directory already exists: " + directory + ". Choose a new name; existing files are never overwritten.");
464
+ let directory = resolve(name);
465
+ let overwrite = false;
466
+ if (pathExists(directory)) {
467
+ overwrite = interactive && answer(await p2.confirm({
468
+ message: `"${name}" already exists. Delete its contents and replace it?`,
469
+ initialValue: false
470
+ }));
471
+ if (!overwrite) {
472
+ const requestedName = name;
473
+ let suffix = 2;
474
+ do {
475
+ const ending = `-${suffix++}`;
476
+ name = requestedName.slice(0, 214 - ending.length) + ending;
477
+ directory = resolve(name);
478
+ } while (pathExists(directory));
479
+ if (!options.json)
480
+ p2.log.info(`Using "${name}" instead.`);
481
+ }
482
+ }
464
483
  if (interactive && options.platforms === undefined) {
465
484
  const selected = answer(await createPlatformPrompt().prompt() ?? []);
466
485
  chosenPlatforms = selectPlatforms(selected.length ? selected.join(",") : "web");
@@ -485,10 +504,12 @@ async function main() {
485
504
  const setup = options.json ? undefined : projectSetup(name, config);
486
505
  let installationStarted = false;
487
506
  try {
507
+ if (spawnSync("git", ["--version"], { stdio: "ignore" }).status !== 0)
508
+ throw new Error("Install Git first to create the app's initial commit.");
488
509
  if (!options.skipInstall && spawnSync("bun", ["--version"], { stdio: "ignore" }).status !== 0)
489
510
  throw new Error("Install Bun first, or pass --skip-install to create the files only.");
490
511
  const template = fileURLToPath(new URL("./templates/default", import.meta.url));
491
- await createProject(template, directory, name, config);
512
+ await createApp(template, directory, name, config, overwrite);
492
513
  if (!options.skipInstall) {
493
514
  installationStarted = true;
494
515
  setup?.installing();
@@ -503,6 +524,19 @@ async function main() {
503
524
  });
504
525
  throw error;
505
526
  }
527
+ const git = (...args) => execFileSync("git", args, { cwd: directory, stdio: "pipe" });
528
+ git("init", "--initial-branch=main");
529
+ git("add", "--all");
530
+ const hasIdentity = spawnSync("git", ["var", "GIT_AUTHOR_IDENT"], {
531
+ cwd: directory,
532
+ stdio: "ignore"
533
+ }).status === 0;
534
+ git(...hasIdentity ? [] : [
535
+ "-c",
536
+ "user.name=create-disposabl-app",
537
+ "-c",
538
+ "user.email=create-disposabl-app@users.noreply.github.com"
539
+ ], "-c", "commit.gpgsign=false", "commit", "-m", "Initial commit");
506
540
  const folders = selectedFolders(config);
507
541
  if (options.json) {
508
542
  console.log(JSON.stringify({
@@ -0,0 +1,100 @@
1
+ name: CI and deploy
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [main]
6
+ types: [opened, synchronize, reopened]
7
+ push:
8
+ branches: [main]
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ concurrency:
14
+ group: ${{ github.workflow }}-${{ github.head_ref || github.ref_name }}
15
+ cancel-in-progress: false
16
+
17
+ jobs:
18
+ pipeline:
19
+ if: github.event.deleted != true && github.repository != 'disposabl-dev/create-disposabl-app'
20
+ runs-on: ubuntu-latest
21
+ timeout-minutes: 30
22
+ steps:
23
+ - name: Check out repository
24
+ uses: actions/checkout@v6
25
+ with:
26
+ persist-credentials: false
27
+
28
+ - name: Install Node.js
29
+ uses: actions/setup-node@v6
30
+ with:
31
+ node-version: "24"
32
+ package-manager-cache: false
33
+
34
+ - name: Install Bun
35
+ uses: oven-sh/setup-bun@v2
36
+ with:
37
+ bun-version: "1.4.4"
38
+
39
+ - name: Install dependencies
40
+ run: bun install --frozen-lockfile
41
+
42
+ - name: Typecheck
43
+ run: bun run typecheck
44
+
45
+ - name: Lint
46
+ run: bun run lint
47
+
48
+ - name: Check formatting
49
+ run: bun fmt --check
50
+
51
+ - name: Test
52
+ # The starter currently has no tests. Run any tests added later.
53
+ run: bun test --pass-with-no-tests
54
+
55
+ - name: Build
56
+ env:
57
+ VITE_CONVEX_URL: https://ci.invalid
58
+ VITE_CONVEX_SITE_URL: https://ci.invalid
59
+ run: bun run build
60
+
61
+ - name: Deploy production
62
+ id: production
63
+ if: github.event_name == 'push'
64
+ working-directory: packages/backend
65
+ env:
66
+ CONVEX_DEPLOY_KEY: ${{ secrets.CONVEX_DEPLOY_KEY }}
67
+ run: |
68
+ bun x --no-install convex deploy --yes \
69
+ --cmd-url-env-var-name VITE_CONVEX_URL \
70
+ --cmd '
71
+ bun x --no-install --package @convex-dev/auth auth --keys-only --prod &&
72
+ VITE_CONVEX_SITE_URL="$CONVEX_SITE_URL" bun run --cwd ../.. build &&
73
+ echo "url=$CONVEX_SITE_URL" >> "$GITHUB_OUTPUT"
74
+ '
75
+ bun x --no-install static-hosting upload --prod --no-spa --dist ../../apps/web/dist/client
76
+
77
+ - name: Deploy preview
78
+ id: preview
79
+ if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
80
+ working-directory: packages/backend
81
+ env:
82
+ CONVEX_DEPLOY_KEY: ${{ secrets.CONVEX_DEPLOY_KEY_PREVIEW }}
83
+ PREVIEW_NAME: ${{ github.head_ref }}
84
+ run: |
85
+ bun x --no-install convex deploy --yes --preview-name "$PREVIEW_NAME" \
86
+ --cmd-url-env-var-name VITE_CONVEX_URL \
87
+ --cmd '
88
+ bun x --no-install --package @convex-dev/auth auth --keys-only --preview-name "$PREVIEW_NAME" &&
89
+ VITE_CONVEX_SITE_URL="$CONVEX_SITE_URL" bun run --cwd ../.. build &&
90
+ echo "url=$CONVEX_SITE_URL" >> "$GITHUB_OUTPUT"
91
+ '
92
+ bun x --no-install static-hosting upload --preview-name "$PREVIEW_NAME" --no-spa --dist ../../apps/web/dist/client
93
+
94
+ - name: Show deployment URL
95
+ if: steps.production.outcome == 'success' || steps.preview.outcome == 'success'
96
+ env:
97
+ DEPLOYMENT_URL: ${{ steps.production.outputs.url || steps.preview.outputs.url }}
98
+ run: |
99
+ printf 'Deployed app: %s\n' "$DEPLOYMENT_URL"
100
+ printf '### Deployment\n\n[Open app](%s)\n' "$DEPLOYMENT_URL" >> "$GITHUB_STEP_SUMMARY"