@takumi-rs/helpers 2.0.0-beta.6 → 2.0.0-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./helpers-4kGcvc-Q.cjs"),t=/url\(\s*(['"]?)(.*?)\1\s*\)/g;function n(e){return e.startsWith(`https://`)||e.startsWith(`http://`)}function r(e,i){if(typeof e==`string`)for(let r of e.matchAll(t)){let e=r[2]?.trim();e&&n(e)&&i.add(e)}else if(Array.isArray(e))for(let t of e)r(t,i)}function i(e){let t=new Set,i=e=>{let a=e=>{e&&(r(e.backgroundImage,t),r(e.maskImage,t))};if(a(e.style),a(e.preset),r(e.tw,t),e.type===`image`){typeof e.src==`string`&&n(e.src)&&t.add(e.src);return}if(e.type===`container`)for(let t of e.children??[])i(t)};return i(e),[...t]}async function a(e,t={}){let n=t.fetch??globalThis.fetch,r=t.timeout===void 0?t.init?.signal:AbortSignal.timeout(t.timeout),i=await n(e,{...t.init,signal:r});if(!i.ok)throw Error(`HTTP ${i.status} ${i.statusText} fetching ${e}`);return i}async function o(e,t){let n=t?.throwOnError??!0,r=t?.timeout??5e3,i=[...new Set(e)].map(async e=>{if(t?.cache?.has(e)){let n=t.cache.get(e);if(n)return{src:e,data:n}}let n=await a(e,{fetch:t?.fetch,timeout:r}).then(e=>e.arrayBuffer());return t?.cache?.set(e,n),{src:e,data:n}});return n?Promise.all(i):(await Promise.allSettled(i)).filter(e=>e.status===`fulfilled`).map(e=>e.value)}const s=`https://fonts.googleapis.com/css2`;function c(e,{weight:t=400,style:n=`normal`}){let r=(Array.isArray(t)?[...t].sort((e,t)=>e-t):[t]).map(String),i=Array.isArray(n)?n:[n],a;return a=i.includes(`italic`)?`ital,wght@${(i.includes(`normal`)?[0,1]:[1]).flatMap(e=>r.map(t=>`${e},${t}`)).sort().join(`;`)}`:`wght@${r.join(`;`)}`,`${e}:${a}`}function l(e,t){let n=new URL(s);return n.searchParams.append(`family`,c(e,t)),t.display&&n.searchParams.set(`display`,t.display),t.text&&n.searchParams.set(`text`,t.text),n.toString()}function u(e,t){return a(e,{...t,init:{headers:{"User-Agent":`Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36`}}}).then(e=>e.text())}const d=/(?:\/\*\s*([^*]+?)\s*\*\/\s*)?@font-face\s*\{([^}]*)\}/g;async function f(e,t={}){return m(await u(l(e,t),t)).map(n=>({name:e,key:n.url,weight:n.weight,style:n.style,data:()=>a(n.url,t).then(e=>e.arrayBuffer())}))}function p(e){let t=[];for(let n of e.split(`,`)){let e=n.trim().replace(/^U\+/i,``);if(e)if(e.includes(`-`)){let[n,r]=e.split(`-`);n&&r&&t.push([parseInt(n,16),parseInt(r,16)])}else if(e.includes(`?`))t.push([parseInt(e.replace(/\?/g,`0`),16),parseInt(e.replace(/\?/g,`F`),16)]);else{let n=parseInt(e,16);t.push([n,n])}}return t}function m(e){let t=[],n=0;for(let r of e.matchAll(d)){let e=r[2];if(!e)continue;let i=e.match(/src:\s*url\(([^)]+)\)/)?.[1]?.replace(/['"]/g,``).trim(),a=e.match(/font-family:\s*['"]?([^'";]+)['"]?/)?.[1]?.trim();if(!i||!a)continue;let o=e.match(/unicode-range:\s*([^;]+)/)?.[1],s=e.match(/font-weight:\s*(\d+)(?:\s+(\d+))?/);t.push({family:a,subset:r[1]?.trim()||`subset-${n}`,url:i,weight:s&&!s[2]?Number(s[1]):void 0,style:e.match(/font-style:\s*([a-z]+)/i)?.[1],ranges:o?p(o):[]}),n+=1}return t}function h(e){let t=new Set,n=e=>{for(let n of e)t.add(n.codePointAt(0))},r=e=>{e.type===`text`?n(e.text):e.type===`container`&&e.children?.forEach(r)};return typeof e==`string`?n(e):Array.isArray(e)?e.forEach(r):r(e),t}function g(e,t){if(e.length===0)return!0;for(let n of t)for(let[t,r]of e)if(n>=t&&n<=r)return!0;return!1}function _(e){let t=new URL(s);for(let n of e)t.searchParams.append(`family`,c(n.family,n));return t.toString()}async function v(e,t){let n=t.cache?.get(e);if(n!==void 0)return n;let r=await u(e,t);return t.cache?.set(e,r),r}async function y(e,t,n={}){let r=h(e);return m(await v(_(t.map(e=>typeof e==`string`?{family:e}:e)),n)).filter(e=>g(e.ranges,r)).map(e=>({name:`${e.family} ${e.subset}`,subsetOf:e.family,key:e.url,weight:e.weight,style:e.style,data:()=>a(e.url,n).then(e=>e.arrayBuffer())}))}exports.container=e.t,exports.em=e.n,exports.extractResourceUrls=i,exports.fetchOk=a,exports.fetchResources=o,exports.fr=e.r,exports.googleFont=f,exports.googleFontSubsets=y,exports.image=e.i,exports.percentage=e.a,exports.rem=e.o,exports.rgba=e.s,exports.style=e.c,exports.text=e.l,exports.vh=e.u,exports.vw=e.d;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./helpers-4kGcvc-Q.cjs"),t=/url\(\s*(['"]?)(.*?)\1\s*\)/g;function n(e){return e.startsWith(`https://`)||e.startsWith(`http://`)}function r(e,i){if(typeof e==`string`)for(let r of e.matchAll(t)){let e=r[2]?.trim();e&&n(e)&&i.add(e)}else if(Array.isArray(e))for(let t of e)r(t,i)}function i(e){let t=new Set,i=e=>{let a=e=>{e&&(r(e.backgroundImage,t),r(e.maskImage,t))};if(a(e.style),a(e.preset),r(e.tw,t),e.type===`image`){typeof e.src==`string`&&n(e.src)&&t.add(e.src);return}if(e.type===`container`)for(let t of e.children??[])i(t)};return i(e),[...t]}async function a(e,t={}){let n=t.fetch??globalThis.fetch,r=t.timeout===void 0?t.init?.signal:AbortSignal.timeout(t.timeout),i=await n(e,{...t.init,signal:r});if(!i.ok)throw Error(`HTTP ${i.status} ${i.statusText} fetching ${e}`);return i}async function o(e,t){let n=t?.throwOnError??!0,r=t?.timeout??5e3,i=[...new Set(e)].map(async e=>{if(t?.cache?.has(e)){let n=t.cache.get(e);if(n)return{src:e,data:n}}let n=await a(e,{fetch:t?.fetch,timeout:r}).then(e=>e.arrayBuffer());return t?.cache?.set(e,n),{src:e,data:n}});return n?Promise.all(i):(await Promise.allSettled(i)).filter(e=>e.status===`fulfilled`).map(e=>e.value)}function s(e,t,n,r){let i=new Map(r),a=n.includes(`italic`),o=a?n.includes(`normal`)?[0,1]:[1]:[void 0],s=[...a?[`ital`]:[],...i.keys(),`wght`].sort(),c=o.flatMap(e=>t.map(t=>s.map(n=>n===`ital`?String(e):n===`wght`?t:i.get(n)??``).join(`,`))).sort();return`${e}:${s.join(`,`)}@${c.join(`;`)}`}function c(e){let t=new URL(`https://fonts.googleapis.com/css2`);for(let n of e.families){if(typeof n==`string`){t.searchParams.append(`family`,s(n,[`400`],[`normal`],[]));continue}let e=n.weight??400,r=(Array.isArray(e)?[...e].sort((e,t)=>e-t):[e]).map(String),i=n.style??`normal`,a=(Array.isArray(i)?i:[i]).map(String),o=Object.entries(n.axes??{}).filter(([e])=>e!==`ital`&&e!==`wght`).map(([e,t])=>[e,String(t)]);t.searchParams.append(`family`,s(n.family,r,a,o))}return e.display&&t.searchParams.set(`display`,e.display),e.text&&t.searchParams.set(`text`,e.text),t.toString()}function l(e,t){return a(e,{...t,init:{headers:{"User-Agent":`Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36`}}}).then(e=>e.text())}async function u(e,t){let n=t.cache?.get(e);if(n!==void 0)return n;let r=await l(e,t);return t.cache?.set(e,r),r}const d=/(?:\/\*\s*([^*]+?)\s*\*\/\s*)?@font-face\s*\{([^}]*)\}/g;function f(e,t){let n=`${e.family} ${e.subset}`,r=e.ranges.map(([e,t])=>`${e}-${t}`).join(`,`);return{name:n,subsetOf:e.family,key:`${n}:${e.weight??``}:${e.style??``}:${r}`,weight:e.weight,style:e.style,ranges:e.ranges,data:()=>a(e.url,t).then(e=>e.arrayBuffer())}}function p(e){let t=[];for(let n of e.split(`,`)){let e=n.trim().replace(/^U\+/i,``);if(e)if(e.includes(`-`)){let[n,r]=e.split(`-`);n&&r&&t.push([parseInt(n,16),parseInt(r,16)])}else if(e.includes(`?`))t.push([parseInt(e.replace(/\?/g,`0`),16),parseInt(e.replace(/\?/g,`F`),16)]);else{let n=parseInt(e,16);t.push([n,n])}}return t}function m(e){let t=[],n=0;for(let r of e.matchAll(d)){let e=r[2];if(!e)continue;let i=e.match(/src:\s*url\(([^)]+)\)/)?.[1]?.replace(/['"]/g,``).trim(),a=e.match(/font-family:\s*['"]?([^'";]+)['"]?/)?.[1]?.trim();if(!i||!a)continue;let o=e.match(/unicode-range:\s*([^;]+)/)?.[1],s=e.match(/font-weight:\s*(\d+)(?:\s+(\d+))?/);t.push({family:a,subset:r[1]?.trim()||`subset-${n}`,url:i,weight:s&&!s[2]?Number(s[1]):void 0,style:e.match(/font-style:\s*([a-z]+)/i)?.[1],ranges:o?p(o):[]}),n+=1}return t}function h(e){let t=new Set,n=e=>{for(let n of e)t.add(n.codePointAt(0))},r=e=>{e.type===`text`?n(e.text):e.type===`container`&&e.children?.forEach(r)};return typeof e==`string`?n(e):Array.isArray(e)?e.forEach(r):r(e),t}function g(e,t){if(e.length===0)return!0;for(let n of t)for(let[t,r]of e)if(n>=t&&n<=r)return!0;return!1}function _({fonts:e,source:t}){let n=h(t);return e.filter(e=>g(e.ranges??[],n))}function v({fonts:e,source:t,subset:n}){return e&&n!==!1?_({fonts:e,source:t}):e}async function y(e){return e.families.length===0?[]:m(await u(c(e),e)).map(t=>f(t,e))}exports.collectCodepoints=h,exports.container=e.t,exports.em=e.n,exports.extractResourceUrls=i,exports.fetchOk=a,exports.fetchResources=o,exports.fr=e.r,exports.googleFonts=y,exports.image=e.i,exports.percentage=e.a,exports.pickFonts=v,exports.rem=e.o,exports.rgba=e.s,exports.style=e.c,exports.subsetFonts=_,exports.text=e.l,exports.vh=e.u,exports.vw=e.d;
package/dist/index.d.cts CHANGED
@@ -1,118 +1,3 @@
1
1
  import { a as NodeMetadata, c as TextNode, i as NodeAttributes, n as ImageNode, o as ReactElementLike, r as Node, s as TextFit, t as ContainerNode } from "./types-xgX3CGse.cjs";
