@optique/git 0.9.0-dev.215 → 0.9.0-dev.217

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/index.cjs CHANGED
@@ -24,97 +24,43 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  const __optique_core_message = __toESM(require("@optique/core/message"));
25
25
  const __optique_core_nonempty = __toESM(require("@optique/core/nonempty"));
26
26
  const isomorphic_git = __toESM(require("isomorphic-git"));
27
+ const node_fs_promises = __toESM(require("node:fs/promises"));
27
28
  const node_process = __toESM(require("node:process"));
28
29
 
29
30
  //#region src/index.ts
31
+ const gitFs = {
32
+ readFile: node_fs_promises.readFile,
33
+ writeFile: node_fs_promises.writeFile,
34
+ mkdir: node_fs_promises.mkdir,
35
+ rmdir: node_fs_promises.rmdir,
36
+ unlink: node_fs_promises.unlink,
37
+ readdir: node_fs_promises.readdir,
38
+ readlink: node_fs_promises.readlink,
39
+ symlink: node_fs_promises.symlink,
40
+ stat: node_fs_promises.stat,
41
+ lstat: node_fs_promises.lstat
42
+ };
30
43
  const METAVAR_BRANCH = "BRANCH";
31
44
  const METAVAR_TAG = "TAG";
32
45
  const METAVAR_REMOTE = "REMOTE";
