@rstackjs/load-config 0.1.2 → 1.0.1

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
@@ -235,6 +235,27 @@ await loadConfig({
235
235
 
236
236
  When using the `native` loader and `fresh` is enabled, `dependencies` contains absolute paths for files imported by the config file.
237
237
 
238
+ ### withConfigMeta
239
+
240
+ Preserve the original config path and dependencies when loading through an adapter:
241
+
242
+ ```ts
243
+ import { withConfigMeta } from '@rstackjs/load-config';
244
+
245
+ const config = { name: 'my-tool' };
246
+
247
+ export default withConfigMeta(config, {
248
+ filePath: '/project/project.config.ts',
249
+ dependencies: ['/project/shared.ts'],
250
+ });
251
+ ```
252
+
253
+ `loadConfig` uses the supplied `filePath` and merges `dependencies` with the adapter file and its collected dependencies, removing duplicates.
254
+
255
+ - Use absolute paths. `filePath: null` means no underlying config was found; `dependencies` is optional.
256
+ - The helper modifies and returns the original config object. Repeated calls replace its metadata. Frozen or non-extensible objects are not supported.
257
+ - Call it on the final config object: object spread and JSON serialization discard the metadata.
258
+
238
259
  ## License
239
260
 
240
261
  [MIT](./LICENSE).
@@ -0,0 +1,2 @@
1
+ /** Share the pending import while allowing a failed import to be retried. */
2
+ export declare const cachedImport: <T>(importer: () => Promise<T>) => (() => Promise<T>);
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  import type { LoadConfigOptions, LoadConfigResult } from './types.js';
2
- export type { ConfigDefinition, ConfigLoader, LoadConfigOptions, LoadConfigResult, } from './types.js';
2
+ export { withConfigMeta } from './meta.js';
3
+ export type { ConfigDefinition, ConfigFileMeta, ConfigLoader, LoadConfigOptions, LoadConfigResult, } from './types.js';
3
4
  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 CHANGED
@@ -9,10 +9,18 @@ const getConfigExport = (configModule, exportName, configPath)=>{
9
9
  if (canReadExport(configModule) && Object.hasOwn(configModule, exportName)) return configModule[exportName];
10
10
  throw new Error(`Cannot find export ${exportName} in config file: ${configPath}`);
11
11
  };
12
+ const cachedImport = (importer)=>{
13
+ let promise;
14
+ return ()=>promise ??= importer().catch((error)=>{
15
+ promise = void 0;
16
+ throw error;
17
+ });
18
+ };
19
+ const getJiti = cachedImport(()=>import("jiti"));
12
20
  const loadWithJiti = async (configPath, exportName, fresh)=>{
13
21
  let createJiti;
14
22
  try {
15
- ({ createJiti } = await import("jiti"));
23
+ ({ createJiti } = await getJiti());
16
24
  } catch (error) {
17
25
  throw new Error('The "jiti" package is required to load this config. Install it with your package manager.', {
18
26
  cause: error
@@ -37,6 +45,23 @@ const loadWithJiti = async (configPath, exportName, fresh)=>{
37
45
  dependencies: []
38
46
  };
39
47
  };
48
+ const CONFIG_META = Symbol.for('@rstackjs/load-config/meta');
49
+ function withConfigMeta(config, meta) {
50
+ Object.defineProperty(config, CONFIG_META, {
51
+ configurable: true,
52
+ value: {
53
+ filePath: meta.filePath,
54
+ dependencies: [
55
+ ...meta.dependencies ?? []
56
+ ]
57
+ }
58
+ });
59
+ return config;
60
+ }
61
+ function getConfigMeta(config) {
62
+ if (null !== config && 'object' == typeof config && Object.hasOwn(config, CONFIG_META)) return config[CONFIG_META];
63
+ }
64
+ const getFreshImport = cachedImport(()=>import("./freshImport.js"));
40
65
  const JS_CONFIG_REGEXP = /\.(?:js|mjs|cjs)$/;
41
66
  const loadWithNative = async (configPath, fresh)=>{
42
67
  const configFileURL = pathToFileURL(configPath).href;
@@ -47,7 +72,7 @@ const loadWithNative = async (configPath, fresh)=>{
47
72
  dependencies: []
48
73
  };
49
74
  }
50
- const { freshImport } = await import("./freshImport.js");
75
+ const { freshImport } = await getFreshImport();
51
76
  const freshImportResult = await freshImport(configFileURL);
52
77
  if (freshImportResult) return {
53
78
  configModule: freshImportResult.result,
@@ -95,19 +120,28 @@ async function loadConfig({ cwd = process.cwd(), path, configFileNames = [], loa
95
120
  }
96
121
  if (!loadedConfig) loadedConfig = await loadWithJiti(configPath, exportName, fresh);
97
122
  const { configExport, dependencies } = loadedConfig;
123
+ let content;
98
124
  if (isConfigFunction(configExport)) {
99
125
  const result = await configExport(...configParams);
100
126
  if (void 0 === result) throw new Error('The config function must return a config object.');
101
- return {
102
- content: result,
103
- filePath: configPath,
104
- dependencies
105
- };
106
- }
107
- return {
108
- content: configExport,
127
+ content = result;
128
+ } else content = configExport;
129
+ const meta = getConfigMeta(content);
130
+ const result = {
131
+ content,
109
132
  filePath: configPath,
110
133
  dependencies
111
134
  };
135
+ if (meta) {
136
+ result.filePath = meta.filePath;
137
+ result.dependencies = [
138
+ ...new Set([
139
+ ...meta.dependencies ?? [],
140
+ configPath,
141
+ ...dependencies
142
+ ])
143
+ ];
144
+ }
145
+ return result;
112
146
  }
113
- export { loadConfig };
147
+ export { loadConfig, withConfigMeta };
package/dist/meta.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ import type { ConfigFileMeta } from './types.js';
2
+ /**
3
+ * Attach file metadata in place and return the original configuration object.
4
+ * Repeated calls replace the metadata. Requires an extensible, unfrozen object.
5
+ * Call this after merging the config: object spread does not preserve metadata.
6
+ */
7
+ export declare function withConfigMeta<Config extends object>(config: Config, meta: ConfigFileMeta): Config;
8
+ export declare function getConfigMeta(config: unknown): ConfigFileMeta | undefined;
package/dist/types.d.ts CHANGED
@@ -43,19 +43,33 @@ export type LoadConfigOptions<Params extends unknown[] = []> = {
43
43
  */
44
44
  fresh?: boolean;
45
45
  };
46
+ export type ConfigFileMeta = {
47
+ /**
48
+ * Absolute path of the actual configuration file.
49
+ * Use `null` when no underlying configuration file was found.
50
+ */
51
+ filePath: string | null;
52
+ /**
53
+ * Absolute paths of additional configuration dependencies.
54
+ * These are merged with the adapter file and its collected dependencies.
55
+ */
56
+ dependencies?: readonly string[];
57
+ };
46
58
  export type LoadConfigResult<Config = unknown> = {
47
59
  /**
48
60
  * The loaded configuration object.
49
61
  */
50
62
  content: Config;
51
63
  /**
52
- * The path to the loaded configuration file.
53
- * Return `null` if the configuration file is not found.
64
+ * The path to the loaded configuration file, or the source set by `withConfigMeta`.
65
+ * Returns `null` if no configuration file was found, including an explicit
66
+ * `null` source set by `withConfigMeta`.
54
67
  */
55
68
  filePath: string | null;
56
69
  /**
57
- * Absolute file paths of statically imported (relative) dependencies of the
58
- * config file.
70
+ * Absolute paths of collected configuration dependencies. When `withConfigMeta`
71
+ * is used, also includes explicit dependencies and the loaded adapter file,
72
+ * with duplicates removed.
59
73
  */
60
74
  dependencies: string[];
61
75
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rstackjs/load-config",
3
- "version": "0.1.2",
3
+ "version": "1.0.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/rstackjs/load-config"
@@ -21,8 +21,7 @@
21
21
  "@types/node": "^24.13.2",
22
22
  "fresh-import": "^0.2.1",
23
23
  "jiti": "^2.7.0",
24
- "prettier": "^3.9.4",
25
- "rstack": "0.0.2",
24
+ "rstack": "^0.6.4",
26
25
  "typescript": "^7.0.2"
27
26
  },
28
27
  "peerDependencies": {
@@ -39,10 +38,11 @@
39
38
  },
40
39
  "scripts": {
41
40
  "build": "rs lib",
41
+ "check": "rs check --type-check",
42
42
  "dev": "rs lib -w",
43
- "lint": "rs lint && prettier -c .",
44
- "lint:write": "rs lint --fix && prettier -w .",
43
+ "format": "rs fmt",
44
+ "lint": "rs lint",
45
45
  "test": "rs test",
46
- "bump": "pnpx bumpp"
46
+ "bump": "pnpm version -m \"release: v%s\""
47
47
  }
48
48
  }