@theholocron/datapad 3.64.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/README.md +101 -0
- package/dist/index.d.mts +101 -0
- package/dist/index.mjs +191 -0
- package/package.json +66 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Newton Koumantzelis
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# `@theholocron/datapad`
|
|
2
|
+
|
|
3
|
+
Generic config-file loading for Holocron. Discover `<name>.config.*`, load
|
|
4
|
+
it (JSON / JS / TS / ESM / CJS — typed configs via [`tsx`](https://tsx.is),
|
|
5
|
+
no build step), layer a dedicated file over a key of a parent file,
|
|
6
|
+
deep-merge, and hand back a plain object.
|
|
7
|
+
|
|
8
|
+
Holocron-agnostic: no schema, no validation, no defaults. Consumers
|
|
9
|
+
(`@theholocron/cli` for `holocron.config.*`, `@theholocron/astromech` for
|
|
10
|
+
`astromech.config.*`) build those on top.
|
|
11
|
+
|
|
12
|
+
> A datapad is a handheld device for reading and holding data. This reads
|
|
13
|
+
> and holds config files.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
pnpm add @theholocron/datapad
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { loadConfigFile, loadLayered, mergeConfig, createDefineConfig } from "@theholocron/datapad";
|
|
25
|
+
|
|
26
|
+
// one file: holocron.config.{ts,js,mjs,cjs,json}
|
|
27
|
+
const found = await loadConfigFile<MyConfig>({ cwd, name: "holocron" });
|
|
28
|
+
// → { config, filepath } | null
|
|
29
|
+
|
|
30
|
+
// a dedicated file layered over the `tasks` key of a parent file
|
|
31
|
+
const layered = await loadLayered<TasksConfig>({
|
|
32
|
+
cwd,
|
|
33
|
+
name: "astromech",
|
|
34
|
+
fallback: { file: "holocron", key: "tasks" },
|
|
35
|
+
});
|
|
36
|
+
// → { config, filepath, sources } | null (dedicated wins on conflict)
|
|
37
|
+
|
|
38
|
+
const merged = mergeConfig(defaults, layered?.config); // vite-style deep merge
|
|
39
|
+
|
|
40
|
+
// a typed identity defineConfig for a specific shape
|
|
41
|
+
export const defineConfig = createDefineConfig<TasksConfig>();
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## API
|
|
45
|
+
|
|
46
|
+
### `loadConfigFile<T>({ cwd, name, extensions? })`
|
|
47
|
+
|
|
48
|
+
Loads the first `<name>.config.<ext>` found in `cwd`. Probe order is
|
|
49
|
+
**TS-first** — `.ts` → `.js` → `.mjs` → `.cjs` → `.json` — overridable via
|
|
50
|
+
`extensions`. Returns `{ config, filepath } | null`. `null` means "no such
|
|
51
|
+
file"; a file that exists but cannot be parsed / loaded / has no default
|
|
52
|
+
export throws `ConfigFileError`.
|
|
53
|
+
|
|
54
|
+
### `loadLayered<T>({ cwd, name, fallback?, extensions? })`
|
|
55
|
+
|
|
56
|
+
`<name>.config.*` layered over the `[fallback.key]` of
|
|
57
|
+
`<fallback.file>.config.*`. Either, both, or neither may be present; the
|
|
58
|
+
dedicated file wins on conflict (`vitest`-over-`vite`). Returns
|
|
59
|
+
`{ config, filepath, sources } | null` — `sources` lists the absolute
|
|
60
|
+
paths actually merged, lowest priority first.
|
|
61
|
+
|
|
62
|
+
### `mergeConfig<T>(base, override)`
|
|
63
|
+
|
|
64
|
+
Deep-merge, `vite`-style: plain objects merge recursively, arrays
|
|
65
|
+
concatenate (`base` first), `undefined` in `override` is skipped, every
|
|
66
|
+
other `override` value replaces `base`. Neither input is mutated.
|
|
67
|
+
|
|
68
|
+
### `createDefineConfig<T>()`
|
|
69
|
+
|
|
70
|
+
Returns a typed identity function — call once per config shape and
|
|
71
|
+
re-export it so config files get autocomplete with zero runtime cost.
|
|
72
|
+
|
|
73
|
+
### `ConfigFileError`
|
|
74
|
+
|
|
75
|
+
Thrown when a file is found but unusable. Carries the offending
|
|
76
|
+
`filepath`. "Not found" is `null`, never an error.
|
|
77
|
+
|
|
78
|
+
## TypeScript configs
|
|
79
|
+
|
|
80
|
+
`.ts` files load through `tsx`'s ESM loader, registered once per process
|
|
81
|
+
on first use. A single run may load several TS configs (`loadLayered`
|
|
82
|
+
reads a dedicated file _and_ a parent file), so the persistent
|
|
83
|
+
`register()` is used rather than the one-off `tsImport()`.
|
|
84
|
+
|
|
85
|
+
## Development
|
|
86
|
+
|
|
87
|
+
| Script | Description |
|
|
88
|
+
| -------------------- | ----------------------- |
|
|
89
|
+
| `pnpm build` | Bundle with tsdown |
|
|
90
|
+
| `pnpm test` | Run the vitest suite |
|
|
91
|
+
| `pnpm test:coverage` | Run tests with coverage |
|
|
92
|
+
| `pnpm typecheck` | `tsc --noEmit` |
|
|
93
|
+
| `pnpm lint` | ESLint |
|
|
94
|
+
|
|
95
|
+
## Releases
|
|
96
|
+
|
|
97
|
+
Automated via semantic-release. See [CHANGELOG.md](../../CHANGELOG.md).
|
|
98
|
+
|
|
99
|
+
## Documentation
|
|
100
|
+
|
|
101
|
+
<https://theholocron.github.io/holocron/>
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
//#region src/define.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Build a typed identity `defineConfig` for a specific config shape.
|
|
4
|
+
*
|
|
5
|
+
* Each consumer (`@theholocron/cli`, `@theholocron/astromech`) calls this
|
|
6
|
+
* once with its own config type and re-exports the result, so config
|
|
7
|
+
* files get autocomplete and type-checking with no runtime cost:
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* // @theholocron/astromech/config
|
|
11
|
+
* export const defineConfig = createDefineConfig<TasksConfig>();
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
declare function createDefineConfig<T>(): <C extends T>(config: C) => C;
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/errors.d.ts
|
|
17
|
+
/**
|
|
18
|
+
* Thrown when a config file is found but cannot be loaded — malformed
|
|
19
|
+
* JSON, a syntax error in a `.ts` / `.js` module, or no usable default
|
|
20
|
+
* export. "Not found" is never an error: {@link loadConfigFile} returns
|
|
21
|
+
* `null` for that.
|
|
22
|
+
*/
|
|
23
|
+
declare class ConfigFileError extends Error {
|
|
24
|
+
/** Absolute path of the offending file, when known. */
|
|
25
|
+
readonly filepath?: string | undefined;
|
|
26
|
+
name: string;
|
|
27
|
+
constructor(message: string,
|
|
28
|
+
/** Absolute path of the offending file, when known. */
|
|
29
|
+
filepath?: string | undefined);
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region src/load.d.ts
|
|
33
|
+
/**
|
|
34
|
+
* Discover and load `<name>.config.*` files. Holocron-agnostic — no
|
|
35
|
+
* schema, no validation, no defaults. Consumers layer those on top.
|
|
36
|
+
*
|
|
37
|
+
* Probe order is **TS-first**: `.ts` → `.js` → `.mjs` → `.cjs` → `.json`.
|
|
38
|
+
* TS is loaded through `tsx`'s `tsImport` (a runtime dependency) so a
|
|
39
|
+
* typed `defineConfig` file works with no build step.
|
|
40
|
+
*/
|
|
41
|
+
/** Default extension probe order, highest priority first. */
|
|
42
|
+
declare const DEFAULT_EXTENSIONS: readonly ["ts", "js", "mjs", "cjs", "json"];
|
|
43
|
+
interface LoadConfigFileOptions {
|
|
44
|
+
/** Directory to look in. */
|
|
45
|
+
cwd: string;
|
|
46
|
+
/** Base name — `"holocron"` resolves `holocron.config.{ts,js,mjs,cjs,json}`. */
|
|
47
|
+
name: string;
|
|
48
|
+
/** Override the extension probe order. */
|
|
49
|
+
extensions?: readonly string[];
|
|
50
|
+
}
|
|
51
|
+
interface Loaded<T> {
|
|
52
|
+
config: T;
|
|
53
|
+
/** Absolute path of the file the value came from. */
|
|
54
|
+
filepath: string;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Load the first `<name>.config.<ext>` that exists in `cwd`. Returns
|
|
58
|
+
* `null` when none is present; throws {@link ConfigFileError} when a file
|
|
59
|
+
* exists but cannot be loaded.
|
|
60
|
+
*/
|
|
61
|
+
declare function loadConfigFile<T>(opts: LoadConfigFileOptions): Promise<Loaded<T> | null>;
|
|
62
|
+
interface LoadLayeredOptions extends LoadConfigFileOptions {
|
|
63
|
+
/**
|
|
64
|
+
* When no dedicated `<name>.config.*` exists — or in addition to it —
|
|
65
|
+
* read `<file>.config.*` and take its `[key]`. The dedicated file (if
|
|
66
|
+
* any) is merged on top.
|
|
67
|
+
*/
|
|
68
|
+
fallback?: {
|
|
69
|
+
file: string;
|
|
70
|
+
key: string;
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
interface LayeredResult<T> {
|
|
74
|
+
config: T;
|
|
75
|
+
/** The dedicated file path if present, else the fallback file path, else `null`. */
|
|
76
|
+
filepath: string | null;
|
|
77
|
+
/** Absolute paths actually merged, lowest priority first. */
|
|
78
|
+
sources: string[];
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* `<name>.config.*` layered over the `[fallback.key]` of
|
|
82
|
+
* `<fallback.file>.config.*`. Either, both, or neither may be present;
|
|
83
|
+
* the dedicated file wins on conflict (vitest-over-vite). Returns `null`
|
|
84
|
+
* when neither source resolves.
|
|
85
|
+
*/
|
|
86
|
+
declare function loadLayered<T>(opts: LoadLayeredOptions): Promise<LayeredResult<T> | null>;
|
|
87
|
+
//#endregion
|
|
88
|
+
//#region src/merge.d.ts
|
|
89
|
+
/**
|
|
90
|
+
* Deep-merge two config objects, `vite`-style:
|
|
91
|
+
*
|
|
92
|
+
* - plain objects merge recursively
|
|
93
|
+
* - arrays concatenate (`base` first, then `override`)
|
|
94
|
+
* - an `undefined` value in `override` is skipped (keeps `base`)
|
|
95
|
+
* - every other `override` value replaces `base`
|
|
96
|
+
*
|
|
97
|
+
* Neither input is mutated.
|
|
98
|
+
*/
|
|
99
|
+
declare function mergeConfig<T>(base: T, override: unknown): T;
|
|
100
|
+
//#endregion
|
|
101
|
+
export { ConfigFileError, DEFAULT_EXTENSIONS, type LayeredResult, type LoadConfigFileOptions, type LoadLayeredOptions, type Loaded, createDefineConfig, loadConfigFile, loadLayered, mergeConfig };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { readFile, stat } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
//#region src/define.ts
|
|
5
|
+
/**
|
|
6
|
+
* Build a typed identity `defineConfig` for a specific config shape.
|
|
7
|
+
*
|
|
8
|
+
* Each consumer (`@theholocron/cli`, `@theholocron/astromech`) calls this
|
|
9
|
+
* once with its own config type and re-exports the result, so config
|
|
10
|
+
* files get autocomplete and type-checking with no runtime cost:
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* // @theholocron/astromech/config
|
|
14
|
+
* export const defineConfig = createDefineConfig<TasksConfig>();
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
function createDefineConfig() {
|
|
18
|
+
return (config) => config;
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/errors.ts
|
|
22
|
+
/**
|
|
23
|
+
* Thrown when a config file is found but cannot be loaded — malformed
|
|
24
|
+
* JSON, a syntax error in a `.ts` / `.js` module, or no usable default
|
|
25
|
+
* export. "Not found" is never an error: {@link loadConfigFile} returns
|
|
26
|
+
* `null` for that.
|
|
27
|
+
*/
|
|
28
|
+
var ConfigFileError = class extends Error {
|
|
29
|
+
filepath;
|
|
30
|
+
name = "ConfigFileError";
|
|
31
|
+
constructor(message, filepath) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.filepath = filepath;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region src/merge.ts
|
|
38
|
+
/**
|
|
39
|
+
* Deep-merge two config objects, `vite`-style:
|
|
40
|
+
*
|
|
41
|
+
* - plain objects merge recursively
|
|
42
|
+
* - arrays concatenate (`base` first, then `override`)
|
|
43
|
+
* - an `undefined` value in `override` is skipped (keeps `base`)
|
|
44
|
+
* - every other `override` value replaces `base`
|
|
45
|
+
*
|
|
46
|
+
* Neither input is mutated.
|
|
47
|
+
*/
|
|
48
|
+
function mergeConfig(base, override) {
|
|
49
|
+
if (!isPlainObject(base) || !isPlainObject(override)) return override === void 0 ? base : override;
|
|
50
|
+
const out = { ...base };
|
|
51
|
+
for (const [key, value] of Object.entries(override)) {
|
|
52
|
+
if (value === void 0) continue;
|
|
53
|
+
const prev = out[key];
|
|
54
|
+
if (Array.isArray(prev) && Array.isArray(value)) out[key] = [...prev, ...value];
|
|
55
|
+
else if (isPlainObject(prev) && isPlainObject(value)) out[key] = mergeConfig(prev, value);
|
|
56
|
+
else out[key] = value;
|
|
57
|
+
}
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
function isPlainObject(value) {
|
|
61
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
62
|
+
const proto = Object.getPrototypeOf(value);
|
|
63
|
+
return proto === Object.prototype || proto === null;
|
|
64
|
+
}
|
|
65
|
+
//#endregion
|
|
66
|
+
//#region src/load.ts
|
|
67
|
+
/**
|
|
68
|
+
* Discover and load `<name>.config.*` files. Holocron-agnostic — no
|
|
69
|
+
* schema, no validation, no defaults. Consumers layer those on top.
|
|
70
|
+
*
|
|
71
|
+
* Probe order is **TS-first**: `.ts` → `.js` → `.mjs` → `.cjs` → `.json`.
|
|
72
|
+
* TS is loaded through `tsx`'s `tsImport` (a runtime dependency) so a
|
|
73
|
+
* typed `defineConfig` file works with no build step.
|
|
74
|
+
*/
|
|
75
|
+
/** Default extension probe order, highest priority first. */
|
|
76
|
+
const DEFAULT_EXTENSIONS = [
|
|
77
|
+
"ts",
|
|
78
|
+
"js",
|
|
79
|
+
"mjs",
|
|
80
|
+
"cjs",
|
|
81
|
+
"json"
|
|
82
|
+
];
|
|
83
|
+
/**
|
|
84
|
+
* Load the first `<name>.config.<ext>` that exists in `cwd`. Returns
|
|
85
|
+
* `null` when none is present; throws {@link ConfigFileError} when a file
|
|
86
|
+
* exists but cannot be loaded.
|
|
87
|
+
*/
|
|
88
|
+
async function loadConfigFile(opts) {
|
|
89
|
+
const extensions = opts.extensions ?? DEFAULT_EXTENSIONS;
|
|
90
|
+
for (const ext of extensions) {
|
|
91
|
+
const filepath = join(opts.cwd, `${opts.name}.config.${ext}`);
|
|
92
|
+
if (!await isFile(filepath)) continue;
|
|
93
|
+
return {
|
|
94
|
+
config: await loadFile(filepath, ext),
|
|
95
|
+
filepath
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* `<name>.config.*` layered over the `[fallback.key]` of
|
|
102
|
+
* `<fallback.file>.config.*`. Either, both, or neither may be present;
|
|
103
|
+
* the dedicated file wins on conflict (vitest-over-vite). Returns `null`
|
|
104
|
+
* when neither source resolves.
|
|
105
|
+
*/
|
|
106
|
+
async function loadLayered(opts) {
|
|
107
|
+
const dedicated = await loadConfigFile(opts);
|
|
108
|
+
let base;
|
|
109
|
+
let baseFile = null;
|
|
110
|
+
if (opts.fallback) {
|
|
111
|
+
const parent = await loadConfigFile({
|
|
112
|
+
cwd: opts.cwd,
|
|
113
|
+
name: opts.fallback.file,
|
|
114
|
+
extensions: opts.extensions
|
|
115
|
+
});
|
|
116
|
+
if (parent && parent.config[opts.fallback.key] !== void 0) {
|
|
117
|
+
base = parent.config[opts.fallback.key];
|
|
118
|
+
baseFile = parent.filepath;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (!dedicated && base === void 0) return null;
|
|
122
|
+
const config = mergeConfig(base, dedicated?.config);
|
|
123
|
+
const sources = [baseFile, dedicated?.filepath ?? null].filter((s) => s !== null);
|
|
124
|
+
return {
|
|
125
|
+
config,
|
|
126
|
+
filepath: dedicated?.filepath ?? baseFile,
|
|
127
|
+
sources
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
async function loadFile(filepath, ext) {
|
|
131
|
+
if (ext === "json") return loadJson(filepath);
|
|
132
|
+
return extractDefault(filepath, ext === "ts" ? await importTs(filepath) : await importModule(filepath));
|
|
133
|
+
}
|
|
134
|
+
async function loadJson(filepath) {
|
|
135
|
+
const text = await readFile(filepath, "utf8");
|
|
136
|
+
try {
|
|
137
|
+
return JSON.parse(text);
|
|
138
|
+
} catch (err) {
|
|
139
|
+
throw new ConfigFileError(`${filepath} is not valid JSON: ${message(err)}`, filepath);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
async function importModule(filepath) {
|
|
143
|
+
try {
|
|
144
|
+
return await import(pathToFileURL(filepath).href);
|
|
145
|
+
} catch (err) {
|
|
146
|
+
throw new ConfigFileError(`could not load ${filepath}: ${message(err)}`, filepath);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
let tsRegistered;
|
|
150
|
+
async function registerTsx() {
|
|
151
|
+
const { register } = await import("tsx/esm/api");
|
|
152
|
+
register();
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Load a `.ts` config. `tsx`'s ESM loader is registered once per process
|
|
156
|
+
* (lazily — only when a `.ts` config is actually loaded). `register()` is
|
|
157
|
+
* used over `tsImport()` because a single run may load several TS configs
|
|
158
|
+
* (`loadLayered` reads a dedicated file *and* a parent file) and
|
|
159
|
+
* `tsImport`'s one-off register/unregister cycle is not reentrant.
|
|
160
|
+
*/
|
|
161
|
+
async function importTs(filepath) {
|
|
162
|
+
tsRegistered ??= registerTsx();
|
|
163
|
+
await tsRegistered;
|
|
164
|
+
try {
|
|
165
|
+
return await import(pathToFileURL(filepath).href);
|
|
166
|
+
} catch (err) {
|
|
167
|
+
throw new ConfigFileError(`could not load ${filepath}: ${message(err)}`, filepath);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Unwrap `export default`. `tsx` CJS-transforms `export default x` into
|
|
172
|
+
* `exports.default = x`, which dynamic import wraps as
|
|
173
|
+
* `{ default: { __esModule: true, default: x } }` — strip the extra layer
|
|
174
|
+
* when present so ESM and CJS outputs both resolve.
|
|
175
|
+
*/
|
|
176
|
+
function extractDefault(filepath, mod) {
|
|
177
|
+
const outer = mod.default;
|
|
178
|
+
const raw = outer?.__esModule === true ? outer.default : outer;
|
|
179
|
+
if (raw === void 0 || raw === null) throw new ConfigFileError(`${filepath} must have a default export (use \`export default defineConfig({…})\`)`, filepath);
|
|
180
|
+
return raw;
|
|
181
|
+
}
|
|
182
|
+
async function isFile(path) {
|
|
183
|
+
try {
|
|
184
|
+
return (await stat(path)).isFile();
|
|
185
|
+
} catch {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const message = (err) => err instanceof Error ? err.message : String(err);
|
|
190
|
+
//#endregion
|
|
191
|
+
export { ConfigFileError, DEFAULT_EXTENSIONS, createDefineConfig, loadConfigFile, loadLayered, mergeConfig };
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@theholocron/datapad",
|
|
3
|
+
"version": "3.64.0",
|
|
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
|
+
"keywords": [
|
|
6
|
+
"config",
|
|
7
|
+
"config-loader",
|
|
8
|
+
"defineconfig",
|
|
9
|
+
"holocron",
|
|
10
|
+
"theholocron",
|
|
11
|
+
"typescript"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/theholocron/holocron/tree/main/packages/datapad#readme",
|
|
14
|
+
"bugs": "https://github.com/theholocron/holocron/issues",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/theholocron/holocron.git",
|
|
18
|
+
"directory": "packages/datapad"
|
|
19
|
+
},
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"author": "Newton Koumantzelis",
|
|
22
|
+
"sideEffects": false,
|
|
23
|
+
"type": "module",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./dist/index.d.mts",
|
|
27
|
+
"import": "./dist/index.mjs",
|
|
28
|
+
"default": "./dist/index.mjs"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist"
|
|
33
|
+
],
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"tsx": "4.23.12"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@theholocron/eslint-config": "^7.32.1",
|
|
39
|
+
"@theholocron/tsconfig": "^7.32.1",
|
|
40
|
+
"@theholocron/tsdown-config": "^7.32.1",
|
|
41
|
+
"@theholocron/vitest-config": "^7.32.1",
|
|
42
|
+
"@types/node": "^26",
|
|
43
|
+
"@vitest/coverage-v8": "^4.1.11",
|
|
44
|
+
"@vitest/eslint-plugin": "^1.6.27",
|
|
45
|
+
"eslint": "^10.8.1",
|
|
46
|
+
"eslint-plugin-n": "^18.3.0",
|
|
47
|
+
"globals": "^17.11.0",
|
|
48
|
+
"tsdown": "^0.22.14",
|
|
49
|
+
"typescript": "^5.9.3",
|
|
50
|
+
"vitest": "^4.1.11"
|
|
51
|
+
},
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=22"
|
|
54
|
+
},
|
|
55
|
+
"publishConfig": {
|
|
56
|
+
"access": "public"
|
|
57
|
+
},
|
|
58
|
+
"scripts": {
|
|
59
|
+
"build": "tsdown",
|
|
60
|
+
"lint": "eslint .",
|
|
61
|
+
"typecheck": "tsc --noEmit",
|
|
62
|
+
"test": "vitest run",
|
|
63
|
+
"test:watch": "vitest",
|
|
64
|
+
"test:coverage": "vitest run --coverage"
|
|
65
|
+
}
|
|
66
|
+
}
|