create-disposabl-app 0.1.2 → 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 +80 -16
- package/dist/templates/default/.github/workflows/pipeline.yml +100 -0
- package/dist/templates/default/bun.lock +767 -38
- package/dist/templates/default/packages/backend/package.json +1 -0
- package/dist/templates/default/packages/disposabl-kit/server/dev-setup.mjs +39 -0
- package/dist/templates/default/turbo.json +9 -1
- package/package.json +51 -3
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 {
|
|
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.
|
|
11
|
+
var version = "0.2.0";
|
|
12
12
|
|
|
13
13
|
// options.ts
|
|
14
14
|
var platforms = [
|
|
@@ -219,20 +219,50 @@ function setupTable(name, config, status, columns = 80) {
|
|
|
219
219
|
function projectSetup(name, config) {
|
|
220
220
|
const live = Boolean(process.stdout.isTTY);
|
|
221
221
|
const status = { installation: "Pending" };
|
|
222
|
-
let
|
|
222
|
+
let previous = [];
|
|
223
|
+
let columns = process.stdout.columns;
|
|
223
224
|
let started;
|
|
224
225
|
let timer;
|
|
225
226
|
function render() {
|
|
226
|
-
|
|
227
|
-
|
|
227
|
+
const frame = setupTable(name, config, status, process.stdout.columns).split(`
|
|
228
|
+
`);
|
|
229
|
+
if (live && previous.length === frame.length && columns === process.stdout.columns) {
|
|
230
|
+
let update = "";
|
|
231
|
+
for (let row = 0;row < frame.length; row++) {
|
|
232
|
+
const before = previous[row];
|
|
233
|
+
const after = frame[row];
|
|
234
|
+
if (before === after)
|
|
235
|
+
continue;
|
|
236
|
+
let start = 0;
|
|
237
|
+
let end = after.length;
|
|
238
|
+
if (before.length === after.length && !before.includes("\x1B") && !after.includes("\x1B")) {
|
|
239
|
+
while (start < end && before[start] === after[start])
|
|
240
|
+
start++;
|
|
241
|
+
while (end > start && before[end - 1] === after[end - 1])
|
|
242
|
+
end--;
|
|
243
|
+
}
|
|
244
|
+
const distance = frame.length - row;
|
|
245
|
+
update += `\x1B[${distance}A\x1B[${start + 1}G`;
|
|
246
|
+
if (start === 0)
|
|
247
|
+
update += "\x1B[2K";
|
|
248
|
+
update += after.slice(start, end);
|
|
249
|
+
update += `\x1B[${distance}B\x1B[1G`;
|
|
250
|
+
}
|
|
251
|
+
if (update)
|
|
252
|
+
process.stdout.write(update);
|
|
253
|
+
previous = frame;
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (live && previous.length) {
|
|
257
|
+
moveCursor(process.stdout, 0, -previous.length);
|
|
228
258
|
cursorTo(process.stdout, 0);
|
|
229
259
|
clearScreenDown(process.stdout);
|
|
230
260
|
}
|
|
231
|
-
|
|
232
|
-
|
|
261
|
+
process.stdout.write(frame.join(`
|
|
262
|
+
`) + `
|
|
233
263
|
`);
|
|
234
|
-
|
|
235
|
-
|
|
264
|
+
previous = frame;
|
|
265
|
+
columns = process.stdout.columns;
|
|
236
266
|
}
|
|
237
267
|
if (live)
|
|
238
268
|
render();
|
|
@@ -272,7 +302,7 @@ import {
|
|
|
272
302
|
} from "node:fs/promises";
|
|
273
303
|
import { existsSync } from "node:fs";
|
|
274
304
|
import { join } from "node:path";
|
|
275
|
-
async function
|
|
305
|
+
async function createApp(template, destination, name, config, overwrite = false) {
|
|
276
306
|
config = {
|
|
277
307
|
...config,
|
|
278
308
|
features: selectFeatures(config.features.join(",") || "none")
|
|
@@ -283,6 +313,8 @@ async function createProject(template, destination, name, config) {
|
|
|
283
313
|
throw new Error("The bundled starter is missing " + folder + "/package.json. Rebuild the CLI.");
|
|
284
314
|
}
|
|
285
315
|
}
|
|
316
|
+
if (overwrite)
|
|
317
|
+
await rm(destination, { recursive: true, force: true });
|
|
286
318
|
await mkdir(destination);
|
|
287
319
|
try {
|
|
288
320
|
for (const entry of await readdir(template)) {
|
|
@@ -343,6 +375,7 @@ The starter includes web and backend authentication code. No appId or Convex dep
|
|
|
343
375
|
|
|
344
376
|
class Cancelled extends Error {
|
|
345
377
|
}
|
|
378
|
+
var pathExists = (path) => lstatSync(path, { throwIfNoEntry: false }) !== undefined;
|
|
346
379
|
function answer(value) {
|
|
347
380
|
if (p2.isCancel(value))
|
|
348
381
|
throw new Cancelled("Cancelled. No files were created.");
|
|
@@ -420,7 +453,7 @@ async function main() {
|
|
|
420
453
|
message: "App name",
|
|
421
454
|
placeholder: "my-app",
|
|
422
455
|
defaultValue: "my-app",
|
|
423
|
-
validate: (value) => validateName(value ?? "")
|
|
456
|
+
validate: (value) => validateName(value ?? "")
|
|
424
457
|
}));
|
|
425
458
|
}
|
|
426
459
|
if (!name)
|
|
@@ -428,9 +461,25 @@ async function main() {
|
|
|
428
461
|
const invalidName = validateName(name);
|
|
429
462
|
if (invalidName)
|
|
430
463
|
throw new Error(invalidName);
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
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
|
+
}
|
|
434
483
|
if (interactive && options.platforms === undefined) {
|
|
435
484
|
const selected = answer(await createPlatformPrompt().prompt() ?? []);
|
|
436
485
|
chosenPlatforms = selectPlatforms(selected.length ? selected.join(",") : "web");
|
|
@@ -455,10 +504,12 @@ async function main() {
|
|
|
455
504
|
const setup = options.json ? undefined : projectSetup(name, config);
|
|
456
505
|
let installationStarted = false;
|
|
457
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.");
|
|
458
509
|
if (!options.skipInstall && spawnSync("bun", ["--version"], { stdio: "ignore" }).status !== 0)
|
|
459
510
|
throw new Error("Install Bun first, or pass --skip-install to create the files only.");
|
|
460
511
|
const template = fileURLToPath(new URL("./templates/default", import.meta.url));
|
|
461
|
-
await
|
|
512
|
+
await createApp(template, directory, name, config, overwrite);
|
|
462
513
|
if (!options.skipInstall) {
|
|
463
514
|
installationStarted = true;
|
|
464
515
|
setup?.installing();
|
|
@@ -473,6 +524,19 @@ async function main() {
|
|
|
473
524
|
});
|
|
474
525
|
throw error;
|
|
475
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");
|
|
476
540
|
const folders = selectedFolders(config);
|
|
477
541
|
if (options.json) {
|
|
478
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"
|