@rstackjs/load-config 0.0.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Rspack Contrib
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,226 @@
1
+ # @rstackjs/load-config
2
+
3
+ <p>
4
+ <a href="https://npmjs.com/package/@rstackjs/load-config">
5
+ <img src="https://img.shields.io/npm/v/@rstackjs/load-config?style=flat-square&colorA=564341&colorB=EDED91" alt="npm version" />
6
+ </a>
7
+ <img src="https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square&colorA=564341&colorB=EDED91" alt="license" />
8
+ <a href="https://npmcharts.com/compare/@rstackjs/load-config?minimal=true"><img src="https://img.shields.io/npm/dm/@rstackjs/load-config.svg?style=flat-square&colorA=564341&colorB=EDED91" alt="downloads" /></a>
9
+ </p>
10
+
11
+ A config loading utility for the Rstack ecosystem, designed for loading JavaScript and TypeScript config files for projects like Rspack and Rsbuild.
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm add @rstackjs/load-config -D
17
+ ```
18
+
19
+ [jiti](https://github.com/unjs/jiti) is an optional peer dependency. Install it only when you use the `jiti` or `auto` loaders:
20
+
21
+ ```bash
22
+ npm add jiti -D
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ```ts
28
+ import { loadConfig } from '@rstackjs/load-config';
29
+
30
+ const result = await loadConfig<{ name: string }>({
31
+ cwd: process.cwd(),
32
+ configFileNames: ['my-tool.config.ts', 'my-tool.config.mjs'],
33
+ });
34
+
35
+ console.log(result.content);
36
+ console.log(result.filePath);
37
+ ```
38
+
39
+ Given a config file:
40
+
41
+ ```ts
42
+ // my-tool.config.ts
43
+ export default {
44
+ name: 'my-tool',
45
+ };
46
+ ```
47
+
48
+ `loadConfig` returns:
49
+
50
+ ```ts
51
+ type LoadConfigResult<Config = unknown> = {
52
+ content: Config;
53
+ filePath: string | null;
54
+ dependencies: string[];
55
+ };
56
+ ```
57
+
58
+ If no config file is found, `content` is an empty object, `filePath` is `null`, and `dependencies` is an empty array.
59
+
60
+ ## Supported Exports
61
+
62
+ Default object export:
63
+
64
+ ```ts
65
+ export default {
66
+ name: 'my-tool',
67
+ };
68
+ ```
69
+
70
+ Function export:
71
+
72
+ ```ts
73
+ export default ({ mode }) => {
74
+ return { mode };
75
+ };
76
+ ```
77
+
78
+ Async function export:
79
+
80
+ ```ts
81
+ export default async ({ mode }) => {
82
+ return { mode };
83
+ };
84
+ ```
85
+
86
+ ## API
87
+
88
+ ### loadConfig
89
+
90
+ ```ts
91
+ function loadConfig<Config = unknown, Params extends unknown[] = []>(
92
+ options?: LoadConfigOptions<Params>,
93
+ ): Promise<LoadConfigResult<Config>>;
94
+ ```
95
+
96
+ ### Options
97
+
98
+ #### cwd
99
+
100
+ The root directory used to resolve config files.
101
+
102
+ - Type: `string`
103
+ - Default: `process.cwd()`
104
+
105
+ #### path
106
+
107
+ A relative or absolute path to a specific config file.
108
+
109
+ - Type: `string`
110
+ - Default: `undefined`
111
+
112
+ When `path` is provided, the file must exist. Relative paths are resolved from `cwd`.
113
+
114
+ ```ts
115
+ await loadConfig({
116
+ path: path.join(import.meta.dirname, 'custom.config.ts'),
117
+ });
118
+ ```
119
+
120
+ #### configFileNames
121
+
122
+ A list of file names to search in `cwd` when `path` is not provided.
123
+
124
+ - Type: `string[]`
125
+ - Default: `[]`
126
+
127
+ ```ts
128
+ await loadConfig({
129
+ configFileNames: [
130
+ 'tool.config.ts',
131
+ 'tool.config.mts',
132
+ 'tool.config.js',
133
+ 'tool.config.mjs',
134
+ ],
135
+ });
136
+ ```
137
+
138
+ #### loader
139
+
140
+ Controls how the config file is loaded.
141
+
142
+ - Type: `'auto' | 'jiti' | 'native'`
143
+ - Default: `'auto'`
144
+
145
+ `auto` uses the native loader when possible and falls back to `jiti`.
146
+
147
+ JavaScript config files (`.js`, `.mjs`, `.cjs`) are always attempted with native dynamic import first; if native import fails and `loader` is not `native`, they fall back to `jiti`.
148
+
149
+ TypeScript config files use the native loader in runtimes with TypeScript support, Bun, or Deno; otherwise they use `jiti`.
150
+
151
+ Set `loader` to `native` to disable the `jiti` fallback.
152
+
153
+ ```ts
154
+ await loadConfig({
155
+ path: 'tool.config.ts',
156
+ loader: 'native',
157
+ });
158
+ ```
159
+
160
+ #### exportName
161
+
162
+ The export to read from the config module.
163
+
164
+ - Type: `string | false`
165
+ - Default: `'default'`
166
+
167
+ Use a string to read a named export:
168
+
169
+ ```ts
170
+ // tool.config.ts
171
+ export const config = {
172
+ name: 'my-tool',
173
+ };
174
+ ```
175
+
176
+ ```ts
177
+ await loadConfig({
178
+ path: 'tool.config.ts',
179
+ exportName: 'config',
180
+ });
181
+ ```
182
+
183
+ Set `exportName` to `false` to execute the config file without reading exports. The returned `content` is an empty object.
184
+
185
+ #### configParams
186
+
187
+ Arguments passed to a function config export.
188
+
189
+ - Type: `Params`
190
+ - Default: `[]`
191
+
192
+ ```ts
193
+ // tool.config.ts
194
+ export default ({ mode }: { mode: string }) => ({
195
+ mode,
196
+ });
197
+ ```
198
+
199
+ ```ts
200
+ const result = await loadConfig<{ mode: string }, [{ mode: string }]>({
201
+ path: 'tool.config.ts',
202
+ configParams: [{ mode: 'production' }],
203
+ });
204
+ ```
205
+
206
+ Config functions may be async, but they must return a config object.
207
+
208
+ #### fresh
209
+
210
+ Bypasses module cache when loading the config.
211
+
212
+ - Type: `boolean`
213
+ - Default: `false`
214
+
215
+ ```ts
216
+ await loadConfig({
217
+ path: 'tool.config.mjs',
218
+ fresh: true,
219
+ });
220
+ ```
221
+
222
+ When using the `native` loader and `fresh` is enabled, `dependencies` contains absolute paths for files imported by the config file.
223
+
224
+ ## License
225
+
226
+ [MIT](./LICENSE).
@@ -0,0 +1,111 @@
1
+ import { Module } from "node:module";
2
+ import { fileURLToPath } from "node:url";
3
+ import { MessageChannel } from "node:worker_threads";
4
+ const instanceId = Math.random().toString(36).slice(2);
5
+ const relativeImportRE = /^\.{1,2}(?:\/|\\)/;
6
+ function escapeRegExp(value) {
7
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
8
+ }
9
+ function buildQueryName() {
10
+ return `fresh-import-${instanceId}`;
11
+ }
12
+ function buildQueryRE(queryName) {
13
+ return new RegExp(`(?:\\?|&)${escapeRegExp(queryName)}=(\\d+),([^&]+)(?:&|$)`);
14
+ }
15
+ function formatTrackingQuery(queryName, id, context) {
16
+ return `?${queryName}=${id},${context}`;
17
+ }
18
+ function trackResolved(specifier, context, result, queryName, queryRE, onDependency) {
19
+ const isRelativeImport = relativeImportRE.test(specifier);
20
+ if ("builtin" === result.format || !isRelativeImport) return result;
21
+ if (!context.parentURL || queryRE.test(result.url) || !result.url.startsWith("file:")) return result;
22
+ const m = queryRE.exec(context.parentURL);
23
+ if (m) {
24
+ const [, id, contextFile] = m;
25
+ onDependency(contextFile, result.url);
26
+ result.url = result.url.replace(/(\?)|$/, (_n, n1)=>`?${queryName}=${id},${contextFile}${"?" === n1 ? "&" : ""}`);
27
+ }
28
+ return result;
29
+ }
30
+ var loader_default = 'data:text/javascript,Math.random().toString(36).slice(2);%0Aconst relativeImportRE = /^\\.{1,2}(%3F:\\/|\\\\)/;%0Afunction escapeRegExp(value) {%0A%09return value.replace(/[.*+%3F^${}()|[\\]\\\\]/g, "\\\\$&");%0A}%0A/**%0A* Build the regex that matches the tracking query `%3F<name>=<id>,<context>`%0A* (or the `&<name>=...` form).%0A*/%0Afunction buildQueryRE(queryName) {%0A%09return new RegExp(`(%3F:\\\\%3F|&)${escapeRegExp(queryName)}=(\\\\d+),([^&]+)(%3F:&|$)`);%0A}%0A/**%0A* Shared body of the resolve hook for both the on-thread and off-thread%0A* importers. Given an already-resolved `result`, decides whether it is a tracked%0A* relative file dependency; if so, reports it via `onDependency` and tags the%0A* URL so the query propagates to its own dependencies.%0A*%0A* The sync/async difference between the two hooks lives entirely in the caller%0A* (which awaits `nextResolve` or not); this function performs no I/O. `result`%0A* is mutated in place and returned.%0A*/%0Afunction trackResolved(specifier, context, result, queryName, queryRE, onDependency) {%0A%09const isRelativeImport = relativeImportRE.test(specifier);%0A%09if (result.format === "builtin" || !isRelativeImport) return result;%0A%09if (!context.parentURL || queryRE.test(result.url) || !result.url.startsWith("file:")) return result;%0A%09const m = queryRE.exec(context.parentURL);%0A%09if (m) {%0A%09%09const [, id, contextFile] = m;%0A%09%09onDependency(contextFile, result.url);%0A%09%09result.url = result.url.replace(/(\\%3F)|$/, (_n, n1) => `%3F${queryName}=${id},${contextFile}${n1 === "%3F" %3F "&" : ""}`);%0A%09}%0A%09return result;%0A}%0A//%23endregion%0A//%23region src/off-thread/loader.ts%0Alet port;%0Alet queryName;%0Alet queryRE;%0Aconst initialize = async (data) => {%0A%09port = data.port;%0A%09queryName = data.queryName;%0A%09queryRE = buildQueryRE(queryName);%0A};%0Aconst resolve = async (specifier, context, nextResolve) => {%0A%09return trackResolved(specifier, context, await nextResolve(specifier, context), queryName, queryRE, (ctx, url) => {%0A%09%09port.postMessage({%0A%09%09%09context: ctx,%0A%09%09%09url%0A%09%09});%0A%09});%0A};%0A//%23endregion%0Aexport { initialize, resolve };%0A';
31
+ let nextId$1 = 0;
32
+ function createOffThreadImporter() {
33
+ const queryName = buildQueryName();
34
+ const { port1, port2 } = new MessageChannel();
35
+ Module.register(loader_default, {
36
+ data: {
37
+ port: port2,
38
+ queryName
39
+ },
40
+ transferList: [
41
+ port2
42
+ ]
43
+ });
44
+ port1.unref();
45
+ return {
46
+ async collect (specifier) {
47
+ const id = nextId$1++;
48
+ const depsList = /* @__PURE__ */ new Set();
49
+ const onMessage = (e)=>{
50
+ if (e.context === specifier) depsList.add(e.url);
51
+ };
52
+ port1.on("message", onMessage);
53
+ port1.unref();
54
+ try {
55
+ const result = await import(specifier + formatTrackingQuery(queryName, id, specifier));
56
+ await new Promise((resolve)=>setImmediate(resolve));
57
+ return {
58
+ result,
59
+ dependencies: [
60
+ ...depsList
61
+ ].filter((url)=>url.startsWith("file:")).map((url)=>fileURLToPath(url))
62
+ };
63
+ } finally{
64
+ port1.off("message", onMessage);
65
+ }
66
+ }
67
+ };
68
+ }
69
+ let nextId = 0;
70
+ function createOnThreadImporter() {
71
+ const registry = /* @__PURE__ */ new Map();
72
+ const queryName = buildQueryName();
73
+ const queryRE = buildQueryRE(queryName);
74
+ const resolve = (specifier, context, nextResolve)=>trackResolved(specifier, context, nextResolve(specifier, context), queryName, queryRE, (ctx, url)=>{
75
+ registry.get(ctx)?.add(url);
76
+ });
77
+ Module.registerHooks({
78
+ resolve
79
+ });
80
+ return {
81
+ async collect (specifier) {
82
+ const id = nextId++;
83
+ const depsList = /* @__PURE__ */ new Set();
84
+ registry.set(specifier, depsList);
85
+ try {
86
+ return {
87
+ result: await import(specifier + formatTrackingQuery(queryName, id, specifier)),
88
+ dependencies: [
89
+ ...depsList
90
+ ].filter((url)=>url.startsWith("file:")).map((url)=>fileURLToPath(url))
91
+ };
92
+ } finally{
93
+ registry.delete(specifier);
94
+ }
95
+ }
96
+ };
97
+ }
98
+ function createImporter() {
99
+ if (Module.registerHooks) return createOnThreadImporter();
100
+ if (Module.register) return createOffThreadImporter();
101
+ }
102
+ let importer;
103
+ let initialized = false;
104
+ function freshImport(specifier) {
105
+ if (!initialized) {
106
+ importer = createImporter();
107
+ initialized = true;
108
+ }
109
+ return importer?.collect(specifier);
110
+ }
111
+ export { freshImport };
@@ -0,0 +1,3 @@
1
+ import type { ConfigDefinition, ConfigFunction } from './types.js';
2
+ export declare const isConfigFunction: <Config, Params extends unknown[]>(configExport: ConfigDefinition<Config, Params>) => configExport is ConfigFunction<Config, Params>;
3
+ export declare const getConfigExport: <Config, Params extends unknown[]>(configModule: unknown, exportName: string | false, configPath: string) => ConfigDefinition<Config, Params>;
@@ -0,0 +1,3 @@
1
+ import type { LoadConfigOptions, LoadConfigResult } from './types.js';
2
+ export type { ConfigDefinition, ConfigLoader, LoadConfigOptions, LoadConfigResult, } from './types.js';
3
+ export declare function loadConfig<Config = unknown, Params extends unknown[] = []>({ cwd, path, configFileNames, loader, exportName, configParams, fresh, }?: LoadConfigOptions<Params>): Promise<LoadConfigResult<Config>>;
package/dist/index.js ADDED
@@ -0,0 +1,117 @@
1
+ import { pathToFileURL } from "node:url";
2
+ import node_fs from "node:fs";
3
+ import { isAbsolute, join } from "node:path";
4
+ const canReadExport = (module)=>null !== module && ('object' == typeof module || 'function' == typeof module);
5
+ const isConfigFunction = (configExport)=>'function' == typeof configExport;
6
+ const getConfigExport = (configModule, exportName, configPath)=>{
7
+ if (false === exportName) return {};
8
+ if ('default' === exportName) return canReadExport(configModule) && 'default' in configModule ? configModule.default : configModule;
9
+ if (canReadExport(configModule) && Object.hasOwn(configModule, exportName)) return configModule[exportName];
10
+ throw new Error(`Cannot find export ${exportName} in config file: ${configPath}`);
11
+ };
12
+ const loadWithJiti = async (configPath, exportName, fresh)=>{
13
+ let createJiti;
14
+ try {
15
+ ({ createJiti } = await import("jiti"));
16
+ } catch (error) {
17
+ throw new Error('The "jiti" package is required to load this config. Install it with your package manager.', {
18
+ cause: error
19
+ });
20
+ }
21
+ const jiti = createJiti(configPath, {
22
+ moduleCache: !fresh,
23
+ interopDefault: true,
24
+ nativeModules: [
25
+ "typescript"
26
+ ]
27
+ });
28
+ if ('default' === exportName) return {
29
+ configExport: await jiti.import(configPath, {
30
+ default: true
31
+ }),
32
+ dependencies: []
33
+ };
34
+ const configModule = await jiti.import(configPath);
35
+ return {
36
+ configExport: getConfigExport(configModule, exportName, configPath),
37
+ dependencies: []
38
+ };
39
+ };
40
+ const JS_CONFIG_REGEXP = /\.(?:js|mjs|cjs)$/;
41
+ const tryFreshImport = async (configFileURL)=>{
42
+ try {
43
+ const { freshImport } = await import("./freshImport.js");
44
+ return await freshImport(configFileURL);
45
+ } catch {}
46
+ };
47
+ const loadWithNative = async (configPath, fresh)=>{
48
+ const configFileURL = pathToFileURL(configPath).href;
49
+ if (!fresh) {
50
+ const configModule = await import(configFileURL);
51
+ return {
52
+ configModule,
53
+ dependencies: []
54
+ };
55
+ }
56
+ const freshImportResult = await tryFreshImport(configFileURL);
57
+ if (freshImportResult) return {
58
+ configModule: freshImportResult.result,
59
+ dependencies: freshImportResult.dependencies.sort()
60
+ };
61
+ const configModule = await import(`${configFileURL}?t=${Date.now()}`);
62
+ return {
63
+ configModule,
64
+ dependencies: []
65
+ };
66
+ };
67
+ const resolveConfigPath = (root, customConfig, configFileNames = [])=>{
68
+ if (customConfig) {
69
+ const customConfigPath = isAbsolute(customConfig) ? customConfig : join(root, customConfig);
70
+ if (node_fs.existsSync(customConfigPath)) return customConfigPath;
71
+ throw new Error(`Cannot find config file: ${customConfigPath}`);
72
+ }
73
+ for (const file of configFileNames){
74
+ const configFile = join(root, file);
75
+ if (node_fs.existsSync(configFile)) return configFile;
76
+ }
77
+ return null;
78
+ };
79
+ async function loadConfig({ cwd = process.cwd(), path, configFileNames = [], loader = 'auto', exportName = 'default', configParams = [], fresh = false } = {}) {
80
+ const configPath = resolveConfigPath(cwd, path, configFileNames);
81
+ if (!configPath) return {
82
+ content: {},
83
+ filePath: configPath,
84
+ dependencies: []
85
+ };
86
+ let loadedConfig;
87
+ const useNative = Boolean('native' === loader || 'auto' === loader && (process.features.typescript || process.versions.bun || process.versions.deno));
88
+ if (useNative || JS_CONFIG_REGEXP.test(configPath)) {
89
+ let result;
90
+ try {
91
+ result = await loadWithNative(configPath, fresh);
92
+ } catch (err) {
93
+ if ('native' === loader) throw err;
94
+ }
95
+ if (result) loadedConfig = {
96
+ configExport: getConfigExport(result.configModule, exportName, configPath),
97
+ dependencies: result.dependencies
98
+ };
99
+ }
100
+ if (!loadedConfig) loadedConfig = await loadWithJiti(configPath, exportName, fresh);
101
+ const { configExport, dependencies } = loadedConfig;
102
+ if (isConfigFunction(configExport)) {
103
+ const result = await configExport(...configParams);
104
+ if (void 0 === result) throw new Error('The config function must return a config object.');
105
+ return {
106
+ content: result,
107
+ filePath: configPath,
108
+ dependencies
109
+ };
110
+ }
111
+ return {
112
+ content: configExport,
113
+ filePath: configPath,
114
+ dependencies
115
+ };
116
+ }
117
+ export { loadConfig };
package/dist/jiti.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import type { LoadedConfig } from './types.js';
2
+ export declare const loadWithJiti: <Config, Params extends unknown[]>(configPath: string, exportName: string | false, fresh: boolean) => Promise<LoadedConfig<Config, Params>>;
@@ -0,0 +1,7 @@
1
+ type NativeLoadResult = {
2
+ configModule: unknown;
3
+ dependencies: string[];
4
+ };
5
+ export declare const JS_CONFIG_REGEXP: RegExp;
6
+ export declare const loadWithNative: (configPath: string, fresh: boolean) => Promise<NativeLoadResult>;
7
+ export {};
@@ -0,0 +1 @@
1
+ export declare const resolveConfigPath: (root: string, customConfig?: string, configFileNames?: string[]) => string | null;
@@ -0,0 +1,62 @@
1
+ export type ConfigLoader = 'auto' | 'jiti' | 'native';
2
+ export type ConfigFunction<Config, Params extends unknown[]> = (...params: Params) => Config | Promise<Config>;
3
+ export type ConfigDefinition<Config, Params extends unknown[]> = Config | ConfigFunction<Config, Params>;
4
+ export type LoadConfigOptions<Params extends unknown[] = []> = {
5
+ /**
6
+ * The root path to resolve the config file.
7
+ * @default process.cwd()
8
+ */
9
+ cwd?: string;
10
+ /**
11
+ * The path to the config file, can be a relative or absolute path.
12
+ * If `path` is not provided, the function will search for the config file in the `cwd`.
13
+ */
14
+ path?: string;
15
+ /**
16
+ * Config file names to search in `cwd` when `path` is not provided.
17
+ * The package-level loader has no built-in framework defaults.
18
+ * @default []
19
+ */
20
+ configFileNames?: string[];
21
+ /**
22
+ * Specify the config loader, can be `auto`, `jiti` or `native`.
23
+ * @default 'auto'
24
+ */
25
+ loader?: ConfigLoader;
26
+ /**
27
+ * The export name to read from the config file.
28
+ * Set to `false` to execute the config file without reading exports.
29
+ * @default 'default'
30
+ */
31
+ exportName?: string | false;
32
+ /**
33
+ * Arguments passed to a function config export.
34
+ * @default []
35
+ */
36
+ configParams?: Params;
37
+ /**
38
+ * Whether to bypass module cache when loading the config.
39
+ * @default false
40
+ */
41
+ fresh?: boolean;
42
+ };
43
+ export type LoadConfigResult<Config = unknown> = {
44
+ /**
45
+ * The loaded configuration object.
46
+ */
47
+ content: Config;
48
+ /**
49
+ * The path to the loaded configuration file.
50
+ * Return `null` if the configuration file is not found.
51
+ */
52
+ filePath: string | null;
53
+ /**
54
+ * Absolute file paths of statically imported (relative) dependencies of the
55
+ * config file.
56
+ */
57
+ dependencies: string[];
58
+ };
59
+ export type LoadedConfig<Config, Params extends unknown[]> = {
60
+ configExport: ConfigDefinition<Config, Params>;
61
+ dependencies: string[];
62
+ };
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@rstackjs/load-config",
3
+ "version": "0.0.0",
4
+ "repository": "https://github.com/rstackjs/load-config",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
+ }
12
+ },
13
+ "types": "./dist/index.d.ts",
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "scripts": {
18
+ "build": "rs lib",
19
+ "dev": "rs lib -w",
20
+ "lint": "rs lint && prettier -c .",
21
+ "lint:write": "rs lint --fix && prettier -w .",
22
+ "test": "rs test",
23
+ "bump": "pnpx bumpp"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^24.13.2",
27
+ "fresh-import": "^0.2.1",
28
+ "jiti": "^2.7.0",
29
+ "prettier": "^3.9.4",
30
+ "rstack": "0.0.2",
31
+ "typescript": "^6.0.3"
32
+ },
33
+ "peerDependencies": {
34
+ "jiti": "^2.0.0"
35
+ },
36
+ "peerDependenciesMeta": {
37
+ "jiti": {
38
+ "optional": true
39
+ }
40
+ },
41
+ "packageManager": "pnpm@11.9.0",
42
+ "publishConfig": {
43
+ "access": "public",
44
+ "registry": "https://registry.npmjs.org/"
45
+ }
46
+ }