2
- import { CSSProperties } from "react";
3
-
4
- //#region src/utils.d.ts
5
- declare function extractResourceUrls(node: Node): string[];
6
- type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
7
- type FetchOptions = {
8
- /** Custom fetch implementation. @default globalThis.fetch */fetch?: FetchLike; /** Abort the request after this many milliseconds. */
9
- timeout?: number;
10
- };
11
- /** Fetches a URL, applying a timeout signal and throwing on a non-OK status. */
12
- declare function fetchOk(url: string, options?: FetchOptions & {
13
- init?: RequestInit;
14
- }): Promise<Response>;
15
- type FetchResourcesOptions = FetchOptions & {
16
- /**
17
- * Whether to throw on any fetch failure. If false, returns only successful fetches.
18
- * @default true
19
- */
20
- throwOnError?: boolean;
21
- /**
22
- * Cache for fetched resources.
23
- * Custom features (like LRU, TTL, etc.) can be implemented by providing an extended `Map<string, ArrayBuffer>`.
24
- */
25
- cache?: Pick<Map<string, ArrayBuffer>, "has" | "get" | "set">;
26
- };
27
- /**
28
- * Fetches multiple resources concurrently, deduplicating URLs.
29
- *
30
- * @param urls - URLs to fetch
31
- * @param options - Fetch options; `timeout` defaults to 5000ms
32
- * @returns Array of { src: string, data: ArrayBuffer }
33
- */
34
- declare function fetchResources(urls: string[], options?: FetchResourcesOptions): Promise<{
35
- src: string;
36
- data: ArrayBuffer;
37
- }[]>;
38
- //#endregion
39
- //#region src/fonts.d.ts
40
- type FontStyle = "normal" | "italic";
41
- type GoogleFontOptions = FetchOptions & {
42
- /**
43
- * `400` for one weight, `[400, 700]` for several, or a range like `"100..900"` to load
44
- * the variable font. A range leaves the weight unset so CSS `font-weight` controls it.
45
- * @default 400
46
- */
47
- weight?: number | number[] | `${number}..${number}`; /** `"normal"`, `"italic"`, or both. @default "normal" */
48
- style?: FontStyle | FontStyle[]; /** Limit the download to the glyphs used in this text. Recommended for OG images. */
49
- text?: string; /** `font-display` strategy passed through to the CSS request. */
50
- display?: "auto" | "block" | "swap" | "fallback" | "optional";
51
- };
52
- /**
53
- * Load a Google Font as descriptors you can pass to a renderer's `fonts`. Fetches the
54
- * Google Fonts CSS, reads the `woff2` URLs, and returns one loader per file. Each file
55
- * downloads when the renderer first needs it; the renderer skips files it already loaded.
56
- *
57
- * @example
58
- * fonts: await googleFont("Inter", { weight: [400, 700] })
59
- * @example
60
- * fonts: await googleFont("Inter", { weight: "100..900" }) // variable
61
- * @example
62
- * fonts: await googleFont("Inter", { weight: 700, style: "italic", text: "Hello" })
63
- */
64
- declare function googleFont(family: string, options?: GoogleFontOptions): Promise<{
65
- name: string;
66
- key: string;
67
- weight: number | undefined;
68
- style: string | undefined;
69
- data: () => Promise<ArrayBuffer>;
70
- }[]>;
71
- /** A loaded Google Font subset, ready to hand to a renderer's `fonts`. */
72
- type GoogleFontSubset = {
73
- /** Unique internal family name (`"{family} {subset}"`); never written in CSS. */name: string; /** Logical family authors reference in `font-family`; expands to every loaded subset. */
74
- subsetOf: string; /** Subset woff2 URL, also the dedup key. */
75
- key: string;
76
- weight?: number;
77
- style?: string; /** Fetches the subset bytes; the renderer skips files it has already loaded. */
78
- data: () => Promise<ArrayBuffer>;
79
- };
80
- /** A Google family to load, plus how — same options as {@link googleFont} minus `text`. */
81
- type GoogleFontFamily = string | ({
82
- family: string;
83
- } & Omit<GoogleFontOptions, "text">);
84
- type GoogleFontSubsetsOptions = FetchOptions & {
85
- /**
86
- * Cache for the Google Fonts CSS, keyed by request URL. Reuse one across renders (e.g. a
87
- * playground re-rendering on every edit) so the metadata is fetched and parsed only once.
88
- */
89
- cache?: Pick<Map<string, string>, "has" | "get" | "set">;
90
- };
91
- /**
92
- * Load only the Google Font subsets that `source` actually renders: scan its codepoints,
93
- * fetch every family's metadata in ONE css2 request, and keep just the intersecting
94
- * `unicode-range` subsets. Each registers uniquely-named under its `subsetOf` family, which
95
- * `font-family: {family}` expands across, so every script routes to the subset that covers
96
- * it. No font anxiety.
97
- *
98
- * @example
99
- * const fonts = await googleFontSubsets(element, ["Inter", "Noto Sans JP"]);
100
- * await render(element, { width, height, fonts });
101
- */
102
- declare function googleFontSubsets(source: string | Node | Node[], families: GoogleFontFamily[], options?: GoogleFontSubsetsOptions): Promise<GoogleFontSubset[]>;
103
- //#endregion
104
- //#region src/helpers.d.ts
105
- declare function container(props: Omit<ContainerNode, "type">): ContainerNode;
106
- declare function text(text: string, style?: CSSProperties): TextNode;
107
- declare function text(props: Omit<TextNode, "type">): TextNode;
108
- declare function image(props: Omit<ImageNode, "type">): ImageNode;
109
- declare function style(style: CSSProperties): CSSProperties;
110
- declare function percentage(percentage: number): `${number}%`;
111
- declare function vw(vw: number): `${number}vw`;
112
- declare function vh(vh: number): `${number}vh`;
113
- declare function em(em: number): `${number}em`;
114
- declare function rem(rem: number): `${number}rem`;
115
- declare function fr(fr: number): `${number}fr`;
116
- declare function rgba(r: number, g: number, b: number, a?: number): `rgb(${number} ${number} ${number} / ${number})`;
117
- //#endregion
118
- export { ContainerNode, FetchLike, FetchOptions, FetchResourcesOptions, GoogleFontFamily, GoogleFontOptions, GoogleFontSubset, GoogleFontSubsetsOptions, ImageNode, Node, NodeAttributes, NodeMetadata, ReactElementLike, TextFit, TextNode, container, em, extractResourceUrls, fetchOk, fetchResources, fr, googleFont, googleFontSubsets, image, percentage, rem, rgba, style, text, vh, vw };
2
+ import { C as fetchOk, S as extractResourceUrls, _ as pickFonts, a as percentage, b as FetchOptions, c as style, d as vw, f as FontSubset, g as googleFonts, h as collectCodepoints, i as image, l as text, m as GoogleFontsOptions, n as em, o as rem, p as GoogleFontFamily, r as fr, s as rgba, t as container, u as vh, v as subsetFonts, w as fetchResources, x as FetchResourcesOptions, y as FetchLike } from "./index-BZPeMqLs.cjs";
3
+ export { ContainerNode, FetchLike, FetchOptions, FetchResourcesOptions, FontSubset, GoogleFontFamily, GoogleFontsOptions, ImageNode, Node, NodeAttributes, NodeMetadata, ReactElementLike, TextFit, TextNode, collectCodepoints, container, em, extractResourceUrls, fetchOk, fetchResources, fr, googleFonts, image, percentage, pickFonts, rem, rgba, style, subsetFonts, text, vh, vw };
package/dist/index.d.mts CHANGED
@@ -1,118 +1,3 @@
1
1
  import { a as NodeMetadata, c as TextNode, i as NodeAttributes, n as ImageNode, o as ReactElementLike, r as Node, s as TextFit, t as ContainerNode } from "./types-xgX3CGse.mjs";
