create-rigg 0.0.3 → 0.0.6

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
@@ -1,8 +1,13 @@
1
1
  # rigg
2
2
 
3
+ <p>
4
+ <a href="https://npmx.dev/package/create-rigg"><img src="https://npmx.dev/api/registry/badge/version/create-rigg" alt="Version"></a>
5
+ <a href="https://www.npmjs.com/package/create-rigg"><img src="https://img.shields.io/npm/v/create-rigg.svg" alt="Version"></a>
6
+ </p>
7
+
3
8
  **The Unified Toolchain Starter for Node.js**
4
9
 
5
- Creates a new Node.js TypeScript project using the same opinionated toolchain as [Vite+](https://github.com/voidzero-dev/vite-plus), but for backend and non-web projects.
10
+ Creates a new Node.js TypeScript project using mostly the same toolchain as [Vite+](https://github.com/voidzero-dev/vite-plus), but for backend and non-web projects.
6
11
 
7
12
  ## Create a new project with rigg
8
13
 
@@ -20,17 +25,17 @@ The project follows most of the same toolchain as Vite+.
20
25
  | [Vitest](https://vitest.dev) | Testing |
21
26
  | [Oxlint](https://oxc.rs/docs/guide/usage/linter) | Linting |
22
27
  | [Oxfmt](https://oxc.rs/docs/guide/usage/formatter) | Formatting |
23
- | [tsdown](https://tsdown.dev) | Build & bundle |
24
28
  | [tsx](https://tsx.is) | Dev-mode execution |
29
+ | [tsdown](https://tsdown.dev) | Build |
25
30
 
26
- ## Backend framework
31
+ ### Backend framework
27
32
 
28
33
  You can also choose one of the following backend frameworks:
29
34
 
30
- - **None** — where you don't need a API framework, or you want to pick your own.
31
35
  - **Hono** — lightweight, modern API framework
32
36
  - **Fastify** — fast and low overhead
33
37
  - **Express** — familiar and widely supported
38
+ - **None** — where you don't need a API framework, or you want to pick your own.
34
39
 
35
40
  ## Scripts
36
41
 
@@ -38,7 +43,7 @@ Every generated project includes:
38
43
 
39
44
  ```bash
40
45
  pnpm dev # Run with tsx (no build step)
41
- pnpm build # Bundle with tsdown
46
+ pnpm build # Build with tsdown
42
47
  pnpm test # Run Vitest
43
48
  pnpm check # Lint + format check + type check
44
49
  pnpm fmt # Format
@@ -47,7 +52,7 @@ pnpm lint # Lint
47
52
  pnpm lint:fix # Lint with auto-fix
48
53
  ```
49
54
 
50
- The `pnpm` commands are just examples, you can also use `npm` or `yarn` or `bun` (depending on your package manager).
55
+ You can also use `npm` or `yarn` or `bun`, depending on your package manager.
51
56
 
52
57
  ## License
53
58
 
package/dist/index.mjs CHANGED
@@ -26,6 +26,12 @@ const FRAMEWORKS = [
26
26
  label: "Express"
27
27
  }
28
28
  ];
29
+ const FRAMEWORK_LABELS = {
30
+ none: "None",
31
+ hono: "Hono",
32
+ fastify: "Fastify",
33
+ express: "Express"
34
+ };
29
35
  const FRAMEWORK_DEPS = {
30
36
  none: {
31
37
  deps: [],
@@ -79,8 +85,15 @@ app.listen(3000, () => {
79
85
  `
80
86
  };
81
87
  //#endregion
88
+ //#region src/pkg-managers.ts
89
+ const PKG_MANAGERS = [
90
+ "npm",
91
+ "pnpm",
92
+ "yarn",
93
+ "bun"
94
+ ];
95
+ //#endregion
82
96
  //#region src/index.ts
83
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
84
97
  /** Creates a colored gradient text effect */
85
98
  function gradient(text, stops, whiteRange) {
86
99
  const chars = [...text];
@@ -97,9 +110,7 @@ function gradient(text, stops, whiteRange) {
97
110
  /** Detects the package manager used to invoke the CLI via npm_config_user_agent. */
98
111
  function detectPkgManager() {
99
112
  const ua = process.env.npm_config_user_agent ?? "";
100
- if (ua.startsWith("pnpm")) return "pnpm";
101
- if (ua.startsWith("bun")) return "bun";
102
- if (ua.startsWith("yarn")) return "yarn";
113
+ for (const pm of PKG_MANAGERS) if (ua.startsWith(pm)) return pm;
103
114
  return "npm";
104
115
  }
105
116
  /** Builds the install/add command args for the given package manager. */
@@ -119,11 +130,14 @@ function addArgs(pkgManager, packages, dev) {
119
130
  /** Runs a command synchronously, exiting the process if it fails. */
120
131
  function run(cmd, args, opts) {
121
132
  const result = spawn.sync(cmd, args, {
122
- stdio: "inherit",
133
+ stdio: "ignore",
123
134
  ...opts
124
135
  });
125
- if (result.status != null && result.status !== 0) process.exit(result.status);
126
136
  if (result.error) throw result.error;
137
+ if (result.status != null && result.status !== 0) {
138
+ p.cancel(`${cmd} ${args.join(" ")} failed with exit code ${result.status}`);
139
+ process.exit(result.status);
140
+ }
127
141
  }
128
142
  /** Recursively copies a directory, renaming _gitignore to .gitignore. */
129
143
  function copyDir(src, dest) {
@@ -141,9 +155,8 @@ function isEmpty(dir) {
141
155
  const files = fs.readdirSync(dir);
142
156
  return files.length === 0 || files.length === 1 && files[0] === ".git";
143
157
  }
144
- /** Prompts for a project name, or reads it from the first CLI argument. */
145
- async function promptProjectName(argv) {
146
- const fromArg = argv._[0] ?? "";
158
+ /** Resolves the project name from the CLI arg or prompts the user. */
159
+ async function resolveProjectName(fromArg) {
147
160
  if (fromArg) return fromArg;
148
161
  const answer = await p.text({
149
162
  message: "Project name:",
@@ -156,10 +169,9 @@ async function promptProjectName(argv) {
156
169
  }
157
170
  return answer || "rigg-project";
158
171
  }
159
- /** Prompts for a backend framework, or reads it from the --template flag. */
160
- async function promptFramework(argv) {
161
- const templateArg = argv.template;
162
- if (templateArg && FRAMEWORK_DEPS[templateArg]) return templateArg;
172
+ /** Resolves the framework from the --framework flag or prompts the user. */
173
+ async function resolveFramework(fromArg) {
174
+ if (fromArg && FRAMEWORK_DEPS[fromArg]) return fromArg;
163
175
  const answer = await p.select({
164
176
  message: "Backend framework:",
165
177
  options: FRAMEWORKS,
@@ -174,7 +186,10 @@ async function promptFramework(argv) {
174
186
  /** Asks the user to confirm before wiping a non-empty target directory. */
175
187
  async function confirmOverwrite(projectName, targetDir) {
176
188
  if (isEmpty(targetDir)) return;
177
- const overwrite = await p.confirm({ message: `${pc.yellow(projectName)} is not empty. Remove existing files and continue?` });
189
+ const overwrite = await p.confirm({
190
+ message: `${pc.white(projectName)} is not empty. Remove existing files and continue?`,
191
+ initialValue: false
192
+ });
178
193
  if (p.isCancel(overwrite) || !overwrite) {
179
194
  p.cancel("Cancelled");
180
195
  process.exit(0);
@@ -185,18 +200,29 @@ async function confirmOverwrite(projectName, targetDir) {
185
200
  });
186
201
  }
187
202
  /** Copies the base template, sets the package name, and writes the framework starter code. */
188
- function scaffoldFiles(projectName, framework, targetDir) {
189
- copyDir(path.join(__dirname, "..", "template"), targetDir);
203
+ function scaffoldFiles(options, targetDir) {
204
+ const directory = path.dirname(fileURLToPath(import.meta.url));
205
+ copyDir(path.join(directory, "..", "template"), targetDir);
190
206
  const pkgJsonPath = path.join(targetDir, "package.json");
191
207
  const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
192
- pkg.name = projectName;
208
+ pkg.name = options.projectName;
193
209
  fs.writeFileSync(pkgJsonPath, JSON.stringify(pkg, null, 2) + "\n");
194
210
  fs.mkdirSync(path.join(targetDir, "src"), { recursive: true });
195
- fs.writeFileSync(path.join(targetDir, "src", "index.ts"), FRAMEWORK_INDEX[framework]);
211
+ fs.writeFileSync(path.join(targetDir, "src", "index.ts"), FRAMEWORK_INDEX[options.framework]);
196
212
  }
197
213
  /** Installs shared dev dependencies and any framework-specific packages. */
198
- function installDependencies(pkgManager, framework, targetDir) {
199
- p.log.step(`Installing dependencies with ${pkgManager}...`);
214
+ function installDependencies(options, targetDir) {
215
+ const { pkgManager, framework, verbose } = options;
216
+ const stdio = verbose ? "inherit" : "ignore";
217
+ p.log.step(`Installing dependencies with ${gradient(pkgManager, [[
218
+ 168,
219
+ 85,
220
+ 247
221
+ ], [
222
+ 99,
223
+ 102,
224
+ 241
225
+ ]])}...`);
200
226
  run(pkgManager, addArgs(pkgManager, [
201
227
  "@types/node",
202
228
  "oxfmt",
@@ -205,38 +231,33 @@ function installDependencies(pkgManager, framework, targetDir) {
205
231
  "tsx",
206
232
  "typescript",
207
233
  "vitest"
208
- ], true), { cwd: targetDir });
209
- /** Install framework dependencies */
234
+ ], true), {
235
+ cwd: targetDir,
236
+ stdio
237
+ });
210
238
  if (framework !== "none") {
211
- p.log.step(`Installing ${framework}...`);
212
- const frameworkDeps = FRAMEWORK_DEPS[framework];
213
- if (frameworkDeps.deps.length > 0) run(pkgManager, addArgs(pkgManager, frameworkDeps.deps, false), { cwd: targetDir });
214
- if (frameworkDeps.devDeps.length > 0) run(pkgManager, addArgs(pkgManager, frameworkDeps.devDeps, true), { cwd: targetDir });
239
+ p.log.step(`Installing ${gradient(FRAMEWORK_LABELS[framework], [[
240
+ 168,
241
+ 85,
242
+ 247
243
+ ], [
244
+ 99,
245
+ 102,
246
+ 241
247
+ ]])}...`);
248
+ const { deps, devDeps } = FRAMEWORK_DEPS[framework];
249
+ if (deps.length > 0) run(pkgManager, addArgs(pkgManager, deps, false), {
250
+ cwd: targetDir,
251
+ stdio
252
+ });
253
+ if (devDeps.length > 0) run(pkgManager, addArgs(pkgManager, devDeps, true), {
254
+ cwd: targetDir,
255
+ stdio
256
+ });
215
257
  }
216
258
  }
217
- /** Prints the gradient outro with next steps. */
218
- function showOutro(projectName, framework, pkgManager) {
219
- const frameworkLabel = FRAMEWORKS.find((f) => f.value === framework)?.label ?? framework;
220
- const outroStops = [[
221
- 168,
222
- 85,
223
- 247
224
- ], [
225
- 99,
226
- 102,
227
- 241
228
- ]];
229
- const outroText = frameworkLabel !== "None" ? `Created ${projectName} with ${frameworkLabel}` : `Created ${projectName}`;
230
- const nameStart = 8;
231
- const outro = gradient(outroText, outroStops, [nameStart, nameStart + projectName.length]);
232
- const devCmd = pkgManager === "npm" ? "npm run dev" : `${pkgManager} dev`;
233
- p.outro(`${outro}\n\n ${pc.dim("Now run:")}\n cd ${projectName}\n ${devCmd}`);
234
- }
235
- async function main() {
236
- const argv = mri(process.argv.slice(2), {
237
- string: ["template"],
238
- alias: { t: "template" }
239
- });
259
+ /** Prints the gradient intro. */
260
+ function showIntro() {
240
261
  p.intro(pc.bold(gradient("rigg - The Unified Toolchain Starter for Node.js", [
241
262
  [
242
263
  255,
@@ -254,46 +275,96 @@ async function main() {
254
275
  241
255
276
  ]
256
277
  ], [0, 6])));
257
- const projectName = await promptProjectName(argv);
258
- const framework = await promptFramework(argv);
259
- const targetDir = path.resolve(process.cwd(), projectName);
260
- const pkgManager = detectPkgManager();
261
- await confirmOverwrite(projectName, targetDir);
262
- scaffoldFiles(projectName, framework, targetDir);
278
+ }
279
+ /** Prints the gradient outro with next steps. */
280
+ function showOutro(options) {
281
+ const { projectName, framework, pkgManager } = options;
282
+ const frameworkLabel = FRAMEWORK_LABELS[framework];
283
+ const title = gradient(frameworkLabel !== "None" ? `Created ${projectName} with ${frameworkLabel}` : `Created ${projectName}`, [[
284
+ 168,
285
+ 85,
286
+ 247
287
+ ], [
288
+ 99,
289
+ 102,
290
+ 241
291
+ ]], [8, 8 + projectName.length]);
292
+ const devCmd = pkgManager === "npm" ? "npm run dev" : `${pkgManager} dev`;
293
+ p.outro(`${title}\n\n ${pc.dim("Now run:")}\n cd ${projectName}\n ${devCmd}`);
294
+ }
295
+ /** Takes the CLI args and resolves all inputs into a single options object. */
296
+ async function resolveOptions(argv) {
297
+ const projectName = await resolveProjectName(argv._[0]);
298
+ await confirmOverwrite(projectName, path.resolve(process.cwd(), projectName));
299
+ return {
300
+ projectName,
301
+ framework: await resolveFramework(argv.framework),
302
+ pkgManager: argv.pm || detectPkgManager(),
303
+ verbose: argv.verbose ?? false
304
+ };
305
+ }
306
+ function initializeGit(options, targetDir) {
307
+ p.log.step("Initializing git repository");
263
308
  run("git", [
264
309
  "init",
265
310
  "-b",
266
311
  "main"
267
312
  ], {
268
313
  cwd: targetDir,
269
- stdio: "ignore"
314
+ stdio: options.verbose ? "inherit" : "ignore"
270
315
  });
271
- p.log.step("Initializing git repository");
272
- installDependencies(pkgManager, framework, targetDir);
316
+ }
317
+ function formatCode(options, targetDir) {
273
318
  p.log.step("Formatting code");
319
+ const { pkgManager, verbose } = options;
320
+ const stdio = verbose ? "inherit" : "ignore";
321
+ const execCmd = pkgManager === "bun" ? "x" : "exec";
322
+ const sep = pkgManager === "npm" || pkgManager === "yarn" ? ["--"] : [];
274
323
  run(pkgManager, [
275
- "exec",
324
+ execCmd,
276
325
  "oxlint",
277
- "--",
326
+ ...sep,
278
327
  "--init"
279
328
  ], {
280
329
  cwd: targetDir,
281
- stdio: "ignore"
330
+ stdio
282
331
  });
283
332
  run(pkgManager, [
284
- "exec",
333
+ execCmd,
285
334
  "oxfmt",
286
- "--",
335
+ ...sep,
287
336
  "--init"
288
337
  ], {
289
338
  cwd: targetDir,
290
- stdio: "ignore"
339
+ stdio
291
340
  });
292
- run(pkgManager, ["exec", "oxfmt"], {
341
+ run(pkgManager, [
342
+ execCmd,
343
+ "oxfmt",
344
+ ...sep,
345
+ "."
346
+ ], {
293
347
  cwd: targetDir,
294
- stdio: "ignore"
348
+ stdio
349
+ });
350
+ }
351
+ async function main() {
352
+ const argv = mri(process.argv.slice(2), {
353
+ string: ["framework", "pm"],
354
+ boolean: ["verbose"],
355
+ alias: {
356
+ f: "framework",
357
+ v: "verbose"
358
+ }
295
359
  });
296
- showOutro(projectName, framework, pkgManager);
360
+ showIntro();
361
+ const options = await resolveOptions(argv);
362
+ const targetDir = path.resolve(process.cwd(), options.projectName);
363
+ scaffoldFiles(options, targetDir);
364
+ initializeGit(options, targetDir);
365
+ installDependencies(options, targetDir);
366
+ formatCode(options, targetDir);
367
+ showOutro(options);
297
368
  }
298
369
  main().catch((err) => {
299
370
  console.error(err);
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "create-rigg",
3
- "version": "0.0.3",
3
+ "version": "0.0.6",
4
4
  "description": "The Unified Toolchain Starter for Node.js backend and non-web projects",
5
5
  "keywords": [
6
6
  "backend",
7
- "node",
8
- "nodejs",
9
7
  "create-rigg",
10
8
  "express",
11
9
  "fastify",
12
10
  "framework",
13
11
  "hono",
12
+ "node",
13
+ "nodejs",
14
14
  "oxfmt",
15
15
  "oxlint",
16
16
  "rigg",
@@ -42,7 +42,8 @@
42
42
  "fmt:check": "oxfmt --check",
43
43
  "check": "oxlint && oxfmt --check && tsc --noEmit",
44
44
  "dev": "tsx src/index.ts",
45
- "build": "tsdown src/index.ts"
45
+ "build": "tsdown src/index.ts",
46
+ "prepublishOnly": "git diff --exit-code && git diff --cached --exit-code && oxlint && oxfmt && tsc --noEmit && vitest run && pnpm build"
46
47
  },
47
48
  "dependencies": {
48
49
  "@clack/prompts": "^1.1.0",
@@ -0,0 +1,5 @@
1
+ import { expect, test } from "vitest";
2
+
3
+ test("example", () => {
4
+ expect(true).toBe(true);
5
+ });
@@ -0,0 +1,7 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ slowTestThreshold: 5_000,
6
+ },
7
+ });