c12 1.5.0 → 1.6.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/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [![Codecov][codecov-src]][codecov-href]
6
6
  [![License][license-src]][license-href]
7
7
 
8
- Smart Configuration Loader.
8
+ c12 (pronounced as /siːtwelv/, like c-twelve) is a smart configuration loader.
9
9
 
10
10
  ## Features
11
11
 
@@ -115,6 +115,10 @@ Specify default configuration. It is applied **before** extending config.
115
115
 
116
116
  Specify override configuration. It has the **highest** priority and is applied **before extending** config.
117
117
 
118
+ ### `omit$Keys`
119
+
120
+ Exclude environment-specific and built-in keys start with `$` in the resolved config. The default is `false`.
121
+
118
122
  ### `jiti`
119
123
 
120
124
  Custom [unjs/jiti](https://github.com/unjs/jiti) instance used to import configuration files.
@@ -123,6 +127,10 @@ Custom [unjs/jiti](https://github.com/unjs/jiti) instance used to import configu
123
127
 
124
128
  Custom [unjs/jiti](https://github.com/unjs/jiti) options to import configuration files.
125
129
 
130
+ ### `giget`
131
+
132
+ Options passed to [unjs/giget](https://github.com/unjs/giget) when extending layer from git source.
133
+
126
134
  ### `envName`
127
135
 
128
136
  Environment name used for [environment specific configuration](#environment-specific-configuration).
@@ -205,6 +213,41 @@ Layers:
205
213
  ]
206
214
  ```
207
215
 
216
+ ## Extending Config Layer from Remote Sources
217
+
218
+ You can also extend configuration from remote sources such as npm or github.
219
+
220
+ In the repo, there should be a `config.ts` (or `config.{name}.ts`) file to be considered as a valid config layer.
221
+
222
+ **Example:** Extend from a github repository
223
+
224
+ ```js
225
+ // config.ts
226
+ export default {
227
+ extends: "gh:user/repo",
228
+ };
229
+ ```
230
+
231
+ **Example:** Extend from a github repository with branch and subpath
232
+
233
+ ```js
234
+ // config.ts
235
+ export default {
236
+ extends: "gh:user/repo/theme#dev",
237
+ };
238
+ ```
239
+
240
+ **Example:** Extend with clone configuration
241
+
242
+ ```js
243
+ // config.ts
244
+ export default {
245
+ extends: ["gh:user/repo", { giget: { auth: process.env.GITHUB_TOKEN } }],
246
+ };
247
+ ```
248
+
249
+ Refer to [unjs/giget](https://giget.unjs.io) for more information.
250
+
208
251
  ## Environment-specific configuration
209
252
 
210
253
  Users can define environment-specific configuration using these config keys:
package/dist/index.cjs CHANGED
@@ -103,6 +103,7 @@ function interpolate(target, source = {}, parse = (v) => v) {
103
103
  }
104
104
  }
105
105
 
106
+ const _normalize = (p) => p?.replace(/\\/g, "/");
106
107
  async function loadConfig(options) {
107
108
  options.cwd = pathe.resolve(process.cwd(), options.cwd || ".");
108
109
  options.name = options.name || "config";
@@ -195,6 +196,13 @@ async function loadConfig(options) {
195
196
  if (options.defaults) {
196
197
  r.config = defu.defu(r.config, options.defaults);
197
198
  }
199
+ if (options.omit$Keys) {
200
+ for (const key in r.config) {
201
+ if (key.startsWith("$")) {
202
+ delete r.config[key];
203
+ }
204
+ }
205
+ }
198
206
  return r;
199
207
  }
200
208
  async function extendConfig(config, options) {
@@ -249,7 +257,14 @@ async function extendConfig(config, options) {
249
257
  }
250
258
  }
251
259
  }
252
- const GIT_PREFIXES = ["gh:", "github:", "gitlab:", "bitbucket:", "https://"];
260
+ const GIGET_PREFIXES = [
261
+ "gh:",
262
+ "github:",
263
+ "gitlab:",
264
+ "bitbucket:",
265
+ "https://",
266
+ "http://"
267
+ ];
253
268
  const NPM_PACKAGE_RE = /^(@[\da-z~-][\d._a-z~-]*\/)?[\da-z~-][\d._a-z~-]*($|\/.*)/;
254
269
  async function resolveConfig(source, options, sourceOptions = {}) {
255
270
  if (options.resolve) {
@@ -258,12 +273,15 @@ async function resolveConfig(source, options, sourceOptions = {}) {
258
273
  return res2;
259
274
  }
260
275
  }
261
- if (GIT_PREFIXES.some((prefix) => source.startsWith(prefix))) {
276
+ if (GIGET_PREFIXES.some((prefix) => source.startsWith(prefix))) {
262
277
  const { downloadTemplate } = await import('giget');
263
278
  const cloneName = source.replace(/\W+/g, "_").split("_").splice(0, 3).join("_") + "_" + ohash.hash(source);
264
279
  let cloneDir;
265
280
  const localNodeModules = pathe.resolve(options.cwd, "node_modules");
266
- if (node_fs.existsSync(localNodeModules)) {
281
+ const parentDir = pathe.dirname(options.cwd);
282
+ if (pathe.basename(parentDir) === ".c12") {
283
+ cloneDir = pathe.join(parentDir, cloneName);
284
+ } else if (node_fs.existsSync(localNodeModules)) {
267
285
  cloneDir = pathe.join(localNodeModules, ".c12", cloneName);
268
286
  } else {
269
287
  cloneDir = process.env.XDG_CACHE_HOME ? pathe.resolve(process.env.XDG_CACHE_HOME, "c12", cloneName) : pathe.resolve(node_os.homedir(), ".cache/c12", cloneName);
@@ -271,7 +289,12 @@ async function resolveConfig(source, options, sourceOptions = {}) {
271
289
  if (node_fs.existsSync(cloneDir)) {
272
290
  await promises.rm(cloneDir, { recursive: true });
273
291
  }
274
- const cloned = await downloadTemplate(source, { dir: cloneDir });
292
+ const cloned = await downloadTemplate(source, {
293
+ dir: cloneDir,
294
+ install: sourceOptions.install,
295
+ ...options.giget,
296
+ ...sourceOptions.giget
297
+ });
275
298
  source = cloned.dir;
276
299
  }
277
300
  if (NPM_PACKAGE_RE.test(source)) {
@@ -319,6 +342,8 @@ async function resolveConfig(source, options, sourceOptions = {}) {
319
342
  if (res.sourceOptions.overrides) {
320
343
  res.config = defu.defu(res.sourceOptions.overrides, res.config);
321
344
  }
345
+ res.configFile = _normalize(res.configFile);
346
+ res.source = _normalize(res.source);
322
347
  return res;
323
348
  }
324
349
 
package/dist/index.d.cts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { JITI } from 'jiti';
2
2
  import { JITIOptions } from 'jiti/dist/types';
3
+ import { DownloadTemplateOptions } from 'giget';
3
4
  import { WatchOptions } from 'chokidar';
4
5
  import { diff } from 'ohash';
5
6
 
@@ -54,6 +55,8 @@ interface C12InputConfig<T extends UserInputConfig = UserInputConfig, MT extends
54
55
  type InputConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta> = C12InputConfig<T, MT> & T;
55
56
  interface SourceOptions<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta> {
56
57
  meta?: MT;
58
+ giget?: DownloadTemplateOptions;
59
+ install?: boolean;
57
60
  overrides?: T;
58
61
  [key: string]: any;
59
62
  }
@@ -81,9 +84,11 @@ interface LoadConfigOptions<T extends UserInputConfig = UserInputConfig, MT exte
81
84
  defaults?: T;
82
85
  defaultConfig?: T;
83
86
  overrides?: T;
87
+ omit$Keys?: boolean;
84
88
  resolve?: (id: string, options: LoadConfigOptions<T, MT>) => null | undefined | ResolvedConfig<T, MT> | Promise<ResolvedConfig<T, MT> | undefined | null>;
85
89
  jiti?: JITI;
86
90
  jitiOptions?: JITIOptions;
91
+ giget?: DownloadTemplateOptions;
87
92
  extend?: false | {
88
93
  extendKey?: string | string[];
89
94
  };
package/dist/index.d.mts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { JITI } from 'jiti';
2
2
  import { JITIOptions } from 'jiti/dist/types';
3
+ import { DownloadTemplateOptions } from 'giget';
3
4
  import { WatchOptions } from 'chokidar';
4
5
  import { diff } from 'ohash';
5
6
 
@@ -54,6 +55,8 @@ interface C12InputConfig<T extends UserInputConfig = UserInputConfig, MT extends
54
55
  type InputConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta> = C12InputConfig<T, MT> & T;
55
56
  interface SourceOptions<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta> {
56
57
  meta?: MT;
58
+ giget?: DownloadTemplateOptions;
59
+ install?: boolean;
57
60
  overrides?: T;
58
61
  [key: string]: any;
59
62
  }
@@ -81,9 +84,11 @@ interface LoadConfigOptions<T extends UserInputConfig = UserInputConfig, MT exte
81
84
  defaults?: T;
82
85
  defaultConfig?: T;
83
86
  overrides?: T;
87
+ omit$Keys?: boolean;
84
88
  resolve?: (id: string, options: LoadConfigOptions<T, MT>) => null | undefined | ResolvedConfig<T, MT> | Promise<ResolvedConfig<T, MT> | undefined | null>;
85
89
  jiti?: JITI;
86
90
  jitiOptions?: JITIOptions;
91
+ giget?: DownloadTemplateOptions;
87
92
  extend?: false | {
88
93
  extendKey?: string | string[];
89
94
  };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { JITI } from 'jiti';
2
2
  import { JITIOptions } from 'jiti/dist/types';
3
+ import { DownloadTemplateOptions } from 'giget';
3
4
  import { WatchOptions } from 'chokidar';
4
5
  import { diff } from 'ohash';
5
6
 
@@ -54,6 +55,8 @@ interface C12InputConfig<T extends UserInputConfig = UserInputConfig, MT extends
54
55
  type InputConfig<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta> = C12InputConfig<T, MT> & T;
55
56
  interface SourceOptions<T extends UserInputConfig = UserInputConfig, MT extends ConfigLayerMeta = ConfigLayerMeta> {
56
57
  meta?: MT;
58
+ giget?: DownloadTemplateOptions;
59
+ install?: boolean;
57
60
  overrides?: T;
58
61
  [key: string]: any;
59
62
  }
@@ -81,9 +84,11 @@ interface LoadConfigOptions<T extends UserInputConfig = UserInputConfig, MT exte
81
84
  defaults?: T;
82
85
  defaultConfig?: T;
83
86
  overrides?: T;
87
+ omit$Keys?: boolean;
84
88
  resolve?: (id: string, options: LoadConfigOptions<T, MT>) => null | undefined | ResolvedConfig<T, MT> | Promise<ResolvedConfig<T, MT> | undefined | null>;
85
89
  jiti?: JITI;
86
90
  jitiOptions?: JITIOptions;
91
+ giget?: DownloadTemplateOptions;
87
92
  extend?: false | {
88
93
  extendKey?: string | string[];
89
94
  };
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { existsSync, promises } from 'node:fs';
2
- import { resolve, join, extname, basename, dirname } from 'pathe';
2
+ import { resolve, dirname, basename, join, extname } from 'pathe';
3
3
  import * as dotenv from 'dotenv';
4
4
  import { rm } from 'node:fs/promises';
5
5
  import { homedir } from 'node:os';
@@ -83,6 +83,7 @@ function interpolate(target, source = {}, parse = (v) => v) {
83
83
  }
84
84
  }
85
85
 
86
+ const _normalize = (p) => p?.replace(/\\/g, "/");
86
87
  async function loadConfig(options) {
87
88
  options.cwd = resolve(process.cwd(), options.cwd || ".");
88
89
  options.name = options.name || "config";
@@ -175,6 +176,13 @@ async function loadConfig(options) {
175
176
  if (options.defaults) {
176
177
  r.config = defu(r.config, options.defaults);
177
178
  }
179
+ if (options.omit$Keys) {
180
+ for (const key in r.config) {
181
+ if (key.startsWith("$")) {
182
+ delete r.config[key];
183
+ }
184
+ }
185
+ }
178
186
  return r;
179
187
  }
180
188
  async function extendConfig(config, options) {
@@ -229,7 +237,14 @@ async function extendConfig(config, options) {
229
237
  }
230
238
  }
231
239
  }
232
- const GIT_PREFIXES = ["gh:", "github:", "gitlab:", "bitbucket:", "https://"];
240
+ const GIGET_PREFIXES = [
241
+ "gh:",
242
+ "github:",
243
+ "gitlab:",
244
+ "bitbucket:",
245
+ "https://",
246
+ "http://"
247
+ ];
233
248
  const NPM_PACKAGE_RE = /^(@[\da-z~-][\d._a-z~-]*\/)?[\da-z~-][\d._a-z~-]*($|\/.*)/;
234
249
  async function resolveConfig(source, options, sourceOptions = {}) {
235
250
  if (options.resolve) {
@@ -238,12 +253,15 @@ async function resolveConfig(source, options, sourceOptions = {}) {
238
253
  return res2;
239
254
  }
240
255
  }
241
- if (GIT_PREFIXES.some((prefix) => source.startsWith(prefix))) {
256
+ if (GIGET_PREFIXES.some((prefix) => source.startsWith(prefix))) {
242
257
  const { downloadTemplate } = await import('giget');
243
258
  const cloneName = source.replace(/\W+/g, "_").split("_").splice(0, 3).join("_") + "_" + hash(source);
244
259
  let cloneDir;
245
260
  const localNodeModules = resolve(options.cwd, "node_modules");
246
- if (existsSync(localNodeModules)) {
261
+ const parentDir = dirname(options.cwd);
262
+ if (basename(parentDir) === ".c12") {
263
+ cloneDir = join(parentDir, cloneName);
264
+ } else if (existsSync(localNodeModules)) {
247
265
  cloneDir = join(localNodeModules, ".c12", cloneName);
248
266
  } else {
249
267
  cloneDir = process.env.XDG_CACHE_HOME ? resolve(process.env.XDG_CACHE_HOME, "c12", cloneName) : resolve(homedir(), ".cache/c12", cloneName);
@@ -251,7 +269,12 @@ async function resolveConfig(source, options, sourceOptions = {}) {
251
269
  if (existsSync(cloneDir)) {
252
270
  await rm(cloneDir, { recursive: true });
253
271
  }
254
- const cloned = await downloadTemplate(source, { dir: cloneDir });
272
+ const cloned = await downloadTemplate(source, {
273
+ dir: cloneDir,
274
+ install: sourceOptions.install,
275
+ ...options.giget,
276
+ ...sourceOptions.giget
277
+ });
255
278
  source = cloned.dir;
256
279
  }
257
280
  if (NPM_PACKAGE_RE.test(source)) {
@@ -299,6 +322,8 @@ async function resolveConfig(source, options, sourceOptions = {}) {
299
322
  if (res.sourceOptions.overrides) {
300
323
  res.config = defu(res.sourceOptions.overrides, res.config);
301
324
  }
325
+ res.configFile = _normalize(res.configFile);
326
+ res.source = _normalize(res.source);
302
327
  return res;
303
328
  }
304
329
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "c12",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Smart Config Loader",
5
5
  "repository": "unjs/c12",
6
6
  "license": "MIT",
@@ -25,16 +25,16 @@
25
25
  "lint": "eslint --ext .ts,.js,.mjs,.cjs . && prettier -c src test",
26
26
  "lint:fix": "eslint --ext .ts,.js,.mjs,.cjs . --fix && prettier -w src test",
27
27
  "prepack": "unbuild",
28
- "release": "changelogen --release && npm publish && git push --follow-tags",
28
+ "release": "pnpm test && changelogen --release && npm publish && git push --follow-tags",
29
29
  "test": "pnpm lint && vitest run --coverage && pnpm test:types",
30
30
  "test:types": "tsc --noEmit"
31
31
  },
32
32
  "dependencies": {
33
33
  "chokidar": "^3.5.3",
34
- "defu": "^6.1.2",
34
+ "defu": "^6.1.3",
35
35
  "dotenv": "^16.3.1",
36
- "giget": "^1.1.3",
37
- "jiti": "^1.20.0",
36
+ "giget": "^1.2.1",
37
+ "jiti": "^1.21.0",
38
38
  "mlly": "^1.4.2",
39
39
  "ohash": "^1.1.3",
40
40
  "pathe": "^1.1.1",
@@ -43,15 +43,16 @@
43
43
  "rc9": "^2.1.1"
44
44
  },
45
45
  "devDependencies": {
46
- "@vitest/coverage-v8": "^0.34.6",
46
+ "@types/node": "^20.10.5",
47
+ "@vitest/coverage-v8": "^1.1.0",
47
48
  "changelogen": "^0.5.5",
48
- "eslint": "^8.51.0",
49
+ "eslint": "^8.56.0",
49
50
  "eslint-config-unjs": "^0.2.1",
50
51
  "expect-type": "^0.17.3",
51
- "prettier": "^3.0.3",
52
- "typescript": "^5.2.2",
52
+ "prettier": "^3.1.1",
53
+ "typescript": "^5.3.3",
53
54
  "unbuild": "^2.0.0",
54
- "vitest": "^0.34.6"
55
+ "vitest": "^1.1.0"
55
56
  },
56
- "packageManager": "pnpm@8.8.0"
57
+ "packageManager": "pnpm@8.13.1"
57
58
  }