feature: vor und nachteile

This commit is contained in:
Hikyu
2026-05-31 13:54:31 +02:00
parent 552a947a1c
commit e3dad17446
23270 changed files with 3024794 additions and 200 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021-PRESENT Anthony Fu <https://github.com/antfu>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+15
View File
@@ -0,0 +1,15 @@
# @unocss/preset-icons
Use **any** icons with **Pure CSS** for [UnoCSS](https://github.com/unocss/unocss).
## Documentation
Please refer to the [documentation](https://unocss.dev/presets/icons).
## Credits
This preset is inspired from [this issue](https://github.com/antfu/unplugin-icons/issues/88) created by [@husayt](https://github.com/husayt). Based on the work of [this PR](https://github.com/antfu/unplugin-icons/pull/90) by [@userquin](https://github.com/userquin).
## License
MIT License &copy; 2021-PRESENT [Anthony Fu](https://github.com/antfu)
+10
View File
@@ -0,0 +1,10 @@
import * as _unocss_core from '@unocss/core';
import { IconsOptions } from './core.mjs';
export { IconsAPI, combineLoaders, createCDNFetchLoader, createPresetIcons, getEnvFlags, icons, parseIconWithLoader } from './core.mjs';
import '@iconify/utils';
import '@iconify/types';
import '@iconify/utils/lib/svg/encode-svg-for-css';
declare const presetIcons: _unocss_core.PresetFactory<object, IconsOptions>;
export { IconsOptions, presetIcons as default, presetIcons };
+10
View File
@@ -0,0 +1,10 @@
import * as _unocss_core from '@unocss/core';
import { IconsOptions } from './core.js';
export { IconsAPI, combineLoaders, createCDNFetchLoader, createPresetIcons, getEnvFlags, icons, parseIconWithLoader } from './core.js';
import '@iconify/utils';
import '@iconify/types';
import '@iconify/utils/lib/svg/encode-svg-for-css';
declare const presetIcons: _unocss_core.PresetFactory<object, IconsOptions>;
export { IconsOptions, presetIcons as default, presetIcons };
+17
View File
@@ -0,0 +1,17 @@
import { c as createCDNLoader } from './shared/preset-icons.BL7F4I2g.mjs';
import { c as createPresetIcons, b as createCDNFetchLoader, l as loadIcon } from './shared/preset-icons.D5PlqJL2.mjs';
export { a as combineLoaders, g as getEnvFlags, i as icons, p as parseIconWithLoader } from './shared/preset-icons.D5PlqJL2.mjs';
import 'ofetch';
import '@unocss/core';
const presetIcons = createPresetIcons(async (options) => {
const fetcher = options?.customFetch;
const cdn = options?.cdn;
if (fetcher && cdn)
return createCDNFetchLoader(fetcher, cdn);
if (cdn)
return createCDNLoader(cdn);
return loadIcon;
});
export { createCDNFetchLoader, createPresetIcons, presetIcons as default, presetIcons };
+358
View File
@@ -0,0 +1,358 @@
import * as _unocss_core from '@unocss/core';
import { Awaitable, CSSObject } from '@unocss/core';
import { CustomIconLoader, InlineCollection, IconCustomizations, UniversalIconLoader, IconifyLoaderOptions } from '@iconify/utils';
import { IconifyJSON } from '@iconify/types';
import { encodeSvgForCss } from '@iconify/utils/lib/svg/encode-svg-for-css';
interface IconMeta {
collection: string;
name: string;
svg: string;
mode?: IconsOptions['mode'];
/**
* @deprecated renamed to `name`
*/
icon: string;
}
interface IconsOptions {
/**
* Scale related to the current font size (1em).
*
* @default 1
*/
scale?: number;
/**
* Mode of generated CSS icons.
*
* - `mask` - use background color and the `mask` property for monochrome icons
* - `background-img` - use background image for the icons, colors are static
* - `auto` - smartly decide mode between `mask` and `background-img` per icon based on its style
*
* @default 'auto'
* @see https://antfu.me/posts/icons-in-pure-css
*/
mode?: 'mask' | 'bg' | 'auto';
/**
* Class prefix for matching icon rules.
*
* @default `i-`
*/
prefix?: string | string[];
/**
* Extra CSS properties applied to the generated CSS
*
* @default {}
*/
extraProperties?: Record<string, string>;
/**
* Emit warning when missing icons are matched
*
* @default false
*/
warn?: boolean;
/**
* `@iconify-json` collections to use (will be also auto installed when missing and `autoInstall` enabled).
*
* This option should be used only when there are new `@iconify-json` collections not listed in the default icons preset collection names.
*
* Adding external collections will not work, you should use `FileSystemIconLoader` from
* `@iconify/utils/lib/loader/fs` or `createExternalPackageIconLoader` from
* `@iconify/utils/lib/loader/external-pkg` instead.
*
* @see https://unocss.dev/presets/icons#filesystemiconloader
* @see https://unocss.dev/presets/icons#externalpackageiconloader
*/
iconifyCollectionsNames?: string[];
/**
* In Node.js environment, the preset will search for the installed iconify dataset automatically.
* When using in the browser, this options is provided to provide dataset with custom loading mechanism.
*/
collections?: Record<string, (() => Awaitable<IconifyJSON>) | undefined | CustomIconLoader | InlineCollection>;
/**
* Rule layer
*
* @default 'icons'
*/
layer?: string;
/**
* Custom icon customizations.
*/
customizations?: Omit<IconCustomizations, 'additionalProps' | 'trimCustomSvg'>;
/**
* Auto install icon sources package when the usages is detected
*
* Only effective in Node.js environment.
*
* @default false
*/
autoInstall?: boolean;
/**
* Path to resolve the iconify collections in Node.js environment.
*
* @default process.cwd()
*/
collectionsNodeResolvePath?: string | string[];
/**
* Custom icon unit.
*
* @default `em`
*/
unit?: string;
/**
* Load icons from CDN. Should starts with `https://` and ends with `/`
*
* Recommends:
* - https://esm.sh/
* - https://cdn.skypack.dev/
*/
cdn?: string;
/**
* Custom fetch function to provide the icon data.
*/
customFetch?: (url: string) => Promise<any>;
/**
* Processor for the CSS object before stringify
*/
processor?: (cssObject: CSSObject, meta: Required<IconMeta>) => void;
}
function getDefaultExportFromCjs (x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}
var collections$1;
var hasRequiredCollections;
function requireCollections () {
if (hasRequiredCollections) return collections$1;
hasRequiredCollections = 1;
collections$1 = [
"academicons",
"akar-icons",
"ant-design",
"arcticons",
"basil",
"bi",
"bitcoin-icons",
"bpmn",
"brandico",
"bubbles",
"bx",
"bxl",
"bxs",
"bytesize",
"carbon",
"catppuccin",
"cbi",
"charm",
"ci",
"cib",
"cif",
"cil",
"circle-flags",
"circum",
"clarity",
"codex",
"codicon",
"covid",
"cryptocurrency-color",
"cryptocurrency",
"cuida",
"dashicons",
"devicon-line",
"devicon-original",
"devicon-plain",
"devicon",
"duo-icons",
"ei",
"el",
"emblemicons",
"emojione-monotone",
"emojione-v1",
"emojione",
"entypo-social",
"entypo",
"eos-icons",
"ep",
"et",
"eva",
"f7",
"fa-brands",
"fa-regular",
"fa-solid",
"fa",
"fa6-brands",
"fa6-regular",
"fa6-solid",
"fad",
"famicons",
"fe",
"feather",
"file-icons",
"flag",
"flagpack",
"flat-color-icons",
"flat-ui",
"flowbite",
"fluent-color",
"fluent-emoji-flat",
"fluent-emoji-high-contrast",
"fluent-emoji",
"fluent-mdl2",
"fluent",
"fontelico",
"fontisto",
"formkit",
"foundation",
"fxemoji",
"gala",
"game-icons",
"garden",
"geo",
"gg",
"gis",
"gravity-ui",
"gridicons",
"grommet-icons",
"guidance",
"healthicons",
"heroicons-outline",
"heroicons-solid",
"heroicons",
"hugeicons",
"humbleicons",
"ic",
"icomoon-free",
"icon-park-outline",
"icon-park-solid",
"icon-park-twotone",
"icon-park",
"iconamoon",
"iconoir",
"icons8",
"il",
"ion",
"iwwa",
"ix",
"jam",
"la",
"lets-icons",
"line-md",
"lineicons",
"logos",
"ls",
"lsicon",
"lucide-lab",
"lucide",
"mage",
"majesticons",
"maki",
"map",
"marketeq",
"material-symbols-light",
"material-symbols",
"mdi-light",
"mdi",
"medical-icon",
"memory",
"meteocons",
"meteor-icons",
"mi",
"mingcute",
"mono-icons",
"mynaui",
"nimbus",
"nonicons",
"noto-v1",
"noto",
"nrk",
"octicon",
"oi",
"ooui",
"openmoji",
"oui",
"pajamas",
"pepicons-pencil",
"pepicons-pop",
"pepicons-print",
"pepicons",
"ph",
"pixelarticons",
"prime",
"proicons",
"ps",
"qlementine-icons",
"quill",
"radix-icons",
"raphael",
"ri",
"rivet-icons",
"si-glyph",
"si",
"simple-icons",
"simple-line-icons",
"skill-icons",
"solar",
"stash",
"streamline-emojis",
"streamline",
"subway",
"svg-spinners",
"system-uicons",
"tabler",
"tdesign",
"teenyicons",
"token-branded",
"token",
"topcoat",
"twemoji",
"typcn",
"uil",
"uim",
"uis",
"uit",
"uiw",
"unjs",
"vaadin",
"vs",
"vscode-icons",
"websymbol",
"weui",
"whh",
"wi",
"wpf",
"zmdi",
"zondicons"
];
return collections$1;
}
var collectionsExports = /*@__PURE__*/ requireCollections();
var collections = /*@__PURE__*/getDefaultExportFromCjs(collectionsExports);
/**
* API for preset-icons
*/
interface IconsAPI {
encodeSvgForCss: typeof encodeSvgForCss;
parseIconWithLoader: typeof parseIconWithLoader;
/**
* This API only available for preset-icons created on Node.js environment
*/
createNodeLoader?: () => Promise<UniversalIconLoader | undefined>;
}
declare function createPresetIcons(lookupIconLoader: (options: IconsOptions) => Promise<UniversalIconLoader>): _unocss_core.PresetFactory<object, IconsOptions>;
declare function combineLoaders(loaders: UniversalIconLoader[]): UniversalIconLoader;
declare function createCDNFetchLoader(fetcher: (url: string) => Promise<any>, cdnBase: string): UniversalIconLoader;
declare function getEnvFlags(): {
isNode: boolean;
isVSCode: boolean;
isESLint: boolean;
};
declare function parseIconWithLoader(body: string, loader: UniversalIconLoader, options?: IconifyLoaderOptions, safeCollectionsNames?: string[]): Promise<{
collection: string;
name: string;
svg: string;
} | undefined>;
export { type IconsAPI, type IconsOptions, combineLoaders, createCDNFetchLoader, createPresetIcons, getEnvFlags, collections as icons, parseIconWithLoader };
+358
View File
@@ -0,0 +1,358 @@
import * as _unocss_core from '@unocss/core';
import { Awaitable, CSSObject } from '@unocss/core';
import { CustomIconLoader, InlineCollection, IconCustomizations, UniversalIconLoader, IconifyLoaderOptions } from '@iconify/utils';
import { IconifyJSON } from '@iconify/types';
import { encodeSvgForCss } from '@iconify/utils/lib/svg/encode-svg-for-css';
interface IconMeta {
collection: string;
name: string;
svg: string;
mode?: IconsOptions['mode'];
/**
* @deprecated renamed to `name`
*/
icon: string;
}
interface IconsOptions {
/**
* Scale related to the current font size (1em).
*
* @default 1
*/
scale?: number;
/**
* Mode of generated CSS icons.
*
* - `mask` - use background color and the `mask` property for monochrome icons
* - `background-img` - use background image for the icons, colors are static
* - `auto` - smartly decide mode between `mask` and `background-img` per icon based on its style
*
* @default 'auto'
* @see https://antfu.me/posts/icons-in-pure-css
*/
mode?: 'mask' | 'bg' | 'auto';
/**
* Class prefix for matching icon rules.
*
* @default `i-`
*/
prefix?: string | string[];
/**
* Extra CSS properties applied to the generated CSS
*
* @default {}
*/
extraProperties?: Record<string, string>;
/**
* Emit warning when missing icons are matched
*
* @default false
*/
warn?: boolean;
/**
* `@iconify-json` collections to use (will be also auto installed when missing and `autoInstall` enabled).
*
* This option should be used only when there are new `@iconify-json` collections not listed in the default icons preset collection names.
*
* Adding external collections will not work, you should use `FileSystemIconLoader` from
* `@iconify/utils/lib/loader/fs` or `createExternalPackageIconLoader` from
* `@iconify/utils/lib/loader/external-pkg` instead.
*
* @see https://unocss.dev/presets/icons#filesystemiconloader
* @see https://unocss.dev/presets/icons#externalpackageiconloader
*/
iconifyCollectionsNames?: string[];
/**
* In Node.js environment, the preset will search for the installed iconify dataset automatically.
* When using in the browser, this options is provided to provide dataset with custom loading mechanism.
*/
collections?: Record<string, (() => Awaitable<IconifyJSON>) | undefined | CustomIconLoader | InlineCollection>;
/**
* Rule layer
*
* @default 'icons'
*/
layer?: string;
/**
* Custom icon customizations.
*/
customizations?: Omit<IconCustomizations, 'additionalProps' | 'trimCustomSvg'>;
/**
* Auto install icon sources package when the usages is detected
*
* Only effective in Node.js environment.
*
* @default false
*/
autoInstall?: boolean;
/**
* Path to resolve the iconify collections in Node.js environment.
*
* @default process.cwd()
*/
collectionsNodeResolvePath?: string | string[];
/**
* Custom icon unit.
*
* @default `em`
*/
unit?: string;
/**
* Load icons from CDN. Should starts with `https://` and ends with `/`
*
* Recommends:
* - https://esm.sh/
* - https://cdn.skypack.dev/
*/
cdn?: string;
/**
* Custom fetch function to provide the icon data.
*/
customFetch?: (url: string) => Promise<any>;
/**
* Processor for the CSS object before stringify
*/
processor?: (cssObject: CSSObject, meta: Required<IconMeta>) => void;
}
function getDefaultExportFromCjs (x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}
var collections$1;
var hasRequiredCollections;
function requireCollections () {
if (hasRequiredCollections) return collections$1;
hasRequiredCollections = 1;
collections$1 = [
"academicons",
"akar-icons",
"ant-design",
"arcticons",
"basil",
"bi",
"bitcoin-icons",
"bpmn",
"brandico",
"bubbles",
"bx",
"bxl",
"bxs",
"bytesize",
"carbon",
"catppuccin",
"cbi",
"charm",
"ci",
"cib",
"cif",
"cil",
"circle-flags",
"circum",
"clarity",
"codex",
"codicon",
"covid",
"cryptocurrency-color",
"cryptocurrency",
"cuida",
"dashicons",
"devicon-line",
"devicon-original",
"devicon-plain",
"devicon",
"duo-icons",
"ei",
"el",
"emblemicons",
"emojione-monotone",
"emojione-v1",
"emojione",
"entypo-social",
"entypo",
"eos-icons",
"ep",
"et",
"eva",
"f7",
"fa-brands",
"fa-regular",
"fa-solid",
"fa",
"fa6-brands",
"fa6-regular",
"fa6-solid",
"fad",
"famicons",
"fe",
"feather",
"file-icons",
"flag",
"flagpack",
"flat-color-icons",
"flat-ui",
"flowbite",
"fluent-color",
"fluent-emoji-flat",
"fluent-emoji-high-contrast",
"fluent-emoji",
"fluent-mdl2",
"fluent",
"fontelico",
"fontisto",
"formkit",
"foundation",
"fxemoji",
"gala",
"game-icons",
"garden",
"geo",
"gg",
"gis",
"gravity-ui",
"gridicons",
"grommet-icons",
"guidance",
"healthicons",
"heroicons-outline",
"heroicons-solid",
"heroicons",
"hugeicons",
"humbleicons",
"ic",
"icomoon-free",
"icon-park-outline",
"icon-park-solid",
"icon-park-twotone",
"icon-park",
"iconamoon",
"iconoir",
"icons8",
"il",
"ion",
"iwwa",
"ix",
"jam",
"la",
"lets-icons",
"line-md",
"lineicons",
"logos",
"ls",
"lsicon",
"lucide-lab",
"lucide",
"mage",
"majesticons",
"maki",
"map",
"marketeq",
"material-symbols-light",
"material-symbols",
"mdi-light",
"mdi",
"medical-icon",
"memory",
"meteocons",
"meteor-icons",
"mi",
"mingcute",
"mono-icons",
"mynaui",
"nimbus",
"nonicons",
"noto-v1",
"noto",
"nrk",
"octicon",
"oi",
"ooui",
"openmoji",
"oui",
"pajamas",
"pepicons-pencil",
"pepicons-pop",
"pepicons-print",
"pepicons",
"ph",
"pixelarticons",
"prime",
"proicons",
"ps",
"qlementine-icons",
"quill",
"radix-icons",
"raphael",
"ri",
"rivet-icons",
"si-glyph",
"si",
"simple-icons",
"simple-line-icons",
"skill-icons",
"solar",
"stash",
"streamline-emojis",
"streamline",
"subway",
"svg-spinners",
"system-uicons",
"tabler",
"tdesign",
"teenyicons",
"token-branded",
"token",
"topcoat",
"twemoji",
"typcn",
"uil",
"uim",
"uis",
"uit",
"uiw",
"unjs",
"vaadin",
"vs",
"vscode-icons",
"websymbol",
"weui",
"whh",
"wi",
"wpf",
"zmdi",
"zondicons"
];
return collections$1;
}
var collectionsExports = /*@__PURE__*/ requireCollections();
var collections = /*@__PURE__*/getDefaultExportFromCjs(collectionsExports);
/**
* API for preset-icons
*/
interface IconsAPI {
encodeSvgForCss: typeof encodeSvgForCss;
parseIconWithLoader: typeof parseIconWithLoader;
/**
* This API only available for preset-icons created on Node.js environment
*/
createNodeLoader?: () => Promise<UniversalIconLoader | undefined>;
}
declare function createPresetIcons(lookupIconLoader: (options: IconsOptions) => Promise<UniversalIconLoader>): _unocss_core.PresetFactory<object, IconsOptions>;
declare function combineLoaders(loaders: UniversalIconLoader[]): UniversalIconLoader;
declare function createCDNFetchLoader(fetcher: (url: string) => Promise<any>, cdnBase: string): UniversalIconLoader;
declare function getEnvFlags(): {
isNode: boolean;
isVSCode: boolean;
isESLint: boolean;
};
declare function parseIconWithLoader(body: string, loader: UniversalIconLoader, options?: IconifyLoaderOptions, safeCollectionsNames?: string[]): Promise<{
collection: string;
name: string;
svg: string;
} | undefined>;
export { type IconsAPI, type IconsOptions, combineLoaders, createCDNFetchLoader, createPresetIcons, getEnvFlags, collections as icons, parseIconWithLoader };
+2
View File
@@ -0,0 +1,2 @@
export { a as combineLoaders, b as createCDNFetchLoader, c as createPresetIcons, g as getEnvFlags, i as icons, p as parseIconWithLoader } from './shared/preset-icons.D5PlqJL2.mjs';
import '@unocss/core';
+24
View File
@@ -0,0 +1,24 @@
import * as _unocss_core from '@unocss/core';
import { UniversalIconLoader } from '@iconify/utils';
import { IconsOptions } from './core.mjs';
export { IconsAPI, combineLoaders, createCDNFetchLoader, createPresetIcons, getEnvFlags, icons, parseIconWithLoader } from './core.mjs';
import '@iconify/types';
import '@iconify/utils/lib/svg/encode-svg-for-css';
/**
* Use any icon with Pure CSS for UnoCSS.
*
* @example
*
* ```html
* <div class="i-mdi-alarm"></div>
* <div class="i-logos-vue text-3xl"></div>
* <button class="i-carbon-sun dark:i-carbon-moon"></div>
* ```
*
* @see https://unocss.dev/presets/icons
*/
declare const presetIcons: _unocss_core.PresetFactory<object, IconsOptions>;
declare function createNodeLoader(): Promise<UniversalIconLoader | undefined>;
export { IconsOptions, createNodeLoader, presetIcons as default, presetIcons };
+24
View File
@@ -0,0 +1,24 @@
import * as _unocss_core from '@unocss/core';
import { UniversalIconLoader } from '@iconify/utils';
import { IconsOptions } from './core.js';
export { IconsAPI, combineLoaders, createCDNFetchLoader, createPresetIcons, getEnvFlags, icons, parseIconWithLoader } from './core.js';
import '@iconify/types';
import '@iconify/utils/lib/svg/encode-svg-for-css';
/**
* Use any icon with Pure CSS for UnoCSS.
*
* @example
*
* ```html
* <div class="i-mdi-alarm"></div>
* <div class="i-logos-vue text-3xl"></div>
* <button class="i-carbon-sun dark:i-carbon-moon"></div>
* ```
*
* @see https://unocss.dev/presets/icons
*/
declare const presetIcons: _unocss_core.PresetFactory<object, IconsOptions>;
declare function createNodeLoader(): Promise<UniversalIconLoader | undefined>;
export { IconsOptions, createNodeLoader, presetIcons as default, presetIcons };
+44
View File
@@ -0,0 +1,44 @@
import { definePreset } from '@unocss/core';
import { c as createCDNLoader } from './shared/preset-icons.BL7F4I2g.mjs';
import { c as createPresetIcons, g as getEnvFlags, a as combineLoaders, l as loadIcon } from './shared/preset-icons.D5PlqJL2.mjs';
export { b as createCDNFetchLoader, i as icons, p as parseIconWithLoader } from './shared/preset-icons.D5PlqJL2.mjs';
import 'ofetch';
const _factory = /* @__PURE__ */ createPresetIcons(async (options) => {
const {
cdn
} = options;
const loaders = [];
const {
isNode,
isVSCode,
isESLint
} = getEnvFlags();
if (isNode && !isVSCode && !isESLint) {
const nodeLoader = await createNodeLoader();
if (nodeLoader !== void 0)
loaders.push(nodeLoader);
}
if (cdn)
loaders.push(createCDNLoader(cdn));
loaders.push(loadIcon);
return combineLoaders(loaders);
});
const presetIcons = /* @__PURE__ */ definePreset((options = {}) => {
const preset = _factory(options);
const api = preset.api;
api.createNodeLoader = createNodeLoader;
return preset;
});
async function createNodeLoader() {
try {
return await import('@iconify/utils/lib/loader/node-loader').then((i) => i?.loadNodeIcon);
} catch {
}
try {
return require("@iconify/utils/lib/loader/node-loader.cjs").loadNodeIcon;
} catch {
}
}
export { combineLoaders, createNodeLoader, createPresetIcons, presetIcons as default, getEnvFlags, presetIcons };
@@ -0,0 +1,8 @@
import { $fetch } from 'ofetch';
import { b as createCDNFetchLoader } from './preset-icons.D5PlqJL2.mjs';
function createCDNLoader(cdnBase) {
return createCDNFetchLoader($fetch, cdnBase);
}
export { createCDNLoader as c };
@@ -0,0 +1,943 @@
import { definePreset, warnOnce } from '@unocss/core';
const defaultIconDimensions = Object.freeze(
{
left: 0,
top: 0,
width: 16,
height: 16
}
);
const defaultIconTransformations = Object.freeze({
rotate: 0,
vFlip: false,
hFlip: false
});
const defaultIconProps = Object.freeze({
...defaultIconDimensions,
...defaultIconTransformations
});
const defaultExtendedIconProps = Object.freeze({
...defaultIconProps,
body: "",
hidden: false
});
const defaultIconSizeCustomisations = Object.freeze({
width: null,
height: null
});
const defaultIconCustomisations = Object.freeze({
// Dimensions
...defaultIconSizeCustomisations,
// Transformations
...defaultIconTransformations
});
function mergeIconTransformations(obj1, obj2) {
const result = {};
if (!obj1.hFlip !== !obj2.hFlip) {
result.hFlip = true;
}
if (!obj1.vFlip !== !obj2.vFlip) {
result.vFlip = true;
}
const rotate = ((obj1.rotate || 0) + (obj2.rotate || 0)) % 4;
if (rotate) {
result.rotate = rotate;
}
return result;
}
function mergeIconData(parent, child) {
const result = mergeIconTransformations(parent, child);
for (const key in defaultExtendedIconProps) {
if (key in defaultIconTransformations) {
if (key in parent && !(key in result)) {
result[key] = defaultIconTransformations[key];
}
} else if (key in child) {
result[key] = child[key];
} else if (key in parent) {
result[key] = parent[key];
}
}
return result;
}
function getIconsTree(data, names) {
const icons = data.icons;
const aliases = data.aliases || /* @__PURE__ */ Object.create(null);
const resolved = /* @__PURE__ */ Object.create(null);
function resolve(name) {
if (icons[name]) {
return resolved[name] = [];
}
if (!(name in resolved)) {
resolved[name] = null;
const parent = aliases[name] && aliases[name].parent;
const value = parent && resolve(parent);
if (value) {
resolved[name] = [parent].concat(value);
}
}
return resolved[name];
}
(names).forEach(resolve);
return resolved;
}
function internalGetIconData(data, name, tree) {
const icons = data.icons;
const aliases = data.aliases || /* @__PURE__ */ Object.create(null);
let currentProps = {};
function parse(name2) {
currentProps = mergeIconData(
icons[name2] || aliases[name2],
currentProps
);
}
parse(name);
tree.forEach(parse);
return mergeIconData(data, currentProps);
}
function getIconData(data, name) {
if (data.icons[name]) {
return internalGetIconData(data, name, []);
}
const tree = getIconsTree(data, [name])[name];
return tree ? internalGetIconData(data, name, tree) : null;
}
const unitsSplit = /(-?[0-9.]*[0-9]+[0-9.]*)/g;
const unitsTest = /^-?[0-9.]*[0-9]+[0-9.]*$/g;
function calculateSize(size, ratio, precision) {
if (ratio === 1) {
return size;
}
precision = precision || 100;
if (typeof size === "number") {
return Math.ceil(size * ratio * precision) / precision;
}
if (typeof size !== "string") {
return size;
}
const oldParts = size.split(unitsSplit);
if (oldParts === null || !oldParts.length) {
return size;
}
const newParts = [];
let code = oldParts.shift();
let isNumber = unitsTest.test(code);
while (true) {
if (isNumber) {
const num = parseFloat(code);
if (isNaN(num)) {
newParts.push(code);
} else {
newParts.push(Math.ceil(num * ratio * precision) / precision);
}
} else {
newParts.push(code);
}
code = oldParts.shift();
if (code === void 0) {
return newParts.join("");
}
isNumber = !isNumber;
}
}
function splitSVGDefs(content, tag = "defs") {
let defs = "";
const index = content.indexOf("<" + tag);
while (index >= 0) {
const start = content.indexOf(">", index);
const end = content.indexOf("</" + tag);
if (start === -1 || end === -1) {
break;
}
const endEnd = content.indexOf(">", end);
if (endEnd === -1) {
break;
}
defs += content.slice(start + 1, end).trim();
content = content.slice(0, index).trim() + content.slice(endEnd + 1);
}
return {
defs,
content
};
}
function mergeDefsAndContent(defs, content) {
return defs ? "<defs>" + defs + "</defs>" + content : content;
}
function wrapSVGContent(body, start, end) {
const split = splitSVGDefs(body);
return mergeDefsAndContent(split.defs, start + split.content + end);
}
const isUnsetKeyword = (value) => value === "unset" || value === "undefined" || value === "none";
function iconToSVG(icon, customisations) {
const fullIcon = {
...defaultIconProps,
...icon
};
const fullCustomisations = {
...defaultIconCustomisations,
...customisations
};
const box = {
left: fullIcon.left,
top: fullIcon.top,
width: fullIcon.width,
height: fullIcon.height
};
let body = fullIcon.body;
[fullIcon, fullCustomisations].forEach((props) => {
const transformations = [];
const hFlip = props.hFlip;
const vFlip = props.vFlip;
let rotation = props.rotate;
if (hFlip) {
if (vFlip) {
rotation += 2;
} else {
transformations.push(
"translate(" + (box.width + box.left).toString() + " " + (0 - box.top).toString() + ")"
);
transformations.push("scale(-1 1)");
box.top = box.left = 0;
}
} else if (vFlip) {
transformations.push(
"translate(" + (0 - box.left).toString() + " " + (box.height + box.top).toString() + ")"
);
transformations.push("scale(1 -1)");
box.top = box.left = 0;
}
let tempValue;
if (rotation < 0) {
rotation -= Math.floor(rotation / 4) * 4;
}
rotation = rotation % 4;
switch (rotation) {
case 1:
tempValue = box.height / 2 + box.top;
transformations.unshift(
"rotate(90 " + tempValue.toString() + " " + tempValue.toString() + ")"
);
break;
case 2:
transformations.unshift(
"rotate(180 " + (box.width / 2 + box.left).toString() + " " + (box.height / 2 + box.top).toString() + ")"
);
break;
case 3:
tempValue = box.width / 2 + box.left;
transformations.unshift(
"rotate(-90 " + tempValue.toString() + " " + tempValue.toString() + ")"
);
break;
}
if (rotation % 2 === 1) {
if (box.left !== box.top) {
tempValue = box.left;
box.left = box.top;
box.top = tempValue;
}
if (box.width !== box.height) {
tempValue = box.width;
box.width = box.height;
box.height = tempValue;
}
}
if (transformations.length) {
body = wrapSVGContent(
body,
'<g transform="' + transformations.join(" ") + '">',
"</g>"
);
}
});
const customisationsWidth = fullCustomisations.width;
const customisationsHeight = fullCustomisations.height;
const boxWidth = box.width;
const boxHeight = box.height;
let width;
let height;
if (customisationsWidth === null) {
height = customisationsHeight === null ? "1em" : customisationsHeight === "auto" ? boxHeight : customisationsHeight;
width = calculateSize(height, boxWidth / boxHeight);
} else {
width = customisationsWidth === "auto" ? boxWidth : customisationsWidth;
height = customisationsHeight === null ? calculateSize(width, boxHeight / boxWidth) : customisationsHeight === "auto" ? boxHeight : customisationsHeight;
}
const attributes = {};
const setAttr = (prop, value) => {
if (!isUnsetKeyword(value)) {
attributes[prop] = value.toString();
}
};
setAttr("width", width);
setAttr("height", height);
const viewBox = [box.left, box.top, boxWidth, boxHeight];
attributes.viewBox = viewBox.join(" ");
return {
attributes,
viewBox,
body
};
}
function encodeSVGforURL(svg) {
return svg.replace(/"/g, "'").replace(/%/g, "%25").replace(/#/g, "%23").replace(/</g, "%3C").replace(/>/g, "%3E").replace(/\s+/g, " ");
}
function encodeSvgForCss(svg) {
let useSvg = svg.startsWith("<svg>") ? svg.replace("<svg>", "<svg >") : svg;
if (!useSvg.includes(" xmlns:xlink=") && useSvg.includes(" xlink:")) {
useSvg = useSvg.replace(
"<svg ",
'<svg xmlns:xlink="http://www.w3.org/1999/xlink" '
);
}
if (!useSvg.includes(" xmlns=")) {
useSvg = useSvg.replace(
"<svg ",
'<svg xmlns="http://www.w3.org/2000/svg" '
);
}
return encodeSVGforURL(useSvg);
}
function trimSVG(str) {
return str.replace(/(['"])\s*\n\s*([^>\\/\s])/g, "$1 $2").replace(/(["';{}><])\s*\n\s*/g, "$1").replace(/\s*\n\s*/g, " ").replace(/\s+"/g, '"').replace(/="\s+/g, '="').replace(/(\s)+\/>/g, "/>").trim();
}
const svgWidthRegex = /\swidth\s*=\s*["']([\w.]+)["']/;
const svgHeightRegex = /\sheight\s*=\s*["']([\w.]+)["']/;
const svgTagRegex = /<svg\s+/;
function configureSvgSize(svg, props, scale) {
const svgNode = svg.slice(0, svg.indexOf(">"));
const check = (prop, regex) => {
const result = regex.exec(svgNode);
const isSet = result != null;
const propValue = props[prop];
if (!propValue && !isUnsetKeyword(propValue)) {
if (typeof scale === "number") {
if (scale > 0) {
props[prop] = calculateSize(
// Base on result from iconToSVG() or 1em
result?.[1] ?? "1em",
scale
);
}
} else if (result) {
props[prop] = result[1];
}
}
return isSet;
};
return [check("width", svgWidthRegex), check("height", svgHeightRegex)];
}
async function mergeIconProps(svg, collection, icon, options, propsProvider, afterCustomizations) {
const { scale, addXmlNs = false } = options ?? {};
const { additionalProps = {}, iconCustomizer } = options?.customizations ?? {};
const props = await propsProvider?.() ?? {};
await iconCustomizer?.(collection, icon, props);
Object.keys(additionalProps).forEach((p) => {
const v = additionalProps[p];
if (v !== void 0 && v !== null)
props[p] = v;
});
afterCustomizations?.(props);
const [widthOnSvg, heightOnSvg] = configureSvgSize(svg, props, scale);
if (addXmlNs) {
if (!svg.includes("xmlns=") && !props["xmlns"]) {
props["xmlns"] = "http://www.w3.org/2000/svg";
}
if (!svg.includes("xmlns:xlink=") && svg.includes("xlink:") && !props["xmlns:xlink"]) {
props["xmlns:xlink"] = "http://www.w3.org/1999/xlink";
}
}
const propsToAdd = Object.keys(props).map(
(p) => p === "width" && widthOnSvg || p === "height" && heightOnSvg ? null : `${p}="${props[p]}"`
).filter((p) => p != null);
if (propsToAdd.length) {
svg = svg.replace(svgTagRegex, `<svg ${propsToAdd.join(" ")} `);
}
if (options) {
const { defaultStyle, defaultClass } = options;
if (defaultClass && !svg.includes("class=")) {
svg = svg.replace(svgTagRegex, `<svg class="${defaultClass}" `);
}
if (defaultStyle && !svg.includes("style=")) {
svg = svg.replace(svgTagRegex, `<svg style="${defaultStyle}" `);
}
}
const usedProps = options?.usedProps;
if (usedProps) {
Object.keys(additionalProps).forEach((p) => {
const v = props[p];
if (v !== void 0 && v !== null)
usedProps[p] = v;
});
if (typeof props.width !== "undefined" && props.width !== null) {
usedProps.width = props.width;
}
if (typeof props.height !== "undefined" && props.height !== null) {
usedProps.height = props.height;
}
}
return svg;
}
async function getCustomIcon(custom, collection, icon, options) {
let result;
try {
if (typeof custom === "function") {
result = await custom(icon);
} else {
const inline = custom[icon];
result = typeof inline === "function" ? await inline() : inline;
}
} catch (err) {
console.warn(
`Failed to load custom icon "${icon}" in "${collection}":`,
err
);
return;
}
if (result) {
const cleanupIdx = result.indexOf("<svg");
if (cleanupIdx > 0)
result = result.slice(cleanupIdx);
const { transform } = options?.customizations ?? {};
result = typeof transform === "function" ? await transform(result, collection, icon) : result;
if (!result.startsWith("<svg")) {
console.warn(
`Custom icon "${icon}" in "${collection}" is not a valid SVG`
);
return result;
}
return await mergeIconProps(
options?.customizations?.trimCustomSvg === true ? trimSVG(result) : result,
collection,
icon,
options,
void 0
);
}
}
async function searchForIcon(iconSet, collection, ids, options) {
let iconData;
const { customize } = options?.customizations ?? {};
for (const id of ids) {
iconData = getIconData(iconSet, id);
if (iconData) {
let defaultCustomizations = {
...defaultIconCustomisations
};
if (typeof customize === "function") {
iconData = Object.assign({}, iconData);
defaultCustomizations = customize(
defaultCustomizations,
iconData,
`${collection}:${id}`
) ?? defaultCustomizations;
}
const {
attributes: { width, height, ...restAttributes },
body
} = iconToSVG(iconData, defaultCustomizations);
const scale = options?.scale;
return await mergeIconProps(
// DON'T remove space on <svg >
`<svg >${body}</svg>`,
collection,
id,
options,
() => {
return { ...restAttributes };
},
(props) => {
const check = (prop, defaultValue) => {
const propValue = props[prop];
let value;
if (!isUnsetKeyword(propValue)) {
if (propValue) {
return;
}
if (typeof scale === "number") {
if (scale) {
value = calculateSize(
// Base on result from iconToSVG() or 1em
defaultValue ?? "1em",
scale
);
}
} else {
value = defaultValue;
}
}
if (!value) {
delete props[prop];
} else {
props[prop] = value;
}
};
check("width", width);
check("height", height);
}
);
}
}
}
const loadIcon = async (collection, icon, options) => {
const custom = options?.customCollections?.[collection];
if (custom) {
if (typeof custom === "function") {
let result;
try {
result = await custom(icon);
} catch (err) {
console.warn(
`Failed to load custom icon "${icon}" in "${collection}":`,
err
);
return;
}
if (result) {
if (typeof result === "string") {
return await getCustomIcon(
() => result,
collection,
icon,
options
);
}
if ("icons" in result) {
const ids = [
icon,
icon.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(),
icon.replace(/([a-z])(\d+)/g, "$1-$2")
];
return await searchForIcon(
result,
collection,
ids,
options
);
}
}
} else {
return await getCustomIcon(custom, collection, icon, options);
}
}
};
function getDefaultExportFromCjs (x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}
var collections;
var hasRequiredCollections;
function requireCollections () {
if (hasRequiredCollections) return collections;
hasRequiredCollections = 1;
collections = [
"academicons",
"akar-icons",
"ant-design",
"arcticons",
"basil",
"bi",
"bitcoin-icons",
"bpmn",
"brandico",
"bubbles",
"bx",
"bxl",
"bxs",
"bytesize",
"carbon",
"catppuccin",
"cbi",
"charm",
"ci",
"cib",
"cif",
"cil",
"circle-flags",
"circum",
"clarity",
"codex",
"codicon",
"covid",
"cryptocurrency-color",
"cryptocurrency",
"cuida",
"dashicons",
"devicon-line",
"devicon-original",
"devicon-plain",
"devicon",
"duo-icons",
"ei",
"el",
"emblemicons",
"emojione-monotone",
"emojione-v1",
"emojione",
"entypo-social",
"entypo",
"eos-icons",
"ep",
"et",
"eva",
"f7",
"fa-brands",
"fa-regular",
"fa-solid",
"fa",
"fa6-brands",
"fa6-regular",
"fa6-solid",
"fad",
"famicons",
"fe",
"feather",
"file-icons",
"flag",
"flagpack",
"flat-color-icons",
"flat-ui",
"flowbite",
"fluent-color",
"fluent-emoji-flat",
"fluent-emoji-high-contrast",
"fluent-emoji",
"fluent-mdl2",
"fluent",
"fontelico",
"fontisto",
"formkit",
"foundation",
"fxemoji",
"gala",
"game-icons",
"garden",
"geo",
"gg",
"gis",
"gravity-ui",
"gridicons",
"grommet-icons",
"guidance",
"healthicons",
"heroicons-outline",
"heroicons-solid",
"heroicons",
"hugeicons",
"humbleicons",
"ic",
"icomoon-free",
"icon-park-outline",
"icon-park-solid",
"icon-park-twotone",
"icon-park",
"iconamoon",
"iconoir",
"icons8",
"il",
"ion",
"iwwa",
"ix",
"jam",
"la",
"lets-icons",
"line-md",
"lineicons",
"logos",
"ls",
"lsicon",
"lucide-lab",
"lucide",
"mage",
"majesticons",
"maki",
"map",
"marketeq",
"material-symbols-light",
"material-symbols",
"mdi-light",
"mdi",
"medical-icon",
"memory",
"meteocons",
"meteor-icons",
"mi",
"mingcute",
"mono-icons",
"mynaui",
"nimbus",
"nonicons",
"noto-v1",
"noto",
"nrk",
"octicon",
"oi",
"ooui",
"openmoji",
"oui",
"pajamas",
"pepicons-pencil",
"pepicons-pop",
"pepicons-print",
"pepicons",
"ph",
"pixelarticons",
"prime",
"proicons",
"ps",
"qlementine-icons",
"quill",
"radix-icons",
"raphael",
"ri",
"rivet-icons",
"si-glyph",
"si",
"simple-icons",
"simple-line-icons",
"skill-icons",
"solar",
"stash",
"streamline-emojis",
"streamline",
"subway",
"svg-spinners",
"system-uicons",
"tabler",
"tdesign",
"teenyicons",
"token-branded",
"token",
"topcoat",
"twemoji",
"typcn",
"uil",
"uim",
"uis",
"uit",
"uiw",
"unjs",
"vaadin",
"vs",
"vscode-icons",
"websymbol",
"weui",
"whh",
"wi",
"wpf",
"zmdi",
"zondicons"
];
return collections;
}
var collectionsExports = /*@__PURE__*/ requireCollections();
const icons = /*@__PURE__*/getDefaultExportFromCjs(collectionsExports);
const COLLECTION_NAME_PARTS_MAX = 3;
function createPresetIcons(lookupIconLoader) {
return definePreset((options = {}) => {
const {
scale = 1,
mode = "auto",
prefix = "i-",
warn = false,
iconifyCollectionsNames,
collections: customCollections,
extraProperties = {},
customizations = {},
autoInstall = false,
collectionsNodeResolvePath,
layer = "icons",
unit,
processor
} = options;
const flags = getEnvFlags();
const loaderOptions = {
addXmlNs: true,
scale,
customCollections,
autoInstall,
cwd: collectionsNodeResolvePath,
// avoid warn from @iconify/loader: we'll warn below if not found
warn: void 0,
customizations: {
...customizations,
additionalProps: { ...extraProperties },
trimCustomSvg: true,
async iconCustomizer(collection, icon, props) {
await customizations.iconCustomizer?.(collection, icon, props);
if (unit) {
if (!props.width)
props.width = `${scale}${unit}`;
if (!props.height)
props.height = `${scale}${unit}`;
}
}
}
};
let iconLoader;
return {
name: "@unocss/preset-icons",
enforce: "pre",
options,
layers: { icons: -30 },
api: {
encodeSvgForCss,
parseIconWithLoader
},
rules: [[
/^([\w:-]+)(?:\?(mask|bg|auto))?$/,
async (matcher) => {
let [full, body, _mode = mode] = matcher;
iconLoader = iconLoader || await lookupIconLoader(options);
const usedProps = {};
const parsed = await parseIconWithLoader(
body,
iconLoader,
{ ...loaderOptions, usedProps },
iconifyCollectionsNames
);
if (!parsed) {
if (warn && !flags.isESLint)
warnOnce(`failed to load icon "${full}"`);
return;
}
let cssObject;
const url = `url("data:image/svg+xml;utf8,${encodeSvgForCss(parsed.svg)}")`;
if (_mode === "auto")
_mode = parsed.svg.includes("currentColor") ? "mask" : "bg";
if (_mode === "mask") {
cssObject = {
"--un-icon": url,
"-webkit-mask": "var(--un-icon) no-repeat",
"mask": "var(--un-icon) no-repeat",
"-webkit-mask-size": "100% 100%",
"mask-size": "100% 100%",
"background-color": "currentColor",
// for Safari https://github.com/elk-zone/elk/pull/264
"color": "inherit",
...usedProps
};
} else {
cssObject = {
"background": `${url} no-repeat`,
"background-size": "100% 100%",
"background-color": "transparent",
...usedProps
};
}
processor?.(cssObject, {
...parsed,
icon: parsed.name,
mode: _mode
});
return cssObject;
},
{ layer, prefix }
]]
};
});
}
function combineLoaders(loaders) {
return async (...args) => {
for (const loader of loaders) {
if (!loader)
continue;
const result = await loader(...args);
if (result)
return result;
}
};
}
function createCDNFetchLoader(fetcher, cdnBase) {
const cache = /* @__PURE__ */ new Map();
function fetchCollection(name) {
if (!icons.includes(name))
return void 0;
if (!cache.has(name))
cache.set(name, fetcher(`${cdnBase}@iconify-json/${name}/icons.json`));
return cache.get(name);
}
return async (collection, icon, options) => {
let result = await loadIcon(collection, icon, options);
if (result)
return result;
const iconSet = await fetchCollection(collection);
if (iconSet) {
const ids = [
icon,
icon.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(),
icon.replace(/([a-z])(\d+)/g, "$1-$2")
];
result = await searchForIcon(iconSet, collection, ids, options);
}
return result;
};
}
function getEnvFlags() {
const isNode = typeof process !== "undefined" && process.stdout && !process.versions.deno;
const isVSCode = isNode && !!process.env.VSCODE_CWD;
const isESLint = isNode && !!process.env.ESLINT;
return {
isNode,
isVSCode,
isESLint
};
}
async function parseIconWithLoader(body, loader, options = {}, safeCollectionsNames = []) {
let collection = "";
let name = "";
let svg;
const allCollections = /* @__PURE__ */ new Set([
...icons,
...safeCollectionsNames,
...Object.keys(options.customCollections || {})
]);
if (body.includes(":")) {
[collection, name] = body.split(":");
if (!allCollections.has(collection))
return;
svg = await loader(collection, name, options);
} else {
const parts = body.split(/-/g);
for (let i = COLLECTION_NAME_PARTS_MAX; i >= 1; i--) {
collection = parts.slice(0, i).join("-");
if (!allCollections.has(collection))
continue;
name = parts.slice(i).join("-");
svg = await loader(collection, name, options);
if (svg)
break;
}
}
if (!svg) {
return;
}
return {
collection,
name,
svg
};
}
export { combineLoaders as a, createCDNFetchLoader as b, createPresetIcons as c, getEnvFlags as g, icons as i, loadIcon as l, parseIconWithLoader as p };
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Anthony Fu <https://github.com/antfu>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,24 @@
# @antfu/utils
[![NPM version](https://img.shields.io/npm/v/@antfu/utils?color=a1b858&label=)](https://www.npmjs.com/package/@antfu/utils)
[![Docs](https://img.shields.io/badge/docs-green)](https://www.jsdocs.io/package/@antfu/utils)
Opinionated collection of common JavaScript / TypeScript utils by [@antfu](https://github.com/antfu).
- Tree-shakable ESM
- Fully typed - with TSDocs
- Type utilities - `Arrayable<T>`, `ElementOf<T>`, etc.
> This package is designed to be used as `devDependencies` and bundled into your dist.
## Sponsors
<p align="center">
<a href="https://cdn.jsdelivr.net/gh/antfu/static/sponsors.svg">
<img src='https://cdn.jsdelivr.net/gh/antfu/static/sponsors.svg'/>
</a>
</p>
## License
[MIT](./LICENSE) License © 2021-PRESENT [Anthony Fu](https://github.com/antfu)
@@ -0,0 +1,884 @@
'use strict';
function clamp(n, min, max) {
return Math.min(max, Math.max(min, n));
}
function sum(...args) {
return flattenArrayable(args).reduce((a, b) => a + b, 0);
}
function lerp(min, max, t) {
const interpolation = clamp(t, 0, 1);
return min + (max - min) * interpolation;
}
function remap(n, inMin, inMax, outMin, outMax) {
const interpolation = (n - inMin) / (inMax - inMin);
return lerp(outMin, outMax, interpolation);
}
function toArray(array) {
array = array ?? [];
return Array.isArray(array) ? array : [array];
}
function flattenArrayable(array) {
return toArray(array).flat(1);
}
function mergeArrayable(...args) {
return args.flatMap((i) => toArray(i));
}
function partition(array, ...filters) {
const result = Array.from({ length: filters.length + 1 }).fill(null).map(() => []);
array.forEach((e, idx, arr) => {
let i = 0;
for (const filter of filters) {
if (filter(e, idx, arr)) {
result[i].push(e);
return;
}
i += 1;
}
result[i].push(e);
});
return result;
}
function uniq(array) {
return Array.from(new Set(array));
}
function uniqueBy(array, equalFn) {
return array.reduce((acc, cur) => {
const index = acc.findIndex((item) => equalFn(cur, item));
if (index === -1)
acc.push(cur);
return acc;
}, []);
}
function last(array) {
return at(array, -1);
}
function remove(array, value) {
if (!array)
return false;
const index = array.indexOf(value);
if (index >= 0) {
array.splice(index, 1);
return true;
}
return false;
}
function at(array, index) {
const len = array.length;
if (!len)
return void 0;
if (index < 0)
index += len;
return array[index];
}
function range(...args) {
let start, stop, step;
if (args.length === 1) {
start = 0;
step = 1;
[stop] = args;
} else {
[start, stop, step = 1] = args;
}
const arr = [];
let current = start;
while (current < stop) {
arr.push(current);
current += step || 1;
}
return arr;
}
function move(arr, from, to) {
arr.splice(to, 0, arr.splice(from, 1)[0]);
return arr;
}
function clampArrayRange(n, arr) {
return clamp(n, 0, arr.length - 1);
}
function sample(arr, quantity) {
return Array.from({ length: quantity }, (_) => arr[Math.round(Math.random() * (arr.length - 1))]);
}
function shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
function filterInPlace(array, predicate) {
for (let i = array.length; i--; i >= 0) {
if (!predicate(array[i], i, array))
array.splice(i, 1);
}
return array;
}
function assert(condition, message) {
if (!condition)
throw new Error(message);
}
const toString = (v) => Object.prototype.toString.call(v);
function getTypeName(v) {
if (v === null)
return "null";
const type = toString(v).slice(8, -1).toLowerCase();
return typeof v === "object" || typeof v === "function" ? type : typeof v;
}
function noop() {
}
function isDeepEqual(value1, value2) {
const type1 = getTypeName(value1);
const type2 = getTypeName(value2);
if (type1 !== type2)
return false;
if (type1 === "array") {
if (value1.length !== value2.length)
return false;
return value1.every((item, i) => {
return isDeepEqual(item, value2[i]);
});
}
if (type1 === "object") {
const keyArr = Object.keys(value1);
if (keyArr.length !== Object.keys(value2).length)
return false;
return keyArr.every((key) => {
return isDeepEqual(value1[key], value2[key]);
});
}
return Object.is(value1, value2);
}
function batchInvoke(functions) {
functions.forEach((fn) => fn && fn());
}
function invoke(fn) {
return fn();
}
function tap(value, callback) {
callback(value);
return value;
}
function notNullish(v) {
return v != null;
}
function noNull(v) {
return v !== null;
}
function notUndefined(v) {
return v !== void 0;
}
function isTruthy(v) {
return Boolean(v);
}
const isDef = (val) => typeof val !== "undefined";
const isBoolean = (val) => typeof val === "boolean";
const isFunction = (val) => typeof val === "function";
const isNumber = (val) => typeof val === "number";
const isString = (val) => typeof val === "string";
const isObject = (val) => toString(val) === "[object Object]";
const isUndefined = (val) => toString(val) === "[object Undefined]";
const isNull = (val) => toString(val) === "[object Null]";
const isRegExp = (val) => toString(val) === "[object RegExp]";
const isDate = (val) => toString(val) === "[object Date]";
const isWindow = (val) => typeof window !== "undefined" && toString(val) === "[object Window]";
const isBrowser = typeof window !== "undefined";
function objectMap(obj, fn) {
return Object.fromEntries(
Object.entries(obj).map(([k, v]) => fn(k, v)).filter(notNullish)
);
}
function isKeyOf(obj, k) {
return k in obj;
}
function objectKeys(obj) {
return Object.keys(obj);
}
function objectEntries(obj) {
return Object.entries(obj);
}
function deepMerge(target, ...sources) {
if (!sources.length)
return target;
const source = sources.shift();
if (source === void 0)
return target;
if (isMergableObject(target) && isMergableObject(source)) {
objectKeys(source).forEach((key) => {
if (key === "__proto__" || key === "constructor" || key === "prototype")
return;
if (isMergableObject(source[key])) {
if (!target[key])
target[key] = {};
if (isMergableObject(target[key])) {
deepMerge(target[key], source[key]);
} else {
target[key] = source[key];
}
} else {
target[key] = source[key];
}
});
}
return deepMerge(target, ...sources);
}
function deepMergeWithArray(target, ...sources) {
if (!sources.length)
return target;
const source = sources.shift();
if (source === void 0)
return target;
if (Array.isArray(target) && Array.isArray(source))
target.push(...source);
if (isMergableObject(target) && isMergableObject(source)) {
objectKeys(source).forEach((key) => {
if (key === "__proto__" || key === "constructor" || key === "prototype")
return;
if (Array.isArray(source[key])) {
if (!target[key])
target[key] = [];
deepMergeWithArray(target[key], source[key]);
} else if (isMergableObject(source[key])) {
if (!target[key])
target[key] = {};
deepMergeWithArray(target[key], source[key]);
} else {
target[key] = source[key];
}
});
}
return deepMergeWithArray(target, ...sources);
}
function isMergableObject(item) {
return isObject(item) && !Array.isArray(item);
}
function objectPick(obj, keys, omitUndefined = false) {
return keys.reduce((n, k) => {
if (k in obj) {
if (!omitUndefined || obj[k] !== void 0)
n[k] = obj[k];
}
return n;
}, {});
}
function clearUndefined(obj) {
Object.keys(obj).forEach((key) => obj[key] === void 0 ? delete obj[key] : {});
return obj;
}
function hasOwnProperty(obj, v) {
if (obj == null)
return false;
return Object.prototype.hasOwnProperty.call(obj, v);
}
/*
How it works:
`this.#head` is an instance of `Node` which keeps track of its current value and nests another instance of `Node` that keeps the value that comes after it. When a value is provided to `.enqueue()`, the code needs to iterate through `this.#head`, going deeper and deeper to find the last value. However, iterating through every single item is slow. This problem is solved by saving a reference to the last value as `this.#tail` so that it can reference it to add a new value.
*/
class Node {
value;
next;
constructor(value) {
this.value = value;
}
}
class Queue {
#head;
#tail;
#size;
constructor() {
this.clear();
}
enqueue(value) {
const node = new Node(value);
if (this.#head) {
this.#tail.next = node;
this.#tail = node;
} else {
this.#head = node;
this.#tail = node;
}
this.#size++;
}
dequeue() {
const current = this.#head;
if (!current) {
return;
}
this.#head = this.#head.next;
this.#size--;
return current.value;
}
peek() {
if (!this.#head) {
return;
}
return this.#head.value;
// TODO: Node.js 18.
// return this.#head?.value;
}
clear() {
this.#head = undefined;
this.#tail = undefined;
this.#size = 0;
}
get size() {
return this.#size;
}
* [Symbol.iterator]() {
let current = this.#head;
while (current) {
yield current.value;
current = current.next;
}
}
}
function pLimit(concurrency) {
validateConcurrency(concurrency);
const queue = new Queue();
let activeCount = 0;
const resumeNext = () => {
if (activeCount < concurrency && queue.size > 0) {
queue.dequeue()();
// Since `pendingCount` has been decreased by one, increase `activeCount` by one.
activeCount++;
}
};
const next = () => {
activeCount--;
resumeNext();
};
const run = async (function_, resolve, arguments_) => {
const result = (async () => function_(...arguments_))();
resolve(result);
try {
await result;
} catch {}
next();
};
const enqueue = (function_, resolve, arguments_) => {
// Queue `internalResolve` instead of the `run` function
// to preserve asynchronous context.
new Promise(internalResolve => {
queue.enqueue(internalResolve);
}).then(
run.bind(undefined, function_, resolve, arguments_),
);
(async () => {
// This function needs to wait until the next microtask before comparing
// `activeCount` to `concurrency`, because `activeCount` is updated asynchronously
// after the `internalResolve` function is dequeued and called. The comparison in the if-statement
// needs to happen asynchronously as well to get an up-to-date value for `activeCount`.
await Promise.resolve();
if (activeCount < concurrency) {
resumeNext();
}
})();
};
const generator = (function_, ...arguments_) => new Promise(resolve => {
enqueue(function_, resolve, arguments_);
});
Object.defineProperties(generator, {
activeCount: {
get: () => activeCount,
},
pendingCount: {
get: () => queue.size,
},
clearQueue: {
value() {
queue.clear();
},
},
concurrency: {
get: () => concurrency,
set(newConcurrency) {
validateConcurrency(newConcurrency);
concurrency = newConcurrency;
queueMicrotask(() => {
// eslint-disable-next-line no-unmodified-loop-condition
while (activeCount < concurrency && queue.size > 0) {
resumeNext();
}
});
},
},
});
return generator;
}
function validateConcurrency(concurrency) {
if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) {
throw new TypeError('Expected `concurrency` to be a number from 1 and up');
}
}
const VOID = Symbol("p-void");
class PInstance extends Promise {
constructor(items = [], options) {
super(() => {
});
this.items = items;
this.options = options;
this.promises = /* @__PURE__ */ new Set();
}
get promise() {
var _a;
let batch;
const items = [...Array.from(this.items), ...Array.from(this.promises)];
if ((_a = this.options) == null ? void 0 : _a.concurrency) {
const limit = pLimit(this.options.concurrency);
batch = Promise.all(items.map((p2) => limit(() => p2)));
} else {
batch = Promise.all(items);
}
return batch.then((l) => l.filter((i) => i !== VOID));
}
add(...args) {
args.forEach((i) => {
this.promises.add(i);
});
}
map(fn) {
return new PInstance(
Array.from(this.items).map(async (i, idx) => {
const v = await i;
if (v === VOID)
return VOID;
return fn(v, idx);
}),
this.options
);
}
filter(fn) {
return new PInstance(
Array.from(this.items).map(async (i, idx) => {
const v = await i;
const r = await fn(v, idx);
if (!r)
return VOID;
return v;
}),
this.options
);
}
forEach(fn) {
return this.map(fn).then();
}
reduce(fn, initialValue) {
return this.promise.then((array) => array.reduce(fn, initialValue));
}
clear() {
this.promises.clear();
}
then(fn) {
const p2 = this.promise;
if (fn)
return p2.then(fn);
else
return p2;
}
catch(fn) {
return this.promise.catch(fn);
}
finally(fn) {
return this.promise.finally(fn);
}
}
function p(items, options) {
return new PInstance(items, options);
}
function createSingletonPromise(fn) {
let _promise;
function wrapper() {
if (!_promise)
_promise = fn();
return _promise;
}
wrapper.reset = async () => {
const _prev = _promise;
_promise = void 0;
if (_prev)
await _prev;
};
return wrapper;
}
function sleep(ms, callback) {
return new Promise(
(resolve) => setTimeout(async () => {
await (callback == null ? void 0 : callback());
resolve();
}, ms)
);
}
function createPromiseLock() {
const locks = [];
return {
async run(fn) {
const p = fn();
locks.push(p);
try {
return await p;
} finally {
remove(locks, p);
}
},
async wait() {
await Promise.allSettled(locks);
},
isWaiting() {
return Boolean(locks.length);
},
clear() {
locks.length = 0;
}
};
}
function createControlledPromise() {
let resolve, reject;
const promise = new Promise((_resolve, _reject) => {
resolve = _resolve;
reject = _reject;
});
promise.resolve = resolve;
promise.reject = reject;
return promise;
}
function slash(str) {
return str.replace(/\\/g, "/");
}
function ensurePrefix(prefix, str) {
if (!str.startsWith(prefix))
return prefix + str;
return str;
}
function ensureSuffix(suffix, str) {
if (!str.endsWith(suffix))
return str + suffix;
return str;
}
function template(str, ...args) {
const [firstArg, fallback] = args;
if (isObject(firstArg)) {
const vars = firstArg;
return str.replace(/\{(\w+)\}/g, (_, key) => vars[key] || ((typeof fallback === "function" ? fallback(key) : fallback) ?? key));
} else {
return str.replace(/\{(\d+)\}/g, (_, key) => {
const index = Number(key);
if (Number.isNaN(index))
return key;
return args[index];
});
}
}
const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
function randomStr(size = 16, dict = urlAlphabet) {
let id = "";
let i = size;
const len = dict.length;
while (i--)
id += dict[Math.random() * len | 0];
return id;
}
function capitalize(str) {
return str[0].toUpperCase() + str.slice(1).toLowerCase();
}
const _reFullWs = /^\s*$/;
function unindent(str) {
const lines = (typeof str === "string" ? str : str[0]).split("\n");
const whitespaceLines = lines.map((line) => _reFullWs.test(line));
const commonIndent = lines.reduce((min, line, idx) => {
var _a;
if (whitespaceLines[idx])
return min;
const indent = (_a = line.match(/^\s*/)) == null ? void 0 : _a[0].length;
return indent === void 0 ? min : Math.min(min, indent);
}, Number.POSITIVE_INFINITY);
let emptyLinesHead = 0;
while (emptyLinesHead < lines.length && whitespaceLines[emptyLinesHead])
emptyLinesHead++;
let emptyLinesTail = 0;
while (emptyLinesTail < lines.length && whitespaceLines[lines.length - emptyLinesTail - 1])
emptyLinesTail++;
return lines.slice(emptyLinesHead, lines.length - emptyLinesTail).map((line) => line.slice(commonIndent)).join("\n");
}
const timestamp = () => +Date.now();
/* eslint-disable no-undefined,no-param-reassign,no-shadow */
/**
* Throttle execution of a function. Especially useful for rate limiting
* execution of handlers on events like resize and scroll.
*
* @param {number} delay - A zero-or-greater delay in milliseconds. For event callbacks, values around 100 or 250 (or even higher)
* are most useful.
* @param {Function} callback - A function to be executed after delay milliseconds. The `this` context and all arguments are passed through,
* as-is, to `callback` when the throttled-function is executed.
* @param {object} [options] - An object to configure options.
* @param {boolean} [options.noTrailing] - Optional, defaults to false. If noTrailing is true, callback will only execute every `delay` milliseconds
* while the throttled-function is being called. If noTrailing is false or unspecified, callback will be executed
* one final time after the last throttled-function call. (After the throttled-function has not been called for
* `delay` milliseconds, the internal counter is reset).
* @param {boolean} [options.noLeading] - Optional, defaults to false. If noLeading is false, the first throttled-function call will execute callback
* immediately. If noLeading is true, the first the callback execution will be skipped. It should be noted that
* callback will never executed if both noLeading = true and noTrailing = true.
* @param {boolean} [options.debounceMode] - If `debounceMode` is true (at begin), schedule `clear` to execute after `delay` ms. If `debounceMode` is
* false (at end), schedule `callback` to execute after `delay` ms.
*
* @returns {Function} A new, throttled, function.
*/
function throttle (delay, callback, options) {
var _ref = options || {},
_ref$noTrailing = _ref.noTrailing,
noTrailing = _ref$noTrailing === void 0 ? false : _ref$noTrailing,
_ref$noLeading = _ref.noLeading,
noLeading = _ref$noLeading === void 0 ? false : _ref$noLeading,
_ref$debounceMode = _ref.debounceMode,
debounceMode = _ref$debounceMode === void 0 ? undefined : _ref$debounceMode;
/*
* After wrapper has stopped being called, this timeout ensures that
* `callback` is executed at the proper times in `throttle` and `end`
* debounce modes.
*/
var timeoutID;
var cancelled = false; // Keep track of the last time `callback` was executed.
var lastExec = 0; // Function to clear existing timeout
function clearExistingTimeout() {
if (timeoutID) {
clearTimeout(timeoutID);
}
} // Function to cancel next exec
function cancel(options) {
var _ref2 = options || {},
_ref2$upcomingOnly = _ref2.upcomingOnly,
upcomingOnly = _ref2$upcomingOnly === void 0 ? false : _ref2$upcomingOnly;
clearExistingTimeout();
cancelled = !upcomingOnly;
}
/*
* The `wrapper` function encapsulates all of the throttling / debouncing
* functionality and when executed will limit the rate at which `callback`
* is executed.
*/
function wrapper() {
for (var _len = arguments.length, arguments_ = new Array(_len), _key = 0; _key < _len; _key++) {
arguments_[_key] = arguments[_key];
}
var self = this;
var elapsed = Date.now() - lastExec;
if (cancelled) {
return;
} // Execute `callback` and update the `lastExec` timestamp.
function exec() {
lastExec = Date.now();
callback.apply(self, arguments_);
}
/*
* If `debounceMode` is true (at begin) this is used to clear the flag
* to allow future `callback` executions.
*/
function clear() {
timeoutID = undefined;
}
if (!noLeading && debounceMode && !timeoutID) {
/*
* Since `wrapper` is being called for the first time and
* `debounceMode` is true (at begin), execute `callback`
* and noLeading != true.
*/
exec();
}
clearExistingTimeout();
if (debounceMode === undefined && elapsed > delay) {
if (noLeading) {
/*
* In throttle mode with noLeading, if `delay` time has
* been exceeded, update `lastExec` and schedule `callback`
* to execute after `delay` ms.
*/
lastExec = Date.now();
if (!noTrailing) {
timeoutID = setTimeout(debounceMode ? clear : exec, delay);
}
} else {
/*
* In throttle mode without noLeading, if `delay` time has been exceeded, execute
* `callback`.
*/
exec();
}
} else if (noTrailing !== true) {
/*
* In trailing throttle mode, since `delay` time has not been
* exceeded, schedule `callback` to execute `delay` ms after most
* recent execution.
*
* If `debounceMode` is true (at begin), schedule `clear` to execute
* after `delay` ms.
*
* If `debounceMode` is false (at end), schedule `callback` to
* execute after `delay` ms.
*/
timeoutID = setTimeout(debounceMode ? clear : exec, debounceMode === undefined ? delay - elapsed : delay);
}
}
wrapper.cancel = cancel; // Return the wrapper function.
return wrapper;
}
/* eslint-disable no-undefined */
/**
* Debounce execution of a function. Debouncing, unlike throttling,
* guarantees that a function is only executed a single time, either at the
* very beginning of a series of calls, or at the very end.
*
* @param {number} delay - A zero-or-greater delay in milliseconds. For event callbacks, values around 100 or 250 (or even higher) are most useful.
* @param {Function} callback - A function to be executed after delay milliseconds. The `this` context and all arguments are passed through, as-is,
* to `callback` when the debounced-function is executed.
* @param {object} [options] - An object to configure options.
* @param {boolean} [options.atBegin] - Optional, defaults to false. If atBegin is false or unspecified, callback will only be executed `delay` milliseconds
* after the last debounced-function call. If atBegin is true, callback will be executed only at the first debounced-function call.
* (After the throttled-function has not been called for `delay` milliseconds, the internal counter is reset).
*
* @returns {Function} A new, debounced function.
*/
function debounce (delay, callback, options) {
var _ref = options || {},
_ref$atBegin = _ref.atBegin,
atBegin = _ref$atBegin === void 0 ? false : _ref$atBegin;
return throttle(delay, callback, {
debounceMode: atBegin !== false
});
}
exports.assert = assert;
exports.at = at;
exports.batchInvoke = batchInvoke;
exports.capitalize = capitalize;
exports.clamp = clamp;
exports.clampArrayRange = clampArrayRange;
exports.clearUndefined = clearUndefined;
exports.createControlledPromise = createControlledPromise;
exports.createPromiseLock = createPromiseLock;
exports.createSingletonPromise = createSingletonPromise;
exports.debounce = debounce;
exports.deepMerge = deepMerge;
exports.deepMergeWithArray = deepMergeWithArray;
exports.ensurePrefix = ensurePrefix;
exports.ensureSuffix = ensureSuffix;
exports.filterInPlace = filterInPlace;
exports.flattenArrayable = flattenArrayable;
exports.getTypeName = getTypeName;
exports.hasOwnProperty = hasOwnProperty;
exports.invoke = invoke;
exports.isBoolean = isBoolean;
exports.isBrowser = isBrowser;
exports.isDate = isDate;
exports.isDeepEqual = isDeepEqual;
exports.isDef = isDef;
exports.isFunction = isFunction;
exports.isKeyOf = isKeyOf;
exports.isNull = isNull;
exports.isNumber = isNumber;
exports.isObject = isObject;
exports.isRegExp = isRegExp;
exports.isString = isString;
exports.isTruthy = isTruthy;
exports.isUndefined = isUndefined;
exports.isWindow = isWindow;
exports.last = last;
exports.lerp = lerp;
exports.mergeArrayable = mergeArrayable;
exports.move = move;
exports.noNull = noNull;
exports.noop = noop;
exports.notNullish = notNullish;
exports.notUndefined = notUndefined;
exports.objectEntries = objectEntries;
exports.objectKeys = objectKeys;
exports.objectMap = objectMap;
exports.objectPick = objectPick;
exports.p = p;
exports.partition = partition;
exports.randomStr = randomStr;
exports.range = range;
exports.remap = remap;
exports.remove = remove;
exports.sample = sample;
exports.shuffle = shuffle;
exports.slash = slash;
exports.sleep = sleep;
exports.sum = sum;
exports.tap = tap;
exports.template = template;
exports.throttle = throttle;
exports.timestamp = timestamp;
exports.toArray = toArray;
exports.toString = toString;
exports.unindent = unindent;
exports.uniq = uniq;
exports.uniqueBy = uniqueBy;
@@ -0,0 +1,624 @@
/**
* Promise, or maybe not
*/
type Awaitable<T> = T | PromiseLike<T>;
/**
* Null or whatever
*/
type Nullable<T> = T | null | undefined;
/**
* Array, or not yet
*/
type Arrayable<T> = T | Array<T>;
/**
* Function
*/
type Fn<T = void> = () => T;
/**
* Constructor
*/
type Constructor<T = void> = new (...args: any[]) => T;
/**
* Infers the element type of an array
*/
type ElementOf<T> = T extends (infer E)[] ? E : never;
/**
* Defines an intersection type of all union items.
*
* @param U Union of any types that will be intersected.
* @returns U items intersected
* @see https://stackoverflow.com/a/50375286/9259330
*/
type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
/**
* Infers the arguments type of a function
*/
type ArgumentsType<T> = T extends ((...args: infer A) => any) ? A : never;
type MergeInsertions<T> = T extends object ? {
[K in keyof T]: MergeInsertions<T[K]>;
} : T;
type DeepMerge<F, S> = MergeInsertions<{
[K in keyof F | keyof S]: K extends keyof S & keyof F ? DeepMerge<F[K], S[K]> : K extends keyof S ? S[K] : K extends keyof F ? F[K] : never;
}>;
/**
* Convert `Arrayable<T>` to `Array<T>`
*
* @category Array
*/
declare function toArray<T>(array?: Nullable<Arrayable<T>>): Array<T>;
/**
* Convert `Arrayable<T>` to `Array<T>` and flatten it
*
* @category Array
*/
declare function flattenArrayable<T>(array?: Nullable<Arrayable<T | Array<T>>>): Array<T>;
/**
* Use rest arguments to merge arrays
*
* @category Array
*/
declare function mergeArrayable<T>(...args: Nullable<Arrayable<T>>[]): Array<T>;
type PartitionFilter<T> = (i: T, idx: number, arr: readonly T[]) => any;
/**
* Divide an array into two parts by a filter function
*
* @category Array
* @example const [odd, even] = partition([1, 2, 3, 4], i => i % 2 != 0)
*/
declare function partition<T>(array: readonly T[], f1: PartitionFilter<T>): [T[], T[]];
declare function partition<T>(array: readonly T[], f1: PartitionFilter<T>, f2: PartitionFilter<T>): [T[], T[], T[]];
declare function partition<T>(array: readonly T[], f1: PartitionFilter<T>, f2: PartitionFilter<T>, f3: PartitionFilter<T>): [T[], T[], T[], T[]];
declare function partition<T>(array: readonly T[], f1: PartitionFilter<T>, f2: PartitionFilter<T>, f3: PartitionFilter<T>, f4: PartitionFilter<T>): [T[], T[], T[], T[], T[]];
declare function partition<T>(array: readonly T[], f1: PartitionFilter<T>, f2: PartitionFilter<T>, f3: PartitionFilter<T>, f4: PartitionFilter<T>, f5: PartitionFilter<T>): [T[], T[], T[], T[], T[], T[]];
declare function partition<T>(array: readonly T[], f1: PartitionFilter<T>, f2: PartitionFilter<T>, f3: PartitionFilter<T>, f4: PartitionFilter<T>, f5: PartitionFilter<T>, f6: PartitionFilter<T>): [T[], T[], T[], T[], T[], T[], T[]];
/**
* Unique an Array
*
* @category Array
*/
declare function uniq<T>(array: readonly T[]): T[];
/**
* Unique an Array by a custom equality function
*
* @category Array
*/
declare function uniqueBy<T>(array: readonly T[], equalFn: (a: any, b: any) => boolean): T[];
/**
* Get last item
*
* @category Array
*/
declare function last(array: readonly []): undefined;
declare function last<T>(array: readonly T[]): T;
/**
* Remove an item from Array
*
* @category Array
*/
declare function remove<T>(array: T[], value: T): boolean;
/**
* Get nth item of Array. Negative for backward
*
* @category Array
*/
declare function at(array: readonly [], index: number): undefined;
declare function at<T>(array: readonly T[], index: number): T;
/**
* Genrate a range array of numbers. The `stop` is exclusive.
*
* @category Array
*/
declare function range(stop: number): number[];
declare function range(start: number, stop: number, step?: number): number[];
/**
* Move element in an Array
*
* @category Array
* @param arr
* @param from
* @param to
*/
declare function move<T>(arr: T[], from: number, to: number): T[];
/**
* Clamp a number to the index range of an array
*
* @category Array
*/
declare function clampArrayRange(n: number, arr: readonly unknown[]): number;
/**
* Get random item(s) from an array
*
* @param arr
* @param quantity - quantity of random items which will be returned
*/
declare function sample<T>(arr: T[], quantity: number): T[];
/**
* Shuffle an array. This function mutates the array.
*
* @category Array
*/
declare function shuffle<T>(array: T[]): T[];
/**
* Filter out items from an array in place.
* This function mutates the array.
* `predicate` get through the array from the end to the start for performance.
*
* Expect this function to be faster than using `Array.prototype.filter` on large arrays.
*
* @category Array
*/
declare function filterInPlace<T>(array: T[], predicate: (item: T, index: number, arr: T[]) => unknown): T[];
declare function assert(condition: boolean, message: string): asserts condition;
declare const toString: (v: any) => string;
declare function getTypeName(v: any): string;
declare function noop(): void;
declare function isDeepEqual(value1: any, value2: any): boolean;
/**
* Call every function in an array
*/
declare function batchInvoke(functions: Nullable<Fn>[]): void;
/**
* Call the function, returning the result
*/
declare function invoke<T>(fn: () => T): T;
/**
* Pass the value through the callback, and return the value
*
* @example
* ```
* function createUser(name: string): User {
* return tap(new User, user => {
* user.name = name
* })
* }
* ```
*/
declare function tap<T>(value: T, callback: (value: T) => void): T;
/**
* Type guard to filter out null-ish values
*
* @category Guards
* @example array.filter(notNullish)
*/
declare function notNullish<T>(v: T | null | undefined): v is NonNullable<T>;
/**
* Type guard to filter out null values
*
* @category Guards
* @example array.filter(noNull)
*/
declare function noNull<T>(v: T | null): v is Exclude<T, null>;
/**
* Type guard to filter out null-ish values
*
* @category Guards
* @example array.filter(notUndefined)
*/
declare function notUndefined<T>(v: T): v is Exclude<T, undefined>;
/**
* Type guard to filter out falsy values
*
* @category Guards
* @example array.filter(isTruthy)
*/
declare function isTruthy<T>(v: T): v is NonNullable<T>;
declare const isDef: <T = any>(val?: T) => val is T;
declare const isBoolean: (val: any) => val is boolean;
declare const isFunction: <T extends Function>(val: any) => val is T;
declare const isNumber: (val: any) => val is number;
declare const isString: (val: unknown) => val is string;
declare const isObject: (val: any) => val is object;
declare const isUndefined: (val: any) => val is undefined;
declare const isNull: (val: any) => val is null;
declare const isRegExp: (val: any) => val is RegExp;
declare const isDate: (val: any) => val is Date;
declare const isWindow: (val: any) => boolean;
declare const isBrowser: boolean;
declare function clamp(n: number, min: number, max: number): number;
declare function sum(...args: number[] | number[][]): number;
/**
* Linearly interpolates between `min` and `max` based on `t`
*
* @category Math
* @param min The minimum value
* @param max The maximum value
* @param t The interpolation value clamped between 0 and 1
* @example
* ```
* const value = lerp(0, 2, 0.5) // value will be 1
* ```
*/
declare function lerp(min: number, max: number, t: number): number;
/**
* Linearly remaps a clamped value from its source range [`inMin`, `inMax`] to the destination range [`outMin`, `outMax`]
*
* @category Math
* @example
* ```
* const value = remap(0.5, 0, 1, 200, 400) // value will be 300
* ```
*/
declare function remap(n: number, inMin: number, inMax: number, outMin: number, outMax: number): number;
/**
* Map key/value pairs for an object, and construct a new one
*
*
* @category Object
*
* Transform:
* @example
* ```
* objectMap({ a: 1, b: 2 }, (k, v) => [k.toString().toUpperCase(), v.toString()])
* // { A: '1', B: '2' }
* ```
*
* Swap key/value:
* @example
* ```
* objectMap({ a: 1, b: 2 }, (k, v) => [v, k])
* // { 1: 'a', 2: 'b' }
* ```
*
* Filter keys:
* @example
* ```
* objectMap({ a: 1, b: 2 }, (k, v) => k === 'a' ? undefined : [k, v])
* // { b: 2 }
* ```
*/
declare function objectMap<K extends string, V, NK extends string | number | symbol = K, NV = V>(obj: Record<K, V>, fn: (key: K, value: V) => [NK, NV] | undefined): Record<NK, NV>;
/**
* Type guard for any key, `k`.
* Marks `k` as a key of `T` if `k` is in `obj`.
*
* @category Object
* @param obj object to query for key `k`
* @param k key to check existence in `obj`
*/
declare function isKeyOf<T extends object>(obj: T, k: keyof any): k is keyof T;
/**
* Strict typed `Object.keys`
*
* @category Object
*/
declare function objectKeys<T extends object>(obj: T): Array<`${keyof T & (string | number | boolean | null | undefined)}`>;
/**
* Strict typed `Object.entries`
*
* @category Object
*/
declare function objectEntries<T extends object>(obj: T): Array<[keyof T, T[keyof T]]>;
/**
* Deep merge
*
* The first argument is the target object, the rest are the sources.
* The target object will be mutated and returned.
*
* @category Object
*/
declare function deepMerge<T extends object = object, S extends object = T>(target: T, ...sources: S[]): DeepMerge<T, S>;
/**
* Deep merge
*
* Differs from `deepMerge` in that it merges arrays instead of overriding them.
*
* The first argument is the target object, the rest are the sources.
* The target object will be mutated and returned.
*
* @category Object
*/
declare function deepMergeWithArray<T extends object = object, S extends object = T>(target: T, ...sources: S[]): DeepMerge<T, S>;
/**
* Create a new subset object by giving keys
*
* @category Object
*/
declare function objectPick<O extends object, T extends keyof O>(obj: O, keys: T[], omitUndefined?: boolean): Pick<O, T>;
/**
* Clear undefined fields from an object. It mutates the object
*
* @category Object
*/
declare function clearUndefined<T extends object>(obj: T): T;
/**
* Determines whether an object has a property with the specified name
*
* @see https://eslint.org/docs/rules/no-prototype-builtins
* @category Object
*/
declare function hasOwnProperty<T>(obj: T, v: PropertyKey): boolean;
interface POptions {
/**
* How many promises are resolved at the same time.
*/
concurrency?: number | undefined;
}
declare class PInstance<T = any> extends Promise<Awaited<T>[]> {
items: Iterable<T>;
options?: POptions | undefined;
private promises;
get promise(): Promise<Awaited<T>[]>;
constructor(items?: Iterable<T>, options?: POptions | undefined);
add(...args: (T | Promise<T>)[]): void;
map<U>(fn: (value: Awaited<T>, index: number) => U): PInstance<Promise<U>>;
filter(fn: (value: Awaited<T>, index: number) => boolean | Promise<boolean>): PInstance<Promise<T>>;
forEach(fn: (value: Awaited<T>, index: number) => void): Promise<void>;
reduce<U>(fn: (previousValue: U, currentValue: Awaited<T>, currentIndex: number, array: Awaited<T>[]) => U, initialValue: U): Promise<U>;
clear(): void;
then(fn?: () => PromiseLike<any>): Promise<any>;
catch(fn?: (err: unknown) => PromiseLike<any>): Promise<any>;
finally(fn?: () => void): Promise<Awaited<T>[]>;
}
/**
* Utility for managing multiple promises.
*
* @see https://github.com/antfu/utils/tree/main/docs/p.md
* @category Promise
* @example
* ```
* import { p } from '@antfu/utils'
*
* const items = [1, 2, 3, 4, 5]
*
* await p(items)
* .map(async i => await multiply(i, 3))
* .filter(async i => await isEven(i))
* // [6, 12]
* ```
*/
declare function p<T = any>(items?: Iterable<T>, options?: POptions): PInstance<T>;
interface SingletonPromiseReturn<T> {
(): Promise<T>;
/**
* Reset current staled promise.
* Await it to have proper shutdown.
*/
reset: () => Promise<void>;
}
/**
* Create singleton promise function
*
* @category Promise
*/
declare function createSingletonPromise<T>(fn: () => Promise<T>): SingletonPromiseReturn<T>;
/**
* Promised `setTimeout`
*
* @category Promise
*/
declare function sleep(ms: number, callback?: Fn<any>): Promise<void>;
/**
* Create a promise lock
*
* @category Promise
* @example
* ```
* const lock = createPromiseLock()
*
* lock.run(async () => {
* await doSomething()
* })
*
* // in anther context:
* await lock.wait() // it will wait all tasking finished
* ```
*/
declare function createPromiseLock(): {
run<T = void>(fn: () => Promise<T>): Promise<T>;
wait(): Promise<void>;
isWaiting(): boolean;
clear(): void;
};
/**
* Promise with `resolve` and `reject` methods of itself
*/
interface ControlledPromise<T = void> extends Promise<T> {
resolve: (value: T | PromiseLike<T>) => void;
reject: (reason?: any) => void;
}
/**
* Return a Promise with `resolve` and `reject` methods
*
* @category Promise
* @example
* ```
* const promise = createControlledPromise()
*
* await promise
*
* // in anther context:
* promise.resolve(data)
* ```
*/
declare function createControlledPromise<T>(): ControlledPromise<T>;
/**
* Replace backslash to slash
*
* @category String
*/
declare function slash(str: string): string;
/**
* Ensure prefix of a string
*
* @category String
*/
declare function ensurePrefix(prefix: string, str: string): string;
/**
* Ensure suffix of a string
*
* @category String
*/
declare function ensureSuffix(suffix: string, str: string): string;
/**
* Dead simple template engine, just like Python's `.format()`
* Support passing variables as either in index based or object/name based approach
* While using object/name based approach, you can pass a fallback value as the third argument
*
* @category String
* @example
* ```
* const result = template(
* 'Hello {0}! My name is {1}.',
* 'Inès',
* 'Anthony'
* ) // Hello Inès! My name is Anthony.
* ```
*
* ```
* const result = namedTemplate(
* '{greet}! My name is {name}.',
* { greet: 'Hello', name: 'Anthony' }
* ) // Hello! My name is Anthony.
* ```
*
* const result = namedTemplate(
* '{greet}! My name is {name}.',
* { greet: 'Hello' }, // name isn't passed hence fallback will be used for name
* 'placeholder'
* ) // Hello! My name is placeholder.
* ```
*/
declare function template(str: string, object: Record<string | number, any>, fallback?: string | ((key: string) => string)): string;
declare function template(str: string, ...args: (string | number | bigint | undefined | null)[]): string;
/**
* Generate a random string
* @category String
*/
declare function randomStr(size?: number, dict?: string): string;
/**
* First letter uppercase, other lowercase
* @category string
* @example
* ```
* capitalize('hello') => 'Hello'
* ```
*/
declare function capitalize(str: string): string;
/**
* Remove common leading whitespace from a template string.
* Will also remove empty lines at the beginning and end.
* @category string
* @example
* ```ts
* const str = unindent`
* if (a) {
* b()
* }
* `
*/
declare function unindent(str: TemplateStringsArray | string): string;
declare const timestamp: () => number;
interface CancelOptions {
upcomingOnly?: boolean;
}
interface Cancel {
cancel: (options?: CancelOptions) => void;
}
interface NoReturn<T extends (...args: any[]) => any> {
(...args: Parameters<T>): void;
}
interface ThrottleOptions {
noTrailing?: boolean;
noLeading?: boolean;
debounceMode?: boolean;
}
interface DebounceOptions {
atBegin?: boolean;
}
type throttle<T extends (...args: any[]) => any> = NoReturn<T> & Cancel;
/**
* Throttle execution of a function. Especially useful for rate limiting
* execution of handlers on events like resize and scroll.
*
* @param delay
* A zero-or-greater delay in milliseconds. For event callbacks, values around
* 100 or 250 (or even higher) are most useful.
*
* @param callback
* A function to be executed after delay milliseconds. The `this` context and
* all arguments are passed through, as-is, to `callback` when the
* throttled-function is executed.
*
* @param options
* An object to configure options.
*
* @param options.noTrailing
* Optional, defaults to false. If noTrailing is true, callback will only execute
* every `delay` milliseconds while the throttled-function is being called. If
* noTrailing is false or unspecified, callback will be executed one final time
* after the last throttled-function call. (After the throttled-function has not
* been called for `delay` milliseconds, the internal counter is reset)
*
* @param options.noLeading
* Optional, defaults to false. If noLeading is false, the first throttled-function
* call will execute callback immediately. If noLeading is true, the first the
* callback execution will be skipped. It should be noted that callback will never
* executed if both noLeading = true and noTrailing = true.
*
* @param options.debounceMode If `debounceMode` is true (at begin), schedule
* `callback` to execute after `delay` ms. If `debounceMode` is false (at end),
* schedule `callback` to execute after `delay` ms.
*
* @return
* A new, throttled, function.
*/
declare function throttle<T extends (...args: any[]) => any>(
delay: number,
callback: T,
options?: ThrottleOptions,
): throttle<T>;
type debounce<T extends (...args: any[]) => any> = NoReturn<T> & Cancel;
/**
* Debounce execution of a function. Debouncing, unlike throttling,
* guarantees that a function is only executed a single time, either at the
* very beginning of a series of calls, or at the very end.
*
* @param delay
* A zero-or-greater delay in milliseconds. For event callbacks, values around
* 100 or 250 (or even higher) are most useful.
*
* @param callback
* A function to be executed after delay milliseconds. The `this` context and
* all arguments are passed through, as-is, to `callback` when the
* debounced-function is executed.
*
* @param options
* An object to configure options.
*
* @param options.atBegin
* If atBegin is false or unspecified, callback will only be executed `delay`
* milliseconds after the last debounced-function call. If atBegin is true,
* callback will be executed only at the first debounced-function call. (After
* the throttled-function has not been called for `delay` milliseconds, the
* internal counter is reset).
*
* @return
* A new, debounced function.
*/
declare function debounce<T extends (...args: any[]) => any>(
delay: number,
callback: T,
options?: DebounceOptions,
): debounce<T>;
export { type ArgumentsType, type Arrayable, type Awaitable, type Constructor, type ControlledPromise, type DeepMerge, type ElementOf, type Fn, type MergeInsertions, type Nullable, type PartitionFilter, type SingletonPromiseReturn, type UnionToIntersection, assert, at, batchInvoke, capitalize, clamp, clampArrayRange, clearUndefined, createControlledPromise, createPromiseLock, createSingletonPromise, debounce, deepMerge, deepMergeWithArray, ensurePrefix, ensureSuffix, filterInPlace, flattenArrayable, getTypeName, hasOwnProperty, invoke, isBoolean, isBrowser, isDate, isDeepEqual, isDef, isFunction, isKeyOf, isNull, isNumber, isObject, isRegExp, isString, isTruthy, isUndefined, isWindow, last, lerp, mergeArrayable, move, noNull, noop, notNullish, notUndefined, objectEntries, objectKeys, objectMap, objectPick, p, partition, randomStr, range, remap, remove, sample, shuffle, slash, sleep, sum, tap, template, throttle, timestamp, toArray, toString, unindent, uniq, uniqueBy };
@@ -0,0 +1,624 @@
/**
* Promise, or maybe not
*/
type Awaitable<T> = T | PromiseLike<T>;
/**
* Null or whatever
*/
type Nullable<T> = T | null | undefined;
/**
* Array, or not yet
*/
type Arrayable<T> = T | Array<T>;
/**
* Function
*/
type Fn<T = void> = () => T;
/**
* Constructor
*/
type Constructor<T = void> = new (...args: any[]) => T;
/**
* Infers the element type of an array
*/
type ElementOf<T> = T extends (infer E)[] ? E : never;
/**
* Defines an intersection type of all union items.
*
* @param U Union of any types that will be intersected.
* @returns U items intersected
* @see https://stackoverflow.com/a/50375286/9259330
*/
type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
/**
* Infers the arguments type of a function
*/
type ArgumentsType<T> = T extends ((...args: infer A) => any) ? A : never;
type MergeInsertions<T> = T extends object ? {
[K in keyof T]: MergeInsertions<T[K]>;
} : T;
type DeepMerge<F, S> = MergeInsertions<{
[K in keyof F | keyof S]: K extends keyof S & keyof F ? DeepMerge<F[K], S[K]> : K extends keyof S ? S[K] : K extends keyof F ? F[K] : never;
}>;
/**
* Convert `Arrayable<T>` to `Array<T>`
*
* @category Array
*/
declare function toArray<T>(array?: Nullable<Arrayable<T>>): Array<T>;
/**
* Convert `Arrayable<T>` to `Array<T>` and flatten it
*
* @category Array
*/
declare function flattenArrayable<T>(array?: Nullable<Arrayable<T | Array<T>>>): Array<T>;
/**
* Use rest arguments to merge arrays
*
* @category Array
*/
declare function mergeArrayable<T>(...args: Nullable<Arrayable<T>>[]): Array<T>;
type PartitionFilter<T> = (i: T, idx: number, arr: readonly T[]) => any;
/**
* Divide an array into two parts by a filter function
*
* @category Array
* @example const [odd, even] = partition([1, 2, 3, 4], i => i % 2 != 0)
*/
declare function partition<T>(array: readonly T[], f1: PartitionFilter<T>): [T[], T[]];
declare function partition<T>(array: readonly T[], f1: PartitionFilter<T>, f2: PartitionFilter<T>): [T[], T[], T[]];
declare function partition<T>(array: readonly T[], f1: PartitionFilter<T>, f2: PartitionFilter<T>, f3: PartitionFilter<T>): [T[], T[], T[], T[]];
declare function partition<T>(array: readonly T[], f1: PartitionFilter<T>, f2: PartitionFilter<T>, f3: PartitionFilter<T>, f4: PartitionFilter<T>): [T[], T[], T[], T[], T[]];
declare function partition<T>(array: readonly T[], f1: PartitionFilter<T>, f2: PartitionFilter<T>, f3: PartitionFilter<T>, f4: PartitionFilter<T>, f5: PartitionFilter<T>): [T[], T[], T[], T[], T[], T[]];
declare function partition<T>(array: readonly T[], f1: PartitionFilter<T>, f2: PartitionFilter<T>, f3: PartitionFilter<T>, f4: PartitionFilter<T>, f5: PartitionFilter<T>, f6: PartitionFilter<T>): [T[], T[], T[], T[], T[], T[], T[]];
/**
* Unique an Array
*
* @category Array
*/
declare function uniq<T>(array: readonly T[]): T[];
/**
* Unique an Array by a custom equality function
*
* @category Array
*/
declare function uniqueBy<T>(array: readonly T[], equalFn: (a: any, b: any) => boolean): T[];
/**
* Get last item
*
* @category Array
*/
declare function last(array: readonly []): undefined;
declare function last<T>(array: readonly T[]): T;
/**
* Remove an item from Array
*
* @category Array
*/
declare function remove<T>(array: T[], value: T): boolean;
/**
* Get nth item of Array. Negative for backward
*
* @category Array
*/
declare function at(array: readonly [], index: number): undefined;
declare function at<T>(array: readonly T[], index: number): T;
/**
* Genrate a range array of numbers. The `stop` is exclusive.
*
* @category Array
*/
declare function range(stop: number): number[];
declare function range(start: number, stop: number, step?: number): number[];
/**
* Move element in an Array
*
* @category Array
* @param arr
* @param from
* @param to
*/
declare function move<T>(arr: T[], from: number, to: number): T[];
/**
* Clamp a number to the index range of an array
*
* @category Array
*/
declare function clampArrayRange(n: number, arr: readonly unknown[]): number;
/**
* Get random item(s) from an array
*
* @param arr
* @param quantity - quantity of random items which will be returned
*/
declare function sample<T>(arr: T[], quantity: number): T[];
/**
* Shuffle an array. This function mutates the array.
*
* @category Array
*/
declare function shuffle<T>(array: T[]): T[];
/**
* Filter out items from an array in place.
* This function mutates the array.
* `predicate` get through the array from the end to the start for performance.
*
* Expect this function to be faster than using `Array.prototype.filter` on large arrays.
*
* @category Array
*/
declare function filterInPlace<T>(array: T[], predicate: (item: T, index: number, arr: T[]) => unknown): T[];
declare function assert(condition: boolean, message: string): asserts condition;
declare const toString: (v: any) => string;
declare function getTypeName(v: any): string;
declare function noop(): void;
declare function isDeepEqual(value1: any, value2: any): boolean;
/**
* Call every function in an array
*/
declare function batchInvoke(functions: Nullable<Fn>[]): void;
/**
* Call the function, returning the result
*/
declare function invoke<T>(fn: () => T): T;
/**
* Pass the value through the callback, and return the value
*
* @example
* ```
* function createUser(name: string): User {
* return tap(new User, user => {
* user.name = name
* })
* }
* ```
*/
declare function tap<T>(value: T, callback: (value: T) => void): T;
/**
* Type guard to filter out null-ish values
*
* @category Guards
* @example array.filter(notNullish)
*/
declare function notNullish<T>(v: T | null | undefined): v is NonNullable<T>;
/**
* Type guard to filter out null values
*
* @category Guards
* @example array.filter(noNull)
*/
declare function noNull<T>(v: T | null): v is Exclude<T, null>;
/**
* Type guard to filter out null-ish values
*
* @category Guards
* @example array.filter(notUndefined)
*/
declare function notUndefined<T>(v: T): v is Exclude<T, undefined>;
/**
* Type guard to filter out falsy values
*
* @category Guards
* @example array.filter(isTruthy)
*/
declare function isTruthy<T>(v: T): v is NonNullable<T>;
declare const isDef: <T = any>(val?: T) => val is T;
declare const isBoolean: (val: any) => val is boolean;
declare const isFunction: <T extends Function>(val: any) => val is T;
declare const isNumber: (val: any) => val is number;
declare const isString: (val: unknown) => val is string;
declare const isObject: (val: any) => val is object;
declare const isUndefined: (val: any) => val is undefined;
declare const isNull: (val: any) => val is null;
declare const isRegExp: (val: any) => val is RegExp;
declare const isDate: (val: any) => val is Date;
declare const isWindow: (val: any) => boolean;
declare const isBrowser: boolean;
declare function clamp(n: number, min: number, max: number): number;
declare function sum(...args: number[] | number[][]): number;
/**
* Linearly interpolates between `min` and `max` based on `t`
*
* @category Math
* @param min The minimum value
* @param max The maximum value
* @param t The interpolation value clamped between 0 and 1
* @example
* ```
* const value = lerp(0, 2, 0.5) // value will be 1
* ```
*/
declare function lerp(min: number, max: number, t: number): number;
/**
* Linearly remaps a clamped value from its source range [`inMin`, `inMax`] to the destination range [`outMin`, `outMax`]
*
* @category Math
* @example
* ```
* const value = remap(0.5, 0, 1, 200, 400) // value will be 300
* ```
*/
declare function remap(n: number, inMin: number, inMax: number, outMin: number, outMax: number): number;
/**
* Map key/value pairs for an object, and construct a new one
*
*
* @category Object
*
* Transform:
* @example
* ```
* objectMap({ a: 1, b: 2 }, (k, v) => [k.toString().toUpperCase(), v.toString()])
* // { A: '1', B: '2' }
* ```
*
* Swap key/value:
* @example
* ```
* objectMap({ a: 1, b: 2 }, (k, v) => [v, k])
* // { 1: 'a', 2: 'b' }
* ```
*
* Filter keys:
* @example
* ```
* objectMap({ a: 1, b: 2 }, (k, v) => k === 'a' ? undefined : [k, v])
* // { b: 2 }
* ```
*/
declare function objectMap<K extends string, V, NK extends string | number | symbol = K, NV = V>(obj: Record<K, V>, fn: (key: K, value: V) => [NK, NV] | undefined): Record<NK, NV>;
/**
* Type guard for any key, `k`.
* Marks `k` as a key of `T` if `k` is in `obj`.
*
* @category Object
* @param obj object to query for key `k`
* @param k key to check existence in `obj`
*/
declare function isKeyOf<T extends object>(obj: T, k: keyof any): k is keyof T;
/**
* Strict typed `Object.keys`
*
* @category Object
*/
declare function objectKeys<T extends object>(obj: T): Array<`${keyof T & (string | number | boolean | null | undefined)}`>;
/**
* Strict typed `Object.entries`
*
* @category Object
*/
declare function objectEntries<T extends object>(obj: T): Array<[keyof T, T[keyof T]]>;
/**
* Deep merge
*
* The first argument is the target object, the rest are the sources.
* The target object will be mutated and returned.
*
* @category Object
*/
declare function deepMerge<T extends object = object, S extends object = T>(target: T, ...sources: S[]): DeepMerge<T, S>;
/**
* Deep merge
*
* Differs from `deepMerge` in that it merges arrays instead of overriding them.
*
* The first argument is the target object, the rest are the sources.
* The target object will be mutated and returned.
*
* @category Object
*/
declare function deepMergeWithArray<T extends object = object, S extends object = T>(target: T, ...sources: S[]): DeepMerge<T, S>;
/**
* Create a new subset object by giving keys
*
* @category Object
*/
declare function objectPick<O extends object, T extends keyof O>(obj: O, keys: T[], omitUndefined?: boolean): Pick<O, T>;
/**
* Clear undefined fields from an object. It mutates the object
*
* @category Object
*/
declare function clearUndefined<T extends object>(obj: T): T;
/**
* Determines whether an object has a property with the specified name
*
* @see https://eslint.org/docs/rules/no-prototype-builtins
* @category Object
*/
declare function hasOwnProperty<T>(obj: T, v: PropertyKey): boolean;
interface POptions {
/**
* How many promises are resolved at the same time.
*/
concurrency?: number | undefined;
}
declare class PInstance<T = any> extends Promise<Awaited<T>[]> {
items: Iterable<T>;
options?: POptions | undefined;
private promises;
get promise(): Promise<Awaited<T>[]>;
constructor(items?: Iterable<T>, options?: POptions | undefined);
add(...args: (T | Promise<T>)[]): void;
map<U>(fn: (value: Awaited<T>, index: number) => U): PInstance<Promise<U>>;
filter(fn: (value: Awaited<T>, index: number) => boolean | Promise<boolean>): PInstance<Promise<T>>;
forEach(fn: (value: Awaited<T>, index: number) => void): Promise<void>;
reduce<U>(fn: (previousValue: U, currentValue: Awaited<T>, currentIndex: number, array: Awaited<T>[]) => U, initialValue: U): Promise<U>;
clear(): void;
then(fn?: () => PromiseLike<any>): Promise<any>;
catch(fn?: (err: unknown) => PromiseLike<any>): Promise<any>;
finally(fn?: () => void): Promise<Awaited<T>[]>;
}
/**
* Utility for managing multiple promises.
*
* @see https://github.com/antfu/utils/tree/main/docs/p.md
* @category Promise
* @example
* ```
* import { p } from '@antfu/utils'
*
* const items = [1, 2, 3, 4, 5]
*
* await p(items)
* .map(async i => await multiply(i, 3))
* .filter(async i => await isEven(i))
* // [6, 12]
* ```
*/
declare function p<T = any>(items?: Iterable<T>, options?: POptions): PInstance<T>;
interface SingletonPromiseReturn<T> {
(): Promise<T>;
/**
* Reset current staled promise.
* Await it to have proper shutdown.
*/
reset: () => Promise<void>;
}
/**
* Create singleton promise function
*
* @category Promise
*/
declare function createSingletonPromise<T>(fn: () => Promise<T>): SingletonPromiseReturn<T>;
/**
* Promised `setTimeout`
*
* @category Promise
*/
declare function sleep(ms: number, callback?: Fn<any>): Promise<void>;
/**
* Create a promise lock
*
* @category Promise
* @example
* ```
* const lock = createPromiseLock()
*
* lock.run(async () => {
* await doSomething()
* })
*
* // in anther context:
* await lock.wait() // it will wait all tasking finished
* ```
*/
declare function createPromiseLock(): {
run<T = void>(fn: () => Promise<T>): Promise<T>;
wait(): Promise<void>;
isWaiting(): boolean;
clear(): void;
};
/**
* Promise with `resolve` and `reject` methods of itself
*/
interface ControlledPromise<T = void> extends Promise<T> {
resolve: (value: T | PromiseLike<T>) => void;
reject: (reason?: any) => void;
}
/**
* Return a Promise with `resolve` and `reject` methods
*
* @category Promise
* @example
* ```
* const promise = createControlledPromise()
*
* await promise
*
* // in anther context:
* promise.resolve(data)
* ```
*/
declare function createControlledPromise<T>(): ControlledPromise<T>;
/**
* Replace backslash to slash
*
* @category String
*/
declare function slash(str: string): string;
/**
* Ensure prefix of a string
*
* @category String
*/
declare function ensurePrefix(prefix: string, str: string): string;
/**
* Ensure suffix of a string
*
* @category String
*/
declare function ensureSuffix(suffix: string, str: string): string;
/**
* Dead simple template engine, just like Python's `.format()`
* Support passing variables as either in index based or object/name based approach
* While using object/name based approach, you can pass a fallback value as the third argument
*
* @category String
* @example
* ```
* const result = template(
* 'Hello {0}! My name is {1}.',
* 'Inès',
* 'Anthony'
* ) // Hello Inès! My name is Anthony.
* ```
*
* ```
* const result = namedTemplate(
* '{greet}! My name is {name}.',
* { greet: 'Hello', name: 'Anthony' }
* ) // Hello! My name is Anthony.
* ```
*
* const result = namedTemplate(
* '{greet}! My name is {name}.',
* { greet: 'Hello' }, // name isn't passed hence fallback will be used for name
* 'placeholder'
* ) // Hello! My name is placeholder.
* ```
*/
declare function template(str: string, object: Record<string | number, any>, fallback?: string | ((key: string) => string)): string;
declare function template(str: string, ...args: (string | number | bigint | undefined | null)[]): string;
/**
* Generate a random string
* @category String
*/
declare function randomStr(size?: number, dict?: string): string;
/**
* First letter uppercase, other lowercase
* @category string
* @example
* ```
* capitalize('hello') => 'Hello'
* ```
*/
declare function capitalize(str: string): string;
/**
* Remove common leading whitespace from a template string.
* Will also remove empty lines at the beginning and end.
* @category string
* @example
* ```ts
* const str = unindent`
* if (a) {
* b()
* }
* `
*/
declare function unindent(str: TemplateStringsArray | string): string;
declare const timestamp: () => number;
interface CancelOptions {
upcomingOnly?: boolean;
}
interface Cancel {
cancel: (options?: CancelOptions) => void;
}
interface NoReturn<T extends (...args: any[]) => any> {
(...args: Parameters<T>): void;
}
interface ThrottleOptions {
noTrailing?: boolean;
noLeading?: boolean;
debounceMode?: boolean;
}
interface DebounceOptions {
atBegin?: boolean;
}
type throttle<T extends (...args: any[]) => any> = NoReturn<T> & Cancel;
/**
* Throttle execution of a function. Especially useful for rate limiting
* execution of handlers on events like resize and scroll.
*
* @param delay
* A zero-or-greater delay in milliseconds. For event callbacks, values around
* 100 or 250 (or even higher) are most useful.
*
* @param callback
* A function to be executed after delay milliseconds. The `this` context and
* all arguments are passed through, as-is, to `callback` when the
* throttled-function is executed.
*
* @param options
* An object to configure options.
*
* @param options.noTrailing
* Optional, defaults to false. If noTrailing is true, callback will only execute
* every `delay` milliseconds while the throttled-function is being called. If
* noTrailing is false or unspecified, callback will be executed one final time
* after the last throttled-function call. (After the throttled-function has not
* been called for `delay` milliseconds, the internal counter is reset)
*
* @param options.noLeading
* Optional, defaults to false. If noLeading is false, the first throttled-function
* call will execute callback immediately. If noLeading is true, the first the
* callback execution will be skipped. It should be noted that callback will never
* executed if both noLeading = true and noTrailing = true.
*
* @param options.debounceMode If `debounceMode` is true (at begin), schedule
* `callback` to execute after `delay` ms. If `debounceMode` is false (at end),
* schedule `callback` to execute after `delay` ms.
*
* @return
* A new, throttled, function.
*/
declare function throttle<T extends (...args: any[]) => any>(
delay: number,
callback: T,
options?: ThrottleOptions,
): throttle<T>;
type debounce<T extends (...args: any[]) => any> = NoReturn<T> & Cancel;
/**
* Debounce execution of a function. Debouncing, unlike throttling,
* guarantees that a function is only executed a single time, either at the
* very beginning of a series of calls, or at the very end.
*
* @param delay
* A zero-or-greater delay in milliseconds. For event callbacks, values around
* 100 or 250 (or even higher) are most useful.
*
* @param callback
* A function to be executed after delay milliseconds. The `this` context and
* all arguments are passed through, as-is, to `callback` when the
* debounced-function is executed.
*
* @param options
* An object to configure options.
*
* @param options.atBegin
* If atBegin is false or unspecified, callback will only be executed `delay`
* milliseconds after the last debounced-function call. If atBegin is true,
* callback will be executed only at the first debounced-function call. (After
* the throttled-function has not been called for `delay` milliseconds, the
* internal counter is reset).
*
* @return
* A new, debounced function.
*/
declare function debounce<T extends (...args: any[]) => any>(
delay: number,
callback: T,
options?: DebounceOptions,
): debounce<T>;
export { type ArgumentsType, type Arrayable, type Awaitable, type Constructor, type ControlledPromise, type DeepMerge, type ElementOf, type Fn, type MergeInsertions, type Nullable, type PartitionFilter, type SingletonPromiseReturn, type UnionToIntersection, assert, at, batchInvoke, capitalize, clamp, clampArrayRange, clearUndefined, createControlledPromise, createPromiseLock, createSingletonPromise, debounce, deepMerge, deepMergeWithArray, ensurePrefix, ensureSuffix, filterInPlace, flattenArrayable, getTypeName, hasOwnProperty, invoke, isBoolean, isBrowser, isDate, isDeepEqual, isDef, isFunction, isKeyOf, isNull, isNumber, isObject, isRegExp, isString, isTruthy, isUndefined, isWindow, last, lerp, mergeArrayable, move, noNull, noop, notNullish, notUndefined, objectEntries, objectKeys, objectMap, objectPick, p, partition, randomStr, range, remap, remove, sample, shuffle, slash, sleep, sum, tap, template, throttle, timestamp, toArray, toString, unindent, uniq, uniqueBy };
@@ -0,0 +1,624 @@
/**
* Promise, or maybe not
*/
type Awaitable<T> = T | PromiseLike<T>;
/**
* Null or whatever
*/
type Nullable<T> = T | null | undefined;
/**
* Array, or not yet
*/
type Arrayable<T> = T | Array<T>;
/**
* Function
*/
type Fn<T = void> = () => T;
/**
* Constructor
*/
type Constructor<T = void> = new (...args: any[]) => T;
/**
* Infers the element type of an array
*/
type ElementOf<T> = T extends (infer E)[] ? E : never;
/**
* Defines an intersection type of all union items.
*
* @param U Union of any types that will be intersected.
* @returns U items intersected
* @see https://stackoverflow.com/a/50375286/9259330
*/
type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
/**
* Infers the arguments type of a function
*/
type ArgumentsType<T> = T extends ((...args: infer A) => any) ? A : never;
type MergeInsertions<T> = T extends object ? {
[K in keyof T]: MergeInsertions<T[K]>;
} : T;
type DeepMerge<F, S> = MergeInsertions<{
[K in keyof F | keyof S]: K extends keyof S & keyof F ? DeepMerge<F[K], S[K]> : K extends keyof S ? S[K] : K extends keyof F ? F[K] : never;
}>;
/**
* Convert `Arrayable<T>` to `Array<T>`
*
* @category Array
*/
declare function toArray<T>(array?: Nullable<Arrayable<T>>): Array<T>;
/**
* Convert `Arrayable<T>` to `Array<T>` and flatten it
*
* @category Array
*/
declare function flattenArrayable<T>(array?: Nullable<Arrayable<T | Array<T>>>): Array<T>;
/**
* Use rest arguments to merge arrays
*
* @category Array
*/
declare function mergeArrayable<T>(...args: Nullable<Arrayable<T>>[]): Array<T>;
type PartitionFilter<T> = (i: T, idx: number, arr: readonly T[]) => any;
/**
* Divide an array into two parts by a filter function
*
* @category Array
* @example const [odd, even] = partition([1, 2, 3, 4], i => i % 2 != 0)
*/
declare function partition<T>(array: readonly T[], f1: PartitionFilter<T>): [T[], T[]];
declare function partition<T>(array: readonly T[], f1: PartitionFilter<T>, f2: PartitionFilter<T>): [T[], T[], T[]];
declare function partition<T>(array: readonly T[], f1: PartitionFilter<T>, f2: PartitionFilter<T>, f3: PartitionFilter<T>): [T[], T[], T[], T[]];
declare function partition<T>(array: readonly T[], f1: PartitionFilter<T>, f2: PartitionFilter<T>, f3: PartitionFilter<T>, f4: PartitionFilter<T>): [T[], T[], T[], T[], T[]];
declare function partition<T>(array: readonly T[], f1: PartitionFilter<T>, f2: PartitionFilter<T>, f3: PartitionFilter<T>, f4: PartitionFilter<T>, f5: PartitionFilter<T>): [T[], T[], T[], T[], T[], T[]];
declare function partition<T>(array: readonly T[], f1: PartitionFilter<T>, f2: PartitionFilter<T>, f3: PartitionFilter<T>, f4: PartitionFilter<T>, f5: PartitionFilter<T>, f6: PartitionFilter<T>): [T[], T[], T[], T[], T[], T[], T[]];
/**
* Unique an Array
*
* @category Array
*/
declare function uniq<T>(array: readonly T[]): T[];
/**
* Unique an Array by a custom equality function
*
* @category Array
*/
declare function uniqueBy<T>(array: readonly T[], equalFn: (a: any, b: any) => boolean): T[];
/**
* Get last item
*
* @category Array
*/
declare function last(array: readonly []): undefined;
declare function last<T>(array: readonly T[]): T;
/**
* Remove an item from Array
*
* @category Array
*/
declare function remove<T>(array: T[], value: T): boolean;
/**
* Get nth item of Array. Negative for backward
*
* @category Array
*/
declare function at(array: readonly [], index: number): undefined;
declare function at<T>(array: readonly T[], index: number): T;
/**
* Genrate a range array of numbers. The `stop` is exclusive.
*
* @category Array
*/
declare function range(stop: number): number[];
declare function range(start: number, stop: number, step?: number): number[];
/**
* Move element in an Array
*
* @category Array
* @param arr
* @param from
* @param to
*/
declare function move<T>(arr: T[], from: number, to: number): T[];
/**
* Clamp a number to the index range of an array
*
* @category Array
*/
declare function clampArrayRange(n: number, arr: readonly unknown[]): number;
/**
* Get random item(s) from an array
*
* @param arr
* @param quantity - quantity of random items which will be returned
*/
declare function sample<T>(arr: T[], quantity: number): T[];
/**
* Shuffle an array. This function mutates the array.
*
* @category Array
*/
declare function shuffle<T>(array: T[]): T[];
/**
* Filter out items from an array in place.
* This function mutates the array.
* `predicate` get through the array from the end to the start for performance.
*
* Expect this function to be faster than using `Array.prototype.filter` on large arrays.
*
* @category Array
*/
declare function filterInPlace<T>(array: T[], predicate: (item: T, index: number, arr: T[]) => unknown): T[];
declare function assert(condition: boolean, message: string): asserts condition;
declare const toString: (v: any) => string;
declare function getTypeName(v: any): string;
declare function noop(): void;
declare function isDeepEqual(value1: any, value2: any): boolean;
/**
* Call every function in an array
*/
declare function batchInvoke(functions: Nullable<Fn>[]): void;
/**
* Call the function, returning the result
*/
declare function invoke<T>(fn: () => T): T;
/**
* Pass the value through the callback, and return the value
*
* @example
* ```
* function createUser(name: string): User {
* return tap(new User, user => {
* user.name = name
* })
* }
* ```
*/
declare function tap<T>(value: T, callback: (value: T) => void): T;
/**
* Type guard to filter out null-ish values
*
* @category Guards
* @example array.filter(notNullish)
*/
declare function notNullish<T>(v: T | null | undefined): v is NonNullable<T>;
/**
* Type guard to filter out null values
*
* @category Guards
* @example array.filter(noNull)
*/
declare function noNull<T>(v: T | null): v is Exclude<T, null>;
/**
* Type guard to filter out null-ish values
*
* @category Guards
* @example array.filter(notUndefined)
*/
declare function notUndefined<T>(v: T): v is Exclude<T, undefined>;
/**
* Type guard to filter out falsy values
*
* @category Guards
* @example array.filter(isTruthy)
*/
declare function isTruthy<T>(v: T): v is NonNullable<T>;
declare const isDef: <T = any>(val?: T) => val is T;
declare const isBoolean: (val: any) => val is boolean;
declare const isFunction: <T extends Function>(val: any) => val is T;
declare const isNumber: (val: any) => val is number;
declare const isString: (val: unknown) => val is string;
declare const isObject: (val: any) => val is object;
declare const isUndefined: (val: any) => val is undefined;
declare const isNull: (val: any) => val is null;
declare const isRegExp: (val: any) => val is RegExp;
declare const isDate: (val: any) => val is Date;
declare const isWindow: (val: any) => boolean;
declare const isBrowser: boolean;
declare function clamp(n: number, min: number, max: number): number;
declare function sum(...args: number[] | number[][]): number;
/**
* Linearly interpolates between `min` and `max` based on `t`
*
* @category Math
* @param min The minimum value
* @param max The maximum value
* @param t The interpolation value clamped between 0 and 1
* @example
* ```
* const value = lerp(0, 2, 0.5) // value will be 1
* ```
*/
declare function lerp(min: number, max: number, t: number): number;
/**
* Linearly remaps a clamped value from its source range [`inMin`, `inMax`] to the destination range [`outMin`, `outMax`]
*
* @category Math
* @example
* ```
* const value = remap(0.5, 0, 1, 200, 400) // value will be 300
* ```
*/
declare function remap(n: number, inMin: number, inMax: number, outMin: number, outMax: number): number;
/**
* Map key/value pairs for an object, and construct a new one
*
*
* @category Object
*
* Transform:
* @example
* ```
* objectMap({ a: 1, b: 2 }, (k, v) => [k.toString().toUpperCase(), v.toString()])
* // { A: '1', B: '2' }
* ```
*
* Swap key/value:
* @example
* ```
* objectMap({ a: 1, b: 2 }, (k, v) => [v, k])
* // { 1: 'a', 2: 'b' }
* ```
*
* Filter keys:
* @example
* ```
* objectMap({ a: 1, b: 2 }, (k, v) => k === 'a' ? undefined : [k, v])
* // { b: 2 }
* ```
*/
declare function objectMap<K extends string, V, NK extends string | number | symbol = K, NV = V>(obj: Record<K, V>, fn: (key: K, value: V) => [NK, NV] | undefined): Record<NK, NV>;
/**
* Type guard for any key, `k`.
* Marks `k` as a key of `T` if `k` is in `obj`.
*
* @category Object
* @param obj object to query for key `k`
* @param k key to check existence in `obj`
*/
declare function isKeyOf<T extends object>(obj: T, k: keyof any): k is keyof T;
/**
* Strict typed `Object.keys`
*
* @category Object
*/
declare function objectKeys<T extends object>(obj: T): Array<`${keyof T & (string | number | boolean | null | undefined)}`>;
/**
* Strict typed `Object.entries`
*
* @category Object
*/
declare function objectEntries<T extends object>(obj: T): Array<[keyof T, T[keyof T]]>;
/**
* Deep merge
*
* The first argument is the target object, the rest are the sources.
* The target object will be mutated and returned.
*
* @category Object
*/
declare function deepMerge<T extends object = object, S extends object = T>(target: T, ...sources: S[]): DeepMerge<T, S>;
/**
* Deep merge
*
* Differs from `deepMerge` in that it merges arrays instead of overriding them.
*
* The first argument is the target object, the rest are the sources.
* The target object will be mutated and returned.
*
* @category Object
*/
declare function deepMergeWithArray<T extends object = object, S extends object = T>(target: T, ...sources: S[]): DeepMerge<T, S>;
/**
* Create a new subset object by giving keys
*
* @category Object
*/
declare function objectPick<O extends object, T extends keyof O>(obj: O, keys: T[], omitUndefined?: boolean): Pick<O, T>;
/**
* Clear undefined fields from an object. It mutates the object
*
* @category Object
*/
declare function clearUndefined<T extends object>(obj: T): T;
/**
* Determines whether an object has a property with the specified name
*
* @see https://eslint.org/docs/rules/no-prototype-builtins
* @category Object
*/
declare function hasOwnProperty<T>(obj: T, v: PropertyKey): boolean;
interface POptions {
/**
* How many promises are resolved at the same time.
*/
concurrency?: number | undefined;
}
declare class PInstance<T = any> extends Promise<Awaited<T>[]> {
items: Iterable<T>;
options?: POptions | undefined;
private promises;
get promise(): Promise<Awaited<T>[]>;
constructor(items?: Iterable<T>, options?: POptions | undefined);
add(...args: (T | Promise<T>)[]): void;
map<U>(fn: (value: Awaited<T>, index: number) => U): PInstance<Promise<U>>;
filter(fn: (value: Awaited<T>, index: number) => boolean | Promise<boolean>): PInstance<Promise<T>>;
forEach(fn: (value: Awaited<T>, index: number) => void): Promise<void>;
reduce<U>(fn: (previousValue: U, currentValue: Awaited<T>, currentIndex: number, array: Awaited<T>[]) => U, initialValue: U): Promise<U>;
clear(): void;
then(fn?: () => PromiseLike<any>): Promise<any>;
catch(fn?: (err: unknown) => PromiseLike<any>): Promise<any>;
finally(fn?: () => void): Promise<Awaited<T>[]>;
}
/**
* Utility for managing multiple promises.
*
* @see https://github.com/antfu/utils/tree/main/docs/p.md
* @category Promise
* @example
* ```
* import { p } from '@antfu/utils'
*
* const items = [1, 2, 3, 4, 5]
*
* await p(items)
* .map(async i => await multiply(i, 3))
* .filter(async i => await isEven(i))
* // [6, 12]
* ```
*/
declare function p<T = any>(items?: Iterable<T>, options?: POptions): PInstance<T>;
interface SingletonPromiseReturn<T> {
(): Promise<T>;
/**
* Reset current staled promise.
* Await it to have proper shutdown.
*/
reset: () => Promise<void>;
}
/**
* Create singleton promise function
*
* @category Promise
*/
declare function createSingletonPromise<T>(fn: () => Promise<T>): SingletonPromiseReturn<T>;
/**
* Promised `setTimeout`
*
* @category Promise
*/
declare function sleep(ms: number, callback?: Fn<any>): Promise<void>;
/**
* Create a promise lock
*
* @category Promise
* @example
* ```
* const lock = createPromiseLock()
*
* lock.run(async () => {
* await doSomething()
* })
*
* // in anther context:
* await lock.wait() // it will wait all tasking finished
* ```
*/
declare function createPromiseLock(): {
run<T = void>(fn: () => Promise<T>): Promise<T>;
wait(): Promise<void>;
isWaiting(): boolean;
clear(): void;
};
/**
* Promise with `resolve` and `reject` methods of itself
*/
interface ControlledPromise<T = void> extends Promise<T> {
resolve: (value: T | PromiseLike<T>) => void;
reject: (reason?: any) => void;
}
/**
* Return a Promise with `resolve` and `reject` methods
*
* @category Promise
* @example
* ```
* const promise = createControlledPromise()
*
* await promise
*
* // in anther context:
* promise.resolve(data)
* ```
*/
declare function createControlledPromise<T>(): ControlledPromise<T>;
/**
* Replace backslash to slash
*
* @category String
*/
declare function slash(str: string): string;
/**
* Ensure prefix of a string
*
* @category String
*/
declare function ensurePrefix(prefix: string, str: string): string;
/**
* Ensure suffix of a string
*
* @category String
*/
declare function ensureSuffix(suffix: string, str: string): string;
/**
* Dead simple template engine, just like Python's `.format()`
* Support passing variables as either in index based or object/name based approach
* While using object/name based approach, you can pass a fallback value as the third argument
*
* @category String
* @example
* ```
* const result = template(
* 'Hello {0}! My name is {1}.',
* 'Inès',
* 'Anthony'
* ) // Hello Inès! My name is Anthony.
* ```
*
* ```
* const result = namedTemplate(
* '{greet}! My name is {name}.',
* { greet: 'Hello', name: 'Anthony' }
* ) // Hello! My name is Anthony.
* ```
*
* const result = namedTemplate(
* '{greet}! My name is {name}.',
* { greet: 'Hello' }, // name isn't passed hence fallback will be used for name
* 'placeholder'
* ) // Hello! My name is placeholder.
* ```
*/
declare function template(str: string, object: Record<string | number, any>, fallback?: string | ((key: string) => string)): string;
declare function template(str: string, ...args: (string | number | bigint | undefined | null)[]): string;
/**
* Generate a random string
* @category String
*/
declare function randomStr(size?: number, dict?: string): string;
/**
* First letter uppercase, other lowercase
* @category string
* @example
* ```
* capitalize('hello') => 'Hello'
* ```
*/
declare function capitalize(str: string): string;
/**
* Remove common leading whitespace from a template string.
* Will also remove empty lines at the beginning and end.
* @category string
* @example
* ```ts
* const str = unindent`
* if (a) {
* b()
* }
* `
*/
declare function unindent(str: TemplateStringsArray | string): string;
declare const timestamp: () => number;
interface CancelOptions {
upcomingOnly?: boolean;
}
interface Cancel {
cancel: (options?: CancelOptions) => void;
}
interface NoReturn<T extends (...args: any[]) => any> {
(...args: Parameters<T>): void;
}
interface ThrottleOptions {
noTrailing?: boolean;
noLeading?: boolean;
debounceMode?: boolean;
}
interface DebounceOptions {
atBegin?: boolean;
}
type throttle<T extends (...args: any[]) => any> = NoReturn<T> & Cancel;
/**
* Throttle execution of a function. Especially useful for rate limiting
* execution of handlers on events like resize and scroll.
*
* @param delay
* A zero-or-greater delay in milliseconds. For event callbacks, values around
* 100 or 250 (or even higher) are most useful.
*
* @param callback
* A function to be executed after delay milliseconds. The `this` context and
* all arguments are passed through, as-is, to `callback` when the
* throttled-function is executed.
*
* @param options
* An object to configure options.
*
* @param options.noTrailing
* Optional, defaults to false. If noTrailing is true, callback will only execute
* every `delay` milliseconds while the throttled-function is being called. If
* noTrailing is false or unspecified, callback will be executed one final time
* after the last throttled-function call. (After the throttled-function has not
* been called for `delay` milliseconds, the internal counter is reset)
*
* @param options.noLeading
* Optional, defaults to false. If noLeading is false, the first throttled-function
* call will execute callback immediately. If noLeading is true, the first the
* callback execution will be skipped. It should be noted that callback will never
* executed if both noLeading = true and noTrailing = true.
*
* @param options.debounceMode If `debounceMode` is true (at begin), schedule
* `callback` to execute after `delay` ms. If `debounceMode` is false (at end),
* schedule `callback` to execute after `delay` ms.
*
* @return
* A new, throttled, function.
*/
declare function throttle<T extends (...args: any[]) => any>(
delay: number,
callback: T,
options?: ThrottleOptions,
): throttle<T>;
type debounce<T extends (...args: any[]) => any> = NoReturn<T> & Cancel;
/**
* Debounce execution of a function. Debouncing, unlike throttling,
* guarantees that a function is only executed a single time, either at the
* very beginning of a series of calls, or at the very end.
*
* @param delay
* A zero-or-greater delay in milliseconds. For event callbacks, values around
* 100 or 250 (or even higher) are most useful.
*
* @param callback
* A function to be executed after delay milliseconds. The `this` context and
* all arguments are passed through, as-is, to `callback` when the
* debounced-function is executed.
*
* @param options
* An object to configure options.
*
* @param options.atBegin
* If atBegin is false or unspecified, callback will only be executed `delay`
* milliseconds after the last debounced-function call. If atBegin is true,
* callback will be executed only at the first debounced-function call. (After
* the throttled-function has not been called for `delay` milliseconds, the
* internal counter is reset).
*
* @return
* A new, debounced function.
*/
declare function debounce<T extends (...args: any[]) => any>(
delay: number,
callback: T,
options?: DebounceOptions,
): debounce<T>;
export { type ArgumentsType, type Arrayable, type Awaitable, type Constructor, type ControlledPromise, type DeepMerge, type ElementOf, type Fn, type MergeInsertions, type Nullable, type PartitionFilter, type SingletonPromiseReturn, type UnionToIntersection, assert, at, batchInvoke, capitalize, clamp, clampArrayRange, clearUndefined, createControlledPromise, createPromiseLock, createSingletonPromise, debounce, deepMerge, deepMergeWithArray, ensurePrefix, ensureSuffix, filterInPlace, flattenArrayable, getTypeName, hasOwnProperty, invoke, isBoolean, isBrowser, isDate, isDeepEqual, isDef, isFunction, isKeyOf, isNull, isNumber, isObject, isRegExp, isString, isTruthy, isUndefined, isWindow, last, lerp, mergeArrayable, move, noNull, noop, notNullish, notUndefined, objectEntries, objectKeys, objectMap, objectPick, p, partition, randomStr, range, remap, remove, sample, shuffle, slash, sleep, sum, tap, template, throttle, timestamp, toArray, toString, unindent, uniq, uniqueBy };
@@ -0,0 +1,816 @@
function clamp(n, min, max) {
return Math.min(max, Math.max(min, n));
}
function sum(...args) {
return flattenArrayable(args).reduce((a, b) => a + b, 0);
}
function lerp(min, max, t) {
const interpolation = clamp(t, 0, 1);
return min + (max - min) * interpolation;
}
function remap(n, inMin, inMax, outMin, outMax) {
const interpolation = (n - inMin) / (inMax - inMin);
return lerp(outMin, outMax, interpolation);
}
function toArray(array) {
array = array ?? [];
return Array.isArray(array) ? array : [array];
}
function flattenArrayable(array) {
return toArray(array).flat(1);
}
function mergeArrayable(...args) {
return args.flatMap((i) => toArray(i));
}
function partition(array, ...filters) {
const result = Array.from({ length: filters.length + 1 }).fill(null).map(() => []);
array.forEach((e, idx, arr) => {
let i = 0;
for (const filter of filters) {
if (filter(e, idx, arr)) {
result[i].push(e);
return;
}
i += 1;
}
result[i].push(e);
});
return result;
}
function uniq(array) {
return Array.from(new Set(array));
}
function uniqueBy(array, equalFn) {
return array.reduce((acc, cur) => {
const index = acc.findIndex((item) => equalFn(cur, item));
if (index === -1)
acc.push(cur);
return acc;
}, []);
}
function last(array) {
return at(array, -1);
}
function remove(array, value) {
if (!array)
return false;
const index = array.indexOf(value);
if (index >= 0) {
array.splice(index, 1);
return true;
}
return false;
}
function at(array, index) {
const len = array.length;
if (!len)
return void 0;
if (index < 0)
index += len;
return array[index];
}
function range(...args) {
let start, stop, step;
if (args.length === 1) {
start = 0;
step = 1;
[stop] = args;
} else {
[start, stop, step = 1] = args;
}
const arr = [];
let current = start;
while (current < stop) {
arr.push(current);
current += step || 1;
}
return arr;
}
function move(arr, from, to) {
arr.splice(to, 0, arr.splice(from, 1)[0]);
return arr;
}
function clampArrayRange(n, arr) {
return clamp(n, 0, arr.length - 1);
}
function sample(arr, quantity) {
return Array.from({ length: quantity }, (_) => arr[Math.round(Math.random() * (arr.length - 1))]);
}
function shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
function filterInPlace(array, predicate) {
for (let i = array.length; i--; i >= 0) {
if (!predicate(array[i], i, array))
array.splice(i, 1);
}
return array;
}
function assert(condition, message) {
if (!condition)
throw new Error(message);
}
const toString = (v) => Object.prototype.toString.call(v);
function getTypeName(v) {
if (v === null)
return "null";
const type = toString(v).slice(8, -1).toLowerCase();
return typeof v === "object" || typeof v === "function" ? type : typeof v;
}
function noop() {
}
function isDeepEqual(value1, value2) {
const type1 = getTypeName(value1);
const type2 = getTypeName(value2);
if (type1 !== type2)
return false;
if (type1 === "array") {
if (value1.length !== value2.length)
return false;
return value1.every((item, i) => {
return isDeepEqual(item, value2[i]);
});
}
if (type1 === "object") {
const keyArr = Object.keys(value1);
if (keyArr.length !== Object.keys(value2).length)
return false;
return keyArr.every((key) => {
return isDeepEqual(value1[key], value2[key]);
});
}
return Object.is(value1, value2);
}
function batchInvoke(functions) {
functions.forEach((fn) => fn && fn());
}
function invoke(fn) {
return fn();
}
function tap(value, callback) {
callback(value);
return value;
}
function notNullish(v) {
return v != null;
}
function noNull(v) {
return v !== null;
}
function notUndefined(v) {
return v !== void 0;
}
function isTruthy(v) {
return Boolean(v);
}
const isDef = (val) => typeof val !== "undefined";
const isBoolean = (val) => typeof val === "boolean";
const isFunction = (val) => typeof val === "function";
const isNumber = (val) => typeof val === "number";
const isString = (val) => typeof val === "string";
const isObject = (val) => toString(val) === "[object Object]";
const isUndefined = (val) => toString(val) === "[object Undefined]";
const isNull = (val) => toString(val) === "[object Null]";
const isRegExp = (val) => toString(val) === "[object RegExp]";
const isDate = (val) => toString(val) === "[object Date]";
const isWindow = (val) => typeof window !== "undefined" && toString(val) === "[object Window]";
const isBrowser = typeof window !== "undefined";
function objectMap(obj, fn) {
return Object.fromEntries(
Object.entries(obj).map(([k, v]) => fn(k, v)).filter(notNullish)
);
}
function isKeyOf(obj, k) {
return k in obj;
}
function objectKeys(obj) {
return Object.keys(obj);
}
function objectEntries(obj) {
return Object.entries(obj);
}
function deepMerge(target, ...sources) {
if (!sources.length)
return target;
const source = sources.shift();
if (source === void 0)
return target;
if (isMergableObject(target) && isMergableObject(source)) {
objectKeys(source).forEach((key) => {
if (key === "__proto__" || key === "constructor" || key === "prototype")
return;
if (isMergableObject(source[key])) {
if (!target[key])
target[key] = {};
if (isMergableObject(target[key])) {
deepMerge(target[key], source[key]);
} else {
target[key] = source[key];
}
} else {
target[key] = source[key];
}
});
}
return deepMerge(target, ...sources);
}
function deepMergeWithArray(target, ...sources) {
if (!sources.length)
return target;
const source = sources.shift();
if (source === void 0)
return target;
if (Array.isArray(target) && Array.isArray(source))
target.push(...source);
if (isMergableObject(target) && isMergableObject(source)) {
objectKeys(source).forEach((key) => {
if (key === "__proto__" || key === "constructor" || key === "prototype")
return;
if (Array.isArray(source[key])) {
if (!target[key])
target[key] = [];
deepMergeWithArray(target[key], source[key]);
} else if (isMergableObject(source[key])) {
if (!target[key])
target[key] = {};
deepMergeWithArray(target[key], source[key]);
} else {
target[key] = source[key];
}
});
}
return deepMergeWithArray(target, ...sources);
}
function isMergableObject(item) {
return isObject(item) && !Array.isArray(item);
}
function objectPick(obj, keys, omitUndefined = false) {
return keys.reduce((n, k) => {
if (k in obj) {
if (!omitUndefined || obj[k] !== void 0)
n[k] = obj[k];
}
return n;
}, {});
}
function clearUndefined(obj) {
Object.keys(obj).forEach((key) => obj[key] === void 0 ? delete obj[key] : {});
return obj;
}
function hasOwnProperty(obj, v) {
if (obj == null)
return false;
return Object.prototype.hasOwnProperty.call(obj, v);
}
/*
How it works:
`this.#head` is an instance of `Node` which keeps track of its current value and nests another instance of `Node` that keeps the value that comes after it. When a value is provided to `.enqueue()`, the code needs to iterate through `this.#head`, going deeper and deeper to find the last value. However, iterating through every single item is slow. This problem is solved by saving a reference to the last value as `this.#tail` so that it can reference it to add a new value.
*/
class Node {
value;
next;
constructor(value) {
this.value = value;
}
}
class Queue {
#head;
#tail;
#size;
constructor() {
this.clear();
}
enqueue(value) {
const node = new Node(value);
if (this.#head) {
this.#tail.next = node;
this.#tail = node;
} else {
this.#head = node;
this.#tail = node;
}
this.#size++;
}
dequeue() {
const current = this.#head;
if (!current) {
return;
}
this.#head = this.#head.next;
this.#size--;
return current.value;
}
peek() {
if (!this.#head) {
return;
}
return this.#head.value;
// TODO: Node.js 18.
// return this.#head?.value;
}
clear() {
this.#head = undefined;
this.#tail = undefined;
this.#size = 0;
}
get size() {
return this.#size;
}
* [Symbol.iterator]() {
let current = this.#head;
while (current) {
yield current.value;
current = current.next;
}
}
}
function pLimit(concurrency) {
validateConcurrency(concurrency);
const queue = new Queue();
let activeCount = 0;
const resumeNext = () => {
if (activeCount < concurrency && queue.size > 0) {
queue.dequeue()();
// Since `pendingCount` has been decreased by one, increase `activeCount` by one.
activeCount++;
}
};
const next = () => {
activeCount--;
resumeNext();
};
const run = async (function_, resolve, arguments_) => {
const result = (async () => function_(...arguments_))();
resolve(result);
try {
await result;
} catch {}
next();
};
const enqueue = (function_, resolve, arguments_) => {
// Queue `internalResolve` instead of the `run` function
// to preserve asynchronous context.
new Promise(internalResolve => {
queue.enqueue(internalResolve);
}).then(
run.bind(undefined, function_, resolve, arguments_),
);
(async () => {
// This function needs to wait until the next microtask before comparing
// `activeCount` to `concurrency`, because `activeCount` is updated asynchronously
// after the `internalResolve` function is dequeued and called. The comparison in the if-statement
// needs to happen asynchronously as well to get an up-to-date value for `activeCount`.
await Promise.resolve();
if (activeCount < concurrency) {
resumeNext();
}
})();
};
const generator = (function_, ...arguments_) => new Promise(resolve => {
enqueue(function_, resolve, arguments_);
});
Object.defineProperties(generator, {
activeCount: {
get: () => activeCount,
},
pendingCount: {
get: () => queue.size,
},
clearQueue: {
value() {
queue.clear();
},
},
concurrency: {
get: () => concurrency,
set(newConcurrency) {
validateConcurrency(newConcurrency);
concurrency = newConcurrency;
queueMicrotask(() => {
// eslint-disable-next-line no-unmodified-loop-condition
while (activeCount < concurrency && queue.size > 0) {
resumeNext();
}
});
},
},
});
return generator;
}
function validateConcurrency(concurrency) {
if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) {
throw new TypeError('Expected `concurrency` to be a number from 1 and up');
}
}
const VOID = Symbol("p-void");
class PInstance extends Promise {
constructor(items = [], options) {
super(() => {
});
this.items = items;
this.options = options;
this.promises = /* @__PURE__ */ new Set();
}
get promise() {
var _a;
let batch;
const items = [...Array.from(this.items), ...Array.from(this.promises)];
if ((_a = this.options) == null ? void 0 : _a.concurrency) {
const limit = pLimit(this.options.concurrency);
batch = Promise.all(items.map((p2) => limit(() => p2)));
} else {
batch = Promise.all(items);
}
return batch.then((l) => l.filter((i) => i !== VOID));
}
add(...args) {
args.forEach((i) => {
this.promises.add(i);
});
}
map(fn) {
return new PInstance(
Array.from(this.items).map(async (i, idx) => {
const v = await i;
if (v === VOID)
return VOID;
return fn(v, idx);
}),
this.options
);
}
filter(fn) {
return new PInstance(
Array.from(this.items).map(async (i, idx) => {
const v = await i;
const r = await fn(v, idx);
if (!r)
return VOID;
return v;
}),
this.options
);
}
forEach(fn) {
return this.map(fn).then();
}
reduce(fn, initialValue) {
return this.promise.then((array) => array.reduce(fn, initialValue));
}
clear() {
this.promises.clear();
}
then(fn) {
const p2 = this.promise;
if (fn)
return p2.then(fn);
else
return p2;
}
catch(fn) {
return this.promise.catch(fn);
}
finally(fn) {
return this.promise.finally(fn);
}
}
function p(items, options) {
return new PInstance(items, options);
}
function createSingletonPromise(fn) {
let _promise;
function wrapper() {
if (!_promise)
_promise = fn();
return _promise;
}
wrapper.reset = async () => {
const _prev = _promise;
_promise = void 0;
if (_prev)
await _prev;
};
return wrapper;
}
function sleep(ms, callback) {
return new Promise(
(resolve) => setTimeout(async () => {
await (callback == null ? void 0 : callback());
resolve();
}, ms)
);
}
function createPromiseLock() {
const locks = [];
return {
async run(fn) {
const p = fn();
locks.push(p);
try {
return await p;
} finally {
remove(locks, p);
}
},
async wait() {
await Promise.allSettled(locks);
},
isWaiting() {
return Boolean(locks.length);
},
clear() {
locks.length = 0;
}
};
}
function createControlledPromise() {
let resolve, reject;
const promise = new Promise((_resolve, _reject) => {
resolve = _resolve;
reject = _reject;
});
promise.resolve = resolve;
promise.reject = reject;
return promise;
}
function slash(str) {
return str.replace(/\\/g, "/");
}
function ensurePrefix(prefix, str) {
if (!str.startsWith(prefix))
return prefix + str;
return str;
}
function ensureSuffix(suffix, str) {
if (!str.endsWith(suffix))
return str + suffix;
return str;
}
function template(str, ...args) {
const [firstArg, fallback] = args;
if (isObject(firstArg)) {
const vars = firstArg;
return str.replace(/\{(\w+)\}/g, (_, key) => vars[key] || ((typeof fallback === "function" ? fallback(key) : fallback) ?? key));
} else {
return str.replace(/\{(\d+)\}/g, (_, key) => {
const index = Number(key);
if (Number.isNaN(index))
return key;
return args[index];
});
}
}
const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
function randomStr(size = 16, dict = urlAlphabet) {
let id = "";
let i = size;
const len = dict.length;
while (i--)
id += dict[Math.random() * len | 0];
return id;
}
function capitalize(str) {
return str[0].toUpperCase() + str.slice(1).toLowerCase();
}
const _reFullWs = /^\s*$/;
function unindent(str) {
const lines = (typeof str === "string" ? str : str[0]).split("\n");
const whitespaceLines = lines.map((line) => _reFullWs.test(line));
const commonIndent = lines.reduce((min, line, idx) => {
var _a;
if (whitespaceLines[idx])
return min;
const indent = (_a = line.match(/^\s*/)) == null ? void 0 : _a[0].length;
return indent === void 0 ? min : Math.min(min, indent);
}, Number.POSITIVE_INFINITY);
let emptyLinesHead = 0;
while (emptyLinesHead < lines.length && whitespaceLines[emptyLinesHead])
emptyLinesHead++;
let emptyLinesTail = 0;
while (emptyLinesTail < lines.length && whitespaceLines[lines.length - emptyLinesTail - 1])
emptyLinesTail++;
return lines.slice(emptyLinesHead, lines.length - emptyLinesTail).map((line) => line.slice(commonIndent)).join("\n");
}
const timestamp = () => +Date.now();
/* eslint-disable no-undefined,no-param-reassign,no-shadow */
/**
* Throttle execution of a function. Especially useful for rate limiting
* execution of handlers on events like resize and scroll.
*
* @param {number} delay - A zero-or-greater delay in milliseconds. For event callbacks, values around 100 or 250 (or even higher)
* are most useful.
* @param {Function} callback - A function to be executed after delay milliseconds. The `this` context and all arguments are passed through,
* as-is, to `callback` when the throttled-function is executed.
* @param {object} [options] - An object to configure options.
* @param {boolean} [options.noTrailing] - Optional, defaults to false. If noTrailing is true, callback will only execute every `delay` milliseconds
* while the throttled-function is being called. If noTrailing is false or unspecified, callback will be executed
* one final time after the last throttled-function call. (After the throttled-function has not been called for
* `delay` milliseconds, the internal counter is reset).
* @param {boolean} [options.noLeading] - Optional, defaults to false. If noLeading is false, the first throttled-function call will execute callback
* immediately. If noLeading is true, the first the callback execution will be skipped. It should be noted that
* callback will never executed if both noLeading = true and noTrailing = true.
* @param {boolean} [options.debounceMode] - If `debounceMode` is true (at begin), schedule `clear` to execute after `delay` ms. If `debounceMode` is
* false (at end), schedule `callback` to execute after `delay` ms.
*
* @returns {Function} A new, throttled, function.
*/
function throttle (delay, callback, options) {
var _ref = options || {},
_ref$noTrailing = _ref.noTrailing,
noTrailing = _ref$noTrailing === void 0 ? false : _ref$noTrailing,
_ref$noLeading = _ref.noLeading,
noLeading = _ref$noLeading === void 0 ? false : _ref$noLeading,
_ref$debounceMode = _ref.debounceMode,
debounceMode = _ref$debounceMode === void 0 ? undefined : _ref$debounceMode;
/*
* After wrapper has stopped being called, this timeout ensures that
* `callback` is executed at the proper times in `throttle` and `end`
* debounce modes.
*/
var timeoutID;
var cancelled = false; // Keep track of the last time `callback` was executed.
var lastExec = 0; // Function to clear existing timeout
function clearExistingTimeout() {
if (timeoutID) {
clearTimeout(timeoutID);
}
} // Function to cancel next exec
function cancel(options) {
var _ref2 = options || {},
_ref2$upcomingOnly = _ref2.upcomingOnly,
upcomingOnly = _ref2$upcomingOnly === void 0 ? false : _ref2$upcomingOnly;
clearExistingTimeout();
cancelled = !upcomingOnly;
}
/*
* The `wrapper` function encapsulates all of the throttling / debouncing
* functionality and when executed will limit the rate at which `callback`
* is executed.
*/
function wrapper() {
for (var _len = arguments.length, arguments_ = new Array(_len), _key = 0; _key < _len; _key++) {
arguments_[_key] = arguments[_key];
}
var self = this;
var elapsed = Date.now() - lastExec;
if (cancelled) {
return;
} // Execute `callback` and update the `lastExec` timestamp.
function exec() {
lastExec = Date.now();
callback.apply(self, arguments_);
}
/*
* If `debounceMode` is true (at begin) this is used to clear the flag
* to allow future `callback` executions.
*/
function clear() {
timeoutID = undefined;
}
if (!noLeading && debounceMode && !timeoutID) {
/*
* Since `wrapper` is being called for the first time and
* `debounceMode` is true (at begin), execute `callback`
* and noLeading != true.
*/
exec();
}
clearExistingTimeout();
if (debounceMode === undefined && elapsed > delay) {
if (noLeading) {
/*
* In throttle mode with noLeading, if `delay` time has
* been exceeded, update `lastExec` and schedule `callback`
* to execute after `delay` ms.
*/
lastExec = Date.now();
if (!noTrailing) {
timeoutID = setTimeout(debounceMode ? clear : exec, delay);
}
} else {
/*
* In throttle mode without noLeading, if `delay` time has been exceeded, execute
* `callback`.
*/
exec();
}
} else if (noTrailing !== true) {
/*
* In trailing throttle mode, since `delay` time has not been
* exceeded, schedule `callback` to execute `delay` ms after most
* recent execution.
*
* If `debounceMode` is true (at begin), schedule `clear` to execute
* after `delay` ms.
*
* If `debounceMode` is false (at end), schedule `callback` to
* execute after `delay` ms.
*/
timeoutID = setTimeout(debounceMode ? clear : exec, debounceMode === undefined ? delay - elapsed : delay);
}
}
wrapper.cancel = cancel; // Return the wrapper function.
return wrapper;
}
/* eslint-disable no-undefined */
/**
* Debounce execution of a function. Debouncing, unlike throttling,
* guarantees that a function is only executed a single time, either at the
* very beginning of a series of calls, or at the very end.
*
* @param {number} delay - A zero-or-greater delay in milliseconds. For event callbacks, values around 100 or 250 (or even higher) are most useful.
* @param {Function} callback - A function to be executed after delay milliseconds. The `this` context and all arguments are passed through, as-is,
* to `callback` when the debounced-function is executed.
* @param {object} [options] - An object to configure options.
* @param {boolean} [options.atBegin] - Optional, defaults to false. If atBegin is false or unspecified, callback will only be executed `delay` milliseconds
* after the last debounced-function call. If atBegin is true, callback will be executed only at the first debounced-function call.
* (After the throttled-function has not been called for `delay` milliseconds, the internal counter is reset).
*
* @returns {Function} A new, debounced function.
*/
function debounce (delay, callback, options) {
var _ref = options || {},
_ref$atBegin = _ref.atBegin,
atBegin = _ref$atBegin === void 0 ? false : _ref$atBegin;
return throttle(delay, callback, {
debounceMode: atBegin !== false
});
}
export { assert, at, batchInvoke, capitalize, clamp, clampArrayRange, clearUndefined, createControlledPromise, createPromiseLock, createSingletonPromise, debounce, deepMerge, deepMergeWithArray, ensurePrefix, ensureSuffix, filterInPlace, flattenArrayable, getTypeName, hasOwnProperty, invoke, isBoolean, isBrowser, isDate, isDeepEqual, isDef, isFunction, isKeyOf, isNull, isNumber, isObject, isRegExp, isString, isTruthy, isUndefined, isWindow, last, lerp, mergeArrayable, move, noNull, noop, notNullish, notUndefined, objectEntries, objectKeys, objectMap, objectPick, p, partition, randomStr, range, remap, remove, sample, shuffle, slash, sleep, sum, tap, template, throttle, timestamp, toArray, toString, unindent, uniq, uniqueBy };
@@ -0,0 +1,66 @@
{
"name": "@antfu/utils",
"type": "module",
"version": "8.1.1",
"packageManager": "pnpm@10.4.0",
"description": "Opinionated collection of common JavaScript / TypeScript utils by @antfu",
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
"license": "MIT",
"funding": "https://github.com/sponsors/antfu",
"homepage": "https://github.com/antfu/utils#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/antfu/utils.git"
},
"bugs": {
"url": "https://github.com/antfu/utils/issues"
},
"keywords": [
"utils"
],
"sideEffects": false,
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
},
"main": "dist/index.cjs",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
"build": "rollup -c",
"dev": "nr build --watch",
"lint": "eslint .",
"lint-fix": "nr lint --fix",
"prepublishOnly": "npm run build",
"release": "bumpp --commit --push --tag && npm publish",
"start": "tsx src/index.ts",
"typecheck": "tsc --noEmit",
"test": "vitest"
},
"devDependencies": {
"@antfu/eslint-config": "^4.2.0",
"@antfu/ni": "^23.3.1",
"@rollup/plugin-alias": "^5.1.1",
"@rollup/plugin-commonjs": "^28.0.2",
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^16.0.0",
"@types/node": "^22.13.4",
"@types/throttle-debounce": "^5.0.2",
"bumpp": "^10.0.3",
"eslint": "^9.20.1",
"p-limit": "^6.2.0",
"rollup": "^4.34.7",
"rollup-plugin-dts": "^6.1.1",
"rollup-plugin-esbuild": "^6.2.0",
"throttle-debounce": "5.0.0",
"tsx": "^4.8.0",
"typescript": "^5.7.3",
"vite": "^6.1.0",
"vitest": "^3.0.5"
}
}
@@ -0,0 +1,355 @@
'use strict';
const colors_keywords = require('./keywords.cjs');
function rgb2hsl(rgb) {
const c1 = rgb.r / 255, c2 = rgb.g / 255, c3 = rgb.b / 255, kmin = Math.min(c1, Math.min(c2, c3)), kmax = Math.max(c1, Math.max(c2, c3)), l = (kmax + kmin) / 2;
let s, h, delta;
if (kmax === kmin) {
s = h = 0;
} else {
if (l < 0.5) {
s = (kmax - kmin) / (kmax + kmin);
} else {
s = (kmax - kmin) / (2 - kmax - kmin);
}
delta = kmax - kmin;
if (kmax === c1) {
h = (c2 - c3) / delta;
} else if (kmax === c2) {
h = 2 + (c3 - c1) / delta;
} else {
h = 4 + (c1 - c2) / delta;
}
h = h * 60;
if (h < 0) {
h += 360;
}
}
return {
type: "hsl",
h,
s: s * 100,
l: l * 100,
alpha: rgb.alpha
};
}
function fromFunction(value) {
if (value.slice(-1) !== ")") {
return null;
}
const parts = value.slice(0, value.length - 1).split("(");
if (parts.length !== 2) {
return null;
}
const func = parts[0].trim();
const content = parts[1].trim();
let values;
let alphaStr;
switch (func) {
case "lch":
case "lab": {
const parts2 = content.split("/");
switch (parts2.length) {
case 2:
alphaStr = parts2[1].trim();
break;
case 1:
break;
default:
return null;
}
values = parts2[0].trim().split(/[\s,]+/);
break;
}
case "rgb":
case "rgba":
case "hsl":
case "hsla": {
values = content.trim().split(/[\s,]+/);
if (values.length === 4) {
alphaStr = values.pop().trim();
}
break;
}
default: {
return {
type: "function",
func,
value: content
};
}
}
let alpha = 1;
if (typeof alphaStr === "string") {
alpha = parseFloat(alphaStr);
const index = alphaStr.indexOf("%");
const hasPercentage = index !== -1;
if (isNaN(alpha) || hasPercentage && index !== alphaStr.length - 1) {
return null;
}
if (hasPercentage) {
alpha /= 100;
}
}
if (alpha < 0 || alpha > 1 || values.length !== 3) {
return null;
}
if (alpha === 0) {
return {
type: "transparent"
};
}
const isPercentage = [];
const numbers = [];
for (let i = 0; i < 3; i++) {
const colorStr = values[i];
const index = colorStr.indexOf("%");
const hasPercentage = index !== -1;
if (hasPercentage && index !== colorStr.length - 1) {
return null;
}
const colorNum = parseFloat(colorStr);
if (isNaN(colorNum)) {
return null;
}
isPercentage.push(hasPercentage);
numbers.push(colorNum);
}
switch (func) {
case "rgb":
case "rgba": {
const hasPercengage = isPercentage[0];
if (hasPercengage !== isPercentage[1] || hasPercengage !== isPercentage[2]) {
return null;
}
let r = numbers[0];
let g = numbers[1];
let b = numbers[2];
if (hasPercengage) {
r = r * 255 / 100;
g = g * 255 / 100;
b = b * 255 / 100;
}
return {
type: "rgb",
r,
g,
b,
alpha
};
}
case "hsl":
case "hsla": {
if (isPercentage[0] || !isPercentage[1] || !isPercentage[2]) {
return null;
}
return {
type: "hsl",
h: numbers[0],
s: numbers[1],
l: numbers[2],
alpha
};
}
case "lab":
case "lch": {
if (!isPercentage[0] || isPercentage[1] || isPercentage[2]) {
return null;
}
return func === "lab" ? {
type: "lab",
l: numbers[0],
a: numbers[1],
b: numbers[2],
alpha
} : {
type: "lch",
l: numbers[0],
c: numbers[1],
h: numbers[2],
alpha
};
}
}
return null;
}
function fromHex(value) {
if (value.slice(0, 1) === "#") {
value = value.slice(1);
}
if (!/^[\da-f]+$/i.test(value)) {
return null;
}
let alpha = 1;
const hex = ["", "", ""];
switch (value.length) {
case 4: {
alpha = parseInt(value[3] + value[3], 16) / 255;
}
case 3: {
hex[0] = value[0] + value[0];
hex[1] = value[1] + value[1];
hex[2] = value[2] + value[2];
break;
}
case 8: {
alpha = parseInt(value[6] + value[7], 16) / 255;
}
case 6: {
hex[0] = value[0] + value[1];
hex[1] = value[2] + value[3];
hex[2] = value[4] + value[5];
break;
}
default:
return null;
}
return alpha === 0 ? {
type: "transparent"
} : {
type: "rgb",
r: parseInt(hex[0], 16),
g: parseInt(hex[1], 16),
b: parseInt(hex[2], 16),
alpha
};
}
function stringToColor(value) {
value = value.toLowerCase().trim();
if (colors_keywords.colorKeywords[value]) {
return { ...colors_keywords.colorKeywords[value] };
}
if (value.indexOf("(") !== -1) {
return fromFunction(value);
}
return fromHex(value);
}
function compareColors(color1, color2) {
if (color1.type === color2.type) {
let testKeys = new Set(Object.keys(color1));
switch (color1.type) {
case "hsl": {
if (color1.s === 0) {
testKeys.delete("h");
}
if (color1.l === 0 || color1.l === 100) {
testKeys.delete("h");
testKeys.delete("s");
}
}
case "rgb":
if (color1.alpha === 0) {
testKeys = /* @__PURE__ */ new Set(["a"]);
}
}
const keys = Array.from(testKeys);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (color1[key] !== color2[key]) {
return false;
}
}
return true;
}
const list = [color1, color2].sort((a, b) => a.type.localeCompare(b.type));
const item1 = list[0];
const item2 = list[1];
switch (item1.type) {
case "hsl": {
switch (item2.type) {
case "rgb": {
return compareColors(item1, rgb2hsl(item2));
}
case "transparent":
return item1.alpha === 0;
}
return false;
}
case "rgb": {
switch (item2.type) {
case "transparent":
return item1.alpha === 0;
}
}
}
return false;
}
function colorToHexString(color, canCompact = true) {
if (color.alpha !== 1) {
return null;
}
let result = "";
const attrs = ["r", "g", "b"];
for (let i = 0; i < attrs.length; i++) {
const value = color[attrs[i]];
if (Math.round(value) !== value) {
return null;
}
const hex = value.toString(16);
result += (value < 16 ? "0" : "") + hex;
}
if (result.length !== 6) {
return null;
}
if (canCompact && result[0] === result[1] && result[2] === result[3] && result[4] === result[5]) {
result = result[0] + result[2] + result[4];
}
return "#" + result;
}
function colorToString(color) {
if (color.alpha === 0) {
return "transparent";
}
switch (color.type) {
case "none":
case "transparent":
return color.type;
case "current":
return "currentColor";
case "rgb": {
const hex = colorToHexString(color);
if (hex !== null) {
return hex;
}
const list = [color.r, color.g, color.b];
if (color.alpha !== 1) {
list.push(color.alpha);
}
return "rgb" + (list.length === 4 ? "a(" : "(") + list.join(", ") + ")";
}
case "hsl": {
const list = [
color.h,
color.s.toString() + "%",
color.l.toString() + "%"
];
if (color.alpha !== 1) {
list.push(color.alpha);
}
return "hsl" + (list.length === 4 ? "a(" : "(") + list.join(", ") + ")";
}
case "lab": {
const list = [color.l.toString() + "%", color.a, color.b];
if (color.alpha !== 1) {
list.push("/ " + color.alpha.toString());
}
return "lab(" + list.join(" ") + ")";
}
case "lch": {
const list = [color.l.toString() + "%", color.c, color.h];
if (color.alpha !== 1) {
list.push("/ " + color.alpha.toString());
}
return "lch(" + list.join(" ") + ")";
}
case "function": {
return color.func + "(" + color.value + ")";
}
}
}
exports.colorToHexString = colorToHexString;
exports.colorToString = colorToString;
exports.compareColors = compareColors;
exports.stringToColor = stringToColor;
@@ -0,0 +1,20 @@
import { Color, RGBColor } from './types.cjs';
/**
* String to color
*/
declare function stringToColor(value: string): Color | null;
/**
* Check if colors are identical
*/
declare function compareColors(color1: Color, color2: Color): boolean;
/**
* Color to hex
*/
declare function colorToHexString(color: RGBColor, canCompact?: boolean): string | null;
/**
* Convert color to string
*/
declare function colorToString(color: Color): string;
export { colorToHexString, colorToString, compareColors, stringToColor };
@@ -0,0 +1,20 @@
import { Color, RGBColor } from './types.mjs';
/**
* String to color
*/
declare function stringToColor(value: string): Color | null;
/**
* Check if colors are identical
*/
declare function compareColors(color1: Color, color2: Color): boolean;
/**
* Color to hex
*/
declare function colorToHexString(color: RGBColor, canCompact?: boolean): string | null;
/**
* Convert color to string
*/
declare function colorToString(color: Color): string;
export { colorToHexString, colorToString, compareColors, stringToColor };
@@ -0,0 +1,20 @@
import { Color, RGBColor } from './types.js';
/**
* String to color
*/
declare function stringToColor(value: string): Color | null;
/**
* Check if colors are identical
*/
declare function compareColors(color1: Color, color2: Color): boolean;
/**
* Color to hex
*/
declare function colorToHexString(color: RGBColor, canCompact?: boolean): string | null;
/**
* Convert color to string
*/
declare function colorToString(color: Color): string;
export { colorToHexString, colorToString, compareColors, stringToColor };
@@ -0,0 +1,350 @@
import { colorKeywords } from './keywords.mjs';
function rgb2hsl(rgb) {
const c1 = rgb.r / 255, c2 = rgb.g / 255, c3 = rgb.b / 255, kmin = Math.min(c1, Math.min(c2, c3)), kmax = Math.max(c1, Math.max(c2, c3)), l = (kmax + kmin) / 2;
let s, h, delta;
if (kmax === kmin) {
s = h = 0;
} else {
if (l < 0.5) {
s = (kmax - kmin) / (kmax + kmin);
} else {
s = (kmax - kmin) / (2 - kmax - kmin);
}
delta = kmax - kmin;
if (kmax === c1) {
h = (c2 - c3) / delta;
} else if (kmax === c2) {
h = 2 + (c3 - c1) / delta;
} else {
h = 4 + (c1 - c2) / delta;
}
h = h * 60;
if (h < 0) {
h += 360;
}
}
return {
type: "hsl",
h,
s: s * 100,
l: l * 100,
alpha: rgb.alpha
};
}
function fromFunction(value) {
if (value.slice(-1) !== ")") {
return null;
}
const parts = value.slice(0, value.length - 1).split("(");
if (parts.length !== 2) {
return null;
}
const func = parts[0].trim();
const content = parts[1].trim();
let values;
let alphaStr;
switch (func) {
case "lch":
case "lab": {
const parts2 = content.split("/");
switch (parts2.length) {
case 2:
alphaStr = parts2[1].trim();
break;
case 1:
break;
default:
return null;
}
values = parts2[0].trim().split(/[\s,]+/);
break;
}
case "rgb":
case "rgba":
case "hsl":
case "hsla": {
values = content.trim().split(/[\s,]+/);
if (values.length === 4) {
alphaStr = values.pop().trim();
}
break;
}
default: {
return {
type: "function",
func,
value: content
};
}
}
let alpha = 1;
if (typeof alphaStr === "string") {
alpha = parseFloat(alphaStr);
const index = alphaStr.indexOf("%");
const hasPercentage = index !== -1;
if (isNaN(alpha) || hasPercentage && index !== alphaStr.length - 1) {
return null;
}
if (hasPercentage) {
alpha /= 100;
}
}
if (alpha < 0 || alpha > 1 || values.length !== 3) {
return null;
}
if (alpha === 0) {
return {
type: "transparent"
};
}
const isPercentage = [];
const numbers = [];
for (let i = 0; i < 3; i++) {
const colorStr = values[i];
const index = colorStr.indexOf("%");
const hasPercentage = index !== -1;
if (hasPercentage && index !== colorStr.length - 1) {
return null;
}
const colorNum = parseFloat(colorStr);
if (isNaN(colorNum)) {
return null;
}
isPercentage.push(hasPercentage);
numbers.push(colorNum);
}
switch (func) {
case "rgb":
case "rgba": {
const hasPercengage = isPercentage[0];
if (hasPercengage !== isPercentage[1] || hasPercengage !== isPercentage[2]) {
return null;
}
let r = numbers[0];
let g = numbers[1];
let b = numbers[2];
if (hasPercengage) {
r = r * 255 / 100;
g = g * 255 / 100;
b = b * 255 / 100;
}
return {
type: "rgb",
r,
g,
b,
alpha
};
}
case "hsl":
case "hsla": {
if (isPercentage[0] || !isPercentage[1] || !isPercentage[2]) {
return null;
}
return {
type: "hsl",
h: numbers[0],
s: numbers[1],
l: numbers[2],
alpha
};
}
case "lab":
case "lch": {
if (!isPercentage[0] || isPercentage[1] || isPercentage[2]) {
return null;
}
return func === "lab" ? {
type: "lab",
l: numbers[0],
a: numbers[1],
b: numbers[2],
alpha
} : {
type: "lch",
l: numbers[0],
c: numbers[1],
h: numbers[2],
alpha
};
}
}
return null;
}
function fromHex(value) {
if (value.slice(0, 1) === "#") {
value = value.slice(1);
}
if (!/^[\da-f]+$/i.test(value)) {
return null;
}
let alpha = 1;
const hex = ["", "", ""];
switch (value.length) {
case 4: {
alpha = parseInt(value[3] + value[3], 16) / 255;
}
case 3: {
hex[0] = value[0] + value[0];
hex[1] = value[1] + value[1];
hex[2] = value[2] + value[2];
break;
}
case 8: {
alpha = parseInt(value[6] + value[7], 16) / 255;
}
case 6: {
hex[0] = value[0] + value[1];
hex[1] = value[2] + value[3];
hex[2] = value[4] + value[5];
break;
}
default:
return null;
}
return alpha === 0 ? {
type: "transparent"
} : {
type: "rgb",
r: parseInt(hex[0], 16),
g: parseInt(hex[1], 16),
b: parseInt(hex[2], 16),
alpha
};
}
function stringToColor(value) {
value = value.toLowerCase().trim();
if (colorKeywords[value]) {
return { ...colorKeywords[value] };
}
if (value.indexOf("(") !== -1) {
return fromFunction(value);
}
return fromHex(value);
}
function compareColors(color1, color2) {
if (color1.type === color2.type) {
let testKeys = new Set(Object.keys(color1));
switch (color1.type) {
case "hsl": {
if (color1.s === 0) {
testKeys.delete("h");
}
if (color1.l === 0 || color1.l === 100) {
testKeys.delete("h");
testKeys.delete("s");
}
}
case "rgb":
if (color1.alpha === 0) {
testKeys = /* @__PURE__ */ new Set(["a"]);
}
}
const keys = Array.from(testKeys);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (color1[key] !== color2[key]) {
return false;
}
}
return true;
}
const list = [color1, color2].sort((a, b) => a.type.localeCompare(b.type));
const item1 = list[0];
const item2 = list[1];
switch (item1.type) {
case "hsl": {
switch (item2.type) {
case "rgb": {
return compareColors(item1, rgb2hsl(item2));
}
case "transparent":
return item1.alpha === 0;
}
return false;
}
case "rgb": {
switch (item2.type) {
case "transparent":
return item1.alpha === 0;
}
}
}
return false;
}
function colorToHexString(color, canCompact = true) {
if (color.alpha !== 1) {
return null;
}
let result = "";
const attrs = ["r", "g", "b"];
for (let i = 0; i < attrs.length; i++) {
const value = color[attrs[i]];
if (Math.round(value) !== value) {
return null;
}
const hex = value.toString(16);
result += (value < 16 ? "0" : "") + hex;
}
if (result.length !== 6) {
return null;
}
if (canCompact && result[0] === result[1] && result[2] === result[3] && result[4] === result[5]) {
result = result[0] + result[2] + result[4];
}
return "#" + result;
}
function colorToString(color) {
if (color.alpha === 0) {
return "transparent";
}
switch (color.type) {
case "none":
case "transparent":
return color.type;
case "current":
return "currentColor";
case "rgb": {
const hex = colorToHexString(color);
if (hex !== null) {
return hex;
}
const list = [color.r, color.g, color.b];
if (color.alpha !== 1) {
list.push(color.alpha);
}
return "rgb" + (list.length === 4 ? "a(" : "(") + list.join(", ") + ")";
}
case "hsl": {
const list = [
color.h,
color.s.toString() + "%",
color.l.toString() + "%"
];
if (color.alpha !== 1) {
list.push(color.alpha);
}
return "hsl" + (list.length === 4 ? "a(" : "(") + list.join(", ") + ")";
}
case "lab": {
const list = [color.l.toString() + "%", color.a, color.b];
if (color.alpha !== 1) {
list.push("/ " + color.alpha.toString());
}
return "lab(" + list.join(" ") + ")";
}
case "lch": {
const list = [color.l.toString() + "%", color.c, color.h];
if (color.alpha !== 1) {
list.push("/ " + color.alpha.toString());
}
return "lch(" + list.join(" ") + ")";
}
case "function": {
return color.func + "(" + color.value + ")";
}
}
}
export { colorToHexString, colorToString, compareColors, stringToColor };
@@ -0,0 +1,190 @@
'use strict';
const colorKeywords = {
transparent: {
type: "transparent"
},
none: {
type: "none"
},
currentcolor: {
type: "current"
}
};
function add(keyword, colors) {
const type = "rgb";
const r = colors[0];
const length = colors.length;
colorKeywords[keyword] = {
type,
r,
g: length > 1 ? colors[1] : r,
b: length > 2 ? colors[2] : r,
alpha: length > 3 ? colors[3] : 1
};
}
add("silver", [192]);
add("gray", [128]);
add("white", [255]);
add("maroon", [128, 0, 0]);
add("red", [255, 0, 0]);
add("purple", [128, 0]);
add("fuchsia", [255, 0]);
add("green", [0, 128]);
add("lime", [0, 255]);
add("olive", [128, 128, 0]);
add("yellow", [255, 255, 0]);
add("navy", [0, 0, 128]);
add("blue", [0, 0, 255]);
add("teal", [0, 128, 128]);
add("aqua", [0, 255, 255]);
add("aliceblue", [240, 248, 255]);
add("antiquewhite", [250, 235, 215]);
add("aqua", [0, 255, 255]);
add("aquamarine", [127, 255, 212]);
add("azure", [240, 255, 255]);
add("beige", [245, 245, 220]);
add("bisque", [255, 228, 196]);
add("black", [0]);
add("blanchedalmond", [255, 235, 205]);
add("blue", [0, 0, 255]);
add("blueviolet", [138, 43, 226]);
add("brown", [165, 42, 42]);
add("burlywood", [222, 184, 135]);
add("cadetblue", [95, 158, 160]);
add("chartreuse", [127, 255, 0]);
add("chocolate", [210, 105, 30]);
add("coral", [255, 127, 80]);
add("cornflowerblue", [100, 149, 237]);
add("cornsilk", [255, 248, 220]);
add("crimson", [220, 20, 60]);
add("cyan", [0, 255, 255]);
add("darkblue", [0, 0, 139]);
add("darkcyan", [0, 139, 139]);
add("darkgoldenrod", [184, 134, 11]);
add("darkgray", [169]);
add("darkgreen", [0, 100]);
add("darkgrey", [169]);
add("darkkhaki", [189, 183, 107]);
add("darkmagenta", [139, 0]);
add("darkolivegreen", [85, 107, 47]);
add("darkorange", [255, 140, 0]);
add("darkorchid", [153, 50, 204]);
add("darkred", [139, 0, 0]);
add("darksalmon", [233, 150, 122]);
add("darkseagreen", [143, 188]);
add("darkslateblue", [72, 61, 139]);
add("darkslategray", [47, 79, 79]);
add("darkslategrey", [47, 79, 79]);
add("darkturquoise", [0, 206, 209]);
add("darkviolet", [148, 0, 211]);
add("deeppink", [255, 20, 147]);
add("deepskyblue", [0, 191, 255]);
add("dimgray", [105]);
add("dimgrey", [105]);
add("dodgerblue", [30, 144, 255]);
add("firebrick", [178, 34, 34]);
add("floralwhite", [255, 250, 240]);
add("forestgreen", [34, 139]);
add("fuchsia", [255, 0]);
add("gainsboro", [220]);
add("ghostwhite", [248, 248, 255]);
add("gold", [255, 215, 0]);
add("goldenrod", [218, 165, 32]);
add("gray", [128]);
add("green", [0, 128]);
add("greenyellow", [173, 255, 47]);
add("grey", [128]);
add("honeydew", [240, 255]);
add("hotpink", [255, 105, 180]);
add("indianred", [205, 92, 92]);
add("indigo", [75, 0, 130]);
add("ivory", [255, 255, 240]);
add("khaki", [240, 230, 140]);
add("lavender", [230, 230, 250]);
add("lavenderblush", [255, 240, 245]);
add("lawngreen", [124, 252, 0]);
add("lemonchiffon", [255, 250, 205]);
add("lightblue", [173, 216, 230]);
add("lightcoral", [240, 128, 128]);
add("lightcyan", [224, 255, 255]);
add("lightgoldenrodyellow", [250, 250, 210]);
add("lightgray", [211]);
add("lightgreen", [144, 238]);
add("lightgrey", [211]);
add("lightpink", [255, 182, 193]);
add("lightsalmon", [255, 160, 122]);
add("lightseagreen", [32, 178, 170]);
add("lightskyblue", [135, 206, 250]);
add("lightslategray", [119, 136, 153]);
add("lightslategrey", [119, 136, 153]);
add("lightsteelblue", [176, 196, 222]);
add("lightyellow", [255, 255, 224]);
add("lime", [0, 255]);
add("limegreen", [50, 205]);
add("linen", [250, 240, 230]);
add("magenta", [255, 0]);
add("maroon", [128, 0, 0]);
add("mediumaquamarine", [102, 205, 170]);
add("mediumblue", [0, 0, 205]);
add("mediumorchid", [186, 85, 211]);
add("mediumpurple", [147, 112, 219]);
add("mediumseagreen", [60, 179, 113]);
add("mediumslateblue", [123, 104, 238]);
add("mediumspringgreen", [0, 250, 154]);
add("mediumturquoise", [72, 209, 204]);
add("mediumvioletred", [199, 21, 133]);
add("midnightblue", [25, 25, 112]);
add("mintcream", [245, 255, 250]);
add("mistyrose", [255, 228, 225]);
add("moccasin", [255, 228, 181]);
add("navajowhite", [255, 222, 173]);
add("navy", [0, 0, 128]);
add("oldlace", [253, 245, 230]);
add("olive", [128, 128, 0]);
add("olivedrab", [107, 142, 35]);
add("orange", [255, 165, 0]);
add("orangered", [255, 69, 0]);
add("orchid", [218, 112, 214]);
add("palegoldenrod", [238, 232, 170]);
add("palegreen", [152, 251]);
add("paleturquoise", [175, 238, 238]);
add("palevioletred", [219, 112, 147]);
add("papayawhip", [255, 239, 213]);
add("peachpuff", [255, 218, 185]);
add("peru", [205, 133, 63]);
add("pink", [255, 192, 203]);
add("plum", [221, 160]);
add("powderblue", [176, 224, 230]);
add("purple", [128, 0]);
add("rebeccapurple", [102, 51, 153]);
add("red", [255, 0, 0]);
add("rosybrown", [188, 143, 143]);
add("royalblue", [65, 105, 225]);
add("saddlebrown", [139, 69, 19]);
add("salmon", [250, 128, 114]);
add("sandybrown", [244, 164, 96]);
add("seagreen", [46, 139, 87]);
add("seashell", [255, 245, 238]);
add("sienna", [160, 82, 45]);
add("silver", [192]);
add("skyblue", [135, 206, 235]);
add("slateblue", [106, 90, 205]);
add("slategray", [112, 128, 144]);
add("slategrey", [112, 128, 144]);
add("snow", [255, 250, 250]);
add("springgreen", [0, 255, 127]);
add("steelblue", [70, 130, 180]);
add("tan", [210, 180, 140]);
add("teal", [0, 128, 128]);
add("thistle", [216, 191]);
add("tomato", [255, 99, 71]);
add("turquoise", [64, 224, 208]);
add("violet", [238, 130]);
add("wheat", [245, 222, 179]);
add("white", [255]);
add("whitesmoke", [245]);
add("yellow", [255, 255, 0]);
add("yellowgreen", [154, 205, 50]);
exports.colorKeywords = colorKeywords;
@@ -0,0 +1,8 @@
import { Color } from './types.cjs';
/**
* Color keywords
*/
declare const colorKeywords: Record<string, Color>;
export { colorKeywords };
@@ -0,0 +1,8 @@
import { Color } from './types.mjs';
/**
* Color keywords
*/
declare const colorKeywords: Record<string, Color>;
export { colorKeywords };
@@ -0,0 +1,8 @@
import { Color } from './types.js';
/**
* Color keywords
*/
declare const colorKeywords: Record<string, Color>;
export { colorKeywords };
@@ -0,0 +1,188 @@
const colorKeywords = {
transparent: {
type: "transparent"
},
none: {
type: "none"
},
currentcolor: {
type: "current"
}
};
function add(keyword, colors) {
const type = "rgb";
const r = colors[0];
const length = colors.length;
colorKeywords[keyword] = {
type,
r,
g: length > 1 ? colors[1] : r,
b: length > 2 ? colors[2] : r,
alpha: length > 3 ? colors[3] : 1
};
}
add("silver", [192]);
add("gray", [128]);
add("white", [255]);
add("maroon", [128, 0, 0]);
add("red", [255, 0, 0]);
add("purple", [128, 0]);
add("fuchsia", [255, 0]);
add("green", [0, 128]);
add("lime", [0, 255]);
add("olive", [128, 128, 0]);
add("yellow", [255, 255, 0]);
add("navy", [0, 0, 128]);
add("blue", [0, 0, 255]);
add("teal", [0, 128, 128]);
add("aqua", [0, 255, 255]);
add("aliceblue", [240, 248, 255]);
add("antiquewhite", [250, 235, 215]);
add("aqua", [0, 255, 255]);
add("aquamarine", [127, 255, 212]);
add("azure", [240, 255, 255]);
add("beige", [245, 245, 220]);
add("bisque", [255, 228, 196]);
add("black", [0]);
add("blanchedalmond", [255, 235, 205]);
add("blue", [0, 0, 255]);
add("blueviolet", [138, 43, 226]);
add("brown", [165, 42, 42]);
add("burlywood", [222, 184, 135]);
add("cadetblue", [95, 158, 160]);
add("chartreuse", [127, 255, 0]);
add("chocolate", [210, 105, 30]);
add("coral", [255, 127, 80]);
add("cornflowerblue", [100, 149, 237]);
add("cornsilk", [255, 248, 220]);
add("crimson", [220, 20, 60]);
add("cyan", [0, 255, 255]);
add("darkblue", [0, 0, 139]);
add("darkcyan", [0, 139, 139]);
add("darkgoldenrod", [184, 134, 11]);
add("darkgray", [169]);
add("darkgreen", [0, 100]);
add("darkgrey", [169]);
add("darkkhaki", [189, 183, 107]);
add("darkmagenta", [139, 0]);
add("darkolivegreen", [85, 107, 47]);
add("darkorange", [255, 140, 0]);
add("darkorchid", [153, 50, 204]);
add("darkred", [139, 0, 0]);
add("darksalmon", [233, 150, 122]);
add("darkseagreen", [143, 188]);
add("darkslateblue", [72, 61, 139]);
add("darkslategray", [47, 79, 79]);
add("darkslategrey", [47, 79, 79]);
add("darkturquoise", [0, 206, 209]);
add("darkviolet", [148, 0, 211]);
add("deeppink", [255, 20, 147]);
add("deepskyblue", [0, 191, 255]);
add("dimgray", [105]);
add("dimgrey", [105]);
add("dodgerblue", [30, 144, 255]);
add("firebrick", [178, 34, 34]);
add("floralwhite", [255, 250, 240]);
add("forestgreen", [34, 139]);
add("fuchsia", [255, 0]);
add("gainsboro", [220]);
add("ghostwhite", [248, 248, 255]);
add("gold", [255, 215, 0]);
add("goldenrod", [218, 165, 32]);
add("gray", [128]);
add("green", [0, 128]);
add("greenyellow", [173, 255, 47]);
add("grey", [128]);
add("honeydew", [240, 255]);
add("hotpink", [255, 105, 180]);
add("indianred", [205, 92, 92]);
add("indigo", [75, 0, 130]);
add("ivory", [255, 255, 240]);
add("khaki", [240, 230, 140]);
add("lavender", [230, 230, 250]);
add("lavenderblush", [255, 240, 245]);
add("lawngreen", [124, 252, 0]);
add("lemonchiffon", [255, 250, 205]);
add("lightblue", [173, 216, 230]);
add("lightcoral", [240, 128, 128]);
add("lightcyan", [224, 255, 255]);
add("lightgoldenrodyellow", [250, 250, 210]);
add("lightgray", [211]);
add("lightgreen", [144, 238]);
add("lightgrey", [211]);
add("lightpink", [255, 182, 193]);
add("lightsalmon", [255, 160, 122]);
add("lightseagreen", [32, 178, 170]);
add("lightskyblue", [135, 206, 250]);
add("lightslategray", [119, 136, 153]);
add("lightslategrey", [119, 136, 153]);
add("lightsteelblue", [176, 196, 222]);
add("lightyellow", [255, 255, 224]);
add("lime", [0, 255]);
add("limegreen", [50, 205]);
add("linen", [250, 240, 230]);
add("magenta", [255, 0]);
add("maroon", [128, 0, 0]);
add("mediumaquamarine", [102, 205, 170]);
add("mediumblue", [0, 0, 205]);
add("mediumorchid", [186, 85, 211]);
add("mediumpurple", [147, 112, 219]);
add("mediumseagreen", [60, 179, 113]);
add("mediumslateblue", [123, 104, 238]);
add("mediumspringgreen", [0, 250, 154]);
add("mediumturquoise", [72, 209, 204]);
add("mediumvioletred", [199, 21, 133]);
add("midnightblue", [25, 25, 112]);
add("mintcream", [245, 255, 250]);
add("mistyrose", [255, 228, 225]);
add("moccasin", [255, 228, 181]);
add("navajowhite", [255, 222, 173]);
add("navy", [0, 0, 128]);
add("oldlace", [253, 245, 230]);
add("olive", [128, 128, 0]);
add("olivedrab", [107, 142, 35]);
add("orange", [255, 165, 0]);
add("orangered", [255, 69, 0]);
add("orchid", [218, 112, 214]);
add("palegoldenrod", [238, 232, 170]);
add("palegreen", [152, 251]);
add("paleturquoise", [175, 238, 238]);
add("palevioletred", [219, 112, 147]);
add("papayawhip", [255, 239, 213]);
add("peachpuff", [255, 218, 185]);
add("peru", [205, 133, 63]);
add("pink", [255, 192, 203]);
add("plum", [221, 160]);
add("powderblue", [176, 224, 230]);
add("purple", [128, 0]);
add("rebeccapurple", [102, 51, 153]);
add("red", [255, 0, 0]);
add("rosybrown", [188, 143, 143]);
add("royalblue", [65, 105, 225]);
add("saddlebrown", [139, 69, 19]);
add("salmon", [250, 128, 114]);
add("sandybrown", [244, 164, 96]);
add("seagreen", [46, 139, 87]);
add("seashell", [255, 245, 238]);
add("sienna", [160, 82, 45]);
add("silver", [192]);
add("skyblue", [135, 206, 235]);
add("slateblue", [106, 90, 205]);
add("slategray", [112, 128, 144]);
add("slategrey", [112, 128, 144]);
add("snow", [255, 250, 250]);
add("springgreen", [0, 255, 127]);
add("steelblue", [70, 130, 180]);
add("tan", [210, 180, 140]);
add("teal", [0, 128, 128]);
add("thistle", [216, 191]);
add("tomato", [255, 99, 71]);
add("turquoise", [64, 224, 208]);
add("violet", [238, 130]);
add("wheat", [245, 222, 179]);
add("white", [255]);
add("whitesmoke", [245]);
add("yellow", [255, 255, 0]);
add("yellowgreen", [154, 205, 50]);
export { colorKeywords };
@@ -0,0 +1,2 @@
'use strict';
@@ -0,0 +1,45 @@
interface RGBColor {
type: 'rgb';
r: number;
g: number;
b: number;
alpha: number;
}
interface HSLColor {
type: 'hsl';
h: number;
s: number;
l: number;
alpha: number;
}
interface LABColor {
type: 'lab';
l: number;
a: number;
b: number;
alpha: number;
}
interface LCHColor {
type: 'lch';
l: number;
c: number;
h: number;
alpha: number;
}
interface FunctionColor {
type: 'function';
func: string;
value: string;
}
interface TransparentColor {
type: 'transparent';
}
interface NoColor {
type: 'none';
}
interface CurrentColor {
type: 'current';
}
type Color = RGBColor | HSLColor | LABColor | LCHColor | FunctionColor | TransparentColor | NoColor | CurrentColor;
export type { Color, CurrentColor, FunctionColor, HSLColor, LABColor, LCHColor, NoColor, RGBColor, TransparentColor };
@@ -0,0 +1,45 @@
interface RGBColor {
type: 'rgb';
r: number;
g: number;
b: number;
alpha: number;
}
interface HSLColor {
type: 'hsl';
h: number;
s: number;
l: number;
alpha: number;
}
interface LABColor {
type: 'lab';
l: number;
a: number;
b: number;
alpha: number;
}
interface LCHColor {
type: 'lch';
l: number;
c: number;
h: number;
alpha: number;
}
interface FunctionColor {
type: 'function';
func: string;
value: string;
}
interface TransparentColor {
type: 'transparent';
}
interface NoColor {
type: 'none';
}
interface CurrentColor {
type: 'current';
}
type Color = RGBColor | HSLColor | LABColor | LCHColor | FunctionColor | TransparentColor | NoColor | CurrentColor;
export type { Color, CurrentColor, FunctionColor, HSLColor, LABColor, LCHColor, NoColor, RGBColor, TransparentColor };
@@ -0,0 +1,45 @@
interface RGBColor {
type: 'rgb';
r: number;
g: number;
b: number;
alpha: number;
}
interface HSLColor {
type: 'hsl';
h: number;
s: number;
l: number;
alpha: number;
}
interface LABColor {
type: 'lab';
l: number;
a: number;
b: number;
alpha: number;
}
interface LCHColor {
type: 'lch';
l: number;
c: number;
h: number;
alpha: number;
}
interface FunctionColor {
type: 'function';
func: string;
value: string;
}
interface TransparentColor {
type: 'transparent';
}
interface NoColor {
type: 'none';
}
interface CurrentColor {
type: 'current';
}
type Color = RGBColor | HSLColor | LABColor | LCHColor | FunctionColor | TransparentColor | NoColor | CurrentColor;
export type { Color, CurrentColor, FunctionColor, HSLColor, LABColor, LCHColor, NoColor, RGBColor, TransparentColor };
@@ -0,0 +1,94 @@
'use strict';
const svg_html = require('../svg/html.cjs');
const svg_size = require('../svg/size.cjs');
const svg_url = require('../svg/url.cjs');
const icon_square = require('../icon/square.cjs');
const svg_build = require('../svg/build.cjs');
require('../icon/defaults.cjs');
require('../customisations/defaults.cjs');
require('../svg/defs.cjs');
function getCommonCSSRules(options) {
const result = {
display: "inline-block",
width: "1em",
height: "1em"
};
const varName = options.varName;
if (options.pseudoSelector) {
result["content"] = "''";
}
switch (options.mode) {
case "background":
if (varName) {
result["background-image"] = "var(--" + varName + ")";
}
result["background-repeat"] = "no-repeat";
result["background-size"] = "100% 100%";
break;
case "mask":
result["background-color"] = "currentColor";
if (varName) {
result["mask-image"] = result["-webkit-mask-image"] = "var(--" + varName + ")";
}
result["mask-repeat"] = result["-webkit-mask-repeat"] = "no-repeat";
result["mask-size"] = result["-webkit-mask-size"] = "100% 100%";
break;
}
return result;
}
function generateItemCSSRules(icon, options) {
const result = {};
const varName = options.varName;
const buildResult = svg_build.iconToSVG(icon);
let viewBox = buildResult.viewBox;
if (viewBox[2] !== viewBox[3]) {
if (options.forceSquare) {
viewBox = icon_square.makeViewBoxSquare(viewBox);
} else {
result["width"] = svg_size.calculateSize("1em", viewBox[2] / viewBox[3]);
}
}
const svg = svg_html.iconToHTML(
buildResult.body.replace(/currentColor/g, options.color || "black"),
{
viewBox: `${viewBox[0]} ${viewBox[1]} ${viewBox[2]} ${viewBox[3]}`,
width: `${viewBox[2]}`,
height: `${viewBox[3]}`
}
);
const url = svg_url.svgToURL(svg);
if (varName) {
result["--" + varName] = url;
} else {
switch (options.mode) {
case "background":
result["background-image"] = url;
break;
case "mask":
result["mask-image"] = result["-webkit-mask-image"] = url;
break;
}
}
return result;
}
function generateItemContent(icon, options) {
const buildResult = svg_build.iconToSVG(icon);
const viewBox = buildResult.viewBox;
const height = options.height;
const width = options.width ?? svg_size.calculateSize(height, viewBox[2] / viewBox[3]);
const svg = svg_html.iconToHTML(
buildResult.body.replace(/currentColor/g, options.color || "black"),
{
viewBox: `${viewBox[0]} ${viewBox[1]} ${viewBox[2]} ${viewBox[3]}`,
width: width.toString(),
height: height.toString()
}
);
return svg_url.svgToURL(svg);
}
exports.generateItemCSSRules = generateItemCSSRules;
exports.generateItemContent = generateItemContent;
exports.getCommonCSSRules = getCommonCSSRules;
@@ -0,0 +1,19 @@
import { IconifyIcon } from '@iconify/types';
import { IconCSSCommonCodeOptions, IconCSSItemOptions, IconContentItemOptions } from './types.cjs';
/**
* Generates common CSS rules for multiple icons, rendered as background/mask
*/
declare function getCommonCSSRules(options: IconCSSCommonCodeOptions): Record<string, string>;
/**
* Generate CSS rules for one icon, rendered as background/mask
*
* This function excludes common rules
*/
declare function generateItemCSSRules(icon: Required<IconifyIcon>, options: IconCSSItemOptions): Record<string, string>;
/**
* Generate content for one icon, rendered as content of pseudo-selector
*/
declare function generateItemContent(icon: Required<IconifyIcon>, options: IconContentItemOptions): string;
export { generateItemCSSRules, generateItemContent, getCommonCSSRules };
@@ -0,0 +1,19 @@
import { IconifyIcon } from '@iconify/types';
import { IconCSSCommonCodeOptions, IconCSSItemOptions, IconContentItemOptions } from './types.mjs';
/**
* Generates common CSS rules for multiple icons, rendered as background/mask
*/
declare function getCommonCSSRules(options: IconCSSCommonCodeOptions): Record<string, string>;
/**
* Generate CSS rules for one icon, rendered as background/mask
*
* This function excludes common rules
*/
declare function generateItemCSSRules(icon: Required<IconifyIcon>, options: IconCSSItemOptions): Record<string, string>;
/**
* Generate content for one icon, rendered as content of pseudo-selector
*/
declare function generateItemContent(icon: Required<IconifyIcon>, options: IconContentItemOptions): string;
export { generateItemCSSRules, generateItemContent, getCommonCSSRules };
@@ -0,0 +1,19 @@
import { IconifyIcon } from '@iconify/types';
import { IconCSSCommonCodeOptions, IconCSSItemOptions, IconContentItemOptions } from './types.js';
/**
* Generates common CSS rules for multiple icons, rendered as background/mask
*/
declare function getCommonCSSRules(options: IconCSSCommonCodeOptions): Record<string, string>;
/**
* Generate CSS rules for one icon, rendered as background/mask
*
* This function excludes common rules
*/
declare function generateItemCSSRules(icon: Required<IconifyIcon>, options: IconCSSItemOptions): Record<string, string>;
/**
* Generate content for one icon, rendered as content of pseudo-selector
*/
declare function generateItemContent(icon: Required<IconifyIcon>, options: IconContentItemOptions): string;
export { generateItemCSSRules, generateItemContent, getCommonCSSRules };
@@ -0,0 +1,90 @@
import { iconToHTML } from '../svg/html.mjs';
import { calculateSize } from '../svg/size.mjs';
import { svgToURL } from '../svg/url.mjs';
import { makeViewBoxSquare } from '../icon/square.mjs';
import { iconToSVG } from '../svg/build.mjs';
import '../icon/defaults.mjs';
import '../customisations/defaults.mjs';
import '../svg/defs.mjs';
function getCommonCSSRules(options) {
const result = {
display: "inline-block",
width: "1em",
height: "1em"
};
const varName = options.varName;
if (options.pseudoSelector) {
result["content"] = "''";
}
switch (options.mode) {
case "background":
if (varName) {
result["background-image"] = "var(--" + varName + ")";
}
result["background-repeat"] = "no-repeat";
result["background-size"] = "100% 100%";
break;
case "mask":
result["background-color"] = "currentColor";
if (varName) {
result["mask-image"] = result["-webkit-mask-image"] = "var(--" + varName + ")";
}
result["mask-repeat"] = result["-webkit-mask-repeat"] = "no-repeat";
result["mask-size"] = result["-webkit-mask-size"] = "100% 100%";
break;
}
return result;
}
function generateItemCSSRules(icon, options) {
const result = {};
const varName = options.varName;
const buildResult = iconToSVG(icon);
let viewBox = buildResult.viewBox;
if (viewBox[2] !== viewBox[3]) {
if (options.forceSquare) {
viewBox = makeViewBoxSquare(viewBox);
} else {
result["width"] = calculateSize("1em", viewBox[2] / viewBox[3]);
}
}
const svg = iconToHTML(
buildResult.body.replace(/currentColor/g, options.color || "black"),
{
viewBox: `${viewBox[0]} ${viewBox[1]} ${viewBox[2]} ${viewBox[3]}`,
width: `${viewBox[2]}`,
height: `${viewBox[3]}`
}
);
const url = svgToURL(svg);
if (varName) {
result["--" + varName] = url;
} else {
switch (options.mode) {
case "background":
result["background-image"] = url;
break;
case "mask":
result["mask-image"] = result["-webkit-mask-image"] = url;
break;
}
}
return result;
}
function generateItemContent(icon, options) {
const buildResult = iconToSVG(icon);
const viewBox = buildResult.viewBox;
const height = options.height;
const width = options.width ?? calculateSize(height, viewBox[2] / viewBox[3]);
const svg = iconToHTML(
buildResult.body.replace(/currentColor/g, options.color || "black"),
{
viewBox: `${viewBox[0]} ${viewBox[1]} ${viewBox[2]} ${viewBox[3]}`,
width: width.toString(),
height: height.toString()
}
);
return svgToURL(svg);
}
export { generateItemCSSRules, generateItemContent, getCommonCSSRules };
@@ -0,0 +1,40 @@
'use strict';
const format = {
selectorStart: {
compressed: "{",
compact: " {",
expanded: " {"
},
selectorEnd: {
compressed: "}",
compact: "; }\n",
expanded: ";\n}\n"
},
rule: {
compressed: "{key}:",
compact: " {key}: ",
expanded: "\n {key}: "
}
};
function formatCSS(data, mode = "expanded") {
const results = [];
for (let i = 0; i < data.length; i++) {
const { selector, rules } = data[i];
const fullSelector = selector instanceof Array ? selector.join(mode === "compressed" ? "," : ", ") : selector;
let entry = fullSelector + format.selectorStart[mode];
let firstRule = true;
for (const key in rules) {
if (!firstRule) {
entry += ";";
}
entry += format.rule[mode].replace("{key}", key) + rules[key];
firstRule = false;
}
entry += format.selectorEnd[mode];
results.push(entry);
}
return results.join(mode === "compressed" ? "" : "\n");
}
exports.formatCSS = formatCSS;
@@ -0,0 +1,10 @@
import { CSSUnformattedItem, CSSFormatMode } from './types.cjs';
/**
* Format data
*
* Key is selector, value is list of rules
*/
declare function formatCSS(data: CSSUnformattedItem[], mode?: CSSFormatMode): string;
export { formatCSS };
@@ -0,0 +1,10 @@
import { CSSUnformattedItem, CSSFormatMode } from './types.mjs';
/**
* Format data
*
* Key is selector, value is list of rules
*/
declare function formatCSS(data: CSSUnformattedItem[], mode?: CSSFormatMode): string;
export { formatCSS };
@@ -0,0 +1,10 @@
import { CSSUnformattedItem, CSSFormatMode } from './types.js';
/**
* Format data
*
* Key is selector, value is list of rules
*/
declare function formatCSS(data: CSSUnformattedItem[], mode?: CSSFormatMode): string;
export { formatCSS };
@@ -0,0 +1,38 @@
const format = {
selectorStart: {
compressed: "{",
compact: " {",
expanded: " {"
},
selectorEnd: {
compressed: "}",
compact: "; }\n",
expanded: ";\n}\n"
},
rule: {
compressed: "{key}:",
compact: " {key}: ",
expanded: "\n {key}: "
}
};
function formatCSS(data, mode = "expanded") {
const results = [];
for (let i = 0; i < data.length; i++) {
const { selector, rules } = data[i];
const fullSelector = selector instanceof Array ? selector.join(mode === "compressed" ? "," : ", ") : selector;
let entry = fullSelector + format.selectorStart[mode];
let firstRule = true;
for (const key in rules) {
if (!firstRule) {
entry += ";";
}
entry += format.rule[mode].replace("{key}", key) + rules[key];
firstRule = false;
}
entry += format.selectorEnd[mode];
results.push(entry);
}
return results.join(mode === "compressed" ? "" : "\n");
}
export { formatCSS };
@@ -0,0 +1,79 @@
'use strict';
const icon_defaults = require('../icon/defaults.cjs');
const css_common = require('./common.cjs');
const css_format = require('./format.cjs');
require('../svg/html.cjs');
require('../svg/size.cjs');
require('../svg/url.cjs');
require('../icon/square.cjs');
require('../svg/build.cjs');
require('../customisations/defaults.cjs');
require('../svg/defs.cjs');
function getIconCSS(icon, options = {}) {
const body = options.customise ? options.customise(icon.body) : icon.body;
const mode = options.mode || (options.color || !body.includes("currentColor") ? "background" : "mask");
let varName = options.varName;
if (varName === void 0 && mode === "mask") {
varName = "svg";
}
const newOptions = {
...options,
// Override mode and varName
mode,
varName
};
if (mode === "background") {
delete newOptions.varName;
}
const rules = {
...options.rules,
...css_common.getCommonCSSRules(newOptions),
...css_common.generateItemCSSRules(
{
...icon_defaults.defaultIconProps,
...icon,
body
},
newOptions
)
};
const selector = options.iconSelector || ".icon";
return css_format.formatCSS(
[
{
selector,
rules
}
],
newOptions.format
);
}
function getIconContentCSS(icon, options) {
const body = options.customise ? options.customise(icon.body) : icon.body;
const content = css_common.generateItemContent(
{
...icon_defaults.defaultIconProps,
...icon,
body
},
options
);
const selector = options.iconSelector || ".icon::after";
return css_format.formatCSS(
[
{
selector,
rules: {
...options.rules,
content
}
}
],
options.format
);
}
exports.getIconCSS = getIconCSS;
exports.getIconContentCSS = getIconContentCSS;
@@ -0,0 +1,13 @@
import { IconifyIcon } from '@iconify/types';
import { IconCSSIconOptions, IconContentIconOptions } from './types.cjs';
/**
* Get CSS for icon, rendered as background or mask
*/
declare function getIconCSS(icon: IconifyIcon, options?: IconCSSIconOptions): string;
/**
* Get CSS for icon, rendered as content
*/
declare function getIconContentCSS(icon: IconifyIcon, options: IconContentIconOptions): string;
export { getIconCSS, getIconContentCSS };
@@ -0,0 +1,13 @@
import { IconifyIcon } from '@iconify/types';
import { IconCSSIconOptions, IconContentIconOptions } from './types.mjs';
/**
* Get CSS for icon, rendered as background or mask
*/
declare function getIconCSS(icon: IconifyIcon, options?: IconCSSIconOptions): string;
/**
* Get CSS for icon, rendered as content
*/
declare function getIconContentCSS(icon: IconifyIcon, options: IconContentIconOptions): string;
export { getIconCSS, getIconContentCSS };
@@ -0,0 +1,13 @@
import { IconifyIcon } from '@iconify/types';
import { IconCSSIconOptions, IconContentIconOptions } from './types.js';
/**
* Get CSS for icon, rendered as background or mask
*/
declare function getIconCSS(icon: IconifyIcon, options?: IconCSSIconOptions): string;
/**
* Get CSS for icon, rendered as content
*/
declare function getIconContentCSS(icon: IconifyIcon, options: IconContentIconOptions): string;
export { getIconCSS, getIconContentCSS };
@@ -0,0 +1,76 @@
import { defaultIconProps } from '../icon/defaults.mjs';
import { getCommonCSSRules, generateItemCSSRules, generateItemContent } from './common.mjs';
import { formatCSS } from './format.mjs';
import '../svg/html.mjs';
import '../svg/size.mjs';
import '../svg/url.mjs';
import '../icon/square.mjs';
import '../svg/build.mjs';
import '../customisations/defaults.mjs';
import '../svg/defs.mjs';
function getIconCSS(icon, options = {}) {
const body = options.customise ? options.customise(icon.body) : icon.body;
const mode = options.mode || (options.color || !body.includes("currentColor") ? "background" : "mask");
let varName = options.varName;
if (varName === void 0 && mode === "mask") {
varName = "svg";
}
const newOptions = {
...options,
// Override mode and varName
mode,
varName
};
if (mode === "background") {
delete newOptions.varName;
}
const rules = {
...options.rules,
...getCommonCSSRules(newOptions),
...generateItemCSSRules(
{
...defaultIconProps,
...icon,
body
},
newOptions
)
};
const selector = options.iconSelector || ".icon";
return formatCSS(
[
{
selector,
rules
}
],
newOptions.format
);
}
function getIconContentCSS(icon, options) {
const body = options.customise ? options.customise(icon.body) : icon.body;
const content = generateItemContent(
{
...defaultIconProps,
...icon,
body
},
options
);
const selector = options.iconSelector || ".icon::after";
return formatCSS(
[
{
selector,
rules: {
...options.rules,
content
}
}
],
options.format
);
}
export { getIconCSS, getIconContentCSS };
@@ -0,0 +1,172 @@
'use strict';
const iconSet_getIcon = require('../icon-set/get-icon.cjs');
const icon_defaults = require('../icon/defaults.cjs');
const css_common = require('./common.cjs');
const css_format = require('./format.cjs');
require('../icon/merge.cjs');
require('../icon/transformations.cjs');
require('../icon-set/tree.cjs');
require('../svg/html.cjs');
require('../svg/size.cjs');
require('../svg/url.cjs');
require('../icon/square.cjs');
require('../svg/build.cjs');
require('../customisations/defaults.cjs');
require('../svg/defs.cjs');
const commonSelector = ".icon--{prefix}";
const iconSelector = ".icon--{prefix}--{name}";
const contentSelector = ".icon--{prefix}--{name}::after";
const defaultSelectors = {
commonSelector,
iconSelector,
overrideSelector: commonSelector + iconSelector
};
function getIconsCSSData(iconSet, names, options = {}) {
const css = [];
const errors = [];
const palette = options.color ? true : void 0;
let mode = options.mode || typeof palette === "boolean" && (palette ? "background" : "mask");
if (!mode) {
for (let i = 0; i < names.length; i++) {
const name = names[i];
const icon = iconSet_getIcon.getIconData(iconSet, name);
if (icon) {
const body = options.customise ? options.customise(icon.body, name) : icon.body;
mode = body.includes("currentColor") ? "mask" : "background";
break;
}
}
if (!mode) {
mode = "mask";
errors.push(
"/* cannot detect icon mode: not set in options and icon set is missing info, rendering as " + mode + " */"
);
}
}
let varName = options.varName;
if (varName === void 0 && mode === "mask") {
varName = "svg";
}
const newOptions = {
...options,
// Override mode and varName
mode,
varName
};
const { commonSelector: commonSelector2, iconSelector: iconSelector2, overrideSelector } = newOptions.iconSelector ? newOptions : defaultSelectors;
const iconSelectorWithPrefix = iconSelector2.replace(
/{prefix}/g,
iconSet.prefix
);
const commonRules = {
...options.rules,
...css_common.getCommonCSSRules(newOptions)
};
const hasCommonRules = commonSelector2 && commonSelector2 !== iconSelector2;
const commonSelectors = /* @__PURE__ */ new Set();
if (hasCommonRules) {
css.push({
selector: commonSelector2.replace(/{prefix}/g, iconSet.prefix),
rules: commonRules
});
}
for (let i = 0; i < names.length; i++) {
const name = names[i];
const iconData = iconSet_getIcon.getIconData(iconSet, name);
if (!iconData) {
errors.push("/* Could not find icon: " + name + " */");
continue;
}
const body = options.customise ? options.customise(iconData.body, name) : iconData.body;
const rules = css_common.generateItemCSSRules(
{
...icon_defaults.defaultIconProps,
...iconData,
body
},
newOptions
);
let requiresOverride = false;
if (hasCommonRules && overrideSelector) {
for (const key in rules) {
if (key in commonRules) {
requiresOverride = true;
}
}
}
const selector = (requiresOverride && overrideSelector ? overrideSelector.replace(/{prefix}/g, iconSet.prefix) : iconSelectorWithPrefix).replace(/{name}/g, name);
css.push({
selector,
rules
});
if (!hasCommonRules) {
commonSelectors.add(selector);
}
}
const result = {
css,
errors
};
if (!hasCommonRules && commonSelectors.size) {
const selector = Array.from(commonSelectors).join(
newOptions.format === "compressed" ? "," : ", "
);
result.common = {
selector,
rules: commonRules
};
}
return result;
}
function getIconsCSS(iconSet, names, options = {}) {
const { css, errors, common } = getIconsCSSData(iconSet, names, options);
if (common) {
if (css.length === 1 && css[0].selector === common.selector) {
css[0].rules = {
// Common first, override later
...common.rules,
...css[0].rules
};
} else {
css.unshift(common);
}
}
return css_format.formatCSS(css, options.format) + (errors.length ? "\n" + errors.join("\n") + "\n" : "");
}
function getIconsContentCSS(iconSet, names, options) {
const errors = [];
const css = [];
const iconSelectorWithPrefix = (options.iconSelector ?? contentSelector).replace(/{prefix}/g, iconSet.prefix);
for (let i = 0; i < names.length; i++) {
const name = names[i];
const iconData = iconSet_getIcon.getIconData(iconSet, name);
if (!iconData) {
errors.push("/* Could not find icon: " + name + " */");
continue;
}
const body = options.customise ? options.customise(iconData.body, name) : iconData.body;
const content = css_common.generateItemContent(
{
...icon_defaults.defaultIconProps,
...iconData,
body
},
options
);
const selector = iconSelectorWithPrefix.replace(/{name}/g, name);
css.push({
selector,
rules: {
...options.rules,
content
}
});
}
return css_format.formatCSS(css, options.format) + (errors.length ? "\n" + errors.join("\n") + "\n" : "");
}
exports.getIconsCSS = getIconsCSS;
exports.getIconsCSSData = getIconsCSSData;
exports.getIconsContentCSS = getIconsContentCSS;
@@ -0,0 +1,22 @@
import { IconifyJSON } from '@iconify/types';
import { IconCSSIconSetOptions, IconContentIconSetOptions, CSSUnformattedItem } from './types.cjs';
interface CSSData {
common?: CSSUnformattedItem;
css: CSSUnformattedItem[];
errors: string[];
}
/**
* Get data for getIconsCSS()
*/
declare function getIconsCSSData(iconSet: IconifyJSON, names: string[], options?: IconCSSIconSetOptions): CSSData;
/**
* Get CSS for icons as background/mask
*/
declare function getIconsCSS(iconSet: IconifyJSON, names: string[], options?: IconCSSIconSetOptions): string;
/**
* Get CSS for icons as content
*/
declare function getIconsContentCSS(iconSet: IconifyJSON, names: string[], options: IconContentIconSetOptions): string;
export { getIconsCSS, getIconsCSSData, getIconsContentCSS };
@@ -0,0 +1,22 @@
import { IconifyJSON } from '@iconify/types';
import { IconCSSIconSetOptions, IconContentIconSetOptions, CSSUnformattedItem } from './types.mjs';
interface CSSData {
common?: CSSUnformattedItem;
css: CSSUnformattedItem[];
errors: string[];
}
/**
* Get data for getIconsCSS()
*/
declare function getIconsCSSData(iconSet: IconifyJSON, names: string[], options?: IconCSSIconSetOptions): CSSData;
/**
* Get CSS for icons as background/mask
*/
declare function getIconsCSS(iconSet: IconifyJSON, names: string[], options?: IconCSSIconSetOptions): string;
/**
* Get CSS for icons as content
*/
declare function getIconsContentCSS(iconSet: IconifyJSON, names: string[], options: IconContentIconSetOptions): string;
export { getIconsCSS, getIconsCSSData, getIconsContentCSS };
@@ -0,0 +1,22 @@
import { IconifyJSON } from '@iconify/types';
import { IconCSSIconSetOptions, IconContentIconSetOptions, CSSUnformattedItem } from './types.js';
interface CSSData {
common?: CSSUnformattedItem;
css: CSSUnformattedItem[];
errors: string[];
}
/**
* Get data for getIconsCSS()
*/
declare function getIconsCSSData(iconSet: IconifyJSON, names: string[], options?: IconCSSIconSetOptions): CSSData;
/**
* Get CSS for icons as background/mask
*/
declare function getIconsCSS(iconSet: IconifyJSON, names: string[], options?: IconCSSIconSetOptions): string;
/**
* Get CSS for icons as content
*/
declare function getIconsContentCSS(iconSet: IconifyJSON, names: string[], options: IconContentIconSetOptions): string;
export { getIconsCSS, getIconsCSSData, getIconsContentCSS };
@@ -0,0 +1,168 @@
import { getIconData } from '../icon-set/get-icon.mjs';
import { defaultIconProps } from '../icon/defaults.mjs';
import { getCommonCSSRules, generateItemCSSRules, generateItemContent } from './common.mjs';
import { formatCSS } from './format.mjs';
import '../icon/merge.mjs';
import '../icon/transformations.mjs';
import '../icon-set/tree.mjs';
import '../svg/html.mjs';
import '../svg/size.mjs';
import '../svg/url.mjs';
import '../icon/square.mjs';
import '../svg/build.mjs';
import '../customisations/defaults.mjs';
import '../svg/defs.mjs';
const commonSelector = ".icon--{prefix}";
const iconSelector = ".icon--{prefix}--{name}";
const contentSelector = ".icon--{prefix}--{name}::after";
const defaultSelectors = {
commonSelector,
iconSelector,
overrideSelector: commonSelector + iconSelector
};
function getIconsCSSData(iconSet, names, options = {}) {
const css = [];
const errors = [];
const palette = options.color ? true : void 0;
let mode = options.mode || typeof palette === "boolean" && (palette ? "background" : "mask");
if (!mode) {
for (let i = 0; i < names.length; i++) {
const name = names[i];
const icon = getIconData(iconSet, name);
if (icon) {
const body = options.customise ? options.customise(icon.body, name) : icon.body;
mode = body.includes("currentColor") ? "mask" : "background";
break;
}
}
if (!mode) {
mode = "mask";
errors.push(
"/* cannot detect icon mode: not set in options and icon set is missing info, rendering as " + mode + " */"
);
}
}
let varName = options.varName;
if (varName === void 0 && mode === "mask") {
varName = "svg";
}
const newOptions = {
...options,
// Override mode and varName
mode,
varName
};
const { commonSelector: commonSelector2, iconSelector: iconSelector2, overrideSelector } = newOptions.iconSelector ? newOptions : defaultSelectors;
const iconSelectorWithPrefix = iconSelector2.replace(
/{prefix}/g,
iconSet.prefix
);
const commonRules = {
...options.rules,
...getCommonCSSRules(newOptions)
};
const hasCommonRules = commonSelector2 && commonSelector2 !== iconSelector2;
const commonSelectors = /* @__PURE__ */ new Set();
if (hasCommonRules) {
css.push({
selector: commonSelector2.replace(/{prefix}/g, iconSet.prefix),
rules: commonRules
});
}
for (let i = 0; i < names.length; i++) {
const name = names[i];
const iconData = getIconData(iconSet, name);
if (!iconData) {
errors.push("/* Could not find icon: " + name + " */");
continue;
}
const body = options.customise ? options.customise(iconData.body, name) : iconData.body;
const rules = generateItemCSSRules(
{
...defaultIconProps,
...iconData,
body
},
newOptions
);
let requiresOverride = false;
if (hasCommonRules && overrideSelector) {
for (const key in rules) {
if (key in commonRules) {
requiresOverride = true;
}
}
}
const selector = (requiresOverride && overrideSelector ? overrideSelector.replace(/{prefix}/g, iconSet.prefix) : iconSelectorWithPrefix).replace(/{name}/g, name);
css.push({
selector,
rules
});
if (!hasCommonRules) {
commonSelectors.add(selector);
}
}
const result = {
css,
errors
};
if (!hasCommonRules && commonSelectors.size) {
const selector = Array.from(commonSelectors).join(
newOptions.format === "compressed" ? "," : ", "
);
result.common = {
selector,
rules: commonRules
};
}
return result;
}
function getIconsCSS(iconSet, names, options = {}) {
const { css, errors, common } = getIconsCSSData(iconSet, names, options);
if (common) {
if (css.length === 1 && css[0].selector === common.selector) {
css[0].rules = {
// Common first, override later
...common.rules,
...css[0].rules
};
} else {
css.unshift(common);
}
}
return formatCSS(css, options.format) + (errors.length ? "\n" + errors.join("\n") + "\n" : "");
}
function getIconsContentCSS(iconSet, names, options) {
const errors = [];
const css = [];
const iconSelectorWithPrefix = (options.iconSelector ?? contentSelector).replace(/{prefix}/g, iconSet.prefix);
for (let i = 0; i < names.length; i++) {
const name = names[i];
const iconData = getIconData(iconSet, name);
if (!iconData) {
errors.push("/* Could not find icon: " + name + " */");
continue;
}
const body = options.customise ? options.customise(iconData.body, name) : iconData.body;
const content = generateItemContent(
{
...defaultIconProps,
...iconData,
body
},
options
);
const selector = iconSelectorWithPrefix.replace(/{name}/g, name);
css.push({
selector,
rules: {
...options.rules,
content
}
});
}
return formatCSS(css, options.format) + (errors.length ? "\n" + errors.join("\n") + "\n" : "");
}
export { getIconsCSS, getIconsCSSData, getIconsContentCSS };
@@ -0,0 +1,2 @@
'use strict';
@@ -0,0 +1,107 @@
/**
* Icon mode
*/
type IconCSSMode = 'mask' | 'background';
/**
* Selector for icon
*/
interface IconCSSIconSelectorOptions {
pseudoSelector?: boolean;
iconSelector?: string;
}
/**
* Selector for icon when generating data from icon set
*/
interface IconCSSSelectorOptions extends IconCSSIconSelectorOptions {
commonSelector?: string;
overrideSelector?: string;
}
/**
* Options common for both multiple icons and single icon
*/
interface IconCSSSharedOptions {
varName?: string | null;
forceSquare?: boolean;
color?: string;
rules?: Record<string, string>;
}
/**
* Mode
*/
interface IconCSSModeOptions {
mode?: IconCSSMode;
}
/**
* Options for generating common code
*
* Requires mode
*/
interface IconCSSCommonCodeOptions extends IconCSSSharedOptions, IconCSSIconSelectorOptions, Required<IconCSSModeOptions> {
}
/**
* Options for generating data for one icon
*/
interface IconCSSItemOptions extends IconCSSSharedOptions, Required<IconCSSModeOptions> {
}
/**
* Selector for icon
*/
interface IconContentIconSelectorOptions {
iconSelector?: string;
}
/**
* Options common for both multiple icons and single icon
*/
interface IconContentSharedOptions {
height: number;
width?: number;
color?: string;
rules?: Record<string, string>;
}
/**
* Options for generating data for one icon
*/
type IconContentItemOptions = IconContentSharedOptions;
/**
* Formatting modes. Same as in SASS
*/
type CSSFormatMode = 'expanded' | 'compact' | 'compressed';
/**
* Item to format
*/
interface CSSUnformattedItem {
selector: string | string[];
rules: Record<string, string>;
}
/**
* Formatting options
*/
interface IconCSSFormatOptions {
format?: CSSFormatMode;
}
/**
* Options for generating data for one icon as background/mask
*/
interface IconCSSIconOptions extends IconCSSSharedOptions, IconCSSIconSelectorOptions, IconCSSModeOptions, IconCSSFormatOptions {
customise?: (content: string) => string;
}
/**
* Options for generating data for one icon as content
*/
interface IconContentIconOptions extends IconContentSharedOptions, IconContentIconSelectorOptions, IconCSSFormatOptions {
customise?: (content: string) => string;
}
/**
* Options for generating multiple icons as background/mask
*/
interface IconCSSIconSetOptions extends IconCSSSharedOptions, IconCSSSelectorOptions, IconCSSModeOptions, IconCSSFormatOptions {
customise?: (content: string, name: string) => string;
}
/**
* Options for generating multiple icons as content
*/
interface IconContentIconSetOptions extends IconContentSharedOptions, IconContentIconSelectorOptions, IconCSSFormatOptions {
customise?: (content: string, name: string) => string;
}
export type { CSSFormatMode, CSSUnformattedItem, IconCSSCommonCodeOptions, IconCSSFormatOptions, IconCSSIconOptions, IconCSSIconSelectorOptions, IconCSSIconSetOptions, IconCSSItemOptions, IconCSSMode, IconCSSModeOptions, IconCSSSelectorOptions, IconCSSSharedOptions, IconContentIconOptions, IconContentIconSelectorOptions, IconContentIconSetOptions, IconContentItemOptions, IconContentSharedOptions };
@@ -0,0 +1,107 @@
/**
* Icon mode
*/
type IconCSSMode = 'mask' | 'background';
/**
* Selector for icon
*/
interface IconCSSIconSelectorOptions {
pseudoSelector?: boolean;
iconSelector?: string;
}
/**
* Selector for icon when generating data from icon set
*/
interface IconCSSSelectorOptions extends IconCSSIconSelectorOptions {
commonSelector?: string;
overrideSelector?: string;
}
/**
* Options common for both multiple icons and single icon
*/
interface IconCSSSharedOptions {
varName?: string | null;
forceSquare?: boolean;
color?: string;
rules?: Record<string, string>;
}
/**
* Mode
*/
interface IconCSSModeOptions {
mode?: IconCSSMode;
}
/**
* Options for generating common code
*
* Requires mode
*/
interface IconCSSCommonCodeOptions extends IconCSSSharedOptions, IconCSSIconSelectorOptions, Required<IconCSSModeOptions> {
}
/**
* Options for generating data for one icon
*/
interface IconCSSItemOptions extends IconCSSSharedOptions, Required<IconCSSModeOptions> {
}
/**
* Selector for icon
*/
interface IconContentIconSelectorOptions {
iconSelector?: string;
}
/**
* Options common for both multiple icons and single icon
*/
interface IconContentSharedOptions {
height: number;
width?: number;
color?: string;
rules?: Record<string, string>;
}
/**
* Options for generating data for one icon
*/
type IconContentItemOptions = IconContentSharedOptions;
/**
* Formatting modes. Same as in SASS
*/
type CSSFormatMode = 'expanded' | 'compact' | 'compressed';
/**
* Item to format
*/
interface CSSUnformattedItem {
selector: string | string[];
rules: Record<string, string>;
}
/**
* Formatting options
*/
interface IconCSSFormatOptions {
format?: CSSFormatMode;
}
/**
* Options for generating data for one icon as background/mask
*/
interface IconCSSIconOptions extends IconCSSSharedOptions, IconCSSIconSelectorOptions, IconCSSModeOptions, IconCSSFormatOptions {
customise?: (content: string) => string;
}
/**
* Options for generating data for one icon as content
*/
interface IconContentIconOptions extends IconContentSharedOptions, IconContentIconSelectorOptions, IconCSSFormatOptions {
customise?: (content: string) => string;
}
/**
* Options for generating multiple icons as background/mask
*/
interface IconCSSIconSetOptions extends IconCSSSharedOptions, IconCSSSelectorOptions, IconCSSModeOptions, IconCSSFormatOptions {
customise?: (content: string, name: string) => string;
}
/**
* Options for generating multiple icons as content
*/
interface IconContentIconSetOptions extends IconContentSharedOptions, IconContentIconSelectorOptions, IconCSSFormatOptions {
customise?: (content: string, name: string) => string;
}
export type { CSSFormatMode, CSSUnformattedItem, IconCSSCommonCodeOptions, IconCSSFormatOptions, IconCSSIconOptions, IconCSSIconSelectorOptions, IconCSSIconSetOptions, IconCSSItemOptions, IconCSSMode, IconCSSModeOptions, IconCSSSelectorOptions, IconCSSSharedOptions, IconContentIconOptions, IconContentIconSelectorOptions, IconContentIconSetOptions, IconContentItemOptions, IconContentSharedOptions };
@@ -0,0 +1,107 @@
/**
* Icon mode
*/
type IconCSSMode = 'mask' | 'background';
/**
* Selector for icon
*/
interface IconCSSIconSelectorOptions {
pseudoSelector?: boolean;
iconSelector?: string;
}
/**
* Selector for icon when generating data from icon set
*/
interface IconCSSSelectorOptions extends IconCSSIconSelectorOptions {
commonSelector?: string;
overrideSelector?: string;
}
/**
* Options common for both multiple icons and single icon
*/
interface IconCSSSharedOptions {
varName?: string | null;
forceSquare?: boolean;
color?: string;
rules?: Record<string, string>;
}
/**
* Mode
*/
interface IconCSSModeOptions {
mode?: IconCSSMode;
}
/**
* Options for generating common code
*
* Requires mode
*/
interface IconCSSCommonCodeOptions extends IconCSSSharedOptions, IconCSSIconSelectorOptions, Required<IconCSSModeOptions> {
}
/**
* Options for generating data for one icon
*/
interface IconCSSItemOptions extends IconCSSSharedOptions, Required<IconCSSModeOptions> {
}
/**
* Selector for icon
*/
interface IconContentIconSelectorOptions {
iconSelector?: string;
}
/**
* Options common for both multiple icons and single icon
*/
interface IconContentSharedOptions {
height: number;
width?: number;
color?: string;
rules?: Record<string, string>;
}
/**
* Options for generating data for one icon
*/
type IconContentItemOptions = IconContentSharedOptions;
/**
* Formatting modes. Same as in SASS
*/
type CSSFormatMode = 'expanded' | 'compact' | 'compressed';
/**
* Item to format
*/
interface CSSUnformattedItem {
selector: string | string[];
rules: Record<string, string>;
}
/**
* Formatting options
*/
interface IconCSSFormatOptions {
format?: CSSFormatMode;
}
/**
* Options for generating data for one icon as background/mask
*/
interface IconCSSIconOptions extends IconCSSSharedOptions, IconCSSIconSelectorOptions, IconCSSModeOptions, IconCSSFormatOptions {
customise?: (content: string) => string;
}
/**
* Options for generating data for one icon as content
*/
interface IconContentIconOptions extends IconContentSharedOptions, IconContentIconSelectorOptions, IconCSSFormatOptions {
customise?: (content: string) => string;
}
/**
* Options for generating multiple icons as background/mask
*/
interface IconCSSIconSetOptions extends IconCSSSharedOptions, IconCSSSelectorOptions, IconCSSModeOptions, IconCSSFormatOptions {
customise?: (content: string, name: string) => string;
}
/**
* Options for generating multiple icons as content
*/
interface IconContentIconSetOptions extends IconContentSharedOptions, IconContentIconSelectorOptions, IconCSSFormatOptions {
customise?: (content: string, name: string) => string;
}
export type { CSSFormatMode, CSSUnformattedItem, IconCSSCommonCodeOptions, IconCSSFormatOptions, IconCSSIconOptions, IconCSSIconSelectorOptions, IconCSSIconSetOptions, IconCSSItemOptions, IconCSSMode, IconCSSModeOptions, IconCSSSelectorOptions, IconCSSSharedOptions, IconContentIconOptions, IconContentIconSelectorOptions, IconContentIconSetOptions, IconContentItemOptions, IconContentSharedOptions };
@@ -0,0 +1,24 @@
'use strict';
function toBoolean(name, value, defaultValue) {
switch (typeof value) {
case "boolean":
return value;
case "number":
return !!value;
case "string":
switch (value.toLowerCase()) {
case "1":
case "true":
case name.toLowerCase():
return true;
case "0":
case "false":
case "":
return false;
}
}
return defaultValue;
}
exports.toBoolean = toBoolean;
@@ -0,0 +1,6 @@
/**
* Get boolean customisation value from attribute
*/
declare function toBoolean(name: string, value: unknown, defaultValue: boolean): boolean;
export { toBoolean };
@@ -0,0 +1,6 @@
/**
* Get boolean customisation value from attribute
*/
declare function toBoolean(name: string, value: unknown, defaultValue: boolean): boolean;
export { toBoolean };
@@ -0,0 +1,6 @@
/**
* Get boolean customisation value from attribute
*/
declare function toBoolean(name: string, value: unknown, defaultValue: boolean): boolean;
export { toBoolean };
@@ -0,0 +1,22 @@
function toBoolean(name, value, defaultValue) {
switch (typeof value) {
case "boolean":
return value;
case "number":
return !!value;
case "string":
switch (value.toLowerCase()) {
case "1":
case "true":
case name.toLowerCase():
return true;
case "0":
case "false":
case "":
return false;
}
}
return defaultValue;
}
export { toBoolean };
@@ -0,0 +1,17 @@
'use strict';
const icon_defaults = require('../icon/defaults.cjs');
const defaultIconSizeCustomisations = Object.freeze({
width: null,
height: null
});
const defaultIconCustomisations = Object.freeze({
// Dimensions
...defaultIconSizeCustomisations,
// Transformations
...icon_defaults.defaultIconTransformations
});
exports.defaultIconCustomisations = defaultIconCustomisations;
exports.defaultIconSizeCustomisations = defaultIconSizeCustomisations;
@@ -0,0 +1,26 @@
import { IconifyTransformations } from '@iconify/types';
/**
* Icon size
*/
type IconifyIconSize = null | string | number;
/**
* Dimensions
*/
interface IconifyIconSizeCustomisations {
width?: IconifyIconSize;
height?: IconifyIconSize;
}
/**
* Icon customisations
*/
interface IconifyIconCustomisations extends IconifyTransformations, IconifyIconSizeCustomisations {
}
type FullIconCustomisations = Required<IconifyIconCustomisations>;
/**
* Default icon customisations values
*/
declare const defaultIconSizeCustomisations: Required<IconifyIconSizeCustomisations>;
declare const defaultIconCustomisations: FullIconCustomisations;
export { type FullIconCustomisations, type IconifyIconCustomisations, type IconifyIconSize, type IconifyIconSizeCustomisations, defaultIconCustomisations, defaultIconSizeCustomisations };
@@ -0,0 +1,26 @@
import { IconifyTransformations } from '@iconify/types';
/**
* Icon size
*/
type IconifyIconSize = null | string | number;
/**
* Dimensions
*/
interface IconifyIconSizeCustomisations {
width?: IconifyIconSize;
height?: IconifyIconSize;
}
/**
* Icon customisations
*/
interface IconifyIconCustomisations extends IconifyTransformations, IconifyIconSizeCustomisations {
}
type FullIconCustomisations = Required<IconifyIconCustomisations>;
/**
* Default icon customisations values
*/
declare const defaultIconSizeCustomisations: Required<IconifyIconSizeCustomisations>;
declare const defaultIconCustomisations: FullIconCustomisations;
export { type FullIconCustomisations, type IconifyIconCustomisations, type IconifyIconSize, type IconifyIconSizeCustomisations, defaultIconCustomisations, defaultIconSizeCustomisations };
@@ -0,0 +1,26 @@
import { IconifyTransformations } from '@iconify/types';
/**
* Icon size
*/
type IconifyIconSize = null | string | number;
/**
* Dimensions
*/
interface IconifyIconSizeCustomisations {
width?: IconifyIconSize;
height?: IconifyIconSize;
}
/**
* Icon customisations
*/
interface IconifyIconCustomisations extends IconifyTransformations, IconifyIconSizeCustomisations {
}
type FullIconCustomisations = Required<IconifyIconCustomisations>;
/**
* Default icon customisations values
*/
declare const defaultIconSizeCustomisations: Required<IconifyIconSizeCustomisations>;
declare const defaultIconCustomisations: FullIconCustomisations;
export { type FullIconCustomisations, type IconifyIconCustomisations, type IconifyIconSize, type IconifyIconSizeCustomisations, defaultIconCustomisations, defaultIconSizeCustomisations };
@@ -0,0 +1,14 @@
import { defaultIconTransformations } from '../icon/defaults.mjs';
const defaultIconSizeCustomisations = Object.freeze({
width: null,
height: null
});
const defaultIconCustomisations = Object.freeze({
// Dimensions
...defaultIconSizeCustomisations,
// Transformations
...defaultIconTransformations
});
export { defaultIconCustomisations, defaultIconSizeCustomisations };
@@ -0,0 +1,18 @@
'use strict';
const separator = /[\s,]+/;
function flipFromString(custom, flip) {
flip.split(separator).forEach((str) => {
const value = str.trim();
switch (value) {
case "horizontal":
custom.hFlip = true;
break;
case "vertical":
custom.vFlip = true;
break;
}
});
}
exports.flipFromString = flipFromString;
@@ -0,0 +1,15 @@
import { IconifyIconCustomisations } from './defaults.cjs';
import '@iconify/types';
/**
* Additional shorthand customisations
*/
interface ShorthandIconCustomisations {
flip?: string;
}
/**
* Apply "flip" string to icon customisations
*/
declare function flipFromString(custom: IconifyIconCustomisations, flip: string): void;
export { type ShorthandIconCustomisations, flipFromString };
@@ -0,0 +1,15 @@
import { IconifyIconCustomisations } from './defaults.mjs';
import '@iconify/types';
/**
* Additional shorthand customisations
*/
interface ShorthandIconCustomisations {
flip?: string;
}
/**
* Apply "flip" string to icon customisations
*/
declare function flipFromString(custom: IconifyIconCustomisations, flip: string): void;
export { type ShorthandIconCustomisations, flipFromString };
@@ -0,0 +1,15 @@
import { IconifyIconCustomisations } from './defaults.js';
import '@iconify/types';
/**
* Additional shorthand customisations
*/
interface ShorthandIconCustomisations {
flip?: string;
}
/**
* Apply "flip" string to icon customisations
*/
declare function flipFromString(custom: IconifyIconCustomisations, flip: string): void;
export { type ShorthandIconCustomisations, flipFromString };
@@ -0,0 +1,16 @@
const separator = /[\s,]+/;
function flipFromString(custom, flip) {
flip.split(separator).forEach((str) => {
const value = str.trim();
switch (value) {
case "horizontal":
custom.hFlip = true;
break;
case "vertical":
custom.vFlip = true;
break;
}
});
}
export { flipFromString };
@@ -0,0 +1,24 @@
'use strict';
const customisations_defaults = require('./defaults.cjs');
require('../icon/defaults.cjs');
function mergeCustomisations(defaults, item) {
const result = {
...defaults
};
for (const key in item) {
const value = item[key];
const valueType = typeof value;
if (key in customisations_defaults.defaultIconSizeCustomisations) {
if (value === null || value && (valueType === "string" || valueType === "number")) {
result[key] = value;
}
} else if (valueType === typeof result[key]) {
result[key] = key === "rotate" ? value % 4 : value;
}
}
return result;
}
exports.mergeCustomisations = mergeCustomisations;
@@ -0,0 +1,9 @@
import { FullIconCustomisations, IconifyIconCustomisations } from './defaults.cjs';
import '@iconify/types';
/**
* Convert IconifyIconCustomisations to FullIconCustomisations, checking value types
*/
declare function mergeCustomisations<T extends FullIconCustomisations>(defaults: T, item: IconifyIconCustomisations): T;
export { mergeCustomisations };
@@ -0,0 +1,9 @@
import { FullIconCustomisations, IconifyIconCustomisations } from './defaults.mjs';
import '@iconify/types';
/**
* Convert IconifyIconCustomisations to FullIconCustomisations, checking value types
*/
declare function mergeCustomisations<T extends FullIconCustomisations>(defaults: T, item: IconifyIconCustomisations): T;
export { mergeCustomisations };
@@ -0,0 +1,9 @@
import { FullIconCustomisations, IconifyIconCustomisations } from './defaults.js';
import '@iconify/types';
/**
* Convert IconifyIconCustomisations to FullIconCustomisations, checking value types
*/
declare function mergeCustomisations<T extends FullIconCustomisations>(defaults: T, item: IconifyIconCustomisations): T;
export { mergeCustomisations };
@@ -0,0 +1,22 @@
import { defaultIconSizeCustomisations } from './defaults.mjs';
import '../icon/defaults.mjs';
function mergeCustomisations(defaults, item) {
const result = {
...defaults
};
for (const key in item) {
const value = item[key];
const valueType = typeof value;
if (key in defaultIconSizeCustomisations) {
if (value === null || value && (valueType === "string" || valueType === "number")) {
result[key] = value;
}
} else if (valueType === typeof result[key]) {
result[key] = key === "rotate" ? value % 4 : value;
}
}
return result;
}
export { mergeCustomisations };
@@ -0,0 +1,35 @@
'use strict';
function rotateFromString(value, defaultValue = 0) {
const units = value.replace(/^-?[0-9.]*/, "");
function cleanup(value2) {
while (value2 < 0) {
value2 += 4;
}
return value2 % 4;
}
if (units === "") {
const num = parseInt(value);
return isNaN(num) ? 0 : cleanup(num);
} else if (units !== value) {
let split = 0;
switch (units) {
case "%":
split = 25;
break;
case "deg":
split = 90;
}
if (split) {
let num = parseFloat(value.slice(0, value.length - units.length));
if (isNaN(num)) {
return 0;
}
num = num / split;
return num % 1 === 0 ? cleanup(num) : 0;
}
}
return defaultValue;
}
exports.rotateFromString = rotateFromString;
@@ -0,0 +1,6 @@
/**
* Get rotation value
*/
declare function rotateFromString(value: string, defaultValue?: number): number;
export { rotateFromString };
@@ -0,0 +1,6 @@
/**
* Get rotation value
*/
declare function rotateFromString(value: string, defaultValue?: number): number;
export { rotateFromString };
@@ -0,0 +1,6 @@
/**
* Get rotation value
*/
declare function rotateFromString(value: string, defaultValue?: number): number;
export { rotateFromString };
@@ -0,0 +1,33 @@
function rotateFromString(value, defaultValue = 0) {
const units = value.replace(/^-?[0-9.]*/, "");
function cleanup(value2) {
while (value2 < 0) {
value2 += 4;
}
return value2 % 4;
}
if (units === "") {
const num = parseInt(value);
return isNaN(num) ? 0 : cleanup(num);
} else if (units !== value) {
let split = 0;
switch (units) {
case "%":
split = 25;
break;
case "deg":
split = 90;
}
if (split) {
let num = parseFloat(value.slice(0, value.length - units.length));
if (isNaN(num)) {
return 0;
}
num = num / split;
return num % 1 === 0 ? cleanup(num) : 0;
}
}
return defaultValue;
}
export { rotateFromString };
@@ -0,0 +1,57 @@
'use strict';
const emoji_convert = require('./convert.cjs');
const emoji_data = require('./data.cjs');
function getEmojiSequenceFromString(value) {
return value.trim().split(/[^0-9A-F]+/i).filter((item) => item.length > 0).map(emoji_convert.getEmojiCodePoint);
}
function getSequenceFromEmojiStringOrKeyword(value) {
if (!value.match(/^[0-9a-fA-F-\s]+$/)) {
const results = [];
for (const codePoint of value) {
const code = codePoint.codePointAt(0);
if (code) {
results.push(code);
} else {
return getEmojiSequenceFromString(value);
}
}
return results;
}
return getEmojiSequenceFromString(value);
}
function splitEmojiSequences(sequence, separator = emoji_data.joinerEmoji) {
const results = [];
let queue = [];
for (let i = 0; i < sequence.length; i++) {
const code = sequence[i];
if (code === separator) {
results.push(queue);
queue = [];
} else {
queue.push(code);
}
}
results.push(queue);
return results;
}
function joinEmojiSequences(sequences, separator = emoji_data.joinerEmoji) {
let results = [];
for (let i = 0; i < sequences.length; i++) {
if (i > 0) {
results.push(separator);
}
results = results.concat(sequences[i]);
}
return results;
}
function getUnqualifiedEmojiSequence(sequence) {
return sequence.filter((num) => num !== emoji_data.vs16Emoji);
}
exports.getEmojiSequenceFromString = getEmojiSequenceFromString;
exports.getSequenceFromEmojiStringOrKeyword = getSequenceFromEmojiStringOrKeyword;
exports.getUnqualifiedEmojiSequence = getUnqualifiedEmojiSequence;
exports.joinEmojiSequences = joinEmojiSequences;
exports.splitEmojiSequences = splitEmojiSequences;
@@ -0,0 +1,42 @@
/**
* Get emoji sequence from string
*
* Examples (shows same emoji sequence formatted differently):
* '1F441 FE0F 200D 1F5E8 FE0F' => [0x1f441, 0xfe0f, 0x200d, 0x1f5e8, 0xfe0f]
* '1f441-fe0f-200d-1f5e8-fe0f' => [0x1f441, 0xfe0f, 0x200d, 0x1f5e8, 0xfe0f]
* '\\uD83D\\uDC41\\uFE0F\\u200D\\uD83D\\uDDE8\\uFE0F' => [0x1f441, 0xfe0f, 0x200d, 0x1f5e8, 0xfe0f]
*/
declare function getEmojiSequenceFromString(value: string): number[];
/**
* Convert emoji sequence or keyword
*
* If sequence is characters list, like '1f441-fe0f', it will be converted to [0x1f441, 0xfe0f]
* If sequence contains anything other than [0-9A-F-\s], it will be converted character by character
*
* This is used to treat keywords, like ':cat:' differently when converting strings to sequences
*/
declare function getSequenceFromEmojiStringOrKeyword(value: string): number[];
/**
* Split emoji sequence by joiner
*
* Result represents one emoji, split in smaller sequences separated by 0x200D
*
* Example:
* [0x1FAF1, 0x1F3FB, 0x200D, 0x1FAF2, 0x1F3FC] => [[0x1FAF1, 0x1F3FB], [0x1FAF2, 0x1F3FC]]
*/
declare function splitEmojiSequences(sequence: number[], separator?: number): number[][];
/**
* Join emoji sequences
*
* Parameter represents one emoji, split in smaller sequences
*
* Example:
* [[0x1FAF1, 0x1F3FB], [0x1FAF2, 0x1F3FC]] => [0x1FAF1, 0x1F3FB, 0x200D, 0x1FAF2, 0x1F3FC]
*/
declare function joinEmojiSequences(sequences: number[][], separator?: number): number[];
/**
* Get unqualified sequence
*/
declare function getUnqualifiedEmojiSequence(sequence: number[]): number[];
export { getEmojiSequenceFromString, getSequenceFromEmojiStringOrKeyword, getUnqualifiedEmojiSequence, joinEmojiSequences, splitEmojiSequences };
@@ -0,0 +1,42 @@
/**
* Get emoji sequence from string
*
* Examples (shows same emoji sequence formatted differently):
* '1F441 FE0F 200D 1F5E8 FE0F' => [0x1f441, 0xfe0f, 0x200d, 0x1f5e8, 0xfe0f]
* '1f441-fe0f-200d-1f5e8-fe0f' => [0x1f441, 0xfe0f, 0x200d, 0x1f5e8, 0xfe0f]
* '\\uD83D\\uDC41\\uFE0F\\u200D\\uD83D\\uDDE8\\uFE0F' => [0x1f441, 0xfe0f, 0x200d, 0x1f5e8, 0xfe0f]
*/
declare function getEmojiSequenceFromString(value: string): number[];
/**
* Convert emoji sequence or keyword
*
* If sequence is characters list, like '1f441-fe0f', it will be converted to [0x1f441, 0xfe0f]
* If sequence contains anything other than [0-9A-F-\s], it will be converted character by character
*
* This is used to treat keywords, like ':cat:' differently when converting strings to sequences
*/
declare function getSequenceFromEmojiStringOrKeyword(value: string): number[];
/**
* Split emoji sequence by joiner
*
* Result represents one emoji, split in smaller sequences separated by 0x200D
*
* Example:
* [0x1FAF1, 0x1F3FB, 0x200D, 0x1FAF2, 0x1F3FC] => [[0x1FAF1, 0x1F3FB], [0x1FAF2, 0x1F3FC]]
*/
declare function splitEmojiSequences(sequence: number[], separator?: number): number[][];
/**
* Join emoji sequences
*
* Parameter represents one emoji, split in smaller sequences
*
* Example:
* [[0x1FAF1, 0x1F3FB], [0x1FAF2, 0x1F3FC]] => [0x1FAF1, 0x1F3FB, 0x200D, 0x1FAF2, 0x1F3FC]
*/
declare function joinEmojiSequences(sequences: number[][], separator?: number): number[];
/**
* Get unqualified sequence
*/
declare function getUnqualifiedEmojiSequence(sequence: number[]): number[];
export { getEmojiSequenceFromString, getSequenceFromEmojiStringOrKeyword, getUnqualifiedEmojiSequence, joinEmojiSequences, splitEmojiSequences };
@@ -0,0 +1,42 @@
/**
* Get emoji sequence from string
*
* Examples (shows same emoji sequence formatted differently):
* '1F441 FE0F 200D 1F5E8 FE0F' => [0x1f441, 0xfe0f, 0x200d, 0x1f5e8, 0xfe0f]
* '1f441-fe0f-200d-1f5e8-fe0f' => [0x1f441, 0xfe0f, 0x200d, 0x1f5e8, 0xfe0f]
* '\\uD83D\\uDC41\\uFE0F\\u200D\\uD83D\\uDDE8\\uFE0F' => [0x1f441, 0xfe0f, 0x200d, 0x1f5e8, 0xfe0f]
*/
declare function getEmojiSequenceFromString(value: string): number[];
/**
* Convert emoji sequence or keyword
*
* If sequence is characters list, like '1f441-fe0f', it will be converted to [0x1f441, 0xfe0f]
* If sequence contains anything other than [0-9A-F-\s], it will be converted character by character
*
* This is used to treat keywords, like ':cat:' differently when converting strings to sequences
*/
declare function getSequenceFromEmojiStringOrKeyword(value: string): number[];
/**
* Split emoji sequence by joiner
*
* Result represents one emoji, split in smaller sequences separated by 0x200D
*
* Example:
* [0x1FAF1, 0x1F3FB, 0x200D, 0x1FAF2, 0x1F3FC] => [[0x1FAF1, 0x1F3FB], [0x1FAF2, 0x1F3FC]]
*/
declare function splitEmojiSequences(sequence: number[], separator?: number): number[][];
/**
* Join emoji sequences
*
* Parameter represents one emoji, split in smaller sequences
*
* Example:
* [[0x1FAF1, 0x1F3FB], [0x1FAF2, 0x1F3FC]] => [0x1FAF1, 0x1F3FB, 0x200D, 0x1FAF2, 0x1F3FC]
*/
declare function joinEmojiSequences(sequences: number[][], separator?: number): number[];
/**
* Get unqualified sequence
*/
declare function getUnqualifiedEmojiSequence(sequence: number[]): number[];
export { getEmojiSequenceFromString, getSequenceFromEmojiStringOrKeyword, getUnqualifiedEmojiSequence, joinEmojiSequences, splitEmojiSequences };
@@ -0,0 +1,51 @@
import { getEmojiCodePoint } from './convert.mjs';
import { joinerEmoji, vs16Emoji } from './data.mjs';
function getEmojiSequenceFromString(value) {
return value.trim().split(/[^0-9A-F]+/i).filter((item) => item.length > 0).map(getEmojiCodePoint);
}
function getSequenceFromEmojiStringOrKeyword(value) {
if (!value.match(/^[0-9a-fA-F-\s]+$/)) {
const results = [];
for (const codePoint of value) {
const code = codePoint.codePointAt(0);
if (code) {
results.push(code);
} else {
return getEmojiSequenceFromString(value);
}
}
return results;
}
return getEmojiSequenceFromString(value);
}
function splitEmojiSequences(sequence, separator = joinerEmoji) {
const results = [];
let queue = [];
for (let i = 0; i < sequence.length; i++) {
const code = sequence[i];
if (code === separator) {
results.push(queue);
queue = [];
} else {
queue.push(code);
}
}
results.push(queue);
return results;
}
function joinEmojiSequences(sequences, separator = joinerEmoji) {
let results = [];
for (let i = 0; i < sequences.length; i++) {
if (i > 0) {
results.push(separator);
}
results = results.concat(sequences[i]);
}
return results;
}
function getUnqualifiedEmojiSequence(sequence) {
return sequence.filter((num) => num !== vs16Emoji);
}
export { getEmojiSequenceFromString, getSequenceFromEmojiStringOrKeyword, getUnqualifiedEmojiSequence, joinEmojiSequences, splitEmojiSequences };
@@ -0,0 +1,92 @@
'use strict';
const emoji_data = require('./data.cjs');
function getEmojiCodePoint(code) {
return parseInt(code, 16);
}
function utf32FirstNum(code) {
return (code - emoji_data.minUTF32 >> 10 | 0) + emoji_data.startUTF32Pair1;
}
function utf32SecondNum(code) {
return (code - emoji_data.minUTF32 & 1023) + emoji_data.startUTF32Pair2;
}
function splitUTF32Number(code) {
if (code >= emoji_data.minUTF32) {
return [utf32FirstNum(code), utf32SecondNum(code)];
}
}
function isUTF32SplitNumber(value) {
if (value >= emoji_data.startUTF32Pair1) {
if (value < emoji_data.startUTF32Pair2) {
return 1;
}
if (value < emoji_data.endUTF32Pair) {
return 2;
}
}
return false;
}
function mergeUTF32Numbers(part1, part2) {
if (part1 < emoji_data.startUTF32Pair1 || part1 >= emoji_data.startUTF32Pair2 || part2 < emoji_data.startUTF32Pair2 || part2 >= emoji_data.endUTF32Pair) {
return;
}
return (part1 - emoji_data.startUTF32Pair1 << 10) + (part2 - emoji_data.startUTF32Pair2) + emoji_data.minUTF32;
}
function getEmojiUnicode(code) {
return String.fromCodePoint(
typeof code === "number" ? code : getEmojiCodePoint(code)
);
}
function convertEmojiSequenceToUTF16(numbers) {
const results = [];
for (let i = 0; i < numbers.length; i++) {
const code = numbers[i];
if (code >= emoji_data.minUTF32) {
results.push(utf32FirstNum(code));
results.push(utf32SecondNum(code));
} else {
results.push(code);
}
}
return results;
}
function convertEmojiSequenceToUTF32(numbers, throwOnError = true) {
const results = [];
for (let i = 0; i < numbers.length; i++) {
const code = numbers[i];
if (code >= emoji_data.minUTF32) {
results.push(code);
continue;
}
const part = isUTF32SplitNumber(code);
if (!part) {
results.push(code);
continue;
}
if (part === 1 && numbers.length > i + 1) {
const merged = mergeUTF32Numbers(code, numbers[i + 1]);
if (merged) {
i++;
results.push(merged);
continue;
}
}
if (throwOnError) {
const nextCode = numbers[i + 1];
throw new Error(
`Invalid UTF-16 sequence: ${code.toString(16)}-${nextCode ? nextCode.toString(16) : "undefined"}`
);
}
results.push(code);
}
return results;
}
exports.convertEmojiSequenceToUTF16 = convertEmojiSequenceToUTF16;
exports.convertEmojiSequenceToUTF32 = convertEmojiSequenceToUTF32;
exports.getEmojiCodePoint = getEmojiCodePoint;
exports.getEmojiUnicode = getEmojiUnicode;
exports.isUTF32SplitNumber = isUTF32SplitNumber;
exports.mergeUTF32Numbers = mergeUTF32Numbers;
exports.splitUTF32Number = splitUTF32Number;
@@ -0,0 +1,35 @@
/**
* Convert string to number
*/
declare function getEmojiCodePoint(code: string): number;
/**
* Get UTF-32 as UTF-16 sequence
*/
declare function splitUTF32Number(code: number): [number, number] | undefined;
/**
* Check if number is UTF-32 split as UTF-16
*
* Returns:
* - 1 if number fits first number in sequence
* - 2 if number fits second number in sequence
* - false on failure
*/
declare function isUTF32SplitNumber(value: number): 1 | 2 | false;
/**
* Get UTF-16 sequence as UTF-32
*/
declare function mergeUTF32Numbers(part1: number, part2: number): number | undefined;
/**
* Convert hexadecimal string or number to unicode
*/
declare function getEmojiUnicode(code: number | string): string;
/**
* Convert sequence to UTF-16
*/
declare function convertEmojiSequenceToUTF16(numbers: number[]): number[];
/**
* Convert sequence to UTF-32
*/
declare function convertEmojiSequenceToUTF32(numbers: number[], throwOnError?: boolean): number[];
export { convertEmojiSequenceToUTF16, convertEmojiSequenceToUTF32, getEmojiCodePoint, getEmojiUnicode, isUTF32SplitNumber, mergeUTF32Numbers, splitUTF32Number };
@@ -0,0 +1,35 @@
/**
* Convert string to number
*/
declare function getEmojiCodePoint(code: string): number;
/**
* Get UTF-32 as UTF-16 sequence
*/
declare function splitUTF32Number(code: number): [number, number] | undefined;
/**
* Check if number is UTF-32 split as UTF-16
*
* Returns:
* - 1 if number fits first number in sequence
* - 2 if number fits second number in sequence
* - false on failure
*/
declare function isUTF32SplitNumber(value: number): 1 | 2 | false;
/**
* Get UTF-16 sequence as UTF-32
*/
declare function mergeUTF32Numbers(part1: number, part2: number): number | undefined;
/**
* Convert hexadecimal string or number to unicode
*/
declare function getEmojiUnicode(code: number | string): string;
/**
* Convert sequence to UTF-16
*/
declare function convertEmojiSequenceToUTF16(numbers: number[]): number[];
/**
* Convert sequence to UTF-32
*/
declare function convertEmojiSequenceToUTF32(numbers: number[], throwOnError?: boolean): number[];
export { convertEmojiSequenceToUTF16, convertEmojiSequenceToUTF32, getEmojiCodePoint, getEmojiUnicode, isUTF32SplitNumber, mergeUTF32Numbers, splitUTF32Number };
@@ -0,0 +1,35 @@
/**
* Convert string to number
*/
declare function getEmojiCodePoint(code: string): number;
/**
* Get UTF-32 as UTF-16 sequence
*/
declare function splitUTF32Number(code: number): [number, number] | undefined;
/**
* Check if number is UTF-32 split as UTF-16
*
* Returns:
* - 1 if number fits first number in sequence
* - 2 if number fits second number in sequence
* - false on failure
*/
declare function isUTF32SplitNumber(value: number): 1 | 2 | false;
/**
* Get UTF-16 sequence as UTF-32
*/
declare function mergeUTF32Numbers(part1: number, part2: number): number | undefined;
/**
* Convert hexadecimal string or number to unicode
*/
declare function getEmojiUnicode(code: number | string): string;
/**
* Convert sequence to UTF-16
*/
declare function convertEmojiSequenceToUTF16(numbers: number[]): number[];
/**
* Convert sequence to UTF-32
*/
declare function convertEmojiSequenceToUTF32(numbers: number[], throwOnError?: boolean): number[];
export { convertEmojiSequenceToUTF16, convertEmojiSequenceToUTF32, getEmojiCodePoint, getEmojiUnicode, isUTF32SplitNumber, mergeUTF32Numbers, splitUTF32Number };
@@ -0,0 +1,84 @@
import { minUTF32, startUTF32Pair1, startUTF32Pair2, endUTF32Pair } from './data.mjs';
function getEmojiCodePoint(code) {
return parseInt(code, 16);
}
function utf32FirstNum(code) {
return (code - minUTF32 >> 10 | 0) + startUTF32Pair1;
}
function utf32SecondNum(code) {
return (code - minUTF32 & 1023) + startUTF32Pair2;
}
function splitUTF32Number(code) {
if (code >= minUTF32) {
return [utf32FirstNum(code), utf32SecondNum(code)];
}
}
function isUTF32SplitNumber(value) {
if (value >= startUTF32Pair1) {
if (value < startUTF32Pair2) {
return 1;
}
if (value < endUTF32Pair) {
return 2;
}
}
return false;
}
function mergeUTF32Numbers(part1, part2) {
if (part1 < startUTF32Pair1 || part1 >= startUTF32Pair2 || part2 < startUTF32Pair2 || part2 >= endUTF32Pair) {
return;
}
return (part1 - startUTF32Pair1 << 10) + (part2 - startUTF32Pair2) + minUTF32;
}
function getEmojiUnicode(code) {
return String.fromCodePoint(
typeof code === "number" ? code : getEmojiCodePoint(code)
);
}
function convertEmojiSequenceToUTF16(numbers) {
const results = [];
for (let i = 0; i < numbers.length; i++) {
const code = numbers[i];
if (code >= minUTF32) {
results.push(utf32FirstNum(code));
results.push(utf32SecondNum(code));
} else {
results.push(code);
}
}
return results;
}
function convertEmojiSequenceToUTF32(numbers, throwOnError = true) {
const results = [];
for (let i = 0; i < numbers.length; i++) {
const code = numbers[i];
if (code >= minUTF32) {
results.push(code);
continue;
}
const part = isUTF32SplitNumber(code);
if (!part) {
results.push(code);
continue;
}
if (part === 1 && numbers.length > i + 1) {
const merged = mergeUTF32Numbers(code, numbers[i + 1]);
if (merged) {
i++;
results.push(merged);
continue;
}
}
if (throwOnError) {
const nextCode = numbers[i + 1];
throw new Error(
`Invalid UTF-16 sequence: ${code.toString(16)}-${nextCode ? nextCode.toString(16) : "undefined"}`
);
}
results.push(code);
}
return results;
}
export { convertEmojiSequenceToUTF16, convertEmojiSequenceToUTF32, getEmojiCodePoint, getEmojiUnicode, isUTF32SplitNumber, mergeUTF32Numbers, splitUTF32Number };
@@ -0,0 +1,26 @@
'use strict';
const joinerEmoji = 8205;
const vs16Emoji = 65039;
const keycapEmoji = 8419;
const emojiComponents = {
// Hair styles
"hair-style": [129456, 129460],
// Skin tones
"skin-tone": [127995, 128e3]
};
const minUTF32 = 65536;
const startUTF32Pair1 = 55296;
const startUTF32Pair2 = 56320;
const endUTF32Pair = 57344;
const emojiVersion = "16.0";
exports.emojiComponents = emojiComponents;
exports.emojiVersion = emojiVersion;
exports.endUTF32Pair = endUTF32Pair;
exports.joinerEmoji = joinerEmoji;
exports.keycapEmoji = keycapEmoji;
exports.minUTF32 = minUTF32;
exports.startUTF32Pair1 = startUTF32Pair1;
exports.startUTF32Pair2 = startUTF32Pair2;
exports.vs16Emoji = vs16Emoji;
@@ -0,0 +1,33 @@
/**
* Various codes
*/
declare const joinerEmoji = 8205;
declare const vs16Emoji = 65039;
declare const keycapEmoji = 8419;
/**
* Variations, UTF-32
*
* First value in array is minimum, second value is maximum+1
*/
type EmojiComponentType = 'skin-tone' | 'hair-style';
type Range = [number, number];
declare const emojiComponents: Record<EmojiComponentType, Range>;
/**
* Minimum UTF-32 number
*/
declare const minUTF32 = 65536;
/**
* Codes for UTF-32 characters presented as UTF-16
*
* startUTF32Pair1 <= code < startUTF32Pair2 -> code for first character in pair
* startUTF32Pair2 <= code < endUTF32Pair -> code for second character in pair
*/
declare const startUTF32Pair1 = 55296;
declare const startUTF32Pair2 = 56320;
declare const endUTF32Pair = 57344;
/**
* Emoji version as string
*/
declare const emojiVersion = "16.0";
export { type EmojiComponentType, emojiComponents, emojiVersion, endUTF32Pair, joinerEmoji, keycapEmoji, minUTF32, startUTF32Pair1, startUTF32Pair2, vs16Emoji };
@@ -0,0 +1,33 @@
/**
* Various codes
*/
declare const joinerEmoji = 8205;
declare const vs16Emoji = 65039;
declare const keycapEmoji = 8419;
/**
* Variations, UTF-32
*
* First value in array is minimum, second value is maximum+1
*/
type EmojiComponentType = 'skin-tone' | 'hair-style';
type Range = [number, number];
declare const emojiComponents: Record<EmojiComponentType, Range>;
/**
* Minimum UTF-32 number
*/
declare const minUTF32 = 65536;
/**
* Codes for UTF-32 characters presented as UTF-16
*
* startUTF32Pair1 <= code < startUTF32Pair2 -> code for first character in pair
* startUTF32Pair2 <= code < endUTF32Pair -> code for second character in pair
*/
declare const startUTF32Pair1 = 55296;
declare const startUTF32Pair2 = 56320;
declare const endUTF32Pair = 57344;
/**
* Emoji version as string
*/
declare const emojiVersion = "16.0";
export { type EmojiComponentType, emojiComponents, emojiVersion, endUTF32Pair, joinerEmoji, keycapEmoji, minUTF32, startUTF32Pair1, startUTF32Pair2, vs16Emoji };
@@ -0,0 +1,33 @@
/**
* Various codes
*/
declare const joinerEmoji = 8205;
declare const vs16Emoji = 65039;
declare const keycapEmoji = 8419;
/**
* Variations, UTF-32
*
* First value in array is minimum, second value is maximum+1
*/
type EmojiComponentType = 'skin-tone' | 'hair-style';
type Range = [number, number];
declare const emojiComponents: Record<EmojiComponentType, Range>;
/**
* Minimum UTF-32 number
*/
declare const minUTF32 = 65536;
/**
* Codes for UTF-32 characters presented as UTF-16
*
* startUTF32Pair1 <= code < startUTF32Pair2 -> code for first character in pair
* startUTF32Pair2 <= code < endUTF32Pair -> code for second character in pair
*/
declare const startUTF32Pair1 = 55296;
declare const startUTF32Pair2 = 56320;
declare const endUTF32Pair = 57344;
/**
* Emoji version as string
*/
declare const emojiVersion = "16.0";
export { type EmojiComponentType, emojiComponents, emojiVersion, endUTF32Pair, joinerEmoji, keycapEmoji, minUTF32, startUTF32Pair1, startUTF32Pair2, vs16Emoji };

Some files were not shown because too many files have changed in this diff Show More