create-shipkit 0.1.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 +50 -0
- package/index.js +162 -0
- package/package.json +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# create-shipkit
|
|
2
|
+
|
|
3
|
+
Scaffold the [Skippie hackathon starter](https://github.com/S-kkipie/hackaton-starter)
|
|
4
|
+
— Next 16 + Elysia + Better Auth + Drizzle/Postgres with a `Project` CRUD
|
|
5
|
+
reference domain (server-driven data-table + TanStack Form).
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx create-shipkit my-app
|
|
11
|
+
# or
|
|
12
|
+
npm create shipkit my-app
|
|
13
|
+
# or
|
|
14
|
+
pnpm create shipkit my-app
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Then:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
cd my-app
|
|
21
|
+
pnpm install
|
|
22
|
+
cp .env.example .env # set DATABASE_URL, BETTER_AUTH_SECRET, NEXT_PUBLIC_APP_URL
|
|
23
|
+
pnpm db:migrate
|
|
24
|
+
pnpm dev
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`BETTER_AUTH_SECRET`: `openssl rand -base64 32`. `DATABASE_URL`: any Postgres
|
|
28
|
+
(Supabase, Neon, local).
|
|
29
|
+
|
|
30
|
+
## What it does
|
|
31
|
+
|
|
32
|
+
- Shallow-clones the template repo (`git clone --depth 1`).
|
|
33
|
+
- Strips the scaffolder + internal docs (`cli/`, `docs/superpowers/`); keeps
|
|
34
|
+
`docs/code-review/` conventions.
|
|
35
|
+
- Renames the project in `package.json`.
|
|
36
|
+
- Re-initializes git with a fresh initial commit.
|
|
37
|
+
|
|
38
|
+
Requires `git` and Node ≥ 18. **Zero runtime dependencies** (instant `npx`).
|
|
39
|
+
|
|
40
|
+
## Publishing (maintainer)
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
cd cli/create-shipkit
|
|
44
|
+
npm login
|
|
45
|
+
npm publish --access public
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The npm package name is `create-shipkit`, which is what enables
|
|
49
|
+
`npm create shipkit` / `npx create-shipkit`. Bump `version` in
|
|
50
|
+
`package.json` before each publish.
|
package/index.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// create-shipkit — scaffold the Skippie hackathon starter.
|
|
3
|
+
// Zero runtime dependencies: shallow-clones the template repo, strips the
|
|
4
|
+
// scaffolder + internal docs, renames the project, and re-inits git.
|
|
5
|
+
|
|
6
|
+
import { execFileSync } from "node:child_process";
|
|
7
|
+
import fs from "node:fs";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { stdin, stdout } from "node:process";
|
|
10
|
+
import readline from "node:readline/promises";
|
|
11
|
+
|
|
12
|
+
const REPO = "https://github.com/S-kkipie/hackaton-starter.git";
|
|
13
|
+
|
|
14
|
+
// Paths (relative to the scaffolded project) that are template-internal and
|
|
15
|
+
// must not ship in a user's project.
|
|
16
|
+
const STRIP = [".git", "cli", "docs/superpowers", ".superpowers"];
|
|
17
|
+
|
|
18
|
+
const c = {
|
|
19
|
+
reset: "\x1b[0m",
|
|
20
|
+
bold: "\x1b[1m",
|
|
21
|
+
dim: "\x1b[2m",
|
|
22
|
+
green: "\x1b[32m",
|
|
23
|
+
cyan: "\x1b[36m",
|
|
24
|
+
red: "\x1b[31m",
|
|
25
|
+
yellow: "\x1b[33m",
|
|
26
|
+
};
|
|
27
|
+
const color = (code, s) => `${code}${s}${c.reset}`;
|
|
28
|
+
|
|
29
|
+
function die(msg) {
|
|
30
|
+
console.error(`\n${color(c.red, "✖")} ${msg}\n`);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function toPackageName(name) {
|
|
35
|
+
return (
|
|
36
|
+
name
|
|
37
|
+
.trim()
|
|
38
|
+
.toLowerCase()
|
|
39
|
+
.replace(/[^a-z0-9-~]+/g, "-")
|
|
40
|
+
.replace(/^-+|-+$/g, "") || "hackaton-app"
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function resolveTargetDir() {
|
|
45
|
+
const arg = process.argv[2];
|
|
46
|
+
if (arg) return arg;
|
|
47
|
+
if (!stdin.isTTY) die("Usage: create-shipkit <project-directory>");
|
|
48
|
+
const rl = readline.createInterface({ input: stdin, output: stdout });
|
|
49
|
+
const answer = await rl.question(
|
|
50
|
+
`${color(c.cyan, "?")} Project directory: ${color(c.dim, "(my-hackaton-app)")} `,
|
|
51
|
+
);
|
|
52
|
+
rl.close();
|
|
53
|
+
return answer.trim() || "my-hackaton-app";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function assertGit() {
|
|
57
|
+
try {
|
|
58
|
+
execFileSync("git", ["--version"], { stdio: "ignore" });
|
|
59
|
+
} catch {
|
|
60
|
+
die("git is required but was not found on your PATH.");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function main() {
|
|
65
|
+
return resolveTargetDir().then((targetArg) => {
|
|
66
|
+
const targetDir = path.resolve(process.cwd(), targetArg);
|
|
67
|
+
const appName = toPackageName(path.basename(targetDir));
|
|
68
|
+
|
|
69
|
+
if (fs.existsSync(targetDir) && fs.readdirSync(targetDir).length > 0) {
|
|
70
|
+
die(
|
|
71
|
+
`Target directory "${targetArg}" already exists and is not empty.`,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
assertGit();
|
|
76
|
+
|
|
77
|
+
console.log(
|
|
78
|
+
`\n${color(c.bold, "create-shipkit")} — scaffolding ${color(c.cyan, appName)}\n`,
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// 1. Shallow clone the template.
|
|
82
|
+
console.log(`${color(c.dim, "›")} Downloading template…`);
|
|
83
|
+
try {
|
|
84
|
+
execFileSync(
|
|
85
|
+
"git",
|
|
86
|
+
["clone", "--depth", "1", "--quiet", REPO, targetDir],
|
|
87
|
+
{ stdio: "inherit" },
|
|
88
|
+
);
|
|
89
|
+
} catch {
|
|
90
|
+
die(
|
|
91
|
+
"Failed to clone the template. Check your network and that the repo is reachable.",
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 2. Strip template-internal files.
|
|
96
|
+
for (const rel of STRIP) {
|
|
97
|
+
fs.rmSync(path.join(targetDir, rel), {
|
|
98
|
+
recursive: true,
|
|
99
|
+
force: true,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// 3. Rename the project in package.json.
|
|
104
|
+
const pkgPath = path.join(targetDir, "package.json");
|
|
105
|
+
try {
|
|
106
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
107
|
+
pkg.name = appName;
|
|
108
|
+
pkg.version = "0.1.0";
|
|
109
|
+
fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 4)}\n`);
|
|
110
|
+
} catch {
|
|
111
|
+
console.warn(
|
|
112
|
+
`${color(c.yellow, "!")} Could not rewrite package.json name — do it manually.`,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 4. Fresh git history + initial commit (best-effort: a missing git
|
|
117
|
+
// identity just leaves the repo uncommitted for the user to commit).
|
|
118
|
+
try {
|
|
119
|
+
execFileSync("git", ["init", "--quiet"], {
|
|
120
|
+
cwd: targetDir,
|
|
121
|
+
stdio: "ignore",
|
|
122
|
+
});
|
|
123
|
+
execFileSync("git", ["add", "-A"], {
|
|
124
|
+
cwd: targetDir,
|
|
125
|
+
stdio: "ignore",
|
|
126
|
+
});
|
|
127
|
+
execFileSync(
|
|
128
|
+
"git",
|
|
129
|
+
[
|
|
130
|
+
"commit",
|
|
131
|
+
"--quiet",
|
|
132
|
+
"-m",
|
|
133
|
+
"Initial commit from create-shipkit",
|
|
134
|
+
],
|
|
135
|
+
{ cwd: targetDir, stdio: "ignore" },
|
|
136
|
+
);
|
|
137
|
+
} catch {
|
|
138
|
+
// non-fatal — user can commit themselves
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// 5. Next steps.
|
|
142
|
+
const rel = path.relative(process.cwd(), targetDir) || ".";
|
|
143
|
+
console.log(
|
|
144
|
+
`\n${color(c.green, "✔")} Created ${color(c.cyan, appName)} at ${rel}\n`,
|
|
145
|
+
);
|
|
146
|
+
console.log(color(c.bold, "Next steps:"));
|
|
147
|
+
console.log(` ${color(c.cyan, `cd ${rel}`)}`);
|
|
148
|
+
console.log(` ${color(c.cyan, "pnpm install")}`);
|
|
149
|
+
console.log(
|
|
150
|
+
` ${color(c.cyan, "cp .env.example .env")} ${color(c.dim, "# then set DATABASE_URL, BETTER_AUTH_SECRET, NEXT_PUBLIC_APP_URL")}`,
|
|
151
|
+
);
|
|
152
|
+
console.log(
|
|
153
|
+
` ${color(c.dim, "#")} BETTER_AUTH_SECRET: ${color(c.cyan, "openssl rand -base64 32")}`,
|
|
154
|
+
);
|
|
155
|
+
console.log(
|
|
156
|
+
` ${color(c.cyan, "pnpm db:migrate")} ${color(c.dim, "# apply migrations")}`,
|
|
157
|
+
);
|
|
158
|
+
console.log(` ${color(c.cyan, "pnpm dev")}\n`);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
main().catch((err) => die(err?.message ?? String(err)));
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-shipkit",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Scaffold a Skippie hackathon starter — Next 16 + Elysia + Better Auth + Drizzle/Postgres, with a Project CRUD reference domain.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-shipkit": "index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"index.js"
|
|
11
|
+
],
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": ">=18"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"create",
|
|
17
|
+
"starter",
|
|
18
|
+
"scaffold",
|
|
19
|
+
"nextjs",
|
|
20
|
+
"elysia",
|
|
21
|
+
"better-auth",
|
|
22
|
+
"drizzle",
|
|
23
|
+
"hackathon"
|
|
24
|
+
],
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/S-kkipie/hackaton-starter.git",
|
|
29
|
+
"directory": "cli/create-shipkit"
|
|
30
|
+
}
|
|
31
|
+
}
|