just-bash-util 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,11 +1,14 @@
1
1
  # just-bash-util
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/just-bash-util)](https://www.npmjs.com/package/just-bash-util)
4
+ [![GitHub](https://img.shields.io/github/license/blindmansion/just-bash-util)](https://github.com/blindmansion/just-bash-util)
5
+
3
6
  CLI command framework, config file discovery, and path utilities for [just-bash](https://www.npmjs.com/package/just-bash).
4
7
 
5
8
  ## Install
6
9
 
7
10
  ```bash
8
- bun add just-bash-util just-bash
11
+ npm install just-bash-util just-bash
9
12
  ```
10
13
 
11
14
  ## Modules
@@ -64,6 +67,7 @@ await serve.invoke({ port: 8080, entry: "app.ts" }, ctx);
64
67
  - `--` passthrough separator
65
68
  - Environment variable fallbacks for options
66
69
  - Levenshtein-based "did you mean?" suggestions for typos
70
+ - Automatic error handling — thrown errors in handlers are caught and returned as clean `ExecResult` with `exitCode: 1`
67
71
 
68
72
  ### `just-bash-util/config` — Config file discovery
69
73
 
@@ -72,7 +76,7 @@ Cosmiconfig-style config search that walks up the directory tree, trying convent
72
76
  ```ts
73
77
  import { searchConfig } from "just-bash-util/config";
74
78
 
75
- // Walks up from cwd trying: package.json#myapp, .myapprc, .myapprc.json, myapp.config.json
79
+ // Walks up from cwd trying: .myapprc, .myapprc.json, myapp.config.json
76
80
  const result = await searchConfig(ctx, { name: "myapp" });
77
81
  if (result) {
78
82
  result.config; // parsed config object
@@ -80,11 +84,14 @@ if (result) {
80
84
  result.isEmpty; // true if config is null/undefined/empty object
81
85
  }
82
86
 
83
- // Customize search places, starting directory, or add custom loaders
87
+ // Find nearest package.json and return its full contents
88
+ const pkg = await searchConfig(ctx, { name: "package", searchPlaces: ["package.json"] });
89
+
90
+ // Extract a tool-specific property from package.json
84
91
  const result2 = await searchConfig(ctx, {
85
92
  name: "myapp",
86
- from: "/specific/start/dir",
87
- searchPlaces: [".myapprc.json", "myapp.config.json"],
93
+ searchPlaces: ["package.json", ".myapprc", ".myapprc.json"],
94
+ packageJsonProp: "myapp",
88
95
  });
89
96
  ```
90
97
 
@@ -123,12 +130,16 @@ import {
123
130
  relative,
124
131
  parse,
125
132
  normalize,
133
+ parsePackageSpecifier,
126
134
  } from "just-bash-util/path";
127
135
 
128
136
  join("src", "utils", "index.ts"); // "src/utils/index.ts"
129
137
  dirname("/project/src/index.ts"); // "/project/src"
130
138
  basename("src/index.ts", ".ts"); // "index"
131
139
  relative("/a/b/c", "/a/d"); // "../../d"
140
+
141
+ parsePackageSpecifier("@vue/shared/dist"); // { name: "@vue/shared", subpath: "./dist" }
142
+ parsePackageSpecifier("lodash/merge"); // { name: "lodash", subpath: "./merge" }
132
143
  ```
133
144
 
134
145
  ## Peer dependencies
@@ -3,7 +3,7 @@ import {
3
3
  dirname,
4
4
  extname,
5
5
  join
6
- } from "./chunk-DAO5RF73.js";
6
+ } from "./chunk-HJ3ZU6QO.js";
7
7
 
8
8
  // src/config/find-up.ts
9
9
  async function findUp(ctx, name, options) {
@@ -88,7 +88,6 @@ function deepMerge(base, override) {
88
88
  var defaultLoader = (content) => parseJsonc(content);
89
89
  function defaultSearchPlaces(name) {
90
90
  return [
91
- "package.json",
92
91
  `.${name}rc`,
93
92
  `.${name}rc.json`,
94
93
  `${name}.config.json`
@@ -133,7 +132,7 @@ async function collectAll(ctx, options) {
133
132
  from = ctx.cwd,
134
133
  searchPlaces = defaultSearchPlaces(name),
135
134
  loaders: customLoaders,
136
- packageJsonProp = name,
135
+ packageJsonProp = false,
137
136
  stopAt = "/",
138
137
  stopWhen
139
138
  } = options;
@@ -184,7 +183,7 @@ async function searchConfig(ctx, options) {
184
183
  from = ctx.cwd,
185
184
  searchPlaces = defaultSearchPlaces(name),
186
185
  loaders: customLoaders,
187
- packageJsonProp = name,
186
+ packageJsonProp = false,
188
187
  stopAt = "/"
189
188
  } = options;
190
189
  let dir = from;
@@ -115,6 +115,30 @@ function format(pathObject) {
115
115
  }
116
116
  return `${root}${resolvedBase}`;
117
117
  }
118
+ function parsePackageSpecifier(specifier) {
119
+ if (specifier.startsWith("@")) {
120
+ const firstSlash2 = specifier.indexOf("/");
121
+ if (firstSlash2 === -1) {
122
+ return { name: specifier, subpath: "." };
123
+ }
124
+ const secondSlash = specifier.indexOf("/", firstSlash2 + 1);
125
+ if (secondSlash === -1) {
126
+ return { name: specifier, subpath: "." };
127
+ }
128
+ return {
129
+ name: specifier.slice(0, secondSlash),
130
+ subpath: `.${specifier.slice(secondSlash)}`
131
+ };
132
+ }
133
+ const firstSlash = specifier.indexOf("/");
134
+ if (firstSlash === -1) {
135
+ return { name: specifier, subpath: "." };
136
+ }
137
+ return {
138
+ name: specifier.slice(0, firstSlash),
139
+ subpath: `.${specifier.slice(firstSlash)}`
140
+ };
141
+ }
118
142
  function relative(from, to) {
119
143
  const fromNorm = normalize(from);
120
144
  const toNorm = normalize(to);
@@ -149,5 +173,6 @@ export {
149
173
  extname,
150
174
  parse,
151
175
  format,
176
+ parsePackageSpecifier,
152
177
  relative
153
178
  };
@@ -720,7 +720,12 @@ var Command = class _Command {
720
720
  }
721
721
  }
722
722
  }
723
- return this.handler(resolved, ctx, { passthrough: [] });
723
+ try {
724
+ return await this.handler(resolved, ctx, { passthrough: [] });
725
+ } catch (err) {
726
+ const message = err instanceof Error ? err.message : String(err);
727
+ return { stdout: "", stderr: message, exitCode: 1 };
728
+ }
724
729
  }
725
730
  // --------------------------------------------------------------------------
726
731
  // Execution
@@ -746,7 +751,12 @@ var Command = class _Command {
746
751
  if (!parsed.ok) {
747
752
  return { stdout: "", stderr: formatErrors(parsed.errors), exitCode: 1 };
748
753
  }
749
- return this.handler(parsed.args, ctx, { passthrough: parsed.passthrough });
754
+ try {
755
+ return await this.handler(parsed.args, ctx, { passthrough: parsed.passthrough });
756
+ } catch (err) {
757
+ const message = err instanceof Error ? err.message : String(err);
758
+ return { stdout: "", stderr: message, exitCode: 1 };
759
+ }
750
760
  }
751
761
  if (firstToken && !firstToken.startsWith("-")) {
752
762
  const suggestions = findSuggestions(firstToken, [...this.children.keys()]);
@@ -8,7 +8,7 @@ import {
8
8
  generateHelp,
9
9
  o,
10
10
  parseArgs
11
- } from "../chunk-OLDCCFR6.js";
11
+ } from "../chunk-QKL3AOEA.js";
12
12
  export {
13
13
  Command,
14
14
  a,
@@ -40,7 +40,6 @@ interface SearchConfigOptions {
40
40
  * Filenames to look for at each directory level, tried in order.
41
41
  *
42
42
  * Defaults:
43
- * - `"package.json"` (extracts the `name` property)
44
43
  * - `".{name}rc"`
45
44
  * - `".{name}rc.json"`
46
45
  * - `"{name}.config.json"`
@@ -54,7 +53,7 @@ interface SearchConfigOptions {
54
53
  /**
55
54
  * Property to extract from `package.json`.
56
55
  * Set to `false` to disable property extraction (load full object).
57
- * Default: the `name` option.
56
+ * Default: `false`.
58
57
  */
59
58
  packageJsonProp?: string | false;
60
59
  /** Directory to stop searching at (default: `"/"`) */
@@ -105,6 +104,16 @@ interface LoadConfigOptions {
105
104
  * const result = await searchConfig(ctx, { name: "myapp" });
106
105
  * if (result) console.log(result.config, result.filepath);
107
106
  *
107
+ * // Find nearest package.json and return its full contents
108
+ * const pkg = await searchConfig(ctx, { name: "package", searchPlaces: ["package.json"] });
109
+ *
110
+ * // Extract a specific property from package.json
111
+ * const result = await searchConfig(ctx, {
112
+ * name: "myapp",
113
+ * searchPlaces: ["package.json", ".myapprc", ".myapprc.json"],
114
+ * packageJsonProp: "myapp",
115
+ * });
116
+ *
108
117
  * // Layered / cascading config
109
118
  * const merged = await searchConfig(ctx, { name: "myapp", merge: true });
110
119
  * ```
@@ -2,8 +2,8 @@ import {
2
2
  findUp,
3
3
  loadConfig,
4
4
  searchConfig
5
- } from "../chunk-DLIJNNDI.js";
6
- import "../chunk-DAO5RF73.js";
5
+ } from "../chunk-FUVD54LW.js";
6
+ import "../chunk-HJ3ZU6QO.js";
7
7
  export {
8
8
  findUp,
9
9
  loadConfig,
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { Command, Handler, Infer, ParseArgsResult, ParseError, a, command, f, formatError, formatErrors, generateHelp, o, parseArgs } from './command/index.js';
2
2
  export { ConfigResult, FindUpOptions, LoadConfigOptions, Loader, SearchConfigOptions, findUp, loadConfig, searchConfig } from './config/index.js';
3
- export { ParsedPath, basename, delimiter, dirname, extname, format, isAbsolute, join, normalize, parse, relative, resolve, sep } from './path/index.js';
3
+ export { PackageSpecifier, ParsedPath, basename, delimiter, dirname, extname, format, isAbsolute, join, normalize, parse, parsePackageSpecifier, relative, resolve, sep } from './path/index.js';
4
4
  import 'just-bash';
package/dist/index.js CHANGED
@@ -8,12 +8,12 @@ import {
8
8
  generateHelp,
9
9
  o,
10
10
  parseArgs
11
- } from "./chunk-OLDCCFR6.js";
11
+ } from "./chunk-QKL3AOEA.js";
12
12
  import {
13
13
  findUp,
14
14
  loadConfig,
15
15
  searchConfig
16
- } from "./chunk-DLIJNNDI.js";
16
+ } from "./chunk-FUVD54LW.js";
17
17
  import {
18
18
  basename,
19
19
  delimiter,
@@ -24,10 +24,11 @@ import {
24
24
  join,
25
25
  normalize,
26
26
  parse,
27
+ parsePackageSpecifier,
27
28
  relative,
28
29
  resolve,
29
30
  sep
30
- } from "./chunk-DAO5RF73.js";
31
+ } from "./chunk-HJ3ZU6QO.js";
31
32
  export {
32
33
  Command,
33
34
  a,
@@ -49,6 +50,7 @@ export {
49
50
  o,
50
51
  parse,
51
52
  parseArgs,
53
+ parsePackageSpecifier,
52
54
  relative,
53
55
  resolve,
54
56
  searchConfig,
@@ -33,7 +33,26 @@ interface ParsedPath {
33
33
  declare function parse(path: string): ParsedPath;
34
34
  /** Build a path string from components (inverse of `parse`) */
35
35
  declare function format(pathObject: Partial<ParsedPath>): string;
36
+ interface PackageSpecifier {
37
+ /** Package name (e.g. `"pkg"` or `"@scope/pkg"`) */
38
+ name: string;
39
+ /** Subpath within the package (e.g. `"./sub/path"`), or `"."` for the root */
40
+ subpath: string;
41
+ }
42
+ /**
43
+ * Parse a bare package specifier into its package name and subpath.
44
+ *
45
+ * Handles both scoped (`@scope/pkg/sub`) and unscoped (`pkg/sub`) specifiers.
46
+ * The subpath is normalized to start with `"./"` (or `"."` for the root).
47
+ *
48
+ * ```ts
49
+ * parsePackageSpecifier("lodash/merge") // { name: "lodash", subpath: "./merge" }
50
+ * parsePackageSpecifier("@vue/shared/dist") // { name: "@vue/shared", subpath: "./dist" }
51
+ * parsePackageSpecifier("react") // { name: "react", subpath: "." }
52
+ * ```
53
+ */
54
+ declare function parsePackageSpecifier(specifier: string): PackageSpecifier;
36
55
  /** Compute the relative path from `from` to `to` */
37
56
  declare function relative(from: string, to: string): string;
38
57
 
39
- export { type ParsedPath, basename, delimiter, dirname, extname, format, isAbsolute, join, normalize, parse, relative, resolve, sep };
58
+ export { type PackageSpecifier, type ParsedPath, basename, delimiter, dirname, extname, format, isAbsolute, join, normalize, parse, parsePackageSpecifier, relative, resolve, sep };
@@ -8,10 +8,11 @@ import {
8
8
  join,
9
9
  normalize,
10
10
  parse,
11
+ parsePackageSpecifier,
11
12
  relative,
12
13
  resolve,
13
14
  sep
14
- } from "../chunk-DAO5RF73.js";
15
+ } from "../chunk-HJ3ZU6QO.js";
15
16
  export {
16
17
  basename,
17
18
  delimiter,
@@ -22,6 +23,7 @@ export {
22
23
  join,
23
24
  normalize,
24
25
  parse,
26
+ parsePackageSpecifier,
25
27
  relative,
26
28
  resolve,
27
29
  sep
package/package.json CHANGED
@@ -1,10 +1,18 @@
1
1
  {
2
2
  "name": "just-bash-util",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "CLI command framework, config file discovery, and path utilities for just-bash",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": "blindmansion",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/blindmansion/just-bash-util.git"
11
+ },
12
+ "homepage": "https://github.com/blindmansion/just-bash-util",
13
+ "bugs": {
14
+ "url": "https://github.com/blindmansion/just-bash-util/issues"
15
+ },
8
16
  "keywords": [
9
17
  "cli",
10
18
  "command",