@visulima/package 5.0.13 → 5.0.15
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/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## @visulima/package [5.0.15](https://github.com/visulima/visulima/compare/%40visulima%2Fpackage%405.0.14...%40visulima%2Fpackage%405.0.15) (2026-08-18)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Dependencies
|
|
5
|
+
|
|
6
|
+
* **@visulima/fs:** upgraded to 6.0.2
|
|
7
|
+
|
|
8
|
+
## @visulima/package [5.0.14](https://github.com/visulima/visulima/compare/%40visulima%2Fpackage%405.0.13...%40visulima%2Fpackage%405.0.14) (2026-08-17)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Dependencies
|
|
12
|
+
|
|
13
|
+
* **@visulima/fs:** upgraded to 6.0.1
|
|
14
|
+
|
|
1
15
|
## @visulima/package [5.0.13](https://github.com/visulima/visulima/compare/%40visulima%2Fpackage%405.0.12...%40visulima%2Fpackage%405.0.13) (2026-08-17)
|
|
2
16
|
|
|
3
17
|
## @visulima/package [5.0.12](https://github.com/visulima/visulima/compare/%40visulima%2Fpackage%405.0.11...%40visulima%2Fpackage%405.0.12) (2026-08-08)
|
package/dist/index.d.ts
CHANGED
|
@@ -2,10 +2,11 @@ export { PackageNotFoundError } from "./error.js";
|
|
|
2
2
|
export { type LockFileEntry, type LockFileIntegrity, type LockFileIntegrityAlgorithm, type LockFileParseResult, type LockFileType, decodeSriIntegrity, parseBunLockFile, parseLockFile, parseLockFileContent, parseLockFileSync, parseNpmLockFile, parsePnpmLockFile, parseYarnLockFile } from "./lockfile.js";
|
|
3
3
|
export { type RootMonorepo, type Strategy, findMonorepoRoot, findMonorepoRootSync } from "./monorepo.js";
|
|
4
4
|
export { findPackageRoot, findPackageRootSync } from "./package.js";
|
|
5
|
-
export { type E as EnsurePackagesOptions, type F as FindPackageJsonCache, type N as NormalizedPackageJson, type a as NormalizedReadResult, type P as PackageJson, c as clearPackageJsonCache, e as ensurePackages, f as findPackageJson, b as findPackageJsonSync, g as getPackageJsonProperty, h as hasPackageJsonAnyDependency, d as hasPackageJsonProperty, p as parsePackageJson, i as parsePackageJsonSync, w as writePackageJson, j as writePackageJsonSync } from "./packem_shared/package-json.d-
|
|
5
|
+
export { type E as EnsurePackagesOptions, type F as FindPackageJsonCache, type N as NormalizedPackageJson, type a as NormalizedReadResult, type P as PackageJson, c as clearPackageJsonCache, e as ensurePackages, f as findPackageJson, b as findPackageJsonSync, g as getPackageJsonProperty, h as hasPackageJsonAnyDependency, d as hasPackageJsonProperty, p as parsePackageJson, i as parsePackageJsonSync, w as writePackageJson, j as writePackageJsonSync } from "./packem_shared/package-json.d-XOvCk6yp.js";
|
|
6
6
|
export { type PackageManager, type PackageManagerResult, findLockFile, findLockFileSync, findPackageManager, findPackageManagerSync, generateMissingPackagesInstallMessage, getPackageManagerVersion, identifyInitiatingPackageManager } from "./package-manager.js";
|
|
7
7
|
export { type PnpmCatalog, type PnpmCatalogs, isPackageInWorkspace, readPnpmCatalogs, readPnpmCatalogsSync, resolveCatalogReference, resolveCatalogReferences, resolveDependenciesCatalogReferences } from "./pnpm.js";
|
|
8
8
|
import '@visulima/fs';
|
|
9
9
|
import '@antfu/install-pkg';
|
|
10
10
|
import 'normalize-package-data';
|
|
11
11
|
import "./packem_shared/json-value.d-AIF0oxA6.js";
|
|
12
|
+
import 'tagged-tag';
|
package/dist/package-json.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import '@visulima/fs';
|
|
2
|
-
export { F as FindPackageJsonCache, a as NormalizedReadResult, c as clearPackageJsonCache, e as ensurePackages, f as findPackageJson, b as findPackageJsonSync, g as getPackageJsonProperty, h as hasPackageJsonAnyDependency, d as hasPackageJsonProperty, p as parsePackageJson, i as parsePackageJsonSync, w as writePackageJson, j as writePackageJsonSync } from "./packem_shared/package-json.d-
|
|
2
|
+
export { F as FindPackageJsonCache, a as NormalizedReadResult, c as clearPackageJsonCache, e as ensurePackages, f as findPackageJson, b as findPackageJsonSync, g as getPackageJsonProperty, h as hasPackageJsonAnyDependency, d as hasPackageJsonProperty, p as parsePackageJson, i as parsePackageJsonSync, w as writePackageJson, j as writePackageJsonSync } from "./packem_shared/package-json.d-XOvCk6yp.js";
|
|
3
3
|
import "./packem_shared/json-value.d-AIF0oxA6.js";
|
|
4
4
|
import '@antfu/install-pkg';
|
|
5
5
|
import 'normalize-package-data';
|
|
6
|
+
import 'tagged-tag';
|
package/dist/package-json.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{createRequire as M}from"node:module";import{installPackage as q}from"@antfu/install-pkg";import{findUp as
|
|
2
|
-
${d(["gray"],"→")} ${
|
|
1
|
+
import{createRequire as M}from"node:module";import{installPackage as q}from"@antfu/install-pkg";import{findUp as K,findUpSync as z,writeJson as U,writeJsonSync as V,readJson as X,readJsonSync as G,readFile as L,readFileSync as F}from"@visulima/fs";import{NotFoundError as S}from"@visulima/fs/error";import{toPath as P,parseJson as j}from"@visulima/fs/utils";import{readYaml as H,readYamlSync as Q}from"@visulima/fs/yaml";import{join as v}from"@visulima/path";import D from"json5";import Z from"normalize-package-data";import{readPnpmCatalogs as _,resolveCatalogReferences as p,readPnpmCatalogsSync as C}from"./pnpm.js";let B;const Y=r=>(B??=M(import.meta.url))(r),y=typeof globalThis<"u"&&typeof globalThis.process<"u"?globalThis.process:process,E=r=>{if(typeof y<"u"&&y.versions&&y.versions.node){const[e,s]=y.versions.node.split(".").map(Number);if(e>22||e===22&&s>=3||e===20&&s>=16)return y.getBuiltinModule(r)}return Y(r)},{existsSync:k}=E("node:fs"),{createInterface:ee}=E("node:readline"),{styleText:d}=E("node:util"),x=r=>{const e=typeof r;return r!==null&&(e==="object"||e==="function")},m=new Set(["__proto__","prototype","constructor"]),re=Math.max(...[...m].map(r=>r.length)),A=1e6,ne=r=>r>="0"&&r<="9";function J(r){if(r==="0")return!0;if(/^[1-9]\d*$/.test(r)){const e=Number.parseInt(r,10);return e<=Number.MAX_SAFE_INTEGER&&e<=A}return!1}function b(r,e){return m.has(r)?!1:(r&&J(r)?e.push(Number.parseInt(r,10)):e.push(r),!0)}function te(r){if(typeof r!="string")throw new TypeError(`Expected a string, got ${typeof r}`);const e=[];let s="",n="",o=!1,t="start",c=!1,i=0;for(const a of r){if(i++,c){s+=a,c=!1;continue}if(a==="\\"){if(t==="index")throw new Error(`Invalid character '${a}' in an index at position ${i}`);if(t==="indexEnd")throw new Error(`Invalid character '${a}' after an index at position ${i}`);c=!0,t=t==="start"?"property":t;continue}switch(a){case".":{if(t==="index")throw new Error(`Invalid character '${a}' in an index at position ${i}`);if(t==="indexEnd"){t="property";break}if(!b(s,e))return[];s="",t="property";break}case"[":{if(t==="index")throw new Error(`Invalid character '${a}' in an index at position ${i}`);if(t==="indexEnd"){t="index";break}if(t==="property"&&s.length<=re&&m.has(s))return[];o=t==="property",t="index";break}case"]":{if(t==="indexEnd")throw new Error(`Invalid character '${a}' after an index at position ${i}`);if(t!=="index"){t==="start"&&(t="property"),s+=a;break}if(n===""){s+="[]",t="property";break}if((s!==""||o)&&!b(s,e))return[];s="",o=!1;const f=Number.parseInt(n,10),l=f<=A&&n===String(f);e.push(l?f:n),n="",t="indexEnd";break}default:{if(t==="index"){if(!ne(a))throw new Error(`Invalid character '${a}' in an index at position ${i}`);n+=a;break}if(t==="indexEnd")throw new Error(`Invalid character '${a}' after an index at position ${i}`);t==="start"&&(t="property"),s+=a}}}switch(c&&(s+="\\"),t){case"property":{if(!b(s,e))return[];break}case"index":throw new Error("Index was not closed");case"start":{e.push("");break}}return e}function N(r){if(typeof r=="string")return te(r);if(Array.isArray(r)){const e=[];for(const[s,n]of r.entries()){if(typeof n!="string"&&typeof n!="number")throw new TypeError(`Expected a string or number for path segment at index ${s}, got ${typeof n}`);if(typeof n=="number"&&!Number.isFinite(n))throw new TypeError(`Path segment at index ${s} must be a finite number, got ${n}`);if(m.has(n))return[];typeof n=="string"&&J(n)?e.push(Number.parseInt(n,10)):e.push(n)}return e}return[]}function u(r,e,s){if(!x(r)||typeof e!="string"&&!Array.isArray(e))return s===void 0?r:s;const n=N(e);if(n.length===0)return s;for(let o=0;o<n.length;o++){const t=n[o];if(r=r[t],r==null){if(o!==n.length-1)return s;break}}return r===void 0?s:r}function se(r,e){if(!x(r)||typeof e!="string"&&!Array.isArray(e))return!1;const s=N(e);if(s.length===0)return!1;for(const n of s){if(!x(r)||!(n in r))return!1;r=r[n]}return!0}const ae=async r=>{const{default:e=!1,message:s,transformer:n}=r,o=c=>{const i=d(["cyan","bold"],"?"),a=d(["bold"],c),f=e?`${d(["greenBright"],"Y")}${d(["gray"],"/n")}`:`y/${d(["yellowBright"],"N")}`;return`${i} ${a} ${d(["gray"],`(${f})`)}`},t=c=>n?n(c):c?d(["greenBright"],"Yes"):d(["yellowBright"],"No");return new Promise(c=>{const i=ee({input:process.stdin,output:process.stdout}),a=o(s);i.question(a,f=>{i.close();const l=f.trim().toLowerCase();if(l===""){c(e);return}if(l==="y"||l==="yes"){console.log(`${d(["greenBright"],"✓")} ${t(!0)}`),c(!0);return}if(l==="n"||l==="no"){console.log(`${d(["yellowBright"],"✗")} ${t(!1)}`),c(!1);return}console.log(`${d(["gray"],"→")} ${t(e)}`),c(e)}),i.on("SIGINT",()=>{i.close(),console.log(`
|
|
2
|
+
${d(["gray"],"→")} ${t(!1)}`),c(!1)})})},oe=typeof process.stdout<"u"&&!process.versions.deno&&!globalThis.window,I=/, ([^,]*)$/,h=new Map,w=new Map,g=(r,e={})=>{const s=e.strict&&e.ignoreWarnings?e.ignoreWarnings.map(String).toSorted((n,o)=>n.localeCompare(o)).join(","):"";return`${r}|s${String(e.strict?1:0)}|c${String(e.resolveCatalogs?1:0)}|j${String(e.json5===!1?0:1)}|y${String(e.yaml===!1?0:1)}|w${s}`},T=r=>r.trimStart().startsWith("{"),W=r=>{const e=`${r}|`;for(const s of[w,h])for(const n of s.keys())n.startsWith(e)&&s.delete(n)};class ce extends Error{constructor(e){super(`The following warnings were encountered while normalizing package data:
|
|
3
3
|
- ${e.join(`
|
|
4
|
-
- `)}`),this.name="PackageJsonValidationError"}}const
|
|
4
|
+
- `)}`),this.name="PackageJsonValidationError"}}const $=(r,e,s=[])=>{const n=[];if(Z(r,o=>{n.push(o)},e),e&&n.length>0){const o=n.filter(t=>!s.some(c=>c instanceof RegExp?c.test(t):c===t));if(o.length>0)throw new ce(o)}return r},ie=async r=>await H(r),fe=r=>Q(r),le=async r=>{const e=await L(r);return D.parse(e)},de=r=>{const e=F(r);return D.parse(e)},O=async(r,e)=>e?.yaml!==!1&&(r.endsWith(".yaml")||r.endsWith(".yml"))?ie(r):e?.json5!==!1&&r.endsWith(".json5")?le(r):X(r),R=(r,e)=>e?.yaml!==!1&&(r.endsWith(".yaml")||r.endsWith(".yml"))?fe(r):e?.json5!==!1&&r.endsWith(".json5")?de(r):G(r),Ee=async(r,e={})=>{const s={type:"file"};r&&(s.cwd=r);const n=["package.json"];e.yaml!==!1&&n.push("package.yaml","package.yml"),e.json5!==!1&&n.push("package.json5");const o=await K(n,s);if(!o)throw new S(`No such file or directory, for ${n.join(", ").replace(I," or $1")} found.`);const t=e.cache&&typeof e.cache!="boolean"?e.cache:w,c=g(o,e);if(e.cache&&t.has(c))return t.get(c);const i=await O(o,e);if(e.resolveCatalogs){const f=await _(o);f&&p(i,f)}$(i,e.strict??!1,e.ignoreWarnings);const a={packageJson:i,path:o};return e.cache&&t.set(c,a),a},Pe=(r,e={})=>{const s={type:"file"};r&&(s.cwd=r);const n=["package.json"];e.yaml!==!1&&n.push("package.yaml","package.yml"),e.json5!==!1&&n.push("package.json5");const o=z(n,s);if(!o)throw new S(`No such file or directory, for ${n.join(", ").replace(I," or $1")} found.`);const t=e.cache&&typeof e.cache!="boolean"?e.cache:w,c=g(o,e);if(e.cache&&t.has(c))return t.get(c);const i=R(o,e);if(e.resolveCatalogs){const f=C(o);f&&p(i,f)}$(i,e.strict??!1,e.ignoreWarnings);const a={packageJson:i,path:o};return e.cache&&t.set(c,a),a},ke=async(r,e={})=>{const{cwd:s,...n}=e,o=P(s??process.cwd()),t=v(o,"package.json");await U(t,r,n),W(t)},Se=(r,e={})=>{const{cwd:s,...n}=e,o=P(s??process.cwd()),t=v(o,"package.json");V(t,r,n),W(t)},je=()=>{w.clear(),h.clear()},ve=(r,e)=>{const s=typeof r=="object"&&!Array.isArray(r);if(!s&&!(typeof r=="string"))throw new TypeError("`packageFile` should be either an `object` or a `string`.");let o,t=!1,c;if(s)o=structuredClone(r);else if(!T(r)&&k(r)){c=r;const a=e?.cache&&typeof e.cache!="boolean"?e.cache:h,f=g(c,e);if(e?.cache&&a.has(f))return a.get(f);o=R(c,e),t=!0}else o=j(r);if(e?.resolveCatalogs)if(t){const a=C(r);a&&p(o,a)}else throw new Error("The 'resolveCatalogs' option can only be used on a file path.");$(o,e?.strict??!1,e?.ignoreWarnings);const i=o;return t&&e?.cache&&(typeof e.cache=="boolean"?h:e.cache).set(g(c,e),i),i},De=async(r,e)=>{const s=typeof r=="object"&&!Array.isArray(r);if(!s&&!(typeof r=="string"))throw new TypeError("`packageFile` should be either an `object` or a `string`.");let o,t=!1,c;if(s)o=structuredClone(r);else if(!T(r)&&k(r)){c=r;const a=e?.cache&&typeof e.cache!="boolean"?e.cache:h,f=g(c,e);if(e?.cache&&a.has(f))return a.get(f);o=await O(c,e),t=!0}else o=j(r);if(e?.resolveCatalogs)if(t){const a=await _(r);a&&p(o,a)}else throw new Error("The 'resolveCatalogs' option can only be used on a file path.");$(o,e?.strict??!1,e?.ignoreWarnings);const i=o;return t&&e?.cache&&(typeof e.cache=="boolean"?h:e.cache).set(g(c,e),i),i},_e=(r,e,s)=>u(r,e,s),Ce=(r,e)=>se(r,e),Ae=(r,e,s)=>{const n=u(r,"dependencies",{}),o=u(r,"devDependencies",{}),t=u(r,"peerDependencies",{}),c={...n,...o,...s?.peerDeps===!1?{}:t};for(const i of e)if(Object.hasOwn(c,i))return!0;return!1},Je=async(r,e,s="dependencies",n={})=>{const o=u(r,"dependencies",{}),t=u(r,"devDependencies",{}),c=u(r,"peerDependencies",{}),i=[],a={deps:!0,devDeps:!0,peerDeps:!1,...n,...n.confirm?{confirm:{...n.confirm}}:{}};for(const l of e)a.deps&&Object.hasOwn(o??{},l)||a.devDeps&&Object.hasOwn(t??{},l)||a.peerDeps&&Object.hasOwn(c??{},l)||i.push(l);if(i.length===0)return;if(process.env.CI||oe&&!process.stdout.isTTY){const l=`Skipping package installation for [${e.join(", ")}] because the process is not interactive.`;if(n.throwOnWarn)throw new Error(l);n.logger?.warn?n.logger.warn(l):console.warn(l);return}if(typeof a.confirm?.message=="function"&&(a.confirm.message=a.confirm.message(i)),a.confirm?.message===void 0){const l=`${i.length===1?"Package is":"Packages are"} required for this config: ${i.join(", ")}. Do you want to install them?`;a.confirm===void 0?a.confirm={message:l}:a.confirm.message=l}await ae(a.confirm)&&await q(i,{...a.installPackage,cwd:a.cwd?P(a.cwd):void 0,dev:s==="devDependencies"})};export{je as clearPackageJsonCache,Je as ensurePackages,Ee as findPackageJson,Pe as findPackageJsonSync,_e as getPackageJsonProperty,Ae as hasPackageJsonAnyDependency,Ce as hasPackageJsonProperty,De as parsePackageJson,ve as parsePackageJsonSync,ke as writePackageJson,Se as writePackageJsonSync};
|
|
@@ -2,6 +2,7 @@ import { WriteJsonOptions } from '@visulima/fs';
|
|
|
2
2
|
import { InstallPackageOptions } from '@antfu/install-pkg';
|
|
3
3
|
import { Package } from 'normalize-package-data';
|
|
4
4
|
import { a as JsonValue, J as JsonObject } from "./json-value.d-AIF0oxA6.js";
|
|
5
|
+
import tag from 'tagged-tag';
|
|
5
6
|
/**
|
|
6
7
|
Matches any [primitive value](https://developer.mozilla.org/en-US/docs/Glossary/Primitive).
|
|
7
8
|
|
|
@@ -363,16 +364,16 @@ An if-else-like type that resolves depending on whether the given type is `any`
|
|
|
363
364
|
|
|
364
365
|
@example
|
|
365
366
|
```
|
|
366
|
-
// When `T` is
|
|
367
|
-
type A = IfNotAnyOrNever<string, 'VALID'
|
|
367
|
+
// When `T` is neither `any` nor `never` (like `string`) => Returns `IfNot` branch
|
|
368
|
+
type A = IfNotAnyOrNever<string, {ifNot: 'VALID'; ifAny: 'IS_ANY'; ifNever: 'IS_NEVER'}>;
|
|
368
369
|
//=> 'VALID'
|
|
369
370
|
|
|
370
371
|
// When `T` is `any` => Returns `IfAny` branch
|
|
371
|
-
type B = IfNotAnyOrNever<any, 'VALID'
|
|
372
|
+
type B = IfNotAnyOrNever<any, {ifNot: 'VALID'; ifAny: 'IS_ANY'; ifNever: 'IS_NEVER'}>;
|
|
372
373
|
//=> 'IS_ANY'
|
|
373
374
|
|
|
374
375
|
// When `T` is `never` => Returns `IfNever` branch
|
|
375
|
-
type C = IfNotAnyOrNever<never, 'VALID'
|
|
376
|
+
type C = IfNotAnyOrNever<never, {ifNot: 'VALID'; ifAny: 'IS_ANY'; ifNever: 'IS_NEVER'}>;
|
|
376
377
|
//=> 'IS_NEVER'
|
|
377
378
|
```
|
|
378
379
|
|
|
@@ -385,7 +386,7 @@ import type {StringRepeat} from 'type-fest';
|
|
|
385
386
|
type NineHundredNinetyNineSpaces = StringRepeat<' ', 999>;
|
|
386
387
|
|
|
387
388
|
// The following implementation is not tail recursive
|
|
388
|
-
type TrimLeft<S extends string> = IfNotAnyOrNever<S, S extends ` ${infer R}` ? TrimLeft<R> : S>;
|
|
389
|
+
type TrimLeft<S extends string> = IfNotAnyOrNever<S, {ifNot: S extends ` ${infer R}` ? TrimLeft<R> : S}>;
|
|
389
390
|
|
|
390
391
|
// Hence, instantiations with long strings will fail
|
|
391
392
|
// @ts-expect-error
|
|
@@ -394,7 +395,7 @@ type T1 = TrimLeft<NineHundredNinetyNineSpaces>;
|
|
|
394
395
|
// Error: Type instantiation is excessively deep and possibly infinite.
|
|
395
396
|
|
|
396
397
|
// To fix this, move the recursion into a helper type
|
|
397
|
-
type TrimLeftOptimised<S extends string> = IfNotAnyOrNever<S, _TrimLeftOptimised<S
|
|
398
|
+
type TrimLeftOptimised<S extends string> = IfNotAnyOrNever<S, {ifNot: _TrimLeftOptimised<S>}>;
|
|
398
399
|
|
|
399
400
|
type _TrimLeftOptimised<S extends string> = S extends ` ${infer R}` ? _TrimLeftOptimised<R> : S;
|
|
400
401
|
|
|
@@ -402,7 +403,11 @@ type T2 = TrimLeftOptimised<NineHundredNinetyNineSpaces>;
|
|
|
402
403
|
//=> ''
|
|
403
404
|
```
|
|
404
405
|
*/
|
|
405
|
-
type IfNotAnyOrNever<T,
|
|
406
|
+
type IfNotAnyOrNever<T, Cases extends {
|
|
407
|
+
ifNot: unknown;
|
|
408
|
+
ifAny?: unknown;
|
|
409
|
+
ifNever?: unknown;
|
|
410
|
+
}> = IsAny<T> extends true ? 'ifAny' extends keyof Cases ? Cases['ifAny'] : any : IsNever<T> extends true ? 'ifNever' extends keyof Cases ? Cases['ifNever'] : never : Cases['ifNot'];
|
|
406
411
|
/**
|
|
407
412
|
Indicates the value of `exactOptionalPropertyTypes` compiler option.
|
|
408
413
|
*/
|
|
@@ -438,7 +443,9 @@ type B = CollapseRestElement<[string?, string?, ...number[]]>;
|
|
|
438
443
|
//=> [string | undefined, string | undefined, number]
|
|
439
444
|
```
|
|
440
445
|
*/
|
|
441
|
-
type CollapseRestElement<TArray extends UnknownArray> = IfNotAnyOrNever<TArray,
|
|
446
|
+
type CollapseRestElement<TArray extends UnknownArray> = IfNotAnyOrNever<TArray, {
|
|
447
|
+
ifNot: _CollapseRestElement<TArray>;
|
|
448
|
+
}>;
|
|
442
449
|
type _CollapseRestElement<TArray extends UnknownArray, ForwardAccumulator extends UnknownArray = [], BackwardAccumulator extends UnknownArray = []> = TArray extends UnknownArray // For distributing `TArray`
|
|
443
450
|
? keyof TArray & `${number}` extends never ?
|
|
444
451
|
// Enters this branch, if `TArray` is empty (e.g., []),
|
|
@@ -501,242 +508,250 @@ type ShouldBeTrue = IsNegative<-1>;
|
|
|
501
508
|
@category Numeric
|
|
502
509
|
*/
|
|
503
510
|
type IsNegative<T extends _Numeric> = T extends Negative<T> ? true : false;
|
|
511
|
+
// eslint-disable-next-line type-fest/require-exported-types
|
|
512
|
+
type TagContainer<Token> = {
|
|
513
|
+
readonly [tag]: Token;
|
|
514
|
+
};
|
|
515
|
+
type Tag<Token extends PropertyKey, TagMetadata> = TagContainer<{ [K in Token]: TagMetadata; }>;
|
|
504
516
|
/**
|
|
505
|
-
Create a
|
|
517
|
+
Create a [tagged type](https://medium.com/@KevinBGreene/surviving-the-typescript-ecosystem-branding-and-type-tagging-6cf6e516523d) that can support [multiple tags](https://github.com/sindresorhus/type-fest/issues/665) and [per-tag metadata](https://medium.com/@ethanresnick/advanced-typescript-tagged-types-improved-with-type-level-metadata-5072fc125fcf).
|
|
506
518
|
|
|
507
|
-
|
|
508
|
-
```
|
|
509
|
-
import type {TupleOf} from 'type-fest';
|
|
519
|
+
A type returned by `Tagged` can be passed to `Tagged` again, to create a type with multiple tags.
|
|
510
520
|
|
|
511
|
-
|
|
512
|
-
//=> [number, number, number]
|
|
521
|
+
A tag's name is usually a string (and must be a string, number, or symbol), but each application of a tag can also contain an arbitrary type as its "metadata". See {@link GetTagMetadata} for examples and explanation.
|
|
513
522
|
|
|
514
|
-
type
|
|
515
|
-
|
|
523
|
+
A type `A` returned by `Tagged` is assignable to another type `B` returned by `Tagged` if and only if:
|
|
524
|
+
- the underlying (untagged) type of `A` is assignable to the underlying type of `B`;
|
|
525
|
+
- `A` contains at least all the tags `B` has;
|
|
526
|
+
- and the metadata type for each of `A`'s tags is assignable to the metadata type of `B`'s corresponding tag.
|
|
516
527
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
528
|
+
There have been several discussions about adding similar features to TypeScript. Unfortunately, nothing has (yet) moved forward:
|
|
529
|
+
- [Microsoft/TypeScript#202](https://github.com/microsoft/TypeScript/issues/202)
|
|
530
|
+
- [Microsoft/TypeScript#4895](https://github.com/microsoft/TypeScript/issues/4895)
|
|
531
|
+
- [Microsoft/TypeScript#33290](https://github.com/microsoft/TypeScript/pull/33290)
|
|
520
532
|
|
|
521
533
|
@example
|
|
522
534
|
```
|
|
523
|
-
import type {
|
|
535
|
+
import type {Tagged} from 'type-fest';
|
|
524
536
|
|
|
525
|
-
type
|
|
537
|
+
type AccountNumber = Tagged<number, 'AccountNumber'>;
|
|
538
|
+
type AccountBalance = Tagged<number, 'AccountBalance'>;
|
|
526
539
|
|
|
527
|
-
|
|
528
|
-
|
|
540
|
+
function createAccountNumber(): AccountNumber {
|
|
541
|
+
// As you can see, casting from a `number` (the underlying type being tagged) is allowed.
|
|
542
|
+
return 2 as AccountNumber;
|
|
543
|
+
}
|
|
529
544
|
|
|
530
|
-
|
|
531
|
-
//=> '3' | '4' | '5' | '6' | '7' | '8'
|
|
532
|
-
```
|
|
545
|
+
declare function getMoneyForAccount(accountNumber: AccountNumber): AccountBalance;
|
|
533
546
|
|
|
534
|
-
|
|
547
|
+
// This will compile successfully.
|
|
548
|
+
getMoneyForAccount(createAccountNumber());
|
|
535
549
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
550
|
+
// But this won't, because it has to be explicitly passed as an `AccountNumber` type!
|
|
551
|
+
// Critically, you could not accidentally use an `AccountBalance` as an `AccountNumber`.
|
|
552
|
+
// @ts-expect-error
|
|
553
|
+
getMoneyForAccount(2);
|
|
539
554
|
|
|
540
|
-
|
|
541
|
-
|
|
555
|
+
// You can also use tagged values like their underlying, untagged type.
|
|
556
|
+
// I.e., this will compile successfully because an `AccountNumber` can be used as a regular `number`.
|
|
557
|
+
// In this sense, the underlying base type is not hidden, which differentiates tagged types from opaque types in other languages.
|
|
558
|
+
const accountNumber = createAccountNumber() + 2;
|
|
542
559
|
```
|
|
543
560
|
|
|
544
|
-
Note: If the type for elements is not specified, it will default to `unknown`.
|
|
545
|
-
|
|
546
561
|
@example
|
|
547
562
|
```
|
|
548
|
-
import type {
|
|
563
|
+
import type {Tagged} from 'type-fest';
|
|
549
564
|
|
|
550
|
-
type
|
|
551
|
-
|
|
565
|
+
// You can apply multiple tags to a type by using `Tagged` repeatedly.
|
|
566
|
+
type Url = Tagged<string, 'URL'>;
|
|
567
|
+
type SpecialCacheKey = Tagged<Url, 'SpecialCacheKey'>;
|
|
568
|
+
|
|
569
|
+
// You can also pass a union of tag names, so this is equivalent to the above, although it doesn't give you the ability to assign distinct metadata to each tag.
|
|
570
|
+
type SpecialCacheKey2 = Tagged<string, 'URL' | 'SpecialCacheKey'>;
|
|
552
571
|
```
|
|
553
572
|
|
|
554
|
-
|
|
573
|
+
@category Type
|
|
574
|
+
*/
|
|
575
|
+
type Tagged<Type, TagName extends PropertyKey, TagMetadata = never> = Type & Tag<TagName, TagMetadata>;
|
|
576
|
+
/**
|
|
577
|
+
Get the untagged portion of a tagged type created with `Tagged`.
|
|
578
|
+
|
|
579
|
+
Why is this necessary?
|
|
580
|
+
|
|
581
|
+
1. Use a `Tagged` type as object keys
|
|
582
|
+
2. Prevent TS4058 error: "Return type of exported function has or is using name X from external module Y but cannot be named"
|
|
555
583
|
|
|
556
584
|
@example
|
|
557
585
|
```
|
|
558
|
-
import type {
|
|
586
|
+
import type {Tagged, UnwrapTagged} from 'type-fest';
|
|
559
587
|
|
|
560
|
-
type
|
|
561
|
-
//=> []
|
|
562
|
-
```
|
|
588
|
+
type AccountType = Tagged<'SAVINGS' | 'CHECKING', 'AccountType'>;
|
|
563
589
|
|
|
564
|
-
|
|
590
|
+
const moneyByAccountType: Record<UnwrapTagged<AccountType>, number> = {
|
|
591
|
+
SAVINGS: 99,
|
|
592
|
+
CHECKING: 0.1,
|
|
593
|
+
};
|
|
565
594
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
import type {TupleOf} from 'type-fest';
|
|
595
|
+
// Without UnwrapTagged, the following expression would throw a type error.
|
|
596
|
+
const money = moneyByAccountType.SAVINGS; // TS error: Property 'SAVINGS' does not exist
|
|
569
597
|
|
|
570
|
-
type
|
|
571
|
-
|
|
598
|
+
// Attempting to pass a non-Tagged type to UnwrapTagged will raise a type error.
|
|
599
|
+
// @ts-expect-error
|
|
600
|
+
type WontWork = UnwrapTagged<string>;
|
|
572
601
|
```
|
|
573
602
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
@category Array
|
|
603
|
+
@category Type
|
|
577
604
|
*/
|
|
578
|
-
type
|
|
579
|
-
type
|
|
580
|
-
type BuildTupleDigitByDigit<Length extends string, Fill, Accumulator extends UnknownArray = []> = Length extends `${infer First extends DigitCharacter}${infer Rest}` ? BuildTupleDigitByDigit<Rest, Fill, [...RepeatTupleTenTimes<Accumulator>, ...DigitTupleOf<First, Fill>]> : Accumulator;
|
|
581
|
-
type RepeatTupleTenTimes<Tuple extends UnknownArray> = [...Tuple, ...Tuple, ...Tuple, ...Tuple, ...Tuple, ...Tuple, ...Tuple, ...Tuple, ...Tuple, ...Tuple];
|
|
582
|
-
type DigitTupleOf<Digit extends DigitCharacter, Fill> = [[], [Fill], [Fill, Fill], [Fill, Fill, Fill], [Fill, Fill, Fill, Fill], [Fill, Fill, Fill, Fill, Fill], [Fill, Fill, Fill, Fill, Fill, Fill], [Fill, Fill, Fill, Fill, Fill, Fill, Fill], [Fill, Fill, Fill, Fill, Fill, Fill, Fill, Fill], [Fill, Fill, Fill, Fill, Fill, Fill, Fill, Fill, Fill]][Digit];
|
|
605
|
+
type UnwrapTagged<TaggedType extends Tag<PropertyKey, any>> = RemoveAllTags<TaggedType>;
|
|
606
|
+
type RemoveAllTags<T> = T extends Tag<PropertyKey, any> ? { [ThisTag in keyof T[typeof tag]]: T extends Tagged<infer Type, ThisTag, T[typeof tag][ThisTag]> ? RemoveAllTags<Type> : never; }[keyof T[typeof tag]] : T;
|
|
583
607
|
/**
|
|
584
|
-
|
|
608
|
+
Returns a boolean for whether the given type is a `string` [literal type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types).
|
|
585
609
|
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
610
|
+
Useful for:
|
|
611
|
+
- providing strongly-typed string manipulation functions
|
|
612
|
+
- constraining strings to be a string literal
|
|
613
|
+
- type utilities, such as when constructing parsers and ASTs
|
|
614
|
+
|
|
615
|
+
The implementation of this type is inspired by the trick mentioned in this [StackOverflow answer](https://stackoverflow.com/a/68261113/420747).
|
|
591
616
|
|
|
592
617
|
@example
|
|
593
618
|
```
|
|
594
|
-
type
|
|
595
|
-
//=> 1234
|
|
619
|
+
import type {IsStringLiteral} from 'type-fest';
|
|
596
620
|
|
|
597
|
-
type
|
|
598
|
-
//=> -1234
|
|
621
|
+
type CapitalizedString<T extends string> = IsStringLiteral<T> extends true ? Capitalize<T> : string;
|
|
599
622
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
//=> -1234.56
|
|
605
|
-
|
|
606
|
-
type PositiveInfinity = StringToNumber<'Infinity'>;
|
|
607
|
-
//=> Infinity
|
|
623
|
+
// https://github.com/yankeeinlondon/native-dash/blob/master/src/capitalize.ts
|
|
624
|
+
function capitalize<T extends Readonly<string>>(input: T): CapitalizedString<T> {
|
|
625
|
+
return (input.slice(0, 1).toUpperCase() + input.slice(1)) as CapitalizedString<T>;
|
|
626
|
+
}
|
|
608
627
|
|
|
609
|
-
|
|
610
|
-
//=>
|
|
628
|
+
const output = capitalize('hello, world!');
|
|
629
|
+
//=> 'Hello, world!'
|
|
611
630
|
```
|
|
612
631
|
|
|
613
|
-
@category String
|
|
614
|
-
@category Numeric
|
|
615
|
-
@category Template literal
|
|
616
|
-
*/
|
|
617
|
-
type StringToNumber<S extends string> = S extends `${infer N extends number}` ? N : S extends 'Infinity' ? PositiveInfinity : S extends '-Infinity' ? NegativeInfinity : never;
|
|
618
|
-
/**
|
|
619
|
-
Returns an array of the characters of the string.
|
|
620
|
-
|
|
621
632
|
@example
|
|
622
633
|
```
|
|
623
|
-
|
|
624
|
-
//=> ['a', 'b', 'c', 'd', 'e']
|
|
634
|
+
// String types with infinite set of possible values return `false`.
|
|
625
635
|
|
|
626
|
-
type
|
|
627
|
-
//=> never
|
|
628
|
-
```
|
|
636
|
+
import type {IsStringLiteral} from 'type-fest';
|
|
629
637
|
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
type StringToArray<S extends string, Result extends string[] = []> = string extends S ? never : S extends `${infer F}${infer R}` ? StringToArray<R, [...Result, F]> : Result;
|
|
633
|
-
/**
|
|
634
|
-
Returns the length of the given string.
|
|
638
|
+
type AllUppercaseStrings = IsStringLiteral<Uppercase<string>>;
|
|
639
|
+
//=> false
|
|
635
640
|
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
type A = StringLength<'abcde'>;
|
|
639
|
-
//=> 5
|
|
641
|
+
type StringsStartingWithOn = IsStringLiteral<`on${string}`>;
|
|
642
|
+
//=> false
|
|
640
643
|
|
|
641
|
-
|
|
642
|
-
|
|
644
|
+
// This behaviour is particularly useful in string manipulation utilities, as infinite string types often require separate handling.
|
|
645
|
+
|
|
646
|
+
type Length<S extends string, Counter extends never[] = []> =
|
|
647
|
+
IsStringLiteral<S> extends false
|
|
648
|
+
? number // return `number` for infinite string types
|
|
649
|
+
: S extends `${string}${infer Tail}`
|
|
650
|
+
? Length<Tail, [...Counter, never]>
|
|
651
|
+
: Counter['length'];
|
|
652
|
+
|
|
653
|
+
type L1 = Length<Lowercase<string>>;
|
|
654
|
+
//=> number
|
|
655
|
+
|
|
656
|
+
type L2 = Length<`${number}`>;
|
|
657
|
+
//=> number
|
|
643
658
|
```
|
|
644
659
|
|
|
645
|
-
@category
|
|
646
|
-
@category
|
|
660
|
+
@category Type Guard
|
|
661
|
+
@category Utilities
|
|
647
662
|
*/
|
|
648
|
-
type
|
|
663
|
+
type IsStringLiteral<S> = IfNotAnyOrNever<S, {
|
|
664
|
+
ifNot: _IsStringLiteral<CollapseLiterals<S extends TagContainer<any> ? UnwrapTagged<S> : S>>;
|
|
665
|
+
ifAny: false;
|
|
666
|
+
ifNever: false;
|
|
667
|
+
}>;
|
|
668
|
+
type _IsStringLiteral<S> =
|
|
669
|
+
// If `T` is an infinite string type (e.g., `on${string}`), `Record<T, never>` produces an index signature,
|
|
670
|
+
// and since `{}` extends index signatures, the result becomes `false`.
|
|
671
|
+
S extends string ? {} extends Record<S, never> ? false : true : false;
|
|
649
672
|
/**
|
|
650
|
-
|
|
673
|
+
Create a tuple type of the specified length with elements of the specified type.
|
|
651
674
|
|
|
652
675
|
@example
|
|
653
676
|
```
|
|
654
|
-
type
|
|
655
|
-
//=> true
|
|
677
|
+
import type {TupleOf} from 'type-fest';
|
|
656
678
|
|
|
657
|
-
type
|
|
658
|
-
//=>
|
|
679
|
+
type RGB = TupleOf<3, number>;
|
|
680
|
+
//=> [number, number, number]
|
|
681
|
+
|
|
682
|
+
type Line = TupleOf<2, {x: number; y: number}>;
|
|
683
|
+
//=> [{x: number; y: number}, {x: number; y: number}]
|
|
684
|
+
|
|
685
|
+
type TicTacToeBoard = TupleOf<3, TupleOf<3, 'X' | 'O' | null>>;
|
|
686
|
+
//=> [['X' | 'O' | null, 'X' | 'O' | null, 'X' | 'O' | null], ['X' | 'O' | null, 'X' | 'O' | null, 'X' | 'O' | null], ['X' | 'O' | null, 'X' | 'O' | null, 'X' | 'O' | null]]
|
|
659
687
|
```
|
|
660
|
-
*/
|
|
661
|
-
type SameLengthPositiveNumericStringGt<A extends string, B extends string> = A extends `${infer FirstA}${infer RestA}` ? B extends `${infer FirstB}${infer RestB}` ? FirstA extends FirstB ? SameLengthPositiveNumericStringGt<RestA, RestB> : PositiveNumericCharacterGt<FirstA, FirstB> : never : false;
|
|
662
|
-
type NumericString = '0123456789';
|
|
663
|
-
/**
|
|
664
|
-
Returns a boolean for whether `A` is greater than `B`, where `A` and `B` are both positive numeric strings.
|
|
665
688
|
|
|
666
689
|
@example
|
|
667
690
|
```
|
|
668
|
-
type
|
|
669
|
-
//=> true
|
|
691
|
+
import type {TupleOf} from 'type-fest';
|
|
670
692
|
|
|
671
|
-
type
|
|
672
|
-
//=> false
|
|
693
|
+
type Range<Start extends number, End extends number> = Exclude<keyof TupleOf<End>, keyof TupleOf<Start>>;
|
|
673
694
|
|
|
674
|
-
type
|
|
675
|
-
//=>
|
|
695
|
+
type ZeroToFour = Range<0, 5>;
|
|
696
|
+
//=> '0' | '1' | '2' | '3' | '4'
|
|
697
|
+
|
|
698
|
+
type ThreeToEight = Range<3, 9>;
|
|
699
|
+
//=> '3' | '4' | '5' | '6' | '7' | '8'
|
|
676
700
|
```
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
/**
|
|
680
|
-
Returns a boolean for whether `A` represents a number greater than `B`, where `A` and `B` are both positive numeric characters.
|
|
701
|
+
|
|
702
|
+
Note: If the specified length is the non-literal `number` type, the result will not be a tuple but a regular array.
|
|
681
703
|
|
|
682
704
|
@example
|
|
683
705
|
```
|
|
684
|
-
type
|
|
685
|
-
//=> true
|
|
706
|
+
import type {TupleOf} from 'type-fest';
|
|
686
707
|
|
|
687
|
-
type
|
|
688
|
-
//=>
|
|
708
|
+
type StringArray = TupleOf<number, string>;
|
|
709
|
+
//=> string[]
|
|
689
710
|
```
|
|
690
|
-
*/
|
|
691
|
-
type PositiveNumericCharacterGt<A extends string, B extends string> = NumericString extends `${infer HeadA}${A}${infer TailA}` ? NumericString extends `${infer HeadB}${B}${infer TailB}` ? HeadA extends `${HeadB}${infer _}${infer __}` ? true : false : never : never;
|
|
692
|
-
/**
|
|
693
|
-
Check whether the given type is a number or a number string.
|
|
694
711
|
|
|
695
|
-
|
|
712
|
+
Note: If the type for elements is not specified, it will default to `unknown`.
|
|
696
713
|
|
|
697
714
|
@example
|
|
698
715
|
```
|
|
699
|
-
type
|
|
700
|
-
//=> true
|
|
716
|
+
import type {TupleOf} from 'type-fest';
|
|
701
717
|
|
|
702
|
-
type
|
|
703
|
-
//=>
|
|
718
|
+
type UnknownTriplet = TupleOf<3>;
|
|
719
|
+
//=> [unknown, unknown, unknown]
|
|
720
|
+
```
|
|
704
721
|
|
|
705
|
-
|
|
706
|
-
//=> true
|
|
722
|
+
Note: If the specified length is negative, the result will be an empty tuple.
|
|
707
723
|
|
|
708
|
-
|
|
709
|
-
|
|
724
|
+
@example
|
|
725
|
+
```
|
|
726
|
+
import type {TupleOf} from 'type-fest';
|
|
710
727
|
|
|
711
|
-
type
|
|
712
|
-
//=>
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
Returns the number with reversed sign.
|
|
728
|
+
type EmptyTuple = TupleOf<-3, string>;
|
|
729
|
+
//=> []
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
Note: If the specified length has a decimal part, the decimal part will be ignored.
|
|
717
733
|
|
|
718
734
|
@example
|
|
719
735
|
```
|
|
720
|
-
type
|
|
721
|
-
//=> 1
|
|
736
|
+
import type {TupleOf} from 'type-fest';
|
|
722
737
|
|
|
723
|
-
type
|
|
724
|
-
//=>
|
|
738
|
+
type DecimalLength = TupleOf<3.5, string>;
|
|
739
|
+
//=> [string, string, string]
|
|
740
|
+
```
|
|
725
741
|
|
|
726
|
-
type
|
|
727
|
-
//=> PositiveInfinity
|
|
742
|
+
Note: If you need a readonly tuple, simply wrap this type with `Readonly`, for example, to create `readonly [number, number, number]` use `Readonly<TupleOf<3, number>>`.
|
|
728
743
|
|
|
729
|
-
|
|
730
|
-
//=> NegativeInfinity
|
|
731
|
-
```
|
|
744
|
+
@category Array
|
|
732
745
|
*/
|
|
733
|
-
type
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
746
|
+
type TupleOf<Length extends number, Fill = unknown> = IfNotAnyOrNever<Length, {
|
|
747
|
+
ifNot: _TupleOf<If<IsNegative<Length>, 0, Length>, Fill>;
|
|
748
|
+
ifAny: Fill[];
|
|
749
|
+
ifNever: [];
|
|
750
|
+
}>;
|
|
751
|
+
type _TupleOf<Length extends number, Fill> = number extends Length ? Fill[] : BuildTupleDigitByDigit<`${Length}`, Fill>;
|
|
752
|
+
type BuildTupleDigitByDigit<Length extends string, Fill, Accumulator extends UnknownArray = []> = Length extends `${infer First extends DigitCharacter}${infer Rest}` ? BuildTupleDigitByDigit<Rest, Fill, [...RepeatTupleTenTimes<Accumulator>, ...DigitTupleOf<First, Fill>]> : Accumulator;
|
|
753
|
+
type RepeatTupleTenTimes<Tuple extends UnknownArray> = [...Tuple, ...Tuple, ...Tuple, ...Tuple, ...Tuple, ...Tuple, ...Tuple, ...Tuple, ...Tuple, ...Tuple];
|
|
754
|
+
type DigitTupleOf<Digit extends DigitCharacter, Fill> = [[], [Fill], [Fill, Fill], [Fill, Fill, Fill], [Fill, Fill, Fill, Fill], [Fill, Fill, Fill, Fill, Fill], [Fill, Fill, Fill, Fill, Fill, Fill], [Fill, Fill, Fill, Fill, Fill, Fill, Fill], [Fill, Fill, Fill, Fill, Fill, Fill, Fill, Fill], [Fill, Fill, Fill, Fill, Fill, Fill, Fill, Fill, Fill]][Digit];
|
|
740
755
|
/**
|
|
741
756
|
Useful to flatten the type output to improve type hints shown in editors. And also to transform an interface into a type to aide with assignability.
|
|
742
757
|
|
|
@@ -1033,6 +1048,57 @@ type Merge<Destination, Source> = Destination extends unknown // For distributin
|
|
|
1033
1048
|
: never; // Should never happen
|
|
1034
1049
|
type _Merge<Destination, Source> = Simplify<SimpleMerge<PickIndexSignature<Destination>, PickIndexSignature<Source>> & SimpleMerge<OmitIndexSignature<Destination>, OmitIndexSignature<Source>>>;
|
|
1035
1050
|
/**
|
|
1051
|
+
Converts a numeric string to a number.
|
|
1052
|
+
|
|
1053
|
+
@example
|
|
1054
|
+
```
|
|
1055
|
+
import type {StringToNumber} from 'type-fest';
|
|
1056
|
+
|
|
1057
|
+
type PositiveInteger = StringToNumber<'1234'>;
|
|
1058
|
+
//=> 1234
|
|
1059
|
+
|
|
1060
|
+
type NegativeInteger = StringToNumber<'-1234'>;
|
|
1061
|
+
//=> -1234
|
|
1062
|
+
|
|
1063
|
+
type PositiveFloat = StringToNumber<'1234.56'>;
|
|
1064
|
+
//=> 1234.56
|
|
1065
|
+
|
|
1066
|
+
type NegativeFloat = StringToNumber<'-1234.56'>;
|
|
1067
|
+
//=> -1234.56
|
|
1068
|
+
|
|
1069
|
+
type PositiveInfinity = StringToNumber<'Infinity'>;
|
|
1070
|
+
//=> Infinity
|
|
1071
|
+
|
|
1072
|
+
type NegativeInfinity = StringToNumber<'-Infinity'>;
|
|
1073
|
+
//=> -Infinity
|
|
1074
|
+
```
|
|
1075
|
+
|
|
1076
|
+
Note: Some strings, such as `'1.50'`, `'0b10'`, or `'12_345'`, may look like they can be converted to numbers, but they don't actually have corresponding numeric literals. So, in such cases, this type produces `never`. See [type-fest#1446](https://github.com/sindresorhus/type-fest/pull/1446) for more details.
|
|
1077
|
+
|
|
1078
|
+
@example
|
|
1079
|
+
```
|
|
1080
|
+
import type {StringToNumber} from 'type-fest';
|
|
1081
|
+
|
|
1082
|
+
type FractionalsEndingInZero = StringToNumber<'1.50'>;
|
|
1083
|
+
//=> never
|
|
1084
|
+
|
|
1085
|
+
type NonDecimalBases = StringToNumber<'0b10' | '0o10' | '0x10'>;
|
|
1086
|
+
//=> never
|
|
1087
|
+
|
|
1088
|
+
type NumericSeparators = StringToNumber<'12_345'>;
|
|
1089
|
+
//=> never
|
|
1090
|
+
```
|
|
1091
|
+
|
|
1092
|
+
@category String
|
|
1093
|
+
@category Numeric
|
|
1094
|
+
@category Template literal
|
|
1095
|
+
*/
|
|
1096
|
+
type StringToNumber<S extends string> = IfNotAnyOrNever<S, {
|
|
1097
|
+
ifNot: _StringToNumber<S>;
|
|
1098
|
+
ifAny: number;
|
|
1099
|
+
}>;
|
|
1100
|
+
type _StringToNumber<S extends string> = S extends `${infer N extends number}` ? number extends N ? `${number}` extends S ? N : never : N : string extends S ? number : S extends 'Infinity' ? PositiveInfinity : S extends '-Infinity' ? NegativeInfinity : never;
|
|
1101
|
+
/**
|
|
1036
1102
|
Merges user specified options with default options.
|
|
1037
1103
|
|
|
1038
1104
|
@example
|
|
@@ -1088,6 +1154,34 @@ type ApplyDefaultOptions<Options extends object, Defaults extends Simplify<Omit<
|
|
|
1088
1154
|
) ? Result : never;
|
|
1089
1155
|
type _ApplyDefaultOptions<Options, Defaults, SpecifiedOptions> = If<IsAny<SpecifiedOptions>, Defaults, If<IsNever<SpecifiedOptions>, Defaults, Merge<Defaults, { [Key in keyof SpecifiedOptions as undefined extends Required<Options>[Key & keyof Options] ? Key : undefined extends SpecifiedOptions[Key] ? never : Key]: SpecifiedOptions[Key]; }>>>;
|
|
1090
1156
|
/**
|
|
1157
|
+
Collapses literal types in a union into their corresponding primitive types, when possible. For example, `CollapseLiterals<'foo' | 'bar' | (string & {})>` returns `string`.
|
|
1158
|
+
|
|
1159
|
+
Note: This doesn't collapse literals within tagged types. For example, `CollapseLiterals<Tagged<'foo' | (string & {}), 'Tag'>>` returns `("foo" & Tag<"Tag", never>) | (string & Tag<"Tag", never>)` and not `string & Tag<"Tag", never>`.
|
|
1160
|
+
|
|
1161
|
+
Use-case: For collapsing unions created using {@link LiteralUnion}.
|
|
1162
|
+
|
|
1163
|
+
@example
|
|
1164
|
+
```
|
|
1165
|
+
import type {LiteralUnion} from 'type-fest';
|
|
1166
|
+
|
|
1167
|
+
type A = CollapseLiterals<'foo' | 'bar' | (string & {})>;
|
|
1168
|
+
//=> string
|
|
1169
|
+
|
|
1170
|
+
type B = CollapseLiterals<LiteralUnion<1 | 2 | 3, number>>;
|
|
1171
|
+
//=> number
|
|
1172
|
+
|
|
1173
|
+
type C = CollapseLiterals<LiteralUnion<'onClick' | 'onChange', `on${string}`>>;
|
|
1174
|
+
//=> `on${string}`
|
|
1175
|
+
|
|
1176
|
+
type D = CollapseLiterals<'click' | 'change' | (`on${string}` & {})>;
|
|
1177
|
+
//=> 'click' | 'change' | `on${string}`
|
|
1178
|
+
|
|
1179
|
+
type E = CollapseLiterals<LiteralUnion<'foo' | 'bar', string> | null | undefined>;
|
|
1180
|
+
//=> string | null | undefined
|
|
1181
|
+
```
|
|
1182
|
+
*/
|
|
1183
|
+
type CollapseLiterals<T> = {} extends T ? T : T extends (infer U) & {} ? U : T;
|
|
1184
|
+
/**
|
|
1091
1185
|
@see {@link SomeExtend}
|
|
1092
1186
|
*/
|
|
1093
1187
|
type SomeExtendOptions = {
|
|
@@ -1174,9 +1268,13 @@ type B = SomeExtend<[1?, 2?, '3'?], string | undefined>;
|
|
|
1174
1268
|
@category Array
|
|
1175
1269
|
*/
|
|
1176
1270
|
type SomeExtend<TArray extends UnknownArray, Type, Options extends SomeExtendOptions = {}> = _SomeExtend<CollapseRestElement<TArray>, Type, ApplyDefaultOptions<SomeExtendOptions, DefaultSomeExtendOptions, Options>>;
|
|
1177
|
-
type _SomeExtend<TArray extends UnknownArray, Type, Options extends Required<SomeExtendOptions>> = IfNotAnyOrNever<TArray,
|
|
1178
|
-
|
|
1179
|
-
|
|
1271
|
+
type _SomeExtend<TArray extends UnknownArray, Type, Options extends Required<SomeExtendOptions>> = IfNotAnyOrNever<TArray, {
|
|
1272
|
+
ifNot: TArray extends readonly [infer First, ...infer Rest] ? IsNever<First> extends true ? Or<Or<IsNever<Type>, IsAny<Type>>, Not<Options['strictNever']>> extends true ?
|
|
1273
|
+
// If target `Type` is also `never`, or is `any`, or `strictNever` is disabled, return `true`.
|
|
1274
|
+
true : _SomeExtend<Rest, Type, Options> : First extends Type ? true : _SomeExtend<Rest, Type, Options> : false;
|
|
1275
|
+
ifAny: false;
|
|
1276
|
+
ifNever: false;
|
|
1277
|
+
}>;
|
|
1180
1278
|
/**
|
|
1181
1279
|
Returns a boolean for whether any of the given elements is `true`.
|
|
1182
1280
|
|
|
@@ -1325,6 +1423,215 @@ type G = Or<never, never>;
|
|
|
1325
1423
|
*/
|
|
1326
1424
|
type Or<A extends boolean, B extends boolean> = OrAll<[A, B]>;
|
|
1327
1425
|
/**
|
|
1426
|
+
@see {@link StringToArray}
|
|
1427
|
+
*/
|
|
1428
|
+
type StringToArrayOptions = {
|
|
1429
|
+
/**
|
|
1430
|
+
When enabled, non-literal parts of the string (e.g., `string`, `Uppercase<string>`) are mapped as single elements instead of being mapped as a rest element.
|
|
1431
|
+
|
|
1432
|
+
Note: Enabling this option can produce misleading results that might not reflect the actual runtime behavior.
|
|
1433
|
+
For example, `StringToArray<string, {mapNonLiteralsDirectly: true}>` returns `[string]`, but at runtime, the string could be `'abc'` (which satisfies `string`), and converting it to an array would result in `['a', 'b', 'c']`, which doesn't satisfy `[string]`.
|
|
1434
|
+
|
|
1435
|
+
So, it is recommended to not enable this option unless you are aware of the implications.
|
|
1436
|
+
|
|
1437
|
+
@default false
|
|
1438
|
+
|
|
1439
|
+
@example
|
|
1440
|
+
```
|
|
1441
|
+
import type {StringToArray} from 'type-fest';
|
|
1442
|
+
|
|
1443
|
+
type A = StringToArray<string, {mapNonLiteralsDirectly: false}>;
|
|
1444
|
+
//=> string[]
|
|
1445
|
+
|
|
1446
|
+
type B = StringToArray<string, {mapNonLiteralsDirectly: true}>;
|
|
1447
|
+
//=> [string]
|
|
1448
|
+
|
|
1449
|
+
type C = StringToArray<`on${string}`, {mapNonLiteralsDirectly: false}>;
|
|
1450
|
+
//=> ['o', 'n', ...string[]]
|
|
1451
|
+
|
|
1452
|
+
type D = StringToArray<`on${string}`, {mapNonLiteralsDirectly: true}>;
|
|
1453
|
+
//=> ['o', 'n', string]
|
|
1454
|
+
|
|
1455
|
+
type E = StringToArray<`${string}xyz`, {mapNonLiteralsDirectly: false}>;
|
|
1456
|
+
//=> [...string[], 'x', 'y', 'z']
|
|
1457
|
+
|
|
1458
|
+
type F = StringToArray<`${string}xyz`, {mapNonLiteralsDirectly: true}>;
|
|
1459
|
+
//=> [string, 'x', 'y', 'z']
|
|
1460
|
+
```
|
|
1461
|
+
*/
|
|
1462
|
+
mapNonLiteralsDirectly?: boolean;
|
|
1463
|
+
};
|
|
1464
|
+
type DefaultStringToArrayOptions = {
|
|
1465
|
+
mapNonLiteralsDirectly: false;
|
|
1466
|
+
};
|
|
1467
|
+
/**
|
|
1468
|
+
Returns an array of the characters of the specified string.
|
|
1469
|
+
|
|
1470
|
+
@example
|
|
1471
|
+
```
|
|
1472
|
+
import type {StringToArray} from 'type-fest';
|
|
1473
|
+
|
|
1474
|
+
type A = StringToArray<'abcde'>;
|
|
1475
|
+
//=> ['a', 'b', 'c', 'd', 'e']
|
|
1476
|
+
|
|
1477
|
+
type B = StringToArray<''>;
|
|
1478
|
+
//=> []
|
|
1479
|
+
|
|
1480
|
+
type C = StringToArray<string>;
|
|
1481
|
+
//=> string[]
|
|
1482
|
+
|
|
1483
|
+
type D = StringToArray<`foo${string}bar`>;
|
|
1484
|
+
//=> ['f', 'o', 'o', ...string[], 'b', 'a', 'r']
|
|
1485
|
+
|
|
1486
|
+
type E = StringToArray<`foo${string}bar`, {mapNonLiteralsDirectly: true}>;
|
|
1487
|
+
//=> ['f', 'o', 'o', string, 'b', 'a', 'r']
|
|
1488
|
+
```
|
|
1489
|
+
|
|
1490
|
+
@see {@link StringToArrayOptions}
|
|
1491
|
+
|
|
1492
|
+
@category String
|
|
1493
|
+
*/
|
|
1494
|
+
type StringToArray<S extends string, Options extends StringToArrayOptions = {}> = IfNotAnyOrNever<S, {
|
|
1495
|
+
ifNot: _StringToArray<S, ApplyDefaultOptions<StringToArrayOptions, DefaultStringToArrayOptions, Options>>;
|
|
1496
|
+
ifAny: unknown[];
|
|
1497
|
+
}>;
|
|
1498
|
+
type _StringToArray<S extends string, Options extends Required<StringToArrayOptions>, Accumulator extends string[] = []> = S extends `${infer First}${infer Rest}` ? Or<IsStringLiteral<First>, Options['mapNonLiteralsDirectly']> extends true ? _StringToArray<Rest, Options, [...Accumulator, First]> : _StringToArray<Rest, Options, [...Accumulator, ...First[]]> : S extends '' ? Accumulator : Options['mapNonLiteralsDirectly'] extends true ? [...Accumulator, S] : [...Accumulator, ...S[]];
|
|
1499
|
+
/**
|
|
1500
|
+
Returns the length of the given string.
|
|
1501
|
+
|
|
1502
|
+
@example
|
|
1503
|
+
```
|
|
1504
|
+
import type {StringLength} from 'type-fest';
|
|
1505
|
+
|
|
1506
|
+
type A = StringLength<'abcde'>;
|
|
1507
|
+
//=> 5
|
|
1508
|
+
|
|
1509
|
+
type B = StringLength<'abcde' | 'fgh'>;
|
|
1510
|
+
//=> 3 | 5
|
|
1511
|
+
```
|
|
1512
|
+
|
|
1513
|
+
For non-literal strings, the result is `number` because the length of a non-literal string can be any number.
|
|
1514
|
+
|
|
1515
|
+
@example
|
|
1516
|
+
```
|
|
1517
|
+
import type {StringLength} from 'type-fest';
|
|
1518
|
+
|
|
1519
|
+
type A = StringLength<string>;
|
|
1520
|
+
//=> number
|
|
1521
|
+
|
|
1522
|
+
type B = StringLength<Uppercase<string>>;
|
|
1523
|
+
//=> number
|
|
1524
|
+
|
|
1525
|
+
type C = StringLength<`${string}abc`>;
|
|
1526
|
+
//=> number
|
|
1527
|
+
```
|
|
1528
|
+
|
|
1529
|
+
@category String
|
|
1530
|
+
@category Template literal
|
|
1531
|
+
*/
|
|
1532
|
+
type StringLength<S extends string> = StringToArray<S>['length'];
|
|
1533
|
+
/**
|
|
1534
|
+
Return a string representation of the given string or number.
|
|
1535
|
+
|
|
1536
|
+
Note: This type is not the return type of the `.toString()` function.
|
|
1537
|
+
*/
|
|
1538
|
+
type ToString<T> = T extends string | number ? `${T}` : never;
|
|
1539
|
+
/**
|
|
1540
|
+
Returns a boolean for whether `A` represents a number greater than `B`, where `A` and `B` are both numeric strings and have the same length.
|
|
1541
|
+
|
|
1542
|
+
@example
|
|
1543
|
+
```
|
|
1544
|
+
type A = SameLengthPositiveNumericStringGt<'50', '10'>;
|
|
1545
|
+
//=> true
|
|
1546
|
+
|
|
1547
|
+
type B = SameLengthPositiveNumericStringGt<'10', '10'>;
|
|
1548
|
+
//=> false
|
|
1549
|
+
```
|
|
1550
|
+
*/
|
|
1551
|
+
type SameLengthPositiveNumericStringGt<A extends string, B extends string> = A extends `${infer FirstA}${infer RestA}` ? B extends `${infer FirstB}${infer RestB}` ? FirstA extends FirstB ? SameLengthPositiveNumericStringGt<RestA, RestB> : PositiveNumericCharacterGt<FirstA, FirstB> : never : false;
|
|
1552
|
+
type NumericString = '0123456789';
|
|
1553
|
+
/**
|
|
1554
|
+
Returns a boolean for whether `A` is greater than `B`, where `A` and `B` are both positive numeric strings.
|
|
1555
|
+
|
|
1556
|
+
@example
|
|
1557
|
+
```
|
|
1558
|
+
type A = PositiveNumericStringGt<'500', '1'>;
|
|
1559
|
+
//=> true
|
|
1560
|
+
|
|
1561
|
+
type B = PositiveNumericStringGt<'1', '1'>;
|
|
1562
|
+
//=> false
|
|
1563
|
+
|
|
1564
|
+
type C = PositiveNumericStringGt<'1', '500'>;
|
|
1565
|
+
//=> false
|
|
1566
|
+
```
|
|
1567
|
+
*/
|
|
1568
|
+
type PositiveNumericStringGt<A extends string, B extends string> = A extends B ? false : [TupleOf<StringLength<A>, 0>, TupleOf<StringLength<B>, 0>] extends (infer R extends [readonly unknown[], readonly unknown[]]) ? R[0] extends [...R[1], ...infer Remain extends readonly unknown[]] ? 0 extends Remain['length'] ? SameLengthPositiveNumericStringGt<A, B> : true : false : never;
|
|
1569
|
+
/**
|
|
1570
|
+
Returns a boolean for whether `A` represents a number greater than `B`, where `A` and `B` are both positive numeric characters.
|
|
1571
|
+
|
|
1572
|
+
@example
|
|
1573
|
+
```
|
|
1574
|
+
type A = PositiveNumericCharacterGt<'5', '1'>;
|
|
1575
|
+
//=> true
|
|
1576
|
+
|
|
1577
|
+
type B = PositiveNumericCharacterGt<'1', '1'>;
|
|
1578
|
+
//=> false
|
|
1579
|
+
```
|
|
1580
|
+
*/
|
|
1581
|
+
type PositiveNumericCharacterGt<A extends string, B extends string> = NumericString extends `${infer HeadA}${A}${infer TailA}` ? NumericString extends `${infer HeadB}${B}${infer TailB}` ? HeadA extends `${HeadB}${infer _}${infer __}` ? true : false : never : never;
|
|
1582
|
+
/**
|
|
1583
|
+
Check whether the given type is a number or a number string.
|
|
1584
|
+
|
|
1585
|
+
Supports floating-point as a string.
|
|
1586
|
+
|
|
1587
|
+
@example
|
|
1588
|
+
```
|
|
1589
|
+
type A = IsNumberLike<'1'>;
|
|
1590
|
+
//=> true
|
|
1591
|
+
|
|
1592
|
+
type B = IsNumberLike<'-1.1'>;
|
|
1593
|
+
//=> true
|
|
1594
|
+
|
|
1595
|
+
type C = IsNumberLike<'5e-20'>;
|
|
1596
|
+
//=> true
|
|
1597
|
+
|
|
1598
|
+
type D = IsNumberLike<1>;
|
|
1599
|
+
//=> true
|
|
1600
|
+
|
|
1601
|
+
type E = IsNumberLike<'a'>;
|
|
1602
|
+
//=> false
|
|
1603
|
+
*/
|
|
1604
|
+
type IsNumberLike<N> = IfNotAnyOrNever<N, {
|
|
1605
|
+
ifNot: N extends number | `${number}` ? true : false;
|
|
1606
|
+
ifAny: boolean;
|
|
1607
|
+
ifNever: false;
|
|
1608
|
+
}>;
|
|
1609
|
+
/**
|
|
1610
|
+
Returns the number with reversed sign.
|
|
1611
|
+
|
|
1612
|
+
@example
|
|
1613
|
+
```
|
|
1614
|
+
type A = ReverseSign<-1>;
|
|
1615
|
+
//=> 1
|
|
1616
|
+
|
|
1617
|
+
type B = ReverseSign<1>;
|
|
1618
|
+
//=> -1
|
|
1619
|
+
|
|
1620
|
+
type C = ReverseSign<NegativeInfinity>;
|
|
1621
|
+
//=> PositiveInfinity
|
|
1622
|
+
|
|
1623
|
+
type D = ReverseSign<PositiveInfinity>;
|
|
1624
|
+
//=> NegativeInfinity
|
|
1625
|
+
```
|
|
1626
|
+
*/
|
|
1627
|
+
type ReverseSign<N extends number> =
|
|
1628
|
+
// Handle edge cases
|
|
1629
|
+
N extends 0 ? 0 : N extends PositiveInfinity ? NegativeInfinity : N extends NegativeInfinity ? PositiveInfinity :
|
|
1630
|
+
// Handle negative numbers
|
|
1631
|
+
`${N}` extends `-${infer P extends number}` ? P :
|
|
1632
|
+
// Handle positive numbers
|
|
1633
|
+
`-${N}` extends `${infer R extends number}` ? R : never;
|
|
1634
|
+
/**
|
|
1328
1635
|
@see {@link AllExtend}
|
|
1329
1636
|
*/
|
|
1330
1637
|
type AllExtendOptions = {
|
|
@@ -1417,9 +1724,13 @@ type B = AllExtend<[1?, 2?, 3?], number | undefined>;
|
|
|
1417
1724
|
@category Array
|
|
1418
1725
|
*/
|
|
1419
1726
|
type AllExtend<TArray extends UnknownArray, Type, Options extends AllExtendOptions = {}> = _AllExtend<CollapseRestElement<TArray>, Type, ApplyDefaultOptions<AllExtendOptions, DefaultAllExtendOptions, Options>>;
|
|
1420
|
-
type _AllExtend<TArray extends UnknownArray, Type, Options extends Required<AllExtendOptions>> = IfNotAnyOrNever<TArray,
|
|
1421
|
-
|
|
1422
|
-
|
|
1727
|
+
type _AllExtend<TArray extends UnknownArray, Type, Options extends Required<AllExtendOptions>> = IfNotAnyOrNever<TArray, {
|
|
1728
|
+
ifNot: TArray extends readonly [infer First, ...infer Rest] ? IsNever<First> extends true ? Or<Or<IsNever<Type>, IsAny<Type>>, Not<Options['strictNever']>> extends true ?
|
|
1729
|
+
// If target `Type` is also `never`, or is `any`, or `strictNever` is disabled, recurse further.
|
|
1730
|
+
_AllExtend<Rest, Type, Options> : false : First extends Type ? _AllExtend<Rest, Type, Options> : false : true;
|
|
1731
|
+
ifAny: false;
|
|
1732
|
+
ifNever: false;
|
|
1733
|
+
}>;
|
|
1423
1734
|
/**
|
|
1424
1735
|
Returns a boolean for whether all of the given elements are `true`.
|
|
1425
1736
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@visulima/package",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.15",
|
|
4
4
|
"description": "A comprehensive package management utility that helps you find root directories, monorepos, package managers, and parse package.json, package.yaml, and package.json5 files with advanced features like catalog resolution.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"anolilab",
|
|
@@ -99,8 +99,8 @@
|
|
|
99
99
|
"MIGRATION-GUIDE.md"
|
|
100
100
|
],
|
|
101
101
|
"dependencies": {
|
|
102
|
-
"@antfu/install-pkg": "^
|
|
103
|
-
"@visulima/fs": "6.0.
|
|
102
|
+
"@antfu/install-pkg": "^2.0.1",
|
|
103
|
+
"@visulima/fs": "6.0.2",
|
|
104
104
|
"@visulima/path": "4.0.0",
|
|
105
105
|
"json5": "^2.2.3",
|
|
106
106
|
"normalize-package-data": "^9.0.0",
|