laufen 0.1.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/LICENSE +21 -0
- package/dist/cli-7rN5k-pl.mjs +292 -0
- package/dist/cli-7rN5k-pl.mjs.map +1 -0
- package/dist/help-DfnQJsVq.mjs +55 -0
- package/dist/help-DfnQJsVq.mjs.map +1 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.mjs +1191 -0
- package/dist/index.mjs.map +1 -0
- package/dist/json-CBMKHpNV.mjs +15 -0
- package/dist/json-CBMKHpNV.mjs.map +1 -0
- package/dist/lauf.d.mts +274 -0
- package/dist/lauf.d.mts.map +1 -0
- package/dist/lauf.mjs +56 -0
- package/dist/lauf.mjs.map +1 -0
- package/dist/runtime/executor.d.mts +1 -0
- package/dist/runtime/executor.mjs +326 -0
- package/dist/runtime/executor.mjs.map +1 -0
- package/dist/runtime/metadata.d.mts +1 -0
- package/dist/runtime/metadata.mjs +58 -0
- package/dist/runtime/metadata.mjs.map +1 -0
- package/dist/schema-CDrHGB6B.mjs +77 -0
- package/dist/schema-CDrHGB6B.mjs.map +1 -0
- package/package.json +69 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Zac Rosenbauer
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { t as safeParseJSON } from "./json-CBMKHpNV.mjs";
|
|
2
|
+
import * as path$1 from "node:path";
|
|
3
|
+
import * as p from "@clack/prompts";
|
|
4
|
+
import { InvalidParametersError, MissingRequiredFlagError, NoSuchCommandError } from "clerc";
|
|
5
|
+
import * as fs from "node:fs";
|
|
6
|
+
import { attempt, attemptAsync } from "es-toolkit";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { loadConfig } from "c12";
|
|
9
|
+
import fg from "fast-glob";
|
|
10
|
+
|
|
11
|
+
//#region src/lib/config-discovery.ts
|
|
12
|
+
/**
|
|
13
|
+
* Config file names to search for, in priority order.
|
|
14
|
+
* `lauf.config.ts` is preferred over `laufen.config.ts` in the same directory.
|
|
15
|
+
*/
|
|
16
|
+
const CONFIG_FILE_NAMES = ["lauf.config.ts", "laufen.config.ts"];
|
|
17
|
+
/**
|
|
18
|
+
* Maximum number of parent directories to traverse before giving up
|
|
19
|
+
* when no git root is found.
|
|
20
|
+
*/
|
|
21
|
+
const MAX_SEARCH_DEPTH = 20;
|
|
22
|
+
/**
|
|
23
|
+
* Detect the search boundary by walking upward from startDir.
|
|
24
|
+
*
|
|
25
|
+
* Stops at the first directory containing a `.git` entry (directory or file,
|
|
26
|
+
* supporting submodules). Falls back to a depth cap of {@link MAX_SEARCH_DEPTH}
|
|
27
|
+
* or the filesystem root, whichever comes first.
|
|
28
|
+
*/
|
|
29
|
+
function detectSearchBoundary(startDir) {
|
|
30
|
+
const walk = (dir, prevDir, depth) => {
|
|
31
|
+
if (dir === prevDir || depth >= MAX_SEARCH_DEPTH) return {
|
|
32
|
+
root: dir,
|
|
33
|
+
source: "cap"
|
|
34
|
+
};
|
|
35
|
+
if (fs.existsSync(path$1.join(dir, ".git"))) return {
|
|
36
|
+
root: dir,
|
|
37
|
+
source: "git"
|
|
38
|
+
};
|
|
39
|
+
return walk(path$1.dirname(dir), dir, depth + 1);
|
|
40
|
+
};
|
|
41
|
+
return walk(path$1.resolve(startDir), "", 0);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Collect directories from startDir up to (and including) boundaryRoot.
|
|
45
|
+
* Returns directories ordered closest-first (startDir → boundaryRoot).
|
|
46
|
+
*/
|
|
47
|
+
function collectSearchDirs(startDir, boundaryRoot) {
|
|
48
|
+
const resolvedBoundary = path$1.resolve(boundaryRoot);
|
|
49
|
+
const collect = (dir, prevDir, acc) => {
|
|
50
|
+
if (dir === prevDir) return acc;
|
|
51
|
+
const next = [...acc, dir];
|
|
52
|
+
if (dir === resolvedBoundary) return next;
|
|
53
|
+
return collect(path$1.dirname(dir), dir, next);
|
|
54
|
+
};
|
|
55
|
+
return collect(path$1.resolve(startDir), "", []);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Derive the config name ('lauf' or 'laufen') from a config filename.
|
|
59
|
+
*/
|
|
60
|
+
function configNameFromFile(fileName) {
|
|
61
|
+
if (fileName.startsWith("laufen")) return "laufen";
|
|
62
|
+
return "lauf";
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Find the closest config file by walking upward from startDir to the
|
|
66
|
+
* search boundary.
|
|
67
|
+
*
|
|
68
|
+
* At each directory, checks for `lauf.config.ts` first, then `laufen.config.ts`.
|
|
69
|
+
* Returns the first (closest) match, or `undefined` if none found.
|
|
70
|
+
*/
|
|
71
|
+
function findConfigFile(startDir) {
|
|
72
|
+
return collectSearchDirs(startDir, detectSearchBoundary(startDir).root).flatMap((dir) => CONFIG_FILE_NAMES.map((name) => ({
|
|
73
|
+
configFile: path$1.join(dir, name),
|
|
74
|
+
configDir: dir,
|
|
75
|
+
configName: configNameFromFile(name)
|
|
76
|
+
})).filter((entry) => fs.existsSync(entry.configFile)))[0];
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Discover all config files within the search boundary.
|
|
80
|
+
*
|
|
81
|
+
* Used by the `--all` flag to aggregate configs from subdirectories.
|
|
82
|
+
* Deduplicates — prefers `lauf.config.ts` over `laufen.config.ts` in the
|
|
83
|
+
* same directory. Results are sorted by path depth (shallowest first).
|
|
84
|
+
*/
|
|
85
|
+
function discoverAllConfigs(startDir) {
|
|
86
|
+
const boundary = detectSearchBoundary(startDir);
|
|
87
|
+
const grouped = fg.sync(CONFIG_FILE_NAMES.map((name) => `**/${name}`), {
|
|
88
|
+
cwd: boundary.root,
|
|
89
|
+
absolute: true,
|
|
90
|
+
onlyFiles: true,
|
|
91
|
+
dot: false,
|
|
92
|
+
ignore: ["**/node_modules/**"]
|
|
93
|
+
}).reduce((acc, filePath) => {
|
|
94
|
+
const dir = path$1.dirname(filePath);
|
|
95
|
+
const configName = configNameFromFile(path$1.basename(filePath));
|
|
96
|
+
const existing = acc[dir];
|
|
97
|
+
if (existing && existing.configName === "lauf") return acc;
|
|
98
|
+
return Object.assign(acc, { [dir]: {
|
|
99
|
+
configFile: filePath,
|
|
100
|
+
configDir: dir,
|
|
101
|
+
configName
|
|
102
|
+
} });
|
|
103
|
+
}, Object.create(null));
|
|
104
|
+
return Object.values(grouped).toSorted((a, b) => a.configDir.split(path$1.sep).length - b.configDir.split(path$1.sep).length);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
//#endregion
|
|
108
|
+
//#region src/lib/config.ts
|
|
109
|
+
/**
|
|
110
|
+
* Runtime check that a value structurally satisfies the DefaultLogger interface.
|
|
111
|
+
* Validates presence of required method names matching Logger + DefaultLogger.
|
|
112
|
+
*/
|
|
113
|
+
const loggerSchema = z.object({
|
|
114
|
+
info: z.function(),
|
|
115
|
+
warn: z.function(),
|
|
116
|
+
error: z.function(),
|
|
117
|
+
success: z.function(),
|
|
118
|
+
message: z.function(),
|
|
119
|
+
newlines: z.function()
|
|
120
|
+
}).optional();
|
|
121
|
+
/**
|
|
122
|
+
* Zod schema that validates the shape of a resolved lauf config.
|
|
123
|
+
*/
|
|
124
|
+
const resolvedLaufConfigSchema = z.object({
|
|
125
|
+
scripts: z.array(z.string()),
|
|
126
|
+
logger: loggerSchema,
|
|
127
|
+
spinner: z.boolean()
|
|
128
|
+
});
|
|
129
|
+
const DEFAULTS = {
|
|
130
|
+
scripts: ["scripts/*.ts"],
|
|
131
|
+
logger: void 0,
|
|
132
|
+
spinner: true
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Validate a raw config value against the resolved config schema.
|
|
136
|
+
*
|
|
137
|
+
* @param raw - Value returned from `c12.loadConfig`
|
|
138
|
+
* @returns Validated config or an Error
|
|
139
|
+
*/
|
|
140
|
+
function validateConfig(raw) {
|
|
141
|
+
const result = resolvedLaufConfigSchema.safeParse(raw);
|
|
142
|
+
if (!result.success) return [/* @__PURE__ */ new Error(`Invalid lauf config: ${result.error.message}`), null];
|
|
143
|
+
return [null, result.data];
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Load a config from a discovered config file via c12.
|
|
147
|
+
*/
|
|
148
|
+
async function loadConfigFromDiscovered(discovered) {
|
|
149
|
+
const loaded = await loadConfig({
|
|
150
|
+
name: discovered.configName,
|
|
151
|
+
cwd: discovered.configDir,
|
|
152
|
+
defaults: DEFAULTS
|
|
153
|
+
});
|
|
154
|
+
if (!loaded.configFile) return DEFAULTS;
|
|
155
|
+
const [error, config] = validateConfig(loaded.config);
|
|
156
|
+
if (error) {
|
|
157
|
+
p.log.warn(`Config validation failed for ${loaded.configFile}: ${error.message}. Using defaults.`);
|
|
158
|
+
return DEFAULTS;
|
|
159
|
+
}
|
|
160
|
+
return config;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Load the closest lauf config by walking upward from cwd.
|
|
164
|
+
*
|
|
165
|
+
* Uses {@link findConfigFile} to locate the nearest config file,
|
|
166
|
+
* then loads it via c12. Returns defaults when no config is found.
|
|
167
|
+
*/
|
|
168
|
+
function loadLaufConfig(cwd) {
|
|
169
|
+
const discovered = findConfigFile(cwd);
|
|
170
|
+
if (!discovered) return Promise.resolve(DEFAULTS);
|
|
171
|
+
return loadConfigFromDiscovered(discovered);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Load the closest config with metadata about where it was found.
|
|
175
|
+
*
|
|
176
|
+
* Returns the resolved config along with the config file path and directory.
|
|
177
|
+
* When no config file is found, returns defaults with `configFile: undefined`
|
|
178
|
+
* and `configDir` set to the provided cwd.
|
|
179
|
+
*/
|
|
180
|
+
async function loadLaufConfigWithMeta(cwd) {
|
|
181
|
+
const discovered = findConfigFile(cwd);
|
|
182
|
+
if (!discovered) return {
|
|
183
|
+
config: DEFAULTS,
|
|
184
|
+
configFile: void 0,
|
|
185
|
+
configDir: cwd
|
|
186
|
+
};
|
|
187
|
+
return {
|
|
188
|
+
config: await loadConfigFromDiscovered(discovered),
|
|
189
|
+
configFile: discovered.configFile,
|
|
190
|
+
configDir: discovered.configDir
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Load all configs within the search boundary.
|
|
195
|
+
*
|
|
196
|
+
* Used by the `--all` flag to aggregate configs from subdirectories.
|
|
197
|
+
* Returns at least one entry — defaults when no config files are found.
|
|
198
|
+
*/
|
|
199
|
+
function loadAllLaufConfigs(startDir) {
|
|
200
|
+
const configs = discoverAllConfigs(startDir);
|
|
201
|
+
if (configs.length === 0) return Promise.resolve([{
|
|
202
|
+
config: DEFAULTS,
|
|
203
|
+
configFile: void 0,
|
|
204
|
+
configDir: startDir
|
|
205
|
+
}]);
|
|
206
|
+
return Promise.all(configs.map(async (discovered) => {
|
|
207
|
+
return {
|
|
208
|
+
config: await loadConfigFromDiscovered(discovered),
|
|
209
|
+
configFile: discovered.configFile,
|
|
210
|
+
configDir: discovered.configDir
|
|
211
|
+
};
|
|
212
|
+
}));
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Safely load lauf config, returning an error tuple instead of throwing.
|
|
216
|
+
*/
|
|
217
|
+
async function safeLoadLaufConfig(cwd) {
|
|
218
|
+
const [error, config] = await attemptAsync(() => loadLaufConfig(cwd));
|
|
219
|
+
if (error !== null) {
|
|
220
|
+
if (error instanceof Error) return [error, null];
|
|
221
|
+
return [new Error(String(error)), null];
|
|
222
|
+
}
|
|
223
|
+
/* v8 ignore start -- TypeScript narrowing guard; attemptAsync types T | null even after error check */
|
|
224
|
+
if (config === null) return [/* @__PURE__ */ new Error("Config loading returned null unexpectedly"), null];
|
|
225
|
+
/* v8 ignore stop */
|
|
226
|
+
return [null, config];
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Safely load lauf config with metadata, returning an error tuple.
|
|
230
|
+
*/
|
|
231
|
+
async function safeLoadLaufConfigWithMeta(cwd) {
|
|
232
|
+
const [error, loaded] = await attemptAsync(() => loadLaufConfigWithMeta(cwd));
|
|
233
|
+
if (error !== null) {
|
|
234
|
+
if (error instanceof Error) return [error, null];
|
|
235
|
+
return [new Error(String(error)), null];
|
|
236
|
+
}
|
|
237
|
+
/* v8 ignore start -- TypeScript narrowing guard; attemptAsync types T | null even after error check */
|
|
238
|
+
if (loaded === null) return [/* @__PURE__ */ new Error("Config loading returned null unexpectedly"), null];
|
|
239
|
+
/* v8 ignore stop */
|
|
240
|
+
return [null, loaded];
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
//#endregion
|
|
244
|
+
//#region src/utils/cli.ts
|
|
245
|
+
/**
|
|
246
|
+
* Safely extract a human-readable message from an unknown caught value.
|
|
247
|
+
*
|
|
248
|
+
* Handles `Error` instances, plain strings, and arbitrary values
|
|
249
|
+
* without throwing.
|
|
250
|
+
*/
|
|
251
|
+
function safeParseError(err) {
|
|
252
|
+
if (err instanceof Error) return err.message;
|
|
253
|
+
if (typeof err === "string") return err;
|
|
254
|
+
return String(err);
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Format Zod validation issues into a single displayable string.
|
|
258
|
+
*
|
|
259
|
+
* @param issues - Zod issue array from a failed `safeParse`
|
|
260
|
+
* @returns Multi-line string with each issue on its own line
|
|
261
|
+
*/
|
|
262
|
+
function formatArgErrors(issues) {
|
|
263
|
+
return `Invalid arguments:\n${issues.map((issue) => ` --${issue.path.join(".")}: ${issue.message}`).join("\n")}`;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Return a contextual hint for known Clerc error types.
|
|
267
|
+
*
|
|
268
|
+
* @param err - The caught error value
|
|
269
|
+
* @returns A hint string, or `undefined` if no hint applies
|
|
270
|
+
*/
|
|
271
|
+
function errorHint(err) {
|
|
272
|
+
if (err instanceof InvalidParametersError || err instanceof MissingRequiredFlagError) return "Run `lauf <command> --help` for usage information.";
|
|
273
|
+
if (err instanceof NoSuchCommandError) return "Run `lauf --help` to see available commands.";
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Read and parse a `package.json` file from the given directory.
|
|
277
|
+
*
|
|
278
|
+
* @param dir - Absolute path to the directory containing `package.json`
|
|
279
|
+
* @returns A tuple of `[error, null]` or `[null, PackageJson]`
|
|
280
|
+
*/
|
|
281
|
+
function readPackageJSON(dir) {
|
|
282
|
+
const filePath = path$1.join(dir, "package.json");
|
|
283
|
+
const [readError, content] = attempt(() => fs.readFileSync(filePath, "utf-8"));
|
|
284
|
+
if (readError) return [readError, null];
|
|
285
|
+
const [parseError, parsed] = safeParseJSON(content);
|
|
286
|
+
if (parseError) return [parseError, null];
|
|
287
|
+
return [null, parsed];
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
//#endregion
|
|
291
|
+
export { loadAllLaufConfigs as a, findConfigFile as c, safeParseError as i, formatArgErrors as n, safeLoadLaufConfig as o, readPackageJSON as r, safeLoadLaufConfigWithMeta as s, errorHint as t };
|
|
292
|
+
//# sourceMappingURL=cli-7rN5k-pl.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-7rN5k-pl.mjs","names":["path","path"],"sources":["../src/lib/config-discovery.ts","../src/lib/config.ts","../src/utils/cli.ts"],"sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport fg from 'fast-glob';\n\n/**\n * Config file names to search for, in priority order.\n * `lauf.config.ts` is preferred over `laufen.config.ts` in the same directory.\n */\nconst CONFIG_FILE_NAMES: readonly string[] = ['lauf.config.ts', 'laufen.config.ts'];\n\n/**\n * Maximum number of parent directories to traverse before giving up\n * when no git root is found.\n */\nconst MAX_SEARCH_DEPTH = 20;\n\n/**\n * The boundary at which upward config searching stops.\n */\nexport interface ConfigSearchBoundary {\n readonly root: string;\n readonly source: 'git' | 'cap';\n}\n\n/**\n * A discovered config file with its location metadata.\n */\nexport interface DiscoveredConfig {\n readonly configFile: string;\n readonly configDir: string;\n readonly configName: 'lauf' | 'laufen';\n}\n\n/**\n * Detect the search boundary by walking upward from startDir.\n *\n * Stops at the first directory containing a `.git` entry (directory or file,\n * supporting submodules). Falls back to a depth cap of {@link MAX_SEARCH_DEPTH}\n * or the filesystem root, whichever comes first.\n */\nexport function detectSearchBoundary(startDir: string): ConfigSearchBoundary {\n const walk = (dir: string, prevDir: string, depth: number): ConfigSearchBoundary => {\n if (dir === prevDir || depth >= MAX_SEARCH_DEPTH) {\n return { root: dir, source: 'cap' };\n }\n if (fs.existsSync(path.join(dir, '.git'))) {\n return { root: dir, source: 'git' };\n }\n return walk(path.dirname(dir), dir, depth + 1);\n };\n\n return walk(path.resolve(startDir), '', 0);\n}\n\n/**\n * Collect directories from startDir up to (and including) boundaryRoot.\n * Returns directories ordered closest-first (startDir → boundaryRoot).\n */\nexport function collectSearchDirs(startDir: string, boundaryRoot: string): readonly string[] {\n const resolvedBoundary = path.resolve(boundaryRoot);\n\n const collect = (dir: string, prevDir: string, acc: readonly string[]): readonly string[] => {\n if (dir === prevDir) {\n return acc;\n }\n const next = [...acc, dir];\n if (dir === resolvedBoundary) {\n return next;\n }\n return collect(path.dirname(dir), dir, next);\n };\n\n return collect(path.resolve(startDir), '', []);\n}\n\n/**\n * Derive the config name ('lauf' or 'laufen') from a config filename.\n */\nfunction configNameFromFile(fileName: string): 'lauf' | 'laufen' {\n if (fileName.startsWith('laufen')) {\n return 'laufen';\n }\n return 'lauf';\n}\n\n/**\n * Find the closest config file by walking upward from startDir to the\n * search boundary.\n *\n * At each directory, checks for `lauf.config.ts` first, then `laufen.config.ts`.\n * Returns the first (closest) match, or `undefined` if none found.\n */\nexport function findConfigFile(startDir: string): DiscoveredConfig | undefined {\n const boundary = detectSearchBoundary(startDir);\n const dirs = collectSearchDirs(startDir, boundary.root);\n\n const candidates = dirs.flatMap((dir) =>\n CONFIG_FILE_NAMES.map(\n (name): DiscoveredConfig => ({\n configFile: path.join(dir, name),\n configDir: dir,\n configName: configNameFromFile(name),\n }),\n ).filter((entry) => fs.existsSync(entry.configFile)),\n );\n\n return candidates[0];\n}\n\n/**\n * Discover all config files within the search boundary.\n *\n * Used by the `--all` flag to aggregate configs from subdirectories.\n * Deduplicates — prefers `lauf.config.ts` over `laufen.config.ts` in the\n * same directory. Results are sorted by path depth (shallowest first).\n */\nexport function discoverAllConfigs(startDir: string): readonly DiscoveredConfig[] {\n const boundary = detectSearchBoundary(startDir);\n\n const files = fg.sync(\n CONFIG_FILE_NAMES.map((name) => `**/${name}`),\n {\n cwd: boundary.root,\n absolute: true,\n onlyFiles: true,\n dot: false,\n ignore: ['**/node_modules/**'],\n },\n );\n\n // Deduplicate: prefer lauf over laufen in the same directory.\n // Builds a record keyed by directory using Object.assign for efficiency.\n const grouped = files.reduce<Record<string, DiscoveredConfig>>(\n (acc, filePath) => {\n const dir = path.dirname(filePath);\n const fileName = path.basename(filePath);\n const configName = configNameFromFile(fileName);\n const existing = acc[dir];\n\n // Keep lauf if already found in this directory\n if (existing && existing.configName === 'lauf') {\n return acc;\n }\n\n return Object.assign(acc, { [dir]: { configFile: filePath, configDir: dir, configName } });\n },\n Object.create(null) as Record<string, DiscoveredConfig>,\n );\n\n return Object.values(grouped).toSorted(\n (a, b) => a.configDir.split(path.sep).length - b.configDir.split(path.sep).length,\n );\n}\n","import * as p from '@clack/prompts';\nimport { loadConfig } from 'c12';\nimport { attemptAsync } from 'es-toolkit';\nimport { z } from 'zod';\n\nimport type { DefaultLogger } from '../types.ts';\nimport type { DiscoveredConfig } from './config-discovery.ts';\nimport { discoverAllConfigs, findConfigFile } from './config-discovery.ts';\nimport type { Result } from './result.ts';\n\nexport interface LaufConfig {\n scripts?: string[];\n logger?: DefaultLogger;\n spinner?: boolean;\n}\n\nexport interface ResolvedLaufConfig {\n scripts: string[];\n logger: DefaultLogger | undefined;\n spinner: boolean;\n}\n\n/**\n * Metadata about a loaded config, including where it was found.\n */\nexport interface LoadedConfig {\n readonly config: ResolvedLaufConfig;\n readonly configFile: string | undefined;\n readonly configDir: string;\n}\n\n/**\n * Runtime check that a value structurally satisfies the DefaultLogger interface.\n * Validates presence of required method names matching Logger + DefaultLogger.\n */\nconst loggerSchema = z\n .object({\n info: z.function(),\n warn: z.function(),\n error: z.function(),\n success: z.function(),\n message: z.function(),\n newlines: z.function(),\n })\n .optional();\n\n/**\n * Zod schema that validates the shape of a resolved lauf config.\n */\nconst resolvedLaufConfigSchema: z.ZodType<ResolvedLaufConfig> = z.object({\n scripts: z.array(z.string()),\n logger: loggerSchema,\n spinner: z.boolean(),\n}) as z.ZodType<ResolvedLaufConfig>;\n\nconst DEFAULTS: ResolvedLaufConfig = {\n scripts: ['scripts/*.ts'],\n logger: undefined,\n spinner: true,\n};\n\n/**\n * Validate a raw config value against the resolved config schema.\n *\n * @param raw - Value returned from `c12.loadConfig`\n * @returns Validated config or an Error\n */\nfunction validateConfig(raw: unknown): Result<ResolvedLaufConfig> {\n const result = resolvedLaufConfigSchema.safeParse(raw);\n if (!result.success) {\n return [new Error(`Invalid lauf config: ${result.error.message}`), null];\n }\n return [null, result.data];\n}\n\n/**\n * Load a config from a discovered config file via c12.\n */\nasync function loadConfigFromDiscovered(discovered: DiscoveredConfig): Promise<ResolvedLaufConfig> {\n const loaded = await loadConfig<LaufConfig>({\n name: discovered.configName,\n cwd: discovered.configDir,\n defaults: DEFAULTS,\n });\n\n if (!loaded.configFile) {\n return DEFAULTS;\n }\n\n const [error, config] = validateConfig(loaded.config);\n if (error) {\n p.log.warn(\n `Config validation failed for ${loaded.configFile}: ${error.message}. Using defaults.`,\n );\n return DEFAULTS;\n }\n return config;\n}\n\n/**\n * Load the closest lauf config by walking upward from cwd.\n *\n * Uses {@link findConfigFile} to locate the nearest config file,\n * then loads it via c12. Returns defaults when no config is found.\n */\nexport function loadLaufConfig(cwd: string): Promise<ResolvedLaufConfig> {\n const discovered = findConfigFile(cwd);\n if (!discovered) {\n return Promise.resolve(DEFAULTS);\n }\n return loadConfigFromDiscovered(discovered);\n}\n\n/**\n * Load the closest config with metadata about where it was found.\n *\n * Returns the resolved config along with the config file path and directory.\n * When no config file is found, returns defaults with `configFile: undefined`\n * and `configDir` set to the provided cwd.\n */\nexport async function loadLaufConfigWithMeta(cwd: string): Promise<LoadedConfig> {\n const discovered = findConfigFile(cwd);\n if (!discovered) {\n return { config: DEFAULTS, configFile: undefined, configDir: cwd };\n }\n\n const config = await loadConfigFromDiscovered(discovered);\n return {\n config,\n configFile: discovered.configFile,\n configDir: discovered.configDir,\n };\n}\n\n/**\n * Load all configs within the search boundary.\n *\n * Used by the `--all` flag to aggregate configs from subdirectories.\n * Returns at least one entry — defaults when no config files are found.\n */\nexport function loadAllLaufConfigs(startDir: string): Promise<readonly LoadedConfig[]> {\n const configs = discoverAllConfigs(startDir);\n if (configs.length === 0) {\n return Promise.resolve([{ config: DEFAULTS, configFile: undefined, configDir: startDir }]);\n }\n\n return Promise.all(\n configs.map(async (discovered): Promise<LoadedConfig> => {\n const config = await loadConfigFromDiscovered(discovered);\n return {\n config,\n configFile: discovered.configFile,\n configDir: discovered.configDir,\n };\n }),\n );\n}\n\n/**\n * Safely load lauf config, returning an error tuple instead of throwing.\n */\nexport async function safeLoadLaufConfig(cwd: string): Promise<Result<ResolvedLaufConfig>> {\n const [error, config] = await attemptAsync(() => loadLaufConfig(cwd));\n\n if (error !== null) {\n if (error instanceof Error) {\n return [error, null];\n }\n return [new Error(String(error)), null];\n }\n\n /* v8 ignore start -- TypeScript narrowing guard; attemptAsync types T | null even after error check */\n if (config === null) {\n return [new Error('Config loading returned null unexpectedly'), null];\n }\n /* v8 ignore stop */\n\n return [null, config];\n}\n\n/**\n * Safely load lauf config with metadata, returning an error tuple.\n */\nexport async function safeLoadLaufConfigWithMeta(cwd: string): Promise<Result<LoadedConfig>> {\n const [error, loaded] = await attemptAsync(() => loadLaufConfigWithMeta(cwd));\n\n if (error !== null) {\n if (error instanceof Error) {\n return [error, null];\n }\n return [new Error(String(error)), null];\n }\n\n /* v8 ignore start -- TypeScript narrowing guard; attemptAsync types T | null even after error check */\n if (loaded === null) {\n return [new Error('Config loading returned null unexpectedly'), null];\n }\n /* v8 ignore stop */\n\n return [null, loaded];\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { InvalidParametersError, MissingRequiredFlagError, NoSuchCommandError } from 'clerc';\nimport { attempt } from 'es-toolkit';\nimport type { PackageJson } from 'type-fest';\nimport type { z } from 'zod';\n\nimport type { Result } from '../lib/result.ts';\nimport { safeParseJSON } from './json.ts';\n\n/**\n * Safely extract a human-readable message from an unknown caught value.\n *\n * Handles `Error` instances, plain strings, and arbitrary values\n * without throwing.\n */\nexport function safeParseError(err: unknown): string {\n if (err instanceof Error) {\n return err.message;\n }\n if (typeof err === 'string') {\n return err;\n }\n return String(err);\n}\n\n/**\n * Format Zod validation issues into a single displayable string.\n *\n * @param issues - Zod issue array from a failed `safeParse`\n * @returns Multi-line string with each issue on its own line\n */\nexport function formatArgErrors(issues: readonly z.ZodIssue[]): string {\n const lines = issues.map((issue) => ` --${issue.path.join('.')}: ${issue.message}`);\n return `Invalid arguments:\\n${lines.join('\\n')}`;\n}\n\n/**\n * Return a contextual hint for known Clerc error types.\n *\n * @param err - The caught error value\n * @returns A hint string, or `undefined` if no hint applies\n */\nexport function errorHint(err: unknown): string | undefined {\n if (err instanceof InvalidParametersError || err instanceof MissingRequiredFlagError) {\n return 'Run `lauf <command> --help` for usage information.';\n }\n if (err instanceof NoSuchCommandError) {\n return 'Run `lauf --help` to see available commands.';\n }\n return undefined;\n}\n\n/**\n * Read and parse a `package.json` file from the given directory.\n *\n * @param dir - Absolute path to the directory containing `package.json`\n * @returns A tuple of `[error, null]` or `[null, PackageJson]`\n */\nexport function readPackageJSON(dir: string): Result<PackageJson> {\n const filePath = path.join(dir, 'package.json');\n const [readError, content] = attempt<string, Error>(() => fs.readFileSync(filePath, 'utf-8'));\n if (readError) {\n return [readError, null];\n }\n\n const [parseError, parsed] = safeParseJSON(content);\n if (parseError) {\n return [parseError, null];\n }\n\n return [null, parsed as PackageJson];\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { n as resolveType, t as extractSchemaFields } from "./schema-CDrHGB6B.mjs";
|
|
2
|
+
import pc from "picocolors";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
//#region src/utils/help.ts
|
|
6
|
+
function formatSuffix(meta) {
|
|
7
|
+
if (meta.required) return pc.red("(required)");
|
|
8
|
+
if (meta.defaultValue !== void 0) return pc.dim(`[default: ${String(meta.defaultValue)}]`);
|
|
9
|
+
return "";
|
|
10
|
+
}
|
|
11
|
+
function formatFlag(meta, maxWidth) {
|
|
12
|
+
const padded = `--${meta.name} <${meta.type}>`.padEnd(maxWidth + 4);
|
|
13
|
+
const suffix = formatSuffix(meta);
|
|
14
|
+
if (suffix) return ` ${padded}${meta.description} ${suffix}`;
|
|
15
|
+
return ` ${padded}${meta.description}`;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Extract argument metadata from Zod arg definitions via JSON Schema introspection.
|
|
19
|
+
*
|
|
20
|
+
* Builds a `z.object()` from the arg definitions, converts to JSON Schema,
|
|
21
|
+
* and maps the schema properties into a typed `ArgMeta[]`.
|
|
22
|
+
*
|
|
23
|
+
* @param args - Zod arg definitions from the script config
|
|
24
|
+
* @returns Array of argument metadata for formatting
|
|
25
|
+
*/
|
|
26
|
+
function extractArgMeta(args) {
|
|
27
|
+
const schema = z.object(args);
|
|
28
|
+
const { properties, required } = extractSchemaFields(z.toJSONSchema(schema));
|
|
29
|
+
return Object.entries(properties).map(([name, prop]) => ({
|
|
30
|
+
name,
|
|
31
|
+
type: resolveType(prop),
|
|
32
|
+
description: prop.description || "",
|
|
33
|
+
defaultValue: prop.default,
|
|
34
|
+
required: prop.default === void 0 && required.includes(name)
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Format help output for a script's arguments.
|
|
39
|
+
*
|
|
40
|
+
* @param scriptName - The script's qualified name
|
|
41
|
+
* @param description - The script's human-readable description
|
|
42
|
+
* @param argsMeta - Extracted argument metadata
|
|
43
|
+
* @returns Formatted help string ready for `console.log`
|
|
44
|
+
*/
|
|
45
|
+
function formatHelp(scriptName, description, argsMeta) {
|
|
46
|
+
const header = `${pc.cyan(scriptName)}\n\n ${description}`;
|
|
47
|
+
if (argsMeta.length === 0) return `${header}\n\n No flags defined.`;
|
|
48
|
+
const maxFlagWidth = argsMeta.reduce((max, meta) => Math.max(max, `--${meta.name} <${meta.type}>`.length), 0);
|
|
49
|
+
const flagLines = argsMeta.map((meta) => formatFlag(meta, maxFlagWidth));
|
|
50
|
+
return `${header}\n\n${pc.bold("FLAGS:")}\n\n${flagLines.join("\n")}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
//#endregion
|
|
54
|
+
export { extractArgMeta, formatHelp };
|
|
55
|
+
//# sourceMappingURL=help-DfnQJsVq.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"help-DfnQJsVq.mjs","names":[],"sources":["../src/utils/help.ts"],"sourcesContent":["import pc from 'picocolors';\nimport { z } from 'zod';\n\nimport type { ArgDefs } from '../types.ts';\nimport { extractSchemaFields, resolveType } from './schema.ts';\n\ninterface ArgMeta {\n readonly name: string;\n readonly type: string;\n readonly description: string;\n readonly defaultValue: unknown;\n readonly required: boolean;\n}\n\nfunction formatSuffix(meta: ArgMeta): string {\n if (meta.required) {\n return pc.red('(required)');\n }\n if (meta.defaultValue !== undefined) {\n return pc.dim(`[default: ${String(meta.defaultValue)}]`);\n }\n return '';\n}\n\nfunction formatFlag(meta: ArgMeta, maxWidth: number): string {\n const flag = `--${meta.name} <${meta.type}>`;\n const padded = flag.padEnd(maxWidth + 4);\n const suffix = formatSuffix(meta);\n if (suffix) {\n return ` ${padded}${meta.description} ${suffix}`;\n }\n return ` ${padded}${meta.description}`;\n}\n\n/**\n * Extract argument metadata from Zod arg definitions via JSON Schema introspection.\n *\n * Builds a `z.object()` from the arg definitions, converts to JSON Schema,\n * and maps the schema properties into a typed `ArgMeta[]`.\n *\n * @param args - Zod arg definitions from the script config\n * @returns Array of argument metadata for formatting\n */\nexport function extractArgMeta(args: ArgDefs): readonly ArgMeta[] {\n const schema = z.object(args);\n const rawJsonSchema = z.toJSONSchema(schema);\n const { properties, required } = extractSchemaFields(rawJsonSchema);\n\n return Object.entries(properties).map(([name, prop]) => ({\n name,\n type: resolveType(prop),\n description: prop.description || '',\n defaultValue: prop.default,\n required: prop.default === undefined && required.includes(name),\n }));\n}\n\n/**\n * Format help output for a script's arguments.\n *\n * @param scriptName - The script's qualified name\n * @param description - The script's human-readable description\n * @param argsMeta - Extracted argument metadata\n * @returns Formatted help string ready for `console.log`\n */\nexport function formatHelp(\n scriptName: string,\n description: string,\n argsMeta: readonly ArgMeta[],\n): string {\n const header = `${pc.cyan(scriptName)}\\n\\n ${description}`;\n\n if (argsMeta.length === 0) {\n return `${header}\\n\\n No flags defined.`;\n }\n\n const maxFlagWidth = argsMeta.reduce(\n (max, meta) => Math.max(max, `--${meta.name} <${meta.type}>`.length),\n 0,\n );\n\n const flagLines = argsMeta.map((meta) => formatFlag(meta, maxFlagWidth));\n\n return `${header}\\n\\n${pc.bold('FLAGS:')}\\n\\n${flagLines.join('\\n')}`;\n}\n"],"mappings":";;;;;AAcA,SAAS,aAAa,MAAuB;AAC3C,KAAI,KAAK,SACP,QAAO,GAAG,IAAI,aAAa;AAE7B,KAAI,KAAK,iBAAiB,OACxB,QAAO,GAAG,IAAI,aAAa,OAAO,KAAK,aAAa,CAAC,GAAG;AAE1D,QAAO;;AAGT,SAAS,WAAW,MAAe,UAA0B;CAE3D,MAAM,SADO,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,GACtB,OAAO,WAAW,EAAE;CACxC,MAAM,SAAS,aAAa,KAAK;AACjC,KAAI,OACF,QAAO,KAAK,SAAS,KAAK,YAAY,GAAG;AAE3C,QAAO,KAAK,SAAS,KAAK;;;;;;;;;;;AAY5B,SAAgB,eAAe,MAAmC;CAChE,MAAM,SAAS,EAAE,OAAO,KAAK;CAE7B,MAAM,EAAE,YAAY,aAAa,oBADX,EAAE,aAAa,OAAO,CACuB;AAEnE,QAAO,OAAO,QAAQ,WAAW,CAAC,KAAK,CAAC,MAAM,WAAW;EACvD;EACA,MAAM,YAAY,KAAK;EACvB,aAAa,KAAK,eAAe;EACjC,cAAc,KAAK;EACnB,UAAU,KAAK,YAAY,UAAa,SAAS,SAAS,KAAK;EAChE,EAAE;;;;;;;;;;AAWL,SAAgB,WACd,YACA,aACA,UACQ;CACR,MAAM,SAAS,GAAG,GAAG,KAAK,WAAW,CAAC,QAAQ;AAE9C,KAAI,SAAS,WAAW,EACtB,QAAO,GAAG,OAAO;CAGnB,MAAM,eAAe,SAAS,QAC3B,KAAK,SAAS,KAAK,IAAI,KAAK,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,GAAG,OAAO,EACpE,EACD;CAED,MAAM,YAAY,SAAS,KAAK,SAAS,WAAW,MAAM,aAAa,CAAC;AAExE,QAAO,GAAG,OAAO,MAAM,GAAG,KAAK,SAAS,CAAC,MAAM,UAAU,KAAK,KAAK"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|