c12 0.1.1 → 0.1.4

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
@@ -13,7 +13,7 @@
13
13
  - RC config support with [unjs/rc9](https://github.com/unjs/rc9)
14
14
  - Multiple sources merged with [unjs/defu](https://github.com/unjs/defu)
15
15
  - `.env` support with [dotenv](https://www.npmjs.com/package/dotenv)
16
- - Support extending nested configurations from multiple local or git sourecs
16
+ - Support extending nested configurations from multiple local or git sources
17
17
 
18
18
  ## Usage
19
19
 
package/dist/index.cjs CHANGED
@@ -100,6 +100,12 @@ async function loadConfig(opts) {
100
100
  opts.name = opts.name || "config";
101
101
  opts.configFile = opts.configFile ?? (opts.name !== "config" ? `${opts.name}.config` : "config");
102
102
  opts.rcFile = opts.rcFile ?? `.${opts.name}rc`;
103
+ if (opts.extend !== false) {
104
+ opts.extend = {
105
+ extendKey: "extends",
106
+ ...opts.extend
107
+ };
108
+ }
103
109
  const r = {
104
110
  config: {},
105
111
  cwd: opts.cwd,
@@ -112,7 +118,7 @@ async function loadConfig(opts) {
112
118
  ...opts.dotenv === true ? {} : opts.dotenv
113
119
  });
114
120
  }
115
- const { config, configFile } = await loadConfigFile(opts.cwd, opts.configFile);
121
+ const { config, configFile } = await resolveConfig(".", opts);
116
122
  if (configFile) {
117
123
  r.configFile = configFile;
118
124
  }
@@ -123,60 +129,68 @@ async function loadConfig(opts) {
123
129
  }
124
130
  Object.assign(configRC, rc9__namespace.read({ name: opts.rcFile, dir: opts.cwd }));
125
131
  }
126
- r.config = defu__default(opts.overrides, config, configRC, opts.defaults);
127
- await extendConfig(r.config, opts.configFile, opts.cwd);
128
- r.layers = r.config._layers;
129
- delete r.config._layers;
130
- r.config = defu__default(r.config, ...r.layers.map((e) => e.config));
132
+ r.config = defu__default(opts.overrides, config, configRC);
133
+ if (opts.extend) {
134
+ await extendConfig(r.config, opts);
135
+ r.layers = r.config._layers;
136
+ delete r.config._layers;
137
+ r.config = defu__default(r.config, ...r.layers.map((e) => e.config));
138
+ }
139
+ if (opts.defaults) {
140
+ r.config = defu__default(r.config, opts.defaults);
141
+ }
131
142
  return r;
132
143
  }
133
- const GIT_PREFIXES = ["github:", "gitlab:", "bitbucket:", "https://"];
134
- async function extendConfig(config, configFile, cwd) {
144
+ async function extendConfig(config, opts) {
135
145
  config._layers = config._layers || [];
136
- const extendSources = (Array.isArray(config.extends) ? config.extends : [config.extends]).filter(Boolean);
137
- delete config.extends;
138
- for (let extendSource of extendSources) {
139
- if (GIT_PREFIXES.some((prefix) => extendSource.startsWith(prefix))) {
140
- const url = new URL(extendSource);
141
- const subPath = url.pathname.split("/").slice(2).join("/");
142
- const gitRepo = url.protocol + url.pathname.split("/").slice(0, 2).join("/");
143
- const tmpdir = pathe.resolve(os__default.tmpdir(), "c12/", gitRepo.replace(/[#:@/\\]/g, "_"));
144
- await fs.promises.rm(tmpdir, { recursive: true }).catch(() => {
145
- });
146
- const gittar = await import('gittar').then((r) => r.default || r);
147
- const tarFile = await gittar.fetch(gitRepo);
148
- await gittar.extract(tarFile, tmpdir);
149
- extendSource = pathe.resolve(tmpdir, subPath);
150
- }
151
- const isDir = !pathe.extname(extendSource);
152
- const _cwd = pathe.resolve(cwd, isDir ? extendSource : pathe.dirname(extendSource));
153
- const _config = await loadConfigFile(_cwd, isDir ? configFile : extendSource);
146
+ if (!opts.extend) {
147
+ return;
148
+ }
149
+ const key = opts.extend.extendKey;
150
+ const extendSources = (Array.isArray(config[key]) ? config[key] : [config[key]]).filter(Boolean);
151
+ delete config[key];
152
+ for (const extendSource of extendSources) {
153
+ const _config = await resolveConfig(extendSource, opts);
154
154
  if (!_config.config) {
155
155
  continue;
156
156
  }
157
- await extendConfig(_config.config, configFile, _cwd);
158
- config._layers.push({
159
- config: _config.config,
160
- cwd: _cwd,
161
- configFile: _config.configFile
162
- });
157
+ await extendConfig(_config.config, { ...opts, cwd: _config.cwd });
158
+ config._layers.push(_config);
163
159
  if (_config.config._layers) {
164
160
  config._layers.push(..._config.config._layers);
165
161
  delete _config.config._layers;
166
162
  }
167
163
  }
168
164
  }
169
- const jiti = createJiti__default(null, { cache: false, interopDefault: true });
170
- async function loadConfigFile(cwd, configFile) {
171
- const res = {
172
- configFile: null,
173
- config: null
174
- };
175
- if (!configFile) {
176
- return res;
165
+ const GIT_PREFIXES = ["github:", "gitlab:", "bitbucket:", "https://"];
166
+ const jiti = createJiti__default(null, { cache: false, interopDefault: true, requireCache: false });
167
+ async function resolveConfig(source, opts) {
168
+ if (opts.resolve) {
169
+ const res2 = await opts.resolve(source, opts);
170
+ if (res2) {
171
+ return res2;
172
+ }
173
+ }
174
+ if (GIT_PREFIXES.some((prefix) => source.startsWith(prefix))) {
175
+ const url = new URL(source);
176
+ const subPath = url.pathname.split("/").slice(2).join("/");
177
+ const gitRepo = url.protocol + url.pathname.split("/").slice(0, 2).join("/");
178
+ const tmpdir = pathe.resolve(os__default.tmpdir(), "c12/", gitRepo.replace(/[#:@/\\]/g, "_"));
179
+ await fs.promises.rm(tmpdir, { recursive: true }).catch(() => {
180
+ });
181
+ const gittar = await import('gittar').then((r) => r.default || r);
182
+ const tarFile = await gittar.fetch(gitRepo);
183
+ await gittar.extract(tarFile, tmpdir);
184
+ source = pathe.resolve(tmpdir, subPath);
185
+ }
186
+ const isDir = !pathe.extname(source);
187
+ const cwd = pathe.resolve(opts.cwd, isDir ? source : pathe.dirname(source));
188
+ if (isDir) {
189
+ source = opts.configFile;
177
190
  }
191
+ const res = { config: {}, cwd };
178
192
  try {
179
- res.configFile = jiti.resolve(pathe.resolve(cwd, configFile), { paths: [cwd] });
193
+ res.configFile = jiti.resolve(pathe.resolve(cwd, source), { paths: [cwd] });
180
194
  res.config = jiti(res.configFile);
181
195
  if (typeof res.config === "function") {
182
196
  res.config = await res.config();
package/dist/index.d.ts CHANGED
@@ -38,9 +38,12 @@ interface InputConfig extends Record<string, any> {
38
38
  }
39
39
  interface ResolvedConfig<T extends InputConfig = InputConfig> {
40
40
  config: T;
41
+ cwd?: string;
42
+ configFile?: string;
43
+ layers?: ResolvedConfig<T>[];
44
+ }
45
+ interface ResolveConfigOptions {
41
46
  cwd: string;
42
- configFile: string;
43
- layers: ResolvedConfig<T>[];
44
47
  }
45
48
  interface LoadConfigOptions<T extends InputConfig = InputConfig> {
46
49
  name?: string;
@@ -51,7 +54,11 @@ interface LoadConfigOptions<T extends InputConfig = InputConfig> {
51
54
  dotenv?: boolean | DotenvOptions;
52
55
  defaults?: T;
53
56
  overrides?: T;
57
+ resolve?: (id: string, opts: LoadConfigOptions) => null | ResolvedConfig | Promise<ResolvedConfig | null>;
58
+ extend?: false | {
59
+ extendKey?: string;
60
+ };
54
61
  }
55
62
  declare function loadConfig<T extends InputConfig = InputConfig>(opts: LoadConfigOptions<T>): Promise<ResolvedConfig<T>>;
56
63
 
57
- export { DotenvOptions, Env, InputConfig, LoadConfigOptions, ResolvedConfig, loadConfig, loadDotenv, setupDotenv };
64
+ export { DotenvOptions, Env, InputConfig, LoadConfigOptions, ResolveConfigOptions, ResolvedConfig, loadConfig, loadDotenv, setupDotenv };
package/dist/index.mjs CHANGED
@@ -76,6 +76,12 @@ async function loadConfig(opts) {
76
76
  opts.name = opts.name || "config";
77
77
  opts.configFile = opts.configFile ?? (opts.name !== "config" ? `${opts.name}.config` : "config");
78
78
  opts.rcFile = opts.rcFile ?? `.${opts.name}rc`;
79
+ if (opts.extend !== false) {
80
+ opts.extend = {
81
+ extendKey: "extends",
82
+ ...opts.extend
83
+ };
84
+ }
79
85
  const r = {
80
86
  config: {},
81
87
  cwd: opts.cwd,
@@ -88,7 +94,7 @@ async function loadConfig(opts) {
88
94
  ...opts.dotenv === true ? {} : opts.dotenv
89
95
  });
90
96
  }
91
- const { config, configFile } = await loadConfigFile(opts.cwd, opts.configFile);
97
+ const { config, configFile } = await resolveConfig(".", opts);
92
98
  if (configFile) {
93
99
  r.configFile = configFile;
94
100
  }
@@ -99,60 +105,68 @@ async function loadConfig(opts) {
99
105
  }
100
106
  Object.assign(configRC, rc9.read({ name: opts.rcFile, dir: opts.cwd }));
101
107
  }
102
- r.config = defu(opts.overrides, config, configRC, opts.defaults);
103
- await extendConfig(r.config, opts.configFile, opts.cwd);
104
- r.layers = r.config._layers;
105
- delete r.config._layers;
106
- r.config = defu(r.config, ...r.layers.map((e) => e.config));
108
+ r.config = defu(opts.overrides, config, configRC);
109
+ if (opts.extend) {
110
+ await extendConfig(r.config, opts);
111
+ r.layers = r.config._layers;
112
+ delete r.config._layers;
113
+ r.config = defu(r.config, ...r.layers.map((e) => e.config));
114
+ }
115
+ if (opts.defaults) {
116
+ r.config = defu(r.config, opts.defaults);
117
+ }
107
118
  return r;
108
119
  }
109
- const GIT_PREFIXES = ["github:", "gitlab:", "bitbucket:", "https://"];
110
- async function extendConfig(config, configFile, cwd) {
120
+ async function extendConfig(config, opts) {
111
121
  config._layers = config._layers || [];
112
- const extendSources = (Array.isArray(config.extends) ? config.extends : [config.extends]).filter(Boolean);
113
- delete config.extends;
114
- for (let extendSource of extendSources) {
115
- if (GIT_PREFIXES.some((prefix) => extendSource.startsWith(prefix))) {
116
- const url = new URL(extendSource);
117
- const subPath = url.pathname.split("/").slice(2).join("/");
118
- const gitRepo = url.protocol + url.pathname.split("/").slice(0, 2).join("/");
119
- const tmpdir = resolve(os.tmpdir(), "c12/", gitRepo.replace(/[#:@/\\]/g, "_"));
120
- await promises.rm(tmpdir, { recursive: true }).catch(() => {
121
- });
122
- const gittar = await import('gittar').then((r) => r.default || r);
123
- const tarFile = await gittar.fetch(gitRepo);
124
- await gittar.extract(tarFile, tmpdir);
125
- extendSource = resolve(tmpdir, subPath);
126
- }
127
- const isDir = !extname(extendSource);
128
- const _cwd = resolve(cwd, isDir ? extendSource : dirname(extendSource));
129
- const _config = await loadConfigFile(_cwd, isDir ? configFile : extendSource);
122
+ if (!opts.extend) {
123
+ return;
124
+ }
125
+ const key = opts.extend.extendKey;
126
+ const extendSources = (Array.isArray(config[key]) ? config[key] : [config[key]]).filter(Boolean);
127
+ delete config[key];
128
+ for (const extendSource of extendSources) {
129
+ const _config = await resolveConfig(extendSource, opts);
130
130
  if (!_config.config) {
131
131
  continue;
132
132
  }
133
- await extendConfig(_config.config, configFile, _cwd);
134
- config._layers.push({
135
- config: _config.config,
136
- cwd: _cwd,
137
- configFile: _config.configFile
138
- });
133
+ await extendConfig(_config.config, { ...opts, cwd: _config.cwd });
134
+ config._layers.push(_config);
139
135
  if (_config.config._layers) {
140
136
  config._layers.push(..._config.config._layers);
141
137
  delete _config.config._layers;
142
138
  }
143
139
  }
144
140
  }
145
- const jiti = createJiti(null, { cache: false, interopDefault: true });
146
- async function loadConfigFile(cwd, configFile) {
147
- const res = {
148
- configFile: null,
149
- config: null
150
- };
151
- if (!configFile) {
152
- return res;
141
+ const GIT_PREFIXES = ["github:", "gitlab:", "bitbucket:", "https://"];
142
+ const jiti = createJiti(null, { cache: false, interopDefault: true, requireCache: false });
143
+ async function resolveConfig(source, opts) {
144
+ if (opts.resolve) {
145
+ const res2 = await opts.resolve(source, opts);
146
+ if (res2) {
147
+ return res2;
148
+ }
149
+ }
150
+ if (GIT_PREFIXES.some((prefix) => source.startsWith(prefix))) {
151
+ const url = new URL(source);
152
+ const subPath = url.pathname.split("/").slice(2).join("/");
153
+ const gitRepo = url.protocol + url.pathname.split("/").slice(0, 2).join("/");
154
+ const tmpdir = resolve(os.tmpdir(), "c12/", gitRepo.replace(/[#:@/\\]/g, "_"));
155
+ await promises.rm(tmpdir, { recursive: true }).catch(() => {
156
+ });
157
+ const gittar = await import('gittar').then((r) => r.default || r);
158
+ const tarFile = await gittar.fetch(gitRepo);
159
+ await gittar.extract(tarFile, tmpdir);
160
+ source = resolve(tmpdir, subPath);
161
+ }
162
+ const isDir = !extname(source);
163
+ const cwd = resolve(opts.cwd, isDir ? source : dirname(source));
164
+ if (isDir) {
165
+ source = opts.configFile;
153
166
  }
167
+ const res = { config: {}, cwd };
154
168
  try {
155
- res.configFile = jiti.resolve(resolve(cwd, configFile), { paths: [cwd] });
169
+ res.configFile = jiti.resolve(resolve(cwd, source), { paths: [cwd] });
156
170
  res.config = jiti(res.configFile);
157
171
  if (typeof res.config === "function") {
158
172
  res.config = await res.config();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "c12",
3
- "version": "0.1.1",
3
+ "version": "0.1.4",
4
4
  "description": "Smart Config Loader",
5
5
  "repository": "unjs/c12",
6
6
  "license": "MIT",