@adamhl8/configs 2.0.0 → 2.2.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 +42 -14
- package/dist/configs/commitlint.base.d.ts +1 -1
- package/dist/configs/commitlint.base.js +1 -1
- package/dist/configs/commitlint.base.js.map +1 -1
- package/dist/configs/knip.base.d.ts +1 -1
- package/dist/configs/knip.base.js +1 -1
- package/dist/configs/knip.base.js.map +1 -1
- package/dist/configs/oxfmt.base.d.ts +1 -1
- package/dist/configs/oxfmt.base.js +1 -1
- package/dist/configs/oxfmt.base.js.map +1 -1
- package/dist/configs/oxlint.base.d.ts +6 -1
- package/dist/configs/oxlint.base.d.ts.map +1 -1
- package/dist/configs/oxlint.base.js +4 -1
- package/dist/configs/oxlint.base.js.map +1 -1
- package/dist/configs/release-it.base.d.ts +1 -1
- package/dist/configs/release-it.base.js +1 -1
- package/dist/configs/release-it.base.js.map +1 -1
- package/dist/configs/tsdown.base.d.ts +1 -1
- package/dist/configs/tsdown.base.d.ts.map +1 -1
- package/dist/configs/tsdown.base.js +1 -1
- package/dist/configs/tsdown.base.js.map +1 -1
- package/dist/merge-config/merge-config.d.ts +19 -0
- package/dist/merge-config/merge-config.d.ts.map +1 -0
- package/dist/merge-config/merge-config.js +30 -0
- package/dist/merge-config/merge-config.js.map +1 -0
- package/dist/merge-config/merge.types.d.ts +51 -0
- package/dist/merge-config/merge.types.d.ts.map +1 -0
- package/package.json +1 -1
- package/dist/utils.d.ts +0 -79
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js +0 -46
- package/dist/utils.js.map +0 -1
package/README.md
CHANGED
|
@@ -8,18 +8,24 @@ bun add -D @adamhl8/configs
|
|
|
8
8
|
|
|
9
9
|
### Config merging
|
|
10
10
|
|
|
11
|
-
Each `fooConfig({ ... })` function deep merges your config into the base config. Arrays are appended to the base array by default. To
|
|
11
|
+
Each `fooConfig({ ... })` function deep merges your config into the base config. Arrays are appended to the base array by default. To use arrays as-is instead, put them in a second config object, which is merged the same way except its arrays replace the base array:
|
|
12
12
|
|
|
13
13
|
```ts
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
import { oxlintConfig } from "@adamhl8/configs"
|
|
15
|
+
|
|
16
|
+
const config = oxlintConfig(
|
|
17
|
+
{
|
|
18
|
+
// appended to the base plugins
|
|
19
|
+
plugins: ["my-plugin"],
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
// used as-is, the base overrides are dropped
|
|
23
|
+
overrides: [],
|
|
24
|
+
},
|
|
25
|
+
)
|
|
20
26
|
```
|
|
21
27
|
|
|
22
|
-
The
|
|
28
|
+
The second config works at any depth: only the arrays you provide are replaced, sibling keys still merge normally, and a key in both objects gets the second object's value. Required keys (e.g. `platform` for `tsdownConfig`) must be provided in the first object.
|
|
23
29
|
|
|
24
30
|
### just
|
|
25
31
|
|
|
@@ -240,16 +246,38 @@ jobs:
|
|
|
240
246
|
secrets: inherit
|
|
241
247
|
```
|
|
242
248
|
|
|
249
|
+
#### Docker
|
|
250
|
+
|
|
251
|
+
Pass `docker-image` to build the repo's `Dockerfile` and push it to `ghcr.io/<owner>/<docker-image>:latest` once the release lands. The image name is separate from the repo name, since they often differ. Omit the input and no image is built.
|
|
252
|
+
|
|
253
|
+
The build checks out the tag the release just created, not `main`, so the image matches the released commit. Callers that use it must also grant `packages: write`, because a called workflow's permissions are capped by what the calling job grants.
|
|
254
|
+
|
|
255
|
+
The optional `GH_TOKEN` secret is passed to the build as a BuildKit secret named `GH_TOKEN`, for images whose build calls the GitHub API. Mount it in the Dockerfile with `RUN --mount=type=secret,id=GH_TOKEN,env=GITHUB_TOKEN`. A Dockerfile that doesn't mount it is unaffected.
|
|
256
|
+
|
|
257
|
+
To deploy after the push, add a job that needs the release. A job waiting on a called workflow waits for all of its jobs, so it runs after the image is pushed.
|
|
258
|
+
|
|
259
|
+
```yaml
|
|
260
|
+
jobs:
|
|
261
|
+
release:
|
|
262
|
+
permissions:
|
|
263
|
+
contents: write
|
|
264
|
+
packages: write
|
|
265
|
+
uses: adamhl8/configs/.github/workflows/release.yml@main
|
|
266
|
+
with:
|
|
267
|
+
docker-image: actual-budget-venmo-importer
|
|
268
|
+
secrets: inherit
|
|
269
|
+
```
|
|
270
|
+
|
|
243
271
|
### Secrets
|
|
244
272
|
|
|
245
273
|
Configure these on each repo that uses the workflows (Settings -> Secrets and variables -> Actions):
|
|
246
274
|
|
|
247
|
-
| Secret | Used by
|
|
248
|
-
| ---------------- |
|
|
249
|
-
| `CI_TOKEN` | `update-deps.yml`
|
|
250
|
-
| `GH_TOKEN` | `ci.yml`
|
|
251
|
-
| `NPM_CI_TOKEN` | `release.yml`
|
|
252
|
-
| `CI_SIGNING_KEY` | `release.yml`
|
|
275
|
+
| Secret | Used by | What it is |
|
|
276
|
+
| ---------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
277
|
+
| `CI_TOKEN` | `update-deps.yml` | PAT with `contents` and `pull-requests` write, so the opened PR triggers CI |
|
|
278
|
+
| `GH_TOKEN` | `ci.yml`, `release.yml` | Optional. PAT for a build that calls the GitHub API. Reaches a `ci.yml` build as `GITHUB_TOKEN`, and a `release.yml` docker build as the `GH_TOKEN` BuildKit secret. Unset, the `ci.yml` build falls back to the automatic token. |
|
|
279
|
+
| `NPM_CI_TOKEN` | `release.yml` | npm automation token with publish access |
|
|
280
|
+
| `CI_SIGNING_KEY` | `release.yml` | Private SSH key that signs the release commit and tag. Add the matching public key to your GitHub account as a Signing Key so the signature verifies as you. Use a dedicated, passphrase-less key, not your personal auth key. |
|
|
253
281
|
|
|
254
282
|
`GITHUB_TOKEN` is provided automatically. The release wrapper grants it `contents: write` to push the release commit and tag and to create the GitHub release.
|
|
255
283
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { OptionalMergeConfigFn } from "../
|
|
1
|
+
import { OptionalMergeConfigFn } from "../merge-config/merge-config.js";
|
|
2
2
|
import { UserConfig } from "@commitlint/types";
|
|
3
3
|
//#region src/configs/commitlint.base.d.ts
|
|
4
4
|
declare const commitlintConfig: OptionalMergeConfigFn<UserConfig, {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createMergeConfigFn } from "../
|
|
1
|
+
import { createMergeConfigFn } from "../merge-config/merge-config.js";
|
|
2
2
|
import conventional from "@commitlint/config-conventional";
|
|
3
3
|
//#region src/configs/commitlint.base.ts
|
|
4
4
|
const [_severity, _condition, conventionalTypes] = conventional.rules["type-enum"];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commitlint.base.js","names":[],"sources":["../../src/configs/commitlint.base.ts"],"sourcesContent":["import conventional from \"@commitlint/config-conventional\"\nimport type { UserConfig } from \"@commitlint/types\"\n\nimport { createMergeConfigFn } from \"#
|
|
1
|
+
{"version":3,"file":"commitlint.base.js","names":[],"sources":["../../src/configs/commitlint.base.ts"],"sourcesContent":["import conventional from \"@commitlint/config-conventional\"\nimport type { UserConfig } from \"@commitlint/types\"\n\nimport { createMergeConfigFn } from \"#merge-config/merge-config.ts\"\n\nconst [_severity, _condition, conventionalTypes] = conventional.rules[\"type-enum\"]\n\nconst EXCLUDED_TYPES = new Set([\"test\", \"style\", \"build\"])\nconst filteredTypes = conventionalTypes.filter((type) => !EXCLUDED_TYPES.has(type))\nconst commitTypes = [...filteredTypes, \"improve\", \"release\"]\n\nconst baseConfig = {\n extends: [\"@commitlint/config-conventional\"],\n rules: {\n \"type-enum\": [2, \"always\", commitTypes],\n },\n} as const satisfies UserConfig\n\nexport const commitlintConfig = createMergeConfigFn<UserConfig, typeof baseConfig>(baseConfig)\n"],"mappings":";;;AAKA,MAAM,CAAC,WAAW,YAAY,qBAAqB,aAAa,MAAM;AAEtE,MAAM,iCAAiB,IAAI,IAAI;CAAC;CAAQ;CAAS;AAAO,CAAC;AAWzD,MAAa,mBAAmB,oBAAmD;CANjF,SAAS,CAAC,iCAAiC;CAC3C,OAAO,EACL,aAAa;EAAC;EAAG;EAAU;GALV,GADC,kBAAkB,QAAQ,SAAS,CAAC,eAAe,IAAI,IAAI,CAC7C;GAAG;GAAW;EAKT;CAAC,EACxC;AAGiF,CAAU"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"knip.base.js","names":[],"sources":["../../src/configs/knip.base.ts"],"sourcesContent":["import type { KnipConfig } from \"knip\"\n\nimport { createMergeConfigFn } from \"#
|
|
1
|
+
{"version":3,"file":"knip.base.js","names":[],"sources":["../../src/configs/knip.base.ts"],"sourcesContent":["import type { KnipConfig } from \"knip\"\n\nimport { createMergeConfigFn } from \"#merge-config/merge-config.ts\"\n\n/** Knip sees `just` in the `prepare` script and the release-it hooks, but it isn't a package.json dependency */\nconst IGNORE_BINARIES = [\"just\"] as const satisfies string[]\n\n/**\n * - Knip resolves `@adamhl8/eslint-plugin-clean-modules` via the oxlint config, but we don't need to install it directly\n * in the consuming project, so it shows as unlisted\n */\nconst IGNORE_DEPENDENCIES = [\"@adamhl8/eslint-plugin-clean-modules\"] as const satisfies string[]\n\nconst baseConfig = {\n project: [\"**/*\"],\n ignoreBinaries: IGNORE_BINARIES,\n ignoreDependencies: IGNORE_DEPENDENCIES,\n bun: {\n entry: [\"**/*.test.ts\"],\n },\n} as const satisfies KnipConfig\n\nexport const knipConfig = createMergeConfigFn<KnipConfig, typeof baseConfig>(baseConfig)\n"],"mappings":";AAsBA,MAAa,aAAa,oBAAmD;CAR3E,SAAS,CAAC,MAAM;CAChB,gBAAgB,CAVO,MAUO;CAC9B,oBAAoB,CALO,sCAKW;CACtC,KAAK,EACH,OAAO,CAAC,cAAc,EACxB;AAG2E,CAAU"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { OptionalMergeConfigFn } from "../
|
|
1
|
+
import { OptionalMergeConfigFn } from "../merge-config/merge-config.js";
|
|
2
2
|
import { OxfmtConfig } from "oxfmt";
|
|
3
3
|
//#region src/configs/oxfmt.base.d.ts
|
|
4
4
|
declare const oxfmtConfig: OptionalMergeConfigFn<OxfmtConfig, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oxfmt.base.js","names":[],"sources":["../../src/configs/oxfmt.base.ts"],"sourcesContent":["import type { OxfmtConfig } from \"oxfmt\"\n\nimport { createMergeConfigFn } from \"#
|
|
1
|
+
{"version":3,"file":"oxfmt.base.js","names":[],"sources":["../../src/configs/oxfmt.base.ts"],"sourcesContent":["import type { OxfmtConfig } from \"oxfmt\"\n\nimport { createMergeConfigFn } from \"#merge-config/merge-config.ts\"\n\nconst baseConfig = {\n printWidth: 120,\n semi: false,\n jsdoc: true,\n sortImports: {\n groups: [\n \"builtin\",\n \"external\",\n [\"subpath\", \"internal\"],\n [\"index\", \"sibling\", \"parent\"],\n \"side_effect\",\n [\"style\", \"side_effect_style\"],\n ],\n },\n sortTailwindcss: true,\n} as const satisfies OxfmtConfig\n\nexport const oxfmtConfig = createMergeConfigFn<OxfmtConfig, typeof baseConfig>(baseConfig)\n"],"mappings":";AAqBA,MAAa,cAAc,oBAAoD;CAhB7E,YAAY;CACZ,MAAM;CACN,OAAO;CACP,aAAa,EACX,QAAQ;EACN;EACA;EACA,CAAC,WAAW,UAAU;EACtB;GAAC;GAAS;GAAW;EAAQ;EAC7B;EACA,CAAC,SAAS,mBAAmB;CAC/B,EACF;CACA,iBAAiB;AAG4D,CAAU"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { OptionalMergeConfigFn } from "../
|
|
1
|
+
import { OptionalMergeConfigFn } from "../merge-config/merge-config.js";
|
|
2
2
|
import { OxlintConfig } from "oxlint";
|
|
3
3
|
//#region src/configs/oxlint.base.d.ts
|
|
4
4
|
declare const oxlintConfig: OptionalMergeConfigFn<OxlintConfig, {
|
|
@@ -23,6 +23,11 @@ declare const oxlintConfig: OptionalMergeConfigFn<OxlintConfig, {
|
|
|
23
23
|
readonly rules: {
|
|
24
24
|
readonly "import/no-default-export": "off";
|
|
25
25
|
};
|
|
26
|
+
}, {
|
|
27
|
+
readonly files: [".release-it.ts"];
|
|
28
|
+
readonly rules: {
|
|
29
|
+
readonly "no-template-curly-in-string": "off";
|
|
30
|
+
};
|
|
26
31
|
}];
|
|
27
32
|
readonly rules: {
|
|
28
33
|
readonly "clean-modules/require-direct-exports": "error";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oxlint.base.d.ts","names":[],"sources":["../../src/configs/oxlint.base.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"oxlint.base.d.ts","names":[],"sources":["../../src/configs/oxlint.base.ts"],"mappings":";;;cAkJa,cAAY,sBAAA;;aA1IrB;aACA;aACA;;;aAIA;aACA;aACA;aACA;aACA;aACA;aACA;;;;;;;eAkCI;;;;;eAMA;;;;;;;;;;;;;;;;;;;iBAiBS;iBAAiB"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createMergeConfigFn } from "../
|
|
1
|
+
import { createMergeConfigFn } from "../merge-config/merge-config.js";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
3
|
const oxlintConfig = createMergeConfigFn({
|
|
4
4
|
options: {
|
|
@@ -42,6 +42,9 @@ const oxlintConfig = createMergeConfigFn({
|
|
|
42
42
|
"tsdown.config.ts"
|
|
43
43
|
],
|
|
44
44
|
rules: { "import/no-default-export": "off" }
|
|
45
|
+
}, {
|
|
46
|
+
files: [".release-it.ts"],
|
|
47
|
+
rules: { "no-template-curly-in-string": "off" }
|
|
45
48
|
}],
|
|
46
49
|
rules: {
|
|
47
50
|
"clean-modules/require-direct-exports": "error",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oxlint.base.js","names":[],"sources":["../../src/configs/oxlint.base.ts"],"sourcesContent":["import { fileURLToPath } from \"node:url\"\n\nimport type { OxlintConfig } from \"oxlint\"\n\nimport { createMergeConfigFn } from \"#
|
|
1
|
+
{"version":3,"file":"oxlint.base.js","names":[],"sources":["../../src/configs/oxlint.base.ts"],"sourcesContent":["import { fileURLToPath } from \"node:url\"\n\nimport type { OxlintConfig } from \"oxlint\"\n\nimport { createMergeConfigFn } from \"#merge-config/merge-config.ts\"\n\nconst baseConfig = {\n options: {\n reportUnusedDisableDirectives: \"error\",\n typeAware: true,\n typeCheck: true,\n },\n\n categories: {\n correctness: \"error\",\n nursery: \"error\",\n pedantic: \"error\",\n perf: \"error\",\n restriction: \"error\",\n style: \"error\",\n suspicious: \"error\",\n },\n\n plugins: [\n \"eslint\",\n \"react\",\n \"unicorn\",\n \"typescript\",\n \"oxc\",\n \"import\",\n \"jsdoc\",\n \"vitest\",\n \"jsx-a11y\",\n \"react-perf\",\n \"promise\",\n \"node\",\n ],\n\n // Resolve to an absolute path so consumers load it from this package's own deps.\n jsPlugins: [fileURLToPath(import.meta.resolve(\"@adamhl8/eslint-plugin-clean-modules\"))],\n\n overrides: [\n {\n files: [\n \".release-it.ts\",\n \"astro.config.ts\",\n \"commitlint.config.ts\",\n \"knip.ts\",\n \"oxfmt.config.ts\",\n \"oxlint.config.ts\",\n \"prisma.config.ts\",\n \"tsdown.config.ts\",\n ],\n rules: {\n \"import/no-default-export\": \"off\",\n },\n },\n {\n files: [\".release-it.ts\"],\n rules: {\n \"no-template-curly-in-string\": \"off\",\n },\n },\n ],\n\n rules: {\n \"clean-modules/require-direct-exports\": \"error\",\n \"clean-modules/require-import-extensions\": \"error\",\n \"clean-modules/require-subpath-imports\": \"error\",\n\n curly: [\"error\", \"multi-or-nest\"],\n \"func-style\": [\"error\", \"expression\"],\n \"react/jsx-curly-brace-presence\": [\"error\", { children: \"never\", propElementValues: \"always\", props: \"never\" }],\n \"require-unicode-regexp\": [\"error\", { requireFlag: \"v\" }],\n \"unicorn/filename-case\": [\n \"error\",\n {\n cases: { kebabCase: true, pascalCase: true },\n },\n ],\n \"unicorn/no-useless-undefined\": [\"error\", { checkArguments: false }],\n\n \"capitalized-comments\": \"off\",\n \"id-length\": \"off\",\n \"import/exports-last\": \"off\",\n \"import/group-exports\": \"off\",\n \"import/max-dependencies\": \"off\",\n \"import/no-named-export\": \"off\",\n \"import/no-nodejs-modules\": \"off\",\n \"import/no-unassigned-import\": \"off\",\n \"import/prefer-default-export\": \"off\",\n \"init-declarations\": \"off\",\n \"jsdoc/require-param\": \"off\",\n \"jsdoc/require-returns\": \"off\",\n \"max-classes-per-file\": \"off\",\n \"max-lines\": \"off\",\n \"max-lines-per-function\": \"off\",\n \"max-statements\": \"off\",\n \"no-console\": \"off\",\n \"no-continue\": \"off\",\n \"no-duplicate-imports\": \"off\",\n \"no-inline-comments\": \"off\",\n \"no-magic-numbers\": \"off\",\n \"no-plusplus\": \"off\",\n \"no-ternary\": \"off\",\n \"no-undef\": \"off\",\n \"no-undefined\": \"off\",\n \"no-useless-return\": \"off\",\n \"no-void\": \"off\",\n \"node/callback-return\": \"off\",\n \"node/no-sync\": \"off\",\n \"oxc/no-async-await\": \"off\",\n \"oxc/no-optional-chaining\": \"off\",\n \"oxc/no-rest-spread-properties\": \"off\",\n \"react/forbid-component-props\": \"off\",\n \"react/jsx-filename-extension\": \"off\",\n \"react/jsx-max-depth\": \"off\",\n \"react/jsx-no-literals\": \"off\",\n \"react/react-in-jsx-scope\": \"off\",\n \"require-await\": \"off\",\n \"sort-imports\": \"off\",\n \"sort-keys\": \"off\",\n \"typescript/consistent-return\": \"off\",\n \"typescript/explicit-function-return-type\": \"off\",\n \"typescript/explicit-module-boundary-types\": \"off\",\n \"typescript/prefer-readonly-parameter-types\": \"off\",\n \"typescript/strict-boolean-expressions\": \"off\",\n \"unicorn/no-array-callback-reference\": \"off\",\n \"unicorn/no-array-method-this-argument\": \"off\",\n \"unicorn/no-await-expression-member\": \"off\",\n \"unicorn/no-process-exit\": \"off\",\n \"unicorn/number-literal-case\": \"off\",\n \"unicorn/switch-case-braces\": \"off\",\n \"vitest/no-hooks\": \"off\",\n \"vitest/no-importing-vitest-globals\": \"off\",\n \"vitest/prefer-describe-function-title\": \"off\",\n \"vitest/prefer-expect-assertions\": \"off\",\n \"vitest/prefer-importing-vitest-globals\": \"off\",\n \"vitest/prefer-lowercase-title\": \"off\",\n \"vitest/prefer-to-be-falsy\": \"off\",\n \"vitest/prefer-to-be-truthy\": \"off\",\n \"vitest/require-hook\": \"off\", // reenable when false positives are fixed?\n \"vitest/require-test-timeout\": \"off\",\n },\n} as const satisfies OxlintConfig\n\nexport const oxlintConfig = createMergeConfigFn<OxlintConfig, typeof baseConfig>(baseConfig)\n"],"mappings":";;AAkJA,MAAa,eAAe,oBAAqD;CA3I/E,SAAS;EACP,+BAA+B;EAC/B,WAAW;EACX,WAAW;CACb;CAEA,YAAY;EACV,aAAa;EACb,SAAS;EACT,UAAU;EACV,MAAM;EACN,aAAa;EACb,OAAO;EACP,YAAY;CACd;CAEA,SAAS;EACP;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;CAGA,WAAW,CAAC,cAAc,OAAO,KAAK,QAAQ,sCAAsC,CAAC,CAAC;CAEtF,WAAW,CACT;EACE,OAAO;GACL;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,OAAO,EACL,4BAA4B,MAC9B;CACF,GACA;EACE,OAAO,CAAC,gBAAgB;EACxB,OAAO,EACL,+BAA+B,MACjC;CACF,CACF;CAEA,OAAO;EACL,wCAAwC;EACxC,2CAA2C;EAC3C,yCAAyC;EAEzC,OAAO,CAAC,SAAS,eAAe;EAChC,cAAc,CAAC,SAAS,YAAY;EACpC,kCAAkC,CAAC,SAAS;GAAE,UAAU;GAAS,mBAAmB;GAAU,OAAO;EAAQ,CAAC;EAC9G,0BAA0B,CAAC,SAAS,EAAE,aAAa,IAAI,CAAC;EACxD,yBAAyB,CACvB,SACA,EACE,OAAO;GAAE,WAAW;GAAM,YAAY;EAAK,EAC7C,CACF;EACA,gCAAgC,CAAC,SAAS,EAAE,gBAAgB,MAAM,CAAC;EAEnE,wBAAwB;EACxB,aAAa;EACb,uBAAuB;EACvB,wBAAwB;EACxB,2BAA2B;EAC3B,0BAA0B;EAC1B,4BAA4B;EAC5B,+BAA+B;EAC/B,gCAAgC;EAChC,qBAAqB;EACrB,uBAAuB;EACvB,yBAAyB;EACzB,wBAAwB;EACxB,aAAa;EACb,0BAA0B;EAC1B,kBAAkB;EAClB,cAAc;EACd,eAAe;EACf,wBAAwB;EACxB,sBAAsB;EACtB,oBAAoB;EACpB,eAAe;EACf,cAAc;EACd,YAAY;EACZ,gBAAgB;EAChB,qBAAqB;EACrB,WAAW;EACX,wBAAwB;EACxB,gBAAgB;EAChB,sBAAsB;EACtB,4BAA4B;EAC5B,iCAAiC;EACjC,gCAAgC;EAChC,gCAAgC;EAChC,uBAAuB;EACvB,yBAAyB;EACzB,4BAA4B;EAC5B,iBAAiB;EACjB,gBAAgB;EAChB,aAAa;EACb,gCAAgC;EAChC,4CAA4C;EAC5C,6CAA6C;EAC7C,8CAA8C;EAC9C,yCAAyC;EACzC,uCAAuC;EACvC,yCAAyC;EACzC,sCAAsC;EACtC,2BAA2B;EAC3B,+BAA+B;EAC/B,8BAA8B;EAC9B,mBAAmB;EACnB,sCAAsC;EACtC,yCAAyC;EACzC,mCAAmC;EACnC,0CAA0C;EAC1C,iCAAiC;EACjC,6BAA6B;EAC7B,8BAA8B;EAC9B,uBAAuB;EACvB,+BAA+B;CACjC;AAG+E,CAAU"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { OptionalMergeConfigFn } from "../
|
|
1
|
+
import { OptionalMergeConfigFn } from "../merge-config/merge-config.js";
|
|
2
2
|
import { Config } from "release-it";
|
|
3
3
|
//#region src/configs/release-it.base.d.ts
|
|
4
4
|
declare const releaseItConfig: OptionalMergeConfigFn<Config, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"release-it.base.js","names":[],"sources":["../../src/configs/release-it.base.ts"],"sourcesContent":["// oxlint-disable no-template-curly-in-string\nimport type { Config } from \"release-it\"\n\nimport { createMergeConfigFn } from \"#
|
|
1
|
+
{"version":3,"file":"release-it.base.js","names":[],"sources":["../../src/configs/release-it.base.ts"],"sourcesContent":["// oxlint-disable no-template-curly-in-string\nimport type { Config } from \"release-it\"\n\nimport { createMergeConfigFn } from \"#merge-config/merge-config.ts\"\n\nconst baseConfig = {\n hooks: {\n \"before:init\": \"just build\",\n \"after:bump\": [\"bun adamhl8-cliff --tag ${version} -o\", \"bun oxfmt CHANGELOG.md\"],\n },\n git: {\n commitMessage: \"release: ${version}\",\n tagName: \"${version}\",\n tagAnnotation: \"release ${version}\",\n addUntrackedFiles: true,\n },\n github: {\n release: true,\n releaseName: \"${version}\",\n releaseNotes: \"bun adamhl8-cliff --unreleased --tag ${version}\",\n },\n npm: {\n publishPackageManager: \"bun\",\n publishPath: \"\",\n publishArgs: [\"--ignore-scripts\"],\n },\n} as const satisfies Config\n\nexport const releaseItConfig = createMergeConfigFn<Config, typeof baseConfig>(baseConfig)\n"],"mappings":";AA4BA,MAAa,kBAAkB,oBAA+C;CAtB5E,OAAO;EACL,eAAe;EACf,cAAc,CAAC,yCAAyC,wBAAwB;CAClF;CACA,KAAK;EACH,eAAe;EACf,SAAS;EACT,eAAe;EACf,mBAAmB;CACrB;CACA,QAAQ;EACN,SAAS;EACT,aAAa;EACb,cAAc;CAChB;CACA,KAAK;EACH,uBAAuB;EACvB,aAAa;EACb,aAAa,CAAC,kBAAkB;CAClC;AAG4E,CAAU"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tsdown.base.d.ts","names":[],"sources":["../../src/configs/tsdown.base.ts"],"mappings":";;;;KAOK,eAAe,YAAY;cAE1B;;;;;;;;;;aAUF;aACA;aACA;;;aAGA;;;aAGA;aACA;;;;;KAOC,qBAAqB,YAAY;cAGhC;;;;;;;aAdF;;;;;;;;;;aAuBA;eACE;;;;KAMD,kBAAkB,YAAY;cAG7B;;;;;;aAjCF;;;;;;;;;;aAuBA;eACE;;;;;;;;
|
|
1
|
+
{"version":3,"file":"tsdown.base.d.ts","names":[],"sources":["../../src/configs/tsdown.base.ts"],"mappings":";;;;KAOK,eAAe,YAAY;cAE1B;;;;;;;;;;aAUF;aACA;aACA;;;aAGA;;;aAGA;aACA;;;;;KAOC,qBAAqB,YAAY;cAGhC;;;;;;;aAdF;;;;;;;;;;aAuBA;eACE;;;;KAMD,kBAAkB,YAAY;cAG7B;;;;;;aAjCF;;;;;;;;;;aAuBA;eACE;;;;;;;;cAiBO,cAAc,cAAc,qBAAqB;cAKjD,oBAAoB,cAAc,2BAA2B;cAK7D,iBAAiB,cAAc,wBAAwB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tsdown.base.js","names":[],"sources":["../../src/configs/tsdown.base.ts"],"sourcesContent":["import type { UserConfig } from \"tsdown\"\nimport type { SetRequired } from \"type-fest\"\n\nimport type { MergeConfigFn } from \"#
|
|
1
|
+
{"version":3,"file":"tsdown.base.js","names":[],"sources":["../../src/configs/tsdown.base.ts"],"sourcesContent":["import type { UserConfig } from \"tsdown\"\nimport type { SetRequired } from \"type-fest\"\n\nimport type { MergeConfigFn } from \"#merge-config/merge-config.ts\"\nimport { createMergeConfigFn } from \"#merge-config/merge-config.ts\"\n\n// Force projects to specify platform\ntype StrictConfig = SetRequired<UserConfig, \"platform\">\n\nconst baseConfig = {\n entry: [\"./src/index.ts\"],\n platform: \"neutral\",\n target: false,\n unbundle: true,\n sourcemap: true,\n minify: \"dce-only\",\n fixedExtension: false,\n hash: false,\n dts: {\n newContext: true,\n tsgo: true,\n sourcemap: true,\n },\n deps: {\n neverBundle: [\"bun\"],\n },\n attw: {\n level: \"error\",\n profile: \"esm-only\",\n },\n publint: true,\n failOnWarn: true,\n} as const satisfies StrictConfig\n\n// Force projects to specify platform and entry\ntype StrictBundleConfig = SetRequired<UserConfig, \"platform\" | \"entry\">\n\n// Single-file bundle (not a published library): bundle everything, drop types/lint/sourcemap.\nconst bundleConfig = {\n ...baseConfig,\n entry: [],\n unbundle: false,\n sourcemap: false,\n dts: false,\n attw: false,\n publint: false,\n inputOptions: {\n experimental: {\n attachDebugInfo: \"none\", // remove region comments from bundled output\n },\n },\n} as const satisfies StrictBundleConfig\n\n// Force projects to specify platform and entry\ntype StrictBinConfig = SetRequired<UserConfig, \"entry\">\n\n// A bin is a node bundle with no output file extension.\nconst binConfig = {\n ...bundleConfig,\n platform: \"node\",\n outExtensions: () => ({ js: \"\" }) as const,\n} as const satisfies StrictBinConfig\n\n// The export annotations forbid the no-arg call (platform/entry must be provided) and are load-bearing for\n// declaration emit: without one, tsgo synthesizes the type and fails with TS2883 on tsdown's non-exported internals.\nexport const tsdownConfig: MergeConfigFn<StrictConfig, typeof baseConfig> = createMergeConfigFn<\n StrictConfig,\n typeof baseConfig\n>(baseConfig)\n\nexport const tsdownBundleConfig: MergeConfigFn<StrictBundleConfig, typeof bundleConfig> = createMergeConfigFn<\n StrictBundleConfig,\n typeof bundleConfig\n>(bundleConfig)\n\nexport const tsdownBinConfig: MergeConfigFn<StrictBinConfig, typeof binConfig> = createMergeConfigFn<\n StrictBinConfig,\n typeof binConfig\n>(binConfig)\n"],"mappings":";;AASA,MAAM,aAAa;CACjB,OAAO,CAAC,gBAAgB;CACxB,UAAU;CACV,QAAQ;CACR,UAAU;CACV,WAAW;CACX,QAAQ;CACR,gBAAgB;CAChB,MAAM;CACN,KAAK;EACH,YAAY;EACZ,MAAM;EACN,WAAW;CACb;CACA,MAAM,EACJ,aAAa,CAAC,KAAK,EACrB;CACA,MAAM;EACJ,OAAO;EACP,SAAS;CACX;CACA,SAAS;CACT,YAAY;AACd;AAMA,MAAM,eAAe;CACnB,GAAG;CACH,OAAO,CAAC;CACR,UAAU;CACV,WAAW;CACX,KAAK;CACL,MAAM;CACN,SAAS;CACT,cAAc,EACZ,cAAc,EACZ,iBAAiB,OACnB,EACF;AACF;AAMA,MAAM,YAAY;CAChB,GAAG;CACH,UAAU;CACV,sBAAsB,EAAE,IAAI,GAAG;AACjC;AAIA,MAAa,eAA+D,oBAG1E,UAAU;AAEZ,MAAa,qBAA6E,oBAGxF,YAAY;AAEd,MAAa,kBAAoE,oBAG/E,SAAS"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Clean, MergedConfig } from "./merge.types.js";
|
|
2
|
+
import { ReadonlyDeep } from "type-fest";
|
|
3
|
+
//#region src/merge-config/merge-config.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* The merge config function. `userConfig` is deep merged into `BaseConfig` with arrays appended to the base array. The
|
|
6
|
+
* optional `replaceConfig` is then deep merged into that result with arrays used as-is, so putting an array there
|
|
7
|
+
* replaces just that key (sibling keys still merge normally, and a key in both configs gets the replace config's
|
|
8
|
+
* value). Required keys must be provided in `userConfig`.
|
|
9
|
+
*
|
|
10
|
+
* The `const` type parameters keep literal types without requiring `as const` at the call site. The constraints accept
|
|
11
|
+
* readonly arrays/objects (e.g. from `as const`), and the result is writable so it stays assignable to the tools' own
|
|
12
|
+
* (mutable) config types.
|
|
13
|
+
*/
|
|
14
|
+
type MergeConfigFn<UserConfig, BaseConfig> = (<const UserConfigToMerge extends ReadonlyDeep<UserConfig>>(userConfig: UserConfigToMerge) => MergedConfig<BaseConfig, UserConfigToMerge>) & (<const UserConfigToMerge extends ReadonlyDeep<UserConfig>, const ReplaceConfigToMerge extends ReadonlyDeep<Partial<UserConfig>>>(userConfig: UserConfigToMerge, replaceConfig: ReplaceConfigToMerge) => MergedConfig<MergedConfig<BaseConfig, UserConfigToMerge>, ReplaceConfigToMerge, "replace">);
|
|
15
|
+
/** A MergeConfigFn that can also be called with no arguments, returning a writable clone of the base config. */
|
|
16
|
+
type OptionalMergeConfigFn<UserConfig, BaseConfig> = MergeConfigFn<UserConfig, BaseConfig> & (() => Clean<BaseConfig>);
|
|
17
|
+
//#endregion
|
|
18
|
+
export { MergeConfigFn, OptionalMergeConfigFn };
|
|
19
|
+
//# sourceMappingURL=merge-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-config.d.ts","names":[],"sources":["../../src/merge-config/merge-config.ts"],"mappings":";;;;;;;;;;;;;KAgCY,cAAc,YAAY,sBAAsB,0BAA0B,aAAa,aACjG,YAAY,sBACT,aAAa,YAAY,8BAEpB,0BAA0B,aAAa,mBACvC,6BAA6B,aAAa,QAAQ,cAExD,YAAY,mBACZ,eAAe,yBACZ,aAAa,aAAa,YAAY,oBAAoB;;KAGrD,sBAAsB,YAAY,cAAc,cAAc,YAAY,qBAC7E,MAAM"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { cloneDeep, mergeWith } from "es-toolkit";
|
|
2
|
+
//#region src/merge-config/merge-config.ts
|
|
3
|
+
/**
|
|
4
|
+
* The runtime counterpart of MergedConfig in merge.types.ts: a deep merge whose customizer applies the array mode. The
|
|
5
|
+
* source is pre-cloned so the result never aliases the caller's objects. The merge visits string keys only, so
|
|
6
|
+
* symbol-keyed properties in user configs are dropped (the types mirror this).
|
|
7
|
+
*/
|
|
8
|
+
const merge = (target, source, mode) => mergeWith(target, cloneDeep(source), (objValue, srcValue) => {
|
|
9
|
+
if (!Array.isArray(srcValue)) return;
|
|
10
|
+
const userArray = srcValue;
|
|
11
|
+
if (mode === "spread" && Array.isArray(objValue)) return [...objValue, ...userArray];
|
|
12
|
+
return userArray;
|
|
13
|
+
});
|
|
14
|
+
/**
|
|
15
|
+
* Creates a config merge function with proper types for merging with a base config. `UserConfig` is not inferrable, so
|
|
16
|
+
* both type arguments must be passed explicitly.
|
|
17
|
+
*/
|
|
18
|
+
const createMergeConfigFn = (baseConfig) => {
|
|
19
|
+
const mergeConfigFn = (userConfig, replaceConfig) => {
|
|
20
|
+
const result = cloneDeep(baseConfig);
|
|
21
|
+
if (userConfig !== void 0) merge(result, userConfig, "spread");
|
|
22
|
+
if (replaceConfig !== void 0) merge(result, replaceConfig, "replace");
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
return mergeConfigFn;
|
|
26
|
+
};
|
|
27
|
+
//#endregion
|
|
28
|
+
export { createMergeConfigFn };
|
|
29
|
+
|
|
30
|
+
//# sourceMappingURL=merge-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-config.js","names":["baseArray"],"sources":["../../src/merge-config/merge-config.ts"],"sourcesContent":["/* oxlint-disable unicorn/prefer-structured-clone -- structuredClone does not properly clone functions */\nimport { cloneDeep, mergeWith } from \"es-toolkit\"\nimport type { ReadonlyDeep } from \"type-fest\"\n\nimport type { Clean, MergedConfig, Mode } from \"#merge-config/merge.types.ts\"\n\n/**\n * The runtime counterpart of MergedConfig in merge.types.ts: a deep merge whose customizer applies the array mode. The\n * source is pre-cloned so the result never aliases the caller's objects. The merge visits string keys only, so\n * symbol-keyed properties in user configs are dropped (the types mirror this).\n */\nconst merge = (target: object, source: object, mode: Mode): object =>\n mergeWith(target, cloneDeep(source), (objValue: unknown, srcValue: unknown) => {\n if (!Array.isArray(srcValue)) return\n const userArray: unknown[] = srcValue\n if (mode === \"spread\" && Array.isArray(objValue)) {\n const baseArray: unknown[] = objValue\n return [...baseArray, ...userArray]\n }\n return userArray\n })\n\n/**\n * The merge config function. `userConfig` is deep merged into `BaseConfig` with arrays appended to the base array. The\n * optional `replaceConfig` is then deep merged into that result with arrays used as-is, so putting an array there\n * replaces just that key (sibling keys still merge normally, and a key in both configs gets the replace config's\n * value). Required keys must be provided in `userConfig`.\n *\n * The `const` type parameters keep literal types without requiring `as const` at the call site. The constraints accept\n * readonly arrays/objects (e.g. from `as const`), and the result is writable so it stays assignable to the tools' own\n * (mutable) config types.\n */\nexport type MergeConfigFn<UserConfig, BaseConfig> = (<const UserConfigToMerge extends ReadonlyDeep<UserConfig>>(\n userConfig: UserConfigToMerge,\n) => MergedConfig<BaseConfig, UserConfigToMerge>) &\n (<\n const UserConfigToMerge extends ReadonlyDeep<UserConfig>,\n const ReplaceConfigToMerge extends ReadonlyDeep<Partial<UserConfig>>,\n >(\n userConfig: UserConfigToMerge,\n replaceConfig: ReplaceConfigToMerge,\n ) => MergedConfig<MergedConfig<BaseConfig, UserConfigToMerge>, ReplaceConfigToMerge, \"replace\">)\n\n/** A MergeConfigFn that can also be called with no arguments, returning a writable clone of the base config. */\nexport type OptionalMergeConfigFn<UserConfig, BaseConfig> = MergeConfigFn<UserConfig, BaseConfig> &\n (() => Clean<BaseConfig>)\n\n/**\n * Creates a config merge function with proper types for merging with a base config. `UserConfig` is not inferrable, so\n * both type arguments must be passed explicitly.\n */\nexport const createMergeConfigFn = <UserConfig extends object, BaseConfig extends ReadonlyDeep<UserConfig>>(\n baseConfig: BaseConfig,\n): OptionalMergeConfigFn<UserConfig, BaseConfig> => {\n // Loosely typed on purpose, the assertion below gives it the precise public signature\n const mergeConfigFn = (userConfig?: object, replaceConfig?: object) => {\n // Start from a clone so consumers can't mutate the shared base config\n const result = cloneDeep(baseConfig)\n if (userConfig !== undefined) merge(result, userConfig, \"spread\")\n if (replaceConfig !== undefined) merge(result, replaceConfig, \"replace\")\n return result\n }\n\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion\n return mergeConfigFn as OptionalMergeConfigFn<UserConfig, BaseConfig>\n}\n"],"mappings":";;;;;;;AAWA,MAAM,SAAS,QAAgB,QAAgB,SAC7C,UAAU,QAAQ,UAAU,MAAM,IAAI,UAAmB,aAAsB;CAC7E,IAAI,CAAC,MAAM,QAAQ,QAAQ,GAAG;CAC9B,MAAM,YAAuB;CAC7B,IAAI,SAAS,YAAY,MAAM,QAAQ,QAAQ,GAE7C,OAAO,CAAC,GAAGA,UAAW,GAAG,SAAS;CAEpC,OAAO;AACT,CAAC;;;;;AA+BH,MAAa,uBACX,eACkD;CAElD,MAAM,iBAAiB,YAAqB,kBAA2B;EAErE,MAAM,SAAS,UAAU,UAAU;EACnC,IAAI,eAAe,KAAA,GAAW,MAAM,QAAQ,YAAY,QAAQ;EAChE,IAAI,kBAAkB,KAAA,GAAW,MAAM,QAAQ,eAAe,SAAS;EACvE,OAAO;CACT;CAGA,OAAO;AACT"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
//#region src/merge-config/merge.types.d.ts
|
|
2
|
+
type AnyFunction = (...args: never[]) => unknown;
|
|
3
|
+
/** How user arrays combine with base arrays: appended in spread mode, used as-is in replace mode. */
|
|
4
|
+
type Mode = "spread" | "replace";
|
|
5
|
+
/**
|
|
6
|
+
* Recursion budget for the merge walk, threaded through every recursive type below. When a call fails the generic
|
|
7
|
+
* constraint, these types run over the full constraint (the whole tool config type, with circular types like Console in
|
|
8
|
+
* it), and the budget makes that walk terminate instead of exploding.
|
|
9
|
+
*/
|
|
10
|
+
type MaxDepth = readonly [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
|
11
|
+
/**
|
|
12
|
+
* Values the walk must keep as-is instead of mapping over (a mapped type would mangle their methods into plain
|
|
13
|
+
* properties): functions, the toStringTag-less built-ins, and everything declaring `Symbol.toStringTag` (Map, Set,
|
|
14
|
+
* Promise, URL, typed arrays, streams, ...).
|
|
15
|
+
*/
|
|
16
|
+
type NonRecursive = AnyFunction | Date | RegExp | Error | {
|
|
17
|
+
readonly [Symbol.toStringTag]: string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Makes a type deeply writable, keeping tuples, functions, and built-ins as-is. Symbol keys are kept: values that reach
|
|
21
|
+
* Clean land in the result as wholesale clones at runtime, which preserve them (and filtering them here would mangle
|
|
22
|
+
* interface-shaped values with symbol-keyed members, e.g. anything iterable). The `T extends object` gate comes first
|
|
23
|
+
* so primitive leaves (most nodes of a config type) cost one relation, and only recursion spends the depth budget.
|
|
24
|
+
*/
|
|
25
|
+
type Clean<T, Depth extends readonly unknown[] = MaxDepth> = T extends object ? Depth extends readonly [unknown, ...infer RemainingDepth] ? T extends NonRecursive ? T : { -readonly [K in keyof T]: Clean<T[K], RemainingDepth>; } : T : T;
|
|
26
|
+
/**
|
|
27
|
+
* In spread mode, base and user arrays concatenate to an element-union array. In replace mode, or over a non-array
|
|
28
|
+
* base, the user array is used as-is.
|
|
29
|
+
*/
|
|
30
|
+
type MergeArrays<BaseValue, UserValue extends readonly unknown[], Depth extends readonly unknown[], M extends Mode> = M extends "spread" ? BaseValue extends readonly unknown[] ? (Clean<BaseValue[number], Depth> | Clean<UserValue[number], Depth>)[] : Clean<UserValue, Depth> : Clean<UserValue, Depth>;
|
|
31
|
+
/**
|
|
32
|
+
* Merge one key's values: combine arrays per the mode, recurse into objects, else user wins. Gated on `UserValue
|
|
33
|
+
* extends object` first for the same primitive-leaf reason as Clean.
|
|
34
|
+
*/
|
|
35
|
+
type MergeValues<BaseValue, UserValue, Depth extends readonly unknown[], M extends Mode> = UserValue extends object ? UserValue extends readonly unknown[] ? MergeArrays<BaseValue, UserValue, Depth, M> : UserValue extends NonRecursive ? UserValue : BaseValue extends readonly unknown[] | NonRecursive ? Clean<UserValue, Depth> : BaseValue extends object ? MergeObjects<BaseValue, UserValue, Depth, M> : Clean<UserValue, Depth> : UserValue;
|
|
36
|
+
/**
|
|
37
|
+
* Merge two object types key by key, in base key order. An optional user key (only possible when the user passes a
|
|
38
|
+
* wide-typed value rather than a literal) may be absent at runtime, so it unions with the base value. User-only symbol
|
|
39
|
+
* keys are dropped because the runtime merge visits string keys only. The final `extends infer Merged` flattens the
|
|
40
|
+
* intersection through an anonymous mapped type: a named helper alias would show up as `SomeAlias<...>` in hovers
|
|
41
|
+
* instead of the plain object.
|
|
42
|
+
*/
|
|
43
|
+
type MergeObjects<BaseConfig, UserConfig, Depth extends readonly unknown[], M extends Mode> = Depth extends readonly [unknown, ...infer RemainingDepth] ? { -readonly [K in keyof BaseConfig]: K extends keyof UserConfig ? undefined extends UserConfig[K] ? MergeValues<BaseConfig[K], Exclude<UserConfig[K], undefined>, RemainingDepth, M> | Clean<BaseConfig[K], RemainingDepth> : MergeValues<BaseConfig[K], UserConfig[K], RemainingDepth, M> : Clean<BaseConfig[K], RemainingDepth>; } & { -readonly [K in keyof UserConfig as K extends keyof BaseConfig | symbol ? never : K]: Clean<UserConfig[K], RemainingDepth>; } extends (infer Merged) ? { [K in keyof Merged]: Merged[K]; } : never : UserConfig;
|
|
44
|
+
/**
|
|
45
|
+
* The merged result type, exact per key: spread-mode arrays show the element-union of base and user, replace-mode
|
|
46
|
+
* arrays show only the user's elements.
|
|
47
|
+
*/
|
|
48
|
+
type MergedConfig<BaseConfig, UserConfig, M extends Mode = "spread"> = MergeObjects<BaseConfig, UserConfig, MaxDepth, M>;
|
|
49
|
+
//#endregion
|
|
50
|
+
export { Clean, MergedConfig, Mode };
|
|
51
|
+
//# sourceMappingURL=merge.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge.types.d.ts","names":[],"sources":["../../src/merge-config/merge.types.ts"],"mappings":";KAWK,kBAAkB;;KAGX;;;;;;KAOP;;;;;;KAOA,eAAe,cAAc,OAAO,SAAS;YAAoB,OAAO;;;;;;;;KAQjE,MAAM,GAAG,mCAAmC,YAAY,mBAChE,0CAA0C,kBACxC,UAAU,eACR,iBACa,WAAW,IAAI,MAAM,EAAE,IAAI,qBAC1C,IACF;;;;;KAMC,YACH,WACA,sCACA,kCACA,UAAU,QACR,qBACA,wCACG,MAAM,mBAAmB,SAAS,MAAM,mBAAmB,YAC5D,MAAM,WAAW,SACnB,MAAM,WAAW;;;;;KAMhB,YAAY,WAAW,WAAW,kCAAkC,UAAU,QAAQ,2BACvF,uCACE,YAAY,WAAW,WAAW,OAAO,KACzC,kBAAkB,eAChB,YACA,uCAAuC,eACrC,MAAM,WAAW,SACjB,2BACE,aAAa,WAAW,WAAW,OAAO,KAC1C,MAAM,WAAW,SACzB;;;;;;;;KASC,aAAa,YAAY,YAAY,kCAAkC,UAAU,QAAQ,0CAEnF,+BAGM,WAAW,aAAa,gBAAgB,+BAC7B,WAAW,KAEvB,YAAY,WAAW,IAAI,QAAQ,WAAW,gBAAgB,gBAAgB,KAC9E,MAAM,WAAW,IAAI,kBACzB,YAAY,WAAW,IAAI,WAAW,IAAI,gBAAgB,KAC5D,MAAM,WAAW,IAAI,kCAEd,WAAW,cAAc,gBAAgB,8BAA8B,IAAI,MACpF,WAAW,IACX,kCAEY,aACX,WAAW,SAAS,OAAO,gBAEhC;;;;;KAMQ,aAAa,YAAY,YAAY,UAAU,mBAAmB,aAC5E,YACA,YACA,UACA"}
|
package/package.json
CHANGED
package/dist/utils.d.ts
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { MergeDeep, ReadonlyDeep, SimplifyDeep, WritableDeep } from "type-fest";
|
|
2
|
-
//#region src/utils.d.ts
|
|
3
|
-
type AnyFunction = (...args: never[]) => unknown;
|
|
4
|
-
/**
|
|
5
|
-
* How a wrapped user array is combined with the base array. - `"merge"` (the default for plain arrays): the user array
|
|
6
|
-
* is appended to the base array - `"replace"`: the user array is used as-is
|
|
7
|
-
*/
|
|
8
|
-
type ArrayMergeMode = "merge" | "replace";
|
|
9
|
-
/**
|
|
10
|
-
* A per-key array override. Wherever a config key takes an array, `{ value: [...], mode: "merge" | "replace" }` may be
|
|
11
|
-
* passed instead; `mode` controls how that key's array combines with the base config's. The wrapper never appears in
|
|
12
|
-
* the merged result. A config value that is exactly this shape is always consumed as a wrapper; an object with extra
|
|
13
|
-
* keys is treated as a plain config object.
|
|
14
|
-
*/
|
|
15
|
-
interface ArrayOverride<Value extends readonly unknown[], Mode extends ArrayMergeMode = ArrayMergeMode> {
|
|
16
|
-
readonly value: Value;
|
|
17
|
-
readonly mode: Mode;
|
|
18
|
-
}
|
|
19
|
-
/** True only when T is exactly the wrapper shape (keys value + mode, nothing else), mirroring isArrayOverride. */
|
|
20
|
-
type IsArrayOverride<T> = T extends {
|
|
21
|
-
readonly value: readonly unknown[];
|
|
22
|
-
readonly mode: ArrayMergeMode;
|
|
23
|
-
} ? [keyof T] extends [keyof ArrayOverride<readonly unknown[]>] ? true : false : false;
|
|
24
|
-
/**
|
|
25
|
-
* For every array-typed value in T, additionally allow `{ value: <that array type>, mode: ArrayMergeMode }`.
|
|
26
|
-
* Distributes over unions, does not recurse into array elements, and leaves functions/primitives alone.
|
|
27
|
-
*/
|
|
28
|
-
type WithArrayOverrides<T> = T extends readonly unknown[] ? T | ArrayOverride<T> : T extends AnyFunction ? T : T extends object ? { [K in keyof T]: WithArrayOverrides<T[K]>; } : T;
|
|
29
|
-
/**
|
|
30
|
-
* True if T contains (at any object depth, not inside arrays) a replace-mode wrapper. Depth is a recursion budget: when
|
|
31
|
-
* a call fails the generic constraint, this type runs over the full constraint (the whole tool config type), and the
|
|
32
|
-
* eager `{ ... }[keyof T]` walk would hit TS2615 on circular types in it (e.g. Console) and bury the real error.
|
|
33
|
-
*/
|
|
34
|
-
type HasReplaceWrapper<T, Depth extends readonly unknown[] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]> = Depth extends readonly [unknown, ...infer RemainingDepth] ? T extends readonly unknown[] ? false : T extends AnyFunction ? false : T extends object ? IsArrayOverride<T> extends true ? T extends {
|
|
35
|
-
readonly mode: "replace";
|
|
36
|
-
} ? true : false : true extends { [K in keyof T]: HasReplaceWrapper<T[K], RemainingDepth>; }[keyof T] ? true : false : false : false;
|
|
37
|
-
/** Keep only the key paths whose leaf is a replace-mode wrapper; unwrap those leaves to their `value` type. */
|
|
38
|
-
type DeepPickReplaceWrappers<T> = { [K in keyof T as HasReplaceWrapper<T[K]> extends true ? K : never]: IsArrayOverride<T[K]> extends true ? T[K] extends {
|
|
39
|
-
readonly value: infer V extends readonly unknown[];
|
|
40
|
-
} ? V : never : DeepPickReplaceWrappers<T[K]>; };
|
|
41
|
-
/** Drop key paths whose leaf is a replace-mode wrapper; unwrap merge-mode wrappers in place. */
|
|
42
|
-
type DeepDropReplaceWrappers<T> = { [K in keyof T as IsArrayOverride<T[K]> extends true ? T[K] extends {
|
|
43
|
-
readonly mode: "replace";
|
|
44
|
-
} ? never : K : K]: T[K] extends readonly unknown[] ? T[K] : T[K] extends AnyFunction ? T[K] : IsArrayOverride<T[K]> extends true ? T[K] extends {
|
|
45
|
-
readonly value: infer V extends readonly unknown[];
|
|
46
|
-
} ? V : never : T[K] extends object ? DeepDropReplaceWrappers<T[K]> : T[K]; };
|
|
47
|
-
/**
|
|
48
|
-
* The merged result type, exact for per-key overrides: pass 1 merges the user config minus replace-wrapper keys
|
|
49
|
-
* (unwrapping merge-mode wrappers, which behave like plain arrays) with array spreading; pass 2 merges the unwrapped
|
|
50
|
-
* replace-wrapper keys with array replacement. Instead of returning `BaseConfig & UserConfig` (from `merge`), this is a
|
|
51
|
-
* more friendly type via `MergeDeep`.
|
|
52
|
-
*/
|
|
53
|
-
type MergedConfig<BaseConfig, UserConfig> = MergeDeep<MergeDeep<SimplifyDeep<WritableDeep<BaseConfig>>, SimplifyDeep<WritableDeep<DeepDropReplaceWrappers<UserConfig>>>, {
|
|
54
|
-
arrayMergeMode: "spread";
|
|
55
|
-
}>, SimplifyDeep<WritableDeep<DeepPickReplaceWrappers<UserConfig>>>, {
|
|
56
|
-
arrayMergeMode: "replace";
|
|
57
|
-
}>;
|
|
58
|
-
/**
|
|
59
|
-
* The merge config function, where the `UserConfig` passed in is merged with `BaseConfig`.
|
|
60
|
-
*
|
|
61
|
-
* The `const` type parameter keeps literal types without requiring `as const` at the call site. The constraint is
|
|
62
|
-
* `WithArrayOverrides<ReadonlyDeep<...>>` so readonly arrays/objects (e.g. from `as const`) are accepted and any
|
|
63
|
-
* array-typed key can alternatively take an ArrayOverride wrapper; the result is `WritableDeep` so it stays assignable
|
|
64
|
-
* to the tools' own (mutable) config types.
|
|
65
|
-
*/
|
|
66
|
-
type MergeConfigFn<UserConfig, BaseConfig> = <const UserConfigToMerge extends WithArrayOverrides<ReadonlyDeep<UserConfig>>>(userConfig: UserConfigToMerge) => MergedConfig<BaseConfig, UserConfigToMerge>;
|
|
67
|
-
/**
|
|
68
|
-
* The optional merge config function, where the `userConfig` argument is optional. - if `UserConfig` is not provided,
|
|
69
|
-
* the return type is `BaseConfig` - if `UserConfig` is provided, the return type is the merged type of `BaseConfig` and
|
|
70
|
-
* `UserConfig`.
|
|
71
|
-
*
|
|
72
|
-
* CRITICAL: must stay a type-alias intersection (NOT `interface ... extends MergeConfigFn`): checking the
|
|
73
|
-
* interface-inheritance relation forces tsc to relate the no-arg signature's return type against the recursive
|
|
74
|
-
* `MergedConfig` type, which explodes to millions of type instantiations.
|
|
75
|
-
*/
|
|
76
|
-
type OptionalMergeConfigFn<UserConfig, BaseConfig> = MergeConfigFn<UserConfig, BaseConfig> & (() => SimplifyDeep<WritableDeep<BaseConfig>>);
|
|
77
|
-
//#endregion
|
|
78
|
-
export { MergeConfigFn, OptionalMergeConfigFn };
|
|
79
|
-
//# sourceMappingURL=utils.d.ts.map
|
package/dist/utils.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","names":[],"sources":["../src/utils.ts"],"mappings":";;KAIK,kBAAkB;;;;;KAMlB;;;;;;;UAQK,cAAc,kCAAkC,aAAa,iBAAiB;WAC7E,OAAO;WACP,MAAM;;;KAiCZ,gBAAgB,KAAK;WAAqB;WAAoC,MAAM;WAC9E,kBAAkB;;;;;KASxB,mBAAmB,KAAK,+BACzB,IAAI,cAAc,KAClB,UAAU,cACR,IACA,sBACK,WAAW,IAAI,mBAAmB,EAAE,SACvC;;;;;;KAOH,kBAAkB,GAAG,qEAAqE,0CAEpF,kBAEP,uCAEE,UAAU,sBAER,mBACE,gBAAgB,kBACd;WAAqB;mCAGL,WAAW,IAAI,kBAAkB,EAAE,IAAI,yBAAwB;;KAOtF,wBAAwB,QAC1B,WAAW,KAAK,kBAAkB,EAAE,mBAAmB,YAAY,gBAAgB,EAAE,mBAClF,EAAE;WAAsB,aAAa;IACnC,YAEF,wBAAwB,EAAE;;KAI3B,wBAAwB,QAC1B,WAAW,KAAK,gBAAgB,EAAE,mBAC/B,EAAE;WAAsB;YAEtB,IACF,IAAI,EAAE,gCACN,EAAE,KACF,EAAE,WAAW,cACX,EAAE,KACF,gBAAgB,EAAE,mBAChB,EAAE;WAAsB,aAAa;IACnC,YAEF,EAAE,oBACA,wBAAwB,EAAE,MAC1B,EAAE;;;;;;;KAST,aAAa,YAAY,cAAc,UAC1C,UACE,aAAa,aAAa,cAC1B,aAAa,aAAa,wBAAwB;EAChD;IAEJ,aAAa,aAAa,wBAAwB;EAChD;;;;;;;;;;KAWQ,cAAc,YAAY,qBAC9B,0BAA0B,mBAAmB,aAAa,cAEhE,YAAY,sBACT,aAAa,YAAY;;;;;;;;;;KAWlB,sBAAsB,YAAY,cAAc,cAAc,YAAY,qBAC7E,aAAa,aAAa"}
|
package/dist/utils.js
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { cloneDeep, isPlainObject, mergeWith } from "es-toolkit";
|
|
2
|
-
//#region src/utils.ts
|
|
3
|
-
/** Runtime wrapper check, mirrored by IsArrayOverride: plain object with exactly the keys value (array) + mode. */
|
|
4
|
-
const isArrayOverride = (v) => {
|
|
5
|
-
if (!isPlainObject(v)) return false;
|
|
6
|
-
if (Object.keys(v).length !== 2) return false;
|
|
7
|
-
const { value } = v;
|
|
8
|
-
const { mode } = v;
|
|
9
|
-
return Array.isArray(value) && (mode === "merge" || mode === "replace");
|
|
10
|
-
};
|
|
11
|
-
/**
|
|
12
|
-
* A wrapper around es-toolkit's `mergeWith` with a custom merge function that concatenates arrays, honoring per-key
|
|
13
|
-
* ArrayOverride wrappers. Wrapper interception must come first: the default path would deep-merge the wrapper as a
|
|
14
|
-
* plain object.
|
|
15
|
-
*/
|
|
16
|
-
const merge = (target, source) => mergeWith(target, source, (objValue, srcValue) => {
|
|
17
|
-
if (isArrayOverride(srcValue)) {
|
|
18
|
-
const { value: userArray, mode } = srcValue;
|
|
19
|
-
if (!Array.isArray(objValue)) return userArray;
|
|
20
|
-
return mode === "merge" ? [...objValue, ...userArray] : userArray;
|
|
21
|
-
}
|
|
22
|
-
if (!Array.isArray(objValue)) return;
|
|
23
|
-
if (!Array.isArray(srcValue)) return srcValue;
|
|
24
|
-
const baseArray = objValue;
|
|
25
|
-
const userArray = srcValue;
|
|
26
|
-
return [...baseArray, ...userArray];
|
|
27
|
-
});
|
|
28
|
-
/**
|
|
29
|
-
* Creates a config merge function with proper types for merging with a base config.
|
|
30
|
-
*
|
|
31
|
-
* `UserConfig` is not inferrable, so both type arguments must be passed explicitly. Returning the exact
|
|
32
|
-
* `OptionalMergeConfigFn` instantiation (instead of relating a looser type to an annotation at the export site) keeps
|
|
33
|
-
* tsc from eagerly evaluating the deep-mapped types over the full config type, which blows the instantiation depth
|
|
34
|
-
* limit on large configs like tsdown's.
|
|
35
|
-
*/
|
|
36
|
-
const createMergeConfigFn = (baseConfig) => {
|
|
37
|
-
const mergeConfigFn = (userConfig) => {
|
|
38
|
-
if (userConfig === void 0) return cloneDeep(baseConfig);
|
|
39
|
-
return merge(cloneDeep(baseConfig), cloneDeep(userConfig));
|
|
40
|
-
};
|
|
41
|
-
return mergeConfigFn;
|
|
42
|
-
};
|
|
43
|
-
//#endregion
|
|
44
|
-
export { createMergeConfigFn };
|
|
45
|
-
|
|
46
|
-
//# sourceMappingURL=utils.js.map
|
package/dist/utils.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","names":["baseArray"],"sources":["../src/utils.ts"],"sourcesContent":["import { cloneDeep, isPlainObject, mergeWith } from \"es-toolkit\"\nimport type { MergeDeep, ReadonlyDeep, WritableDeep, SimplifyDeep } from \"type-fest\"\n\ntype AnyObj = object\ntype AnyFunction = (...args: never[]) => unknown\n\n/**\n * How a wrapped user array is combined with the base array. - `\"merge\"` (the default for plain arrays): the user array\n * is appended to the base array - `\"replace\"`: the user array is used as-is\n */\ntype ArrayMergeMode = \"merge\" | \"replace\"\n\n/**\n * A per-key array override. Wherever a config key takes an array, `{ value: [...], mode: \"merge\" | \"replace\" }` may be\n * passed instead; `mode` controls how that key's array combines with the base config's. The wrapper never appears in\n * the merged result. A config value that is exactly this shape is always consumed as a wrapper; an object with extra\n * keys is treated as a plain config object.\n */\ninterface ArrayOverride<Value extends readonly unknown[], Mode extends ArrayMergeMode = ArrayMergeMode> {\n readonly value: Value\n readonly mode: Mode\n}\n\n/** Runtime wrapper check, mirrored by IsArrayOverride: plain object with exactly the keys value (array) + mode. */\nconst isArrayOverride = (v: unknown): v is { value: unknown[]; mode: ArrayMergeMode } => {\n if (!isPlainObject(v)) return false\n if (Object.keys(v).length !== 2) return false\n const { value } = v\n const { mode } = v\n return Array.isArray(value) && (mode === \"merge\" || mode === \"replace\")\n}\n\n/**\n * A wrapper around es-toolkit's `mergeWith` with a custom merge function that concatenates arrays, honoring per-key\n * ArrayOverride wrappers. Wrapper interception must come first: the default path would deep-merge the wrapper as a\n * plain object.\n */\nconst merge = <T extends AnyObj, S extends AnyObj>(target: T, source: S): T & S =>\n mergeWith(target, source, (objValue: unknown, srcValue: unknown) => {\n if (isArrayOverride(srcValue)) {\n const { value: userArray, mode } = srcValue\n if (!Array.isArray(objValue)) return userArray\n const baseArray: unknown[] = objValue\n return mode === \"merge\" ? [...baseArray, ...userArray] : userArray\n }\n if (!Array.isArray(objValue)) return\n if (!Array.isArray(srcValue)) return srcValue\n const baseArray: unknown[] = objValue\n const userArray: unknown[] = srcValue\n return [...baseArray, ...userArray]\n })\n\n/** True only when T is exactly the wrapper shape (keys value + mode, nothing else), mirroring isArrayOverride. */\ntype IsArrayOverride<T> = T extends { readonly value: readonly unknown[]; readonly mode: ArrayMergeMode }\n ? [keyof T] extends [keyof ArrayOverride<readonly unknown[]>]\n ? true\n : false\n : false\n\n/**\n * For every array-typed value in T, additionally allow `{ value: <that array type>, mode: ArrayMergeMode }`.\n * Distributes over unions, does not recurse into array elements, and leaves functions/primitives alone.\n */\ntype WithArrayOverrides<T> = T extends readonly unknown[]\n ? T | ArrayOverride<T>\n : T extends AnyFunction\n ? T\n : T extends object\n ? { [K in keyof T]: WithArrayOverrides<T[K]> }\n : T\n\n/**\n * True if T contains (at any object depth, not inside arrays) a replace-mode wrapper. Depth is a recursion budget: when\n * a call fails the generic constraint, this type runs over the full constraint (the whole tool config type), and the\n * eager `{ ... }[keyof T]` walk would hit TS2615 on circular types in it (e.g. Console) and bury the real error.\n */\ntype HasReplaceWrapper<T, Depth extends readonly unknown[] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]> = Depth extends readonly [\n unknown,\n ...infer RemainingDepth,\n]\n ? T extends readonly unknown[]\n ? false\n : T extends AnyFunction\n ? false\n : T extends object\n ? IsArrayOverride<T> extends true\n ? T extends { readonly mode: \"replace\" }\n ? true\n : false\n : true extends { [K in keyof T]: HasReplaceWrapper<T[K], RemainingDepth> }[keyof T]\n ? true\n : false\n : false\n : false\n\n/** Keep only the key paths whose leaf is a replace-mode wrapper; unwrap those leaves to their `value` type. */\ntype DeepPickReplaceWrappers<T> = {\n [K in keyof T as HasReplaceWrapper<T[K]> extends true ? K : never]: IsArrayOverride<T[K]> extends true\n ? T[K] extends { readonly value: infer V extends readonly unknown[] }\n ? V\n : never\n : DeepPickReplaceWrappers<T[K]>\n}\n\n/** Drop key paths whose leaf is a replace-mode wrapper; unwrap merge-mode wrappers in place. */\ntype DeepDropReplaceWrappers<T> = {\n [K in keyof T as IsArrayOverride<T[K]> extends true\n ? T[K] extends { readonly mode: \"replace\" }\n ? never\n : K\n : K]: T[K] extends readonly unknown[]\n ? T[K]\n : T[K] extends AnyFunction\n ? T[K]\n : IsArrayOverride<T[K]> extends true\n ? T[K] extends { readonly value: infer V extends readonly unknown[] }\n ? V\n : never\n : T[K] extends object\n ? DeepDropReplaceWrappers<T[K]>\n : T[K]\n}\n\n/**\n * The merged result type, exact for per-key overrides: pass 1 merges the user config minus replace-wrapper keys\n * (unwrapping merge-mode wrappers, which behave like plain arrays) with array spreading; pass 2 merges the unwrapped\n * replace-wrapper keys with array replacement. Instead of returning `BaseConfig & UserConfig` (from `merge`), this is a\n * more friendly type via `MergeDeep`.\n */\ntype MergedConfig<BaseConfig, UserConfig> = MergeDeep<\n MergeDeep<\n SimplifyDeep<WritableDeep<BaseConfig>>,\n SimplifyDeep<WritableDeep<DeepDropReplaceWrappers<UserConfig>>>,\n { arrayMergeMode: \"spread\" }\n >,\n SimplifyDeep<WritableDeep<DeepPickReplaceWrappers<UserConfig>>>,\n { arrayMergeMode: \"replace\" }\n>\n\n/**\n * The merge config function, where the `UserConfig` passed in is merged with `BaseConfig`.\n *\n * The `const` type parameter keeps literal types without requiring `as const` at the call site. The constraint is\n * `WithArrayOverrides<ReadonlyDeep<...>>` so readonly arrays/objects (e.g. from `as const`) are accepted and any\n * array-typed key can alternatively take an ArrayOverride wrapper; the result is `WritableDeep` so it stays assignable\n * to the tools' own (mutable) config types.\n */\nexport type MergeConfigFn<UserConfig, BaseConfig> = <\n const UserConfigToMerge extends WithArrayOverrides<ReadonlyDeep<UserConfig>>,\n>(\n userConfig: UserConfigToMerge,\n) => MergedConfig<BaseConfig, UserConfigToMerge>\n\n/**\n * The optional merge config function, where the `userConfig` argument is optional. - if `UserConfig` is not provided,\n * the return type is `BaseConfig` - if `UserConfig` is provided, the return type is the merged type of `BaseConfig` and\n * `UserConfig`.\n *\n * CRITICAL: must stay a type-alias intersection (NOT `interface ... extends MergeConfigFn`): checking the\n * interface-inheritance relation forces tsc to relate the no-arg signature's return type against the recursive\n * `MergedConfig` type, which explodes to millions of type instantiations.\n */\nexport type OptionalMergeConfigFn<UserConfig, BaseConfig> = MergeConfigFn<UserConfig, BaseConfig> &\n (() => SimplifyDeep<WritableDeep<BaseConfig>>)\n\n/**\n * Creates a config merge function with proper types for merging with a base config.\n *\n * `UserConfig` is not inferrable, so both type arguments must be passed explicitly. Returning the exact\n * `OptionalMergeConfigFn` instantiation (instead of relating a looser type to an annotation at the export site) keeps\n * tsc from eagerly evaluating the deep-mapped types over the full config type, which blows the instantiation depth\n * limit on large configs like tsdown's.\n */\nexport const createMergeConfigFn = <UserConfig extends AnyObj, BaseConfig extends ReadonlyDeep<UserConfig>>(\n baseConfig: BaseConfig,\n): OptionalMergeConfigFn<UserConfig, BaseConfig> => {\n // We don't care about the specific type of userConfig here because we assert mergeConfigFn as the correct type below\n const mergeConfigFn = (userConfig?: AnyObj) => {\n // Return a clone so consumers can't mutate the shared base config\n // oxlint-disable-next-line unicorn/prefer-structured-clone - structuredClone does not properly clone functions\n if (userConfig === undefined) return cloneDeep(baseConfig)\n // Clone both target and source so we never mutate the original objects\n // oxlint-disable-next-line unicorn/prefer-structured-clone - structuredClone does not properly clone functions\n return merge(cloneDeep(baseConfig), cloneDeep(userConfig))\n }\n\n // CRITICAL: two-step assertion required: a direct cast makes tsc run a comparability check relating the\n // implementation signature against the recursive MergedConfig type (millions of instantiations).\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion\n return mergeConfigFn as unknown as OptionalMergeConfigFn<UserConfig, BaseConfig>\n}\n"],"mappings":";;;AAwBA,MAAM,mBAAmB,MAAgE;CACvF,IAAI,CAAC,cAAc,CAAC,GAAG,OAAO;CAC9B,IAAI,OAAO,KAAK,CAAC,CAAC,CAAC,WAAW,GAAG,OAAO;CACxC,MAAM,EAAE,UAAU;CAClB,MAAM,EAAE,SAAS;CACjB,OAAO,MAAM,QAAQ,KAAK,MAAM,SAAS,WAAW,SAAS;AAC/D;;;;;;AAOA,MAAM,SAA6C,QAAW,WAC5D,UAAU,QAAQ,SAAS,UAAmB,aAAsB;CAClE,IAAI,gBAAgB,QAAQ,GAAG;EAC7B,MAAM,EAAE,OAAO,WAAW,SAAS;EACnC,IAAI,CAAC,MAAM,QAAQ,QAAQ,GAAG,OAAO;EAErC,OAAO,SAAS,UAAU,CAAC,GAAGA,UAAW,GAAG,SAAS,IAAI;CAC3D;CACA,IAAI,CAAC,MAAM,QAAQ,QAAQ,GAAG;CAC9B,IAAI,CAAC,MAAM,QAAQ,QAAQ,GAAG,OAAO;CACrC,MAAM,YAAuB;CAC7B,MAAM,YAAuB;CAC7B,OAAO,CAAC,GAAG,WAAW,GAAG,SAAS;AACpC,CAAC;;;;;;;;;AA2HH,MAAa,uBACX,eACkD;CAElD,MAAM,iBAAiB,eAAwB;EAG7C,IAAI,eAAe,KAAA,GAAW,OAAO,UAAU,UAAU;EAGzD,OAAO,MAAM,UAAU,UAAU,GAAG,UAAU,UAAU,CAAC;CAC3D;CAKA,OAAO;AACT"}
|