clispark 1.1.0 → 1.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/README.md +45 -18
- package/dist/cli.js +427 -17
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,27 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
Interactive scaffolding tool for new CLI projects. Run `npx clispark` to generate a new, ready-to-run TypeScript CLI project with consistent logging, error handling, and command structure — no manual setup required.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/clispark)
|
|
6
|
+
[](https://github.com/martinwichner/clispark/actions/workflows/ci.yml)
|
|
7
|
+
[](LICENSE)
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
## Quickstart
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
| M2 | Project-scaffold engine (file generation, git init, install & build) | ✅ Done |
|
|
13
|
-
| M2.5 | Generator's own logging & error handling (dogfooding) | ✅ Done |
|
|
14
|
-
| M3 | Core runtime features in generated boilerplate (auto command registration, logging, error handling, testing, example command) | ✅ Done |
|
|
15
|
-
| M4 | Private registry support (`.npmrc` generation, wired into scaffold's own `npm install`) | ✅ Done |
|
|
16
|
-
| M5 | Documentation (`ARCHITECTURE.md`) & release automation (CI, Conventional-Commits versioning, npm publish pipeline) | ✅ Done |
|
|
17
|
-
| M6 | Update mechanism for already-generated projects | 🔜 Next |
|
|
11
|
+
```bash
|
|
12
|
+
npx clispark
|
|
13
|
+
```
|
|
18
14
|
|
|
19
|
-
|
|
15
|
+
Answer three prompts — project name, work/private profile, and (for "work") an optional private registry URL — and clispark scaffolds a new directory, running `git init`, `npm install`, and `npm run build` for you. Thirty seconds later:
|
|
20
16
|
|
|
21
17
|
```bash
|
|
22
|
-
|
|
18
|
+
cd my-cli
|
|
19
|
+
node bin/run.js hello
|
|
23
20
|
```
|
|
24
21
|
|
|
25
|
-
|
|
22
|
+
...prints a greeting from your first working command, with structured logging and clean error handling already wired up.
|
|
26
23
|
|
|
27
24
|
## What you get
|
|
28
25
|
|
|
@@ -35,19 +32,49 @@ Every generated project includes:
|
|
|
35
32
|
- **A first example command** (`hello`) as a starting point for your own commands
|
|
36
33
|
- **A clean build pipeline** (`tsup`) producing a directly runnable binary
|
|
37
34
|
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npx clispark
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The wizard asks:
|
|
42
|
+
|
|
43
|
+
1. **Project name** — checked for availability against the target npm registry as you type; a taken name prompts you to try another instead of blocking hard.
|
|
44
|
+
2. **Profile** — `work` or `private`. `work` unlocks an optional registry URL prompt.
|
|
45
|
+
3. **Registry URL** (work profile only) — leave empty for the public npm registry, or point at a private/company registry. If set, an `.npmrc` is generated so every future `npm install` in the project uses it automatically.
|
|
46
|
+
|
|
47
|
+
Scaffolding then happens automatically: files are copied, `git init` plus an initial commit run, and `npm install && npm run build` leave you with a directly runnable project.
|
|
48
|
+
|
|
49
|
+
## Updating a project
|
|
50
|
+
|
|
51
|
+
Generated projects track which files and `package.json` fields are generator-managed ("core") versus yours, in a `.clispark/manifest.json` written at scaffold time. From inside a generated project:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npx clispark update
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
pulls in the latest core improvements (base command, logger, build/test config, `ARCHITECTURE.md`, and the relevant `package.json` dependencies/scripts) from whichever clispark version `npx` resolves. It never touches anything under `src/commands/`, your `README.md`, or an `.npmrc` you added — those are yours. If you've hand-edited a core file yourself, `update` leaves it alone and reports it as skipped rather than overwriting your changes. It refuses to run against an unclean git working tree, and commits its own changes as a single, easily revertible commit.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npx clispark releasenotes
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
shows what changed between the clispark version your project last updated to and the latest one available, pulled straight from the project's GitHub releases.
|
|
64
|
+
|
|
38
65
|
## Tech stack
|
|
39
66
|
|
|
40
67
|
**Generator itself (`clispark`):** TypeScript, [commander](https://github.com/tj/commander.js) (CLI structure), [@clack/prompts](https://github.com/bombshell-dev/clack) (interactive wizard), `cross-spawn` (cross-platform shelling out to git/npm), `pino` + `env-paths` (own logging), `tsup` + `vitest`.
|
|
41
68
|
|
|
42
69
|
**Generated boilerplate:** TypeScript, [oclif](https://oclif.io/) (command framework), `pino` + `env-paths` (logging), `tsup` (build), `vitest` + `@oclif/test` (testing).
|
|
43
70
|
|
|
44
|
-
## Releases
|
|
71
|
+
## Releases & CI
|
|
45
72
|
|
|
46
|
-
Releases are automated via [release-please](https://github.com/googleapis/release-please):
|
|
73
|
+
Releases are automated via [release-please](https://github.com/googleapis/release-please): commits to `master` follow [Conventional Commits](https://www.conventionalcommits.org/), and merging the running release PR publishes to npm via [Trusted Publishing](https://docs.npmjs.com/trusted-publishers) (OIDC) — no long-lived npm token involved. `ci.yml` runs on every push/PR: unit tests, typecheck, build, a blocking `npm audit`, and an end-to-end scaffold smoke test.
|
|
47
74
|
|
|
48
|
-
|
|
75
|
+
## Development status
|
|
49
76
|
|
|
50
|
-
|
|
77
|
+
clispark is under active development — see [`CHANGELOG.md`](CHANGELOG.md) for the full release history and the [implementation plans](docs/superpowers/plans/) for the reasoning behind each milestone.
|
|
51
78
|
|
|
52
79
|
## Development notes
|
|
53
80
|
|
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { createRequire } from "module";
|
|
5
|
-
import
|
|
4
|
+
import { createRequire as createRequire2 } from "module";
|
|
5
|
+
import path5 from "path";
|
|
6
6
|
import { Command } from "commander";
|
|
7
7
|
|
|
8
8
|
// src/wizard.ts
|
|
@@ -87,9 +87,92 @@ async function runWizard(deps = defaultDeps) {
|
|
|
87
87
|
// src/scaffold.ts
|
|
88
88
|
import spawn from "cross-spawn";
|
|
89
89
|
import { fileURLToPath } from "url";
|
|
90
|
+
import path2 from "path";
|
|
91
|
+
import { cp, readdir, readFile as readFile2, rename, writeFile as writeFile2 } from "fs/promises";
|
|
92
|
+
|
|
93
|
+
// src/manifest.ts
|
|
94
|
+
import { createHash } from "crypto";
|
|
95
|
+
import { createRequire } from "module";
|
|
96
|
+
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
90
97
|
import path from "path";
|
|
91
|
-
|
|
92
|
-
|
|
98
|
+
var CORE_FILE_PATHS = [
|
|
99
|
+
"bin/run.js",
|
|
100
|
+
"src/index.ts",
|
|
101
|
+
"src/base-command.ts",
|
|
102
|
+
"src/logger.ts",
|
|
103
|
+
"tsup.config.ts",
|
|
104
|
+
"vitest.config.ts",
|
|
105
|
+
"tsconfig.json",
|
|
106
|
+
"ARCHITECTURE.md",
|
|
107
|
+
".gitignore"
|
|
108
|
+
];
|
|
109
|
+
var CORE_SCRIPT_NAMES = ["build", "postbuild", "pretest", "test", "typecheck"];
|
|
110
|
+
function templateSourcePath(relativePath) {
|
|
111
|
+
return relativePath === ".gitignore" ? "gitignore" : relativePath;
|
|
112
|
+
}
|
|
113
|
+
function hashContent(content) {
|
|
114
|
+
return createHash("sha256").update(content).digest("hex");
|
|
115
|
+
}
|
|
116
|
+
async function hashCoreFiles(dir) {
|
|
117
|
+
const hashes = {};
|
|
118
|
+
for (const relativePath of CORE_FILE_PATHS) {
|
|
119
|
+
const content = await readFile(path.join(dir, relativePath), "utf8");
|
|
120
|
+
hashes[relativePath] = hashContent(content);
|
|
121
|
+
}
|
|
122
|
+
return hashes;
|
|
123
|
+
}
|
|
124
|
+
function extractCoreFields(pkg2) {
|
|
125
|
+
const coreDependencies = { ...pkg2.dependencies ?? {}, ...pkg2.devDependencies ?? {} };
|
|
126
|
+
const coreScripts = {};
|
|
127
|
+
for (const name of CORE_SCRIPT_NAMES) {
|
|
128
|
+
const value = pkg2.scripts?.[name];
|
|
129
|
+
if (value !== void 0) coreScripts[name] = value;
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
coreDependencies,
|
|
133
|
+
coreScripts,
|
|
134
|
+
coreFields: {
|
|
135
|
+
engines: pkg2.engines ?? {},
|
|
136
|
+
oclif: pkg2.oclif ?? {}
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
async function buildManifest(targetDir, generatorVersion) {
|
|
141
|
+
const coreFiles = await hashCoreFiles(targetDir);
|
|
142
|
+
const pkg2 = JSON.parse(await readFile(path.join(targetDir, "package.json"), "utf8"));
|
|
143
|
+
const { coreDependencies, coreScripts, coreFields } = extractCoreFields(pkg2);
|
|
144
|
+
return { generatorVersion, coreFiles, coreDependencies, coreScripts, coreFields };
|
|
145
|
+
}
|
|
146
|
+
var MANIFEST_RELATIVE_PATH = path.join(".clispark", "manifest.json");
|
|
147
|
+
async function writeManifest(targetDir, manifest) {
|
|
148
|
+
const manifestPath = path.join(targetDir, MANIFEST_RELATIVE_PATH);
|
|
149
|
+
await mkdir(path.dirname(manifestPath), { recursive: true });
|
|
150
|
+
await writeFile(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
|
|
151
|
+
}
|
|
152
|
+
async function readManifest(targetDir) {
|
|
153
|
+
try {
|
|
154
|
+
const content = await readFile(path.join(targetDir, MANIFEST_RELATIVE_PATH), "utf8");
|
|
155
|
+
return JSON.parse(content);
|
|
156
|
+
} catch {
|
|
157
|
+
return void 0;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
async function requireManifest(targetDir) {
|
|
161
|
+
const manifest = await readManifest(targetDir);
|
|
162
|
+
if (!manifest) {
|
|
163
|
+
throw new Error(
|
|
164
|
+
"No .clispark/manifest.json found \u2014 this project predates update support, or is not a clispark project."
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
return manifest;
|
|
168
|
+
}
|
|
169
|
+
var require2 = createRequire(import.meta.url);
|
|
170
|
+
function getGeneratorVersion() {
|
|
171
|
+
return require2("../package.json").version;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// src/scaffold.ts
|
|
175
|
+
var TEMPLATE_DIR = path2.join(path2.dirname(fileURLToPath(import.meta.url)), "..", "templates", "base");
|
|
93
176
|
async function assertTargetDirIsUsable(targetDir) {
|
|
94
177
|
let entries;
|
|
95
178
|
try {
|
|
@@ -101,21 +184,24 @@ async function assertTargetDirIsUsable(targetDir) {
|
|
|
101
184
|
throw new Error(`Directory "${targetDir}" already exists and is not empty.`);
|
|
102
185
|
}
|
|
103
186
|
}
|
|
187
|
+
function applyPlaceholders(content, projectName) {
|
|
188
|
+
return content.replaceAll("{{projectName}}", projectName);
|
|
189
|
+
}
|
|
104
190
|
async function replacePlaceholder(filePath, projectName) {
|
|
105
|
-
const content = (await
|
|
106
|
-
await
|
|
191
|
+
const content = applyPlaceholders(await readFile2(filePath, "utf8"), projectName);
|
|
192
|
+
await writeFile2(filePath, content);
|
|
107
193
|
}
|
|
108
194
|
async function copyTemplate(options) {
|
|
109
195
|
const { projectName, targetDir, registryUrl } = options;
|
|
110
196
|
await assertTargetDirIsUsable(targetDir);
|
|
111
197
|
await cp(TEMPLATE_DIR, targetDir, { recursive: true });
|
|
112
|
-
await rename(
|
|
113
|
-
await replacePlaceholder(
|
|
114
|
-
await replacePlaceholder(
|
|
115
|
-
await replacePlaceholder(
|
|
116
|
-
await replacePlaceholder(
|
|
198
|
+
await rename(path2.join(targetDir, "gitignore"), path2.join(targetDir, ".gitignore"));
|
|
199
|
+
await replacePlaceholder(path2.join(targetDir, "package.json"), projectName);
|
|
200
|
+
await replacePlaceholder(path2.join(targetDir, "README.md"), projectName);
|
|
201
|
+
await replacePlaceholder(path2.join(targetDir, "src", "logger.ts"), projectName);
|
|
202
|
+
await replacePlaceholder(path2.join(targetDir, "ARCHITECTURE.md"), projectName);
|
|
117
203
|
if (registryUrl && registryUrl !== DEFAULT_REGISTRY_URL) {
|
|
118
|
-
await
|
|
204
|
+
await writeFile2(path2.join(targetDir, ".npmrc"), `registry=${registryUrl}
|
|
119
205
|
`);
|
|
120
206
|
}
|
|
121
207
|
}
|
|
@@ -136,6 +222,8 @@ var defaultScaffoldDeps = { runCommand: defaultRunCommand };
|
|
|
136
222
|
async function scaffoldProject(options, deps = defaultScaffoldDeps) {
|
|
137
223
|
await copyTemplate(options);
|
|
138
224
|
const { targetDir } = options;
|
|
225
|
+
const manifest = await buildManifest(targetDir, getGeneratorVersion());
|
|
226
|
+
await writeManifest(targetDir, manifest);
|
|
139
227
|
await deps.runCommand("git", ["init"], targetDir);
|
|
140
228
|
await deps.runCommand("git", ["add", "-A"], targetDir);
|
|
141
229
|
await deps.runCommand("git", ["commit", "-m", "chore: initial scaffold from clispark"], targetDir);
|
|
@@ -146,7 +234,7 @@ async function scaffoldProject(options, deps = defaultScaffoldDeps) {
|
|
|
146
234
|
// src/logger.ts
|
|
147
235
|
import { randomBytes } from "crypto";
|
|
148
236
|
import { mkdirSync } from "fs";
|
|
149
|
-
import
|
|
237
|
+
import path3 from "path";
|
|
150
238
|
import envPaths from "env-paths";
|
|
151
239
|
import pino from "pino";
|
|
152
240
|
var paths = envPaths("clispark", { suffix: "" });
|
|
@@ -157,7 +245,7 @@ function buildLogFileName(commandName) {
|
|
|
157
245
|
}
|
|
158
246
|
function createLogger(commandName, logDir = paths.log) {
|
|
159
247
|
mkdirSync(logDir, { recursive: true });
|
|
160
|
-
const logFilePath =
|
|
248
|
+
const logFilePath = path3.join(logDir, buildLogFileName(commandName));
|
|
161
249
|
const logger = pino(pino.destination({ dest: logFilePath, sync: true }));
|
|
162
250
|
return { logger, logFilePath };
|
|
163
251
|
}
|
|
@@ -189,15 +277,319 @@ function withLogging(commandName, action, logDir = paths.log) {
|
|
|
189
277
|
};
|
|
190
278
|
}
|
|
191
279
|
|
|
280
|
+
// src/update.ts
|
|
281
|
+
import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
282
|
+
import path4 from "path";
|
|
283
|
+
import spawn2 from "cross-spawn";
|
|
284
|
+
|
|
285
|
+
// src/reconcile.ts
|
|
286
|
+
function reconcileEntry(currentLiveValue, oldManifestValue, newTemplateValue, isEqual) {
|
|
287
|
+
if (currentLiveValue === void 0) {
|
|
288
|
+
return { outcome: "added", value: newTemplateValue };
|
|
289
|
+
}
|
|
290
|
+
if (oldManifestValue !== void 0 && isEqual(currentLiveValue, oldManifestValue)) {
|
|
291
|
+
return { outcome: "replaced", value: newTemplateValue };
|
|
292
|
+
}
|
|
293
|
+
return { outcome: "skipped", value: oldManifestValue ?? currentLiveValue };
|
|
294
|
+
}
|
|
295
|
+
function stringEquals(a, b) {
|
|
296
|
+
return a === b;
|
|
297
|
+
}
|
|
298
|
+
function deepEquals(a, b) {
|
|
299
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// src/releasenotes.ts
|
|
303
|
+
function stripV(version) {
|
|
304
|
+
return version.startsWith("v") ? version.slice(1) : version;
|
|
305
|
+
}
|
|
306
|
+
function compareVersions(a, b) {
|
|
307
|
+
const partsA = stripV(a).split(".").map(Number);
|
|
308
|
+
const partsB = stripV(b).split(".").map(Number);
|
|
309
|
+
const length = Math.max(partsA.length, partsB.length);
|
|
310
|
+
for (let i = 0; i < length; i++) {
|
|
311
|
+
const diff = (partsA[i] ?? 0) - (partsB[i] ?? 0);
|
|
312
|
+
if (diff !== 0) return diff;
|
|
313
|
+
}
|
|
314
|
+
return 0;
|
|
315
|
+
}
|
|
316
|
+
var RELEASES_URL = "https://api.github.com/repos/martinwichner/clispark/releases";
|
|
317
|
+
async function fetchReleaseNotes(targetDir, fetchFn = fetch) {
|
|
318
|
+
const manifest = await requireManifest(targetDir);
|
|
319
|
+
const fromVersion = manifest.generatorVersion;
|
|
320
|
+
const toVersion = getGeneratorVersion();
|
|
321
|
+
if (compareVersions(fromVersion, toVersion) >= 0) {
|
|
322
|
+
return { status: "up-to-date", fromVersion, toVersion, releases: [] };
|
|
323
|
+
}
|
|
324
|
+
const response = await fetchFn(RELEASES_URL);
|
|
325
|
+
if (!response.ok) {
|
|
326
|
+
throw new Error(`Failed to fetch release notes: GitHub API responded with ${response.status}`);
|
|
327
|
+
}
|
|
328
|
+
const allReleases = await response.json();
|
|
329
|
+
const releases = allReleases.filter((r) => compareVersions(r.tag_name, fromVersion) > 0 && compareVersions(r.tag_name, toVersion) <= 0).sort((a, b) => compareVersions(b.tag_name, a.tag_name));
|
|
330
|
+
return { status: "releases-found", fromVersion, toVersion, releases };
|
|
331
|
+
}
|
|
332
|
+
function formatReleaseNotes(result) {
|
|
333
|
+
if (result.status === "up-to-date") {
|
|
334
|
+
return `You're on the latest clispark version (v${result.toVersion}), nothing to show.`;
|
|
335
|
+
}
|
|
336
|
+
if (result.releases.length === 0) {
|
|
337
|
+
return `No published releases found between v${result.fromVersion} and v${result.toVersion}.`;
|
|
338
|
+
}
|
|
339
|
+
return result.releases.map((r) => `## ${r.tag_name}
|
|
340
|
+
|
|
341
|
+
${r.body}`).join("\n\n");
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// src/update-package-json.ts
|
|
345
|
+
function currentDependencyValue(pkg2, name) {
|
|
346
|
+
if (pkg2.dependencies?.[name] !== void 0) return { section: "dependencies", value: pkg2.dependencies[name] };
|
|
347
|
+
if (pkg2.devDependencies?.[name] !== void 0) {
|
|
348
|
+
return { section: "devDependencies", value: pkg2.devDependencies[name] };
|
|
349
|
+
}
|
|
350
|
+
return void 0;
|
|
351
|
+
}
|
|
352
|
+
function mergePackageJson(currentPkg, oldManifest, newTemplatePkg) {
|
|
353
|
+
const updatedPkg = { ...currentPkg };
|
|
354
|
+
let changed = false;
|
|
355
|
+
const dependencies = [];
|
|
356
|
+
const coreDependencies = {};
|
|
357
|
+
const dependencyNames = /* @__PURE__ */ new Set([
|
|
358
|
+
...Object.keys(newTemplatePkg.dependencies ?? {}),
|
|
359
|
+
...Object.keys(newTemplatePkg.devDependencies ?? {})
|
|
360
|
+
]);
|
|
361
|
+
for (const name of dependencyNames) {
|
|
362
|
+
const inNewDependencies = newTemplatePkg.dependencies?.[name] !== void 0;
|
|
363
|
+
const newValue = (inNewDependencies ? newTemplatePkg.dependencies : newTemplatePkg.devDependencies)[name];
|
|
364
|
+
const current = currentDependencyValue(currentPkg, name);
|
|
365
|
+
const oldValue = oldManifest.coreDependencies[name];
|
|
366
|
+
const result = reconcileEntry(current?.value, oldValue, newValue, stringEquals);
|
|
367
|
+
dependencies.push({ key: name, outcome: result.outcome });
|
|
368
|
+
coreDependencies[name] = result.value;
|
|
369
|
+
if (result.outcome !== "skipped" && result.value !== current?.value) {
|
|
370
|
+
changed = true;
|
|
371
|
+
const section = current?.section ?? (inNewDependencies ? "dependencies" : "devDependencies");
|
|
372
|
+
updatedPkg[section] = { ...updatedPkg[section], [name]: result.value };
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
const scripts = [];
|
|
376
|
+
const coreScripts = {};
|
|
377
|
+
for (const name of CORE_SCRIPT_NAMES) {
|
|
378
|
+
const newValue = newTemplatePkg.scripts?.[name];
|
|
379
|
+
if (newValue === void 0) continue;
|
|
380
|
+
const currentValue = currentPkg.scripts?.[name];
|
|
381
|
+
const oldValue = oldManifest.coreScripts[name];
|
|
382
|
+
const result = reconcileEntry(currentValue, oldValue, newValue, stringEquals);
|
|
383
|
+
scripts.push({ key: name, outcome: result.outcome });
|
|
384
|
+
coreScripts[name] = result.value;
|
|
385
|
+
if (result.outcome !== "skipped" && result.value !== currentValue) {
|
|
386
|
+
changed = true;
|
|
387
|
+
updatedPkg.scripts = { ...updatedPkg.scripts, [name]: result.value };
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
const fields = [];
|
|
391
|
+
let enginesValue = oldManifest.coreFields.engines;
|
|
392
|
+
let oclifValue = oldManifest.coreFields.oclif;
|
|
393
|
+
if (newTemplatePkg.engines !== void 0) {
|
|
394
|
+
const enginesResult = reconcileEntry(
|
|
395
|
+
currentPkg.engines,
|
|
396
|
+
oldManifest.coreFields.engines,
|
|
397
|
+
newTemplatePkg.engines,
|
|
398
|
+
deepEquals
|
|
399
|
+
);
|
|
400
|
+
fields.push({ key: "engines", outcome: enginesResult.outcome });
|
|
401
|
+
enginesValue = enginesResult.value;
|
|
402
|
+
if (enginesResult.outcome !== "skipped" && !deepEquals(enginesResult.value, currentPkg.engines)) {
|
|
403
|
+
changed = true;
|
|
404
|
+
updatedPkg.engines = enginesResult.value;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
if (newTemplatePkg.oclif !== void 0) {
|
|
408
|
+
const oclifResult = reconcileEntry(
|
|
409
|
+
currentPkg.oclif,
|
|
410
|
+
oldManifest.coreFields.oclif,
|
|
411
|
+
newTemplatePkg.oclif,
|
|
412
|
+
deepEquals
|
|
413
|
+
);
|
|
414
|
+
fields.push({ key: "oclif", outcome: oclifResult.outcome });
|
|
415
|
+
oclifValue = oclifResult.value;
|
|
416
|
+
if (oclifResult.outcome !== "skipped" && !deepEquals(oclifResult.value, currentPkg.oclif)) {
|
|
417
|
+
changed = true;
|
|
418
|
+
updatedPkg.oclif = oclifResult.value;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
return {
|
|
422
|
+
updatedPkg,
|
|
423
|
+
changed,
|
|
424
|
+
dependencies,
|
|
425
|
+
scripts,
|
|
426
|
+
fields,
|
|
427
|
+
coreDependencies,
|
|
428
|
+
coreScripts,
|
|
429
|
+
coreFields: { engines: enginesValue, oclif: oclifValue }
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// src/update.ts
|
|
434
|
+
async function defaultRunCommand2(command, args, cwd) {
|
|
435
|
+
await new Promise((resolve, reject) => {
|
|
436
|
+
const child = spawn2(command, args, { cwd, stdio: "inherit" });
|
|
437
|
+
child.on("error", reject);
|
|
438
|
+
child.on("close", (code) => {
|
|
439
|
+
if (code === 0) resolve();
|
|
440
|
+
else reject(new Error(`Command "${command} ${args.join(" ")}" exited with code ${code}`));
|
|
441
|
+
});
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
async function defaultCaptureCommand(command, args, cwd) {
|
|
445
|
+
return new Promise((resolve, reject) => {
|
|
446
|
+
const child = spawn2(command, args, { cwd });
|
|
447
|
+
let stdout = "";
|
|
448
|
+
child.stdout?.on("data", (chunk) => {
|
|
449
|
+
stdout += chunk.toString();
|
|
450
|
+
});
|
|
451
|
+
child.on("error", reject);
|
|
452
|
+
child.on("close", (code) => {
|
|
453
|
+
if (code === 0) resolve(stdout);
|
|
454
|
+
else reject(new Error(`Command "${command} ${args.join(" ")}" exited with code ${code}`));
|
|
455
|
+
});
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
var defaultUpdateDeps = { runCommand: defaultRunCommand2, captureCommand: defaultCaptureCommand };
|
|
459
|
+
async function updateProject(targetDir, deps = defaultUpdateDeps) {
|
|
460
|
+
const status = (await deps.captureCommand("git", ["status", "--porcelain"], targetDir)).trim();
|
|
461
|
+
if (status.length > 0) {
|
|
462
|
+
throw new Error("Working tree is not clean. Commit or stash your changes before running update.");
|
|
463
|
+
}
|
|
464
|
+
const oldManifest = await requireManifest(targetDir);
|
|
465
|
+
const toVersion = getGeneratorVersion();
|
|
466
|
+
const fromVersion = oldManifest.generatorVersion;
|
|
467
|
+
if (compareVersions(fromVersion, toVersion) >= 0) {
|
|
468
|
+
return { status: "up-to-date", fromVersion, toVersion, files: [], dependencies: [], scripts: [], fields: [] };
|
|
469
|
+
}
|
|
470
|
+
const currentPkg = JSON.parse(await readFile3(path4.join(targetDir, "package.json"), "utf8"));
|
|
471
|
+
const projectName = currentPkg.name;
|
|
472
|
+
const newTemplatePkg = JSON.parse(
|
|
473
|
+
applyPlaceholders(await readFile3(path4.join(TEMPLATE_DIR, "package.json"), "utf8"), projectName)
|
|
474
|
+
);
|
|
475
|
+
const files = [];
|
|
476
|
+
const newCoreFiles = {};
|
|
477
|
+
const fileWrites = [];
|
|
478
|
+
for (const relativePath of CORE_FILE_PATHS) {
|
|
479
|
+
const newContent = applyPlaceholders(
|
|
480
|
+
await readFile3(path4.join(TEMPLATE_DIR, templateSourcePath(relativePath)), "utf8"),
|
|
481
|
+
projectName
|
|
482
|
+
);
|
|
483
|
+
const newHash = hashContent(newContent);
|
|
484
|
+
let currentHash;
|
|
485
|
+
try {
|
|
486
|
+
currentHash = hashContent(await readFile3(path4.join(targetDir, relativePath), "utf8"));
|
|
487
|
+
} catch {
|
|
488
|
+
currentHash = void 0;
|
|
489
|
+
}
|
|
490
|
+
const result = reconcileEntry(currentHash, oldManifest.coreFiles[relativePath], newHash, stringEquals);
|
|
491
|
+
files.push({ path: relativePath, outcome: result.outcome });
|
|
492
|
+
newCoreFiles[relativePath] = result.value;
|
|
493
|
+
if (result.outcome === "added" || result.outcome === "replaced") {
|
|
494
|
+
fileWrites.push({ targetPath: path4.join(targetDir, relativePath), content: newContent });
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
for (const relativePath of Object.keys(oldManifest.coreFiles)) {
|
|
498
|
+
if (CORE_FILE_PATHS.includes(relativePath)) continue;
|
|
499
|
+
try {
|
|
500
|
+
await readFile3(path4.join(targetDir, relativePath), "utf8");
|
|
501
|
+
files.push({ path: relativePath, outcome: "no-longer-core" });
|
|
502
|
+
} catch {
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
const pkgMerge = mergePackageJson(currentPkg, oldManifest, newTemplatePkg);
|
|
506
|
+
const hasFileChanges = files.some(
|
|
507
|
+
(f) => f.outcome === "added" || f.outcome === "replaced" || f.outcome === "no-longer-core"
|
|
508
|
+
);
|
|
509
|
+
const hasChanges = hasFileChanges || pkgMerge.changed;
|
|
510
|
+
if (!hasChanges) {
|
|
511
|
+
return {
|
|
512
|
+
status: "no-changes",
|
|
513
|
+
fromVersion,
|
|
514
|
+
toVersion,
|
|
515
|
+
files,
|
|
516
|
+
dependencies: pkgMerge.dependencies,
|
|
517
|
+
scripts: pkgMerge.scripts,
|
|
518
|
+
fields: pkgMerge.fields
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
for (const write of fileWrites) {
|
|
522
|
+
await mkdir2(path4.dirname(write.targetPath), { recursive: true });
|
|
523
|
+
await writeFile3(write.targetPath, write.content);
|
|
524
|
+
}
|
|
525
|
+
if (pkgMerge.changed) {
|
|
526
|
+
await writeFile3(path4.join(targetDir, "package.json"), JSON.stringify(pkgMerge.updatedPkg, null, 2) + "\n");
|
|
527
|
+
}
|
|
528
|
+
const newManifest = {
|
|
529
|
+
generatorVersion: toVersion,
|
|
530
|
+
coreFiles: newCoreFiles,
|
|
531
|
+
coreDependencies: pkgMerge.coreDependencies,
|
|
532
|
+
coreScripts: pkgMerge.coreScripts,
|
|
533
|
+
coreFields: pkgMerge.coreFields
|
|
534
|
+
};
|
|
535
|
+
await writeManifest(targetDir, newManifest);
|
|
536
|
+
await deps.runCommand("git", ["add", "-A"], targetDir);
|
|
537
|
+
await deps.runCommand("git", ["commit", "-m", `chore: update clispark core to v${toVersion}`], targetDir);
|
|
538
|
+
return {
|
|
539
|
+
status: "updated",
|
|
540
|
+
fromVersion,
|
|
541
|
+
toVersion,
|
|
542
|
+
files,
|
|
543
|
+
dependencies: pkgMerge.dependencies,
|
|
544
|
+
scripts: pkgMerge.scripts,
|
|
545
|
+
fields: pkgMerge.fields
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
function formatUpdateSummary(result) {
|
|
549
|
+
if (result.status === "up-to-date") {
|
|
550
|
+
return `Already up to date (v${result.toVersion}).`;
|
|
551
|
+
}
|
|
552
|
+
const added = result.files.filter((f) => f.outcome === "added");
|
|
553
|
+
const replaced = result.files.filter((f) => f.outcome === "replaced");
|
|
554
|
+
const skipped = result.files.filter((f) => f.outcome === "skipped");
|
|
555
|
+
const noLongerCore = result.files.filter((f) => f.outcome === "no-longer-core");
|
|
556
|
+
const lines = [];
|
|
557
|
+
if (result.status === "no-changes") {
|
|
558
|
+
lines.push(
|
|
559
|
+
noLongerCore.length ? `No changes applied: nothing to update since v${result.fromVersion}.` : `No changes applied: every core file/field has been modified locally since v${result.fromVersion}.`
|
|
560
|
+
);
|
|
561
|
+
} else {
|
|
562
|
+
lines.push(`Updated core from v${result.fromVersion} to v${result.toVersion}.`);
|
|
563
|
+
}
|
|
564
|
+
if (added.length) lines.push(` New: ${added.map((f) => f.path).join(", ")}`);
|
|
565
|
+
if (replaced.length) lines.push(` Updated: ${replaced.map((f) => f.path).join(", ")}`);
|
|
566
|
+
if (skipped.length) lines.push(` Skipped (locally modified): ${skipped.map((f) => f.path).join(", ")}`);
|
|
567
|
+
if (noLongerCore.length) {
|
|
568
|
+
lines.push(
|
|
569
|
+
` No longer part of the core, safe to remove manually: ${noLongerCore.map((f) => f.path).join(", ")}`
|
|
570
|
+
);
|
|
571
|
+
}
|
|
572
|
+
const fieldOutcomes = [...result.dependencies, ...result.scripts, ...result.fields].filter(
|
|
573
|
+
(o) => o.outcome !== "skipped"
|
|
574
|
+
);
|
|
575
|
+
if (fieldOutcomes.length) {
|
|
576
|
+
lines.push(` package.json: ${fieldOutcomes.map((o) => `${o.key} (${o.outcome})`).join(", ")}`);
|
|
577
|
+
}
|
|
578
|
+
if (result.status === "updated") {
|
|
579
|
+
lines.push('Run "clispark releasenotes" to see what changed.');
|
|
580
|
+
}
|
|
581
|
+
return lines.join("\n");
|
|
582
|
+
}
|
|
583
|
+
|
|
192
584
|
// src/cli.ts
|
|
193
|
-
var
|
|
194
|
-
var pkg =
|
|
585
|
+
var require3 = createRequire2(import.meta.url);
|
|
586
|
+
var pkg = require3("../package.json");
|
|
195
587
|
var program = new Command();
|
|
196
588
|
program.name("clispark").description("Interactive scaffolding tool for new CLI projects").version(pkg.version);
|
|
197
589
|
program.action(
|
|
198
590
|
withLogging("scaffold", async (logger) => {
|
|
199
591
|
const answers = await runWizard();
|
|
200
|
-
const targetDir =
|
|
592
|
+
const targetDir = path5.join(process.cwd(), answers.projectName);
|
|
201
593
|
logger.info({ projectName: answers.projectName, targetDir }, "scaffold started");
|
|
202
594
|
await scaffoldProject({ projectName: answers.projectName, targetDir, registryUrl: answers.registryUrl });
|
|
203
595
|
logger.info({ projectName: answers.projectName }, "scaffold completed");
|
|
@@ -205,6 +597,24 @@ program.action(
|
|
|
205
597
|
Done! Your new CLI project is ready at ${targetDir}`);
|
|
206
598
|
})
|
|
207
599
|
);
|
|
600
|
+
program.command("update").description("Update generator-managed core files and dependencies to the latest clispark version").action(
|
|
601
|
+
withLogging("update", async (logger) => {
|
|
602
|
+
const targetDir = process.cwd();
|
|
603
|
+
logger.info({ targetDir }, "update started");
|
|
604
|
+
const result = await updateProject(targetDir);
|
|
605
|
+
logger.info({ status: result.status }, "update completed");
|
|
606
|
+
console.log(formatUpdateSummary(result));
|
|
607
|
+
})
|
|
608
|
+
);
|
|
609
|
+
program.command("releasenotes").description("Show what changed between this project's generator version and the latest clispark version").action(
|
|
610
|
+
withLogging("releasenotes", async (logger) => {
|
|
611
|
+
const targetDir = process.cwd();
|
|
612
|
+
logger.info({ targetDir }, "releasenotes started");
|
|
613
|
+
const result = await fetchReleaseNotes(targetDir);
|
|
614
|
+
logger.info({ status: result.status }, "releasenotes completed");
|
|
615
|
+
console.log(formatReleaseNotes(result));
|
|
616
|
+
})
|
|
617
|
+
);
|
|
208
618
|
program.parseAsync(process.argv).catch((error) => {
|
|
209
619
|
console.error(error);
|
|
210
620
|
process.exit(1);
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/wizard.ts","../src/registry.ts","../src/scaffold.ts","../src/logger.ts"],"sourcesContent":["// src/cli.ts\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { Command } from 'commander';\nimport { runWizard } from './wizard.js';\nimport { scaffoldProject } from './scaffold.js';\nimport { withLogging } from './logger.js';\n\nconst require = createRequire(import.meta.url);\nconst pkg = require('../package.json') as { version: string };\n\nconst program = new Command();\n\nprogram\n .name('clispark')\n .description('Interactive scaffolding tool for new CLI projects')\n .version(pkg.version);\n\nprogram.action(\n withLogging('scaffold', async (logger) => {\n const answers = await runWizard();\n const targetDir = path.join(process.cwd(), answers.projectName);\n\n logger.info({ projectName: answers.projectName, targetDir }, 'scaffold started');\n await scaffoldProject({ projectName: answers.projectName, targetDir, registryUrl: answers.registryUrl });\n logger.info({ projectName: answers.projectName }, 'scaffold completed');\n\n console.log(`\\nDone! Your new CLI project is ready at ${targetDir}`);\n }),\n);\n\nprogram.parseAsync(process.argv).catch((error: unknown) => {\n console.error(error);\n process.exit(1);\n});\n","// src/wizard.ts\nimport { intro, outro, text, select, log, isCancel, cancel } from '@clack/prompts';\nimport { checkPackageNameAvailability, DEFAULT_REGISTRY_URL } from './registry.js';\nimport type { Profile, WizardAnswers } from './types.js';\n\nexport interface WizardDeps {\n checkAvailability: typeof checkPackageNameAvailability;\n}\n\nconst defaultDeps: WizardDeps = {\n checkAvailability: checkPackageNameAvailability,\n};\n\nfunction exitIfCancelled(value: unknown): void {\n if (isCancel(value)) {\n cancel('Operation cancelled.');\n process.exit(1);\n }\n}\n\nfunction validateProjectName(value: string): string | undefined {\n if (!value || value.trim().length === 0) return 'Project name is required.';\n if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(value)) {\n return 'Use lowercase letters and numbers, with single hyphens between words (no leading, trailing, or repeated hyphens).';\n }\n return undefined;\n}\n\nexport async function runWizard(deps: WizardDeps = defaultDeps): Promise<WizardAnswers> {\n intro('clispark — scaffold a new CLI project');\n\n const nameValue = await text({\n message: 'Project name',\n validate: validateProjectName,\n });\n exitIfCancelled(nameValue);\n let projectName = nameValue as string;\n\n const profileValue = await select({\n message: 'Is this a work or private project?',\n options: [\n { value: 'private', label: 'Private' },\n { value: 'work', label: 'Work' },\n ],\n });\n exitIfCancelled(profileValue);\n const profile = profileValue as Profile;\n\n let registryUrl = DEFAULT_REGISTRY_URL;\n if (profile === 'work') {\n const registryValue = await text({\n message: 'Custom npm registry URL (leave empty for npmjs.org)',\n placeholder: DEFAULT_REGISTRY_URL,\n defaultValue: DEFAULT_REGISTRY_URL,\n });\n exitIfCancelled(registryValue);\n registryUrl = (registryValue as string) || DEFAULT_REGISTRY_URL;\n }\n\n let nameAvailability = await deps.checkAvailability(projectName, registryUrl);\n\n while (nameAvailability === 'taken') {\n log.warn(`\"${projectName}\" is already taken on ${registryUrl}. Please choose a different name.`);\n\n const retryValue = await text({\n message: 'Project name',\n validate: validateProjectName,\n });\n exitIfCancelled(retryValue);\n projectName = retryValue as string;\n\n nameAvailability = await deps.checkAvailability(projectName, registryUrl);\n }\n\n if (nameAvailability === 'unverified') {\n log.warn(`Could not verify availability of \"${projectName}\" on ${registryUrl}. Continuing anyway.`);\n }\n\n outro(`Ready to scaffold \"${projectName}\".`);\n\n return { projectName, profile, registryUrl, nameAvailability };\n}\n","export const DEFAULT_REGISTRY_URL = 'https://registry.npmjs.org';\n\nexport type NameCheckResult = 'available' | 'taken' | 'unverified';\n\nexport async function checkPackageNameAvailability(\n name: string,\n registryUrl: string = DEFAULT_REGISTRY_URL,\n): Promise<NameCheckResult> {\n const url = `${registryUrl.replace(/\\/$/, '')}/${encodeURIComponent(name)}`;\n\n try {\n const response = await fetch(url);\n if (response.status === 404) return 'available';\n if (response.status === 200) return 'taken';\n return 'unverified';\n } catch {\n return 'unverified';\n }\n}\n","import spawn from 'cross-spawn';\nimport { fileURLToPath } from 'node:url';\nimport path from 'node:path';\nimport { cp, readdir, readFile, rename, writeFile } from 'node:fs/promises';\nimport { DEFAULT_REGISTRY_URL } from './registry.js';\n\nexport interface ScaffoldOptions {\n projectName: string;\n targetDir: string;\n registryUrl?: string;\n}\n\nconst TEMPLATE_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'templates', 'base');\n\nasync function assertTargetDirIsUsable(targetDir: string): Promise<void> {\n let entries: string[];\n try {\n entries = await readdir(targetDir);\n } catch {\n return;\n }\n if (entries.length > 0) {\n throw new Error(`Directory \"${targetDir}\" already exists and is not empty.`);\n }\n}\n\nasync function replacePlaceholder(filePath: string, projectName: string): Promise<void> {\n const content = (await readFile(filePath, 'utf8')).replaceAll('{{projectName}}', projectName);\n await writeFile(filePath, content);\n}\n\nexport async function copyTemplate(options: ScaffoldOptions): Promise<void> {\n const { projectName, targetDir, registryUrl } = options;\n\n await assertTargetDirIsUsable(targetDir);\n await cp(TEMPLATE_DIR, targetDir, { recursive: true });\n\n await rename(path.join(targetDir, 'gitignore'), path.join(targetDir, '.gitignore'));\n\n await replacePlaceholder(path.join(targetDir, 'package.json'), projectName);\n await replacePlaceholder(path.join(targetDir, 'README.md'), projectName);\n await replacePlaceholder(path.join(targetDir, 'src', 'logger.ts'), projectName);\n await replacePlaceholder(path.join(targetDir, 'ARCHITECTURE.md'), projectName);\n\n if (registryUrl && registryUrl !== DEFAULT_REGISTRY_URL) {\n await writeFile(path.join(targetDir, '.npmrc'), `registry=${registryUrl}\\n`);\n }\n}\n\nexport interface ScaffoldDeps {\n runCommand: (command: string, args: string[], cwd: string) => Promise<void>;\n}\n\nasync function defaultRunCommand(command: string, args: string[], cwd: string): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const child = spawn(command, args, { cwd, stdio: 'inherit' });\n child.on('error', reject);\n child.on('close', (code) => {\n if (code === 0) {\n resolve();\n } else {\n reject(new Error(`Command \"${command} ${args.join(' ')}\" exited with code ${code}`));\n }\n });\n });\n}\n\nconst defaultScaffoldDeps: ScaffoldDeps = { runCommand: defaultRunCommand };\n\nexport async function scaffoldProject(\n options: ScaffoldOptions,\n deps: ScaffoldDeps = defaultScaffoldDeps,\n): Promise<void> {\n await copyTemplate(options);\n\n const { targetDir } = options;\n await deps.runCommand('git', ['init'], targetDir);\n await deps.runCommand('git', ['add', '-A'], targetDir);\n await deps.runCommand('git', ['commit', '-m', 'chore: initial scaffold from clispark'], targetDir);\n await deps.runCommand('npm', ['install'], targetDir);\n await deps.runCommand('npm', ['run', 'build'], targetDir);\n}\n","import { randomBytes } from 'node:crypto';\nimport { mkdirSync } from 'node:fs';\nimport path from 'node:path';\nimport envPaths from 'env-paths';\nimport pino, { type Logger } from 'pino';\n\nconst paths = envPaths('clispark', { suffix: '' });\n\nexport interface LoggerHandle {\n logger: Logger;\n logFilePath: string;\n}\n\nfunction buildLogFileName(commandName: string): string {\n const timestamp = new Date().toISOString().replaceAll(/[:.]/g, '-');\n const suffix = randomBytes(3).toString('hex');\n return `${commandName}-${timestamp}-${suffix}.log`;\n}\n\nexport function createLogger(commandName: string, logDir: string = paths.log): LoggerHandle {\n mkdirSync(logDir, { recursive: true });\n\n const logFilePath = path.join(logDir, buildLogFileName(commandName));\n const logger = pino(pino.destination({ dest: logFilePath, sync: true }));\n\n return { logger, logFilePath };\n}\n\nexport function withLogging(\n commandName: string,\n action: (logger: Logger) => Promise<void>,\n logDir: string = paths.log,\n): () => Promise<void> {\n return async () => {\n let handle: LoggerHandle;\n try {\n handle = createLogger(commandName, logDir);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`\\n✖ ${message}`);\n process.exit(1);\n return;\n }\n\n const { logger, logFilePath } = handle;\n logger.info({ command: commandName }, 'started');\n\n try {\n await action(logger);\n logger.info({ command: commandName }, 'completed');\n } catch (error) {\n logger.error({ command: commandName, err: error }, 'failed');\n const message = error instanceof Error ? error.message : String(error);\n console.error(`\\n✖ ${message}`);\n console.error(`Details: ${logFilePath}`);\n process.exit(1);\n }\n };\n}\n"],"mappings":"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","names":["path","path","require","path"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/wizard.ts","../src/registry.ts","../src/scaffold.ts","../src/manifest.ts","../src/logger.ts","../src/update.ts","../src/reconcile.ts","../src/releasenotes.ts","../src/update-package-json.ts"],"sourcesContent":["// src/cli.ts\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { Command } from 'commander';\nimport { runWizard } from './wizard.js';\nimport { scaffoldProject } from './scaffold.js';\nimport { withLogging } from './logger.js';\nimport { formatUpdateSummary, updateProject } from './update.js';\nimport { fetchReleaseNotes, formatReleaseNotes } from './releasenotes.js';\n\nconst require = createRequire(import.meta.url);\nconst pkg = require('../package.json') as { version: string };\n\nconst program = new Command();\n\nprogram\n .name('clispark')\n .description('Interactive scaffolding tool for new CLI projects')\n .version(pkg.version);\n\nprogram.action(\n withLogging('scaffold', async (logger) => {\n const answers = await runWizard();\n const targetDir = path.join(process.cwd(), answers.projectName);\n\n logger.info({ projectName: answers.projectName, targetDir }, 'scaffold started');\n await scaffoldProject({ projectName: answers.projectName, targetDir, registryUrl: answers.registryUrl });\n logger.info({ projectName: answers.projectName }, 'scaffold completed');\n\n console.log(`\\nDone! Your new CLI project is ready at ${targetDir}`);\n }),\n);\n\nprogram\n .command('update')\n .description('Update generator-managed core files and dependencies to the latest clispark version')\n .action(\n withLogging('update', async (logger) => {\n const targetDir = process.cwd();\n logger.info({ targetDir }, 'update started');\n const result = await updateProject(targetDir);\n logger.info({ status: result.status }, 'update completed');\n console.log(formatUpdateSummary(result));\n }),\n );\n\nprogram\n .command('releasenotes')\n .description(\"Show what changed between this project's generator version and the latest clispark version\")\n .action(\n withLogging('releasenotes', async (logger) => {\n const targetDir = process.cwd();\n logger.info({ targetDir }, 'releasenotes started');\n const result = await fetchReleaseNotes(targetDir);\n logger.info({ status: result.status }, 'releasenotes completed');\n console.log(formatReleaseNotes(result));\n }),\n );\n\nprogram.parseAsync(process.argv).catch((error: unknown) => {\n console.error(error);\n process.exit(1);\n});\n","// src/wizard.ts\nimport { intro, outro, text, select, log, isCancel, cancel } from '@clack/prompts';\nimport { checkPackageNameAvailability, DEFAULT_REGISTRY_URL } from './registry.js';\nimport type { Profile, WizardAnswers } from './types.js';\n\nexport interface WizardDeps {\n checkAvailability: typeof checkPackageNameAvailability;\n}\n\nconst defaultDeps: WizardDeps = {\n checkAvailability: checkPackageNameAvailability,\n};\n\nfunction exitIfCancelled(value: unknown): void {\n if (isCancel(value)) {\n cancel('Operation cancelled.');\n process.exit(1);\n }\n}\n\nfunction validateProjectName(value: string): string | undefined {\n if (!value || value.trim().length === 0) return 'Project name is required.';\n if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(value)) {\n return 'Use lowercase letters and numbers, with single hyphens between words (no leading, trailing, or repeated hyphens).';\n }\n return undefined;\n}\n\nexport async function runWizard(deps: WizardDeps = defaultDeps): Promise<WizardAnswers> {\n intro('clispark — scaffold a new CLI project');\n\n const nameValue = await text({\n message: 'Project name',\n validate: validateProjectName,\n });\n exitIfCancelled(nameValue);\n let projectName = nameValue as string;\n\n const profileValue = await select({\n message: 'Is this a work or private project?',\n options: [\n { value: 'private', label: 'Private' },\n { value: 'work', label: 'Work' },\n ],\n });\n exitIfCancelled(profileValue);\n const profile = profileValue as Profile;\n\n let registryUrl = DEFAULT_REGISTRY_URL;\n if (profile === 'work') {\n const registryValue = await text({\n message: 'Custom npm registry URL (leave empty for npmjs.org)',\n placeholder: DEFAULT_REGISTRY_URL,\n defaultValue: DEFAULT_REGISTRY_URL,\n });\n exitIfCancelled(registryValue);\n registryUrl = (registryValue as string) || DEFAULT_REGISTRY_URL;\n }\n\n let nameAvailability = await deps.checkAvailability(projectName, registryUrl);\n\n while (nameAvailability === 'taken') {\n log.warn(`\"${projectName}\" is already taken on ${registryUrl}. Please choose a different name.`);\n\n const retryValue = await text({\n message: 'Project name',\n validate: validateProjectName,\n });\n exitIfCancelled(retryValue);\n projectName = retryValue as string;\n\n nameAvailability = await deps.checkAvailability(projectName, registryUrl);\n }\n\n if (nameAvailability === 'unverified') {\n log.warn(`Could not verify availability of \"${projectName}\" on ${registryUrl}. Continuing anyway.`);\n }\n\n outro(`Ready to scaffold \"${projectName}\".`);\n\n return { projectName, profile, registryUrl, nameAvailability };\n}\n","export const DEFAULT_REGISTRY_URL = 'https://registry.npmjs.org';\n\nexport type NameCheckResult = 'available' | 'taken' | 'unverified';\n\nexport async function checkPackageNameAvailability(\n name: string,\n registryUrl: string = DEFAULT_REGISTRY_URL,\n): Promise<NameCheckResult> {\n const url = `${registryUrl.replace(/\\/$/, '')}/${encodeURIComponent(name)}`;\n\n try {\n const response = await fetch(url);\n if (response.status === 404) return 'available';\n if (response.status === 200) return 'taken';\n return 'unverified';\n } catch {\n return 'unverified';\n }\n}\n","import spawn from 'cross-spawn';\nimport { fileURLToPath } from 'node:url';\nimport path from 'node:path';\nimport { cp, readdir, readFile, rename, writeFile } from 'node:fs/promises';\nimport { DEFAULT_REGISTRY_URL } from './registry.js';\nimport { buildManifest, getGeneratorVersion, writeManifest } from './manifest.js';\n\nexport interface ScaffoldOptions {\n projectName: string;\n targetDir: string;\n registryUrl?: string;\n}\n\nexport const TEMPLATE_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'templates', 'base');\n\nasync function assertTargetDirIsUsable(targetDir: string): Promise<void> {\n let entries: string[];\n try {\n entries = await readdir(targetDir);\n } catch {\n return;\n }\n if (entries.length > 0) {\n throw new Error(`Directory \"${targetDir}\" already exists and is not empty.`);\n }\n}\n\nexport function applyPlaceholders(content: string, projectName: string): string {\n return content.replaceAll('{{projectName}}', projectName);\n}\n\nasync function replacePlaceholder(filePath: string, projectName: string): Promise<void> {\n const content = applyPlaceholders(await readFile(filePath, 'utf8'), projectName);\n await writeFile(filePath, content);\n}\n\nexport async function copyTemplate(options: ScaffoldOptions): Promise<void> {\n const { projectName, targetDir, registryUrl } = options;\n\n await assertTargetDirIsUsable(targetDir);\n await cp(TEMPLATE_DIR, targetDir, { recursive: true });\n\n await rename(path.join(targetDir, 'gitignore'), path.join(targetDir, '.gitignore'));\n\n await replacePlaceholder(path.join(targetDir, 'package.json'), projectName);\n await replacePlaceholder(path.join(targetDir, 'README.md'), projectName);\n await replacePlaceholder(path.join(targetDir, 'src', 'logger.ts'), projectName);\n await replacePlaceholder(path.join(targetDir, 'ARCHITECTURE.md'), projectName);\n\n if (registryUrl && registryUrl !== DEFAULT_REGISTRY_URL) {\n await writeFile(path.join(targetDir, '.npmrc'), `registry=${registryUrl}\\n`);\n }\n}\n\nexport interface ScaffoldDeps {\n runCommand: (command: string, args: string[], cwd: string) => Promise<void>;\n}\n\nasync function defaultRunCommand(command: string, args: string[], cwd: string): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const child = spawn(command, args, { cwd, stdio: 'inherit' });\n child.on('error', reject);\n child.on('close', (code) => {\n if (code === 0) {\n resolve();\n } else {\n reject(new Error(`Command \"${command} ${args.join(' ')}\" exited with code ${code}`));\n }\n });\n });\n}\n\nconst defaultScaffoldDeps: ScaffoldDeps = { runCommand: defaultRunCommand };\n\nexport async function scaffoldProject(\n options: ScaffoldOptions,\n deps: ScaffoldDeps = defaultScaffoldDeps,\n): Promise<void> {\n await copyTemplate(options);\n\n const { targetDir } = options;\n const manifest = await buildManifest(targetDir, getGeneratorVersion());\n await writeManifest(targetDir, manifest);\n\n await deps.runCommand('git', ['init'], targetDir);\n await deps.runCommand('git', ['add', '-A'], targetDir);\n await deps.runCommand('git', ['commit', '-m', 'chore: initial scaffold from clispark'], targetDir);\n await deps.runCommand('npm', ['install'], targetDir);\n await deps.runCommand('npm', ['run', 'build'], targetDir);\n}\n","// src/manifest.ts\nimport { createHash } from 'node:crypto';\nimport { createRequire } from 'node:module';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\n\nexport const CORE_FILE_PATHS = [\n 'bin/run.js',\n 'src/index.ts',\n 'src/base-command.ts',\n 'src/logger.ts',\n 'tsup.config.ts',\n 'vitest.config.ts',\n 'tsconfig.json',\n 'ARCHITECTURE.md',\n '.gitignore',\n] as const;\n\nexport const CORE_SCRIPT_NAMES = ['build', 'postbuild', 'pretest', 'test', 'typecheck'] as const;\n\n/** The base template stores .gitignore as \"gitignore\" (renamed on copy); every other core path is identical in the template and in a generated project. */\nexport function templateSourcePath(relativePath: string): string {\n return relativePath === '.gitignore' ? 'gitignore' : relativePath;\n}\n\nexport interface Manifest {\n generatorVersion: string;\n coreFiles: Record<string, string>;\n coreDependencies: Record<string, string>;\n coreScripts: Record<string, string>;\n coreFields: {\n engines: Record<string, string>;\n oclif: Record<string, unknown>;\n };\n}\n\nexport interface PackageJsonCore {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n scripts?: Record<string, string>;\n engines?: Record<string, string>;\n oclif?: Record<string, unknown>;\n}\n\nexport function hashContent(content: string): string {\n return createHash('sha256').update(content).digest('hex');\n}\n\nexport async function hashCoreFiles(dir: string): Promise<Record<string, string>> {\n const hashes: Record<string, string> = {};\n for (const relativePath of CORE_FILE_PATHS) {\n const content = await readFile(path.join(dir, relativePath), 'utf8');\n hashes[relativePath] = hashContent(content);\n }\n return hashes;\n}\n\nexport function extractCoreFields(\n pkg: PackageJsonCore,\n): Pick<Manifest, 'coreDependencies' | 'coreScripts' | 'coreFields'> {\n const coreDependencies = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };\n\n const coreScripts: Record<string, string> = {};\n for (const name of CORE_SCRIPT_NAMES) {\n const value = pkg.scripts?.[name];\n if (value !== undefined) coreScripts[name] = value;\n }\n\n return {\n coreDependencies,\n coreScripts,\n coreFields: {\n engines: pkg.engines ?? {},\n oclif: pkg.oclif ?? {},\n },\n };\n}\n\nexport async function buildManifest(targetDir: string, generatorVersion: string): Promise<Manifest> {\n const coreFiles = await hashCoreFiles(targetDir);\n const pkg = JSON.parse(await readFile(path.join(targetDir, 'package.json'), 'utf8')) as PackageJsonCore;\n const { coreDependencies, coreScripts, coreFields } = extractCoreFields(pkg);\n return { generatorVersion, coreFiles, coreDependencies, coreScripts, coreFields };\n}\n\nexport const MANIFEST_RELATIVE_PATH = path.join('.clispark', 'manifest.json');\n\nexport async function writeManifest(targetDir: string, manifest: Manifest): Promise<void> {\n const manifestPath = path.join(targetDir, MANIFEST_RELATIVE_PATH);\n await mkdir(path.dirname(manifestPath), { recursive: true });\n await writeFile(manifestPath, JSON.stringify(manifest, null, 2) + '\\n');\n}\n\nexport async function readManifest(targetDir: string): Promise<Manifest | undefined> {\n try {\n const content = await readFile(path.join(targetDir, MANIFEST_RELATIVE_PATH), 'utf8');\n return JSON.parse(content) as Manifest;\n } catch {\n return undefined;\n }\n}\n\nexport async function requireManifest(targetDir: string): Promise<Manifest> {\n const manifest = await readManifest(targetDir);\n if (!manifest) {\n throw new Error(\n 'No .clispark/manifest.json found — this project predates update support, or is not a clispark project.',\n );\n }\n return manifest;\n}\n\nconst require = createRequire(import.meta.url);\n\nexport function getGeneratorVersion(): string {\n return (require('../package.json') as { version: string }).version;\n}\n","import { randomBytes } from 'node:crypto';\nimport { mkdirSync } from 'node:fs';\nimport path from 'node:path';\nimport envPaths from 'env-paths';\nimport pino, { type Logger } from 'pino';\n\nconst paths = envPaths('clispark', { suffix: '' });\n\nexport interface LoggerHandle {\n logger: Logger;\n logFilePath: string;\n}\n\nfunction buildLogFileName(commandName: string): string {\n const timestamp = new Date().toISOString().replaceAll(/[:.]/g, '-');\n const suffix = randomBytes(3).toString('hex');\n return `${commandName}-${timestamp}-${suffix}.log`;\n}\n\nexport function createLogger(commandName: string, logDir: string = paths.log): LoggerHandle {\n mkdirSync(logDir, { recursive: true });\n\n const logFilePath = path.join(logDir, buildLogFileName(commandName));\n const logger = pino(pino.destination({ dest: logFilePath, sync: true }));\n\n return { logger, logFilePath };\n}\n\nexport function withLogging(\n commandName: string,\n action: (logger: Logger) => Promise<void>,\n logDir: string = paths.log,\n): () => Promise<void> {\n return async () => {\n let handle: LoggerHandle;\n try {\n handle = createLogger(commandName, logDir);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`\\n✖ ${message}`);\n process.exit(1);\n return;\n }\n\n const { logger, logFilePath } = handle;\n logger.info({ command: commandName }, 'started');\n\n try {\n await action(logger);\n logger.info({ command: commandName }, 'completed');\n } catch (error) {\n logger.error({ command: commandName, err: error }, 'failed');\n const message = error instanceof Error ? error.message : String(error);\n console.error(`\\n✖ ${message}`);\n console.error(`Details: ${logFilePath}`);\n process.exit(1);\n }\n };\n}\n","// src/update.ts\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport spawn from 'cross-spawn';\nimport { applyPlaceholders, TEMPLATE_DIR } from './scaffold.js';\nimport {\n CORE_FILE_PATHS,\n getGeneratorVersion,\n hashContent,\n requireManifest,\n templateSourcePath,\n writeManifest,\n type Manifest,\n} from './manifest.js';\nimport { reconcileEntry, stringEquals } from './reconcile.js';\nimport { compareVersions } from './releasenotes.js';\nimport { mergePackageJson, type FieldOutcome, type PackageJsonShape } from './update-package-json.js';\n\nexport interface UpdateDeps {\n runCommand: (command: string, args: string[], cwd: string) => Promise<void>;\n captureCommand: (command: string, args: string[], cwd: string) => Promise<string>;\n}\n\nasync function defaultRunCommand(command: string, args: string[], cwd: string): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const child = spawn(command, args, { cwd, stdio: 'inherit' });\n child.on('error', reject);\n child.on('close', (code) => {\n if (code === 0) resolve();\n else reject(new Error(`Command \"${command} ${args.join(' ')}\" exited with code ${code}`));\n });\n });\n}\n\nasync function defaultCaptureCommand(command: string, args: string[], cwd: string): Promise<string> {\n return new Promise<string>((resolve, reject) => {\n const child = spawn(command, args, { cwd });\n let stdout = '';\n child.stdout?.on('data', (chunk: Buffer) => {\n stdout += chunk.toString();\n });\n child.on('error', reject);\n child.on('close', (code) => {\n if (code === 0) resolve(stdout);\n else reject(new Error(`Command \"${command} ${args.join(' ')}\" exited with code ${code}`));\n });\n });\n}\n\nconst defaultUpdateDeps: UpdateDeps = { runCommand: defaultRunCommand, captureCommand: defaultCaptureCommand };\n\nexport interface FileOutcomeEntry {\n path: string;\n outcome: 'added' | 'replaced' | 'skipped' | 'no-longer-core';\n}\n\nexport interface UpdateResult {\n status: 'up-to-date' | 'no-changes' | 'updated';\n fromVersion: string;\n toVersion: string;\n files: FileOutcomeEntry[];\n dependencies: FieldOutcome[];\n scripts: FieldOutcome[];\n fields: FieldOutcome[];\n}\n\nexport async function updateProject(targetDir: string, deps: UpdateDeps = defaultUpdateDeps): Promise<UpdateResult> {\n const status = (await deps.captureCommand('git', ['status', '--porcelain'], targetDir)).trim();\n if (status.length > 0) {\n throw new Error('Working tree is not clean. Commit or stash your changes before running update.');\n }\n\n const oldManifest = await requireManifest(targetDir);\n const toVersion = getGeneratorVersion();\n const fromVersion = oldManifest.generatorVersion;\n\n if (compareVersions(fromVersion, toVersion) >= 0) {\n return { status: 'up-to-date', fromVersion, toVersion, files: [], dependencies: [], scripts: [], fields: [] };\n }\n\n const currentPkg = JSON.parse(await readFile(path.join(targetDir, 'package.json'), 'utf8')) as PackageJsonShape;\n const projectName = currentPkg.name;\n\n const newTemplatePkg = JSON.parse(\n applyPlaceholders(await readFile(path.join(TEMPLATE_DIR, 'package.json'), 'utf8'), projectName),\n ) as PackageJsonShape;\n\n const files: FileOutcomeEntry[] = [];\n const newCoreFiles: Record<string, string> = {};\n const fileWrites: { targetPath: string; content: string }[] = [];\n\n for (const relativePath of CORE_FILE_PATHS) {\n const newContent = applyPlaceholders(\n await readFile(path.join(TEMPLATE_DIR, templateSourcePath(relativePath)), 'utf8'),\n projectName,\n );\n const newHash = hashContent(newContent);\n\n let currentHash: string | undefined;\n try {\n currentHash = hashContent(await readFile(path.join(targetDir, relativePath), 'utf8'));\n } catch {\n currentHash = undefined;\n }\n\n const result = reconcileEntry(currentHash, oldManifest.coreFiles[relativePath], newHash, stringEquals);\n files.push({ path: relativePath, outcome: result.outcome });\n newCoreFiles[relativePath] = result.value;\n\n if (result.outcome === 'added' || result.outcome === 'replaced') {\n fileWrites.push({ targetPath: path.join(targetDir, relativePath), content: newContent });\n }\n }\n\n for (const relativePath of Object.keys(oldManifest.coreFiles)) {\n if ((CORE_FILE_PATHS as readonly string[]).includes(relativePath)) continue;\n try {\n await readFile(path.join(targetDir, relativePath), 'utf8');\n files.push({ path: relativePath, outcome: 'no-longer-core' });\n } catch {\n // already gone locally, nothing to report\n }\n }\n\n const pkgMerge = mergePackageJson(currentPkg, oldManifest, newTemplatePkg);\n\n const hasFileChanges = files.some(\n (f) => f.outcome === 'added' || f.outcome === 'replaced' || f.outcome === 'no-longer-core',\n );\n const hasChanges = hasFileChanges || pkgMerge.changed;\n\n if (!hasChanges) {\n return {\n status: 'no-changes',\n fromVersion,\n toVersion,\n files,\n dependencies: pkgMerge.dependencies,\n scripts: pkgMerge.scripts,\n fields: pkgMerge.fields,\n };\n }\n\n for (const write of fileWrites) {\n await mkdir(path.dirname(write.targetPath), { recursive: true });\n await writeFile(write.targetPath, write.content);\n }\n\n if (pkgMerge.changed) {\n await writeFile(path.join(targetDir, 'package.json'), JSON.stringify(pkgMerge.updatedPkg, null, 2) + '\\n');\n }\n\n const newManifest: Manifest = {\n generatorVersion: toVersion,\n coreFiles: newCoreFiles,\n coreDependencies: pkgMerge.coreDependencies,\n coreScripts: pkgMerge.coreScripts,\n coreFields: pkgMerge.coreFields,\n };\n await writeManifest(targetDir, newManifest);\n\n await deps.runCommand('git', ['add', '-A'], targetDir);\n await deps.runCommand('git', ['commit', '-m', `chore: update clispark core to v${toVersion}`], targetDir);\n\n return {\n status: 'updated',\n fromVersion,\n toVersion,\n files,\n dependencies: pkgMerge.dependencies,\n scripts: pkgMerge.scripts,\n fields: pkgMerge.fields,\n };\n}\n\nexport function formatUpdateSummary(result: UpdateResult): string {\n if (result.status === 'up-to-date') {\n return `Already up to date (v${result.toVersion}).`;\n }\n\n const added = result.files.filter((f) => f.outcome === 'added');\n const replaced = result.files.filter((f) => f.outcome === 'replaced');\n const skipped = result.files.filter((f) => f.outcome === 'skipped');\n const noLongerCore = result.files.filter((f) => f.outcome === 'no-longer-core');\n\n const lines: string[] = [];\n if (result.status === 'no-changes') {\n lines.push(\n noLongerCore.length\n ? `No changes applied: nothing to update since v${result.fromVersion}.`\n : `No changes applied: every core file/field has been modified locally since v${result.fromVersion}.`,\n );\n } else {\n lines.push(`Updated core from v${result.fromVersion} to v${result.toVersion}.`);\n }\n\n if (added.length) lines.push(` New: ${added.map((f) => f.path).join(', ')}`);\n if (replaced.length) lines.push(` Updated: ${replaced.map((f) => f.path).join(', ')}`);\n if (skipped.length) lines.push(` Skipped (locally modified): ${skipped.map((f) => f.path).join(', ')}`);\n if (noLongerCore.length) {\n lines.push(\n ` No longer part of the core, safe to remove manually: ${noLongerCore.map((f) => f.path).join(', ')}`,\n );\n }\n\n const fieldOutcomes = [...result.dependencies, ...result.scripts, ...result.fields].filter(\n (o) => o.outcome !== 'skipped',\n );\n if (fieldOutcomes.length) {\n lines.push(` package.json: ${fieldOutcomes.map((o) => `${o.key} (${o.outcome})`).join(', ')}`);\n }\n\n if (result.status === 'updated') {\n lines.push('Run \"clispark releasenotes\" to see what changed.');\n }\n\n return lines.join('\\n');\n}\n","export type ReconcileOutcome = 'added' | 'replaced' | 'skipped';\n\nexport interface ReconcileResult<T> {\n outcome: ReconcileOutcome;\n value: T;\n}\n\nexport function reconcileEntry<T>(\n currentLiveValue: T | undefined,\n oldManifestValue: T | undefined,\n newTemplateValue: T,\n isEqual: (a: T, b: T) => boolean,\n): ReconcileResult<T> {\n if (currentLiveValue === undefined) {\n return { outcome: 'added', value: newTemplateValue };\n }\n if (oldManifestValue !== undefined && isEqual(currentLiveValue, oldManifestValue)) {\n return { outcome: 'replaced', value: newTemplateValue };\n }\n return { outcome: 'skipped', value: oldManifestValue ?? currentLiveValue };\n}\n\nexport function stringEquals(a: string, b: string): boolean {\n return a === b;\n}\n\nexport function deepEquals(a: unknown, b: unknown): boolean {\n return JSON.stringify(a) === JSON.stringify(b);\n}\n","// src/releasenotes.ts\nimport { getGeneratorVersion, requireManifest } from './manifest.js';\n\nexport interface GitHubRelease {\n tag_name: string;\n name: string;\n body: string;\n}\n\nfunction stripV(version: string): string {\n return version.startsWith('v') ? version.slice(1) : version;\n}\n\nexport function compareVersions(a: string, b: string): number {\n const partsA = stripV(a).split('.').map(Number);\n const partsB = stripV(b).split('.').map(Number);\n const length = Math.max(partsA.length, partsB.length);\n for (let i = 0; i < length; i++) {\n const diff = (partsA[i] ?? 0) - (partsB[i] ?? 0);\n if (diff !== 0) return diff;\n }\n return 0;\n}\n\nexport interface ReleaseNotesResult {\n status: 'up-to-date' | 'releases-found';\n fromVersion: string;\n toVersion: string;\n releases: GitHubRelease[];\n}\n\nconst RELEASES_URL = 'https://api.github.com/repos/martinwichner/clispark/releases';\n\nexport async function fetchReleaseNotes(\n targetDir: string,\n fetchFn: typeof fetch = fetch,\n): Promise<ReleaseNotesResult> {\n const manifest = await requireManifest(targetDir);\n const fromVersion = manifest.generatorVersion;\n const toVersion = getGeneratorVersion();\n\n if (compareVersions(fromVersion, toVersion) >= 0) {\n return { status: 'up-to-date', fromVersion, toVersion, releases: [] };\n }\n\n const response = await fetchFn(RELEASES_URL);\n if (!response.ok) {\n throw new Error(`Failed to fetch release notes: GitHub API responded with ${response.status}`);\n }\n const allReleases = (await response.json()) as GitHubRelease[];\n\n const releases = allReleases\n .filter((r) => compareVersions(r.tag_name, fromVersion) > 0 && compareVersions(r.tag_name, toVersion) <= 0)\n .sort((a, b) => compareVersions(b.tag_name, a.tag_name));\n\n return { status: 'releases-found', fromVersion, toVersion, releases };\n}\n\nexport function formatReleaseNotes(result: ReleaseNotesResult): string {\n if (result.status === 'up-to-date') {\n return `You're on the latest clispark version (v${result.toVersion}), nothing to show.`;\n }\n if (result.releases.length === 0) {\n return `No published releases found between v${result.fromVersion} and v${result.toVersion}.`;\n }\n return result.releases.map((r) => `## ${r.tag_name}\\n\\n${r.body}`).join('\\n\\n');\n}\n","import { CORE_SCRIPT_NAMES, type Manifest } from './manifest.js';\nimport { deepEquals, reconcileEntry, stringEquals, type ReconcileOutcome } from './reconcile.js';\n\nexport interface PackageJsonShape {\n name: string;\n version: string;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n scripts?: Record<string, string>;\n engines?: Record<string, string>;\n oclif?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\nexport interface FieldOutcome {\n key: string;\n outcome: ReconcileOutcome;\n}\n\nexport interface PackageJsonMergeResult {\n updatedPkg: PackageJsonShape;\n changed: boolean;\n dependencies: FieldOutcome[];\n scripts: FieldOutcome[];\n fields: FieldOutcome[];\n coreDependencies: Record<string, string>;\n coreScripts: Record<string, string>;\n coreFields: Manifest['coreFields'];\n}\n\nfunction currentDependencyValue(\n pkg: PackageJsonShape,\n name: string,\n): { section: 'dependencies' | 'devDependencies'; value: string } | undefined {\n if (pkg.dependencies?.[name] !== undefined) return { section: 'dependencies', value: pkg.dependencies[name] };\n if (pkg.devDependencies?.[name] !== undefined) {\n return { section: 'devDependencies', value: pkg.devDependencies[name] };\n }\n return undefined;\n}\n\nexport function mergePackageJson(\n currentPkg: PackageJsonShape,\n oldManifest: Manifest,\n newTemplatePkg: PackageJsonShape,\n): PackageJsonMergeResult {\n const updatedPkg: PackageJsonShape = { ...currentPkg };\n let changed = false;\n\n const dependencies: FieldOutcome[] = [];\n const coreDependencies: Record<string, string> = {};\n const dependencyNames = new Set([\n ...Object.keys(newTemplatePkg.dependencies ?? {}),\n ...Object.keys(newTemplatePkg.devDependencies ?? {}),\n ]);\n\n for (const name of dependencyNames) {\n const inNewDependencies = newTemplatePkg.dependencies?.[name] !== undefined;\n const newValue = (inNewDependencies ? newTemplatePkg.dependencies : newTemplatePkg.devDependencies)![name];\n const current = currentDependencyValue(currentPkg, name);\n const oldValue = oldManifest.coreDependencies[name];\n\n const result = reconcileEntry(current?.value, oldValue, newValue, stringEquals);\n dependencies.push({ key: name, outcome: result.outcome });\n coreDependencies[name] = result.value;\n\n if (result.outcome !== 'skipped' && result.value !== current?.value) {\n changed = true;\n const section = current?.section ?? (inNewDependencies ? 'dependencies' : 'devDependencies');\n updatedPkg[section] = { ...updatedPkg[section], [name]: result.value };\n }\n }\n\n const scripts: FieldOutcome[] = [];\n const coreScripts: Record<string, string> = {};\n\n for (const name of CORE_SCRIPT_NAMES) {\n const newValue = newTemplatePkg.scripts?.[name];\n if (newValue === undefined) continue;\n const currentValue = currentPkg.scripts?.[name];\n const oldValue = oldManifest.coreScripts[name];\n\n const result = reconcileEntry(currentValue, oldValue, newValue, stringEquals);\n scripts.push({ key: name, outcome: result.outcome });\n coreScripts[name] = result.value;\n\n if (result.outcome !== 'skipped' && result.value !== currentValue) {\n changed = true;\n updatedPkg.scripts = { ...updatedPkg.scripts, [name]: result.value };\n }\n }\n\n const fields: FieldOutcome[] = [];\n let enginesValue = oldManifest.coreFields.engines;\n let oclifValue = oldManifest.coreFields.oclif;\n\n if (newTemplatePkg.engines !== undefined) {\n const enginesResult = reconcileEntry(\n currentPkg.engines,\n oldManifest.coreFields.engines,\n newTemplatePkg.engines,\n deepEquals,\n );\n fields.push({ key: 'engines', outcome: enginesResult.outcome });\n enginesValue = enginesResult.value;\n if (enginesResult.outcome !== 'skipped' && !deepEquals(enginesResult.value, currentPkg.engines)) {\n changed = true;\n updatedPkg.engines = enginesResult.value;\n }\n }\n\n if (newTemplatePkg.oclif !== undefined) {\n const oclifResult = reconcileEntry(\n currentPkg.oclif,\n oldManifest.coreFields.oclif,\n newTemplatePkg.oclif,\n deepEquals,\n );\n fields.push({ key: 'oclif', outcome: oclifResult.outcome });\n oclifValue = oclifResult.value;\n if (oclifResult.outcome !== 'skipped' && !deepEquals(oclifResult.value, currentPkg.oclif)) {\n changed = true;\n updatedPkg.oclif = oclifResult.value;\n }\n }\n\n return {\n updatedPkg,\n changed,\n dependencies,\n scripts,\n fields,\n coreDependencies,\n coreScripts,\n coreFields: { engines: enginesValue, oclif: oclifValue },\n };\n}\n"],"mappings":"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","names":["createRequire","path","path","readFile","writeFile","pkg","require","path","readFile","writeFile","path","mkdir","readFile","writeFile","path","spawn","pkg","defaultRunCommand","spawn","readFile","path","mkdir","writeFile","require","createRequire","path"]}
|