astrotype 0.1.2 → 0.1.4

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,7 +1,7 @@
1
1
  import path from "node:path";
2
2
  import { getFontByName } from "../lib/registry.mjs";
3
3
  import {
4
- buildFontImports,
4
+ ensureAstroFontsApiSetup,
5
5
  isAstroProject,
6
6
  writeAstrotypeCss,
7
7
  } from "../lib/add-utils.mjs";
@@ -30,10 +30,19 @@ export async function addFontCommand(fontName, options = {}) {
30
30
  process.exit(1);
31
31
  }
32
32
 
33
- const fontImports = buildFontImports([font]);
34
- const cssText = `${fontImports}
35
-
36
- ${toCssRules(font)}`;
33
+ const cssText = `${toCssRules(font)}`;
37
34
  const cssPath = writeAstrotypeCss(targetDir, font.name, cssText);
38
- console.log(`Installed font "${font.name}" into ${path.relative(targetDir, cssPath)}.`);
35
+ const fontsSetup = ensureAstroFontsApiSetup(targetDir, [font]);
36
+ console.log(`Installed font "${font.name}" -> ${path.relative(targetDir, cssPath)}`);
37
+ if (fontsSetup.configured) {
38
+ console.log("Wired Astro Fonts API entries for this font.");
39
+ if (fontsSetup.manualMergeRequired) {
40
+ console.log(
41
+ `Heads up: existing fonts config found in ${path.relative(targetDir, fontsSetup.configPath)}. Merge astrotypeFonts there.`
42
+ );
43
+ }
44
+ } else {
45
+ console.log(`Skipped Astro Fonts API setup: ${fontsSetup.reason}`);
46
+ }
47
+ console.log(`Next step: map your app classes/tokens to ${font.font.variable || `--font-${font.name}`}.`);
39
48
  }
@@ -1,7 +1,7 @@
1
1
  import path from "node:path";
2
2
  import { getFontByName, getPairingByName } from "../lib/registry.mjs";
3
3
  import {
4
- buildFontImports,
4
+ ensureAstroFontsApiSetup,
5
5
  isAstroProject,
6
6
  writeAstrotypeCss,
7
7
  } from "../lib/add-utils.mjs";
@@ -40,14 +40,22 @@ export async function addPairingCommand(pairingName, options = {}) {
40
40
  process.exit(1);
41
41
  }
42
42
 
