Initial commit
This commit is contained in:
21
node_modules/unifont/LICENCE
generated
vendored
Normal file
21
node_modules/unifont/LICENCE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Daniel Roe
|
||||
|
||||
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.
|
||||
665
node_modules/unifont/README.md
generated
vendored
Normal file
665
node_modules/unifont/README.md
generated
vendored
Normal file
@@ -0,0 +1,665 @@
|
||||
# unifont
|
||||
|
||||
[![npm version][npm-version-src]][npm-version-href]
|
||||
[![npm downloads][npm-downloads-src]][npm-downloads-href]
|
||||
[![Github Actions][github-actions-src]][github-actions-href]
|
||||
[![Codecov][codecov-src]][codecov-href]
|
||||
|
||||
> Framework agnostic tools for accessing data from font CDNs and providers.
|
||||
|
||||
## Installation
|
||||
|
||||
Using npm:
|
||||
|
||||
```
|
||||
npm i unifont
|
||||
```
|
||||
|
||||
Using pnpm:
|
||||
|
||||
```
|
||||
pnpm add unifont
|
||||
```
|
||||
|
||||
Using yarn:
|
||||
|
||||
```
|
||||
yarn add unifont
|
||||
```
|
||||
|
||||
## Getting started
|
||||
|
||||
This package is ESM-only.
|
||||
|
||||
```js
|
||||
import { createUnifont, providers } from 'unifont'
|
||||
|
||||
const unifont = await createUnifont([
|
||||
providers.google(),
|
||||
])
|
||||
|
||||
const availableFonts = await unifont.listFonts()
|
||||
const { fonts } = await unifont.resolveFont('Poppins')
|
||||
```
|
||||
|
||||
## Built-in providers
|
||||
|
||||
The following providers are built-in but you can build [custom providers](#building-your-own-provider) too.
|
||||
|
||||
### Adobe
|
||||
|
||||
A provider for [Adobe Fonts](https://fonts.adobe.com/).
|
||||
|
||||
```js
|
||||
import { providers } from 'unifont'
|
||||
|
||||
providers.adobe({ /* options */ })
|
||||
```
|
||||
|
||||
#### Options
|
||||
|
||||
##### `id`
|
||||
|
||||
- Type: `string | string[]`
|
||||
- Required
|
||||
|
||||
```js
|
||||
import { providers } from 'unifont'
|
||||
|
||||
providers.adobe({ id: 'your-id' })
|
||||
providers.adobe({ id: ['foo', 'bar'] })
|
||||
```
|
||||
|
||||
It is recommended to load these IDs as environment variables.
|
||||
|
||||
### Bunny
|
||||
|
||||
A provider for [Bunny Fonts](https://fonts.bunny.net/).
|
||||
|
||||
```js
|
||||
import { providers } from 'unifont'
|
||||
|
||||
providers.bunny()
|
||||
```
|
||||
|
||||
### Fontshare
|
||||
|
||||
A provider for [Fontshare](https://www.fontshare.com/).
|
||||
|
||||
```js
|
||||
import { providers } from 'unifont'
|
||||
|
||||
providers.fontshare()
|
||||
```
|
||||
|
||||
### Fontsource
|
||||
|
||||
A provider for [Fontsource](https://fontsource.org/).
|
||||
|
||||
```js
|
||||
import { providers } from 'unifont'
|
||||
|
||||
providers.fontsource()
|
||||
```
|
||||
|
||||
It uses the API, not installed NPM packages (see [PR #189](https://github.com/unjs/unifont/pull/189)).
|
||||
|
||||
### Google
|
||||
|
||||
A provider for [Google Fonts](https://fonts.google.com/).
|
||||
|
||||
```js
|
||||
import { providers } from 'unifont'
|
||||
|
||||
providers.google()
|
||||
```
|
||||
|
||||
#### Options
|
||||
|
||||
##### `experimental.variableAxis`
|
||||
|
||||
- Type: `{ [fontFamily: string]: Partial<Record<VariableAxis, ([string, string] | string)[]>> }`
|
||||
|
||||
Allows setting variable axis configuration on a per-font basis:
|
||||
|
||||
```js
|
||||
import { providers } from 'unifont'
|
||||
|
||||
providers.google({
|
||||
experimental: {
|
||||
variableAxis: {
|
||||
Poppins: {
|
||||
slnt: [['-15', '0']],
|
||||
CASL: [['0', '1']],
|
||||
CRSV: ['1'],
|
||||
MONO: [['0', '1']],
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
Overriden by the `experimental.variableAxis` family option.
|
||||
|
||||
##### `experimental.glyphs`
|
||||
|
||||
- Type: `{ [fontFamily: string]: string[] }`
|
||||
|
||||
Allows specifying a list of glyphs to be included in the font for each font family. This can reduce the size of the font file:
|
||||
|
||||
```js
|
||||
import { providers } from 'unifont'
|
||||
|
||||
providers.google({
|
||||
experimental: {
|
||||
glyphs: {
|
||||
Poppins: ['Hello', 'World']
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
Overriden by the `experimental.glyphs` family option.
|
||||
|
||||
#### Family options
|
||||
|
||||
##### `experimental.variableAxis`
|
||||
|
||||
- Type: `Partial<Record<VariableAxis, ([string, string] | string)[]>>`
|
||||
|
||||
Allows setting variable axis configuration on a per-font basis:
|
||||
|
||||
```js
|
||||
import { createUnifont, providers } from 'unifont'
|
||||
|
||||
const unifont = await createUnifont([
|
||||
providers.google(),
|
||||
])
|
||||
|
||||
const { fonts } = await unifont.resolveFont('Poppins', {
|
||||
options: {
|
||||
google: {
|
||||
experimental: {
|
||||
variableAxis: {
|
||||
slnt: [['-15', '0']],
|
||||
CASL: [['0', '1']],
|
||||
CRSV: ['1'],
|
||||
MONO: [['0', '1']],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
##### `experimental.glyphs`
|
||||
|
||||
- Type: `string[]`
|
||||
|
||||
Allows specifying a list of glyphs to be included in the font for each font family. This can reduce the size of the font file:
|
||||
|
||||
```js
|
||||
import { createUnifont, providers } from 'unifont'
|
||||
|
||||
const unifont = await createUnifont([
|
||||
providers.google(),
|
||||
])
|
||||
|
||||
const { fonts } = await unifont.resolveFont('Poppins', {
|
||||
options: {
|
||||
google: {
|
||||
experimental: {
|
||||
glyphs: ['Hello', 'World'],
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Google icons
|
||||
|
||||
A provider for [Google Icons](https://fonts.google.com/icons).
|
||||
|
||||
```js
|
||||
import { providers } from 'unifont'
|
||||
|
||||
providers.googleicons()
|
||||
```
|
||||
|
||||
#### Options
|
||||
|
||||
##### `experimental.glyphs`
|
||||
|
||||
- Type: `{ [fontFamily: string]: string[] }`
|
||||
|
||||
Allows specifying a list of glyphs to be included in the font for each font family. This can reduce the size of the font file:
|
||||
|
||||
```js
|
||||
import { providers } from 'unifont'
|
||||
|
||||
providers.googleicons({
|
||||
experimental: {
|
||||
glyphs: {
|
||||
'Material Symbols Outlined': ['arrow_right', 'favorite', 'arrow_drop_down']
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
Only available when resolving the new `Material Symbols` icons. Overriden by the `experimental.glyphs` family option.
|
||||
|
||||
#### Family options
|
||||
|
||||
##### `experimental.glyphs`
|
||||
|
||||
- Type: `string[]`
|
||||
|
||||
Allows specifying a list of glyphs to be included in the font for each font family. This can reduce the size of the font file:
|
||||
|
||||
```js
|
||||
import { createUnifont, providers } from 'unifont'
|
||||
|
||||
const unifont = await createUnifont([
|
||||
providers.googleicons(),
|
||||
])
|
||||
|
||||
const { fonts } = await unifont.resolveFont('Poppins', {
|
||||
options: {
|
||||
googleicons: {
|
||||
experimental: {
|
||||
'Material Symbols Outlined': ['arrow_right', 'favorite', 'arrow_drop_down']
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
Only available when resolving the new `Material Symbols` icons.
|
||||
|
||||
## `Unifont`
|
||||
|
||||
Use `createUnifont()` to create a `Unifont` instance. It requires an array of font providers at this first parameter:
|
||||
|
||||
```js
|
||||
import { createUnifont, providers } from 'unifont'
|
||||
|
||||
const unifont = await createUnifont([
|
||||
providers.google(),
|
||||
])
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
`createUnifont()` accepts options as its 2nd parameter.
|
||||
|
||||
#### `storage`
|
||||
|
||||
- `Type`: `Storage`
|
||||
|
||||
Allows caching the results of font APIs to avoid unnecessary hits to them. Uses a memory cache by default.
|
||||
|
||||
This storage type is compatible with [`unstorage`](https://unstorage.unjs.io.):
|
||||
|
||||
```ts
|
||||
import { createUnifont, providers } from 'unifont'
|
||||
import { createStorage } from 'unstorage'
|
||||
import fsDriver from 'unstorage/drivers/fs-lite'
|
||||
|
||||
const storage = createStorage({
|
||||
driver: fsDriver({ base: 'node_modules/.cache/unifont' }),
|
||||
})
|
||||
|
||||
const unifont = await createUnifont([
|
||||
providers.google()
|
||||
], { storage })
|
||||
|
||||
// cached data is stored in `node_modules/.cache/unifont`
|
||||
await unifont.resolveFont('Poppins')
|
||||
```
|
||||
|
||||
#### `throwOnError`
|
||||
|
||||
- Type: `boolean`
|
||||
|
||||
Allows throwing on error if a font provider:
|
||||
|
||||
- Fails to initialize
|
||||
- Fails while calling `resolveFont()`
|
||||
- Fails while calling `listFonts()`
|
||||
|
||||
If set to `false` (default), an error will be logged to the console instead:
|
||||
|
||||
```ts
|
||||
import { createUnifont, providers } from 'unifont'
|
||||
|
||||
const unifont = await createUnifont([
|
||||
providers.google()
|
||||
], { throwOnError: true })
|
||||
```
|
||||
|
||||
### Methods
|
||||
|
||||
#### `resolveFont()`
|
||||
|
||||
- Type: `(fontFamily: string, options?: Partial<ResolveFontOptions>, providers?: T[]) => Promise<ResolveFontResult & { provider?: T }>`
|
||||
|
||||
Retrieves font face data from available providers:
|
||||
|
||||
```js
|
||||
import { createUnifont, providers } from 'unifont'
|
||||
|
||||
const unifont = await createUnifont([
|
||||
providers.google(),
|
||||
providers.fontsource(),
|
||||
])
|
||||
|
||||
const { fonts } = await unifont.resolveFont('Poppins')
|
||||
```
|
||||
|
||||
It loops through all providers and returns the result of the first provider that can return some data.
|
||||
|
||||
##### Options
|
||||
|
||||
It accepts options as the 2nd parameter. Each provider chooses to support them or not.
|
||||
|
||||
###### `weights`
|
||||
|
||||
- Type: `string[]`
|
||||
- Default: `['400']`
|
||||
|
||||
Specifies what weights to retrieve. Variable weights must me in the format `<min> <max>`:
|
||||
|
||||
```js
|
||||
import { createUnifont, providers } from 'unifont'
|
||||
|
||||
const unifont = await createUnifont([
|
||||
providers.google(),
|
||||
])
|
||||
|
||||
const { fonts } = await unifont.resolveFont('Poppins', {
|
||||
weights: ['300', '500 900']
|
||||
})
|
||||
```
|
||||
|
||||
###### `styles`
|
||||
|
||||
- Type: `('normal' | 'italic' | 'oblique')[]`
|
||||
- Default: `['normal', 'italic']`
|
||||
|
||||
Specifies what styles to retrieve:
|
||||
|
||||
```js
|
||||
import { createUnifont, providers } from 'unifont'
|
||||
|
||||
const unifont = await createUnifont([
|
||||
providers.google(),
|
||||
])
|
||||
|
||||
const { fonts } = await unifont.resolveFont('Poppins', {
|
||||
styles: ['normal']
|
||||
})
|
||||
```
|
||||
|
||||
###### `subsets`
|
||||
|
||||
- Type: `string[]`
|
||||
- Default: `['cyrillic-ext', 'cyrillic', 'greek-ext', 'greek', 'vietnamese', 'latin-ext', 'latin']`
|
||||
|
||||
Specifies what subsets to retrieve:
|
||||
|
||||
```js
|
||||
import { createUnifont, providers } from 'unifont'
|
||||
|
||||
const unifont = await createUnifont([
|
||||
providers.google(),
|
||||
])
|
||||
|
||||
const { fonts } = await unifont.resolveFont('Poppins', {
|
||||
subsets: ['latin']
|
||||
})
|
||||
```
|
||||
|
||||
###### `options`
|
||||
|
||||
- Type: `{ [key: string]?: Record<string, any> }`
|
||||
|
||||
A provider can define options to provide on a font family basis. Types will be automatically inferred:
|
||||
|
||||
```js
|
||||
import { createUnifont, providers } from 'unifont'
|
||||
|
||||
const unifont = await createUnifont([
|
||||
providers.google(),
|
||||
])
|
||||
|
||||
const { fonts } = await unifont.resolveFont('Poppins', {
|
||||
options: {
|
||||
google: {
|
||||
experimental: {
|
||||
glyphs: ['Hello', 'World']
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
###### `formats`
|
||||
|
||||
- Type: `('woff2' | 'woff' | 'otf' | 'ttf' | 'eot')[]`
|
||||
- Default: `['woff2']`
|
||||
|
||||
Specifies what font formats to retrieve:
|
||||
|
||||
```js
|
||||
import { createUnifont, providers } from 'unifont'
|
||||
|
||||
const unifont = await createUnifont([
|
||||
providers.google(),
|
||||
])
|
||||
|
||||
const { fonts } = await unifont.resolveFont('Poppins', {
|
||||
formats: ['woff2', 'woff2']
|
||||
})
|
||||
```
|
||||
|
||||
##### Providers
|
||||
|
||||
- Type: `string[]`
|
||||
|
||||
By default it uses all the providers provided to `createUnifont()`. However you can restrict usage to only a subset:
|
||||
|
||||
```js
|
||||
import { createUnifont, providers } from 'unifont'
|
||||
|
||||
const unifont = await createUnifont([
|
||||
providers.google(),
|
||||
providers.fontsource(),
|
||||
])
|
||||
|
||||
const { fonts } = await unifont.resolveFont('Poppins', {}, ['google'])
|
||||
```
|
||||
|
||||
#### `listFonts()`
|
||||
|
||||
- Type: `(providers?: T[]) => Promise<string[] | undefined>`
|
||||
|
||||
Retrieves font names available for all providers:
|
||||
|
||||
```js
|
||||
import { createUnifont, providers } from 'unifont'
|
||||
|
||||
const unifont = await createUnifont([
|
||||
providers.google(),
|
||||
])
|
||||
|
||||
const availableFonts = await unifont.listFont()
|
||||
```
|
||||
|
||||
It may return `undefined` if no provider is able to return names.
|
||||
|
||||
##### Providers
|
||||
|
||||
- Type: `string[]`
|
||||
|
||||
By default it uses all the providers provided to `createUnifont()`. However you can restrict usage to only a subset:
|
||||
|
||||
```js
|
||||
import { createUnifont, providers } from 'unifont'
|
||||
|
||||
const unifont = await createUnifont([
|
||||
providers.google(),
|
||||
providers.fontsource(),
|
||||
])
|
||||
|
||||
const availableFonts = await unifont.listFont(['google'])
|
||||
```
|
||||
|
||||
## Building your own provider
|
||||
|
||||
### Defining a provider
|
||||
|
||||
To build your own font provider, use the `defineFontProvider()` helper:
|
||||
|
||||
```ts
|
||||
import { defineFontProvider } from 'unifont'
|
||||
|
||||
export const myProvider = defineFontProvider(/* ... */)
|
||||
```
|
||||
|
||||
It accepts a unique name as a first argument and a callback function as 2nd argument:
|
||||
|
||||
```ts
|
||||
import { defineFontProvider } from 'unifont'
|
||||
|
||||
export const myProvider = defineFontProvider('my-provider', async (options, ctx) => {
|
||||
// ...
|
||||
})
|
||||
```
|
||||
|
||||
If you use options, you can simply annotate it:
|
||||
|
||||
```ts
|
||||
import { defineFontProvider } from 'unifont'
|
||||
|
||||
export interface MyProviderOptions {
|
||||
foo?: string
|
||||
}
|
||||
|
||||
export const myProvider = defineFontProvider('my-provider', async (options: MyProviderOptions, ctx) => {
|
||||
// ...
|
||||
})
|
||||
```
|
||||
|
||||
The context (`ctx`) gives access to the [`storage`](#storage), allowing you to cache results. We'll see how below.
|
||||
|
||||
### Initialization
|
||||
|
||||
The callback runs when a `Unifont` instance is created. It is used for initialization logic, such as fetching the list of available fonts:
|
||||
|
||||
```ts
|
||||
import { defineFontProvider } from 'unifont'
|
||||
|
||||
export const myProvider = defineFontProvider('my-provider', async (options, ctx) => {
|
||||
const fonts: { name: string, cssUrl: string }[] = await ctx.storage.getItem('my-provider:meta.json', () => fetch('https://api.example.com/fonts.json').then(res => res.json()))
|
||||
|
||||
// ...
|
||||
})
|
||||
```
|
||||
|
||||
You can now use this data in the methods.
|
||||
|
||||
### `listFonts()`
|
||||
|
||||
While optional, it's easy to implement this method now that we have the full list:
|
||||
|
||||
```ts
|
||||
import { defineFontProvider } from 'unifont'
|
||||
|
||||
export const myProvider = defineFontProvider('my-provider', async (options, ctx) => {
|
||||
const fonts: { name: string, cssUrl: string }[] = [/* ... */]
|
||||
|
||||
return {
|
||||
listFonts() {
|
||||
return fonts.map(font => font.name)
|
||||
}
|
||||
// ...
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### `resolveFont()`
|
||||
|
||||
This is where most of the logic lies. It depends a lot on how the provider works, and often involves parsing CSS files. Have a look at the implementation of built-in providers for inspiration!
|
||||
|
||||
```ts
|
||||
import { hash } from 'ohash'
|
||||
import { defineFontProvider } from 'unifont'
|
||||
|
||||
export const myProvider = defineFontProvider('my-provider', async (options, ctx) => {
|
||||
const fonts: { name: string, cssUrl: string }[] = [/* ... */]
|
||||
|
||||
return {
|
||||
// ...
|
||||
async resolveFont(fontFamily, options) {
|
||||
const font = fonts.find(font => font.name === fontFamily)
|
||||
if (!font) {
|
||||
return
|
||||
}
|
||||
|
||||
return {
|
||||
fonts: await ctx.storage.getItem(`my-provider:${fontFamily}-${hash(options)}-data.json`, async () => {
|
||||
// Fetch an API, extract CSS...
|
||||
return [/* ... */]
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
If you use family options, you can override the type of `options` and it will be inferred:
|
||||
|
||||
```ts
|
||||
import type { ResolveFontOptions } from 'unifont'
|
||||
import { hash } from 'ohash'
|
||||
import { defineFontProvider } from 'unifont'
|
||||
|
||||
export interface MyProviderFamilyOptions {
|
||||
foo?: string
|
||||
}
|
||||
|
||||
export const myProvider = defineFontProvider('my-provider', async (options, ctx) => {
|
||||
// ...
|
||||
|
||||
return {
|
||||
// ...
|
||||
async resolveFont(fontFamily, options: ResolveFontOptions<MyProviderFamilyOptions>) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## 💻 Development
|
||||
|
||||
- Clone this repository
|
||||
- Enable [Corepack](https://github.com/nodejs/corepack) using `corepack enable`
|
||||
- Install dependencies using `pnpm install`
|
||||
- Run interactive tests using `pnpm dev`
|
||||
|
||||
## License
|
||||
|
||||
Made with ❤️
|
||||
|
||||
Published under [MIT License](./LICENCE).
|
||||
|
||||
<!-- Badges -->
|
||||
|
||||
[npm-version-src]: https://img.shields.io/npm/v/unifont?style=flat-square
|
||||
[npm-version-href]: https://npmjs.com/package/unifont
|
||||
[npm-downloads-src]: https://img.shields.io/npm/dm/unifont?style=flat-square
|
||||
[npm-downloads-href]: https://npm.chart.dev/unifont
|
||||
[github-actions-src]: https://img.shields.io/github/actions/workflow/status/unjs/unifont/ci.yml?branch=main&style=flat-square
|
||||
[github-actions-href]: https://github.com/unjs/unifont/actions?query=workflow%3Aci
|
||||
[codecov-src]: https://img.shields.io/codecov/c/gh/unjs/unifont/main?style=flat-square
|
||||
[codecov-href]: https://codecov.io/gh/unjs/unifont
|
||||
18
node_modules/unifont/dist/chunk-15K8U1wQ.mjs
generated
vendored
Normal file
18
node_modules/unifont/dist/chunk-15K8U1wQ.mjs
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
//#region rolldown:runtime
|
||||
var __defProp = Object.defineProperty;
|
||||
var __exportAll = (all, symbols) => {
|
||||
let target = {};
|
||||
for (var name in all) {
|
||||
__defProp(target, name, {
|
||||
get: all[name],
|
||||
enumerable: true
|
||||
});
|
||||
}
|
||||
if (symbols) {
|
||||
__defProp(target, Symbol.toStringTag, { value: "Module" });
|
||||
}
|
||||
return target;
|
||||
};
|
||||
|
||||
//#endregion
|
||||
export { __exportAll as t };
|
||||
206
node_modules/unifont/dist/index.d.mts
generated
vendored
Normal file
206
node_modules/unifont/dist/index.d.mts
generated
vendored
Normal file
@@ -0,0 +1,206 @@
|
||||
//#region src/utils.d.ts
|
||||
declare function defineFontProvider<Name extends string, Provider$1 extends ProviderDefinition<never, never>>(name: Name, provider: Provider$1): Provider$1 extends ProviderDefinition<infer Options, infer FamilyOptions> ? ProviderFactory<Name, Options, FamilyOptions> : never;
|
||||
declare const formatMap: {
|
||||
woff2: string;
|
||||
woff: string;
|
||||
otf: string;
|
||||
ttf: string;
|
||||
eot: string;
|
||||
};
|
||||
//#endregion
|
||||
//#region src/types.d.ts
|
||||
type Awaitable$1<T> = T | Promise<T>;
|
||||
interface ProviderContext {
|
||||
storage: {
|
||||
getItem: {
|
||||
<T = unknown>(key: string): Promise<T | null>;
|
||||
<T = unknown>(key: string, init: () => Awaitable$1<T>): Promise<T>;
|
||||
};
|
||||
setItem: (key: string, value: unknown) => Awaitable$1<void>;
|
||||
};
|
||||
}
|
||||
type FontStyles = 'normal' | 'italic' | 'oblique';
|
||||
type FontFormat = keyof typeof formatMap;
|
||||
interface ResolveFontOptions<FamilyOptions$1 extends Record<string, any> | never = never> {
|
||||
weights: string[];
|
||||
styles: FontStyles[];
|
||||
subsets: string[];
|
||||
formats: FontFormat[];
|
||||
options?: [FamilyOptions$1] extends [never] ? undefined : FamilyOptions$1;
|
||||
}
|
||||
interface RemoteFontSource {
|
||||
url: string;
|
||||
originalURL?: string;
|
||||
format?: string;
|
||||
tech?: string;
|
||||
}
|
||||
interface LocalFontSource {
|
||||
name: string;
|
||||
}
|
||||
interface FontFaceMeta {
|
||||
/** The priority of the font face, usually used to indicate fallbacks. Smaller is more prioritized. */
|
||||
priority?: number;
|
||||
/**
|
||||
* The subset name of the font face. Many fonts provides font subsets such as latin, latin-ext, cyrillic, etc.
|
||||
*/
|
||||
subset?: string;
|
||||
/**
|
||||
* A `RequestInit` object that should be used when fetching this font. This can be useful for
|
||||
* adding authorization headers and other metadata required for a font request.
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/RequestInit
|
||||
*/
|
||||
init?: RequestInit;
|
||||
}
|
||||
interface FontFaceData {
|
||||
src: Array<LocalFontSource | RemoteFontSource>;
|
||||
/**
|
||||
* The font-display descriptor.
|
||||
* @default 'swap'
|
||||
*/
|
||||
display?: 'auto' | 'block' | 'swap' | 'fallback' | 'optional';
|
||||
/** A font-weight value. */
|
||||
weight?: string | number | [number, number];
|
||||
/** A font-stretch value. */
|
||||
stretch?: string;
|
||||
/** A font-style value. */
|
||||
style?: string;
|
||||
/** The range of Unicode code points to be used from the font. */
|
||||
unicodeRange?: string[];
|
||||
/** Allows control over advanced typographic features in OpenType fonts. */
|
||||
featureSettings?: string;
|
||||
/** Allows low-level control over OpenType or TrueType font variations, by specifying the four letter axis names of the features to vary, along with their variation values. */
|
||||
variationSettings?: string;
|
||||
/** Metadata for the font face used by unifont */
|
||||
meta?: FontFaceMeta;
|
||||
}
|
||||
interface ResolveFontResult {
|
||||
/**
|
||||
* Return data used to generate @font-face declarations.
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face
|
||||
*/
|
||||
fonts: FontFaceData[];
|
||||
fallbacks?: string[];
|
||||
}
|
||||
interface InitializedProvider<FamilyOptions$1 extends Record<string, any> = never> {
|
||||
resolveFont: (family: string, options: ResolveFontOptions<FamilyOptions$1>) => Awaitable$1<ResolveFontResult | undefined>;
|
||||
listFonts?: (() => Awaitable$1<string[] | undefined>) | undefined;
|
||||
}
|
||||
interface ProviderDefinition<Options$1 extends Record<string, any> = never, FamilyOptions$1 extends Record<string, any> = never> {
|
||||
(options: Options$1, ctx: ProviderContext): Awaitable$1<InitializedProvider<FamilyOptions$1> | undefined>;
|
||||
}
|
||||
interface Provider<Name extends string = string, FamilyOptions$1 extends Record<string, any> = never> {
|
||||
_name: Name;
|
||||
_options: unknown;
|
||||
(ctx: ProviderContext): Awaitable$1<InitializedProvider<FamilyOptions$1> | undefined>;
|
||||
}
|
||||
type ProviderFactory<Name extends string, Options$1 extends Record<string, any> = never, FamilyOptions$1 extends Record<string, any> = never> = [Options$1] extends [never] ? () => Provider<Name, FamilyOptions$1> : Partial<Options$1> extends Options$1 ? (options?: Options$1) => Provider<Name, FamilyOptions$1> : (options: Options$1) => Provider<Name, FamilyOptions$1>;
|
||||
//#endregion
|
||||
//#region src/providers/google.d.ts
|
||||
type VariableAxis = 'opsz' | 'slnt' | 'wdth' | (string & {});
|
||||
interface GoogleProviderOptions {
|
||||
experimental?: {
|
||||
/**
|
||||
* Experimental: Setting variable axis configuration on a per-font basis.
|
||||
*/
|
||||
variableAxis?: {
|
||||
[fontFamily: string]: Partial<Record<VariableAxis, ([string, string] | string)[]>>;
|
||||
};
|
||||
/**
|
||||
* Experimental: Specifying a list of glyphs to be included in the font for each font family.
|
||||
* This can reduce the size of the font file.
|
||||
*/
|
||||
glyphs?: {
|
||||
[fontFamily: string]: string[];
|
||||
};
|
||||
};
|
||||
}
|
||||
interface GoogleFamilyOptions {
|
||||
experimental?: {
|
||||
/**
|
||||
* Experimental: Setting variable axis configuration on a per-font basis.
|
||||
*/
|
||||
variableAxis?: Partial<Record<VariableAxis, ([string, string] | string)[]>>;
|
||||
/**
|
||||
* Experimental: Specifying a list of glyphs to be included in the font for each font family.
|
||||
* This can reduce the size of the font file.
|
||||
*/
|
||||
glyphs?: string[];
|
||||
};
|
||||
}
|
||||
declare const _default$5: (options?: GoogleProviderOptions | undefined) => Provider<"google", GoogleFamilyOptions>;
|
||||
//#endregion
|
||||
//#region src/providers/googleicons.d.ts
|
||||
interface GoogleiconsProviderOptions {
|
||||
experimental?: {
|
||||
/**
|
||||
* Experimental: Specifying a list of icons to be included in the font for each font family.
|
||||
* This can reduce the size of the font file.
|
||||
*
|
||||
* **Only available when resolving the new `Material Symbols` icons.**
|
||||
*/
|
||||
glyphs?: {
|
||||
[fontFamily: string]: string[];
|
||||
};
|
||||
};
|
||||
}
|
||||
interface GoogleiconsFamilyOptions {
|
||||
experimental?: {
|
||||
/**
|
||||
* Experimental: Specifying a list of icons to be included in the font for each font family.
|
||||
* This can reduce the size of the font file.
|
||||
*
|
||||
* **Only available when resolving the new `Material Symbols` icons.**
|
||||
*/
|
||||
glyphs?: string[];
|
||||
};
|
||||
}
|
||||
declare const _default$4: (options?: GoogleiconsProviderOptions | undefined) => Provider<"googleicons", GoogleiconsFamilyOptions>;
|
||||
//#endregion
|
||||
//#region src/providers/adobe.d.ts
|
||||
interface AdobeProviderOptions {
|
||||
id: string[] | string;
|
||||
}
|
||||
declare const _default$3: (options: AdobeProviderOptions) => Provider<"adobe", never>;
|
||||
//#endregion
|
||||
//#region src/providers/bunny.d.ts
|
||||
declare const _default$2: () => Provider<"bunny", never>;
|
||||
//#endregion
|
||||
//#region src/providers/fontshare.d.ts
|
||||
declare const _default$1: () => Provider<"fontshare", never>;
|
||||
//#endregion
|
||||
//#region src/providers/fontsource.d.ts
|
||||
declare const _default: () => Provider<"fontsource", never>;
|
||||
declare namespace providers_d_exports {
|
||||
export { _default$3 as adobe, _default$2 as bunny, _default$1 as fontshare, _default as fontsource, _default$5 as google, _default$4 as googleicons };
|
||||
}
|
||||
//#endregion
|
||||
//#region src/cache.d.ts
|
||||
type Awaitable<T> = T | Promise<T>;
|
||||
type StorageValue = string | Record<string, unknown>;
|
||||
interface Storage {
|
||||
getItem: (key: string) => Awaitable<any | null>;
|
||||
setItem: <T extends StorageValue = StorageValue>(key: string, value: T) => Awaitable<void>;
|
||||
}
|
||||
//#endregion
|
||||
//#region src/unifont.d.ts
|
||||
interface UnifontOptions {
|
||||
storage?: Storage;
|
||||
throwOnError?: boolean;
|
||||
}
|
||||
type ExtractFamilyOptions<T extends Provider> = Exclude<Parameters<NonNullable<Awaited<ReturnType<T>>>['resolveFont']>[1]['options'], undefined>;
|
||||
interface Unifont<T extends Provider[]> {
|
||||
resolveFont: (fontFamily: string, options?: Partial<ResolveFontOptions<{ [K in T[number] as K['_name']]?: ExtractFamilyOptions<K> }>>, providers?: T[number]['_name'][]) => Promise<ResolveFontResult & {
|
||||
provider?: T[number]['_name'];
|
||||
}>;
|
||||
listFonts: (providers?: T[number]['_name'][]) => Promise<string[] | undefined>;
|
||||
}
|
||||
declare const defaultResolveOptions: ResolveFontOptions;
|
||||
declare function createUnifont<T extends [Provider, ...Provider[]]>(providers: T, unifontOptions?: UnifontOptions): Promise<Unifont<T>>;
|
||||
//#endregion
|
||||
//#region src/index.d.ts
|
||||
/** @deprecated */
|
||||
type GoogleOptions = GoogleProviderOptions;
|
||||
/** @deprecated */
|
||||
type GoogleiconsOptions = GoogleiconsProviderOptions;
|
||||
//#endregion
|
||||
export { type AdobeProviderOptions, type FontFaceData, type FontFaceMeta, type FontStyles, type GoogleFamilyOptions, GoogleOptions, type GoogleProviderOptions, type GoogleiconsFamilyOptions, GoogleiconsOptions, type GoogleiconsProviderOptions, type InitializedProvider, type LocalFontSource, type Provider, type ProviderContext, type ProviderDefinition, type ProviderFactory, type RemoteFontSource, type ResolveFontOptions, type ResolveFontResult, type Unifont, type UnifontOptions, createUnifont, defaultResolveOptions, defineFontProvider, providers_d_exports as providers };
|
||||
737
node_modules/unifont/dist/index.mjs
generated
vendored
Normal file
737
node_modules/unifont/dist/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,737 @@
|
||||
import { t as __exportAll } from "./chunk-15K8U1wQ.mjs";
|
||||
import { hash } from "ohash";
|
||||
import { findAll, generate, parse } from "css-tree";
|
||||
import { ofetch } from "ofetch";
|
||||
|
||||
//#region src/css/parse.ts
|
||||
const extractableKeyMap = {
|
||||
"src": "src",
|
||||
"font-display": "display",
|
||||
"font-weight": "weight",
|
||||
"font-style": "style",
|
||||
"font-feature-settings": "featureSettings",
|
||||
"font-variation-settings": "variationSettings",
|
||||
"unicode-range": "unicodeRange"
|
||||
};
|
||||
const formatPriorityList = Object.values({
|
||||
woff2: "woff2",
|
||||
woff: "woff",
|
||||
otf: "opentype",
|
||||
ttf: "truetype",
|
||||
eot: "embedded-opentype",
|
||||
svg: "svg"
|
||||
});
|
||||
function extractFontFaceData(css, family) {
|
||||
const fontFaces = [];
|
||||
for (const node of findAll(parse(css), (node$1) => node$1.type === "Atrule" && node$1.name === "font-face")) {
|
||||
/* v8 ignore next 3 */
|
||||
if (node.type !== "Atrule" || node.name !== "font-face") continue;
|
||||
if (family) {
|
||||
if (!node.block?.children.some((child) => {
|
||||
if (child.type !== "Declaration" || child.property !== "font-family") return false;
|
||||
const value = extractCSSValue(child);
|
||||
const slug = family.toLowerCase();
|
||||
if (typeof value === "string" && value.toLowerCase() === slug) return true;
|
||||
if (Array.isArray(value) && value.length > 0 && value.some((v) => v.toLowerCase() === slug)) return true;
|
||||
return false;
|
||||
})) continue;
|
||||
}
|
||||
const data = {};
|
||||
for (const child of node.block?.children || []) if (child.type === "Declaration" && child.property in extractableKeyMap) {
|
||||
const value = extractCSSValue(child);
|
||||
data[extractableKeyMap[child.property]] = ["src", "unicode-range"].includes(child.property) && !Array.isArray(value) ? [value] : value;
|
||||
}
|
||||
if (!data.src) continue;
|
||||
fontFaces.push(data);
|
||||
}
|
||||
return mergeFontSources(fontFaces);
|
||||
}
|
||||
function processRawValue(value) {
|
||||
return value.split(",").map((v) => v.trim().replace(/^(?<quote>['"])(.*)\k<quote>$/, "$2"));
|
||||
}
|
||||
function extractCSSValue(node) {
|
||||
if (node.value.type === "Raw") return processRawValue(node.value.value);
|
||||
const values = [];
|
||||
let buffer = "";
|
||||
for (const child of node.value.children) {
|
||||
if (child.type === "Function") {
|
||||
if (child.name === "local" && child.children.first?.type === "String") values.push({ name: child.children.first.value });
|
||||
if (child.name === "format") {
|
||||
if (child.children.first?.type === "String") values.at(-1).format = child.children.first.value;
|
||||
else if (child.children.first?.type === "Identifier") values.at(-1).format = child.children.first.name;
|
||||
}
|
||||
if (child.name === "tech") {
|
||||
if (child.children.first?.type === "String") values.at(-1).tech = child.children.first.value;
|
||||
else if (child.children.first?.type === "Identifier") values.at(-1).tech = child.children.first.name;
|
||||
}
|
||||
}
|
||||
if (child.type === "Url") values.push({ url: child.value });
|
||||
if (child.type === "Identifier") buffer = buffer ? `${buffer} ${child.name}` : child.name;
|
||||
if (child.type === "String") values.push(child.value);
|
||||
if (child.type === "Dimension") {
|
||||
const dimensionValue = child.value + child.unit;
|
||||
buffer = buffer ? `${buffer} ${dimensionValue}` : dimensionValue;
|
||||
}
|
||||
if (child.type === "Operator" && child.value === "," && buffer) {
|
||||
values.push(buffer);
|
||||
buffer = "";
|
||||
}
|
||||
if (child.type === "UnicodeRange") values.push(child.value);
|
||||
if (child.type === "Number") values.push(Number(child.value));
|
||||
}
|
||||
if (buffer) values.push(buffer);
|
||||
if (values.length === 1) return values[0];
|
||||
return values;
|
||||
}
|
||||
function mergeFontSources(data) {
|
||||
const mergedData = [];
|
||||
for (const face of data) {
|
||||
const keys = Object.keys(face).filter((k) => k !== "src");
|
||||
const existing = mergedData.find((f) => Object.keys(f).length === keys.length + 1 && keys.every((key) => f[key]?.toString() === face[key]?.toString()));
|
||||
if (existing) {
|
||||
for (const s of face.src) if (existing.src.every((src) => "url" in src ? !("url" in s) || s.url !== src.url : !("name" in s) || s.name !== src.name)) existing.src.push(s);
|
||||
} else mergedData.push(face);
|
||||
}
|
||||
for (const face of mergedData) face.src.sort((a, b) => {
|
||||
return ("format" in a ? formatPriorityList.indexOf(a.format || "woff2") : -2) - ("format" in b ? formatPriorityList.indexOf(b.format || "woff2") : -2);
|
||||
});
|
||||
return mergedData;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
//#region src/fetch.ts
|
||||
function mini$fetch(url, options) {
|
||||
const retries = options?.retries ?? 3;
|
||||
const retryDelay = options?.retryDelay ?? 1e3;
|
||||
return ofetch(url, {
|
||||
baseURL: options?.baseURL,
|
||||
query: options?.query,
|
||||
responseType: options?.responseType ?? "text",
|
||||
headers: options?.headers,
|
||||
retry: false
|
||||
}).catch((err) => {
|
||||
if (retries <= 0) throw err;
|
||||
console.warn(`Could not fetch from \`${(options?.baseURL ?? "") + url}\`. Will retry in \`${retryDelay}ms\`. \`${retries}\` retries left.`);
|
||||
return new Promise((resolve) => setTimeout(resolve, retryDelay)).then(() => mini$fetch(url, {
|
||||
...options,
|
||||
retries: retries - 1
|
||||
}));
|
||||
});
|
||||
}
|
||||
const $fetch = Object.assign(mini$fetch, { create: (defaults) => (url, options) => mini$fetch(url, {
|
||||
...defaults,
|
||||
...options
|
||||
}) });
|
||||
|
||||
//#endregion
|
||||
//#region src/utils.ts
|
||||
function defineFontProvider(name, provider) {
|
||||
return ((options) => Object.assign(provider.bind(null, options || {}), {
|
||||
_name: name,
|
||||
_options: options
|
||||
}));
|
||||
}
|
||||
function prepareWeights({ inputWeights, weights, hasVariableWeights }) {
|
||||
const collectedWeights = [];
|
||||
for (const weight of inputWeights) {
|
||||
if (weight.includes(" ")) {
|
||||
if (hasVariableWeights) {
|
||||
collectedWeights.push(weight);
|
||||
continue;
|
||||
}
|
||||
const [min, max] = weight.split(" ");
|
||||
collectedWeights.push(...weights.filter((_w) => {
|
||||
const w = Number(_w);
|
||||
return w >= Number(min) && w <= Number(max);
|
||||
}).map((w) => String(w)));
|
||||
continue;
|
||||
}
|
||||
if (weights.includes(weight)) collectedWeights.push(weight);
|
||||
}
|
||||
return [...new Set(collectedWeights)].map((weight) => ({
|
||||
weight,
|
||||
variable: weight.includes(" ")
|
||||
}));
|
||||
}
|
||||
function splitCssIntoSubsets(input) {
|
||||
const data = [];
|
||||
const comments = [];
|
||||
const nodes = findAll(parse(input, {
|
||||
positions: true,
|
||||
onComment(value, loc) {
|
||||
comments.push({
|
||||
value: value.trim(),
|
||||
endLine: loc.end.line
|
||||
});
|
||||
}
|
||||
}), (node) => node.type === "Atrule" && node.name === "font-face");
|
||||
if (comments.length === 0) return [{
|
||||
subset: null,
|
||||
css: input
|
||||
}];
|
||||
for (const node of nodes) {
|
||||
const comment = comments.filter((comment$1) => comment$1.endLine < node.loc.start.line).at(-1);
|
||||
data.push({
|
||||
subset: comment?.value ?? null,
|
||||
css: generate(node)
|
||||
});
|
||||
}
|
||||
return data;
|
||||
}
|
||||
const formatMap = {
|
||||
woff2: "woff2",
|
||||
woff: "woff",
|
||||
otf: "opentype",
|
||||
ttf: "truetype",
|
||||
eot: "embedded-opentype"
|
||||
};
|
||||
function computeIdFromSource(source) {
|
||||
return "name" in source ? source.name : source.url;
|
||||
}
|
||||
function cleanFontFaces(fonts, _formats) {
|
||||
const formats = _formats.map((format) => formatMap[format]);
|
||||
const result = [];
|
||||
const hashToIndex = /* @__PURE__ */ new Map();
|
||||
for (const { src: _src, meta, ...font } of fonts) {
|
||||
const key = hash(font);
|
||||
const index = hashToIndex.get(key);
|
||||
const src = _src.map((source) => "name" in source ? source : {
|
||||
...source,
|
||||
...source.format ? { format: formatMap[source.format] ?? source.format } : {}
|
||||
}).filter((source) => "name" in source || !source.format || formats.includes(source.format));
|
||||
if (src.length === 0) continue;
|
||||
if (index === void 0) {
|
||||
hashToIndex.set(key, result.push({
|
||||
...font,
|
||||
...meta ? { meta } : {},
|
||||
src
|
||||
}) - 1);
|
||||
continue;
|
||||
}
|
||||
const existing = result[index];
|
||||
const ids = new Set(existing.src.map((source) => computeIdFromSource(source)));
|
||||
existing.src.push(...src.filter((source) => {
|
||||
const id = computeIdFromSource(source);
|
||||
return !ids.has(id) && ids.add(id);
|
||||
}));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
//#region src/providers/adobe.ts
|
||||
const fontCSSAPI = $fetch.create({ baseURL: "https://use.typekit.net" });
|
||||
async function getAdobeFontMeta(id) {
|
||||
const { kit } = await $fetch(`https://typekit.com/api/v1/json/kits/${id}/published`, { responseType: "json" });
|
||||
return kit;
|
||||
}
|
||||
const KIT_REFRESH_TIMEOUT = 300 * 1e3;
|
||||
var adobe_default = defineFontProvider("adobe", async (options, ctx) => {
|
||||
if (!options.id) return;
|
||||
const familyMap = /* @__PURE__ */ new Map();
|
||||
const notFoundFamilies = /* @__PURE__ */ new Set();
|
||||
const fonts = { kits: [] };
|
||||
let lastRefreshKitTime;
|
||||
const kits = typeof options.id === "string" ? [options.id] : options.id;
|
||||
await fetchKits();
|
||||
async function fetchKits(bypassCache = false) {
|
||||
familyMap.clear();
|
||||
notFoundFamilies.clear();
|
||||
fonts.kits = [];
|
||||
await Promise.all(kits.map(async (id) => {
|
||||
let meta;
|
||||
const key = `adobe:meta-${id}.json`;
|
||||
if (bypassCache) {
|
||||
meta = await getAdobeFontMeta(id);
|
||||
await ctx.storage.setItem(key, meta);
|
||||
} else meta = await ctx.storage.getItem(key, () => getAdobeFontMeta(id));
|
||||
if (!meta) throw new TypeError("No font metadata found in adobe response.");
|
||||
fonts.kits.push(meta);
|
||||
for (const family of meta.families) familyMap.set(family.name, family.id);
|
||||
}));
|
||||
}
|
||||
async function getFontDetails(family, options$1) {
|
||||
options$1.weights = options$1.weights.map(String);
|
||||
for (const kit of fonts.kits) {
|
||||
const font = kit.families.find((f) => f.name === family);
|
||||
if (!font) continue;
|
||||
const weights = prepareWeights({
|
||||
inputWeights: options$1.weights,
|
||||
hasVariableWeights: false,
|
||||
weights: font.variations.map((v) => `${v.slice(-1)}00`)
|
||||
}).map((w) => w.weight);
|
||||
const styles = [];
|
||||
for (const style of font.variations) {
|
||||
if (style.includes("i") && !options$1.styles.includes("italic")) continue;
|
||||
if (!weights.includes(String(`${style.slice(-1)}00`))) continue;
|
||||
styles.push(style);
|
||||
}
|
||||
if (styles.length === 0) continue;
|
||||
return extractFontFaceData(await fontCSSAPI(`/${kit.id}.css`), font.css_names[0] ?? family.toLowerCase().split(" ").join("-")).filter((font$1) => {
|
||||
const [lowerWeight, upperWeight] = Array.isArray(font$1.weight) ? font$1.weight : [0, 0];
|
||||
return (!options$1.styles || !font$1.style || options$1.styles.includes(font$1.style)) && (!weights || !font$1.weight || Array.isArray(font$1.weight) ? weights.some((weight) => Number(weight) <= upperWeight || Number(weight) >= lowerWeight) : weights.includes(String(font$1.weight)));
|
||||
});
|
||||
}
|
||||
return [];
|
||||
}
|
||||
return {
|
||||
listFonts() {
|
||||
return [...familyMap.keys()];
|
||||
},
|
||||
async resolveFont(family, options$1) {
|
||||
if (notFoundFamilies.has(family)) return;
|
||||
if (!familyMap.has(family)) {
|
||||
const lastRefetch = lastRefreshKitTime || 0;
|
||||
if (Date.now() - lastRefetch > KIT_REFRESH_TIMEOUT) {
|
||||
lastRefreshKitTime = Date.now();
|
||||
await fetchKits(true);
|
||||
}
|
||||
}
|
||||
if (!familyMap.has(family)) {
|
||||
notFoundFamilies.add(family);
|
||||
return;
|
||||
}
|
||||
return { fonts: await ctx.storage.getItem(`adobe:${family}-${hash(options$1)}-data.json`, () => getFontDetails(family, options$1)) };
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
//#endregion
|
||||
//#region src/providers/bunny.ts
|
||||
const fontAPI$2 = $fetch.create({ baseURL: "https://fonts.bunny.net" });
|
||||
var bunny_default = defineFontProvider("bunny", async (_options, ctx) => {
|
||||
const familyMap = /* @__PURE__ */ new Map();
|
||||
const fonts = await ctx.storage.getItem("bunny:meta.json", () => fontAPI$2("/list", { responseType: "json" }));
|
||||
for (const [id, family] of Object.entries(fonts)) familyMap.set(family.familyName, id);
|
||||
async function getFontDetails(family, options) {
|
||||
const id = familyMap.get(family);
|
||||
const font = fonts[id];
|
||||
const weights = prepareWeights({
|
||||
inputWeights: options.weights,
|
||||
hasVariableWeights: false,
|
||||
weights: font.weights.map(String)
|
||||
});
|
||||
const styleMap = {
|
||||
italic: "i",
|
||||
oblique: "i",
|
||||
normal: ""
|
||||
};
|
||||
const styles = new Set(options.styles.map((i) => styleMap[i]));
|
||||
if (weights.length === 0 || styles.size === 0) return [];
|
||||
const css = await fontAPI$2("/css", { query: { family: `${id}:${weights.flatMap((w) => [...styles].map((s) => `${w.weight}${s}`)).join(",")}` } });
|
||||
const resolvedFontFaceData = [];
|
||||
const groups = splitCssIntoSubsets(css).filter((group) => group.subset ? options.subsets.includes(group.subset) : true);
|
||||
for (const group of groups) {
|
||||
const data = extractFontFaceData(group.css);
|
||||
data.map((f) => {
|
||||
f.meta ??= {};
|
||||
if (group.subset) f.meta.subset = group.subset;
|
||||
return f;
|
||||
});
|
||||
resolvedFontFaceData.push(...data);
|
||||
}
|
||||
return cleanFontFaces(resolvedFontFaceData, options.formats);
|
||||
}
|
||||
return {
|
||||
listFonts() {
|
||||
return [...familyMap.keys()];
|
||||
},
|
||||
async resolveFont(fontFamily, defaults) {
|
||||
if (!familyMap.has(fontFamily)) return;
|
||||
return { fonts: await ctx.storage.getItem(`bunny:${fontFamily}-${hash(defaults)}-data.json`, () => getFontDetails(fontFamily, defaults)) };
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
//#endregion
|
||||
//#region src/providers/fontshare.ts
|
||||
const fontAPI$1 = $fetch.create({ baseURL: "https://api.fontshare.com/v2" });
|
||||
var fontshare_default = defineFontProvider("fontshare", async (_options, ctx) => {
|
||||
const fontshareFamilies = /* @__PURE__ */ new Set();
|
||||
const fonts = await ctx.storage.getItem("fontshare:meta.json", async () => {
|
||||
const fonts$1 = [];
|
||||
let offset = 0;
|
||||
let chunk;
|
||||
do {
|
||||
chunk = await fontAPI$1("/fonts", {
|
||||
responseType: "json",
|
||||
query: {
|
||||
offset,
|
||||
limit: 100
|
||||
}
|
||||
});
|
||||
fonts$1.push(...chunk.fonts);
|
||||
offset++;
|
||||
} while (chunk.has_more);
|
||||
return fonts$1;
|
||||
});
|
||||
for (const font of fonts) fontshareFamilies.add(font.name);
|
||||
async function getFontDetails(family, options) {
|
||||
const font = fonts.find((f) => f.name === family);
|
||||
const numbers = [];
|
||||
const weights = prepareWeights({
|
||||
inputWeights: options.weights,
|
||||
hasVariableWeights: false,
|
||||
weights: font.styles.map((s) => String(s.weight.weight))
|
||||
}).map((w) => w.weight);
|
||||
for (const style of font.styles) {
|
||||
if (style.is_italic && !options.styles.includes("italic")) continue;
|
||||
if (!style.is_italic && !options.styles.includes("normal")) continue;
|
||||
if (!weights.includes(String(style.weight.weight))) continue;
|
||||
numbers.push(style.weight.number);
|
||||
}
|
||||
if (numbers.length === 0) return [];
|
||||
return cleanFontFaces(extractFontFaceData(await fontAPI$1(`/css?f[]=${`${font.slug}@${numbers.join(",")}`}`)), options.formats);
|
||||
}
|
||||
return {
|
||||
listFonts() {
|
||||
return [...fontshareFamilies];
|
||||
},
|
||||
async resolveFont(fontFamily, defaults) {
|
||||
if (!fontshareFamilies.has(fontFamily)) return;
|
||||
return { fonts: await ctx.storage.getItem(`fontshare:${fontFamily}-${hash(defaults)}-data.json`, () => getFontDetails(fontFamily, defaults)) };
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
//#endregion
|
||||
//#region src/providers/fontsource.ts
|
||||
const fontAPI = $fetch.create({ baseURL: "https://api.fontsource.org/v1" });
|
||||
var fontsource_default = defineFontProvider("fontsource", async (_options, ctx) => {
|
||||
const fonts = await ctx.storage.getItem("fontsource:meta.json", () => fontAPI("/fonts", { responseType: "json" }));
|
||||
const familyMap = /* @__PURE__ */ new Map();
|
||||
for (const meta of fonts) familyMap.set(meta.family, meta);
|
||||
async function getFontDetails(family, options) {
|
||||
const font = familyMap.get(family);
|
||||
const weights = prepareWeights({
|
||||
inputWeights: options.weights,
|
||||
hasVariableWeights: font.variable,
|
||||
weights: font.weights.map(String)
|
||||
});
|
||||
const styles = options.styles.filter((style) => font.styles.includes(style));
|
||||
const subsets = options.subsets ? options.subsets.filter((subset) => font.subsets.includes(subset)) : [font.defSubset];
|
||||
if (weights.length === 0 || styles.length === 0) return [];
|
||||
const fontDetail = await fontAPI(`/fonts/${font.id}`, { responseType: "json" });
|
||||
const fontFaceData = [];
|
||||
for (const subset of subsets) for (const style of styles) for (const { weight, variable } of weights) {
|
||||
if (variable) {
|
||||
try {
|
||||
const variableAxes = await ctx.storage.getItem(`fontsource:${font.family}-axes.json`, () => fontAPI(`/variable/${font.id}`, { responseType: "json" }));
|
||||
if (variableAxes && variableAxes.axes.wght) fontFaceData.push({
|
||||
style,
|
||||
weight: [Number(variableAxes.axes.wght.min), Number(variableAxes.axes.wght.max)],
|
||||
src: [{
|
||||
url: `https://cdn.jsdelivr.net/fontsource/fonts/${font.id}:vf@latest/${subset}-wght-${style}.woff2`,
|
||||
format: "woff2"
|
||||
}],
|
||||
unicodeRange: fontDetail.unicodeRange[subset]?.split(","),
|
||||
meta: { subset }
|
||||
});
|
||||
} catch {
|
||||
console.error(`Could not download variable axes metadata for \`${font.family}\` from \`fontsource\`. \`unifont\` will not be able to inject variable axes for ${font.family}.`);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const variantUrl = fontDetail.variants[weight][style][subset].url;
|
||||
fontFaceData.push({
|
||||
style,
|
||||
weight,
|
||||
src: Object.entries(variantUrl).map(([format, url]) => ({
|
||||
url,
|
||||
format
|
||||
})),
|
||||
unicodeRange: fontDetail.unicodeRange[subset]?.split(","),
|
||||
meta: { subset }
|
||||
});
|
||||
}
|
||||
return cleanFontFaces(fontFaceData, options.formats);
|
||||
}
|
||||
return {
|
||||
listFonts() {
|
||||
return [...familyMap.keys()];
|
||||
},
|
||||
async resolveFont(fontFamily, options) {
|
||||
if (!familyMap.has(fontFamily)) return;
|
||||
return { fonts: await ctx.storage.getItem(`fontsource:${fontFamily}-${hash(options)}-data.json`, () => getFontDetails(fontFamily, options)) };
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
//#endregion
|
||||
//#region src/providers/google.ts
|
||||
const userAgents = {
|
||||
eot: "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0)",
|
||||
ttf: "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; de-at) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1",
|
||||
woff: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0",
|
||||
woff2: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
|
||||
};
|
||||
var google_default = defineFontProvider("google", async (providerOptions, ctx) => {
|
||||
const googleFonts = await ctx.storage.getItem("google:meta.json", () => $fetch("https://fonts.google.com/metadata/fonts", { responseType: "json" }).then((r) => r.familyMetadataList));
|
||||
const styleMap = {
|
||||
italic: "1",
|
||||
oblique: "1",
|
||||
normal: "0"
|
||||
};
|
||||
async function getFontDetails(family, options) {
|
||||
const font = googleFonts.find((font$1) => font$1.family === family);
|
||||
const styles = [...new Set(options.styles.map((i) => styleMap[i]))].sort();
|
||||
const glyphs = (options.options?.experimental?.glyphs ?? providerOptions.experimental?.glyphs?.[family])?.join("");
|
||||
const weights = prepareWeights({
|
||||
inputWeights: options.weights,
|
||||
hasVariableWeights: font.axes.some((a) => a.tag === "wght"),
|
||||
weights: Object.keys(font.fonts)
|
||||
}).map((v) => v.variable ? {
|
||||
weight: v.weight.replace(" ", ".."),
|
||||
variable: v.variable
|
||||
} : v);
|
||||
if (weights.length === 0 || styles.length === 0) return [];
|
||||
const resolvedAxes = [];
|
||||
let resolvedVariants = [];
|
||||
const variableAxis = options.options?.experimental?.variableAxis ?? providerOptions.experimental?.variableAxis?.[family];
|
||||
const candidateAxes = [
|
||||
"wght",
|
||||
"ital",
|
||||
...Object.keys(variableAxis ?? {})
|
||||
].sort(googleFlavoredSorting);
|
||||
for (const axis of candidateAxes) {
|
||||
const axisValue = {
|
||||
wght: weights.map((v) => v.weight),
|
||||
ital: styles
|
||||
}[axis] ?? variableAxis[axis].map((v) => Array.isArray(v) ? `${v[0]}..${v[1]}` : v);
|
||||
if (resolvedVariants.length === 0) resolvedVariants = axisValue;
|
||||
else resolvedVariants = resolvedVariants.flatMap((v) => [...axisValue].map((o) => [v, o].join(","))).sort();
|
||||
resolvedAxes.push(axis);
|
||||
}
|
||||
let priority = 0;
|
||||
const resolvedFontFaceData = [];
|
||||
for (const format of options.formats) {
|
||||
const userAgent = userAgents[format];
|
||||
if (!userAgent) continue;
|
||||
const groups = splitCssIntoSubsets(await $fetch("/css2", {
|
||||
baseURL: "https://fonts.googleapis.com",
|
||||
headers: { "user-agent": userAgent },
|
||||
query: {
|
||||
family: `${family}:${resolvedAxes.join(",")}@${resolvedVariants.join(";")}`,
|
||||
...glyphs && { text: glyphs }
|
||||
}
|
||||
})).filter((group) => group.subset ? options.subsets.includes(group.subset) : true);
|
||||
for (const group of groups) {
|
||||
const data = extractFontFaceData(group.css);
|
||||
data.map((f) => {
|
||||
f.meta ??= {};
|
||||
f.meta.priority = priority;
|
||||
if (group.subset) f.meta.subset = group.subset;
|
||||
return f;
|
||||
});
|
||||
resolvedFontFaceData.push(...data);
|
||||
}
|
||||
priority++;
|
||||
}
|
||||
return cleanFontFaces(resolvedFontFaceData, options.formats);
|
||||
}
|
||||
return {
|
||||
listFonts() {
|
||||
return googleFonts.map((font) => font.family);
|
||||
},
|
||||
async resolveFont(fontFamily, options) {
|
||||
if (!googleFonts.some((font) => font.family === fontFamily)) return;
|
||||
return { fonts: await ctx.storage.getItem(`google:${fontFamily}-${hash(options)}-data.json`, () => getFontDetails(fontFamily, options)) };
|
||||
}
|
||||
};
|
||||
});
|
||||
function googleFlavoredSorting(a, b) {
|
||||
const isALowercase = a.charAt(0) === a.charAt(0).toLowerCase();
|
||||
const isBLowercase = b.charAt(0) === b.charAt(0).toLowerCase();
|
||||
if (isALowercase !== isBLowercase) return Number(isBLowercase) - Number(isALowercase);
|
||||
else return a.localeCompare(b);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
//#region src/providers/googleicons.ts
|
||||
var googleicons_default = defineFontProvider("googleicons", async (providerOptions, ctx) => {
|
||||
const googleIcons = await ctx.storage.getItem("googleicons:meta.json", async () => {
|
||||
const data = await $fetch("https://fonts.google.com/metadata/icons?key=material_symbols&incomplete=true");
|
||||
return JSON.parse(data.substring(data.indexOf("\n") + 1)).families;
|
||||
});
|
||||
async function getFontDetails(family, options) {
|
||||
const iconNames = (options.options?.experimental?.glyphs ?? providerOptions.experimental?.glyphs?.[family])?.join("");
|
||||
let css = "";
|
||||
for (const format of options.formats) {
|
||||
const userAgent = userAgents[format];
|
||||
if (!userAgent) continue;
|
||||
if (family.includes("Icons")) css += await $fetch("/icon", {
|
||||
baseURL: "https://fonts.googleapis.com",
|
||||
headers: { "user-agent": userAgent },
|
||||
query: { family }
|
||||
});
|
||||
else css += await $fetch("/css2", {
|
||||
baseURL: "https://fonts.googleapis.com",
|
||||
headers: { "user-agent": userAgent },
|
||||
query: {
|
||||
family: `${family}:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200`,
|
||||
...iconNames && { icon_names: iconNames }
|
||||
}
|
||||
});
|
||||
}
|
||||
return cleanFontFaces(extractFontFaceData(css), options.formats);
|
||||
}
|
||||
return {
|
||||
listFonts() {
|
||||
return googleIcons;
|
||||
},
|
||||
async resolveFont(fontFamily, options) {
|
||||
if (!googleIcons.includes(fontFamily)) return;
|
||||
return { fonts: await ctx.storage.getItem(`googleicons:${fontFamily}-${hash(options)}-data.json`, () => getFontDetails(fontFamily, options)) };
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
//#endregion
|
||||
//#region src/providers.ts
|
||||
var providers_exports = /* @__PURE__ */ __exportAll({
|
||||
adobe: () => adobe_default,
|
||||
bunny: () => bunny_default,
|
||||
fontshare: () => fontshare_default,
|
||||
fontsource: () => fontsource_default,
|
||||
google: () => google_default,
|
||||
googleicons: () => googleicons_default
|
||||
});
|
||||
|
||||
//#endregion
|
||||
//#region package.json
|
||||
var version = "0.7.3";
|
||||
|
||||
//#endregion
|
||||
//#region src/cache.ts
|
||||
function memoryStorage() {
|
||||
const cache = /* @__PURE__ */ new Map();
|
||||
return {
|
||||
getItem(key) {
|
||||
return cache.get(key);
|
||||
},
|
||||
setItem(key, value) {
|
||||
cache.set(key, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
const ONE_WEEK = 1e3 * 60 * 60 * 24 * 7;
|
||||
function createAsyncStorage(storage, options = {}) {
|
||||
const prefix = options?.cachedBy?.length ? `${createCacheKey(...options.cachedBy)}:` : "";
|
||||
const resolveKey = (key) => `${prefix}${key}`;
|
||||
return {
|
||||
async getItem(key, init) {
|
||||
const resolvedKey = resolveKey(key);
|
||||
const now = Date.now();
|
||||
const res = await storage.getItem(resolvedKey);
|
||||
if (res && res.expires > now && res.version === version) return res.data;
|
||||
if (!init) return null;
|
||||
const data = await init();
|
||||
await storage.setItem(resolvedKey, {
|
||||
expires: now + ONE_WEEK,
|
||||
version,
|
||||
data
|
||||
});
|
||||
return data;
|
||||
},
|
||||
async setItem(key, data) {
|
||||
await storage.setItem(resolveKey(key), {
|
||||
expires: Date.now() + ONE_WEEK,
|
||||
version,
|
||||
data
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
function createCacheKey(...fragments) {
|
||||
return fragments.map((f) => {
|
||||
return sanitize(typeof f === "string" ? f : hash(f));
|
||||
}).join(":");
|
||||
}
|
||||
function sanitize(input) {
|
||||
if (!input) return "";
|
||||
return input.replace(/[^\w.-]/g, "_");
|
||||
}
|
||||
|
||||
//#endregion
|
||||
//#region src/unifont.ts
|
||||
const defaultResolveOptions = {
|
||||
weights: ["400"],
|
||||
styles: ["normal", "italic"],
|
||||
subsets: [
|
||||
"cyrillic-ext",
|
||||
"cyrillic",
|
||||
"greek-ext",
|
||||
"greek",
|
||||
"vietnamese",
|
||||
"latin-ext",
|
||||
"latin"
|
||||
],
|
||||
formats: ["woff2"]
|
||||
};
|
||||
async function createUnifont(providers, unifontOptions) {
|
||||
const stack = {};
|
||||
const storage = unifontOptions?.storage ?? memoryStorage();
|
||||
for (const provider of providers) stack[provider._name] = void 0;
|
||||
await Promise.all(providers.map(async (provider) => {
|
||||
const context = { storage: createAsyncStorage(storage, { cachedBy: [provider._name, provider._options] }) };
|
||||
try {
|
||||
const initializedProvider = await provider(context);
|
||||
if (initializedProvider) stack[provider._name] = initializedProvider;
|
||||
} catch (cause) {
|
||||
const message = `Could not initialize provider \`${provider._name}\`. \`unifont\` will not be able to process fonts provided by this provider.`;
|
||||
if (unifontOptions?.throwOnError) throw new Error(message, { cause });
|
||||
console.error(message, cause);
|
||||
}
|
||||
if (!stack[provider._name]?.resolveFont) delete stack[provider._name];
|
||||
}));
|
||||
const allProviders = Object.keys(stack);
|
||||
async function resolveFont(fontFamily, options = {}, providers$1 = allProviders) {
|
||||
const mergedOptions = {
|
||||
...defaultResolveOptions,
|
||||
...options
|
||||
};
|
||||
for (const id of providers$1) {
|
||||
const provider = stack[id];
|
||||
try {
|
||||
const result = await provider?.resolveFont(fontFamily, {
|
||||
...mergedOptions,
|
||||
options: mergedOptions.options?.[id]
|
||||
});
|
||||
if (result) return {
|
||||
provider: id,
|
||||
...result
|
||||
};
|
||||
} catch (cause) {
|
||||
const message = `Could not resolve font face for \`${fontFamily}\` from \`${id}\` provider.`;
|
||||
if (unifontOptions?.throwOnError) throw new Error(message, { cause });
|
||||
console.error(message, cause);
|
||||
}
|
||||
}
|
||||
return { fonts: [] };
|
||||
}
|
||||
async function listFonts(providers$1 = allProviders) {
|
||||
let names;
|
||||
for (const id of providers$1) {
|
||||
const provider = stack[id];
|
||||
try {
|
||||
const result = await provider?.listFonts?.();
|
||||
if (result) {
|
||||
names ??= [];
|
||||
names.push(...result);
|
||||
}
|
||||
} catch (cause) {
|
||||
const message = `Could not list names from \`${id}\` provider.`;
|
||||
if (unifontOptions?.throwOnError) throw new Error(message, { cause });
|
||||
console.error(message, cause);
|
||||
}
|
||||
}
|
||||
return names;
|
||||
}
|
||||
return {
|
||||
resolveFont,
|
||||
listFonts
|
||||
};
|
||||
}
|
||||
|
||||
//#endregion
|
||||
export { createUnifont, defaultResolveOptions, defineFontProvider, providers_exports as providers };
|
||||
62
node_modules/unifont/package.json
generated
vendored
Normal file
62
node_modules/unifont/package.json
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"name": "unifont",
|
||||
"type": "module",
|
||||
"version": "0.7.3",
|
||||
"description": "Framework agnostic tools for accessing data from font CDNs and providers",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/unjs/unifont.git"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": "./dist/index.mjs"
|
||||
},
|
||||
"main": "./dist/index.mjs",
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.mts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"dependencies": {
|
||||
"css-tree": "^3.1.0",
|
||||
"ofetch": "^1.5.1",
|
||||
"ohash": "^2.0.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "6.7.3",
|
||||
"@types/css-tree": "2.3.11",
|
||||
"@types/node": "24.10.7",
|
||||
"@vitest/coverage-v8": "4.0.17",
|
||||
"bumpp": "10.3.2",
|
||||
"changelogithub": "14.0.0",
|
||||
"eslint": "9.39.2",
|
||||
"lint-staged": "16.2.7",
|
||||
"simple-git-hooks": "2.13.1",
|
||||
"tsdown": "0.19.0",
|
||||
"typescript": "5.9.3",
|
||||
"unstorage": "1.17.3",
|
||||
"vite": "7.3.1",
|
||||
"vitest": "4.0.17"
|
||||
},
|
||||
"resolutions": {
|
||||
"unifont": "link:."
|
||||
},
|
||||
"simple-git-hooks": {
|
||||
"pre-commit": "npx lint-staged"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,ts,mjs,cjs,json,.*rc}": [
|
||||
"npx eslint --fix"
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsdown",
|
||||
"dev": "vitest dev",
|
||||
"lint": "eslint .",
|
||||
"release": "bumpp",
|
||||
"test": "pnpm test:unit --coverage && pnpm test:types",
|
||||
"test:unit": "vitest",
|
||||
"test:types": "tsc --noEmit"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user