33
- let defaultFs = null;
34
- let fsLoading = null;
35
- async function getDefaultFs() {
36
- if (defaultFs) return await defaultFs;
37
- if (fsLoading) return await fsLoading;
38
- fsLoading = (async () => {
39
- const nodeFs = await import("node:fs/promises");
40
- const { TextDecoder } = await import("node:util");
41
- const decoder = new TextDecoder();
42
- defaultFs = {
43
- async readFile(path) {
44
- const data = await nodeFs.readFile(path);
45
- if (path.endsWith("/index") || path.endsWith(".idx")) return data;
46
- return decoder.decode(data);
47
- },
48
- async writeFile(path, data) {
49
- await nodeFs.writeFile(path, data);
50
- },
51
- async mkdir(path, options) {
52
- await nodeFs.mkdir(path, options);
53
- },
54
- async rmdir(path, options) {
55
- await nodeFs.rmdir(path, options);
56
- },
57
- async unlink(path) {
58
- await nodeFs.unlink(path);
59
- },
60
- async readdir(path) {
61
- const entries = await nodeFs.readdir(path, { withFileTypes: false });
62
- return entries.filter((e) => typeof e === "string");
63
- },
64
- async lstat(path) {
65
- return await nodeFs.lstat(path);
66
- },
67
- async stat(path) {
68
- return await nodeFs.stat(path);
69
- },
70
- async readlink(path) {
71
- return await nodeFs.readlink(path);
72
- },
73
- async symlink(target, path) {
74
- await nodeFs.symlink(target, path, "file");
75
- },
76
- async chmod(path, mode) {
77
- await nodeFs.chmod(path, mode);
78
- },
79
- async chown(path, uid, gid) {
80
- await nodeFs.chown(path, uid, gid);
81
- },
82
- async rename(oldPath, newPath) {
83
- await nodeFs.rename(oldPath, newPath);
84
- },
85
- async copyFile(srcPath, destPath) {
86
- await nodeFs.copyFile(srcPath, destPath);
87
- },
88
- async exists(path) {
89
- try {
90
- await nodeFs.stat(path);
91
- return true;
92
- } catch {
93
- return false;
94
- }
95
- }
96
- };
97
- return defaultFs;
98
- })();
99
- return fsLoading;
46
+ function getRepoDir(dirOption) {
47
+ return dirOption ?? (typeof node_process.default !== "undefined" ? node_process.default.cwd() : ".");
100
48
  }
101
49
  function createAsyncValueParser(options, metavar, parseFn, suggestFn) {
102
50
  return {
103
51
  $mode: "async",
104
52
  metavar,
105
- async parse(input) {
106
- const fs = options?.fs ?? await getDefaultFs();
107
- const dir = options?.dir ?? (typeof node_process.default !== "undefined" ? node_process.default.cwd() : ".");
53
+ parse(input) {
54
+ const dir = getRepoDir(options?.dir);
108
55
  (0, __optique_core_nonempty.ensureNonEmptyString)(metavar);
109
- return parseFn(fs, dir, input);
56
+ return parseFn(dir, input);
110
57
  },
111
58
  format(value) {
112
59
  return value;
113
60
  },
114
61
  async *suggest(prefix) {
115
- const fs = options?.fs ?? await getDefaultFs();
116
- const dir = options?.dir ?? (typeof node_process.default !== "undefined" ? node_process.default.cwd() : ".");
117
- if (suggestFn) yield* suggestFn(fs, dir, prefix);
62
+ const dir = getRepoDir(options?.dir);
63
+ if (suggestFn) yield* suggestFn(dir, prefix);
118
64
  }
119
65
  };
120
66
  }
@@ -138,10 +84,10 @@ function createAsyncValueParser(options, metavar, parseFn, suggestFn) {
138
84
  */
139
85
  function gitBranch(options) {
140
86
  const metavar = options?.metavar ?? METAVAR_BRANCH;
141
- return createAsyncValueParser(options, metavar, async (fs, dir, input) => {
87
+ return createAsyncValueParser(options, metavar, async (dir, input) => {
142
88
  try {
143
89
  const branches = await isomorphic_git.listBranches({
144
- fs,
90
+ fs: gitFs,
145
91
  dir
146
92
  });
147
93
  if (branches.includes(input)) return {
@@ -158,10 +104,10 @@ function gitBranch(options) {
158
104
  error: __optique_core_message.message`Failed to list branches. Ensure ${(0, __optique_core_message.text)(dir)} is a valid git repository.`
159
105
  };
160
106
  }
161
- }, async function* suggestBranch(fs, dir, prefix) {
107
+ }, async function* suggestBranch(dir, prefix) {
162
108
  try {
163
109
  const branches = await isomorphic_git.listBranches({
164
- fs,
110
+ fs: gitFs,
165
111
  dir
166
112
  });
167
113
  for (const branch of branches) if (branch.startsWith(prefix)) yield {
@@ -192,10 +138,10 @@ function gitBranch(options) {
192
138
  */
193
139
  function gitRemoteBranch(remote, options) {
194
140
  const metavar = options?.metavar ?? METAVAR_BRANCH;
195
- return createAsyncValueParser(options, metavar, async (fs, dir, input) => {
141
+ return createAsyncValueParser(options, metavar, async (dir, input) => {
196
142
  try {
197
143
  const branches = await isomorphic_git.listBranches({
198
- fs,
144
+ fs: gitFs,
199
145
  dir,
200
146
  remote
201
147
  });
@@ -213,10 +159,10 @@ function gitRemoteBranch(remote, options) {
213
159
  error: __optique_core_message.message`Failed to list remote branches. Ensure remote ${(0, __optique_core_message.text)(remote)} exists.`
214
160
  };
215
161
  }
216
- }, async function* suggestRemoteBranch(fs, dir, prefix) {
162
+ }, async function* suggestRemoteBranch(dir, prefix) {
217
163
  try {
218
164
  const branches = await isomorphic_git.listBranches({
219
- fs,
165
+ fs: gitFs,
220
166
  dir,
221
167
  remote
222
168
  });
@@ -230,27 +176,16 @@ function gitRemoteBranch(remote, options) {
230
176
  /**
231
177
  * Creates a value parser that validates tag names.
232
178
  *
233
- * This parser uses isomorphic-git to verify that the provided input
234
- * matches an existing tag in the repository.
235
- *
236
179
  * @param options Configuration options for the parser.
237
180
  * @returns A value parser that accepts existing tag names.
238
181
  * @since 0.9.0
239
- *
240
- * @example
241
- * ~~~~ typescript
242
- * import { gitTag } from "@optique/git";
243
- * import { option } from "@optique/core/primitives";
244
- *
245
- * const parser = option("-t", "--tag", gitTag());
246
- * ~~~~
247
182
  */
248
183
  function gitTag(options) {
249
184
  const metavar = options?.metavar ?? METAVAR_TAG;
250
- return createAsyncValueParser(options, metavar, async (fs, dir, input) => {
185
+ return createAsyncValueParser(options, metavar, async (dir, input) => {
251
186
  try {
252
187
  const tags = await isomorphic_git.listTags({
253
- fs,
188
+ fs: gitFs,
254
189
  dir
255
190
  });
256
191
  if (tags.includes(input)) return {
@@ -267,10 +202,10 @@ function gitTag(options) {
267
202
  error: __optique_core_message.message`Failed to list tags. Ensure ${(0, __optique_core_message.text)(dir)} is a valid git repository.`
268
203
  };
269
204
  }
270
- }, async function* suggestTag(fs, dir, prefix) {
205
+ }, async function* suggestTag(dir, prefix) {
271
206
  try {
272
207
  const tags = await isomorphic_git.listTags({
273
- fs,
208
+ fs: gitFs,
274
209
  dir
275
210
  });
276
211
  for (const tag of tags) if (tag.startsWith(prefix)) yield {
@@ -283,38 +218,26 @@ function gitTag(options) {
283
218
  /**
284
219
  * Creates a value parser that validates remote names.
285
220
  *
286
- * This parser uses isomorphic-git to verify that the provided input
287
- * matches an existing remote in the repository.
288
- *
289
221
  * @param options Configuration options for the parser.
290
222
  * @returns A value parser that accepts existing remote names.
291
223
  * @since 0.9.0
292
- *
293
- * @example
294
- * ~~~~ typescript
295
- * import { gitRemote } from "@optique/git";
296
- * import { option } from "@optique/core/primitives";
297
- *
298
- * const parser = option("-r", "--remote", gitRemote());
299
- * ~~~~
300
224
  */
301
225
  function gitRemote(options) {
302
226
  const metavar = options?.metavar ?? METAVAR_REMOTE;
303
- return createAsyncValueParser(options, metavar, async (fs, dir, input) => {
227
+ return createAsyncValueParser(options, metavar, async (dir, input) => {
304
228
  try {
305
229
  const remotes = await isomorphic_git.listRemotes({
306
- fs,
230
+ fs: gitFs,
307
231
  dir
308
232
  });
309
- const remoteNames = [];
310
- for (const r of remotes) if ("remote" in r && typeof r.remote === "string") remoteNames.push(r.remote);
311
- if (remoteNames.includes(input)) return {
233
+ const names = remotes.map((r) => r.remote);
234
+ if (names.includes(input)) return {
312
235
  success: true,
313
236
  value: input
314
237
  };
315
238
  return {
316
239
  success: false,
317
- error: __optique_core_message.message`Remote ${(0, __optique_core_message.text)(input)} does not exist. Available remotes: ${remoteNames.join(", ")}`
240
+ error: __optique_core_message.message`Remote ${(0, __optique_core_message.text)(input)} does not exist. Available remotes: ${names.join(", ")}`
318
241
  };
319
242
  } catch {
320
243
  return {
@@ -322,13 +245,13 @@ function gitRemote(options) {
322
245
  error: __optique_core_message.message`Failed to list remotes. Ensure ${(0, __optique_core_message.text)(dir)} is a valid git repository.`
323
246
  };
324
247
  }
325
- }, async function* suggestRemote(fs, dir, prefix) {
248
+ }, async function* suggestRemote(dir, prefix) {
326
249
  try {
327
250
  const remotes = await isomorphic_git.listRemotes({
328
- fs,
251
+ fs: gitFs,
329
252
  dir
330
253
  });
331
- for (const r of remotes) if ("remote" in r && typeof r.remote === "string" && r.remote.startsWith(prefix)) yield {
254
+ for (const r of remotes) if (r.remote.startsWith(prefix)) yield {
332
255
  kind: "literal",
333
256
  text: r.remote
334
257
  };
@@ -338,35 +261,34 @@ function gitRemote(options) {
338
261
  /**
339
262
  * Creates a value parser that validates commit SHAs.
340
263
  *
341
- * This parser uses isomorphic-git to verify that the provided input
342
- * is a valid commit SHA (full or shortened) that exists in the repository.
264
+ * This parser resolves the provided commit reference to its full 40-character
265
+ * OID.
343
266
  *
344
267
  * @param options Configuration options for the parser.
345
- * @returns A value parser that accepts valid commit SHAs.
268
+ * @returns A value parser that accepts existing commit SHAs.
346
269
  * @since 0.9.0
347
- *
348
- * @example
349
- * ~~~~ typescript
350
- * import { gitCommit } from "@optique/git";
351
- * import { option } from "@optique/core/primitives";
352
- *
353
- * const parser = option("-c", "--commit", gitCommit());
354
- * ~~~~
355
270
  */
356
271
  function gitCommit(options) {
357
272
  const metavar = options?.metavar ?? "COMMIT";
358
- return createAsyncValueParser(options, metavar, async (fs, dir, input) => {
273
+ return createAsyncValueParser(options, metavar, async (dir, input) => {
274
+ try {
275
+ (0, __optique_core_nonempty.ensureNonEmptyString)(input);
276
+ } catch {
277
+ return {
278
+ success: false,
279
+ error: __optique_core_message.message`Invalid commit SHA: ${(0, __optique_core_message.text)(input)}`
280
+ };
281
+ }
282
+ if (input.length < 4 || input.length > 40) return {
283
+ success: false,
284
+ error: __optique_core_message.message`Commit ${(0, __optique_core_message.text)(input)} must be between 4 and 40 characters.`
285
+ };
359
286
  try {
360
287
  const oid = await isomorphic_git.expandOid({
361
- fs,
288
+ fs: gitFs,
362
289
  dir,
363
290
  oid: input
364
291
  });
365
- await isomorphic_git.readObject({
366
- fs,
367
- dir,
368
- oid
369
- });
370
292
  return {
371
293
  success: true,
372
294
  value: oid
@@ -377,80 +299,63 @@ function gitCommit(options) {
377
299
  error: __optique_core_message.message`Commit ${(0, __optique_core_message.text)(input)} does not exist. Provide a valid commit SHA.`
378
300
  };
379
301
  }
380
- }, async function* suggestCommit(fs, dir, prefix) {
381
- try {
382
- const branches = await isomorphic_git.listBranches({
383
- fs,
384
- dir
385
- });
386
- const commits = [];
387
- for (const branch of branches.slice(0, 10)) try {
388
- const oid = await isomorphic_git.resolveRef({
389
- fs,
390
- dir,
391
- ref: branch
392
- });
393
- if (oid.startsWith(prefix)) commits.push(oid);
394
- } catch {}
395
- for (const commit of [...new Set(commits)].slice(0, 10)) yield {
396
- kind: "literal",
397
- text: commit
398
- };
399
- } catch {}
400
302
  });
401
303
  }
402
304
  /**
403
- * Creates a value parser that validates any git reference
404
- * (branches, tags, or commits).
305
+ * Creates a value parser that validates any git reference.
405
306
  *
406
- * This parser uses isomorphic-git to verify that the provided input
407
- * resolves to a valid git reference (branch, tag, or commit SHA).
307
+ * Accepts branch names, tag names, or commit SHAs and resolves them to the
308
+ * corresponding commit OID.
408
309
  *
409
310
  * @param options Configuration options for the parser.
410
- * @returns A value parser that accepts branches, tags, or commit SHAs.
311
+ * @returns A value parser that accepts any git reference.
411
312
  * @since 0.9.0
412
- *
413
- * @example
414
- * ~~~~ typescript
415
- * import { gitRef } from "@optique/git";
416
- * import { option } from "@optique/core/primitives";
417
- *
418
- * const parser = option("--ref", gitRef());
419
- * ~~~~
420
313
  */
421
314
  function gitRef(options) {
422
315
  const metavar = options?.metavar ?? "REF";
423
- return createAsyncValueParser(options, metavar, async (fs, dir, input) => {
316
+ return createAsyncValueParser(options, metavar, async (dir, input) => {
424
317
  try {
425
- const oid = await isomorphic_git.resolveRef({
426
- fs,
318
+ const resolved = await isomorphic_git.resolveRef({
319
+ fs: gitFs,
427
320
  dir,
428
321
  ref: input
429
322
  });
430
323
  return {
431
324
  success: true,
432
- value: oid
325
+ value: resolved
433
326
  };
434
327
  } catch {
435
- return {
436
- success: false,
437
- error: __optique_core_message.message`Reference ${(0, __optique_core_message.text)(input)} does not exist. Provide a valid branch, tag, or commit SHA.`
438
- };
328
+ try {
329
+ const oid = await isomorphic_git.expandOid({
330
+ fs: gitFs,
331
+ dir,
332
+ oid: input
333
+ });
334
+ return {
335
+ success: true,
336
+ value: oid
337
+ };
338
+ } catch {
339
+ return {
340
+ success: false,
341
+ error: __optique_core_message.message`Reference ${(0, __optique_core_message.text)(input)} does not exist. Provide a valid branch, tag, or commit SHA.`
342
+ };
343
+ }
439
344
  }
440
- }, async function* suggestRef(fs, dir, prefix) {
345
+ }, async function* suggestRef(dir, prefix) {
441
346
  try {
442
347
  const branches = await isomorphic_git.listBranches({
443
- fs,
348
+ fs: gitFs,
349
+ dir
350
+ });
351
+ const tags = await isomorphic_git.listTags({
352
+ fs: gitFs,
444
353
  dir
445
354
  });
446
355
  for (const branch of branches) if (branch.startsWith(prefix)) yield {
447
356
  kind: "literal",
448
357
  text: branch
449
358
  };
450
- const tags = await isomorphic_git.listTags({
451
- fs,
452
- dir
453
- });
454
359
  for (const tag of tags) if (tag.startsWith(prefix)) yield {
455
360
  kind: "literal",
456
361
  text: tag
@@ -459,51 +364,20 @@ function gitRef(options) {
459
364
  });
460
365
  }
461
366
  /**
462
- * Creates a factory for git parsers with shared configuration.
463
- *
464
- * This function returns an object with methods for creating individual git
465
- * parsers that share the same configuration (filesystem and directory).
367
+ * Creates a set of git parsers with shared configuration.
466
368
  *
467
- * @param options Shared configuration options for all parsers.
468
- * @returns An object with methods for creating individual git parsers.
369
+ * @param options Shared configuration for the parsers.
370
+ * @returns An object containing git parsers.
469
371
  * @since 0.9.0
470
- *
471
- * @example
472
- * ~~~~ typescript
473
- * import { createGitParsers } from "@optique/git";
474
- *
475
- * const git = createGitParsers({ dir: "/path/to/repo" });
476
- *
477
- * const branchParser = git.branch();
478
- * const tagParser = git.tag();
479
- * ~~~~
480
372
  */
481
373
  function createGitParsers(options) {
482
374
  return {
483
- branch: (parserOptions) => gitBranch({
484
- ...options,
485
- ...parserOptions
486
- }),
487
- remoteBranch: (remote, parserOptions) => gitRemoteBranch(remote, {
488
- ...options,
489
- ...parserOptions
490
- }),
491
- tag: (parserOptions) => gitTag({
492
- ...options,
493
- ...parserOptions
494
- }),
495
- remote: (parserOptions) => gitRemote({
496
- ...options,
497
- ...parserOptions
498
- }),
499
- commit: (parserOptions) => gitCommit({
500
- ...options,
501
- ...parserOptions
502
- }),
503
- ref: (parserOptions) => gitRef({
504
- ...options,
505
- ...parserOptions
506
- })
375
+ branch: (branchOptions) => gitBranch(branchOptions ?? options),
376
+ remoteBranch: (remote, branchOptions) => gitRemoteBranch(remote, branchOptions ?? options),
377
+ tag: (tagOptions) => gitTag(tagOptions ?? options),
378
+ remote: (remoteOptions) => gitRemote(remoteOptions ?? options),
379
+ commit: (commitOptions) => gitCommit(commitOptions ?? options),
380
+ ref: (refOptions) => gitRef(refOptions ?? options)
507
381
  };
508
382
  }
509
383