43
- const fontImports = buildFontImports(fonts);
44
- const cssText = `${fontImports}
45
-
46
- ${toCssRules(pairing.css)}
43
+ const cssText = `${toCssRules(pairing.css)}
47
44
  `;
48
45
  const pairingCssPath = writeAstrotypeCss(targetDir, pairing.name, cssText);
49
-
50
- console.log(`Installed pairing "${pairing.name}" into ${path.relative(targetDir, pairingCssPath)}.`);
51
- console.log("No Tailwind color utilities are required; styles are CSS-token driven.");
46
+ const fontsSetup = ensureAstroFontsApiSetup(targetDir, fonts);
47
+
48
+ console.log(`Installed pairing "${pairing.name}" -> ${path.relative(targetDir, pairingCssPath)}`);
49
+ if (fontsSetup.configured) {
50
+ console.log("Wired Astro Fonts API entries for this pairing.");
51
+ if (fontsSetup.manualMergeRequired) {
52
+ console.log(
53
+ `Heads up: existing fonts config found in ${path.relative(targetDir, fontsSetup.configPath)}. Merge astrotypeFonts there.`
54
+ );
55
+ }
56
+ } else {
57
+ console.log(`Skipped Astro Fonts API setup: ${fontsSetup.reason}`);
58
+ }
59
+ console.log("Next step: map your app classes/tokens to --font-heading, --font-body, and --font-mono.");
52
60
  }
53
61
 
@@ -22,28 +22,140 @@ export function isAstroProject(targetDir) {
22
22
  );
23
23
  }
24
24
 
25
- export function buildFontImports(fontEntries) {
26
- const lines = [];
27
- for (const font of fontEntries) {
28
- if (!font) continue;
29
- if (font.font.provider === "npm" && font.font.npmPackage === "geist") {
30
- if (font.name === "geist") lines.push('@import "geist/font/sans.css";');
31
- if (font.name === "geist-mono") lines.push('@import "geist/font/mono.css";');
32
- continue;
33
- }
34
- if (font.font.dependency) {
35
- lines.push(`@import "${font.font.dependency}";`);
36
- continue;
37
- }
38
- if (font.font.provider === "google") {
39
- lines.push(
40
- `@import url("https://fonts.googleapis.com/css2?family=${encodeURIComponent(
41
- font.title
42
- ).replace(/%20/g, "+")}:wght@400;500;600;700&display=swap");`
43
- );
25
+ function toAstroFontDescriptor(font) {
26
+ if (!font?.font?.provider) return null;
27
+ const provider = String(font.font.provider).toLowerCase();
28
+ const cssVariable = font.font.variable || `--font-${font.name}`;
29
+ if (provider === "google") {
30
+ return {
31
+ name: font.name,
32
+ familyName: font.title,
33
+ cssVariable,
34
+ provider: "google",
35
+ options: null,
36
+ };
37
+ }
38
+ if (provider === "npm") {
39
+ const npmPackage = font.font.npmPackage || font.font.package;
40
+ if (!npmPackage) return null;
41
+ let file = null;
42
+ if (npmPackage === "geist") {
43
+ if (font.name === "geist") file = "font/sans.css";
44
+ if (font.name === "geist-mono") file = "font/mono.css";
44
45
  }
46
+ return {
47
+ name: font.name,
48
+ familyName: font.title,
49
+ cssVariable,
50
+ provider: "npm",
51
+ options: {
52
+ package: npmPackage,
53
+ ...(file ? { file } : {}),
54
+ },
55
+ };
56
+ }
57
+ return null;
58
+ }
59
+
60
+ function serializeAstroFontDescriptor(descriptor) {
61
+ const optionsLine = descriptor.options
62
+ ? `,\n options: ${JSON.stringify(descriptor.options)}`
63
+ : "";
64
+ return ` {
65
+ provider: fontProviders.${descriptor.provider}(),
66
+ name: ${JSON.stringify(descriptor.familyName)},
67
+ cssVariable: ${JSON.stringify(descriptor.cssVariable)}${optionsLine}
68
+ }`;
69
+ }
70
+
71
+ function getAstroConfigPath(targetDir) {
72
+ const astroConfigCandidates = [
73
+ "astro.config.mjs",
74
+ "astro.config.js",
75
+ "astro.config.ts",
76
+ "astro.config.cjs",
77
+ ];
78
+ for (const file of astroConfigCandidates) {
79
+ const filePath = path.join(targetDir, file);
80
+ if (fs.existsSync(filePath)) return filePath;
81
+ }
82
+ return null;
83
+ }
84
+
85
+ function ensureAstroConfigHasAstrotypeImport(configText) {
86
+ const importLine = 'import { astrotypeFonts } from "./src/astrotypes/fonts.mjs";';
87
+ if (configText.includes(importLine)) return configText;
88
+ const defineConfigImportRegex = /import\s*\{[^}]*defineConfig[^}]*\}\s*from\s*["']astro\/config["'];?\n?/;
89
+ const match = configText.match(defineConfigImportRegex);
90
+ if (!match) return configText;
91
+ return configText.replace(match[0], `${match[0]}${importLine}\n`);
92
+ }
93
+
94
+ function ensureAstroConfigHasFontsProperty(configText) {
95
+ if (/\bfonts\s*:/.test(configText)) return { text: configText, injected: false };
96
+ let next = configText.replace(
97
+ /defineConfig\(\s*\{/,
98
+ "defineConfig({\n fonts: astrotypeFonts,"
99
+ );
100
+ next = next.replace(/fonts: astrotypeFonts,\s*\}\);/m, "fonts: astrotypeFonts,\n});");
101
+ return { text: next, injected: next !== configText };
102
+ }
103
+
104
+ export function ensureAstroFontsApiSetup(targetDir, fontEntries) {
105
+ const descriptors = fontEntries.map(toAstroFontDescriptor).filter(Boolean);
106
+ if (descriptors.length === 0) {
107
+ return {
108
+ configured: false,
109
+ reason: "No compatible providers found for Astro Fonts API.",
110
+ };
111
+ }
112
+
113
+ const astrotypeDir = path.join(targetDir, "src", "astrotypes");
114
+ ensureDir(astrotypeDir);
115
+ const statePath = path.join(astrotypeDir, "fonts.state.json");
116
+ const prior = fs.existsSync(statePath)
117
+ ? JSON.parse(fs.readFileSync(statePath, "utf-8"))
118
+ : [];
119
+ const byVariable = new Map();
120
+ for (const entry of prior) {
121
+ if (entry?.cssVariable) byVariable.set(entry.cssVariable, entry);
45
122
  }
46
- return [...new Set(lines)].join("\n");
123
+ for (const descriptor of descriptors) {
124
+ byVariable.set(descriptor.cssVariable, descriptor);
125
+ }
126
+ const merged = [...byVariable.values()].sort((a, b) =>
127
+ a.cssVariable.localeCompare(b.cssVariable)
128
+ );
129
+ fs.writeFileSync(statePath, `${JSON.stringify(merged, null, 2)}\n`);
130
+
131
+ const modulePath = path.join(astrotypeDir, "fonts.mjs");
132
+ const lines = merged.map((entry) => serializeAstroFontDescriptor(entry)).join(",\n");
133
+ const moduleText = `import { fontProviders } from "astro/config";
134
+
135
+ export const astrotypeFonts = [
136
+ ${lines}
137
+ ];
138
+ `;
139
+ fs.writeFileSync(modulePath, moduleText);
140
+
141
+ const astroConfigPath = getAstroConfigPath(targetDir);
142
+ if (!astroConfigPath) {
143
+ return {
144
+ configured: false,
145
+ reason: "No astro.config.* file found.",
146
+ };
147
+ }
148
+
149
+ let configText = fs.readFileSync(astroConfigPath, "utf-8");
150
+ configText = ensureAstroConfigHasAstrotypeImport(configText);
151
+ const result = ensureAstroConfigHasFontsProperty(configText);
152
+ fs.writeFileSync(astroConfigPath, result.text);
153
+
154
+ return {
155
+ configured: true,
156
+ configPath: astroConfigPath,
157
+ manualMergeRequired: !result.injected && /\bfonts\s*:/.test(configText),
158
+ };
47
159
  }
48
160
 
49
161
  export function ensureImport(globalCssPath, importLine) {
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.2",
5
+ "version": "0.1.4",
6
6
  "private": false,
7
7
  "keywords": [
8
8
  "astro",