2
- import { CSSProperties } from "react";
3
-
4
- //#region src/utils.d.ts
5
- declare function extractResourceUrls(node: Node): string[];
6
- type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
7
- type FetchOptions = {
8
- /** Custom fetch implementation. @default globalThis.fetch */fetch?: FetchLike; /** Abort the request after this many milliseconds. */
9
- timeout?: number;
10
- };
11
- /** Fetches a URL, applying a timeout signal and throwing on a non-OK status. */
12
- declare function fetchOk(url: string, options?: FetchOptions & {
13
- init?: RequestInit;
14
- }): Promise<Response>;
15
- type FetchResourcesOptions = FetchOptions & {
16
- /**
17
- * Whether to throw on any fetch failure. If false, returns only successful fetches.
18
- * @default true
19
- */
20
- throwOnError?: boolean;
21
- /**
22
- * Cache for fetched resources.
23
- * Custom features (like LRU, TTL, etc.) can be implemented by providing an extended `Map<string, ArrayBuffer>`.
24
- */
25
- cache?: Pick<Map<string, ArrayBuffer>, "has" | "get" | "set">;
26
- };
27
- /**
28
- * Fetches multiple resources concurrently, deduplicating URLs.
29
- *
30
- * @param urls - URLs to fetch
31
- * @param options - Fetch options; `timeout` defaults to 5000ms
32
- * @returns Array of { src: string, data: ArrayBuffer }
33
- */
34
- declare function fetchResources(urls: string[], options?: FetchResourcesOptions): Promise<{
35
- src: string;
36
- data: ArrayBuffer;
37
- }[]>;
38
- //#endregion
39
- //#region src/fonts.d.ts
40
- type FontStyle = "normal" | "italic";
41
- type GoogleFontOptions = FetchOptions & {
42
- /**
43
- * `400` for one weight, `[400, 700]` for several, or a range like `"100..900"` to load
44
- * the variable font. A range leaves the weight unset so CSS `font-weight` controls it.
45
- * @default 400
46
- */
47
- weight?: number | number[] | `${number}..${number}`; /** `"normal"`, `"italic"`, or both. @default "normal" */
48
- style?: FontStyle | FontStyle[]; /** Limit the download to the glyphs used in this text. Recommended for OG images. */
49
- text?: string; /** `font-display` strategy passed through to the CSS request. */
50
- display?: "auto" | "block" | "swap" | "fallback" | "optional";
51
- };
52
- /**
53
- * Load a Google Font as descriptors you can pass to a renderer's `fonts`. Fetches the
54
- * Google Fonts CSS, reads the `woff2` URLs, and returns one loader per file. Each file
55
- * downloads when the renderer first needs it; the renderer skips files it already loaded.
56
- *
57
- * @example
58
- * fonts: await googleFont("Inter", { weight: [400, 700] })
59
- * @example
60
- * fonts: await googleFont("Inter", { weight: "100..900" }) // variable
61
- * @example
62
- * fonts: await googleFont("Inter", { weight: 700, style: "italic", text: "Hello" })
63
- */
64
- declare function googleFont(family: string, options?: GoogleFontOptions): Promise<{
65
- name: string;
66
- key: string;
67
- weight: number | undefined;
68
- style: string | undefined;
69
- data: () => Promise<ArrayBuffer>;
70
- }[]>;
71
- /** A loaded Google Font subset, ready to hand to a renderer's `fonts`. */
72
- type GoogleFontSubset = {
73
- /** Unique internal family name (`"{family} {subset}"`); never written in CSS. */name: string; /** Logical family authors reference in `font-family`; expands to every loaded subset. */
74
- subsetOf: string; /** Subset woff2 URL, also the dedup key. */
75
- key: string;
76
- weight?: number;
77
- style?: string; /** Fetches the subset bytes; the renderer skips files it has already loaded. */
78
- data: () => Promise<ArrayBuffer>;
79
- };
80
- /** A Google family to load, plus how — same options as {@link googleFont} minus `text`. */
81
- type GoogleFontFamily = string | ({
82
- family: string;
83
- } & Omit<GoogleFontOptions, "text">);
84
- type GoogleFontSubsetsOptions = FetchOptions & {
85
- /**
86
- * Cache for the Google Fonts CSS, keyed by request URL. Reuse one across renders (e.g. a
87
- * playground re-rendering on every edit) so the metadata is fetched and parsed only once.
88
- */
89
- cache?: Pick<Map<string, string>, "has" | "get" | "set">;
90
- };
91
- /**
92
- * Load only the Google Font subsets that `source` actually renders: scan its codepoints,
93
- * fetch every family's metadata in ONE css2 request, and keep just the intersecting
94
- * `unicode-range` subsets. Each registers uniquely-named under its `subsetOf` family, which
95
- * `font-family: {family}` expands across, so every script routes to the subset that covers
96
- * it. No font anxiety.
97
- *
98
- * @example
99
- * const fonts = await googleFontSubsets(element, ["Inter", "Noto Sans JP"]);
100
- * await render(element, { width, height, fonts });
101
- */
102
- declare function googleFontSubsets(source: string | Node | Node[], families: GoogleFontFamily[], options?: GoogleFontSubsetsOptions): Promise<GoogleFontSubset[]>;
103
- //#endregion
104
- //#region src/helpers.d.ts
105
- declare function container(props: Omit<ContainerNode, "type">): ContainerNode;
106
- declare function text(text: string, style?: CSSProperties): TextNode;
107
- declare function text(props: Omit<TextNode, "type">): TextNode;
108
- declare function image(props: Omit<ImageNode, "type">): ImageNode;
109
- declare function style(style: CSSProperties): CSSProperties;
110
- declare function percentage(percentage: number): `${number}%`;
111
- declare function vw(vw: number): `${number}vw`;
112
- declare function vh(vh: number): `${number}vh`;
113
- declare function em(em: number): `${number}em`;
114
- declare function rem(rem: number): `${number}rem`;
115
- declare function fr(fr: number): `${number}fr`;
116
- declare function rgba(r: number, g: number, b: number, a?: number): `rgb(${number} ${number} ${number} / ${number})`;
117
- //#endregion
118
- export { ContainerNode, FetchLike, FetchOptions, FetchResourcesOptions, GoogleFontFamily, GoogleFontOptions, GoogleFontSubset, GoogleFontSubsetsOptions, ImageNode, Node, NodeAttributes, NodeMetadata, ReactElementLike, TextFit, TextNode, container, em, extractResourceUrls, fetchOk, fetchResources, fr, googleFont, googleFontSubsets, image, percentage, rem, rgba, style, text, vh, vw };
2
+ import { C as fetchOk, S as extractResourceUrls, _ as pickFonts, a as percentage, b as FetchOptions, c as style, d as vw, f as FontSubset, g as googleFonts, h as collectCodepoints, i as image, l as text, m as GoogleFontsOptions, n as em, o as rem, p as GoogleFontFamily, r as fr, s as rgba, t as container, u as vh, v as subsetFonts, w as fetchResources, x as FetchResourcesOptions, y as FetchLike } from "./index-BSz7yODB.mjs";
3
+ export { ContainerNode, FetchLike, FetchOptions, FetchResourcesOptions, FontSubset, GoogleFontFamily, GoogleFontsOptions, ImageNode, Node, NodeAttributes, NodeMetadata, ReactElementLike, TextFit, TextNode, collectCodepoints, container, em, extractResourceUrls, fetchOk, fetchResources, fr, googleFonts, image, percentage, pickFonts, rem, rgba, style, subsetFonts, text, vh, vw };
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{a as e,c as t,d as n,i as r,l as i,n as a,o,r as s,s as c,t as l,u}from"./helpers-CVQCT1Rh.mjs";const d=/url\(\s*(['"]?)(.*?)\1\s*\)/g;function f(e){return e.startsWith(`https://`)||e.startsWith(`http://`)}function p(e,t){if(typeof e==`string`)for(let n of e.matchAll(d)){let e=n[2]?.trim();e&&f(e)&&t.add(e)}else if(Array.isArray(e))for(let n of e)p(n,t)}function m(e){let t=new Set,n=e=>{let r=e=>{e&&(p(e.backgroundImage,t),p(e.maskImage,t))};if(r(e.style),r(e.preset),p(e.tw,t),e.type===`image`){typeof e.src==`string`&&f(e.src)&&t.add(e.src);return}if(e.type===`container`)for(let t of e.children??[])n(t)};return n(e),[...t]}async function h(e,t={}){let n=t.fetch??globalThis.fetch,r=t.timeout===void 0?t.init?.signal:AbortSignal.timeout(t.timeout),i=await n(e,{...t.init,signal:r});if(!i.ok)throw Error(`HTTP ${i.status} ${i.statusText} fetching ${e}`);return i}async function g(e,t){let n=t?.throwOnError??!0,r=t?.timeout??5e3,i=[...new Set(e)].map(async e=>{if(t?.cache?.has(e)){let n=t.cache.get(e);if(n)return{src:e,data:n}}let n=await h(e,{fetch:t?.fetch,timeout:r}).then(e=>e.arrayBuffer());return t?.cache?.set(e,n),{src:e,data:n}});return n?Promise.all(i):(await Promise.allSettled(i)).filter(e=>e.status===`fulfilled`).map(e=>e.value)}const _=`https://fonts.googleapis.com/css2`;function v(e,{weight:t=400,style:n=`normal`}){let r=(Array.isArray(t)?[...t].sort((e,t)=>e-t):[t]).map(String),i=Array.isArray(n)?n:[n],a;return a=i.includes(`italic`)?`ital,wght@${(i.includes(`normal`)?[0,1]:[1]).flatMap(e=>r.map(t=>`${e},${t}`)).sort().join(`;`)}`:`wght@${r.join(`;`)}`,`${e}:${a}`}function y(e,t){let n=new URL(_);return n.searchParams.append(`family`,v(e,t)),t.display&&n.searchParams.set(`display`,t.display),t.text&&n.searchParams.set(`text`,t.text),n.toString()}function b(e,t){return h(e,{...t,init:{headers:{"User-Agent":`Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36`}}}).then(e=>e.text())}const x=/(?:\/\*\s*([^*]+?)\s*\*\/\s*)?@font-face\s*\{([^}]*)\}/g;async function S(e,t={}){return w(await b(y(e,t),t)).map(n=>({name:e,key:n.url,weight:n.weight,style:n.style,data:()=>h(n.url,t).then(e=>e.arrayBuffer())}))}function C(e){let t=[];for(let n of e.split(`,`)){let e=n.trim().replace(/^U\+/i,``);if(e)if(e.includes(`-`)){let[n,r]=e.split(`-`);n&&r&&t.push([parseInt(n,16),parseInt(r,16)])}else if(e.includes(`?`))t.push([parseInt(e.replace(/\?/g,`0`),16),parseInt(e.replace(/\?/g,`F`),16)]);else{let n=parseInt(e,16);t.push([n,n])}}return t}function w(e){let t=[],n=0;for(let r of e.matchAll(x)){let e=r[2];if(!e)continue;let i=e.match(/src:\s*url\(([^)]+)\)/)?.[1]?.replace(/['"]/g,``).trim(),a=e.match(/font-family:\s*['"]?([^'";]+)['"]?/)?.[1]?.trim();if(!i||!a)continue;let o=e.match(/unicode-range:\s*([^;]+)/)?.[1],s=e.match(/font-weight:\s*(\d+)(?:\s+(\d+))?/);t.push({family:a,subset:r[1]?.trim()||`subset-${n}`,url:i,weight:s&&!s[2]?Number(s[1]):void 0,style:e.match(/font-style:\s*([a-z]+)/i)?.[1],ranges:o?C(o):[]}),n+=1}return t}function T(e){let t=new Set,n=e=>{for(let n of e)t.add(n.codePointAt(0))},r=e=>{e.type===`text`?n(e.text):e.type===`container`&&e.children?.forEach(r)};return typeof e==`string`?n(e):Array.isArray(e)?e.forEach(r):r(e),t}function E(e,t){if(e.length===0)return!0;for(let n of t)for(let[t,r]of e)if(n>=t&&n<=r)return!0;return!1}function D(e){let t=new URL(_);for(let n of e)t.searchParams.append(`family`,v(n.family,n));return t.toString()}async function O(e,t){let n=t.cache?.get(e);if(n!==void 0)return n;let r=await b(e,t);return t.cache?.set(e,r),r}async function k(e,t,n={}){let r=T(e);return w(await O(D(t.map(e=>typeof e==`string`?{family:e}:e)),n)).filter(e=>E(e.ranges,r)).map(e=>({name:`${e.family} ${e.subset}`,subsetOf:e.family,key:e.url,weight:e.weight,style:e.style,data:()=>h(e.url,n).then(e=>e.arrayBuffer())}))}export{l as container,a as em,m as extractResourceUrls,h as fetchOk,g as fetchResources,s as fr,S as googleFont,k as googleFontSubsets,r as image,e as percentage,o as rem,c as rgba,t as style,i as text,u as vh,n as vw};
1
+ import{a as e,c as t,d as n,i as r,l as i,n as a,o,r as s,s as c,t as l,u}from"./helpers-CVQCT1Rh.mjs";const d=/url\(\s*(['"]?)(.*?)\1\s*\)/g;function f(e){return e.startsWith(`https://`)||e.startsWith(`http://`)}function p(e,t){if(typeof e==`string`)for(let n of e.matchAll(d)){let e=n[2]?.trim();e&&f(e)&&t.add(e)}else if(Array.isArray(e))for(let n of e)p(n,t)}function m(e){let t=new Set,n=e=>{let r=e=>{e&&(p(e.backgroundImage,t),p(e.maskImage,t))};if(r(e.style),r(e.preset),p(e.tw,t),e.type===`image`){typeof e.src==`string`&&f(e.src)&&t.add(e.src);return}if(e.type===`container`)for(let t of e.children??[])n(t)};return n(e),[...t]}async function h(e,t={}){let n=t.fetch??globalThis.fetch,r=t.timeout===void 0?t.init?.signal:AbortSignal.timeout(t.timeout),i=await n(e,{...t.init,signal:r});if(!i.ok)throw Error(`HTTP ${i.status} ${i.statusText} fetching ${e}`);return i}async function g(e,t){let n=t?.throwOnError??!0,r=t?.timeout??5e3,i=[...new Set(e)].map(async e=>{if(t?.cache?.has(e)){let n=t.cache.get(e);if(n)return{src:e,data:n}}let n=await h(e,{fetch:t?.fetch,timeout:r}).then(e=>e.arrayBuffer());return t?.cache?.set(e,n),{src:e,data:n}});return n?Promise.all(i):(await Promise.allSettled(i)).filter(e=>e.status===`fulfilled`).map(e=>e.value)}function _(e,t,n,r){let i=new Map(r),a=n.includes(`italic`),o=a?n.includes(`normal`)?[0,1]:[1]:[void 0],s=[...a?[`ital`]:[],...i.keys(),`wght`].sort(),c=o.flatMap(e=>t.map(t=>s.map(n=>n===`ital`?String(e):n===`wght`?t:i.get(n)??``).join(`,`))).sort();return`${e}:${s.join(`,`)}@${c.join(`;`)}`}function v(e){let t=new URL(`https://fonts.googleapis.com/css2`);for(let n of e.families){if(typeof n==`string`){t.searchParams.append(`family`,_(n,[`400`],[`normal`],[]));continue}let e=n.weight??400,r=(Array.isArray(e)?[...e].sort((e,t)=>e-t):[e]).map(String),i=n.style??`normal`,a=(Array.isArray(i)?i:[i]).map(String),o=Object.entries(n.axes??{}).filter(([e])=>e!==`ital`&&e!==`wght`).map(([e,t])=>[e,String(t)]);t.searchParams.append(`family`,_(n.family,r,a,o))}return e.display&&t.searchParams.set(`display`,e.display),e.text&&t.searchParams.set(`text`,e.text),t.toString()}function y(e,t){return h(e,{...t,init:{headers:{"User-Agent":`Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36`}}}).then(e=>e.text())}async function b(e,t){let n=t.cache?.get(e);if(n!==void 0)return n;let r=await y(e,t);return t.cache?.set(e,r),r}const x=/(?:\/\*\s*([^*]+?)\s*\*\/\s*)?@font-face\s*\{([^}]*)\}/g;function S(e,t){let n=`${e.family} ${e.subset}`,r=e.ranges.map(([e,t])=>`${e}-${t}`).join(`,`);return{name:n,subsetOf:e.family,key:`${n}:${e.weight??``}:${e.style??``}:${r}`,weight:e.weight,style:e.style,ranges:e.ranges,data:()=>h(e.url,t).then(e=>e.arrayBuffer())}}function C(e){let t=[];for(let n of e.split(`,`)){let e=n.trim().replace(/^U\+/i,``);if(e)if(e.includes(`-`)){let[n,r]=e.split(`-`);n&&r&&t.push([parseInt(n,16),parseInt(r,16)])}else if(e.includes(`?`))t.push([parseInt(e.replace(/\?/g,`0`),16),parseInt(e.replace(/\?/g,`F`),16)]);else{let n=parseInt(e,16);t.push([n,n])}}return t}function w(e){let t=[],n=0;for(let r of e.matchAll(x)){let e=r[2];if(!e)continue;let i=e.match(/src:\s*url\(([^)]+)\)/)?.[1]?.replace(/['"]/g,``).trim(),a=e.match(/font-family:\s*['"]?([^'";]+)['"]?/)?.[1]?.trim();if(!i||!a)continue;let o=e.match(/unicode-range:\s*([^;]+)/)?.[1],s=e.match(/font-weight:\s*(\d+)(?:\s+(\d+))?/);t.push({family:a,subset:r[1]?.trim()||`subset-${n}`,url:i,weight:s&&!s[2]?Number(s[1]):void 0,style:e.match(/font-style:\s*([a-z]+)/i)?.[1],ranges:o?C(o):[]}),n+=1}return t}function T(e){let t=new Set,n=e=>{for(let n of e)t.add(n.codePointAt(0))},r=e=>{e.type===`text`?n(e.text):e.type===`container`&&e.children?.forEach(r)};return typeof e==`string`?n(e):Array.isArray(e)?e.forEach(r):r(e),t}function E(e,t){if(e.length===0)return!0;for(let n of t)for(let[t,r]of e)if(n>=t&&n<=r)return!0;return!1}function D({fonts:e,source:t}){let n=T(t);return e.filter(e=>E(e.ranges??[],n))}function O({fonts:e,source:t,subset:n}){return e&&n!==!1?D({fonts:e,source:t}):e}async function k(e){return e.families.length===0?[]:w(await b(v(e),e)).map(t=>S(t,e))}export{T as collectCodepoints,l as container,a as em,m as extractResourceUrls,h as fetchOk,g as fetchResources,s as fr,k as googleFonts,r as image,e as percentage,O as pickFonts,o as rem,c as rgba,t as style,D as subsetFonts,i as text,u as vh,n as vw};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takumi-rs/helpers",
3
- "version": "2.0.0-beta.6",
3
+ "version": "2.0.0-beta.7",
4
4
  "description": "Utility helpers for converting JSX/HTML to Takumi node trees and handling resources.",
5
5
  "keywords": [
6
6
  "css",
@@ -85,15 +85,15 @@
85
85
  "publish-lint": "attw --pack . && publint --strict ."
86
86
  },
87
87
  "devDependencies": {
88
- "@types/bun": "catalog:",
89
- "@types/react": "catalog:",
90
- "@types/react-dom": "catalog:",
91
- "lucide-react": "catalog:",
92
- "react": "catalog:",
93
- "react-dom": "catalog:",
94
- "tsdown": "catalog:",
88
+ "@types/bun": "^1.3.14",
89
+ "@types/react": "19.2.17",
90
+ "@types/react-dom": "^19.2.3",
91
+ "lucide-react": "^1.21.0",
92
+ "react": "^19.2.7",
93
+ "react-dom": "^19.2.7",
94
+ "tsdown": "^0.22.3",
95
95
  "ultrahtml": "^1.6.0",
96
- "unrun": "catalog:"
96
+ "unrun": "^0.3.1"
97
97
  },
98
98
  "peerDependencies": {
99
99
  "react": "^19.2.5",