gistajs 0.0.6 → 0.0.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/dist/bin.cjs +217 -43
- package/dist/index.d.ts +32 -2
- package/dist/index.js +214 -42
- package/package.json +5 -3
package/dist/bin.cjs
CHANGED
|
@@ -64,7 +64,7 @@ var import_node_process2 = __toESM(require("process"), 1);
|
|
|
64
64
|
var tar = __toESM(require("tar"), 1);
|
|
65
65
|
|
|
66
66
|
// src/git.ts
|
|
67
|
-
var
|
|
67
|
+
var import_node_child_process2 = require("child_process");
|
|
68
68
|
|
|
69
69
|
// src/prompt.ts
|
|
70
70
|
var import_node_process = __toESM(require("process"), 1);
|
|
@@ -116,6 +116,24 @@ async function confirm(rl, message) {
|
|
|
116
116
|
return answer === "y" || answer === "yes";
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
// src/subprocess.ts
|
|
120
|
+
var import_node_child_process = require("child_process");
|
|
121
|
+
async function run(command, args, cwd) {
|
|
122
|
+
await new Promise((resolve2, reject) => {
|
|
123
|
+
let child = (0, import_node_child_process.spawn)(command, args, {
|
|
124
|
+
cwd,
|
|
125
|
+
stdio: "inherit"
|
|
126
|
+
});
|
|
127
|
+
child.once("error", reject);
|
|
128
|
+
child.once("exit", (code) => {
|
|
129
|
+
if (code === 0) resolve2();
|
|
130
|
+
else {
|
|
131
|
+
reject(new Error(`${command} ${args.join(" ")} exited with code ${code}`));
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
119
137
|
// src/git.ts
|
|
120
138
|
var defaultDeps = {
|
|
121
139
|
promptForGitIdentity,
|
|
@@ -164,7 +182,7 @@ async function resolveGitIdentity(cwd, deps) {
|
|
|
164
182
|
}
|
|
165
183
|
function readGitConfig(cwd, key) {
|
|
166
184
|
try {
|
|
167
|
-
return (0,
|
|
185
|
+
return (0, import_node_child_process2.execFileSync)("git", ["config", "--get", key], {
|
|
168
186
|
cwd,
|
|
169
187
|
encoding: "utf-8"
|
|
170
188
|
}).trim();
|
|
@@ -172,22 +190,6 @@ function readGitConfig(cwd, key) {
|
|
|
172
190
|
return "";
|
|
173
191
|
}
|
|
174
192
|
}
|
|
175
|
-
async function run(command, args, cwd) {
|
|
176
|
-
await new Promise((resolve2, reject) => {
|
|
177
|
-
let child = (0, import_node_child_process.spawn)(command, args, {
|
|
178
|
-
cwd,
|
|
179
|
-
stdio: "inherit"
|
|
180
|
-
});
|
|
181
|
-
child.once("error", reject);
|
|
182
|
-
child.once("exit", (code) => {
|
|
183
|
-
if (code === 0) resolve2();
|
|
184
|
-
else
|
|
185
|
-
reject(
|
|
186
|
-
new Error(`${command} ${args.join(" ")} exited with code ${code}`)
|
|
187
|
-
);
|
|
188
|
-
});
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
193
|
|
|
192
194
|
// src/create.ts
|
|
193
195
|
async function createProject(starter, options) {
|
|
@@ -314,28 +316,160 @@ async function assertSafeProjectRoot(root) {
|
|
|
314
316
|
await (0, import_promises2.mkdir)(parent, { recursive: true });
|
|
315
317
|
}
|
|
316
318
|
|
|
319
|
+
// src/diff.ts
|
|
320
|
+
var import_node_child_process3 = require("child_process");
|
|
321
|
+
var import_promises3 = require("fs/promises");
|
|
322
|
+
var import_node_os2 = require("os");
|
|
323
|
+
var import_node_path2 = require("path");
|
|
324
|
+
var defaultDeps2 = {
|
|
325
|
+
mkdtemp: import_promises3.mkdtemp,
|
|
326
|
+
rm: import_promises3.rm,
|
|
327
|
+
run,
|
|
328
|
+
runOutput
|
|
329
|
+
};
|
|
330
|
+
async function diffStarter(starter, options, deps = defaultDeps2) {
|
|
331
|
+
if (!options.fromReleaseKey || !options.toReleaseKey) {
|
|
332
|
+
throw new Error("Diff requires both from and to release keys");
|
|
333
|
+
}
|
|
334
|
+
let root = await deps.mkdtemp((0, import_node_path2.join)((0, import_node_os2.tmpdir)(), "gistajs-diff-"));
|
|
335
|
+
let repoUrl = getStarterRepoUrl(starter);
|
|
336
|
+
let fromTag = resolveStarterTagName(starter.slug, options.fromReleaseKey);
|
|
337
|
+
let toTag = resolveStarterTagName(starter.slug, options.toReleaseKey);
|
|
338
|
+
try {
|
|
339
|
+
await git2(root, ["init", "-q"], deps);
|
|
340
|
+
await git2(root, ["remote", "add", "origin", repoUrl], deps);
|
|
341
|
+
await fetchTag(root, fromTag, repoUrl, deps);
|
|
342
|
+
await fetchTag(root, toTag, repoUrl, deps);
|
|
343
|
+
return await gitOutput(
|
|
344
|
+
root,
|
|
345
|
+
["diff", "--stat", `refs/tags/${fromTag}`, `refs/tags/${toTag}`],
|
|
346
|
+
repoUrl,
|
|
347
|
+
deps
|
|
348
|
+
);
|
|
349
|
+
} finally {
|
|
350
|
+
await deps.rm(root, { recursive: true, force: true });
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
function resolveStarterTagName(starter, releaseKey) {
|
|
354
|
+
return `${starter}/${releaseKey}`;
|
|
355
|
+
}
|
|
356
|
+
function getStarterRepoUrl(starter) {
|
|
357
|
+
return `https://github.com/${starter.repo}.git`;
|
|
358
|
+
}
|
|
359
|
+
async function fetchTag(cwd, tag, repoUrl, deps) {
|
|
360
|
+
try {
|
|
361
|
+
await git2(
|
|
362
|
+
cwd,
|
|
363
|
+
["fetch", "--quiet", "--no-tags", "origin", `refs/tags/${tag}:refs/tags/${tag}`],
|
|
364
|
+
deps
|
|
365
|
+
);
|
|
366
|
+
} catch (error) {
|
|
367
|
+
throw new Error(
|
|
368
|
+
`Failed to fetch tag ${tag} from ${repoUrl}: ${getErrorMessage(error)}`
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
async function git2(cwd, args, deps) {
|
|
373
|
+
await deps.run("git", args, cwd);
|
|
374
|
+
}
|
|
375
|
+
async function gitOutput(cwd, args, repoUrl, deps) {
|
|
376
|
+
try {
|
|
377
|
+
return await deps.runOutput("git", args, cwd);
|
|
378
|
+
} catch (error) {
|
|
379
|
+
throw new Error(
|
|
380
|
+
`Failed to diff starter snapshots from ${repoUrl}: ${getErrorMessage(error)}`
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
function getErrorMessage(error) {
|
|
385
|
+
return error instanceof Error ? error.message : String(error);
|
|
386
|
+
}
|
|
387
|
+
async function runOutput(command, args, cwd) {
|
|
388
|
+
return await new Promise((resolve2, reject) => {
|
|
389
|
+
let stdout = "";
|
|
390
|
+
let stderr = "";
|
|
391
|
+
let child = (0, import_node_child_process3.spawn)(command, args, {
|
|
392
|
+
cwd,
|
|
393
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
394
|
+
});
|
|
395
|
+
child.stdout.on("data", (chunk) => {
|
|
396
|
+
stdout += String(chunk);
|
|
397
|
+
});
|
|
398
|
+
child.stderr.on("data", (chunk) => {
|
|
399
|
+
stderr += String(chunk);
|
|
400
|
+
});
|
|
401
|
+
child.once("error", reject);
|
|
402
|
+
child.once("exit", (code) => {
|
|
403
|
+
if (code === 0) {
|
|
404
|
+
resolve2(stdout);
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
let detail = stderr.trim();
|
|
408
|
+
reject(
|
|
409
|
+
new Error(
|
|
410
|
+
detail || `${command} ${args.join(" ")} exited with code ${code}`
|
|
411
|
+
)
|
|
412
|
+
);
|
|
413
|
+
});
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
|
|
317
417
|
// src/cli.ts
|
|
318
|
-
|
|
418
|
+
var defaultDeps3 = {
|
|
419
|
+
loadCatalog,
|
|
420
|
+
createProject,
|
|
421
|
+
diffStarter,
|
|
422
|
+
promptForStarter,
|
|
423
|
+
stdout: console
|
|
424
|
+
};
|
|
425
|
+
var UsageError = class extends Error {
|
|
426
|
+
constructor(message) {
|
|
427
|
+
super(message);
|
|
428
|
+
this.name = "UsageError";
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
async function runCli(argv = import_node_process3.default.argv.slice(2), deps = defaultDeps3) {
|
|
319
432
|
let [command, ...rest] = argv;
|
|
320
433
|
if (!command || command === "--help" || command === "-h") {
|
|
321
|
-
|
|
434
|
+
deps.stdout.log(getHelpText());
|
|
322
435
|
return;
|
|
323
436
|
}
|
|
324
|
-
if (command
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
437
|
+
if (command === "create") {
|
|
438
|
+
let options = parseCreateArgs(rest);
|
|
439
|
+
if (!options.projectName) {
|
|
440
|
+
throw new UsageError("Project name is required");
|
|
441
|
+
}
|
|
442
|
+
let catalog = await deps.loadCatalog(options.catalogUrl);
|
|
443
|
+
let starterName = options.starter || await deps.promptForStarter(catalog);
|
|
444
|
+
let starter = catalog.find((entry) => entry.slug === starterName);
|
|
445
|
+
if (!starter) {
|
|
446
|
+
throw new UsageError(`Unknown starter: ${starterName}`);
|
|
447
|
+
}
|
|
448
|
+
let root = await deps.createProject(starter, options);
|
|
449
|
+
deps.stdout.log(`Created ${starter.slug} project in ${root}`);
|
|
450
|
+
return;
|
|
333
451
|
}
|
|
334
|
-
if (
|
|
335
|
-
|
|
452
|
+
if (command === "diff") {
|
|
453
|
+
let options = parseDiffArgs(rest);
|
|
454
|
+
if (!options.starter) {
|
|
455
|
+
throw new UsageError("Starter is required");
|
|
456
|
+
}
|
|
457
|
+
if (!options.fromReleaseKey) {
|
|
458
|
+
throw new UsageError("From release key is required");
|
|
459
|
+
}
|
|
460
|
+
if (!options.toReleaseKey) {
|
|
461
|
+
throw new UsageError("To release key is required");
|
|
462
|
+
}
|
|
463
|
+
let catalog = await deps.loadCatalog(options.catalogUrl);
|
|
464
|
+
let starter = catalog.find((entry) => entry.slug === options.starter);
|
|
465
|
+
if (!starter) {
|
|
466
|
+
throw new UsageError(`Unknown starter: ${options.starter}`);
|
|
467
|
+
}
|
|
468
|
+
let output = await deps.diffStarter(starter, options);
|
|
469
|
+
deps.stdout.log(output.trimEnd());
|
|
470
|
+
return;
|
|
336
471
|
}
|
|
337
|
-
|
|
338
|
-
console.log(`Created ${starter.slug} project in ${root}`);
|
|
472
|
+
throw new UsageError(`Unknown command: ${command}`);
|
|
339
473
|
}
|
|
340
474
|
async function main() {
|
|
341
475
|
try {
|
|
@@ -343,6 +477,9 @@ async function main() {
|
|
|
343
477
|
} catch (error) {
|
|
344
478
|
let message = error instanceof Error ? error.message : String(error);
|
|
345
479
|
console.error(message);
|
|
480
|
+
if (error instanceof UsageError) {
|
|
481
|
+
console.error(getHelpText());
|
|
482
|
+
}
|
|
346
483
|
import_node_process3.default.exitCode = 1;
|
|
347
484
|
}
|
|
348
485
|
}
|
|
@@ -359,7 +496,7 @@ function parseCreateArgs(argv) {
|
|
|
359
496
|
continue;
|
|
360
497
|
}
|
|
361
498
|
if (arg === "--starter") {
|
|
362
|
-
if (!argv[index + 1]) throw new
|
|
499
|
+
if (!argv[index + 1]) throw new UsageError("--starter requires a value");
|
|
363
500
|
options.starter = argv[index + 1];
|
|
364
501
|
index += 1;
|
|
365
502
|
continue;
|
|
@@ -373,22 +510,59 @@ function parseCreateArgs(argv) {
|
|
|
373
510
|
continue;
|
|
374
511
|
}
|
|
375
512
|
if (arg === "--catalog-url") {
|
|
376
|
-
if (!argv[index + 1])
|
|
513
|
+
if (!argv[index + 1]) {
|
|
514
|
+
throw new UsageError("--catalog-url requires a value");
|
|
515
|
+
}
|
|
377
516
|
options.catalogUrl = argv[index + 1];
|
|
378
517
|
index += 1;
|
|
379
518
|
continue;
|
|
380
519
|
}
|
|
381
|
-
throw new
|
|
520
|
+
throw new UsageError(`Unknown argument: ${arg}`);
|
|
382
521
|
}
|
|
383
522
|
return options;
|
|
384
523
|
}
|
|
385
|
-
function
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
524
|
+
function parseDiffArgs(argv) {
|
|
525
|
+
let options = {};
|
|
526
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
527
|
+
let arg = argv[index];
|
|
528
|
+
if (!arg) continue;
|
|
529
|
+
if (!arg.startsWith("--") && !options.starter) {
|
|
530
|
+
options.starter = arg;
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
533
|
+
if (!arg.startsWith("--") && !options.fromReleaseKey) {
|
|
534
|
+
options.fromReleaseKey = arg;
|
|
535
|
+
continue;
|
|
536
|
+
}
|
|
537
|
+
if (!arg.startsWith("--") && !options.toReleaseKey) {
|
|
538
|
+
options.toReleaseKey = arg;
|
|
539
|
+
continue;
|
|
540
|
+
}
|
|
541
|
+
if (arg === "--catalog-url") {
|
|
542
|
+
if (!argv[index + 1]) {
|
|
543
|
+
throw new UsageError("--catalog-url requires a value");
|
|
544
|
+
}
|
|
545
|
+
options.catalogUrl = argv[index + 1];
|
|
546
|
+
index += 1;
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
throw new UsageError(`Unknown argument: ${arg}`);
|
|
550
|
+
}
|
|
551
|
+
return options;
|
|
552
|
+
}
|
|
553
|
+
function getHelpText() {
|
|
554
|
+
return [
|
|
555
|
+
"Usage:",
|
|
556
|
+
" gistajs create <project-name> [--starter <slug>] [--no-install] [--no-git]",
|
|
557
|
+
" gistajs diff <starter> <from-release-key> <to-release-key>",
|
|
558
|
+
"",
|
|
559
|
+
"Examples:",
|
|
560
|
+
" gistajs create my-app",
|
|
561
|
+
" gistajs create my-app --starter website",
|
|
562
|
+
" gistajs diff auth 2026-03-28-001 2026-03-29-001",
|
|
563
|
+
"",
|
|
564
|
+
"Run `gistajs` with no arguments to show this help."
|
|
565
|
+
].join("\n");
|
|
392
566
|
}
|
|
393
567
|
|
|
394
568
|
// src/bin.ts
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { mkdtemp, rm } from 'node:fs/promises';
|
|
2
|
+
|
|
1
3
|
type StarterSpec = {
|
|
2
4
|
slug: string;
|
|
3
5
|
repo: string;
|
|
@@ -12,9 +14,37 @@ type CreateOptions = {
|
|
|
12
14
|
git?: boolean;
|
|
13
15
|
catalogUrl?: string;
|
|
14
16
|
};
|
|
17
|
+
type DiffOptions = {
|
|
18
|
+
starter?: string;
|
|
19
|
+
fromReleaseKey?: string;
|
|
20
|
+
toReleaseKey?: string;
|
|
21
|
+
catalogUrl?: string;
|
|
22
|
+
};
|
|
15
23
|
|
|
16
|
-
declare function
|
|
24
|
+
declare function loadCatalog(url?: string): Promise<StarterSpec[]>;
|
|
17
25
|
|
|
18
26
|
declare function createProject(starter: StarterSpec, options: CreateOptions): Promise<string>;
|
|
19
27
|
|
|
20
|
-
|
|
28
|
+
declare function run(command: string, args: string[], cwd: string): Promise<void>;
|
|
29
|
+
|
|
30
|
+
type DiffDeps = {
|
|
31
|
+
mkdtemp: typeof mkdtemp;
|
|
32
|
+
rm: typeof rm;
|
|
33
|
+
run: typeof run;
|
|
34
|
+
runOutput: typeof runOutput;
|
|
35
|
+
};
|
|
36
|
+
declare function diffStarter(starter: StarterSpec, options: DiffOptions, deps?: DiffDeps): Promise<string>;
|
|
37
|
+
declare function runOutput(command: string, args: string[], cwd: string): Promise<string>;
|
|
38
|
+
|
|
39
|
+
declare function promptForStarter(starters: StarterSpec[]): Promise<string>;
|
|
40
|
+
|
|
41
|
+
type CliDeps = {
|
|
42
|
+
loadCatalog: typeof loadCatalog;
|
|
43
|
+
createProject: typeof createProject;
|
|
44
|
+
diffStarter: typeof diffStarter;
|
|
45
|
+
promptForStarter: typeof promptForStarter;
|
|
46
|
+
stdout: Pick<typeof console, 'log'>;
|
|
47
|
+
};
|
|
48
|
+
declare function runCli(argv?: string[], deps?: CliDeps): Promise<void>;
|
|
49
|
+
|
|
50
|
+
export { type CreateOptions, type DiffOptions, type StarterSpec, createProject, diffStarter, runCli };
|
package/dist/index.js
CHANGED
|
@@ -49,7 +49,7 @@ import process2 from "process";
|
|
|
49
49
|
import * as tar from "tar";
|
|
50
50
|
|
|
51
51
|
// src/git.ts
|
|
52
|
-
import { execFileSync
|
|
52
|
+
import { execFileSync } from "child_process";
|
|
53
53
|
|
|
54
54
|
// src/prompt.ts
|
|
55
55
|
import process from "process";
|
|
@@ -101,6 +101,24 @@ async function confirm(rl, message) {
|
|
|
101
101
|
return answer === "y" || answer === "yes";
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
// src/subprocess.ts
|
|
105
|
+
import { spawn } from "child_process";
|
|
106
|
+
async function run(command, args, cwd) {
|
|
107
|
+
await new Promise((resolve2, reject) => {
|
|
108
|
+
let child = spawn(command, args, {
|
|
109
|
+
cwd,
|
|
110
|
+
stdio: "inherit"
|
|
111
|
+
});
|
|
112
|
+
child.once("error", reject);
|
|
113
|
+
child.once("exit", (code) => {
|
|
114
|
+
if (code === 0) resolve2();
|
|
115
|
+
else {
|
|
116
|
+
reject(new Error(`${command} ${args.join(" ")} exited with code ${code}`));
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
104
122
|
// src/git.ts
|
|
105
123
|
var defaultDeps = {
|
|
106
124
|
promptForGitIdentity,
|
|
@@ -157,22 +175,6 @@ function readGitConfig(cwd, key) {
|
|
|
157
175
|
return "";
|
|
158
176
|
}
|
|
159
177
|
}
|
|
160
|
-
async function run(command, args, cwd) {
|
|
161
|
-
await new Promise((resolve2, reject) => {
|
|
162
|
-
let child = spawn(command, args, {
|
|
163
|
-
cwd,
|
|
164
|
-
stdio: "inherit"
|
|
165
|
-
});
|
|
166
|
-
child.once("error", reject);
|
|
167
|
-
child.once("exit", (code) => {
|
|
168
|
-
if (code === 0) resolve2();
|
|
169
|
-
else
|
|
170
|
-
reject(
|
|
171
|
-
new Error(`${command} ${args.join(" ")} exited with code ${code}`)
|
|
172
|
-
);
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
178
|
|
|
177
179
|
// src/create.ts
|
|
178
180
|
async function createProject(starter, options) {
|
|
@@ -299,28 +301,160 @@ async function assertSafeProjectRoot(root) {
|
|
|
299
301
|
await mkdir(parent, { recursive: true });
|
|
300
302
|
}
|
|
301
303
|
|
|
304
|
+
// src/diff.ts
|
|
305
|
+
import { spawn as spawn2 } from "child_process";
|
|
306
|
+
import { mkdtemp as mkdtemp2, rm as rm2 } from "fs/promises";
|
|
307
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
308
|
+
import { join as join2 } from "path";
|
|
309
|
+
var defaultDeps2 = {
|
|
310
|
+
mkdtemp: mkdtemp2,
|
|
311
|
+
rm: rm2,
|
|
312
|
+
run,
|
|
313
|
+
runOutput
|
|
314
|
+
};
|
|
315
|
+
async function diffStarter(starter, options, deps = defaultDeps2) {
|
|
316
|
+
if (!options.fromReleaseKey || !options.toReleaseKey) {
|
|
317
|
+
throw new Error("Diff requires both from and to release keys");
|
|
318
|
+
}
|
|
319
|
+
let root = await deps.mkdtemp(join2(tmpdir2(), "gistajs-diff-"));
|
|
320
|
+
let repoUrl = getStarterRepoUrl(starter);
|
|
321
|
+
let fromTag = resolveStarterTagName(starter.slug, options.fromReleaseKey);
|
|
322
|
+
let toTag = resolveStarterTagName(starter.slug, options.toReleaseKey);
|
|
323
|
+
try {
|
|
324
|
+
await git2(root, ["init", "-q"], deps);
|
|
325
|
+
await git2(root, ["remote", "add", "origin", repoUrl], deps);
|
|
326
|
+
await fetchTag(root, fromTag, repoUrl, deps);
|
|
327
|
+
await fetchTag(root, toTag, repoUrl, deps);
|
|
328
|
+
return await gitOutput(
|
|
329
|
+
root,
|
|
330
|
+
["diff", "--stat", `refs/tags/${fromTag}`, `refs/tags/${toTag}`],
|
|
331
|
+
repoUrl,
|
|
332
|
+
deps
|
|
333
|
+
);
|
|
334
|
+
} finally {
|
|
335
|
+
await deps.rm(root, { recursive: true, force: true });
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
function resolveStarterTagName(starter, releaseKey) {
|
|
339
|
+
return `${starter}/${releaseKey}`;
|
|
340
|
+
}
|
|
341
|
+
function getStarterRepoUrl(starter) {
|
|
342
|
+
return `https://github.com/${starter.repo}.git`;
|
|
343
|
+
}
|
|
344
|
+
async function fetchTag(cwd, tag, repoUrl, deps) {
|
|
345
|
+
try {
|
|
346
|
+
await git2(
|
|
347
|
+
cwd,
|
|
348
|
+
["fetch", "--quiet", "--no-tags", "origin", `refs/tags/${tag}:refs/tags/${tag}`],
|
|
349
|
+
deps
|
|
350
|
+
);
|
|
351
|
+
} catch (error) {
|
|
352
|
+
throw new Error(
|
|
353
|
+
`Failed to fetch tag ${tag} from ${repoUrl}: ${getErrorMessage(error)}`
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
async function git2(cwd, args, deps) {
|
|
358
|
+
await deps.run("git", args, cwd);
|
|
359
|
+
}
|
|
360
|
+
async function gitOutput(cwd, args, repoUrl, deps) {
|
|
361
|
+
try {
|
|
362
|
+
return await deps.runOutput("git", args, cwd);
|
|
363
|
+
} catch (error) {
|
|
364
|
+
throw new Error(
|
|
365
|
+
`Failed to diff starter snapshots from ${repoUrl}: ${getErrorMessage(error)}`
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
function getErrorMessage(error) {
|
|
370
|
+
return error instanceof Error ? error.message : String(error);
|
|
371
|
+
}
|
|
372
|
+
async function runOutput(command, args, cwd) {
|
|
373
|
+
return await new Promise((resolve2, reject) => {
|
|
374
|
+
let stdout = "";
|
|
375
|
+
let stderr = "";
|
|
376
|
+
let child = spawn2(command, args, {
|
|
377
|
+
cwd,
|
|
378
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
379
|
+
});
|
|
380
|
+
child.stdout.on("data", (chunk) => {
|
|
381
|
+
stdout += String(chunk);
|
|
382
|
+
});
|
|
383
|
+
child.stderr.on("data", (chunk) => {
|
|
384
|
+
stderr += String(chunk);
|
|
385
|
+
});
|
|
386
|
+
child.once("error", reject);
|
|
387
|
+
child.once("exit", (code) => {
|
|
388
|
+
if (code === 0) {
|
|
389
|
+
resolve2(stdout);
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
let detail = stderr.trim();
|
|
393
|
+
reject(
|
|
394
|
+
new Error(
|
|
395
|
+
detail || `${command} ${args.join(" ")} exited with code ${code}`
|
|
396
|
+
)
|
|
397
|
+
);
|
|
398
|
+
});
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
|
|
302
402
|
// src/cli.ts
|
|
303
|
-
|
|
403
|
+
var defaultDeps3 = {
|
|
404
|
+
loadCatalog,
|
|
405
|
+
createProject,
|
|
406
|
+
diffStarter,
|
|
407
|
+
promptForStarter,
|
|
408
|
+
stdout: console
|
|
409
|
+
};
|
|
410
|
+
var UsageError = class extends Error {
|
|
411
|
+
constructor(message) {
|
|
412
|
+
super(message);
|
|
413
|
+
this.name = "UsageError";
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
async function runCli(argv = process3.argv.slice(2), deps = defaultDeps3) {
|
|
304
417
|
let [command, ...rest] = argv;
|
|
305
418
|
if (!command || command === "--help" || command === "-h") {
|
|
306
|
-
|
|
419
|
+
deps.stdout.log(getHelpText());
|
|
307
420
|
return;
|
|
308
421
|
}
|
|
309
|
-
if (command
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
422
|
+
if (command === "create") {
|
|
423
|
+
let options = parseCreateArgs(rest);
|
|
424
|
+
if (!options.projectName) {
|
|
425
|
+
throw new UsageError("Project name is required");
|
|
426
|
+
}
|
|
427
|
+
let catalog = await deps.loadCatalog(options.catalogUrl);
|
|
428
|
+
let starterName = options.starter || await deps.promptForStarter(catalog);
|
|
429
|
+
let starter = catalog.find((entry) => entry.slug === starterName);
|
|
430
|
+
if (!starter) {
|
|
431
|
+
throw new UsageError(`Unknown starter: ${starterName}`);
|
|
432
|
+
}
|
|
433
|
+
let root = await deps.createProject(starter, options);
|
|
434
|
+
deps.stdout.log(`Created ${starter.slug} project in ${root}`);
|
|
435
|
+
return;
|
|
318
436
|
}
|
|
319
|
-
if (
|
|
320
|
-
|
|
437
|
+
if (command === "diff") {
|
|
438
|
+
let options = parseDiffArgs(rest);
|
|
439
|
+
if (!options.starter) {
|
|
440
|
+
throw new UsageError("Starter is required");
|
|
441
|
+
}
|
|
442
|
+
if (!options.fromReleaseKey) {
|
|
443
|
+
throw new UsageError("From release key is required");
|
|
444
|
+
}
|
|
445
|
+
if (!options.toReleaseKey) {
|
|
446
|
+
throw new UsageError("To release key is required");
|
|
447
|
+
}
|
|
448
|
+
let catalog = await deps.loadCatalog(options.catalogUrl);
|
|
449
|
+
let starter = catalog.find((entry) => entry.slug === options.starter);
|
|
450
|
+
if (!starter) {
|
|
451
|
+
throw new UsageError(`Unknown starter: ${options.starter}`);
|
|
452
|
+
}
|
|
453
|
+
let output = await deps.diffStarter(starter, options);
|
|
454
|
+
deps.stdout.log(output.trimEnd());
|
|
455
|
+
return;
|
|
321
456
|
}
|
|
322
|
-
|
|
323
|
-
console.log(`Created ${starter.slug} project in ${root}`);
|
|
457
|
+
throw new UsageError(`Unknown command: ${command}`);
|
|
324
458
|
}
|
|
325
459
|
function parseCreateArgs(argv) {
|
|
326
460
|
let options = {
|
|
@@ -335,7 +469,7 @@ function parseCreateArgs(argv) {
|
|
|
335
469
|
continue;
|
|
336
470
|
}
|
|
337
471
|
if (arg === "--starter") {
|
|
338
|
-
if (!argv[index + 1]) throw new
|
|
472
|
+
if (!argv[index + 1]) throw new UsageError("--starter requires a value");
|
|
339
473
|
options.starter = argv[index + 1];
|
|
340
474
|
index += 1;
|
|
341
475
|
continue;
|
|
@@ -349,24 +483,62 @@ function parseCreateArgs(argv) {
|
|
|
349
483
|
continue;
|
|
350
484
|
}
|
|
351
485
|
if (arg === "--catalog-url") {
|
|
352
|
-
if (!argv[index + 1])
|
|
486
|
+
if (!argv[index + 1]) {
|
|
487
|
+
throw new UsageError("--catalog-url requires a value");
|
|
488
|
+
}
|
|
353
489
|
options.catalogUrl = argv[index + 1];
|
|
354
490
|
index += 1;
|
|
355
491
|
continue;
|
|
356
492
|
}
|
|
357
|
-
throw new
|
|
493
|
+
throw new UsageError(`Unknown argument: ${arg}`);
|
|
358
494
|
}
|
|
359
495
|
return options;
|
|
360
496
|
}
|
|
361
|
-
function
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
497
|
+
function parseDiffArgs(argv) {
|
|
498
|
+
let options = {};
|
|
499
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
500
|
+
let arg = argv[index];
|
|
501
|
+
if (!arg) continue;
|
|
502
|
+
if (!arg.startsWith("--") && !options.starter) {
|
|
503
|
+
options.starter = arg;
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
if (!arg.startsWith("--") && !options.fromReleaseKey) {
|
|
507
|
+
options.fromReleaseKey = arg;
|
|
508
|
+
continue;
|
|
509
|
+
}
|
|
510
|
+
if (!arg.startsWith("--") && !options.toReleaseKey) {
|
|
511
|
+
options.toReleaseKey = arg;
|
|
512
|
+
continue;
|
|
513
|
+
}
|
|
514
|
+
if (arg === "--catalog-url") {
|
|
515
|
+
if (!argv[index + 1]) {
|
|
516
|
+
throw new UsageError("--catalog-url requires a value");
|
|
517
|
+
}
|
|
518
|
+
options.catalogUrl = argv[index + 1];
|
|
519
|
+
index += 1;
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
throw new UsageError(`Unknown argument: ${arg}`);
|
|
523
|
+
}
|
|
524
|
+
return options;
|
|
525
|
+
}
|
|
526
|
+
function getHelpText() {
|
|
527
|
+
return [
|
|
528
|
+
"Usage:",
|
|
529
|
+
" gistajs create <project-name> [--starter <slug>] [--no-install] [--no-git]",
|
|
530
|
+
" gistajs diff <starter> <from-release-key> <to-release-key>",
|
|
531
|
+
"",
|
|
532
|
+
"Examples:",
|
|
533
|
+
" gistajs create my-app",
|
|
534
|
+
" gistajs create my-app --starter website",
|
|
535
|
+
" gistajs diff auth 2026-03-28-001 2026-03-29-001",
|
|
536
|
+
"",
|
|
537
|
+
"Run `gistajs` with no arguments to show this help."
|
|
538
|
+
].join("\n");
|
|
368
539
|
}
|
|
369
540
|
export {
|
|
370
541
|
createProject,
|
|
542
|
+
diffStarter,
|
|
371
543
|
runCli
|
|
372
544
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gistajs",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"description": "Create Gista.js starter projects",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -48,8 +48,10 @@
|
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"build": "tsup",
|
|
51
|
-
"test": "vitest",
|
|
51
|
+
"test": "vitest run",
|
|
52
|
+
"test:watch": "vitest",
|
|
52
53
|
"typecheck": "tsc -b",
|
|
53
|
-
"
|
|
54
|
+
"release:prepare": "pnpm typecheck && pnpm build && pnpm test",
|
|
55
|
+
"np": "pnpm release:prepare && np"
|
|
54
56
|
}
|
|
55
57
|
}
|