stera-ui 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-57DNZ2YS.js → chunk-AXIACJ2C.js} +5 -1
- package/dist/{chunk-PUJGGJCB.js → chunk-MFUZBXO6.js} +36 -23
- package/dist/cli.js +523 -55
- package/dist/index.js +2 -2
- package/dist/schema/index.d.ts +36 -18
- package/dist/schema/index.js +1 -1
- package/dist/utils/index.d.ts +7 -1
- package/dist/utils/index.js +1 -1
- package/package.json +4 -1
|
@@ -34,7 +34,11 @@ var configSchema = z.object({
|
|
|
34
34
|
ui: z.string(),
|
|
35
35
|
lib: z.string(),
|
|
36
36
|
hooks: z.string()
|
|
37
|
-
})
|
|
37
|
+
}),
|
|
38
|
+
fonts: z.object({
|
|
39
|
+
strategy: z.enum(["stera-default", "keep-existing", "skip"]),
|
|
40
|
+
nextFont: z.boolean().optional()
|
|
41
|
+
}).optional()
|
|
38
42
|
});
|
|
39
43
|
|
|
40
44
|
export {
|
|
@@ -165,7 +165,22 @@ function detectProject(cwd) {
|
|
|
165
165
|
rsc = fs2.existsSync(path3.join(cwd, appDir));
|
|
166
166
|
}
|
|
167
167
|
const existingCssFile = findTailwindCssFile(cwd);
|
|
168
|
-
|
|
168
|
+
let layoutFile = null;
|
|
169
|
+
if (framework === "next") {
|
|
170
|
+
const appDir = hasSrc ? "src/app" : "app";
|
|
171
|
+
for (const ext of ["tsx", "jsx", "ts", "js"]) {
|
|
172
|
+
const candidate = `${appDir}/layout.${ext}`;
|
|
173
|
+
if (fs2.existsSync(path3.join(cwd, candidate))) {
|
|
174
|
+
layoutFile = candidate;
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
} else if (framework === "vite") {
|
|
179
|
+
if (fs2.existsSync(path3.join(cwd, "index.html"))) {
|
|
180
|
+
layoutFile = "index.html";
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return { hasSrc, framework, rsc, existingCssFile, layoutFile };
|
|
169
184
|
}
|
|
170
185
|
|
|
171
186
|
// src/utils/detect-globals.ts
|
|
@@ -183,7 +198,8 @@ function hasGlobalsCss(config, projectRoot) {
|
|
|
183
198
|
// src/utils/install-deps.ts
|
|
184
199
|
import fs4 from "fs";
|
|
185
200
|
import path5 from "path";
|
|
186
|
-
import {
|
|
201
|
+
import { detect } from "@antfu/ni";
|
|
202
|
+
import { execa } from "execa";
|
|
187
203
|
|
|
188
204
|
// src/utils/format.ts
|
|
189
205
|
var BRAND = "\x1B[38;2;166;244;48m";
|
|
@@ -241,10 +257,16 @@ function createSpinner(text) {
|
|
|
241
257
|
}
|
|
242
258
|
|
|
243
259
|
// src/utils/install-deps.ts
|
|
244
|
-
function detectPackageManager(cwd) {
|
|
245
|
-
|
|
246
|
-
if (
|
|
247
|
-
if (
|
|
260
|
+
async function detectPackageManager(cwd) {
|
|
261
|
+
const detected = await detect({ programmatic: true, cwd });
|
|
262
|
+
if (detected === "pnpm@6" || detected === "pnpm") return "pnpm";
|
|
263
|
+
if (detected === "yarn@berry" || detected === "yarn") return "yarn";
|
|
264
|
+
if (detected === "bun") return "bun";
|
|
265
|
+
if (detected === "npm") return "npm";
|
|
266
|
+
const userAgent = process.env.npm_config_user_agent || "";
|
|
267
|
+
if (userAgent.startsWith("pnpm")) return "pnpm";
|
|
268
|
+
if (userAgent.startsWith("yarn")) return "yarn";
|
|
269
|
+
if (userAgent.startsWith("bun")) return "bun";
|
|
248
270
|
return "npm";
|
|
249
271
|
}
|
|
250
272
|
function getInstallArgs(pm, deps) {
|
|
@@ -272,25 +294,16 @@ async function installDependencies(deps, cwd) {
|
|
|
272
294
|
}
|
|
273
295
|
const toInstall = deps.filter((dep) => !existing.has(dep));
|
|
274
296
|
if (toInstall.length === 0) return;
|
|
275
|
-
const pm = detectPackageManager(cwd);
|
|
297
|
+
const pm = await detectPackageManager(cwd);
|
|
276
298
|
const [cmd, args] = getInstallArgs(pm, toInstall);
|
|
277
299
|
const spinner = createSpinner(`Installing with ${pm}`);
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
spinner.fail(`Install failed (exit ${code})`);
|
|
286
|
-
reject(new Error(`${pm} exited with code ${code}`));
|
|
287
|
-
}
|
|
288
|
-
});
|
|
289
|
-
proc.on("error", (err) => {
|
|
290
|
-
spinner.fail(`Install failed`);
|
|
291
|
-
reject(err);
|
|
292
|
-
});
|
|
293
|
-
});
|
|
300
|
+
try {
|
|
301
|
+
await execa(cmd, args, { cwd });
|
|
302
|
+
spinner.succeed(`Installed with ${pm}`);
|
|
303
|
+
} catch (error) {
|
|
304
|
+
spinner.fail(`Install failed`);
|
|
305
|
+
throw error;
|
|
306
|
+
}
|
|
294
307
|
}
|
|
295
308
|
|
|
296
309
|
// src/utils/write-files.ts
|
package/dist/cli.js
CHANGED
|
@@ -182,7 +182,8 @@ async function writeComponentFiles(items, config, projectRoot, options = {}) {
|
|
|
182
182
|
// src/utils/install-deps.ts
|
|
183
183
|
import fs3 from "fs";
|
|
184
184
|
import path4 from "path";
|
|
185
|
-
import {
|
|
185
|
+
import { detect } from "@antfu/ni";
|
|
186
|
+
import { execa } from "execa";
|
|
186
187
|
|
|
187
188
|
// src/utils/format.ts
|
|
188
189
|
var BRAND = "\x1B[38;2;166;244;48m";
|
|
@@ -240,10 +241,16 @@ function createSpinner(text) {
|
|
|
240
241
|
}
|
|
241
242
|
|
|
242
243
|
// src/utils/install-deps.ts
|
|
243
|
-
function detectPackageManager(cwd) {
|
|
244
|
-
|
|
245
|
-
if (
|
|
246
|
-
if (
|
|
244
|
+
async function detectPackageManager(cwd) {
|
|
245
|
+
const detected = await detect({ programmatic: true, cwd });
|
|
246
|
+
if (detected === "pnpm@6" || detected === "pnpm") return "pnpm";
|
|
247
|
+
if (detected === "yarn@berry" || detected === "yarn") return "yarn";
|
|
248
|
+
if (detected === "bun") return "bun";
|
|
249
|
+
if (detected === "npm") return "npm";
|
|
250
|
+
const userAgent = process.env.npm_config_user_agent || "";
|
|
251
|
+
if (userAgent.startsWith("pnpm")) return "pnpm";
|
|
252
|
+
if (userAgent.startsWith("yarn")) return "yarn";
|
|
253
|
+
if (userAgent.startsWith("bun")) return "bun";
|
|
247
254
|
return "npm";
|
|
248
255
|
}
|
|
249
256
|
function getInstallArgs(pm, deps) {
|
|
@@ -271,25 +278,16 @@ async function installDependencies(deps, cwd) {
|
|
|
271
278
|
}
|
|
272
279
|
const toInstall = deps.filter((dep) => !existing.has(dep));
|
|
273
280
|
if (toInstall.length === 0) return;
|
|
274
|
-
const pm = detectPackageManager(cwd);
|
|
281
|
+
const pm = await detectPackageManager(cwd);
|
|
275
282
|
const [cmd, args] = getInstallArgs(pm, toInstall);
|
|
276
283
|
const spinner = createSpinner(`Installing with ${pm}`);
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
spinner.fail(`Install failed (exit ${code})`);
|
|
285
|
-
reject(new Error(`${pm} exited with code ${code}`));
|
|
286
|
-
}
|
|
287
|
-
});
|
|
288
|
-
proc.on("error", (err) => {
|
|
289
|
-
spinner.fail(`Install failed`);
|
|
290
|
-
reject(err);
|
|
291
|
-
});
|
|
292
|
-
});
|
|
284
|
+
try {
|
|
285
|
+
await execa(cmd, args, { cwd });
|
|
286
|
+
spinner.succeed(`Installed with ${pm}`);
|
|
287
|
+
} catch (error) {
|
|
288
|
+
spinner.fail(`Install failed`);
|
|
289
|
+
throw error;
|
|
290
|
+
}
|
|
293
291
|
}
|
|
294
292
|
|
|
295
293
|
// src/utils/transform.ts
|
|
@@ -446,8 +444,9 @@ async function add(components, options) {
|
|
|
446
444
|
}
|
|
447
445
|
|
|
448
446
|
// src/commands/init.ts
|
|
449
|
-
import
|
|
450
|
-
import
|
|
447
|
+
import fs11 from "fs";
|
|
448
|
+
import path12 from "path";
|
|
449
|
+
import { select, confirm } from "@inquirer/prompts";
|
|
451
450
|
|
|
452
451
|
// src/utils/detect-project.ts
|
|
453
452
|
import fs5 from "fs";
|
|
@@ -488,11 +487,375 @@ function detectProject(cwd) {
|
|
|
488
487
|
rsc = fs5.existsSync(path7.join(cwd, appDir));
|
|
489
488
|
}
|
|
490
489
|
const existingCssFile = findTailwindCssFile(cwd);
|
|
491
|
-
|
|
490
|
+
let layoutFile = null;
|
|
491
|
+
if (framework === "next") {
|
|
492
|
+
const appDir = hasSrc ? "src/app" : "app";
|
|
493
|
+
for (const ext of ["tsx", "jsx", "ts", "js"]) {
|
|
494
|
+
const candidate = `${appDir}/layout.${ext}`;
|
|
495
|
+
if (fs5.existsSync(path7.join(cwd, candidate))) {
|
|
496
|
+
layoutFile = candidate;
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
} else if (framework === "vite") {
|
|
501
|
+
if (fs5.existsSync(path7.join(cwd, "index.html"))) {
|
|
502
|
+
layoutFile = "index.html";
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
return { hasSrc, framework, rsc, existingCssFile, layoutFile };
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// src/utils/detect-fonts.ts
|
|
509
|
+
import fs6 from "fs";
|
|
510
|
+
import path8 from "path";
|
|
511
|
+
function readIfExists(filePath) {
|
|
512
|
+
try {
|
|
513
|
+
return fs6.readFileSync(filePath, "utf-8");
|
|
514
|
+
} catch {
|
|
515
|
+
return null;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
function detectNextFont(layoutContent) {
|
|
519
|
+
const hasNextFont = /import.*from\s+['"]next\/font/.test(layoutContent) || /import.*from\s+['"]@next\/font/.test(layoutContent) || /import.*from\s+['"]geist\/font/.test(layoutContent);
|
|
520
|
+
let nextFontVariable = null;
|
|
521
|
+
const varMatch = layoutContent.match(/variable:\s*['"](--.+?)['"]/);
|
|
522
|
+
if (varMatch) {
|
|
523
|
+
nextFontVariable = varMatch[1];
|
|
524
|
+
}
|
|
525
|
+
return { hasNextFont, nextFontVariable };
|
|
526
|
+
}
|
|
527
|
+
function detectFontFace(cssContent) {
|
|
528
|
+
const hasFontFace = /@font-face\s*\{/.test(cssContent);
|
|
529
|
+
const fontFamilies = [];
|
|
530
|
+
const regex = /font-family:\s*['"]?([^'";}\n]+?)['"]?\s*;/g;
|
|
531
|
+
let match;
|
|
532
|
+
while ((match = regex.exec(cssContent)) !== null) {
|
|
533
|
+
const family = match[1].trim();
|
|
534
|
+
if (family && !fontFamilies.includes(family)) {
|
|
535
|
+
fontFamilies.push(family);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
return { hasFontFace, fontFamilies };
|
|
539
|
+
}
|
|
540
|
+
function detectGoogleFonts(content) {
|
|
541
|
+
return content.includes("fonts.googleapis.com") || content.includes("fonts.gstatic.com");
|
|
542
|
+
}
|
|
543
|
+
function detectFonts(cwd, project) {
|
|
544
|
+
const result = {
|
|
545
|
+
hasNextFont: false,
|
|
546
|
+
hasFontFace: false,
|
|
547
|
+
hasGoogleFontsLink: false,
|
|
548
|
+
fontFamilies: [],
|
|
549
|
+
nextFontVariable: null
|
|
550
|
+
};
|
|
551
|
+
if (project.layoutFile) {
|
|
552
|
+
const layoutContent = readIfExists(path8.join(cwd, project.layoutFile));
|
|
553
|
+
if (layoutContent) {
|
|
554
|
+
const nextFontResult = detectNextFont(layoutContent);
|
|
555
|
+
result.hasNextFont = nextFontResult.hasNextFont;
|
|
556
|
+
result.nextFontVariable = nextFontResult.nextFontVariable;
|
|
557
|
+
if (detectGoogleFonts(layoutContent)) {
|
|
558
|
+
result.hasGoogleFontsLink = true;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
if (project.existingCssFile) {
|
|
563
|
+
const cssContent = readIfExists(path8.join(cwd, project.existingCssFile));
|
|
564
|
+
if (cssContent) {
|
|
565
|
+
const fontFaceResult = detectFontFace(cssContent);
|
|
566
|
+
result.hasFontFace = fontFaceResult.hasFontFace;
|
|
567
|
+
result.fontFamilies.push(...fontFaceResult.fontFamilies);
|
|
568
|
+
if (detectGoogleFonts(cssContent)) {
|
|
569
|
+
result.hasGoogleFontsLink = true;
|
|
570
|
+
}
|
|
571
|
+
const importRegex = /@import\s+['"](.+?)['"]/g;
|
|
572
|
+
let importMatch;
|
|
573
|
+
while ((importMatch = importRegex.exec(cssContent)) !== null) {
|
|
574
|
+
const importPath = importMatch[1];
|
|
575
|
+
const resolvedPath = path8.join(
|
|
576
|
+
cwd,
|
|
577
|
+
path8.dirname(project.existingCssFile),
|
|
578
|
+
importPath
|
|
579
|
+
);
|
|
580
|
+
const importedContent = readIfExists(resolvedPath);
|
|
581
|
+
if (importedContent) {
|
|
582
|
+
const imported = detectFontFace(importedContent);
|
|
583
|
+
if (imported.hasFontFace) result.hasFontFace = true;
|
|
584
|
+
for (const family of imported.fontFamilies) {
|
|
585
|
+
if (!result.fontFamilies.includes(family)) {
|
|
586
|
+
result.fontFamilies.push(family);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
for (const htmlFile of ["index.html", "src/index.html"]) {
|
|
594
|
+
const htmlContent = readIfExists(path8.join(cwd, htmlFile));
|
|
595
|
+
if (htmlContent && detectGoogleFonts(htmlContent)) {
|
|
596
|
+
result.hasGoogleFontsLink = true;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
return result;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// src/utils/patch-css-vars.ts
|
|
603
|
+
import fs7 from "fs";
|
|
604
|
+
function patchCssVariables(filePath, patches) {
|
|
605
|
+
const content = fs7.readFileSync(filePath, "utf-8");
|
|
606
|
+
const lines = content.split("\n");
|
|
607
|
+
const patched = [];
|
|
608
|
+
for (let i = 0; i < lines.length; i++) {
|
|
609
|
+
for (const [prop, newValue] of Object.entries(patches)) {
|
|
610
|
+
const regex = new RegExp(`(\\s*${escapeRegex(prop)}:\\s*).+?(;)`);
|
|
611
|
+
if (regex.test(lines[i])) {
|
|
612
|
+
lines[i] = lines[i].replace(regex, `$1${newValue}$2`);
|
|
613
|
+
if (!patched.includes(prop)) patched.push(prop);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
if (patched.length > 0) {
|
|
618
|
+
fs7.writeFileSync(filePath, lines.join("\n"), "utf-8");
|
|
619
|
+
}
|
|
620
|
+
return patched;
|
|
621
|
+
}
|
|
622
|
+
function removeCssImport(filePath, importPath) {
|
|
623
|
+
const content = fs7.readFileSync(filePath, "utf-8");
|
|
624
|
+
const pattern = new RegExp(
|
|
625
|
+
`@import\\s+['"]${escapeRegex(importPath)}['"]\\s*;?\\n?`
|
|
626
|
+
);
|
|
627
|
+
if (!pattern.test(content)) return false;
|
|
628
|
+
fs7.writeFileSync(filePath, content.replace(pattern, ""), "utf-8");
|
|
629
|
+
return true;
|
|
630
|
+
}
|
|
631
|
+
function escapeRegex(str) {
|
|
632
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// src/utils/setup-fonts-generic.ts
|
|
636
|
+
import fs9 from "fs";
|
|
637
|
+
import path10 from "path";
|
|
638
|
+
|
|
639
|
+
// src/utils/copy-fonts.ts
|
|
640
|
+
import fs8 from "fs";
|
|
641
|
+
import path9 from "path";
|
|
642
|
+
import { createRequire } from "module";
|
|
643
|
+
var FONT_FILES = [
|
|
644
|
+
{
|
|
645
|
+
src: "geist-sans/Geist-Variable.woff2",
|
|
646
|
+
dest: "geist/Geist-Variable.woff2"
|
|
647
|
+
},
|
|
648
|
+
{
|
|
649
|
+
src: "geist-sans/Geist-Italic[wght].woff2",
|
|
650
|
+
dest: "geist/Geist-Italic.woff2"
|
|
651
|
+
},
|
|
652
|
+
{
|
|
653
|
+
src: "geist-mono/GeistMono-Variable.woff2",
|
|
654
|
+
dest: "geist-mono/GeistMono-Variable.woff2"
|
|
655
|
+
},
|
|
656
|
+
{
|
|
657
|
+
src: "geist-mono/GeistMono-Italic[wght].woff2",
|
|
658
|
+
dest: "geist-mono/GeistMono-Italic.woff2"
|
|
659
|
+
}
|
|
660
|
+
];
|
|
661
|
+
function resolveGeistFontsDir(cwd) {
|
|
662
|
+
const require2 = createRequire(path9.join(cwd, "package.json"));
|
|
663
|
+
const geistEntry = require2.resolve("geist/font/sans");
|
|
664
|
+
const distDir = path9.dirname(geistEntry);
|
|
665
|
+
return path9.join(distDir, "fonts");
|
|
666
|
+
}
|
|
667
|
+
function copyFonts(cwd) {
|
|
668
|
+
const spinner = createSpinner("Copying font files");
|
|
669
|
+
const fontsDir = resolveGeistFontsDir(cwd);
|
|
670
|
+
const publicFontsDir = path9.join(cwd, "public", "fonts");
|
|
671
|
+
const written = [];
|
|
672
|
+
for (const file of FONT_FILES) {
|
|
673
|
+
const srcPath = path9.join(fontsDir, file.src);
|
|
674
|
+
const destPath = path9.join(publicFontsDir, file.dest);
|
|
675
|
+
if (fs8.existsSync(destPath)) continue;
|
|
676
|
+
if (!fs8.existsSync(srcPath)) {
|
|
677
|
+
spinner.fail(`Font file not found: ${file.src}`);
|
|
678
|
+
throw new Error(
|
|
679
|
+
`Could not find ${srcPath}. Is the geist package installed?`
|
|
680
|
+
);
|
|
681
|
+
}
|
|
682
|
+
fs8.mkdirSync(path9.dirname(destPath), { recursive: true });
|
|
683
|
+
fs8.copyFileSync(srcPath, destPath);
|
|
684
|
+
written.push(path9.relative(cwd, destPath));
|
|
685
|
+
}
|
|
686
|
+
if (written.length > 0) {
|
|
687
|
+
spinner.succeed(`Copied ${written.length} font files to public/fonts/`);
|
|
688
|
+
} else {
|
|
689
|
+
spinner.succeed("Font files already exist");
|
|
690
|
+
}
|
|
691
|
+
return written;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// src/utils/setup-fonts-generic.ts
|
|
695
|
+
var FONTS_CSS_CONTENT = `/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
696
|
+
Stera UI font registry
|
|
697
|
+
To add a font:
|
|
698
|
+
1. Drop .woff2 file(s) into public/fonts/{family-name}/
|
|
699
|
+
2. Add an @font-face block below
|
|
700
|
+
3. Add a --font-* variable in globals.css
|
|
701
|
+
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
702
|
+
|
|
703
|
+
/* \u2500\u2500\u2500 Geist Sans \u2500\u2500\u2500 */
|
|
704
|
+
@font-face {
|
|
705
|
+
font-family: 'Geist';
|
|
706
|
+
src: url('/fonts/geist/Geist-Variable.woff2') format('woff2');
|
|
707
|
+
font-weight: 100 900;
|
|
708
|
+
font-style: normal;
|
|
709
|
+
font-display: swap;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
@font-face {
|
|
713
|
+
font-family: 'Geist';
|
|
714
|
+
src: url('/fonts/geist/Geist-Italic.woff2') format('woff2');
|
|
715
|
+
font-weight: 100 900;
|
|
716
|
+
font-style: italic;
|
|
717
|
+
font-display: swap;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
/* \u2500\u2500\u2500 Geist Mono \u2500\u2500\u2500 */
|
|
721
|
+
@font-face {
|
|
722
|
+
font-family: 'Geist Mono';
|
|
723
|
+
src: url('/fonts/geist-mono/GeistMono-Variable.woff2') format('woff2');
|
|
724
|
+
font-weight: 100 900;
|
|
725
|
+
font-style: normal;
|
|
726
|
+
font-display: swap;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
@font-face {
|
|
730
|
+
font-family: 'Geist Mono';
|
|
731
|
+
src: url('/fonts/geist-mono/GeistMono-Italic.woff2') format('woff2');
|
|
732
|
+
font-weight: 100 900;
|
|
733
|
+
font-style: italic;
|
|
734
|
+
font-display: swap;
|
|
735
|
+
}
|
|
736
|
+
`;
|
|
737
|
+
var PRELOAD_TAGS = `<link rel="preload" href="/fonts/geist/Geist-Variable.woff2" as="font" type="font/woff2" crossorigin />
|
|
738
|
+
<link rel="preload" href="/fonts/geist-mono/GeistMono-Variable.woff2" as="font" type="font/woff2" crossorigin />`;
|
|
739
|
+
function setupFontsGeneric(cwd, cssDir) {
|
|
740
|
+
copyFonts(cwd);
|
|
741
|
+
const fontsPath = path10.join(cssDir, "fonts.css");
|
|
742
|
+
if (!fs9.existsSync(fontsPath)) {
|
|
743
|
+
fs9.mkdirSync(path10.dirname(fontsPath), { recursive: true });
|
|
744
|
+
fs9.writeFileSync(fontsPath, FONTS_CSS_CONTENT, "utf-8");
|
|
745
|
+
console.log(` ${CHECK} Created ${path10.relative(cwd, fontsPath)}`);
|
|
746
|
+
} else {
|
|
747
|
+
console.log(` ${dim(path10.relative(cwd, fontsPath) + " (already exists, skipped)")}`);
|
|
748
|
+
}
|
|
749
|
+
return { preloadTags: PRELOAD_TAGS };
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
// src/utils/setup-fonts-nextjs.ts
|
|
753
|
+
import fs10 from "fs";
|
|
754
|
+
import path11 from "path";
|
|
755
|
+
var GEIST_IMPORTS = `import { GeistSans } from "geist/font/sans"
|
|
756
|
+
import { GeistMono } from "geist/font/mono"`;
|
|
757
|
+
function canPatchLayout(content) {
|
|
758
|
+
if (!/<html[\s>]/.test(content)) return false;
|
|
759
|
+
if ((content.match(/<html[\s>]/g) || []).length > 1) return false;
|
|
760
|
+
return true;
|
|
761
|
+
}
|
|
762
|
+
function insertImports(content, imports) {
|
|
763
|
+
const lines = content.split("\n");
|
|
764
|
+
let lastImportIndex = -1;
|
|
765
|
+
for (let i = 0; i < lines.length; i++) {
|
|
766
|
+
const trimmed = lines[i].trim();
|
|
767
|
+
if (trimmed.startsWith("import ") || trimmed.startsWith("import{") || trimmed.startsWith("import ")) {
|
|
768
|
+
lastImportIndex = i;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
if (lastImportIndex >= 0) {
|
|
772
|
+
lines.splice(lastImportIndex + 1, 0, imports);
|
|
773
|
+
} else {
|
|
774
|
+
lines.unshift(imports);
|
|
775
|
+
}
|
|
776
|
+
return lines.join("\n");
|
|
777
|
+
}
|
|
778
|
+
function addClassNameToHtml(content) {
|
|
779
|
+
const fontClasses = "${GeistSans.variable} ${GeistMono.variable}";
|
|
780
|
+
const templateMatch = content.match(
|
|
781
|
+
/(<html[^>]*className=\{`)(.*?)(`\})/s
|
|
782
|
+
);
|
|
783
|
+
if (templateMatch) {
|
|
784
|
+
const existing = templateMatch[2].trim();
|
|
785
|
+
const newClassName = existing ? `${existing} ${fontClasses}` : fontClasses;
|
|
786
|
+
return content.replace(
|
|
787
|
+
templateMatch[0],
|
|
788
|
+
`${templateMatch[1]}${newClassName}${templateMatch[3]}`
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
const exprMatch = content.match(
|
|
792
|
+
/(<html[^>]*className=\{)([^}`]+)(\})/
|
|
793
|
+
);
|
|
794
|
+
if (exprMatch) {
|
|
795
|
+
return content.replace(
|
|
796
|
+
exprMatch[0],
|
|
797
|
+
`${exprMatch[1]}\`\${${exprMatch[2].trim()}} ${fontClasses}\`${exprMatch[3]}`
|
|
798
|
+
);
|
|
799
|
+
}
|
|
800
|
+
const stringMatch = content.match(
|
|
801
|
+
/(<html[^>]*className=)(["'])([^"']*)(["'])/
|
|
802
|
+
);
|
|
803
|
+
if (stringMatch) {
|
|
804
|
+
const existing = stringMatch[3].trim();
|
|
805
|
+
const newClassName = existing ? `${existing} ${fontClasses}` : fontClasses;
|
|
806
|
+
return content.replace(
|
|
807
|
+
stringMatch[0],
|
|
808
|
+
`${stringMatch[1]}{\`${newClassName}\`}`
|
|
809
|
+
);
|
|
810
|
+
}
|
|
811
|
+
return content.replace(
|
|
812
|
+
/<html(\s)/,
|
|
813
|
+
`<html className={\`${fontClasses}\`}$1`
|
|
814
|
+
);
|
|
815
|
+
}
|
|
816
|
+
function generateSnippet() {
|
|
817
|
+
return `
|
|
818
|
+
Add these imports to your root layout file:
|
|
819
|
+
|
|
820
|
+
${dim(GEIST_IMPORTS)}
|
|
821
|
+
|
|
822
|
+
Then add the font variables to your <html> tag:
|
|
823
|
+
|
|
824
|
+
${dim("<html className={`${GeistSans.variable} ${GeistMono.variable}`}>")}
|
|
825
|
+
`;
|
|
826
|
+
}
|
|
827
|
+
function setupFontsNextjs(cwd, layoutFile, cssFile) {
|
|
828
|
+
const layoutPath = path11.join(cwd, layoutFile);
|
|
829
|
+
const cssPath = path11.join(cwd, cssFile);
|
|
830
|
+
let layoutContent = fs10.readFileSync(layoutPath, "utf-8");
|
|
831
|
+
if (layoutContent.includes("geist/font/sans") || layoutContent.includes("GeistSans")) {
|
|
832
|
+
console.log(` ${dim("geist/font already configured in " + layoutFile)}`);
|
|
833
|
+
return true;
|
|
834
|
+
}
|
|
835
|
+
if (!canPatchLayout(layoutContent)) {
|
|
836
|
+
console.log(` ${WARN} Could not auto-patch ${layoutFile}`);
|
|
837
|
+
console.log(generateSnippet());
|
|
838
|
+
return false;
|
|
839
|
+
}
|
|
840
|
+
layoutContent = insertImports(layoutContent, GEIST_IMPORTS);
|
|
841
|
+
layoutContent = addClassNameToHtml(layoutContent);
|
|
842
|
+
fs10.writeFileSync(layoutPath, layoutContent, "utf-8");
|
|
843
|
+
console.log(` ${CHECK} Updated ${layoutFile} with geist/font imports`);
|
|
844
|
+
if (fs10.existsSync(cssPath)) {
|
|
845
|
+
const patched = patchCssVariables(cssPath, {
|
|
846
|
+
"--font-display": "var(--font-geist-sans)",
|
|
847
|
+
"--font-body": "var(--font-geist-sans)"
|
|
848
|
+
});
|
|
849
|
+
if (patched.length > 0) {
|
|
850
|
+
console.log(` ${CHECK} Updated font variables in ${path11.relative(cwd, cssPath)}`);
|
|
851
|
+
}
|
|
852
|
+
removeCssImport(cssPath, "./fonts.css");
|
|
853
|
+
}
|
|
854
|
+
return true;
|
|
492
855
|
}
|
|
493
856
|
|
|
494
857
|
// src/commands/init.ts
|
|
495
|
-
function buildConfig(project) {
|
|
858
|
+
function buildConfig(project, fontChoice) {
|
|
496
859
|
let cssPath;
|
|
497
860
|
if (project.existingCssFile) {
|
|
498
861
|
cssPath = project.existingCssFile;
|
|
@@ -511,6 +874,10 @@ function buildConfig(project) {
|
|
|
511
874
|
ui: "@/components/ui",
|
|
512
875
|
lib: "@/lib",
|
|
513
876
|
hooks: "@/hooks"
|
|
877
|
+
},
|
|
878
|
+
fonts: {
|
|
879
|
+
strategy: fontChoice.strategy,
|
|
880
|
+
...fontChoice.useNextFont ? { nextFont: true } : {}
|
|
514
881
|
}
|
|
515
882
|
};
|
|
516
883
|
}
|
|
@@ -518,7 +885,7 @@ function stripTailwindCoreImport(content) {
|
|
|
518
885
|
return content.split("\n").filter((line) => !line.trim().match(/^@import\s+["']tailwindcss["']/)).join("\n");
|
|
519
886
|
}
|
|
520
887
|
function insertImportLine(filePath, importStatement) {
|
|
521
|
-
const content =
|
|
888
|
+
const content = fs11.readFileSync(filePath, "utf-8");
|
|
522
889
|
const lines = content.split("\n");
|
|
523
890
|
let lastImportIndex = -1;
|
|
524
891
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -531,10 +898,100 @@ function insertImportLine(filePath, importStatement) {
|
|
|
531
898
|
} else {
|
|
532
899
|
lines.unshift(importStatement);
|
|
533
900
|
}
|
|
534
|
-
|
|
901
|
+
fs11.writeFileSync(filePath, lines.join("\n"), "utf-8");
|
|
902
|
+
}
|
|
903
|
+
async function promptFontChoice(project, detected, skipPrompts) {
|
|
904
|
+
const hasExistingFonts = detected.hasNextFont || detected.hasFontFace || detected.hasGoogleFontsLink;
|
|
905
|
+
const defaultStrategy = hasExistingFonts ? "keep-existing" : "stera-default";
|
|
906
|
+
if (skipPrompts) {
|
|
907
|
+
return {
|
|
908
|
+
strategy: defaultStrategy,
|
|
909
|
+
useNextFont: project.framework === "next" && defaultStrategy === "stera-default"
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
if (hasExistingFonts) {
|
|
913
|
+
const sources = [];
|
|
914
|
+
if (detected.fontFamilies.length > 0) {
|
|
915
|
+
sources.push(detected.fontFamilies.join(", "));
|
|
916
|
+
}
|
|
917
|
+
if (detected.hasNextFont) sources.push("next/font");
|
|
918
|
+
if (detected.hasGoogleFontsLink) sources.push("Google Fonts");
|
|
919
|
+
console.log(` Detected existing fonts: ${sources.join(" via ")}
|
|
920
|
+
`);
|
|
921
|
+
}
|
|
922
|
+
const choices = [];
|
|
923
|
+
if (hasExistingFonts) {
|
|
924
|
+
choices.push({
|
|
925
|
+
value: "keep-existing",
|
|
926
|
+
name: "Keep my current fonts",
|
|
927
|
+
description: "Update CSS variables to use your existing fonts"
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
choices.push({
|
|
931
|
+
value: "stera-default",
|
|
932
|
+
name: "Use Stera UI default fonts (Geist Sans + Geist Mono)",
|
|
933
|
+
description: "Self-hosted, optimized variable fonts"
|
|
934
|
+
});
|
|
935
|
+
choices.push({
|
|
936
|
+
value: "skip",
|
|
937
|
+
name: "Skip font setup",
|
|
938
|
+
description: "Configure fonts manually later"
|
|
939
|
+
});
|
|
940
|
+
const strategy = await select({
|
|
941
|
+
message: "How would you like to set up fonts?",
|
|
942
|
+
choices,
|
|
943
|
+
default: defaultStrategy
|
|
944
|
+
});
|
|
945
|
+
let useNextFont = false;
|
|
946
|
+
if (project.framework === "next" && strategy === "stera-default") {
|
|
947
|
+
useNextFont = await confirm({
|
|
948
|
+
message: "Use next/font for automatic optimization? (recommended)",
|
|
949
|
+
default: true
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
console.log("");
|
|
953
|
+
return { strategy, useNextFont };
|
|
954
|
+
}
|
|
955
|
+
async function executeFontStrategy(fontChoice, project, detected, config, cwd) {
|
|
956
|
+
const cssDir = path12.dirname(path12.resolve(cwd, config.css));
|
|
957
|
+
const cssPath = path12.resolve(cwd, config.css);
|
|
958
|
+
if (fontChoice.strategy === "skip") {
|
|
959
|
+
return {};
|
|
960
|
+
}
|
|
961
|
+
if (fontChoice.strategy === "keep-existing") {
|
|
962
|
+
if (fs11.existsSync(cssPath)) {
|
|
963
|
+
const fontFamily = detected.fontFamilies.length > 0 ? detected.fontFamilies[0] : null;
|
|
964
|
+
const fontValue = detected.nextFontVariable ? `var(${detected.nextFontVariable})` : fontFamily ?? "Geist";
|
|
965
|
+
const patched = patchCssVariables(cssPath, {
|
|
966
|
+
"--font-display": fontValue,
|
|
967
|
+
"--font-body": fontValue
|
|
968
|
+
});
|
|
969
|
+
const steraUiPath = path12.join(cssDir, "stera-ui.css");
|
|
970
|
+
if (fs11.existsSync(steraUiPath)) {
|
|
971
|
+
patchCssVariables(steraUiPath, {
|
|
972
|
+
"--font-display": fontValue,
|
|
973
|
+
"--font-body": fontValue
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
if (patched.length > 0) {
|
|
977
|
+
console.log(` ${CHECK} Updated font variables to use ${fontValue}`);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
return {};
|
|
981
|
+
}
|
|
982
|
+
if (fontChoice.useNextFont && project.layoutFile) {
|
|
983
|
+
await installDependencies(["geist"], cwd);
|
|
984
|
+
const targetCss = fs11.existsSync(path12.join(cssDir, "stera-ui.css")) ? path12.join(cssDir, "stera-ui.css") : cssPath;
|
|
985
|
+
setupFontsNextjs(cwd, project.layoutFile, targetCss);
|
|
986
|
+
return {};
|
|
987
|
+
} else {
|
|
988
|
+
await installDependencies(["geist"], cwd);
|
|
989
|
+
const result = setupFontsGeneric(cwd, cssDir);
|
|
990
|
+
return { preloadTags: result.preloadTags };
|
|
991
|
+
}
|
|
535
992
|
}
|
|
536
993
|
async function init(options) {
|
|
537
|
-
const cwd = options.cwd ?
|
|
994
|
+
const cwd = options.cwd ? path12.resolve(options.cwd) : process.cwd();
|
|
538
995
|
console.log(`
|
|
539
996
|
${LOGO} Stera UI
|
|
540
997
|
`);
|
|
@@ -544,8 +1001,8 @@ async function init(options) {
|
|
|
544
1001
|
console.log(' Run "stera-ui add <component>" to add components.\n');
|
|
545
1002
|
return;
|
|
546
1003
|
}
|
|
547
|
-
const pkgPath =
|
|
548
|
-
if (!
|
|
1004
|
+
const pkgPath = path12.join(cwd, "package.json");
|
|
1005
|
+
if (!fs11.existsSync(pkgPath)) {
|
|
549
1006
|
console.error(
|
|
550
1007
|
" Error: No package.json found. Please run this in a project directory."
|
|
551
1008
|
);
|
|
@@ -560,47 +1017,41 @@ async function init(options) {
|
|
|
560
1017
|
console.log(` Detected ${parts.join(" project with ")} project
|
|
561
1018
|
`);
|
|
562
1019
|
}
|
|
563
|
-
const
|
|
564
|
-
const
|
|
565
|
-
|
|
1020
|
+
const detectedFonts = detectFonts(cwd, project);
|
|
1021
|
+
const fontChoice = await promptFontChoice(
|
|
1022
|
+
project,
|
|
1023
|
+
detectedFonts,
|
|
1024
|
+
options.yes ?? false
|
|
1025
|
+
);
|
|
1026
|
+
const config = buildConfig(project, fontChoice);
|
|
1027
|
+
const configPath = path12.join(cwd, CONFIG_FILE);
|
|
1028
|
+
fs11.writeFileSync(
|
|
566
1029
|
configPath,
|
|
567
1030
|
JSON.stringify(config, null, 2) + "\n",
|
|
568
1031
|
"utf-8"
|
|
569
1032
|
);
|
|
570
1033
|
console.log(` ${CHECK} Created components.json`);
|
|
571
|
-
const cssDir =
|
|
1034
|
+
const cssDir = path12.dirname(path12.resolve(cwd, config.css));
|
|
572
1035
|
if (project.existingCssFile) {
|
|
573
1036
|
if (hasGlobalsCss(config, cwd)) {
|
|
574
1037
|
console.log(
|
|
575
|
-
` ${CHECK} Stera already initialised ${dim(`(${
|
|
1038
|
+
` ${CHECK} Stera already initialised ${dim(`(${path12.join(path12.dirname(project.existingCssFile), "stera-ui.css")} exists)`)}`
|
|
576
1039
|
);
|
|
577
1040
|
} else {
|
|
578
1041
|
try {
|
|
579
1042
|
const globalsItem = await fetchRegistryItem("globals");
|
|
580
1043
|
const globalsContent = globalsItem.files[0].content ?? "";
|
|
581
1044
|
const steraUiContent = stripTailwindCoreImport(globalsContent);
|
|
582
|
-
const steraUiPath =
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
console.log(` ${CHECK} Created ${
|
|
586
|
-
const existingCssPath =
|
|
1045
|
+
const steraUiPath = path12.join(cssDir, "stera-ui.css");
|
|
1046
|
+
fs11.mkdirSync(cssDir, { recursive: true });
|
|
1047
|
+
fs11.writeFileSync(steraUiPath, steraUiContent, "utf-8");
|
|
1048
|
+
console.log(` ${CHECK} Created ${path12.relative(cwd, steraUiPath)}`);
|
|
1049
|
+
const existingCssPath = path12.resolve(cwd, project.existingCssFile);
|
|
587
1050
|
insertImportLine(existingCssPath, "@import './stera-ui.css';");
|
|
588
1051
|
console.log(` ${CHECK} Added @import to ${project.existingCssFile}`);
|
|
589
1052
|
} catch {
|
|
590
1053
|
}
|
|
591
1054
|
}
|
|
592
|
-
try {
|
|
593
|
-
const fontsItem = await fetchRegistryItem("fonts");
|
|
594
|
-
const fontsPath = path8.join(cssDir, "fonts.css");
|
|
595
|
-
fs6.mkdirSync(path8.dirname(fontsPath), { recursive: true });
|
|
596
|
-
if (!fs6.existsSync(fontsPath)) {
|
|
597
|
-
fs6.writeFileSync(fontsPath, fontsItem.files[0].content ?? "", "utf-8");
|
|
598
|
-
console.log(` ${CHECK} Created ${path8.relative(cwd, fontsPath)}`);
|
|
599
|
-
} else {
|
|
600
|
-
console.log(` ${dim(path8.relative(cwd, fontsPath) + " (already exists, skipped)")}`);
|
|
601
|
-
}
|
|
602
|
-
} catch {
|
|
603
|
-
}
|
|
604
1055
|
} else {
|
|
605
1056
|
const resolved = await resolveDependencies(["globals"]);
|
|
606
1057
|
const { written } = await writeComponentFiles(resolved, config, cwd);
|
|
@@ -608,6 +1059,13 @@ async function init(options) {
|
|
|
608
1059
|
console.log(` ${CHECK} Created ${file}`);
|
|
609
1060
|
}
|
|
610
1061
|
}
|
|
1062
|
+
const fontResult = await executeFontStrategy(
|
|
1063
|
+
fontChoice,
|
|
1064
|
+
project,
|
|
1065
|
+
detectedFonts,
|
|
1066
|
+
config,
|
|
1067
|
+
cwd
|
|
1068
|
+
);
|
|
611
1069
|
try {
|
|
612
1070
|
const globalsItem = await fetchRegistryItem("globals");
|
|
613
1071
|
if (globalsItem.dependencies) {
|
|
@@ -620,8 +1078,18 @@ async function init(options) {
|
|
|
620
1078
|
console.log("");
|
|
621
1079
|
console.log(" Next steps:");
|
|
622
1080
|
console.log(" stera-ui add <component>");
|
|
623
|
-
|
|
624
|
-
|
|
1081
|
+
if (fontResult.preloadTags) {
|
|
1082
|
+
console.log("");
|
|
1083
|
+
console.log(` ${dim("For optimal font loading, add these to your HTML <head>:")}`);
|
|
1084
|
+
console.log("");
|
|
1085
|
+
for (const line of fontResult.preloadTags.split("\n")) {
|
|
1086
|
+
console.log(` ${dim(line)}`);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
if (fontChoice.strategy === "skip") {
|
|
1090
|
+
console.log("");
|
|
1091
|
+
console.log(` ${dim("Font setup was skipped. See https://ui.stera.sh for font configuration docs.")}`);
|
|
1092
|
+
}
|
|
625
1093
|
console.log("");
|
|
626
1094
|
}
|
|
627
1095
|
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
registryItemFileSchema,
|
|
12
12
|
registryItemSchema,
|
|
13
13
|
registryItemTypeSchema
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-AXIACJ2C.js";
|
|
15
15
|
import {
|
|
16
16
|
CHECK,
|
|
17
17
|
CONFIG_FILE,
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
resolveOutputPath,
|
|
32
32
|
transformImports,
|
|
33
33
|
writeComponentFiles
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-MFUZBXO6.js";
|
|
35
35
|
export {
|
|
36
36
|
CHECK,
|
|
37
37
|
CONFIG_FILE,
|
package/dist/schema/index.d.ts
CHANGED
|
@@ -8,13 +8,13 @@ declare const registryItemFileSchema: z.ZodObject<{
|
|
|
8
8
|
content: z.ZodOptional<z.ZodString>;
|
|
9
9
|
target: z.ZodOptional<z.ZodString>;
|
|
10
10
|
}, "strip", z.ZodTypeAny, {
|
|
11
|
-
path: string;
|
|
12
11
|
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
12
|
+
path: string;
|
|
13
13
|
content?: string | undefined;
|
|
14
14
|
target?: string | undefined;
|
|
15
15
|
}, {
|
|
16
|
-
path: string;
|
|
17
16
|
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
17
|
+
path: string;
|
|
18
18
|
content?: string | undefined;
|
|
19
19
|
target?: string | undefined;
|
|
20
20
|
}>;
|
|
@@ -30,33 +30,33 @@ declare const registryItemSchema: z.ZodObject<{
|
|
|
30
30
|
content: z.ZodOptional<z.ZodString>;
|
|
31
31
|
target: z.ZodOptional<z.ZodString>;
|
|
32
32
|
}, "strip", z.ZodTypeAny, {
|
|
33
|
-
path: string;
|
|
34
33
|
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
34
|
+
path: string;
|
|
35
35
|
content?: string | undefined;
|
|
36
36
|
target?: string | undefined;
|
|
37
37
|
}, {
|
|
38
|
-
path: string;
|
|
39
38
|
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
39
|
+
path: string;
|
|
40
40
|
content?: string | undefined;
|
|
41
41
|
target?: string | undefined;
|
|
42
42
|
}>, "many">;
|
|
43
43
|
}, "strip", z.ZodTypeAny, {
|
|
44
|
-
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
45
44
|
name: string;
|
|
45
|
+
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
46
46
|
files: {
|
|
47
|
-
path: string;
|
|
48
47
|
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
48
|
+
path: string;
|
|
49
49
|
content?: string | undefined;
|
|
50
50
|
target?: string | undefined;
|
|
51
51
|
}[];
|
|
52
52
|
dependencies?: string[] | undefined;
|
|
53
53
|
registryDependencies?: string[] | undefined;
|
|
54
54
|
}, {
|
|
55
|
-
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
56
55
|
name: string;
|
|
56
|
+
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
57
57
|
files: {
|
|
58
|
-
path: string;
|
|
59
58
|
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
59
|
+
path: string;
|
|
60
60
|
content?: string | undefined;
|
|
61
61
|
target?: string | undefined;
|
|
62
62
|
}[];
|
|
@@ -77,33 +77,33 @@ declare const registryIndexSchema: z.ZodObject<{
|
|
|
77
77
|
content: z.ZodOptional<z.ZodString>;
|
|
78
78
|
target: z.ZodOptional<z.ZodString>;
|
|
79
79
|
}, "strip", z.ZodTypeAny, {
|
|
80
|
-
path: string;
|
|
81
80
|
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
81
|
+
path: string;
|
|
82
82
|
content?: string | undefined;
|
|
83
83
|
target?: string | undefined;
|
|
84
84
|
}, {
|
|
85
|
-
path: string;
|
|
86
85
|
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
86
|
+
path: string;
|
|
87
87
|
content?: string | undefined;
|
|
88
88
|
target?: string | undefined;
|
|
89
89
|
}>, "many">;
|
|
90
90
|
}, "strip", z.ZodTypeAny, {
|
|
91
|
-
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
92
91
|
name: string;
|
|
92
|
+
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
93
93
|
files: {
|
|
94
|
-
path: string;
|
|
95
94
|
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
95
|
+
path: string;
|
|
96
96
|
content?: string | undefined;
|
|
97
97
|
target?: string | undefined;
|
|
98
98
|
}[];
|
|
99
99
|
dependencies?: string[] | undefined;
|
|
100
100
|
registryDependencies?: string[] | undefined;
|
|
101
101
|
}, {
|
|
102
|
-
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
103
102
|
name: string;
|
|
103
|
+
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
104
104
|
files: {
|
|
105
|
-
path: string;
|
|
106
105
|
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
106
|
+
path: string;
|
|
107
107
|
content?: string | undefined;
|
|
108
108
|
target?: string | undefined;
|
|
109
109
|
}[];
|
|
@@ -113,11 +113,11 @@ declare const registryIndexSchema: z.ZodObject<{
|
|
|
113
113
|
}, "strip", z.ZodTypeAny, {
|
|
114
114
|
name: string;
|
|
115
115
|
items: {
|
|
116
|
-
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
117
116
|
name: string;
|
|
117
|
+
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
118
118
|
files: {
|
|
119
|
-
path: string;
|
|
120
119
|
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
120
|
+
path: string;
|
|
121
121
|
content?: string | undefined;
|
|
122
122
|
target?: string | undefined;
|
|
123
123
|
}[];
|
|
@@ -127,11 +127,11 @@ declare const registryIndexSchema: z.ZodObject<{
|
|
|
127
127
|
}, {
|
|
128
128
|
name: string;
|
|
129
129
|
items: {
|
|
130
|
-
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
131
130
|
name: string;
|
|
131
|
+
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
132
132
|
files: {
|
|
133
|
-
path: string;
|
|
134
133
|
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
134
|
+
path: string;
|
|
135
135
|
content?: string | undefined;
|
|
136
136
|
target?: string | undefined;
|
|
137
137
|
}[];
|
|
@@ -163,6 +163,16 @@ declare const configSchema: z.ZodObject<{
|
|
|
163
163
|
lib: string;
|
|
164
164
|
hooks: string;
|
|
165
165
|
}>;
|
|
166
|
+
fonts: z.ZodOptional<z.ZodObject<{
|
|
167
|
+
strategy: z.ZodEnum<["stera-default", "keep-existing", "skip"]>;
|
|
168
|
+
nextFont: z.ZodOptional<z.ZodBoolean>;
|
|
169
|
+
}, "strip", z.ZodTypeAny, {
|
|
170
|
+
strategy: "stera-default" | "keep-existing" | "skip";
|
|
171
|
+
nextFont?: boolean | undefined;
|
|
172
|
+
}, {
|
|
173
|
+
strategy: "stera-default" | "keep-existing" | "skip";
|
|
174
|
+
nextFont?: boolean | undefined;
|
|
175
|
+
}>>;
|
|
166
176
|
}, "strip", z.ZodTypeAny, {
|
|
167
177
|
version: 1;
|
|
168
178
|
css: string;
|
|
@@ -174,6 +184,10 @@ declare const configSchema: z.ZodObject<{
|
|
|
174
184
|
hooks: string;
|
|
175
185
|
};
|
|
176
186
|
$schema?: string | undefined;
|
|
187
|
+
fonts?: {
|
|
188
|
+
strategy: "stera-default" | "keep-existing" | "skip";
|
|
189
|
+
nextFont?: boolean | undefined;
|
|
190
|
+
} | undefined;
|
|
177
191
|
}, {
|
|
178
192
|
version: 1;
|
|
179
193
|
css: string;
|
|
@@ -185,6 +199,10 @@ declare const configSchema: z.ZodObject<{
|
|
|
185
199
|
hooks: string;
|
|
186
200
|
};
|
|
187
201
|
$schema?: string | undefined;
|
|
202
|
+
fonts?: {
|
|
203
|
+
strategy: "stera-default" | "keep-existing" | "skip";
|
|
204
|
+
nextFont?: boolean | undefined;
|
|
205
|
+
} | undefined;
|
|
188
206
|
}>;
|
|
189
207
|
type SteraConfig = z.infer<typeof configSchema>;
|
|
190
208
|
|
package/dist/schema/index.js
CHANGED
package/dist/utils/index.d.ts
CHANGED
|
@@ -12,6 +12,10 @@ interface SteraConfig {
|
|
|
12
12
|
lib: string;
|
|
13
13
|
hooks: string;
|
|
14
14
|
};
|
|
15
|
+
fonts?: {
|
|
16
|
+
strategy: "stera-default" | "keep-existing" | "skip";
|
|
17
|
+
nextFont?: boolean;
|
|
18
|
+
};
|
|
15
19
|
}
|
|
16
20
|
declare const CONFIG_FILE = "components.json";
|
|
17
21
|
declare function findConfigPath(cwd: string): string | null;
|
|
@@ -50,6 +54,8 @@ interface ProjectInfo {
|
|
|
50
54
|
framework: "next" | "vite" | "other";
|
|
51
55
|
rsc: boolean;
|
|
52
56
|
existingCssFile: string | null;
|
|
57
|
+
/** Path to the root layout file (layout.tsx for Next.js, index.html for Vite) */
|
|
58
|
+
layoutFile: string | null;
|
|
53
59
|
}
|
|
54
60
|
/**
|
|
55
61
|
* Detect the project structure to inform configuration defaults.
|
|
@@ -69,7 +75,7 @@ declare function detectProject(cwd: string): ProjectInfo;
|
|
|
69
75
|
declare function hasGlobalsCss(config: SteraConfig, projectRoot: string): boolean;
|
|
70
76
|
|
|
71
77
|
type PackageManager = "pnpm" | "npm" | "yarn" | "bun";
|
|
72
|
-
declare function detectPackageManager(cwd: string): PackageManager
|
|
78
|
+
declare function detectPackageManager(cwd: string): Promise<PackageManager>;
|
|
73
79
|
/**
|
|
74
80
|
* Install npm dependencies, skipping any already in the consumer's package.json.
|
|
75
81
|
*/
|
package/dist/utils/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stera-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Add Stera UI components to your project.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,7 +33,10 @@
|
|
|
33
33
|
"dist"
|
|
34
34
|
],
|
|
35
35
|
"dependencies": {
|
|
36
|
+
"@antfu/ni": "^30.0.0",
|
|
37
|
+
"@inquirer/prompts": "^7.0.0",
|
|
36
38
|
"commander": "^13.1.0",
|
|
39
|
+
"execa": "^9.6.1",
|
|
37
40
|
"tsconfig-paths": "^4.2.0",
|
|
38
41
|
"zod": "^3.24.0"
|
|
39
42
|
},
|