@theholocron/datapad 5.0.0-alpha.113 → 5.0.0-alpha.114
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 +17 -8
- package/dist/index.d.mts +22 -4
- package/dist/index.mjs +40 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -43,13 +43,22 @@ export const defineConfig = createDefineConfig<TasksConfig>();
|
|
|
43
43
|
|
|
44
44
|
## API
|
|
45
45
|
|
|
46
|
-
### `loadConfigFile<T>({ cwd, name, extensions? })`
|
|
47
|
-
|
|
48
|
-
Loads the first `<name>.config.<ext>` found
|
|
49
|
-
**TS-first** — `.ts` → `.js` → `.mjs` → `.cjs` → `.json` — overridable
|
|
50
|
-
`extensions`. Returns `{ config, filepath } | null`. `null` means "no
|
|
51
|
-
file"; a file that exists but cannot be parsed /
|
|
52
|
-
export throws `ConfigFileError`.
|
|
46
|
+
### `loadConfigFile<T>({ cwd, name, extensions?, walkUp? })`
|
|
47
|
+
|
|
48
|
+
Loads the first `<name>.config.<ext>` found starting at `cwd`. Probe order
|
|
49
|
+
is **TS-first** — `.ts` → `.js` → `.mjs` → `.cjs` → `.json` — overridable
|
|
50
|
+
via `extensions`. Returns `{ config, filepath } | null`. `null` means "no
|
|
51
|
+
such file anywhere searched"; a file that exists but cannot be parsed /
|
|
52
|
+
loaded / has no default export throws `ConfigFileError`.
|
|
53
|
+
|
|
54
|
+
`walkUp: true` (off by default) adds the "nearest config" search
|
|
55
|
+
`cosmiconfig`/`postcss`/`stylelint`/ESLint's flat config all do: when `cwd`
|
|
56
|
+
has no match, check its parent, then its parent's parent, and so on —
|
|
57
|
+
stopping at the first directory containing a `.git` entry (that directory
|
|
58
|
+
is still searched before stopping) or the filesystem root. Never wanders
|
|
59
|
+
past a repo boundary into an unrelated enclosing directory. `loadLayered`
|
|
60
|
+
passes its own `walkUp` through to both the dedicated-file and
|
|
61
|
+
fallback-file lookups.
|
|
53
62
|
|
|
54
63
|
### `loadConfigFromContent<T>({ dir, content, name, extension })`
|
|
55
64
|
|
|
@@ -67,7 +76,7 @@ Built for `@theholocron/sentinel`'s `validateConfig()` — it fetches a
|
|
|
67
76
|
repo's `holocron.config.*` from GitHub's API, not a local checkout, but
|
|
68
77
|
still needs the fetched content to execute as a real module.
|
|
69
78
|
|
|
70
|
-
### `loadLayered<T>({ cwd, name, fallback?, extensions? })`
|
|
79
|
+
### `loadLayered<T>({ cwd, name, fallback?, extensions?, walkUp? })`
|
|
71
80
|
|
|
72
81
|
`<name>.config.*` layered over the `[fallback.key]` of
|
|
73
82
|
`<fallback.file>.config.*`. Either, both, or neither may be present; the
|
package/dist/index.d.mts
CHANGED
|
@@ -39,16 +39,32 @@ declare class ConfigFileError extends Error {
|
|
|
39
39
|
* Probe order is **TS-first**: `.ts` → `.js` → `.mjs` → `.cjs` → `.json`.
|
|
40
40
|
* TS is loaded through `tsx`'s `tsImport` (a runtime dependency) so a
|
|
41
41
|
* typed `defineConfig` file works with no build step.
|
|
42
|
+
*
|
|
43
|
+
* `walkUp` (off by default) adds the "nearest config" search
|
|
44
|
+
* cosmiconfig/postcss/stylelint/ESLint's flat config all do: when `cwd`
|
|
45
|
+
* itself has no match, check its parent, then its parent's parent, and so
|
|
46
|
+
* on — stopping at the first directory containing a `.git` entry (checked
|
|
47
|
+
* after that directory's own config probe, so the project root itself is
|
|
48
|
+
* still searched) or the filesystem root. Never wanders past a repo
|
|
49
|
+
* boundary into an unrelated enclosing directory.
|
|
42
50
|
*/
|
|
43
51
|
/** Default extension probe order, highest priority first. */
|
|
44
52
|
declare const DEFAULT_EXTENSIONS: readonly ["ts", "js", "mjs", "cjs", "json"];
|
|
45
53
|
interface LoadConfigFileOptions {
|
|
46
|
-
/** Directory to
|
|
54
|
+
/** Directory to start looking in. */
|
|
47
55
|
cwd: string;
|
|
48
56
|
/** Base name — `"holocron"` resolves `holocron.config.{ts,js,mjs,cjs,json}`. */
|
|
49
57
|
name: string;
|
|
50
58
|
/** Override the extension probe order. */
|
|
51
59
|
extensions?: readonly string[];
|
|
60
|
+
/**
|
|
61
|
+
* Walk upward through ancestor directories when `cwd` itself has no
|
|
62
|
+
* match, stopping at the first directory containing a `.git` entry (that
|
|
63
|
+
* directory is still searched before stopping) or the filesystem root.
|
|
64
|
+
* Off by default — exact-`cwd`-only, unchanged behavior for any existing
|
|
65
|
+
* caller that doesn't pass this.
|
|
66
|
+
*/
|
|
67
|
+
walkUp?: boolean;
|
|
52
68
|
}
|
|
53
69
|
interface Loaded<T> {
|
|
54
70
|
config: T;
|
|
@@ -56,9 +72,11 @@ interface Loaded<T> {
|
|
|
56
72
|
filepath: string;
|
|
57
73
|
}
|
|
58
74
|
/**
|
|
59
|
-
* Load the first `<name>.config.<ext>`
|
|
60
|
-
* `
|
|
61
|
-
*
|
|
75
|
+
* Load the first `<name>.config.<ext>` found starting at `cwd` — just
|
|
76
|
+
* `cwd` itself by default, or walking up through ancestor directories when
|
|
77
|
+
* `walkUp` is set (see the module doc comment for the stop conditions).
|
|
78
|
+
* Returns `null` when none is present anywhere searched; throws
|
|
79
|
+
* {@link ConfigFileError} when a file exists but cannot be loaded.
|
|
62
80
|
*/
|
|
63
81
|
declare function loadConfigFile<T>(opts: LoadConfigFileOptions): Promise<Loaded<T> | null>;
|
|
64
82
|
interface LoadLayeredOptions extends LoadConfigFileOptions {
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readFile, stat, writeFile } from "node:fs/promises";
|
|
2
|
-
import { join } from "node:path";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
3
|
import { pathToFileURL } from "node:url";
|
|
4
4
|
//#region src/define.ts
|
|
5
5
|
/**
|
|
@@ -73,6 +73,14 @@ function isPlainObject(value) {
|
|
|
73
73
|
* Probe order is **TS-first**: `.ts` → `.js` → `.mjs` → `.cjs` → `.json`.
|
|
74
74
|
* TS is loaded through `tsx`'s `tsImport` (a runtime dependency) so a
|
|
75
75
|
* typed `defineConfig` file works with no build step.
|
|
76
|
+
*
|
|
77
|
+
* `walkUp` (off by default) adds the "nearest config" search
|
|
78
|
+
* cosmiconfig/postcss/stylelint/ESLint's flat config all do: when `cwd`
|
|
79
|
+
* itself has no match, check its parent, then its parent's parent, and so
|
|
80
|
+
* on — stopping at the first directory containing a `.git` entry (checked
|
|
81
|
+
* after that directory's own config probe, so the project root itself is
|
|
82
|
+
* still searched) or the filesystem root. Never wanders past a repo
|
|
83
|
+
* boundary into an unrelated enclosing directory.
|
|
76
84
|
*/
|
|
77
85
|
/** Default extension probe order, highest priority first. */
|
|
78
86
|
const DEFAULT_EXTENSIONS = [
|
|
@@ -83,21 +91,30 @@ const DEFAULT_EXTENSIONS = [
|
|
|
83
91
|
"json"
|
|
84
92
|
];
|
|
85
93
|
/**
|
|
86
|
-
* Load the first `<name>.config.<ext>`
|
|
87
|
-
* `
|
|
88
|
-
*
|
|
94
|
+
* Load the first `<name>.config.<ext>` found starting at `cwd` — just
|
|
95
|
+
* `cwd` itself by default, or walking up through ancestor directories when
|
|
96
|
+
* `walkUp` is set (see the module doc comment for the stop conditions).
|
|
97
|
+
* Returns `null` when none is present anywhere searched; throws
|
|
98
|
+
* {@link ConfigFileError} when a file exists but cannot be loaded.
|
|
89
99
|
*/
|
|
90
100
|
async function loadConfigFile(opts) {
|
|
91
101
|
const extensions = opts.extensions ?? DEFAULT_EXTENSIONS;
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
102
|
+
let dir = opts.cwd;
|
|
103
|
+
for (;;) {
|
|
104
|
+
for (const ext of extensions) {
|
|
105
|
+
const filepath = join(dir, `${opts.name}.config.${ext}`);
|
|
106
|
+
if (!await isFile(filepath)) continue;
|
|
107
|
+
return {
|
|
108
|
+
config: await loadFile(filepath, ext),
|
|
109
|
+
filepath
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if (!opts.walkUp) return null;
|
|
113
|
+
if (await hasGitEntry(dir)) return null;
|
|
114
|
+
const parent = dirname(dir);
|
|
115
|
+
if (parent === dir) return null;
|
|
116
|
+
dir = parent;
|
|
99
117
|
}
|
|
100
|
-
return null;
|
|
101
118
|
}
|
|
102
119
|
/**
|
|
103
120
|
* `<name>.config.*` layered over the `[fallback.key]` of
|
|
@@ -113,7 +130,8 @@ async function loadLayered(opts) {
|
|
|
113
130
|
const parent = await loadConfigFile({
|
|
114
131
|
cwd: opts.cwd,
|
|
115
132
|
name: opts.fallback.file,
|
|
116
|
-
extensions: opts.extensions
|
|
133
|
+
extensions: opts.extensions,
|
|
134
|
+
walkUp: opts.walkUp
|
|
117
135
|
});
|
|
118
136
|
if (parent && parent.config[opts.fallback.key] !== void 0) {
|
|
119
137
|
base = parent.config[opts.fallback.key];
|
|
@@ -206,6 +224,15 @@ async function isFile(path) {
|
|
|
206
224
|
return false;
|
|
207
225
|
}
|
|
208
226
|
}
|
|
227
|
+
/** `.git` is a directory in a normal checkout, a file in a worktree/submodule — existence is all that matters. */
|
|
228
|
+
async function hasGitEntry(dir) {
|
|
229
|
+
try {
|
|
230
|
+
await stat(join(dir, ".git"));
|
|
231
|
+
return true;
|
|
232
|
+
} catch {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
209
236
|
const message = (err) => err instanceof Error ? err.message : String(err);
|
|
210
237
|
//#endregion
|
|
211
238
|
export { ConfigFileError, DEFAULT_EXTENSIONS, createDefineConfig, loadConfigFile, loadConfigFromContent, loadLayered, mergeConfig };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theholocron/datapad",
|
|
3
|
-
"version": "5.0.0-alpha.
|
|
3
|
+
"version": "5.0.0-alpha.114",
|
|
4
4
|
"description": "Generic config-file loading for Holocron — discover, load (JSON/JS/TS/ESM/CJS), and merge <name>.config.* files with a typed defineConfig.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"config",
|