@driftime/sanity-plugin-font 0.0.0-stage → 0.1.1

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.
Files changed (47) hide show
  1. package/README.md +492 -2
  2. package/dist/catalog-BVw1r_SG.js +183 -0
  3. package/dist/catalog-BVw1r_SG.js.map +1 -0
  4. package/dist/catalog-KAqAIjzw.js +96 -0
  5. package/dist/catalog-KAqAIjzw.js.map +1 -0
  6. package/dist/catalog.d.ts +29 -0
  7. package/dist/catalog.d.ts.map +1 -0
  8. package/dist/catalog.js +10 -0
  9. package/dist/catalog.js.map +1 -0
  10. package/dist/family-BAGnEjZg.js +36 -0
  11. package/dist/family-BAGnEjZg.js.map +1 -0
  12. package/dist/font-styles.d.ts +21 -0
  13. package/dist/font-styles.d.ts.map +1 -0
  14. package/dist/font-styles.js +43 -0
  15. package/dist/font-styles.js.map +1 -0
  16. package/dist/index.d.ts +76 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +2364 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/names.d.ts +8 -0
  21. package/dist/names.d.ts.map +1 -0
  22. package/dist/names.js +0 -0
  23. package/dist/next-loader.d.ts +19 -0
  24. package/dist/next-loader.d.ts.map +1 -0
  25. package/dist/next-loader.js +28 -0
  26. package/dist/next-loader.js.map +1 -0
  27. package/dist/next.d.ts +42 -0
  28. package/dist/next.d.ts.map +1 -0
  29. package/dist/next.js +48 -0
  30. package/dist/next.js.map +1 -0
  31. package/dist/render.d.ts +12 -0
  32. package/dist/render.d.ts.map +1 -0
  33. package/dist/render.js +183 -0
  34. package/dist/render.js.map +1 -0
  35. package/dist/resolve-Bkp0dEmr.d.ts +81 -0
  36. package/dist/resolve-Bkp0dEmr.d.ts.map +1 -0
  37. package/dist/types-DZl90ROd.d.ts +106 -0
  38. package/dist/types-DZl90ROd.d.ts.map +1 -0
  39. package/dist/types-DheP5tax.js +27 -0
  40. package/dist/types-DheP5tax.js.map +1 -0
  41. package/dist/utils-BV38iODp.js +29 -0
  42. package/dist/utils-BV38iODp.js.map +1 -0
  43. package/dist/vite.d.ts +61 -0
  44. package/dist/vite.d.ts.map +1 -0
  45. package/dist/vite.js +53 -0
  46. package/dist/vite.js.map +1 -0
  47. package/package.json +120 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"next-loader.js","names":["path","isDefined","writeFontCatalog","createCatalogModule","LoaderContext","resourcePath","generateCatalogModule","catalog","process","cwd","undefined","relative","dirname","file","replaceAll","startsWith","LoaderContext","generateCatalogModule","fontCatalogLoader","this","code","resourcePath"],"sources":["../src/build/loader.ts","../src/next-loader.ts"],"sourcesContent":["import path from \"node:path\";\n\nimport { isDefined } from \"@repo/lib/utils\";\n\nimport { writeFontCatalog } from \"@/build/catalog\";\nimport { createCatalogModule } from \"@/build/module\";\n\n/**\n * The parts of a webpack or Turbopack loader's context the build step uses.\n *\n * @public\n */\nexport interface LoaderContext {\n /** Path of the module being loaded. */\n resourcePath: string;\n}\n\n/**\n * Generates the module that replaces the placeholder catalog, importing the catalog from where the build step wrote it.\n *\n * @param resourcePath - Path of the placeholder being replaced.\n * @returns The module's code.\n */\nexport async function generateCatalogModule(resourcePath: string) {\n const catalog = await writeFontCatalog(process.cwd());\n if (!isDefined(catalog)) return createCatalogModule(undefined);\n\n const relative = path.relative(path.dirname(resourcePath), catalog.file).replaceAll(\"\\\\\", \"/\");\n\n return createCatalogModule(relative.startsWith(\".\") ? relative : `./${relative}`);\n}\n","import type { LoaderContext } from \"@/build/loader\";\nimport { generateCatalogModule } from \"@/build/loader\";\n\n/**\n * Webpack and Turbopack loader that replaces the placeholder catalog with the one the build step downloaded.\n *\n * @param this - The loader context.\n * @returns The catalog module's code.\n * @public\n */\nasync function fontCatalogLoader(this: LoaderContext) {\n const code = await generateCatalogModule(this.resourcePath);\n\n return code;\n}\n\nexport default fontCatalogLoader;\n"],"mappings":";;;;;;;;;AAuBA,eAAsBM,sBAAsBD,cAAsB;CAChE,IAAME,UAAU,MAAML,iBAAiBM,QAAQC,IAAI,CAAC;CACpD,IAAI,CAACR,UAAUM,OAAO,GAAG,OAAOJ,oBAAoBO,KAAAA,CAAS;CAE7D,IAAMC,WAAWX,KAAKW,SAASX,KAAKY,QAAQP,YAAY,GAAGE,QAAQM,IAAI,CAAC,CAACC,WAAW,MAAM,GAAG;CAE7F,OAAOX,oBAAoBQ,SAASI,WAAW,GAAG,IAAIJ,WAAW,KAAKA,UAAU;AAClF;;;;;;;;ACpBA,eAAeO,oBAAuC;CAGpD,OAAOE,MAFYH,sBAAsB,KAAKI,YAAY;AAG5D"}
