astrotype 0.1.4 → 0.1.6

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.
@@ -1,6 +1,7 @@
1
1
  import path from "node:path";
2
- import { getFontByName, getPairingByName } from "../lib/registry.mjs";
2
+ import { getAllPairings, getFontByName, getPairingByName } from "../lib/registry.mjs";
3
3
  import {
4
+ activatePairingCssImport,
4
5
  ensureAstroFontsApiSetup,
5
6
  isAstroProject,
6
7
  writeAstrotypeCss,
@@ -17,6 +18,52 @@ function toCssRules(ruleMap) {
17
18
  .join("\n\n");
18
19
  }
19
20
 
21
+ function fontFamilyFallback(font) {
22
+ const raw =
23
+ font?.font?.provider === "google"
24
+ ? String(font?.font?.family || font?.title || "")
25
+ : String(font?.title || "");
26
+ const normalized = raw.replace(/ Variable$/, "").replace(/"/g, '\\"');
27
+ return `"${normalized}"`;
28
+ }
29
+
30
+ function withPairingFontFallbacks(pairing, fonts) {
31
+ const headingFont = fonts[0];
32
+ const bodyFont = fonts[1];
33
+ const monoFont = fonts[2];
34
+ const css = JSON.parse(JSON.stringify(pairing.css || {}));
35
+ const root = { ...css[":root"] };
36
+
37
+ if (headingFont) {
38
+ const variable = headingFont.font?.variable || "--font-heading";
39
+ root["--font-heading"] = `var(${variable}, ${fontFamilyFallback(headingFont)})`;
40
+ }
41
+ if (bodyFont) {
42
+ const variable = bodyFont.font?.variable || "--font-body";
43
+ root["--font-body"] = `var(${variable}, ${fontFamilyFallback(bodyFont)})`;
44
+ }
45
+ if (monoFont) {
46
+ const variable = monoFont.font?.variable || "--font-mono";
47
+ root["--font-mono"] = `var(${variable}, ${fontFamilyFallback(monoFont)})`;
48
+ }
49
+ css[":root"] = root;
50
+ return css;
51
+ }
52
+
53
+ function toUtilityClassRules() {
54
+ return `.font-heading {
55
+ font-family: var(--font-heading);
56
+ }
57
+
58
+ .font-body {
59
+ font-family: var(--font-body);
60
+ }
61
+
62
+ .font-mono {
63
+ font-family: var(--font-mono);
64
+ }`;
65
+ }
66
+
20
67
  export async function addPairingCommand(pairingName, options = {}) {
21
68
  const targetDir = path.resolve(options.cwd || process.cwd());
22
69
  if (!isAstroProject(targetDir)) {
@@ -40,12 +87,20 @@ export async function addPairingCommand(pairingName, options = {}) {
40
87
  process.exit(1);
41
88
  }
42
89
 
43
- const cssText = `${toCssRules(pairing.css)}
90
+ const cssRulesWithFallbacks = withPairingFontFallbacks(pairing, fonts);
91
+ const cssText = `${toCssRules(cssRulesWithFallbacks)}
92
+
93
+ ${toUtilityClassRules()}
44
94
  `;
45
95
  const pairingCssPath = writeAstrotypeCss(targetDir, pairing.name, cssText);
96
+ const pairingSlugs = getAllPairings().map((item) => item.name).filter(Boolean);
97
+ const importUpdate = activatePairingCssImport(targetDir, pairing.name, pairingSlugs);
46
98
  const fontsSetup = ensureAstroFontsApiSetup(targetDir, fonts);
47
99
 
48
100
  console.log(`Installed pairing "${pairing.name}" -> ${path.relative(targetDir, pairingCssPath)}`);
101
+ if (importUpdate.removed.length > 0) {
102
+ console.log(`Switched active pairing import (removed: ${importUpdate.removed.join(", ")}).`);
103
+ }
49
104
  if (fontsSetup.configured) {
50
105
  console.log("Wired Astro Fonts API entries for this pairing.");
51
106
  if (fontsSetup.manualMergeRequired) {
@@ -38,10 +38,14 @@ function toAstroFontDescriptor(font) {
38
38
  if (provider === "npm") {
39
39
  const npmPackage = font.font.npmPackage || font.font.package;
40
40
  if (!npmPackage) return null;
41
- let file = null;
42
41
  if (npmPackage === "geist") {
43
- if (font.name === "geist") file = "font/sans.css";
44
- if (font.name === "geist-mono") file = "font/mono.css";
42
+ return {
43
+ name: font.name,
44
+ familyName: font.name === "geist-mono" ? "Geist Mono" : "Geist",
45
+ cssVariable,
46
+ provider: "google",
47
+ options: null,
48
+ };
45
49
  }
46
50
  return {
47
51
  name: font.name,
@@ -50,7 +54,6 @@ function toAstroFontDescriptor(font) {
50
54
  provider: "npm",
51
55
  options: {
52
56
  package: npmPackage,
53
- ...(file ? { file } : {}),
54
57
  },
55
58
  };
56
59
  }
@@ -58,11 +61,15 @@ function toAstroFontDescriptor(font) {
58
61
  }
59
62
 
60
63
  function serializeAstroFontDescriptor(descriptor) {
64
+ const providerCall =
65
+ descriptor.provider === "npm"
66
+ ? 'fontProviders.npm({ cdn: "https://cdn.jsdelivr.net/npm/" })'
67
+ : `fontProviders.${descriptor.provider}()`;
61
68
  const optionsLine = descriptor.options
62
69
  ? `,\n options: ${JSON.stringify(descriptor.options)}`
63
70
  : "";
64
71
  return ` {
65
- provider: fontProviders.${descriptor.provider}(),
72
+ provider: ${providerCall},
66
73
  name: ${JSON.stringify(descriptor.familyName)},
67
74
  cssVariable: ${JSON.stringify(descriptor.cssVariable)}${optionsLine}
68
75
  }`;
@@ -150,18 +157,29 @@ ${lines}
150
157
  configText = ensureAstroConfigHasAstrotypeImport(configText);
151
158
  const result = ensureAstroConfigHasFontsProperty(configText);
152
159
  fs.writeFileSync(astroConfigPath, result.text);
160
+ const isAlreadyWired = /\bfonts\s*:\s*astrotypeFonts\b/.test(result.text);
153
161
 
154
162
  return {
155
163
  configured: true,
156
164
  configPath: astroConfigPath,
157
- manualMergeRequired: !result.injected && /\bfonts\s*:/.test(configText),
165
+ manualMergeRequired: !result.injected && !isAlreadyWired && /\bfonts\s*:/.test(configText),
158
166
  };
159
167
  }
160
168
 
161
169
  export function ensureImport(globalCssPath, importLine) {
162
170
  const css = fs.existsSync(globalCssPath) ? fs.readFileSync(globalCssPath, "utf-8") : "";
163
171
  if (css.includes(importLine)) return;
164
- fs.writeFileSync(globalCssPath, `${importLine}\n${css}`);
172
+ const lines = css.split(/\r?\n/);
173
+ let lastImportIndex = -1;
174
+ for (let index = 0; index < lines.length; index += 1) {
175
+ if (/^\s*@import\s+/.test(lines[index])) lastImportIndex = index;
176
+ }
177
+ if (lastImportIndex === -1) {
178
+ fs.writeFileSync(globalCssPath, `${importLine}\n${css}`);
179
+ return;
180
+ }
181
+ lines.splice(lastImportIndex + 1, 0, importLine);
182
+ fs.writeFileSync(globalCssPath, `${lines.join("\n")}\n`);
165
183
  }
166
184
 
167
185
  export function writeAstrotypeCss(targetDir, slug, cssText) {
@@ -175,3 +193,53 @@ export function writeAstrotypeCss(targetDir, slug, cssText) {
175
193
  ensureImport(globalCssPath, `@import "./astrotypes/${slug}.css";`);
176
194
  return cssPath;
177
195
  }
196
+
197
+ export function activatePairingCssImport(targetDir, activePairingSlug, pairingSlugs) {
198
+ const globalCssPath = path.join(targetDir, "src", "styles", "global.css");
199
+ ensureDir(path.dirname(globalCssPath));
200
+ const importLine = `@import "./astrotypes/${activePairingSlug}.css";`;
201
+ const knownPairings = new Set(pairingSlugs || []);
202
+
203
+ if (!fs.existsSync(globalCssPath)) {
204
+ fs.writeFileSync(globalCssPath, `${importLine}\n`);
205
+ return { removed: [], added: true };
206
+ }
207
+
208
+ const css = fs.readFileSync(globalCssPath, "utf-8");
209
+ const lines = css.split(/\r?\n/);
210
+ const keptLines = [];
211
+ const removed = [];
212
+ let hasActive = false;
213
+
214
+ for (const line of lines) {
215
+ const match = line.match(
216
+ /^\s*@import\s+["']\.\/astrotypes\/([a-z0-9-]+)\.css["'];\s*$/
217
+ );
218
+ if (!match) {
219
+ keptLines.push(line);
220
+ continue;
221
+ }
222
+ const slug = match[1];
223
+ if (slug === activePairingSlug) {
224
+ hasActive = true;
225
+ keptLines.push(importLine);
226
+ continue;
227
+ }
228
+ if (knownPairings.has(slug)) {
229
+ removed.push(slug);
230
+ continue;
231
+ }
232
+ keptLines.push(line);
233
+ }
234
+
235
+ if (!hasActive) {
236
+ let lastImportIndex = -1;
237
+ for (let index = 0; index < keptLines.length; index += 1) {
238
+ if (/^\s*@import\s+/.test(keptLines[index])) lastImportIndex = index;
239
+ }
240
+ if (lastImportIndex === -1) keptLines.unshift(importLine);
241
+ else keptLines.splice(lastImportIndex + 1, 0, importLine);
242
+ }
243
+ fs.writeFileSync(globalCssPath, `${keptLines.join("\n")}\n`);
244
+ return { removed, added: !hasActive };
245
+ }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "astrotype",
3
3
  "description": "Astro-first font registry and CLI for installing pairings or single fonts.",
4
4
  "type": "module",
5
- "version": "0.1.4",
5
+ "version": "0.1.6",
6
6
  "private": false,
7
7
  "keywords": [
8
8
  "astro",