create-snagset 0.1.7 → 0.1.8

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
@@ -19,6 +19,11 @@ bun create snagset my-review # then: bun install && bun start
19
19
  ```
20
20
  </details>
21
21
 
22
+ Already in the folder you want it in — a fresh `git init`, say? Pass `.`
23
+ instead of a name (`pnpm create snagset .`) and skip the `cd`. A `.git`, a
24
+ `.DS_Store` or an editor folder is fine; anything the scaffold would
25
+ overwrite is refused.
26
+
22
27
  Bring a Postgres URL — a free Neon or Supabase branch, or a local install — and
23
28
  put it in `.env` as `DATABASE_URL`. Open `http://localhost:8080` and create the
24
29
  first owner — an instance nobody has claimed shows a setup screen rather than a
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { scaffold } from "./scaffold.js";
2
+ import { relative } from "node:path";
3
+ import { packageName, scaffold } from "./scaffold.js";
3
4
  /**
4
5
  * The bin. Argument handling and exit codes only — everything that touches the
5
6
  * filesystem is in `scaffold.ts`, which has no side effects at import time and
@@ -9,22 +10,25 @@ const target = process.argv[2];
9
10
  if (!target || target.startsWith("-")) {
10
11
  process.stderr.write("usage: create-snagset <directory>\n\n" +
11
12
  " pnpm create snagset my-review\n" +
13
+ " pnpm create snagset . (into the directory you are in)\n" +
12
14
  " cd my-review && pnpm install && pnpm start\n");
13
15
  process.exit(2);
14
16
  }
17
+ let dir;
15
18
  try {
16
- scaffold(target);
19
+ dir = scaffold(target);
17
20
  }
18
21
  catch (e) {
19
22
  process.stderr.write(`✗ ${e instanceof Error ? e.message : String(e)}\n`);
20
23
  process.exit(2);
21
24
  }
22
- const name = target.split("/").pop() || "snagset";
23
- process.stdout.write(`\n ✓ ${name}\n\n` +
25
+ // No `cd .` when it was scaffolded where we stand.
26
+ const into = relative(process.cwd(), dir);
27
+ process.stdout.write(`\n ✓ ${packageName(dir)}\n\n` +
24
28
  ` 1. Put a Postgres URL in .env as DATABASE_URL.\n` +
25
29
  ` A free Supabase or Neon project works, so does a local install;\n` +
26
30
  ` .env says which Supabase string goes where.\n\n` +
27
- ` cd ${target}\n` +
31
+ (into ? ` cd ${into}\n` : "") +
28
32
  ` pnpm install && pnpm start\n\n` +
29
33
  ` (npm, yarn and bun work the same way.)\n\n` +
30
34
  ` Then open http://localhost:8080 and create the first owner.\n` +
package/dist/scaffold.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
2
- import { dirname, join, resolve } from "node:path";
2
+ import { basename, dirname, join, resolve } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { randomBytes } from "node:crypto";
5
5
  /**
@@ -70,15 +70,35 @@ export const RENAME = {
70
70
  * A module with side effects at import time cannot be tested; the fix is not a
71
71
  * cleverer guard.
72
72
  */
73
+ /**
74
+ * What may already be in the directory. `create-snagset .` runs in a folder
75
+ * somebody made first, and by then it usually holds a `.git` from `git init`
76
+ * or a `.DS_Store` from Finder — refusing those would make `.` useless. None of
77
+ * these is a file the template writes, so nothing here can be overwritten.
78
+ * The list `create-next-app` keeps, minus the files we would write over.
79
+ */
80
+ export const HARMLESS = new Set([".git", ".DS_Store", "Thumbs.db", ".idea", ".vscode", "LICENSE"]);
81
+ /**
82
+ * The directory's own name, made a valid npm package name. `.` has to resolve
83
+ * first — `create-snagset .` once meant `"name": "."` — and a folder is often
84
+ * called something npm refuses, like `Client Review`.
85
+ */
86
+ export function packageName(dir) {
87
+ return (basename(dir)
88
+ .toLowerCase()
89
+ .replace(/[^a-z0-9._-]+/g, "-")
90
+ .replace(/^[._-]+|-+$/g, "") || "snagset");
91
+ }
73
92
  export function scaffold(target, cwd = process.cwd()) {
74
93
  const dir = resolve(cwd, target);
75
94
  // A non-empty directory is refused rather than merged into. Merging is how a
76
95
  // scaffolder overwrites somebody's `.env`, and there is no undo for that.
77
- if (existsSync(dir) && readdirSync(dir).length > 0) {
78
- throw new Error(`${target} already exists and is not empty.`);
96
+ const clutter = existsSync(dir) ? readdirSync(dir).filter((f) => !HARMLESS.has(f)) : [];
97
+ if (clutter.length > 0) {
98
+ throw new Error(`${target} already exists and is not empty (${clutter.slice(0, 3).join(", ")}${clutter.length > 3 ? ", …" : ""}).`);
79
99
  }
80
100
  mkdirSync(join(dir, "api"), { recursive: true });
81
- const name = target.split("/").pop() || "snagset";
101
+ const name = packageName(dir);
82
102
  // Pinned to THIS scaffolder's version, so `create-snagset@1.4.2` produces a
83
103
  // repo on `@snagset/server@1.4.2`. A caret here would let a fresh scaffold
84
104
  // resolve to a server this template has never been run against.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-snagset",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Scaffold your own Snagset instance. One command, Postgres included, one variable to fill in.",