feat: init

This commit is contained in:
2026-02-13 22:02:30 +01:00
commit 8f9ff830fb
16711 changed files with 3307340 additions and 0 deletions

21
node_modules/google-fonts-helper/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Datalogix
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.

158
node_modules/google-fonts-helper/README.md generated vendored Normal file
View File

@@ -0,0 +1,158 @@
# google-fonts-helper
[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]
[![Github Actions CI][github-actions-ci-src]][github-actions-ci-href]
[![Codecov][codecov-src]][codecov-href]
[![License][license-src]][license-href]
> [Google Fonts Helper](https://developers.google.com/fonts)
[📖 **Release Notes**](./CHANGELOG.md)
## Install
Install using npm, yarn or pnpm:
```bash
npm install google-fonts-helper
# or
yarn add google-fonts-helper
# or
pnpm add google-fonts-helper
```
Import into your Node.js project:
```js
const {
constructURL,
merge,
isValidURL,
parse,
download,
} = require("google-fonts-helper");
// or
import {
constructURL,
merge,
isValidURL,
parse,
download,
} from "google-fonts-helper";
```
## Usage
### `constructURL(): string`
```ts
constructURL({ families: { Roboto: true } });
// https://fonts.googleapis.com/css2?family=Roboto
constructURL({ families: { Roboto: true, Lato: true } });
// https://fonts.googleapis.com/css2?family=Roboto&family=Lato
constructURL({ families: { "Crimson Pro": { wght: "200..400" } } });
// https://fonts.googleapis.com/css2?family=Crimson+Pro:wght@200..400
constructURL({
families: {
Roboto: true,
Lato: {
wght: 100,
},
Raleway: {
wght: [400],
ital: [100, 400],
},
},
});
// https://fonts.googleapis.com/css2?family=Roboto&family=Lato:wght@100&family=Raleway:ital,wght@0,400;1,100;1,400
```
### `merge(...fonts: GoogleFonts[]): GoogleFonts`
```ts
merge({ families: { Roboto: true } }, { families: { Lato: true } });
// { families: { Roboto: true, Lato: true } }
merge({ families: { Roboto: true } }, { families: { Roboto: [300, 400] } });
// { families: { Roboto: [300, 400] } }
```
### `isValidURL(url: string): boolean`
```ts
isValidURL("https://fonts.googleapis.com/css2?family=Roboto");
// true
isValidURL("https://foo.bar");
// false
```
### `parse(url: string): GoogleFonts`
```ts
parse("https://fonts.googleapis.com/css2?family=Roboto");
// { families: { Roboto: true } }
parse("https://fonts.googleapis.com/css2?family=Roboto&family=Lato");
// { families: { Roboto: true, Lato: true } }
parse("https://fonts.googleapis.com/css2?family=Crimson+Pro:wght@200..400");
// { families: { 'Crimson Pro': { wght: '200..400' } }
parse("https://foo.bar");
// {}
```
### `download(url: string, option?: DownloadOptions): Downloader`
```ts
const downloader = download('https://fonts.googleapis.com/css2?family=Roboto', {
base64: false,
overwriting: false,
outputDir: './',
stylePath: 'fonts.css',
fontsDir: 'fonts',
fontsPath: './fonts'
})
downloader.hook('download-font:before', (font: FontInputOutput) {
console.log(font)
})
downloader.hook('download-font:done', (font: FontInputOutput) {
console.log(font)
})
downloader.hook('download:start', () => {
console.log('Downloading fonts...')
})
downloader.hook('download:complete', () => {
console.log('Download fonts completed.')
})
await downloader.execute()
```
## License
[MIT License](./LICENSE)
Copyright (c) Datalogix
<!-- Badges -->
[npm-version-src]: https://img.shields.io/npm/v/google-fonts-helper/latest.svg
[npm-version-href]: https://npmjs.com/package/google-fonts-helper
[npm-downloads-src]: https://img.shields.io/npm/dt/google-fonts-helper.svg
[npm-downloads-href]: https://npmjs.com/package/google-fonts-helper
[github-actions-ci-src]: https://github.com/datalogix/google-fonts-helper/workflows/ci/badge.svg
[github-actions-ci-href]: https://github.com/datalogix/google-fonts-helper/actions?query=workflow%3Aci
[codecov-src]: https://img.shields.io/codecov/c/github/datalogix/google-fonts-helper.svg
[codecov-href]: https://codecov.io/gh/datalogix/google-fonts-helper
[license-src]: https://img.shields.io/npm/l/google-fonts-helper.svg
[license-href]: https://npmjs.com/package/google-fonts-helper

420
node_modules/google-fonts-helper/dist/index.cjs generated vendored Normal file
View File

@@ -0,0 +1,420 @@
'use strict';
const ufo = require('ufo');
const node_fs = require('node:fs');
const node_path = require('node:path');
const ofetch = require('ofetch');
const hookable = require('hookable');
const deepmerge = require('deepmerge');
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
const deepmerge__default = /*#__PURE__*/_interopDefaultCompat(deepmerge);
const GOOGLE_FONTS_DOMAIN = "fonts.googleapis.com";
function isValidDisplay(display) {
return ["auto", "block", "swap", "fallback", "optional"].includes(display);
}
function parseStyle(style) {
if (["wght", "normal", "regular"].includes(style.toLowerCase())) {
return "wght";
}
if (["ital", "italic", "i"].includes(style.toLowerCase())) {
return "ital";
}
return style;
}
function cartesianProduct(...a) {
return a.length < 2 ? a : a.reduce((a2, b) => a2.flatMap((d) => b.map((e) => [d, e].flat())));
}
function parseFamilyName(name) {
return decodeURIComponent(name).replace(/\+/g, " ");
}
function constructURL({
families,
display,
subsets,
text
} = {}) {
const _subsets = (Array.isArray(subsets) ? subsets : [subsets]).filter(Boolean);
const family = convertFamiliesToArray(families ?? {});
if (family.length < 1) {
return false;
}
const query = {
family
};
if (display && isValidDisplay(display)) {
query.display = display;
}
if (_subsets.length > 0) {
query.subset = _subsets.join(",");
}
if (text) {
query.text = text;
}
return ufo.withHttps(ufo.withQuery(ufo.resolveURL(GOOGLE_FONTS_DOMAIN, "css2"), query));
}
function convertFamiliesToArray(families) {
const result = [];
Object.entries(families).forEach(([name, values]) => {
if (!name) {
return;
}
name = parseFamilyName(name);
if (typeof values === "string" && String(values).includes("..")) {
result.push(`${name}:wght@${values}`);
return;
}
if (Array.isArray(values) && values.length > 0) {
result.push(`${name}:wght@${values.join(";")}`);
return;
}
if (Object.keys(values).length > 0) {
const axes = {};
let italicWeights = [];
Object.entries(values).sort(([styleA], [styleB]) => styleA.localeCompare(styleB)).forEach(([style, weight]) => {
const parsedStyle = parseStyle(style);
if (parsedStyle === "ital") {
axes[parsedStyle] = ["0", "1"];
if (weight === true || weight === 400 || weight === 1) {
italicWeights = ["*"];
} else {
italicWeights = Array.isArray(weight) ? weight.map((w) => String(w)) : [weight];
}
} else {
axes[parseStyle(style)] = Array.isArray(weight) ? weight.map((w) => String(w)) : [weight];
}
});
const strictlyItalic = [];
if (Object.keys(axes).length === 1 && Object.hasOwn(axes, "ital")) {
if (!(italicWeights.includes("*") || italicWeights.length === 1 && italicWeights.includes("400"))) {
axes.wght = italicWeights;
strictlyItalic.push(...italicWeights);
}
} else if (Object.hasOwn(axes, "wght") && !italicWeights.includes("*")) {
strictlyItalic.push(...italicWeights.filter((w) => !axes.wght.includes(w)));
axes.wght = [.../* @__PURE__ */ new Set([...axes.wght, ...italicWeights])];
}
const axisTagList = Object.keys(axes).sort((axisA, axisB) => {
const isLowerA = axisA[0] === axisA[0].toLowerCase();
const isLowerB = axisB[0] === axisB[0].toLowerCase();
if (isLowerA && !isLowerB) {
return -1;
}
if (!isLowerA && isLowerB) {
return 1;
}
return axisA.localeCompare(axisB);
});
if (axisTagList.length === 1 && axisTagList.includes("ital")) {
result.push(`${name}:ital@1`);
return;
}
let axisTupleArrays = cartesianProduct(...axisTagList.map((tag) => axes[tag]), [[]]);
const italicIndex = axisTagList.findIndex((i) => i === "ital");
if (italicIndex !== -1) {
const weightIndex = axisTagList.findIndex((i) => i === "wght");
if (weightIndex !== -1) {
axisTupleArrays = axisTupleArrays.filter((axisTuple) => axisTuple[italicIndex] === "0" && !strictlyItalic.includes(axisTuple[weightIndex]) || axisTuple[italicIndex] === "1" && italicWeights.includes(axisTuple[weightIndex]));
}
}
const axisTupleList = axisTupleArrays.sort((axisTupleA, axisTupleB) => {
for (let i = 0; i < axisTupleA.length; i++) {
const compareResult = parseInt(axisTupleA[i]) - parseInt(axisTupleB[i]);
if (compareResult !== 0) {
return compareResult;
}
}
return 0;
}).map((axisTuple) => axisTuple.join(",")).join(";");
result.push(`${name}:${axisTagList.join(",")}@${axisTupleList}`);
return;
}
if (values) {
result.push(name);
}
});
return result;
}
function isValidURL(url) {
return RegExp(GOOGLE_FONTS_DOMAIN).test(url);
}
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
class Downloader extends hookable.Hookable {
constructor(url, options) {
super();
this.url = url;
__publicField(this, "config");
this.config = {
base64: false,
overwriting: false,
outputDir: "./",
stylePath: "fonts.css",
fontsDir: "fonts",
fontsPath: "./fonts",
headers: [["user-agent", [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
"AppleWebKit/537.36 (KHTML, like Gecko)",
"Chrome/98.0.4758.102 Safari/537.36"
].join(" ")]],
...options
};
}
async execute() {
if (!isValidURL(this.url)) {
throw new Error("Invalid Google Fonts URL");
}
const { outputDir, stylePath, headers, fontsPath } = this.config;
const cssPath = node_path.resolve(outputDir, stylePath);
let overwriting = this.config.overwriting;
if (!overwriting && node_fs.existsSync(cssPath)) {
const currentCssContent = node_fs.readFileSync(cssPath, "utf-8");
const currentUrl = (currentCssContent.split(/\r?\n/, 1).shift() || "").replace("/*", "").replace("*/", "").trim();
if (currentUrl === this.url) {
return false;
}
overwriting = true;
}
await this.callHook("download:start");
const { searchParams } = new URL(this.url);
const subsets = searchParams.get("subset") ? searchParams.get("subset")?.split(",") : void 0;
await this.callHook("download-css:before", this.url);
const _css = await ofetch.ofetch(this.url, { headers });
const { fonts: fontsFromCss, css: cssContent } = parseFontsFromCss(_css, fontsPath, subsets);
await this.callHook("download-css:done", this.url, cssContent, fontsFromCss);
const fonts = await this.downloadFonts(fontsFromCss);
await this.callHook("write-css:before", cssPath, cssContent, fonts);
const newContent = this.writeCss(cssPath, `/* ${this.url} */
${cssContent}`, fonts);
await this.callHook("write-css:done", cssPath, newContent, cssContent);
await this.callHook("download:complete");
return true;
}
async downloadFonts(fonts) {
const { headers, base64, outputDir, fontsDir } = this.config;
const downloadedFonts = [];
const _fonts = [];
for (const font of fonts) {
const downloadedFont = downloadedFonts.find((f) => f.inputFont === font.inputFont);
if (downloadedFont) {
if (base64) {
font.outputText = downloadedFont.outputText;
} else {
node_fs.copyFileSync(
node_path.resolve(outputDir, fontsDir, downloadedFont.outputFont),
node_path.resolve(outputDir, fontsDir, font.outputFont)
);
}
_fonts.push(font);
continue;
}
await this.callHook("download-font:before", font);
const response = await ofetch.ofetch.raw(font.inputFont, { headers, responseType: "arrayBuffer" });
if (!response?._data) {
_fonts.push(font);
continue;
}
const buffer = Buffer.from(response?._data);
if (base64) {
const mime = response.headers.get("content-type") ?? "font/woff2";
font.outputText = `url('data:${mime};base64,${buffer.toString("base64")}')`;
} else {
const fontPath = node_path.resolve(outputDir, fontsDir, font.outputFont);
node_fs.mkdirSync(node_path.dirname(fontPath), { recursive: true });
node_fs.writeFileSync(fontPath, buffer);
}
_fonts.push(font);
await this.callHook("download-font:done", font);
downloadedFonts.push(font);
}
return _fonts;
}
writeCss(path, content, fonts) {
for (const font of fonts) {
content = content.replace(font.inputText, font.outputText);
}
node_fs.mkdirSync(node_path.dirname(path), { recursive: true });
node_fs.writeFileSync(path, content, "utf-8");
return content;
}
}
function parseFontsFromCss(content, fontsPath, subsets) {
const css = [];
const fonts = [];
const re = {
face: /\s*(?:\/\*\s*(.*?)\s*\*\/)?[^@]*?@font-face\s*{(?:[^}]*?)}\s*/gi,
family: /font-family\s*:\s*(?:'|")?([^;]*?)(?:'|")?\s*;/i,
style: /font-style\s*:\s*([^;]*?)\s*;/i,
weight: /font-weight\s*:\s*([^;]*?)\s*;/i,
url: /url\s*\(\s*(?:'|")?\s*([^]*?)\s*(?:'|")?\s*\)\s*?/gi
};
let match1;
while ((match1 = re.face.exec(content)) !== null) {
const [fontface, subset] = match1;
const familyRegExpArray = re.family.exec(fontface);
const family = familyRegExpArray ? familyRegExpArray[1] : "";
const styleRegExpArray = re.style.exec(fontface);
const style = styleRegExpArray ? styleRegExpArray[1] : "";
const weightRegExpArray = re.weight.exec(fontface);
const weight = weightRegExpArray ? weightRegExpArray[1] : "";
if (subsets && subsets.length && !subsets.includes(subset)) {
continue;
}
css.push(fontface);
let match2;
while ((match2 = re.url.exec(fontface)) !== null) {
const [forReplace, url] = match2;
const ext = node_path.extname(url).replace(/^\./, "") || "woff2";
const newFilename = formatFontFileName("{family}-{style}-{weight}-{subset}.{ext}", {
family: family.replace(/\s+/g, "_"),
style: style.replace(/\s+/g, "_") || "normal",
weight: weight.replace(/\s+/g, "_") || "",
subset: subset || "text",
ext
}).replace(/\.$/, "");
fonts.push({
inputFont: url,
outputFont: newFilename,
inputText: forReplace,
outputText: `url('${node_path.posix.join(fontsPath, newFilename)}')`
});
}
}
return {
css: css.join("\n"),
fonts
};
}
function formatFontFileName(template, values) {
return Object.entries(values).filter(([key]) => /^[a-z0-9_-]+$/gi.test(key)).map(([key, value]) => [new RegExp(`([^{]|^){${key}}([^}]|$)`, "g"), `$1${value}$2`]).reduce((str, [regexp, replacement]) => str.replace(regexp, String(replacement)), template).replace(/({|}){2}/g, "$1");
}
function download(url, options) {
return new Downloader(url, options);
}
function merge(...fonts) {
return deepmerge__default.all(fonts);
}
function parse(url) {
const result = {};
if (!isValidURL(url)) {
return result;
}
const { searchParams, pathname } = new URL(url);
if (!searchParams.has("family")) {
return result;
}
const families = convertFamiliesObject(searchParams.getAll("family"), pathname.endsWith("2"));
if (Object.keys(families).length < 1) {
return result;
}
result.families = families;
const display = searchParams.get("display");
if (display && isValidDisplay(display)) {
result.display = display;
}
const subsets = searchParams.get("subset");
if (subsets) {
result.subsets = subsets.split(",");
}
const text = searchParams.get("text");
if (text) {
result.text = text;
}
return result;
}
function convertFamiliesObject(families, v2 = true) {
const result = {};
families.flatMap((family) => family.split("|")).forEach((family) => {
if (!family) {
return;
}
if (!family.includes(":")) {
result[family] = true;
return;
}
const parts = family.split(":");
if (!parts[1]) {
return;
}
const values = {};
if (!v2) {
parts[1].split(",").forEach((style) => {
const styleParsed = parseStyle(style);
if (styleParsed === "wght") {
values.wght = true;
}
if (styleParsed === "ital") {
values.ital = true;
}
if (styleParsed === "bold" || styleParsed === "b") {
values.wght = 700;
}
if (styleParsed === "bolditalic" || styleParsed === "bi") {
values.ital = 700;
}
});
}
if (v2) {
let [styles, weights] = parts[1].split("@");
if (!weights) {
weights = String(styles).replace(",", ";");
styles = "wght";
}
styles.split(",").forEach((style) => {
const styleParsed = parseStyle(style);
values[styleParsed] = weights.split(";").map((weight) => {
if (/^\+?\d+$/.test(weight)) {
return parseInt(weight);
}
const [pos, w] = weight.split(",");
const index = styleParsed === "wght" ? 0 : 1;
if (!w) {
return weight;
}
if (parseInt(pos) !== index) {
return 0;
}
if (/^\+?\d+$/.test(w)) {
return parseInt(w);
}
return w;
}).filter((v) => parseInt(v.toString()) > 0 || v.toString().includes(".."));
if (!values[styleParsed].length) {
values[styleParsed] = true;
return;
}
if (values[styleParsed].length > 1) {
return;
}
const first = values[styleParsed][0];
if (String(first).includes("..")) {
values[styleParsed] = first;
}
if (first === 1 || first === true) {
values[styleParsed] = true;
}
});
}
result[parseFamilyName(parts[0])] = values;
});
return result;
}
exports.Downloader = Downloader;
exports.constructURL = constructURL;
exports.download = download;
exports.isValidURL = isValidURL;
exports.merge = merge;
exports.parse = parse;

15178
node_modules/google-fonts-helper/dist/index.d.cts generated vendored Normal file

File diff suppressed because it is too large Load Diff

15178
node_modules/google-fonts-helper/dist/index.d.mts generated vendored Normal file

File diff suppressed because it is too large Load Diff

15178
node_modules/google-fonts-helper/dist/index.d.ts generated vendored Normal file

File diff suppressed because it is too large Load Diff

409
node_modules/google-fonts-helper/dist/index.mjs generated vendored Normal file
View File

@@ -0,0 +1,409 @@
import { withHttps, withQuery, resolveURL } from 'ufo';
import { existsSync, readFileSync, copyFileSync, mkdirSync, writeFileSync } from 'node:fs';
import { resolve, dirname, extname, posix } from 'node:path';
import { ofetch } from 'ofetch';
import { Hookable } from 'hookable';
import deepmerge from 'deepmerge';
const GOOGLE_FONTS_DOMAIN = "fonts.googleapis.com";
function isValidDisplay(display) {
return ["auto", "block", "swap", "fallback", "optional"].includes(display);
}
function parseStyle(style) {
if (["wght", "normal", "regular"].includes(style.toLowerCase())) {
return "wght";
}
if (["ital", "italic", "i"].includes(style.toLowerCase())) {
return "ital";
}
return style;
}
function cartesianProduct(...a) {
return a.length < 2 ? a : a.reduce((a2, b) => a2.flatMap((d) => b.map((e) => [d, e].flat())));
}
function parseFamilyName(name) {
return decodeURIComponent(name).replace(/\+/g, " ");
}
function constructURL({
families,
display,
subsets,
text
} = {}) {
const _subsets = (Array.isArray(subsets) ? subsets : [subsets]).filter(Boolean);
const family = convertFamiliesToArray(families ?? {});
if (family.length < 1) {
return false;
}
const query = {
family
};
if (display && isValidDisplay(display)) {
query.display = display;
}
if (_subsets.length > 0) {
query.subset = _subsets.join(",");
}
if (text) {
query.text = text;
}
return withHttps(withQuery(resolveURL(GOOGLE_FONTS_DOMAIN, "css2"), query));
}
function convertFamiliesToArray(families) {
const result = [];
Object.entries(families).forEach(([name, values]) => {
if (!name) {
return;
}
name = parseFamilyName(name);
if (typeof values === "string" && String(values).includes("..")) {
result.push(`${name}:wght@${values}`);
return;
}
if (Array.isArray(values) && values.length > 0) {
result.push(`${name}:wght@${values.join(";")}`);
return;
}
if (Object.keys(values).length > 0) {
const axes = {};
let italicWeights = [];
Object.entries(values).sort(([styleA], [styleB]) => styleA.localeCompare(styleB)).forEach(([style, weight]) => {
const parsedStyle = parseStyle(style);
if (parsedStyle === "ital") {
axes[parsedStyle] = ["0", "1"];
if (weight === true || weight === 400 || weight === 1) {
italicWeights = ["*"];
} else {
italicWeights = Array.isArray(weight) ? weight.map((w) => String(w)) : [weight];
}
} else {
axes[parseStyle(style)] = Array.isArray(weight) ? weight.map((w) => String(w)) : [weight];
}
});
const strictlyItalic = [];
if (Object.keys(axes).length === 1 && Object.hasOwn(axes, "ital")) {
if (!(italicWeights.includes("*") || italicWeights.length === 1 && italicWeights.includes("400"))) {
axes.wght = italicWeights;
strictlyItalic.push(...italicWeights);
}
} else if (Object.hasOwn(axes, "wght") && !italicWeights.includes("*")) {
strictlyItalic.push(...italicWeights.filter((w) => !axes.wght.includes(w)));
axes.wght = [.../* @__PURE__ */ new Set([...axes.wght, ...italicWeights])];
}
const axisTagList = Object.keys(axes).sort((axisA, axisB) => {
const isLowerA = axisA[0] === axisA[0].toLowerCase();
const isLowerB = axisB[0] === axisB[0].toLowerCase();
if (isLowerA && !isLowerB) {
return -1;
}
if (!isLowerA && isLowerB) {
return 1;
}
return axisA.localeCompare(axisB);
});
if (axisTagList.length === 1 && axisTagList.includes("ital")) {
result.push(`${name}:ital@1`);
return;
}
let axisTupleArrays = cartesianProduct(...axisTagList.map((tag) => axes[tag]), [[]]);
const italicIndex = axisTagList.findIndex((i) => i === "ital");
if (italicIndex !== -1) {
const weightIndex = axisTagList.findIndex((i) => i === "wght");
if (weightIndex !== -1) {
axisTupleArrays = axisTupleArrays.filter((axisTuple) => axisTuple[italicIndex] === "0" && !strictlyItalic.includes(axisTuple[weightIndex]) || axisTuple[italicIndex] === "1" && italicWeights.includes(axisTuple[weightIndex]));
}
}
const axisTupleList = axisTupleArrays.sort((axisTupleA, axisTupleB) => {
for (let i = 0; i < axisTupleA.length; i++) {
const compareResult = parseInt(axisTupleA[i]) - parseInt(axisTupleB[i]);
if (compareResult !== 0) {
return compareResult;
}
}
return 0;
}).map((axisTuple) => axisTuple.join(",")).join(";");
result.push(`${name}:${axisTagList.join(",")}@${axisTupleList}`);
return;
}
if (values) {
result.push(name);
}
});
return result;
}
function isValidURL(url) {
return RegExp(GOOGLE_FONTS_DOMAIN).test(url);
}
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
class Downloader extends Hookable {
constructor(url, options) {
super();
this.url = url;
__publicField(this, "config");
this.config = {
base64: false,
overwriting: false,
outputDir: "./",
stylePath: "fonts.css",
fontsDir: "fonts",
fontsPath: "./fonts",
headers: [["user-agent", [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
"AppleWebKit/537.36 (KHTML, like Gecko)",
"Chrome/98.0.4758.102 Safari/537.36"
].join(" ")]],
...options
};
}
async execute() {
if (!isValidURL(this.url)) {
throw new Error("Invalid Google Fonts URL");
}
const { outputDir, stylePath, headers, fontsPath } = this.config;
const cssPath = resolve(outputDir, stylePath);
let overwriting = this.config.overwriting;
if (!overwriting && existsSync(cssPath)) {
const currentCssContent = readFileSync(cssPath, "utf-8");
const currentUrl = (currentCssContent.split(/\r?\n/, 1).shift() || "").replace("/*", "").replace("*/", "").trim();
if (currentUrl === this.url) {
return false;
}
overwriting = true;
}
await this.callHook("download:start");
const { searchParams } = new URL(this.url);
const subsets = searchParams.get("subset") ? searchParams.get("subset")?.split(",") : void 0;
await this.callHook("download-css:before", this.url);
const _css = await ofetch(this.url, { headers });
const { fonts: fontsFromCss, css: cssContent } = parseFontsFromCss(_css, fontsPath, subsets);
await this.callHook("download-css:done", this.url, cssContent, fontsFromCss);
const fonts = await this.downloadFonts(fontsFromCss);
await this.callHook("write-css:before", cssPath, cssContent, fonts);
const newContent = this.writeCss(cssPath, `/* ${this.url} */
${cssContent}`, fonts);
await this.callHook("write-css:done", cssPath, newContent, cssContent);
await this.callHook("download:complete");
return true;
}
async downloadFonts(fonts) {
const { headers, base64, outputDir, fontsDir } = this.config;
const downloadedFonts = [];
const _fonts = [];
for (const font of fonts) {
const downloadedFont = downloadedFonts.find((f) => f.inputFont === font.inputFont);
if (downloadedFont) {
if (base64) {
font.outputText = downloadedFont.outputText;
} else {
copyFileSync(
resolve(outputDir, fontsDir, downloadedFont.outputFont),
resolve(outputDir, fontsDir, font.outputFont)
);
}
_fonts.push(font);
continue;
}
await this.callHook("download-font:before", font);
const response = await ofetch.raw(font.inputFont, { headers, responseType: "arrayBuffer" });
if (!response?._data) {
_fonts.push(font);
continue;
}
const buffer = Buffer.from(response?._data);
if (base64) {
const mime = response.headers.get("content-type") ?? "font/woff2";
font.outputText = `url('data:${mime};base64,${buffer.toString("base64")}')`;
} else {
const fontPath = resolve(outputDir, fontsDir, font.outputFont);
mkdirSync(dirname(fontPath), { recursive: true });
writeFileSync(fontPath, buffer);
}
_fonts.push(font);
await this.callHook("download-font:done", font);
downloadedFonts.push(font);
}
return _fonts;
}
writeCss(path, content, fonts) {
for (const font of fonts) {
content = content.replace(font.inputText, font.outputText);
}
mkdirSync(dirname(path), { recursive: true });
writeFileSync(path, content, "utf-8");
return content;
}
}
function parseFontsFromCss(content, fontsPath, subsets) {
const css = [];
const fonts = [];
const re = {
face: /\s*(?:\/\*\s*(.*?)\s*\*\/)?[^@]*?@font-face\s*{(?:[^}]*?)}\s*/gi,
family: /font-family\s*:\s*(?:'|")?([^;]*?)(?:'|")?\s*;/i,
style: /font-style\s*:\s*([^;]*?)\s*;/i,
weight: /font-weight\s*:\s*([^;]*?)\s*;/i,
url: /url\s*\(\s*(?:'|")?\s*([^]*?)\s*(?:'|")?\s*\)\s*?/gi
};
let match1;
while ((match1 = re.face.exec(content)) !== null) {
const [fontface, subset] = match1;
const familyRegExpArray = re.family.exec(fontface);
const family = familyRegExpArray ? familyRegExpArray[1] : "";
const styleRegExpArray = re.style.exec(fontface);
const style = styleRegExpArray ? styleRegExpArray[1] : "";
const weightRegExpArray = re.weight.exec(fontface);
const weight = weightRegExpArray ? weightRegExpArray[1] : "";
if (subsets && subsets.length && !subsets.includes(subset)) {
continue;
}
css.push(fontface);
let match2;
while ((match2 = re.url.exec(fontface)) !== null) {
const [forReplace, url] = match2;
const ext = extname(url).replace(/^\./, "") || "woff2";
const newFilename = formatFontFileName("{family}-{style}-{weight}-{subset}.{ext}", {
family: family.replace(/\s+/g, "_"),
style: style.replace(/\s+/g, "_") || "normal",
weight: weight.replace(/\s+/g, "_") || "",
subset: subset || "text",
ext
}).replace(/\.$/, "");
fonts.push({
inputFont: url,
outputFont: newFilename,
inputText: forReplace,
outputText: `url('${posix.join(fontsPath, newFilename)}')`
});
}
}
return {
css: css.join("\n"),
fonts
};
}
function formatFontFileName(template, values) {
return Object.entries(values).filter(([key]) => /^[a-z0-9_-]+$/gi.test(key)).map(([key, value]) => [new RegExp(`([^{]|^){${key}}([^}]|$)`, "g"), `$1${value}$2`]).reduce((str, [regexp, replacement]) => str.replace(regexp, String(replacement)), template).replace(/({|}){2}/g, "$1");
}
function download(url, options) {
return new Downloader(url, options);
}
function merge(...fonts) {
return deepmerge.all(fonts);
}
function parse(url) {
const result = {};
if (!isValidURL(url)) {
return result;
}
const { searchParams, pathname } = new URL(url);
if (!searchParams.has("family")) {
return result;
}
const families = convertFamiliesObject(searchParams.getAll("family"), pathname.endsWith("2"));
if (Object.keys(families).length < 1) {
return result;
}
result.families = families;
const display = searchParams.get("display");
if (display && isValidDisplay(display)) {
result.display = display;
}
const subsets = searchParams.get("subset");
if (subsets) {
result.subsets = subsets.split(",");
}
const text = searchParams.get("text");
if (text) {
result.text = text;
}
return result;
}
function convertFamiliesObject(families, v2 = true) {
const result = {};
families.flatMap((family) => family.split("|")).forEach((family) => {
if (!family) {
return;
}
if (!family.includes(":")) {
result[family] = true;
return;
}
const parts = family.split(":");
if (!parts[1]) {
return;
}
const values = {};
if (!v2) {
parts[1].split(",").forEach((style) => {
const styleParsed = parseStyle(style);
if (styleParsed === "wght") {
values.wght = true;
}
if (styleParsed === "ital") {
values.ital = true;
}
if (styleParsed === "bold" || styleParsed === "b") {
values.wght = 700;
}
if (styleParsed === "bolditalic" || styleParsed === "bi") {
values.ital = 700;
}
});
}
if (v2) {
let [styles, weights] = parts[1].split("@");
if (!weights) {
weights = String(styles).replace(",", ";");
styles = "wght";
}
styles.split(",").forEach((style) => {
const styleParsed = parseStyle(style);
values[styleParsed] = weights.split(";").map((weight) => {
if (/^\+?\d+$/.test(weight)) {
return parseInt(weight);
}
const [pos, w] = weight.split(",");
const index = styleParsed === "wght" ? 0 : 1;
if (!w) {
return weight;
}
if (parseInt(pos) !== index) {
return 0;
}
if (/^\+?\d+$/.test(w)) {
return parseInt(w);
}
return w;
}).filter((v) => parseInt(v.toString()) > 0 || v.toString().includes(".."));
if (!values[styleParsed].length) {
values[styleParsed] = true;
return;
}
if (values[styleParsed].length > 1) {
return;
}
const first = values[styleParsed][0];
if (String(first).includes("..")) {
values[styleParsed] = first;
}
if (first === 1 || first === true) {
values[styleParsed] = true;
}
});
}
result[parseFamilyName(parts[0])] = values;
});
return result;
}
export { Downloader, constructURL, download, isValidURL, merge, parse };

46
node_modules/google-fonts-helper/package.json generated vendored Normal file
View File

@@ -0,0 +1,46 @@
{
"name": "google-fonts-helper",
"version": "3.7.3",
"description": "Google Fonts Helper",
"repository": "datalogix/google-fonts-helper",
"license": "MIT",
"sideEffects": false,
"type": "module",
"exports": {
".": {
"require": "./dist/index.cjs",
"import": "./dist/index.mjs",
"types": "./dist/index.d.ts"
}
},
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"dependencies": {
"deepmerge": "^4.3.1",
"hookable": "^5.5.3",
"ofetch": "^1.4.1",
"ufo": "^1.5.4"
},
"devDependencies": {
"@nuxtjs/eslint-config-typescript": "latest",
"@types/fs-extra": "latest",
"@vitest/coverage-v8": "latest",
"changelogen": "latest",
"eslint": "latest",
"tempy": "latest",
"typescript": "latest",
"unbuild": "latest",
"vitest": "latest"
},
"scripts": {
"build": "unbuild",
"dev": "vitest",
"lint": "eslint --ext .ts,.js .",
"release": "pnpm test && pnpm build && changelogen --release --push && pnpm publish",
"test": "pnpm lint && vitest run --coverage"
}
}