package/dist/next.d.ts ADDED
@@ -0,0 +1,42 @@
1
+ /**
2
+ * The parts of a webpack configuration the build step extends.
3
+ *
4
+ * @public
5
+ */
6
+ interface SanityFontWebpackConfig {
7
+ /** Module settings, whose rules the build step adds to. */
8
+ module?: {
9
+ rules?: unknown[];
10
+ };
11
+ }
12
+ /**
13
+ * The parts of a Next.js configuration the build step extends, so the package doesn't depend on Next.js's own types.
14
+ *
15
+ * @public
16
+ */
17
+ interface SanityFontNextConfig {
18
+ /** Turbopack settings, whose rules the build step adds to. */
19
+ turbopack?: {
20
+ rules?: Record<string, unknown>;
21
+ };
22
+ /** Customises webpack, for projects that build with it instead of Turbopack. */
23
+ webpack?: SanityFontWebpackCustomizer | null;
24
+ }
25
+ /**
26
+ * Customises webpack, as Next.js's `webpack` option does. The context is typed `never` because the build step only
27
+ * passes Next.js's own context through, so any context type a project's function declares is accepted.
28
+ *
29
+ * @public
30
+ */
31
+ type SanityFontWebpackCustomizer = (config: SanityFontWebpackConfig, context: never) => SanityFontWebpackConfig;
32
+ /**
33
+ * Adds the font build step to a Next.js configuration, for Studios embedded in a Next.js app. It works with both
34
+ * Turbopack and webpack.
35
+ *
36
+ * @returns A function that extends the Next.js configuration into an async configuration function, which downloads the
37
+ * catalog before Next.js starts building.
38
+ * @public
39
+ */
40
+ export declare function withFonts(): <Config extends SanityFontNextConfig>(config: Config) => (phase: string) => Promise<Config>;
41
+ export type { SanityFontNextConfig, SanityFontWebpackConfig, SanityFontWebpackCustomizer };
42
+ //# sourceMappingURL=next.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"next.d.ts","names":["SanityFontWebpackConfig","SanityFontNextConfig","Record","SanityFontWebpackCustomizer","withFonts","Config","Promise"],"sources":["../src/build/next.ts"],"mappings":"AAmBA;;;;;UAAiBA;;EAEf;IAAW;;;;;;;;UAQIC;;EAEf;IAAc,QAAQC;;;EAEtB,UAAUC;;;;;;;;KASAA,+BAA+B,QAAQH,yBAAyB,mBAAmBA;;;;;;;;;wBAa/EI,cAIe,eAAeH,sBAAoB,QAAUI,YAAM,kBAiBnDC,QAAAD"}
package/dist/next.js ADDED
@@ -0,0 +1,48 @@
1
+ import { i as isCatalogModule, n as catalogModulePath, t as writeFontCatalog } from "./catalog-BVw1r_SG.js";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ /** Path of the loader, published next to this file. */
5
+ const loaderPath = fileURLToPath(new URL("next-loader.js", import.meta.url));
6
+ /**
7
+ * Adds the font build step to a Next.js configuration, for Studios embedded in a Next.js app. It works with both
8
+ * Turbopack and webpack.
9
+ *
10
+ * @returns A function that extends the Next.js configuration into an async configuration function, which downloads the
11
+ * catalog before Next.js starts building.
12
+ * @public
13
+ */
14
+ function withFonts() {
15
+ let loader = { loader: loaderPath }, placeholder = path.basename(path.dirname(path.dirname(catalogModulePath)));
16
+ return function extendConfig(config) {
17
+ let { turbopack, webpack } = config, extendedConfig = {
18
+ ...config,
19
+ turbopack: {
20
+ ...turbopack,
21
+ rules: {
22
+ ...turbopack?.rules,
23
+ [`**/${placeholder}/dist/catalog.js`]: {
24
+ loaders: [loader],
25
+ as: "*.js"
26
+ }
27
+ }
28
+ },
29
+ webpack(webpackConfig, context) {
30
+ let resolved = webpack?.(webpackConfig, context) ?? webpackConfig, rule = {
31
+ test: isCatalogModule,
32
+ use: [loader]
33
+ };
34
+ return {
35
+ ...resolved,
36
+ module: {
37
+ ...resolved.module,
38
+ rules: [...resolved.module?.rules ?? [], rule]
39
+ }
40
+ };
41
+ }
42
+ };
43
+ return async (phase) => ((phase === "phase-development-server" || phase === "phase-production-build") && await writeFontCatalog(process.cwd()), extendedConfig);
44
+ };
45
+ }
46
+ export { withFonts };
47
+
48
+ //# sourceMappingURL=next.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"next.js","names":["path","fileURLToPath","writeFontCatalog","catalogModulePath","isCatalogModule","WebpackRule","test","resource","use","loader","SanityFontWebpackConfig","module","rules","SanityFontNextConfig","turbopack","Record","webpack","SanityFontWebpackCustomizer","config","context","loaderPath","URL","import","meta","url","withFonts","placeholder","basename","dirname","extendConfig","Config","extendedConfig","loaders","as","webpackConfig","resolved","rule","phase","process","cwd"],"sources":["../src/build/next.ts"],"sourcesContent":["import path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport { writeFontCatalog } from \"@/build/catalog\";\nimport { catalogModulePath, isCatalogModule } from \"@/build/module\";\n\n/** A webpack rule, limited to what the build step adds. */\ninterface WebpackRule {\n /** Matches the placeholder catalog. */\n test: (resource: string) => boolean;\n /** The loader that replaces it. */\n use: { loader: string }[];\n}\n\n/**\n * The parts of a webpack configuration the build step extends.\n *\n * @public\n */\nexport interface SanityFontWebpackConfig {\n /** Module settings, whose rules the build step adds to. */\n module?: { rules?: unknown[] };\n}\n\n/**\n * The parts of a Next.js configuration the build step extends, so the package doesn't depend on Next.js's own types.\n *\n * @public\n */\nexport interface SanityFontNextConfig {\n /** Turbopack settings, whose rules the build step adds to. */\n turbopack?: { rules?: Record<string, unknown> };\n /** Customises webpack, for projects that build with it instead of Turbopack. */\n webpack?: SanityFontWebpackCustomizer | null;\n}\n\n/**\n * Customises webpack, as Next.js's `webpack` option does. The context is typed `never` because the build step only\n * passes Next.js's own context through, so any context type a project's function declares is accepted.\n *\n * @public\n */\nexport type SanityFontWebpackCustomizer = (config: SanityFontWebpackConfig, context: never) => SanityFontWebpackConfig;\n\n/** Path of the loader, published next to this file. */\nconst loaderPath = fileURLToPath(new URL(\"next-loader.js\", import.meta.url));\n\n/**\n * Adds the font build step to a Next.js configuration, for Studios embedded in a Next.js app. It works with both\n * Turbopack and webpack.\n *\n * @returns A function that extends the Next.js configuration into an async configuration function, which downloads the\n * catalog before Next.js starts building.\n * @public\n */\nexport function withFonts() {\n const loader = { loader: loaderPath };\n const placeholder = path.basename(path.dirname(path.dirname(catalogModulePath)));\n\n return function extendConfig<Config extends SanityFontNextConfig>(config: Config) {\n const { turbopack, webpack } = config;\n const extendedConfig: Config = {\n ...config,\n turbopack: {\n ...turbopack,\n rules: { ...turbopack?.rules, [`**/${placeholder}/dist/catalog.js`]: { loaders: [loader], as: \"*.js\" } },\n },\n webpack(webpackConfig: SanityFontWebpackConfig, context: never) {\n const resolved = webpack?.(webpackConfig, context) ?? webpackConfig;\n const rule: WebpackRule = { test: isCatalogModule, use: [loader] };\n\n return { ...resolved, module: { ...resolved.module, rules: [...(resolved.module?.rules ?? []), rule] } };\n },\n };\n\n // Turbopack never finds a file that wasn't there when the build started, even with a cache from an earlier build.\n return async (phase: string) => {\n // `next start` also loads the configuration, where `node_modules` may be read-only and nothing is bundled.\n if (phase === \"phase-development-server\" || phase === \"phase-production-build\") {\n await writeFontCatalog(process.cwd());\n }\n\n return extendedConfig;\n };\n };\n}\n"],"mappings":";;;;AA6CA,MAAMoB,aAAanB,cAAc,IAAIoB,IAAI,kBAAkBC,YAAYE,GAAG,CAAC;;;;;;;;;AAU3E,SAAgBC,YAAY;CAC1B,IAAMhB,SAAS,EAAEA,QAAQW,WAAW,GAC9BM,cAAc1B,KAAK2B,SAAS3B,KAAK4B,QAAQ5B,KAAK4B,QAAQzB,iBAAiB,CAAC,CAAC;CAE/E,OAAO,SAAS0B,aAAkDX,QAAgB;EAChF,IAAM,EAAEJ,WAAWE,YAAYE,QACzBa,iBAAyB;GAC7B,GAAGb;GACHJ,WAAW;IACT,GAAGA;IACHF,OAAO;KAAE,GAAGE,WAAWF;MAAQ,MAAMc,YAAW,oBAAqB;MAAEM,SAAS,CAACvB,MAAM;MAAGwB,IAAI;KAAO;IAAE;GACzG;GACAjB,QAAQkB,eAAwCf,SAAgB;IAC9D,IAAMgB,WAAWnB,UAAUkB,eAAef,OAAO,KAAKe,eAChDE,OAAoB;KAAE9B,MAAMF;KAAiBI,KAAK,CAACC,MAAM;IAAE;IAEjE,OAAO;KAAE,GAAG0B;KAAUxB,QAAQ;MAAE,GAAGwB,SAASxB;MAAQC,OAAO,CAAC,GAAIuB,SAASxB,QAAQC,SAAS,CAAA,GAAKwB,IAAI;KAAE;IAAE;GACzG;EACF;EAGA,OAAO,OAAOC,YAERA,UAAU,8BAA8BA,UAAU,6BACpD,MAAMnC,iBAAiBoC,QAAQC,IAAI,CAAC,GAG/BR;CAEX;AACF"}
@@ -0,0 +1,12 @@
1
+ import { a as resolveFont, i as SanityResolvedFont, n as SanityFontPreload, r as SanityResolveFontOptions, t as SanityFontPreconnect } from "./resolve-Bkp0dEmr.js";
2
+ import { c as SanityFontStyle, i as SanityFontDisplay, l as SanityFontWeight, n as SanityFontAsset, o as SanityFontFile, r as SanityFontCategory, s as SanityFontSource, t as SanityFont } from "./types-DZl90ROd.js";
3
+ import { FontStyles, SanityFontStylesProps } from "@driftime/sanity-plugin-font/font-styles";
4
+ /**
5
+ * GROQ projection that expands an uploaded font's files, so their URLs arrive with the font. Spread it into the
6
+ * projection of a font field, such as `headingFont { ${fontFragment} }`.
7
+ *
8
+ * @public
9
+ */
10
+ export declare const fontFragment: string;
11
+ export { FontStyles, type SanityFont, type SanityFontAsset, type SanityFontCategory, type SanityFontDisplay, type SanityFontFile, type SanityFontPreconnect, type SanityFontPreload, type SanityFontSource, type SanityFontStyle, type SanityFontStylesProps, type SanityFontWeight, type SanityResolveFontOptions, type SanityResolvedFont, resolveFont };
12
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","names":[],"sources":["../src/groq/fragments.ts"],"mappings":";;;;;;;;;qBAQa"}
package/dist/render.js ADDED
@@ -0,0 +1,183 @@
1
+ import { t as isDefined } from "./utils-BV38iODp.js";
2
+ import { n as createStylesheetUrl, t as getFamilyName } from "./family-BAGnEjZg.js";
3
+ import { a as fontStyles, n as fontDisplays, s as fontWeights, t as fontCategories } from "./types-DheP5tax.js";
4
+ import { stegaClean } from "@sanity/client/stega";
5
+ import { FontStyles } from "@driftime/sanity-plugin-font/font-styles";
6
+ /** Font stack shown in place of each kind of typeface while it loads, or if it can't. */
7
+ const fontFallbacks = {
8
+ "sans-serif": "ui-sans-serif, system-ui, sans-serif",
9
+ serif: "ui-serif, Georgia, serif",
10
+ display: "ui-sans-serif, system-ui, sans-serif",
11
+ handwriting: "cursive",
12
+ monospace: "ui-monospace, monospace"
13
+ };
14
+ /**
15
+ * Hashes text into a short identifier that's the same every time for the same text, for naming generated classes.
16
+ *
17
+ * @param value - The text.
18
+ * @returns The identifier, in letters and digits.
19
+ */
20
+ function hashText(value) {
21
+ let hash = 5381;
22
+ for (let character of value) hash = (hash * 33 + (character.codePointAt(0) ?? 0)) % 4294967296;
23
+ return hash.toString(36);
24
+ }
25
+ /** Generic font families, which CSS reads as keywords and so are never quoted. */
26
+ const genericFamilies = /* @__PURE__ */ new Set([
27
+ "serif",
28
+ "sans-serif",
29
+ "monospace",
30
+ "cursive",
31
+ "fantasy",
32
+ "system-ui",
33
+ "ui-serif",
34
+ "ui-sans-serif",
35
+ "ui-monospace",
36
+ "ui-rounded",
37
+ "emoji",
38
+ "math",
39
+ "fangsong"
40
+ ]), variablePattern = /^--[A-Za-z_][\w-]*$/u, fileFormats = {
41
+ woff2: {
42
+ format: "woff2",
43
+ type: "font/woff2"
44
+ },
45
+ woff: {
46
+ format: "woff",
47
+ type: "font/woff"
48
+ }
49
+ };
50
+ /**
51
+ * Quotes text as a CSS string, so a stored value can't end the string and add rules of its own, or end the `<style>`
52
+ * element a site puts the rules in.
53
+ *
54
+ * @param value - The text.
55
+ * @returns The quoted string.
56
+ */
57
+ function quoteCss(value) {
58
+ return `"${value.replaceAll(/[\n\r\f]/gu, " ").replaceAll(/["\\]/gu, String.raw`\$&`).replaceAll("<", String.raw`\3c `)}"`;
59
+ }
60
+ /**
61
+ * Narrows a stored value to one of a list's members.
62
+ *
63
+ * @param list - The members.
64
+ * @param value - The stored value.
65
+ * @returns The value, or undefined when it isn't a member.
66
+ */
67
+ function readMember(list, value) {
68
+ return list.find((member) => member === value);
69
+ }
70
+ /**
71
+ * Builds the `@font-face` rule and preload for one uploaded file.
72
+ *
73
+ * @param family - Name of the family, cleaned.
74
+ * @param file - The file.
75
+ * @param display - How the font is shown while it loads.
76
+ * @returns The rule and preload, or undefined when the file's URL or format can't be read.
77
+ */
78
+ function resolveFontFile(family, file, display) {
79
+ let url = stegaClean(file.asset?.url);
80
+ if (!isDefined(url) || !url.startsWith("https://")) return;
81
+ let extension = new URL(url).pathname.split(".").at(-1)?.toLowerCase(), format = isDefined(extension) ? fileFormats[extension] : void 0;
82
+ if (!isDefined(format)) return;
83
+ let weight = readMember(fontWeights, file.weight) ?? 400, maxWeight = readMember(fontWeights, file.maxWeight), style = readMember(fontStyles, stegaClean(file.style)) ?? "normal", weightRange = isDefined(maxWeight) && maxWeight > weight ? `${weight} ${maxWeight}` : `${weight}`;
84
+ return {
85
+ rule: [
86
+ "@font-face {",
87
+ ` font-family: ${quoteCss(family)};`,
88
+ ` font-style: ${style};`,
89
+ ` font-weight: ${weightRange};`,
90
+ ` font-display: ${display};`,
91
+ ` src: url(${quoteCss(url)}) format("${format.format}");`,
92
+ "}"
93
+ ].join("\n"),
94
+ preload: {
95
+ href: url,
96
+ type: format.type
97
+ }
98
+ };
99
+ }
100
+ /**
101
+ * Works out how a font is loaded: Google's stylesheet, or the `@font-face` rules and files of an upload.
102
+ *
103
+ * @param value - The stored font.
104
+ * @param family - Name of the family, cleaned.
105
+ * @param display - How the font is shown while it loads.
106
+ * @returns How the font is loaded, or undefined when an uploaded font has no file that can be read.
107
+ */
108
+ function resolveLoading(value, family, display) {
109
+ let { source, weights, styles, files } = value ?? {};
110
+ if (stegaClean(source) === "upload") {
111
+ let resolved = (files ?? []).flatMap((file) => {
112
+ let resolvedFile = resolveFontFile(family, file, display);
113
+ return isDefined(resolvedFile) ? [resolvedFile] : [];
114
+ });
115
+ return isDefined(resolved) ? {
116
+ fontFace: resolved.map(({ rule }) => rule).join("\n"),
117
+ preconnect: [...new Set(resolved.map(({ preload }) => new URL(preload.href).origin))].map((href) => ({
118
+ href,
119
+ crossOrigin: !0
120
+ })),
121
+ preload: resolved.map(({ preload }) => preload)
122
+ } : void 0;
123
+ }
124
+ let loadedWeights = (weights ?? []).flatMap((weight) => {
125
+ let member = readMember(fontWeights, weight);
126
+ return isDefined(member) ? [member] : [];
127
+ }), loadedStyles = (styles ?? []).flatMap((style) => {
128
+ let member = readMember(fontStyles, stegaClean(style));
129
+ return isDefined(member) ? [member] : [];
130
+ });
131
+ return {
132
+ stylesheet: createStylesheetUrl(family, loadedWeights, loadedStyles, display),
133
+ preconnect: [{
134
+ href: "https://fonts.googleapis.com",
135
+ crossOrigin: !1
136
+ }, {
137
+ href: "https://fonts.gstatic.com",
138
+ crossOrigin: !0
139
+ }],
140
+ preload: []
141
+ };
142
+ }
143
+ /**
144
+ * Resolves a stored font into what a site needs to load and apply it, shaped like a `next/font` font: a class and an
145
+ * inline style that set the font, and a class that defines a CSS variable, with the rules behind them. An uploaded
146
+ * font's files are only read when the query expands their assets.
147
+ *
148
+ * @param value - The stored font.
149
+ * @param options - How the font is loaded and shown, and the CSS variable to define.
150
+ * @returns The font, or undefined when nothing usable was stored.
151
+ * @public
152
+ */
153
+ function resolveFont(value, options = {}) {
154
+ let family = getFamilyName(value);
155
+ if (!isDefined(family)) return;
156
+ let { display: requestedDisplay, preload: hinted = !0, fallback } = options, loading = resolveLoading(value, family, readMember(fontDisplays, requestedDisplay) ?? "swap");
157
+ if (!isDefined(loading)) return;
158
+ let category = readMember(fontCategories, stegaClean(value?.category)) ?? "sans-serif", stack = (fallback ?? []).map((name) => name.trim()).filter((name) => isDefined(name)), fallbackStack = isDefined(stack) ? stack.map((name) => genericFamilies.has(name) ? name : quoteCss(name)).join(", ") : fontFallbacks[category], fontFamily = `${quoteCss(family)}, ${fallbackStack}`, className = `font-${hashText(`${fontFamily} ${"stylesheet" in loading ? loading.stylesheet : loading.fontFace}`)}`, variableName = isDefined(options.variable) && variablePattern.test(options.variable) ? options.variable : void 0, variable = isDefined(variableName) ? `${className}-${variableName.slice(2)}` : void 0, css = [
159
+ loading.fontFace,
160
+ `.${className} { font-family: ${fontFamily}; }`,
161
+ isDefined(variable) && isDefined(variableName) ? `.${variable} { ${variableName}: ${fontFamily}; }` : void 0
162
+ ].filter((rule) => isDefined(rule)).join("\n"), preconnect = hinted ? loading.preconnect : [], preload = hinted ? loading.preload : [];
163
+ return {
164
+ className,
165
+ style: { fontFamily },
166
+ ...isDefined(variable) && { variable },
167
+ fontFamily,
168
+ ..."stylesheet" in loading ? { stylesheet: loading.stylesheet } : { fontFace: loading.fontFace },
169
+ css,
170
+ preconnect,
171
+ preload
172
+ };
173
+ }
174
+ /**
175
+ * GROQ projection that expands an uploaded font's files, so their URLs arrive with the font. Spread it into the
176
+ * projection of a font field, such as `headingFont { ${fontFragment} }`.
177
+ *
178
+ * @public
179
+ */
180
+ const fontFragment = "..., files[] { ..., asset-> { url } }";
181
+ export { FontStyles, fontFragment, resolveFont };
182
+
183
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","names":["SanityFontCategory","fontFallbacks","Record","serif","display","handwriting","monospace","hashText","value","hash","character","codePointAt","toString","isDefined","stegaClean","fontFallbacks","getFamilyName","createStylesheetUrl","hashText","SanityFont","SanityFontCategory","SanityFontDisplay","SanityFontFile","SanityFontStyle","SanityFontWeight","fontCategories","fontDisplays","fontStyles","fontWeights","SanityResolveFontOptions","display","preload","fallback","variable","SanityFontPreconnect","href","crossOrigin","SanityFontPreload","type","SanityResolvedFont","className","style","fontFamily","stylesheet","fontFace","css","preconnect","genericFamilies","Set","variablePattern","fileFormats","Record","format","woff2","woff","quoteCss","value","escaped","replaceAll","String","raw","readMember","list","Member","find","member","resolveFontFile","family","file","url","asset","startsWith","undefined","extension","URL","pathname","split","at","toLowerCase","weight","maxWeight","weightRange","rule","join","resolveLoading","source","weights","styles","files","resolved","flatMap","resolvedFile","map","origin","loadedWeights","loadedStyles","resolveFont","options","requestedDisplay","hinted","loading","category","stack","name","trim","filter","fallbackStack","has","variableName","test","slice","SanityFont","SanityFontAsset","SanityFontFile","fontFragment"],"sources":["../src/config/fallbacks.ts","../src/lib/hash.ts","../src/lib/resolve.ts","../src/groq/fragments.ts"],"sourcesContent":["import type { SanityFontCategory } from \"@/types\";\n\n/** Font stack shown in place of each kind of typeface while it loads, or if it can't. */\nexport const fontFallbacks: Record<SanityFontCategory, string> = {\n \"sans-serif\": \"ui-sans-serif, system-ui, sans-serif\",\n serif: \"ui-serif, Georgia, serif\",\n display: \"ui-sans-serif, system-ui, sans-serif\",\n handwriting: \"cursive\",\n monospace: \"ui-monospace, monospace\",\n};\n","/**\n * Hashes text into a short identifier that's the same every time for the same text, for naming generated classes.\n *\n * @param value - The text.\n * @returns The identifier, in letters and digits.\n */\nexport function hashText(value: string) {\n let hash = 5381;\n\n // A polynomial hash, kept to 32 bits so it stays exact in a JavaScript number.\n for (const character of value) {\n hash = (hash * 33 + (character.codePointAt(0) ?? 0)) % 4_294_967_296;\n }\n\n return hash.toString(36);\n}\n","import { isDefined } from \"@repo/lib/utils\";\nimport { stegaClean } from \"@sanity/client/stega\";\n\nimport { fontFallbacks } from \"@/config/fallbacks\";\nimport { getFamilyName } from \"@/lib/family\";\nimport { createStylesheetUrl } from \"@/lib/google\";\nimport { hashText } from \"@/lib/hash\";\nimport type {\n SanityFont,\n SanityFontCategory,\n SanityFontDisplay,\n SanityFontFile,\n SanityFontStyle,\n SanityFontWeight,\n} from \"@/types\";\nimport { fontCategories, fontDisplays, fontStyles, fontWeights } from \"@/types\";\n\n/**\n * Options for resolving a font.\n *\n * @public\n */\nexport interface SanityResolveFontOptions {\n /** How the font is shown while it loads, as CSS's `font-display` names it. Defaults to `swap`. */\n display?: SanityFontDisplay;\n /** Whether the page hints that the font's files should start downloading early. Defaults to `true`. */\n preload?: boolean;\n /** Fonts shown in its place while it loads, or if it can't, such as `[\"Geist\", \"sans-serif\"]`. Defaults to a fallback for its kind of typeface. */\n fallback?: string[];\n /**\n * CSS variable the `variable` class defines, such as `--font-heading`. A name that isn't a valid custom property is\n * ignored, and without one there's no `variable` class.\n */\n variable?: string;\n}\n\n/**\n * An origin to connect to early, with whether the connection needs CORS to match the requests that follow.\n *\n * @public\n */\nexport interface SanityFontPreconnect {\n /** The origin. */\n href: string;\n /** Whether the requests to it use CORS, as font files do. */\n crossOrigin: boolean;\n}\n\n/**\n * An uploaded file to preload, with the attributes a preload link needs.\n *\n * @public\n */\nexport interface SanityFontPreload {\n /** Address of the file. */\n href: string;\n /** MIME type of the file, such as `font/woff2`. */\n type: string;\n}\n\n/**\n * A stored font, ready for a site to load and apply.\n *\n * @public\n */\nexport interface SanityResolvedFont {\n /** Class that sets the font as an element's `font-family`. */\n className: string;\n /** Inline style that sets the font as an element's `font-family`. */\n style: { fontFamily: string };\n /** Class that defines the CSS variable named in the options, or undefined when none was named. */\n variable?: string;\n /** Value for `font-family`: the family, then a fallback for its kind of typeface. */\n fontFamily: string;\n /** Google Fonts stylesheet to link, or undefined for an uploaded font. */\n stylesheet?: string;\n /** `@font-face` rules for an uploaded font, or undefined for a Google font. */\n fontFace?: string;\n /** Every rule the classes and an uploaded font's files need, for a `<style>` element. */\n css: string;\n /** Origins to connect to early, so the font's files start downloading sooner. */\n preconnect: SanityFontPreconnect[];\n /** Uploaded files to preload, empty for a Google font. */\n preload: SanityFontPreload[];\n}\n\n/** Generic font families, which CSS reads as keywords and so are never quoted. */\nconst genericFamilies = new Set([\n \"serif\",\n \"sans-serif\",\n \"monospace\",\n \"cursive\",\n \"fantasy\",\n \"system-ui\",\n \"ui-serif\",\n \"ui-sans-serif\",\n \"ui-monospace\",\n \"ui-rounded\",\n \"emoji\",\n \"math\",\n \"fangsong\",\n]);\n\n/** A valid CSS custom property name, which is also safe to use in a class name. */\nconst variablePattern = /^--[A-Za-z_][\\w-]*$/u;\n\n/** Format and MIME type of each font file extension. */\nconst fileFormats: Record<string, { format: string; type: string }> = {\n woff2: { format: \"woff2\", type: \"font/woff2\" },\n woff: { format: \"woff\", type: \"font/woff\" },\n};\n\n/**\n * Quotes text as a CSS string, so a stored value can't end the string and add rules of its own, or end the `<style>`\n * element a site puts the rules in.\n *\n * @param value - The text.\n * @returns The quoted string.\n */\nfunction quoteCss(value: string) {\n const escaped = value\n .replaceAll(/[\\n\\r\\f]/gu, \" \")\n .replaceAll(/[\"\\\\]/gu, String.raw`\\$&`)\n .replaceAll(\"<\", String.raw`\\3c `);\n\n return `\"${escaped}\"`;\n}\n\n/**\n * Narrows a stored value to one of a list's members.\n *\n * @param list - The members.\n * @param value - The stored value.\n * @returns The value, or undefined when it isn't a member.\n */\nfunction readMember<Member>(list: readonly Member[], value: unknown) {\n return list.find((member) => member === value);\n}\n\n/**\n * Builds the `@font-face` rule and preload for one uploaded file.\n *\n * @param family - Name of the family, cleaned.\n * @param file - The file.\n * @param display - How the font is shown while it loads.\n * @returns The rule and preload, or undefined when the file's URL or format can't be read.\n */\nfunction resolveFontFile(family: string, file: SanityFontFile, display: SanityFontDisplay) {\n const url = stegaClean(file.asset?.url);\n if (!isDefined(url) || !url.startsWith(\"https://\")) return undefined;\n\n const extension = new URL(url).pathname.split(\".\").at(-1)?.toLowerCase();\n const format = isDefined(extension) ? fileFormats[extension] : undefined;\n if (!isDefined(format)) return undefined;\n\n const weight = readMember(fontWeights, file.weight) ?? 400;\n const maxWeight = readMember(fontWeights, file.maxWeight);\n const style = readMember(fontStyles, stegaClean(file.style)) ?? \"normal\";\n const weightRange = isDefined(maxWeight) && maxWeight > weight ? `${weight} ${maxWeight}` : `${weight}`;\n\n const rule = [\n \"@font-face {\",\n ` font-family: ${quoteCss(family)};`,\n ` font-style: ${style};`,\n ` font-weight: ${weightRange};`,\n ` font-display: ${display};`,\n ` src: url(${quoteCss(url)}) format(\"${format.format}\");`,\n \"}\",\n ].join(\"\\n\");\n\n return { rule, preload: { href: url, type: format.type } };\n}\n\n/**\n * Works out how a font is loaded: Google's stylesheet, or the `@font-face` rules and files of an upload.\n *\n * @param value - The stored font.\n * @param family - Name of the family, cleaned.\n * @param display - How the font is shown while it loads.\n * @returns How the font is loaded, or undefined when an uploaded font has no file that can be read.\n */\nfunction resolveLoading(value: SanityFont | undefined, family: string, display: SanityFontDisplay) {\n const { source, weights, styles, files } = value ?? {};\n\n if (stegaClean(source) === \"upload\") {\n const resolved = (files ?? []).flatMap((file) => {\n const resolvedFile = resolveFontFile(family, file, display);\n\n return isDefined(resolvedFile) ? [resolvedFile] : [];\n });\n if (!isDefined(resolved)) return undefined;\n\n return {\n fontFace: resolved.map(({ rule }) => rule).join(\"\\n\"),\n preconnect: [...new Set(resolved.map(({ preload }) => new URL(preload.href).origin))].map((href) => ({\n href,\n crossOrigin: true,\n })),\n preload: resolved.map(({ preload }) => preload),\n };\n }\n\n const loadedWeights = (weights ?? []).flatMap((weight) => {\n const member: SanityFontWeight | undefined = readMember(fontWeights, weight);\n\n return isDefined(member) ? [member] : [];\n });\n const loadedStyles = (styles ?? []).flatMap((style) => {\n const member: SanityFontStyle | undefined = readMember(fontStyles, stegaClean(style));\n\n return isDefined(member) ? [member] : [];\n });\n\n return {\n stylesheet: createStylesheetUrl(family, loadedWeights, loadedStyles, display),\n // The stylesheet is fetched without CORS and the font files with it, so each origin is connected to to match.\n preconnect: [\n { href: \"https://fonts.googleapis.com\", crossOrigin: false },\n { href: \"https://fonts.gstatic.com\", crossOrigin: true },\n ],\n preload: [],\n };\n}\n\n/**\n * Resolves a stored font into what a site needs to load and apply it, shaped like a `next/font` font: a class and an\n * inline style that set the font, and a class that defines a CSS variable, with the rules behind them. An uploaded\n * font's files are only read when the query expands their assets.\n *\n * @param value - The stored font.\n * @param options - How the font is loaded and shown, and the CSS variable to define.\n * @returns The font, or undefined when nothing usable was stored.\n * @public\n */\nexport function resolveFont(\n value: SanityFont | undefined,\n options: SanityResolveFontOptions = {},\n): SanityResolvedFont | undefined {\n const family = getFamilyName(value);\n if (!isDefined(family)) return undefined;\n\n const { display: requestedDisplay, preload: hinted = true, fallback } = options;\n const display = readMember(fontDisplays, requestedDisplay) ?? \"swap\";\n const loading = resolveLoading(value, family, display);\n if (!isDefined(loading)) return undefined;\n\n const category: SanityFontCategory = readMember(fontCategories, stegaClean(value?.category)) ?? \"sans-serif\";\n const stack = (fallback ?? []).map((name) => name.trim()).filter((name) => isDefined(name));\n const fallbackStack = isDefined(stack)\n ? stack.map((name) => (genericFamilies.has(name) ? name : quoteCss(name))).join(\", \")\n : fontFallbacks[category];\n const fontFamily = `${quoteCss(family)}, ${fallbackStack}`;\n const className = `font-${hashText(`${fontFamily} ${\"stylesheet\" in loading ? loading.stylesheet : loading.fontFace}`)}`;\n const variableName =\n isDefined(options.variable) && variablePattern.test(options.variable) ? options.variable : undefined;\n const variable = isDefined(variableName) ? `${className}-${variableName.slice(2)}` : undefined;\n\n const css = [\n loading.fontFace,\n `.${className} { font-family: ${fontFamily}; }`,\n isDefined(variable) && isDefined(variableName) ? `.${variable} { ${variableName}: ${fontFamily}; }` : undefined,\n ]\n .filter((rule) => isDefined(rule))\n .join(\"\\n\");\n\n const preconnect = hinted ? loading.preconnect : [];\n const preload = hinted ? loading.preload : [];\n\n return {\n className,\n style: { fontFamily },\n ...(isDefined(variable) && { variable }),\n fontFamily,\n ...(\"stylesheet\" in loading ? { stylesheet: loading.stylesheet } : { fontFace: loading.fontFace }),\n css,\n preconnect,\n preload,\n };\n}\n","import type { SanityFont, SanityFontAsset, SanityFontFile } from \"@/types\";\n\n/**\n * GROQ projection that expands an uploaded font's files, so their URLs arrive with the font. Spread it into the\n * projection of a font field, such as `headingFont { ${fontFragment} }`.\n *\n * @public\n */\nexport const fontFragment = `..., ${\"files\" satisfies keyof SanityFont}[] { ..., ${\"asset\" satisfies keyof SanityFontFile}-> { ${\"url\" satisfies keyof SanityFontAsset} } }`;\n"],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ import { i as SanityFontDisplay, t as SanityFont } from "./types-DZl90ROd.js";
2
+ /**
3
+ * Options for resolving a font.
4
+ *
5
+ * @public
6
+ */
7
+ interface SanityResolveFontOptions {
8
+ /** How the font is shown while it loads, as CSS's `font-display` names it. Defaults to `swap`. */
9
+ display?: SanityFontDisplay;
10
+ /** Whether the page hints that the font's files should start downloading early. Defaults to `true`. */
11
+ preload?: boolean;
12
+ /** Fonts shown in its place while it loads, or if it can't, such as `["Geist", "sans-serif"]`. Defaults to a fallback for its kind of typeface. */
13
+ fallback?: string[];
14
+ /**
15
+ * CSS variable the `variable` class defines, such as `--font-heading`. A name that isn't a valid custom property is
16
+ * ignored, and without one there's no `variable` class.
17
+ */
18
+ variable?: string;
19
+ }
20
+ /**
21
+ * An origin to connect to early, with whether the connection needs CORS to match the requests that follow.
22
+ *
23
+ * @public
24
+ */
25
+ interface SanityFontPreconnect {
26
+ /** The origin. */
27
+ href: string;
28
+ /** Whether the requests to it use CORS, as font files do. */
29
+ crossOrigin: boolean;
30
+ }
31
+ /**
32
+ * An uploaded file to preload, with the attributes a preload link needs.
33
+ *
34
+ * @public
35
+ */
36
+ interface SanityFontPreload {
37
+ /** Address of the file. */
38
+ href: string;
39
+ /** MIME type of the file, such as `font/woff2`. */
40
+ type: string;
41
+ }
42
+ /**
43
+ * A stored font, ready for a site to load and apply.
44
+ *
45
+ * @public
46
+ */
47
+ interface SanityResolvedFont {
48
+ /** Class that sets the font as an element's `font-family`. */
49
+ className: string;
50
+ /** Inline style that sets the font as an element's `font-family`. */
51
+ style: {
52
+ fontFamily: string;
53
+ };
54
+ /** Class that defines the CSS variable named in the options, or undefined when none was named. */
55
+ variable?: string;
56
+ /** Value for `font-family`: the family, then a fallback for its kind of typeface. */
57
+ fontFamily: string;
58
+ /** Google Fonts stylesheet to link, or undefined for an uploaded font. */
59
+ stylesheet?: string;
60
+ /** `@font-face` rules for an uploaded font, or undefined for a Google font. */
61
+ fontFace?: string;
62
+ /** Every rule the classes and an uploaded font's files need, for a `<style>` element. */
63
+ css: string;
64
+ /** Origins to connect to early, so the font's files start downloading sooner. */
65
+ preconnect: SanityFontPreconnect[];
66
+ /** Uploaded files to preload, empty for a Google font. */
67
+ preload: SanityFontPreload[];
68
+ }
69
+ /**
70
+ * Resolves a stored font into what a site needs to load and apply it, shaped like a `next/font` font: a class and an
71
+ * inline style that set the font, and a class that defines a CSS variable, with the rules behind them. An uploaded
72
+ * font's files are only read when the query expands their assets.
73
+ *
74
+ * @param value - The stored font.
75
+ * @param options - How the font is loaded and shown, and the CSS variable to define.
76
+ * @returns The font, or undefined when nothing usable was stored.
77
+ * @public
78
+ */
79
+ declare function resolveFont(value: SanityFont | undefined, options?: SanityResolveFontOptions): SanityResolvedFont | undefined;
80
+ export { resolveFont as a, SanityResolvedFont as i, SanityFontPreload as n, SanityResolveFontOptions as r, SanityFontPreconnect as t };
81
+ //# sourceMappingURL=resolve-Bkp0dEmr.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-Bkp0dEmr.d.ts","names":["SanityFont","SanityFontDisplay","SanityResolveFontOptions","SanityFontPreconnect","SanityFontPreload","SanityResolvedFont","resolveFont"],"sources":["../src/lib/resolve.ts"],"mappings":";;;;;;UAsBiBE;;EAEf,UAAUD;;EAEV;;EAEA;;;;;EAKA;;;;;;;UAQeE;;EAEf;;EAEA;;;;;;;UAQeC;;EAEf;;EAEA;;;;;;;UAQeC;;EAEf;;EAEA;IAAS;;;EAET;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA,YAAYF;;EAEZ,SAASC;;;;;;;;;;;;iBAuJKE,YACd,OAAON,wBACP,UAASE,2BACRG"}
@@ -0,0 +1,106 @@
1
+ import { SanityFontNames } from "@driftime/sanity-plugin-font/names";
2
+ /** Schema type name of the font object. */
3
+ declare const fontTypeName = "font";
4
+ /** Schema type name of an uploaded font file. */
5
+ declare const fontFileTypeName = "fontFile";
6
+ /** Where a font comes from, in the order the picker offers them. */
7
+ declare const fontSources: readonly ["google", "upload"];
8
+ /**
9
+ * Where a font comes from: the Google Fonts catalog, or files uploaded to the dataset.
10
+ *
11
+ * @public
12
+ */
13
+ type SanityFontSource = (typeof fontSources)[number];
14
+ /** Accepts any string without losing a union's autocomplete suggestions. */
15
+ type AnyString = string & NonNullable<unknown>;
16
+ /**
17
+ * Name of a Google Fonts family, checked against the catalog once the build step has downloaded it, or any name before
18
+ * then.
19
+ *
20
+ * @public
21
+ */
22
+ type SanityFontFamily = [SanityFontNames] extends [never] ? AnyString : SanityFontNames;
23
+ /** Kinds of typeface, in the order the picker lists them. */
24
+ declare const fontCategories: readonly ["sans-serif", "serif", "display", "handwriting", "monospace"];
25
+ /**
26
+ * Kind of typeface, which decides the fallback shown while the font loads.
27
+ *
28
+ * @public
29
+ */
30
+ type SanityFontCategory = (typeof fontCategories)[number];
31
+ /** How a font is shown while it loads, as CSS's `font-display` names them. */
32
+ declare const fontDisplays: readonly ["auto", "block", "swap", "fallback", "optional"];
33
+ /**
34
+ * How a font is shown while it loads, as CSS's `font-display` names them.
35
+ *
36
+ * @public
37
+ */
38
+ type SanityFontDisplay = (typeof fontDisplays)[number];
39
+ /** Weights a font can be loaded in, from thinnest to heaviest. */
40
+ declare const fontWeights: readonly [100, 200, 300, 400, 500, 600, 700, 800, 900];
41
+ /**
42
+ * Weight a font can be loaded in.
43
+ *
44
+ * @public
45
+ */
46
+ type SanityFontWeight = (typeof fontWeights)[number];
47
+ /** Styles a font can be loaded in. */
48
+ declare const fontStyles: readonly ["normal", "italic"];
49
+ /**
50
+ * Style a font can be loaded in.
51
+ *
52
+ * @public
53
+ */
54
+ type SanityFontStyle = (typeof fontStyles)[number];
55
+ /**
56
+ * File asset an uploaded font file points to. A query expands it to read the URL.
57
+ *
58
+ * @public
59
+ */
60
+ interface SanityFontAsset {
61
+ /** ID of the asset, until the query expands it. */
62
+ _ref?: string;
63
+ /** Address the file is served from, once the query expands the asset. */
64
+ url?: string;
65
+ }
66
+ /**
67
+ * One uploaded font file, with the weight and style it holds.
68
+ *
69
+ * @public
70
+ */
71
+ interface SanityFontFile {
72
+ _type: typeof fontFileTypeName;
73
+ _key: string;
74
+ /** The file asset. */
75
+ asset?: SanityFontAsset;
76
+ /** Name of the family the file belongs to, read from the file. */
77
+ family?: string;
78
+ /** Weight the file holds, or the lightest a variable font file covers. */
79
+ weight?: SanityFontWeight;
80
+ /** Heaviest weight a variable font file covers, or undefined for a file with one weight. */
81
+ maxWeight?: SanityFontWeight;
82
+ /** Style the file holds. */
83
+ style?: SanityFontStyle;
84
+ }
85
+ /**
86
+ * Stored font: a Google Fonts family with the weights and styles to load, or a family uploaded as files.
87
+ *
88
+ * @public
89
+ */
90
+ interface SanityFont {
91
+ _type: typeof fontTypeName;
92
+ /** Where the font comes from. */
93
+ source?: SanityFontSource;
94
+ /** Name of the font family. */
95
+ family?: string;
96
+ /** Kind of typeface. */
97
+ category?: SanityFontCategory;
98
+ /** Weights to load from Google Fonts, saved when the family is chosen. */
99
+ weights?: SanityFontWeight[];
100
+ /** Styles to load from Google Fonts, saved when the family is chosen. */
101
+ styles?: SanityFontStyle[];
102
+ /** Uploaded files, one for each weight and style. */
103
+ files?: SanityFontFile[];
104
+ }
105
+ export { SanityFontFamily as a, SanityFontStyle as c, SanityFontDisplay as i, SanityFontWeight as l, SanityFontAsset as n, SanityFontFile as o, SanityFontCategory as r, SanityFontSource as s, SanityFont as t, fontTypeName as u };
106
+ //# sourceMappingURL=types-DZl90ROd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types-DZl90ROd.d.ts","names":["SanityFontNames","fontTypeName","fontFileTypeName","fontSources","SanityFontSource","AnyString","NonNullable","SanityFontFamily","fontCategories","SanityFontCategory","fontDisplays","SanityFontDisplay","fontWeights","SanityFontWeight","fontStyles","SanityFontStyle","SanityFontAsset","SanityFontFile","SanityFont"],"sources":["../src/types.ts"],"mappings":";;cAGaC;;cAGAC;;cAGAC;;;;;;KAODC,2BAA2BD;;KAGlCE,qBAAqBC;;;;;;;KAQdC,oBAAoBP,mCAAmCK,YAAYL;;cAGlEQ;;;;;;KAODC,6BAA6BD;;cAG5BE;;;;;;KAODC,4BAA4BD;;cAG3BE;;;;;;KAODC,2BAA2BD;;cAG1BE;;;;;;KAODC,0BAA0BD;;;;;;UAOrBE;;EAEf;;EAEA;;;;;;;UAQeC;EACf,cAAcf;EACd;;EAEA,QAAQc;;EAER;;EAEA,SAASH;;EAET,YAAYA;;EAEZ,QAAQE;;;;;;;UAQOG;EACf,cAAcjB;;EAEd,SAASG;;EAET;;EAEA,WAAWK;;EAEX,UAAUI;;EAEV,SAASE;;EAET,QAAQE"}
@@ -0,0 +1,27 @@
1
+ /** Schema type name of the font object. */
2
+ const fontTypeName = "font", fontFileTypeName = "fontFile", fontSources = ["google", "upload"], fontCategories = [
3
+ "sans-serif",
4
+ "serif",
5
+ "display",
6
+ "handwriting",
7
+ "monospace"
8
+ ], fontDisplays = [
9
+ "auto",
10
+ "block",
11
+ "swap",
12
+ "fallback",
13
+ "optional"
14
+ ], fontWeights = [
15
+ 100,
16
+ 200,
17
+ 300,
18
+ 400,
19
+ 500,
20
+ 600,
21
+ 700,
22
+ 800,
23
+ 900
24
+ ], fontStyles = ["normal", "italic"];
25
+ export { fontStyles as a, fontSources as i, fontDisplays as n, fontTypeName as o, fontFileTypeName as r, fontWeights as s, fontCategories as t };
26
+
27
+ //# sourceMappingURL=types-DheP5tax.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types-DheP5tax.js","names":["SanityFontNames","fontTypeName","fontFileTypeName","fontSources","const","SanityFontSource","AnyString","NonNullable","SanityFontFamily","fontCategories","SanityFontCategory","fontDisplays","SanityFontDisplay","fontWeights","SanityFontWeight","fontStyles","SanityFontStyle","SanityFontAsset","_ref","url","SanityFontFile","_type","_key","asset","family","weight","maxWeight","style","SanityFont","source","category","weights","styles","files"],"sources":["../src/types.ts"],"sourcesContent":["import type { SanityFontNames } from \"@driftime/sanity-plugin-font/names\";\n\n/** Schema type name of the font object. */\nexport const fontTypeName = \"font\";\n\n/** Schema type name of an uploaded font file. */\nexport const fontFileTypeName = \"fontFile\";\n\n/** Where a font comes from, in the order the picker offers them. */\nexport const fontSources = [\"google\", \"upload\"] as const;\n\n/**\n * Where a font comes from: the Google Fonts catalog, or files uploaded to the dataset.\n *\n * @public\n */\nexport type SanityFontSource = (typeof fontSources)[number];\n\n/** Accepts any string without losing a union's autocomplete suggestions. */\ntype AnyString = string & NonNullable<unknown>;\n\n/**\n * Name of a Google Fonts family, checked against the catalog once the build step has downloaded it, or any name before\n * then.\n *\n * @public\n */\nexport type SanityFontFamily = [SanityFontNames] extends [never] ? AnyString : SanityFontNames;\n\n/** Kinds of typeface, in the order the picker lists them. */\nexport const fontCategories = [\"sans-serif\", \"serif\", \"display\", \"handwriting\", \"monospace\"] as const;\n\n/**\n * Kind of typeface, which decides the fallback shown while the font loads.\n *\n * @public\n */\nexport type SanityFontCategory = (typeof fontCategories)[number];\n\n/** How a font is shown while it loads, as CSS's `font-display` names them. */\nexport const fontDisplays = [\"auto\", \"block\", \"swap\", \"fallback\", \"optional\"] as const;\n\n/**\n * How a font is shown while it loads, as CSS's `font-display` names them.\n *\n * @public\n */\nexport type SanityFontDisplay = (typeof fontDisplays)[number];\n\n/** Weights a font can be loaded in, from thinnest to heaviest. */\nexport const fontWeights = [100, 200, 300, 400, 500, 600, 700, 800, 900] as const;\n\n/**\n * Weight a font can be loaded in.\n *\n * @public\n */\nexport type SanityFontWeight = (typeof fontWeights)[number];\n\n/** Styles a font can be loaded in. */\nexport const fontStyles = [\"normal\", \"italic\"] as const;\n\n/**\n * Style a font can be loaded in.\n *\n * @public\n */\nexport type SanityFontStyle = (typeof fontStyles)[number];\n\n/**\n * File asset an uploaded font file points to. A query expands it to read the URL.\n *\n * @public\n */\nexport interface SanityFontAsset {\n /** ID of the asset, until the query expands it. */\n _ref?: string;\n /** Address the file is served from, once the query expands the asset. */\n url?: string;\n}\n\n/**\n * One uploaded font file, with the weight and style it holds.\n *\n * @public\n */\nexport interface SanityFontFile {\n _type: typeof fontFileTypeName;\n _key: string;\n /** The file asset. */\n asset?: SanityFontAsset;\n /** Name of the family the file belongs to, read from the file. */\n family?: string;\n /** Weight the file holds, or the lightest a variable font file covers. */\n weight?: SanityFontWeight;\n /** Heaviest weight a variable font file covers, or undefined for a file with one weight. */\n maxWeight?: SanityFontWeight;\n /** Style the file holds. */\n style?: SanityFontStyle;\n}\n\n/**\n * Stored font: a Google Fonts family with the weights and styles to load, or a family uploaded as files.\n *\n * @public\n */\nexport interface SanityFont {\n _type: typeof fontTypeName;\n /** Where the font comes from. */\n source?: SanityFontSource;\n /** Name of the font family. */\n family?: string;\n /** Kind of typeface. */\n category?: SanityFontCategory;\n /** Weights to load from Google Fonts, saved when the family is chosen. */\n weights?: SanityFontWeight[];\n /** Styles to load from Google Fonts, saved when the family is chosen. */\n styles?: SanityFontStyle[];\n /** Uploaded files, one for each weight and style. */\n files?: SanityFontFile[];\n}\n"],"mappings":";AAGA,MAAaC,eAAe,QAGfC,mBAAmB,YAGnBC,cAAc,CAAC,UAAU,QAAQ,GAqBjCM,iBAAiB;CAAC;CAAc;CAAS;CAAW;CAAe;AAAW,GAU9EE,eAAe;CAAC;CAAQ;CAAS;CAAQ;CAAY;AAAU,GAU/DE,cAAc;CAAC;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;AAAG,GAU1DE,aAAa,CAAC,UAAU,QAAQ"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Checks whether a value is present. `false`, blank strings, and empty plain objects count as absent,
3
+ * as does an array with no present elements.
4
+ *
5
+ * @param value - The value to check.
6
+ * @returns True if the value is present.
7
+ */
8
+ function isDefined(value) {
9
+ if (value == null || value === !1) return !1;
10
+ if (typeof value == "string") return value.trim() !== "";
11
+ if (Array.isArray(value)) return value.some((element) => isDefined(element));
12
+ if (typeof value == "object") {
13
+ let prototype = Reflect.getPrototypeOf(value);
14
+ return prototype !== Object.prototype && prototype !== null || Object.keys(value).length > 0;
15
+ }
16
+ return !0;
17
+ }
18
+ /**
19
+ * Checks whether a value is an object whose keys can be read.
20
+ *
21
+ * @param value - The value to check.
22
+ * @returns True if the value is an object.
23
+ */
24
+ function isRecord(value) {
25
+ return typeof value == "object" && !!value;
26
+ }
27
+ export { isRecord as n, isDefined as t };
28
+
29
+ //# sourceMappingURL=utils-BV38iODp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils-BV38iODp.js","names":["Nullable","T","isDefined","value","undefined","trim","Array","isArray","some","element","prototype","Reflect","getPrototypeOf","Object","keys","length","convertCase","format","words","replaceAll","toLowerCase","_match","character","toUpperCase","isRecord","Record","readPath","path","current","segment"],"sources":["../../@repo/lib/utils.ts"],"sourcesContent":["/** A value that may be null or undefined. */\ntype Nullable<T> = T | null | undefined;\n\n/**\n * Checks whether a value is present. `false`, blank strings, and empty plain objects count as absent,\n * as does an array with no present elements.\n *\n * @param value - The value to check.\n * @returns True if the value is present.\n */\nexport function isDefined<T>(value: Nullable<T> | false): value is T {\n if (value === undefined || value === null || value === false) return false;\n if (typeof value === \"string\") return value.trim() !== \"\";\n if (Array.isArray(value)) return value.some((element) => isDefined(element));\n\n if (typeof value === \"object\") {\n const prototype = Reflect.getPrototypeOf(value);\n if (prototype !== Object.prototype && prototype !== null) return true;\n\n return Object.keys(value).length > 0;\n }\n\n return true;\n}\n\n/**\n * Converts a string to another case.\n *\n * @param value - The string to convert.\n * @param format - The case to convert to.\n * @returns The converted string.\n */\nexport function convertCase(value: string, format: \"kebab\" | \"snake\" | \"camel\" | \"pascal\" | \"title\" | \"sentence\") {\n const words = value\n .replaceAll(/(?<lower>[a-z0-9])(?<upper>[A-Z])/gu, \"$<lower> $<upper>\")\n .replaceAll(/[-_\\s]+/gu, \" \")\n .trim()\n .toLowerCase();\n\n switch (format) {\n case \"kebab\": {\n return words.replaceAll(/\\s+/gu, \"-\");\n }\n case \"snake\": {\n return words.replaceAll(/\\s+/gu, \"_\");\n }\n case \"camel\": {\n return words.replaceAll(/\\s+(?<character>.)/gu, (_match, character: string) => character.toUpperCase());\n }\n case \"pascal\": {\n return words.replaceAll(/(?:^|\\s+)(?<character>.)/gu, (_match, character: string) => character.toUpperCase());\n }\n case \"title\": {\n return words.replaceAll(/\\b\\w/gu, (character) => character.toUpperCase());\n }\n case \"sentence\": {\n return words.replaceAll(/^(?<character>.)/gu, (character) => character.toUpperCase());\n }\n default: {\n return value;\n }\n }\n}\n\n/**\n * Checks whether a value is an object whose keys can be read.\n *\n * @param value - The value to check.\n * @returns True if the value is an object.\n */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\n/**\n * Reads a nested value by following a path of keys.\n *\n * @param value - The object to read from.\n * @param path - The keys to follow.\n * @returns The value, or undefined when the path doesn't exist.\n */\nexport function readPath(value: unknown, path: string[]) {\n let current: unknown = value;\n\n for (const segment of path) {\n if (!isRecord(current)) return undefined;\n\n current = current[segment];\n }\n\n return current;\n}\n"],"mappings":";;;;;;;AAUA,SAAgBE,UAAaC,OAAwC;CACnE,IAAIA,SAAiC,QAAQA,UAAU,IAAO,OAAO;CACrE,IAAI,OAAOA,SAAU,UAAU,OAAOA,MAAME,KAAK,MAAM;CACvD,IAAIC,MAAMC,QAAQJ,KAAK,GAAG,OAAOA,MAAMK,MAAMC,YAAYP,UAAUO,OAAO,CAAC;CAE3E,IAAI,OAAON,SAAU,UAAU;EAC7B,IAAMO,YAAYC,QAAQC,eAAeT,KAAK;EAG9C,OAFIO,cAAcG,OAAOH,aAAaA,cAAc,QAE7CG,OAAOC,KAAKX,KAAK,CAAC,CAACY,SAAS;CACrC;CAEA,OAAO;AACT;;;;;;;AA+CA,SAAgBS,SAASrB,OAAkD;CACzE,OAAO,OAAOA,SAAU,cAAYA;AACtC"}