stera-ui 0.3.3 → 0.4.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.
@@ -1,3 +1,7 @@
1
+ import {
2
+ fetchRegistryItem
3
+ } from "./chunk-T3QWOVJR.js";
4
+
1
5
  // src/utils/transform.ts
2
6
  function applyTransforms(content, ctx, transforms) {
3
7
  return transforms.reduce((acc, fn) => fn(acc, ctx), content);
@@ -210,28 +214,65 @@ function detectAliasPrefix(cwd) {
210
214
  return first ? first.replace(/\/\*$/, "") : null;
211
215
  }
212
216
 
213
- // src/utils/detect-globals.ts
214
- import fs3 from "fs";
217
+ // src/utils/write-stera-css.ts
218
+ import fs3 from "fs/promises";
215
219
  import path4 from "path";
216
- function hasGlobalsCss(config, projectRoot) {
217
- const cssPath = path4.resolve(projectRoot, config.css);
218
- if (!fs3.existsSync(cssPath)) {
219
- return false;
220
+ function stripImportLines(content) {
221
+ const lines = content.split("\n");
222
+ const imports = [];
223
+ const kept = [];
224
+ for (const line of lines) {
225
+ if (line.trim().match(/^@import\s+["'].+["']/)) {
226
+ if (!line.trim().match(/^@import\s+["']tailwindcss["']/)) {
227
+ imports.push(line.trim());
228
+ }
229
+ } else {
230
+ kept.push(line);
231
+ }
220
232
  }
221
- const steraPath = path4.join(path4.dirname(cssPath), "stera-ui.css");
222
- if (fs3.existsSync(steraPath)) {
223
- return true;
233
+ return { content: kept.join("\n"), imports };
234
+ }
235
+ async function writeSteraUiCss(config, cwd) {
236
+ const item = await fetchRegistryItem("globals");
237
+ const rawContent = item.files?.[0]?.content ?? "";
238
+ const { content, imports } = stripImportLines(rawContent);
239
+ const cssDir = path4.dirname(path4.resolve(cwd, config.css));
240
+ const steraUiPath = path4.join(cssDir, "stera-ui.css");
241
+ await fs3.mkdir(cssDir, { recursive: true });
242
+ await fs3.writeFile(steraUiPath, content, "utf-8");
243
+ return {
244
+ steraUiPath,
245
+ extraImports: imports,
246
+ dependencies: item.dependencies ?? []
247
+ };
248
+ }
249
+
250
+ // src/utils/css-imports.ts
251
+ import fs4 from "fs/promises";
252
+ async function insertImportLine(filePath, importStatement) {
253
+ const content = await fs4.readFile(filePath, "utf-8");
254
+ const importPath = importStatement.match(/["']([^"']+)["']/)?.[1];
255
+ if (importPath) {
256
+ const escaped = importPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
257
+ if (new RegExp(`@import\\s+["']${escaped}["']`).test(content)) return;
258
+ }
259
+ const lines = content.split("\n");
260
+ let lastImportIndex = -1;
261
+ for (let i = 0; i < lines.length; i++) {
262
+ if (lines[i].trim().startsWith("@import")) {
263
+ lastImportIndex = i;
264
+ }
224
265
  }
225
- try {
226
- const contents = fs3.readFileSync(cssPath, "utf-8");
227
- return contents.includes("--color-bg-surface");
228
- } catch {
229
- return false;
266
+ if (lastImportIndex >= 0) {
267
+ lines.splice(lastImportIndex + 1, 0, importStatement);
268
+ } else {
269
+ lines.unshift(importStatement);
230
270
  }
271
+ await fs4.writeFile(filePath, lines.join("\n"), "utf-8");
231
272
  }
232
273
 
233
274
  // src/utils/install-deps.ts
234
- import fs4 from "fs";
275
+ import fs5 from "fs";
235
276
  import path5 from "path";
236
277
  import { detect } from "@antfu/ni";
237
278
  import { execa } from "execa";
@@ -320,9 +361,9 @@ async function installDependencies(deps, cwd) {
320
361
  if (deps.length === 0) return;
321
362
  const pkgPath = path5.join(cwd, "package.json");
322
363
  let existing = /* @__PURE__ */ new Set();
323
- if (fs4.existsSync(pkgPath)) {
364
+ if (fs5.existsSync(pkgPath)) {
324
365
  try {
325
- const pkg = JSON.parse(fs4.readFileSync(pkgPath, "utf-8"));
366
+ const pkg = JSON.parse(fs5.readFileSync(pkgPath, "utf-8"));
326
367
  existing = new Set(Object.keys({ ...pkg.dependencies, ...pkg.devDependencies }));
327
368
  } catch {
328
369
  }
@@ -349,7 +390,7 @@ or use "pnpm add -w <package>" manually to install at the root.`
349
390
  }
350
391
 
351
392
  // src/utils/write-files.ts
352
- import fs5 from "fs";
393
+ import fs6 from "fs";
353
394
  import path6 from "path";
354
395
  import readline from "readline/promises";
355
396
  async function confirmOverwrite(relativePath) {
@@ -372,8 +413,8 @@ async function writeComponentFiles(items, config, projectRoot, options = {}) {
372
413
  for (const file of item.files ?? []) {
373
414
  const outputPath = resolveOutputPath(config, projectRoot, file);
374
415
  const relativePath = path6.relative(projectRoot, outputPath);
375
- if (fs5.existsSync(outputPath)) {
376
- const existing = fs5.readFileSync(outputPath, "utf-8");
416
+ if (fs6.existsSync(outputPath)) {
417
+ const existing = fs6.readFileSync(outputPath, "utf-8");
377
418
  if (existing === file.content) {
378
419
  skipped.push(relativePath);
379
420
  continue;
@@ -387,8 +428,8 @@ async function writeComponentFiles(items, config, projectRoot, options = {}) {
387
428
  }
388
429
  }
389
430
  const dir = path6.dirname(outputPath);
390
- fs5.mkdirSync(dir, { recursive: true });
391
- fs5.writeFileSync(outputPath, file.content ?? "", "utf-8");
431
+ fs6.mkdirSync(dir, { recursive: true });
432
+ fs6.writeFileSync(outputPath, file.content ?? "", "utf-8");
392
433
  written.push(relativePath);
393
434
  }
394
435
  }
@@ -396,7 +437,7 @@ async function writeComponentFiles(items, config, projectRoot, options = {}) {
396
437
  }
397
438
 
398
439
  // src/utils/update-fonts.ts
399
- import { existsSync, promises as fs6 } from "fs";
440
+ import { existsSync, promises as fs7 } from "fs";
400
441
  import path7 from "path";
401
442
  import {
402
443
  Project,
@@ -437,10 +478,10 @@ async function updateFonts(fonts, project, cwd, options = {}) {
437
478
  if (!layoutPath) return;
438
479
  if (options.silent) {
439
480
  try {
440
- const layoutContent = await fs6.readFile(layoutPath, "utf-8");
481
+ const layoutContent = await fs7.readFile(layoutPath, "utf-8");
441
482
  const updatedContent = transformLayoutFonts(layoutContent, fonts);
442
483
  if (updatedContent !== layoutContent) {
443
- await fs6.writeFile(layoutPath, updatedContent, "utf-8");
484
+ await fs7.writeFile(layoutPath, updatedContent, "utf-8");
444
485
  }
445
486
  } catch {
446
487
  }
@@ -448,10 +489,10 @@ async function updateFonts(fonts, project, cwd, options = {}) {
448
489
  }
449
490
  const spinner = createSpinner("Updating fonts");
450
491
  try {
451
- const layoutContent = await fs6.readFile(layoutPath, "utf-8");
492
+ const layoutContent = await fs7.readFile(layoutPath, "utf-8");
452
493
  const updatedContent = transformLayoutFonts(layoutContent, fonts);
453
494
  if (updatedContent !== layoutContent) {
454
- await fs6.writeFile(layoutPath, updatedContent, "utf-8");
495
+ await fs7.writeFile(layoutPath, updatedContent, "utf-8");
455
496
  }
456
497
  spinner.succeed("Updated layout with font imports");
457
498
  } catch {
@@ -715,7 +756,8 @@ export {
715
756
  loadConfig2 as loadConfig,
716
757
  detectProject,
717
758
  detectAliasPrefix,
718
- hasGlobalsCss,
759
+ writeSteraUiCss,
760
+ insertImportLine,
719
761
  brand,
720
762
  dim,
721
763
  LOGO,
package/dist/cli.js CHANGED
@@ -204,7 +204,6 @@ var dim = (text) => wrap(DIM, text);
204
204
  var LOGO = brand("\u273B");
205
205
  var CHECK = brand("\u2713");
206
206
  var CROSS = "\u2717";
207
- var WARN = "!";
208
207
 
209
208
  // src/utils/spinner.ts
210
209
  var FRAMES = ["\u2022", "\u2726", "\u2736", "\u273B"].map(brand);
@@ -362,30 +361,78 @@ var transformImports = (content, ctx) => {
362
361
  );
363
362
  };
364
363
 
365
- // src/utils/detect-globals.ts
366
- import fs4 from "fs";
364
+ // src/utils/write-stera-css.ts
365
+ import fs4 from "fs/promises";
367
366
  import path5 from "path";
368
- function hasGlobalsCss(config, projectRoot) {
369
- const cssPath = path5.resolve(projectRoot, config.css);
370
- if (!fs4.existsSync(cssPath)) {
371
- return false;
372
- }
373
- const steraPath = path5.join(path5.dirname(cssPath), "stera-ui.css");
374
- if (fs4.existsSync(steraPath)) {
375
- return true;
376
- }
377
- try {
378
- const contents = fs4.readFileSync(cssPath, "utf-8");
379
- return contents.includes("--color-bg-surface");
380
- } catch {
381
- return false;
367
+ function stripImportLines(content) {
368
+ const lines = content.split("\n");
369
+ const imports = [];
370
+ const kept = [];
371
+ for (const line of lines) {
372
+ if (line.trim().match(/^@import\s+["'].+["']/)) {
373
+ if (!line.trim().match(/^@import\s+["']tailwindcss["']/)) {
374
+ imports.push(line.trim());
375
+ }
376
+ } else {
377
+ kept.push(line);
378
+ }
382
379
  }
380
+ return { content: kept.join("\n"), imports };
381
+ }
382
+ async function writeSteraUiCss(config, cwd) {
383
+ const item = await fetchRegistryItem("globals");
384
+ const rawContent = item.files?.[0]?.content ?? "";
385
+ const { content, imports } = stripImportLines(rawContent);
386
+ const cssDir = path5.dirname(path5.resolve(cwd, config.css));
387
+ const steraUiPath = path5.join(cssDir, "stera-ui.css");
388
+ await fs4.mkdir(cssDir, { recursive: true });
389
+ await fs4.writeFile(steraUiPath, content, "utf-8");
390
+ return {
391
+ steraUiPath,
392
+ extraImports: imports,
393
+ dependencies: item.dependencies ?? []
394
+ };
383
395
  }
384
396
 
385
397
  // src/commands/add.ts
386
398
  var transforms = [transformImports];
387
399
  async function add(components, options) {
388
400
  const cwd = options.cwd ? path6.resolve(options.cwd) : process.cwd();
401
+ const configPath = findConfigPath(cwd);
402
+ if (!configPath) {
403
+ console.error(` ${CROSS} ${CONFIG_FILE} not found. Run "stera-ui init" first.`);
404
+ process.exit(1);
405
+ }
406
+ const config = loadConfig(cwd);
407
+ if (!config) {
408
+ console.error(` ${CROSS} Failed to parse ${CONFIG_FILE}.`);
409
+ process.exit(1);
410
+ }
411
+ const projectRoot = path6.dirname(configPath);
412
+ if (components.includes("globals")) {
413
+ if (components.length > 1) {
414
+ console.error(
415
+ ` ${CROSS} "stera-ui add globals" refreshes design tokens and cannot be combined with other components.`
416
+ );
417
+ console.error(
418
+ ` Run "stera-ui add globals" and "stera-ui add ${components.filter((c) => c !== "globals").join(" ")}" separately.`
419
+ );
420
+ process.exit(1);
421
+ }
422
+ const spinner2 = createSpinner("Refreshing base styles");
423
+ let result;
424
+ try {
425
+ result = await writeSteraUiCss(config, projectRoot);
426
+ } catch (err) {
427
+ spinner2.fail("Failed to refresh base styles");
428
+ throw err;
429
+ }
430
+ spinner2.succeed(
431
+ `Refreshed ${path6.relative(projectRoot, result.steraUiPath)}`
432
+ );
433
+ console.log("");
434
+ return;
435
+ }
389
436
  const spinner = createSpinner("Fetching registry");
390
437
  try {
391
438
  for (const name of components) {
@@ -400,17 +447,6 @@ async function add(components, options) {
400
447
  spinner.fail("Failed to fetch registry");
401
448
  throw err;
402
449
  }
403
- const configPath = findConfigPath(cwd);
404
- if (!configPath) {
405
- spinner.fail(`${CONFIG_FILE} not found. Run "stera-ui init" first.`);
406
- process.exit(1);
407
- }
408
- const config = loadConfig(cwd);
409
- if (!config) {
410
- spinner.fail(`Failed to parse ${CONFIG_FILE}.`);
411
- process.exit(1);
412
- }
413
- const projectRoot = path6.dirname(configPath);
414
450
  let resolved;
415
451
  try {
416
452
  resolved = await resolveDependencies(components);
@@ -419,14 +455,6 @@ async function add(components, options) {
419
455
  throw err;
420
456
  }
421
457
  spinner.succeed("Registry resolved");
422
- const isAddingGlobals = components.includes("globals");
423
- if (!isAddingGlobals && !hasGlobalsCss(config, projectRoot)) {
424
- console.warn(`
425
- ${WARN} Stera design tokens not found in ${config.css}`);
426
- console.warn(` Components may not render correctly.`);
427
- console.warn(` Run "stera-ui add globals" to install base styles.
428
- `);
429
- }
430
458
  for (const item of resolved) {
431
459
  for (const file of item.files ?? []) {
432
460
  if (file.content) {
@@ -478,7 +506,8 @@ async function add(components, options) {
478
506
  }
479
507
 
480
508
  // src/commands/init.ts
481
- import fs9 from "fs";
509
+ import fs10 from "fs";
510
+ import fsp from "fs/promises";
482
511
  import path10 from "path";
483
512
  import { select } from "@inquirer/prompts";
484
513
 
@@ -987,6 +1016,30 @@ function parseTemplateLiteralToCnArgs(templateLiteral) {
987
1016
  return [...staticArgs, ...variableArgs];
988
1017
  }
989
1018
 
1019
+ // src/utils/css-imports.ts
1020
+ import fs9 from "fs/promises";
1021
+ async function insertImportLine(filePath, importStatement) {
1022
+ const content = await fs9.readFile(filePath, "utf-8");
1023
+ const importPath = importStatement.match(/["']([^"']+)["']/)?.[1];
1024
+ if (importPath) {
1025
+ const escaped = importPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1026
+ if (new RegExp(`@import\\s+["']${escaped}["']`).test(content)) return;
1027
+ }
1028
+ const lines = content.split("\n");
1029
+ let lastImportIndex = -1;
1030
+ for (let i = 0; i < lines.length; i++) {
1031
+ if (lines[i].trim().startsWith("@import")) {
1032
+ lastImportIndex = i;
1033
+ }
1034
+ }
1035
+ if (lastImportIndex >= 0) {
1036
+ lines.splice(lastImportIndex + 1, 0, importStatement);
1037
+ } else {
1038
+ lines.unshift(importStatement);
1039
+ }
1040
+ await fs9.writeFile(filePath, lines.join("\n"), "utf-8");
1041
+ }
1042
+
990
1043
  // src/commands/init.ts
991
1044
  var DEFAULT_FONT_NAMES = ["font-geist", "font-geist-mono"];
992
1045
  function buildConfig(project, strategy, aliasPrefix) {
@@ -1014,42 +1067,6 @@ function buildConfig(project, strategy, aliasPrefix) {
1014
1067
  }
1015
1068
  };
1016
1069
  }
1017
- function stripImportLines(content) {
1018
- const lines = content.split("\n");
1019
- const imports = [];
1020
- const kept = [];
1021
- for (const line of lines) {
1022
- if (line.trim().match(/^@import\s+["'].+["']/)) {
1023
- if (!line.trim().match(/^@import\s+["']tailwindcss["']/)) {
1024
- imports.push(line.trim());
1025
- }
1026
- } else {
1027
- kept.push(line);
1028
- }
1029
- }
1030
- return { content: kept.join("\n"), imports };
1031
- }
1032
- function insertImportLine(filePath, importStatement) {
1033
- const content = fs9.readFileSync(filePath, "utf-8");
1034
- const importPath = importStatement.match(/["']([^"']+)["']/)?.[1];
1035
- if (importPath) {
1036
- const escaped = importPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1037
- if (new RegExp(`@import\\s+["']${escaped}["']`).test(content)) return;
1038
- }
1039
- const lines = content.split("\n");
1040
- let lastImportIndex = -1;
1041
- for (let i = 0; i < lines.length; i++) {
1042
- if (lines[i].trim().startsWith("@import")) {
1043
- lastImportIndex = i;
1044
- }
1045
- }
1046
- if (lastImportIndex >= 0) {
1047
- lines.splice(lastImportIndex + 1, 0, importStatement);
1048
- } else {
1049
- lines.unshift(importStatement);
1050
- }
1051
- fs9.writeFileSync(filePath, lines.join("\n"), "utf-8");
1052
- }
1053
1070
  async function promptFontStrategy(detected, skipPrompts) {
1054
1071
  const hasExistingFonts = detected.hasNextFont || detected.hasFontFace || detected.hasGoogleFontsLink;
1055
1072
  const defaultStrategy = hasExistingFonts ? "keep-existing" : "stera-default";
@@ -1112,26 +1129,17 @@ async function fetchDefaultFonts() {
1112
1129
  }
1113
1130
  async function executeFontStrategy(strategy, project, detected, config, cwd) {
1114
1131
  if (strategy === "skip") return;
1132
+ const cssDir = path10.dirname(path10.resolve(cwd, config.css));
1133
+ const steraUiPath = path10.join(cssDir, "stera-ui.css");
1115
1134
  if (strategy === "keep-existing") {
1116
- const cssPath = path10.resolve(cwd, config.css);
1117
- const cssDir = path10.dirname(cssPath);
1118
- if (fs9.existsSync(cssPath)) {
1119
- const fontFamily = detected.fontFamilies.length > 0 ? detected.fontFamilies[0] : null;
1120
- const fontValue = detected.nextFontVariable ? `var(${detected.nextFontVariable})` : fontFamily ?? "Geist";
1121
- const patched = patchCssVariables(cssPath, {
1122
- "--font-heading": fontValue,
1123
- "--font-sans": fontValue
1124
- });
1125
- const steraUiPath = path10.join(cssDir, "stera-ui.css");
1126
- if (fs9.existsSync(steraUiPath)) {
1127
- patchCssVariables(steraUiPath, {
1128
- "--font-heading": fontValue,
1129
- "--font-sans": fontValue
1130
- });
1131
- }
1132
- if (patched.length > 0) {
1133
- console.log(` ${CHECK} Updated font variables to use ${fontValue}`);
1134
- }
1135
+ const fontFamily = detected.fontFamilies.length > 0 ? detected.fontFamilies[0] : null;
1136
+ const fontValue = detected.nextFontVariable ? `var(${detected.nextFontVariable})` : fontFamily ?? "Geist";
1137
+ const patched = patchCssVariables(steraUiPath, {
1138
+ "--font-heading": fontValue,
1139
+ "--font-sans": fontValue
1140
+ });
1141
+ if (patched.length > 0) {
1142
+ console.log(` ${CHECK} Updated font variables to use ${fontValue}`);
1135
1143
  }
1136
1144
  return;
1137
1145
  }
@@ -1142,25 +1150,15 @@ async function executeFontStrategy(strategy, project, detected, config, cwd) {
1142
1150
  await installDependencies(fontTree.dependencies, cwd);
1143
1151
  }
1144
1152
  if (fontTree.cssImports.length > 0) {
1145
- const cssPath = path10.resolve(cwd, config.css);
1146
- const cssDir = path10.dirname(cssPath);
1147
- const targetCss = fs9.existsSync(path10.join(cssDir, "stera-ui.css")) ? path10.join(cssDir, "stera-ui.css") : cssPath;
1148
- if (fs9.existsSync(targetCss)) {
1149
- for (const importLine of fontTree.cssImports) {
1150
- insertImportLine(targetCss, importLine);
1151
- }
1152
- console.log(` ${CHECK} Added font imports to ${path10.relative(cwd, targetCss)}`);
1153
+ for (const importLine of fontTree.cssImports) {
1154
+ await insertImportLine(steraUiPath, importLine);
1153
1155
  }
1156
+ console.log(` ${CHECK} Added font imports to ${path10.relative(cwd, steraUiPath)}`);
1154
1157
  }
1155
1158
  if (Object.keys(fontTree.cssVars).length > 0) {
1156
- const cssPath = path10.resolve(cwd, config.css);
1157
- const cssDir = path10.dirname(cssPath);
1158
- const targetCss = fs9.existsSync(path10.join(cssDir, "stera-ui.css")) ? path10.join(cssDir, "stera-ui.css") : cssPath;
1159
- if (fs9.existsSync(targetCss)) {
1160
- const patched = patchCssVariables(targetCss, fontTree.cssVars);
1161
- if (patched.length > 0) {
1162
- console.log(` ${CHECK} Updated font variables in ${path10.relative(cwd, targetCss)}`);
1163
- }
1159
+ const patched = patchCssVariables(steraUiPath, fontTree.cssVars);
1160
+ if (patched.length > 0) {
1161
+ console.log(` ${CHECK} Updated font variables in ${path10.relative(cwd, steraUiPath)}`);
1164
1162
  }
1165
1163
  }
1166
1164
  await updateFonts(fonts, project, cwd);
@@ -1177,7 +1175,7 @@ async function init(options) {
1177
1175
  return;
1178
1176
  }
1179
1177
  const pkgPath = path10.join(cwd, "package.json");
1180
- if (!fs9.existsSync(pkgPath)) {
1178
+ if (!fs10.existsSync(pkgPath)) {
1181
1179
  console.error(
1182
1180
  " Error: No package.json found. Please run this in a project directory."
1183
1181
  );
@@ -1212,58 +1210,42 @@ async function init(options) {
1212
1210
  );
1213
1211
  const config = buildConfig(project, strategy, aliasPrefix);
1214
1212
  const configPath = path10.join(cwd, CONFIG_FILE);
1215
- fs9.writeFileSync(
1213
+ fs10.writeFileSync(
1216
1214
  configPath,
1217
1215
  JSON.stringify(config, null, 2) + "\n",
1218
1216
  "utf-8"
1219
1217
  );
1220
1218
  console.log(` ${CHECK} Created components.json`);
1221
- const cssDir = path10.dirname(path10.resolve(cwd, config.css));
1222
- if (project.existingCssFile) {
1223
- if (hasGlobalsCss(config, cwd)) {
1224
- console.log(
1225
- ` ${CHECK} Stera already initialised ${dim(`(${path10.join(path10.dirname(project.existingCssFile), "stera-ui.css")} exists)`)}`
1226
- );
1227
- } else {
1228
- const fetchSpinner = createSpinner("Fetching base styles");
1229
- let globalsItem;
1230
- try {
1231
- globalsItem = await fetchRegistryItem("globals");
1232
- } catch {
1233
- fetchSpinner.fail("Base styles not found \u2014 skipping");
1234
- globalsItem = null;
1235
- }
1236
- if (globalsItem) {
1237
- fetchSpinner.succeed("Base styles resolved");
1238
- const globalsContent = globalsItem.files?.[0]?.content ?? "";
1239
- const { content: steraUiContent, imports } = stripImportLines(globalsContent);
1240
- const steraUiPath = path10.join(cssDir, "stera-ui.css");
1241
- fs9.mkdirSync(cssDir, { recursive: true });
1242
- fs9.writeFileSync(steraUiPath, steraUiContent, "utf-8");
1243
- console.log(` ${CHECK} Created ${path10.relative(cwd, steraUiPath)}`);
1244
- const existingCssPath = path10.resolve(cwd, project.existingCssFile);
1245
- for (const imp of imports) {
1246
- insertImportLine(existingCssPath, imp);
1247
- }
1248
- insertImportLine(existingCssPath, "@import './stera-ui.css';");
1249
- console.log(` ${CHECK} Added @import to ${project.existingCssFile}`);
1250
- }
1219
+ const fetchSpinner = createSpinner("Fetching base styles");
1220
+ let steraCssResult;
1221
+ try {
1222
+ steraCssResult = await writeSteraUiCss(config, cwd);
1223
+ } catch (err) {
1224
+ fetchSpinner.fail("Failed to fetch base styles");
1225
+ throw err;
1226
+ }
1227
+ fetchSpinner.succeed("Base styles resolved");
1228
+ console.log(
1229
+ ` ${CHECK} Created ${path10.relative(cwd, steraCssResult.steraUiPath)}`
1230
+ );
1231
+ const globalsPath = project.existingCssFile ? path10.resolve(cwd, project.existingCssFile) : path10.resolve(cwd, config.css);
1232
+ if (fs10.existsSync(globalsPath)) {
1233
+ for (const imp of steraCssResult.extraImports) {
1234
+ await insertImportLine(globalsPath, imp);
1251
1235
  }
1236
+ await insertImportLine(globalsPath, '@import "./stera-ui.css";');
1237
+ console.log(
1238
+ ` ${CHECK} Added @import to ${path10.relative(cwd, globalsPath)}`
1239
+ );
1252
1240
  } else {
1253
- const fetchSpinner = createSpinner("Fetching base styles");
1254
- let resolved;
1255
- try {
1256
- resolved = await resolveDependencies(["globals"]);
1257
- } catch (err) {
1258
- fetchSpinner.fail("Failed to fetch base styles");
1259
- throw err;
1260
- }
1261
- fetchSpinner.succeed("Base styles resolved");
1262
- const filtered = resolved.filter((item) => item.name !== "fonts");
1263
- const { written } = await writeComponentFiles(filtered, config, cwd);
1264
- for (const file of written) {
1265
- console.log(` ${CHECK} Created ${file}`);
1266
- }
1241
+ await fsp.mkdir(path10.dirname(globalsPath), { recursive: true });
1242
+ const scaffold = [
1243
+ '@import "tailwindcss";',
1244
+ ...steraCssResult.extraImports,
1245
+ '@import "./stera-ui.css";'
1246
+ ].join("\n") + "\n";
1247
+ await fsp.writeFile(globalsPath, scaffold, "utf-8");
1248
+ console.log(` ${CHECK} Created ${path10.relative(cwd, globalsPath)}`);
1267
1249
  }
1268
1250
  await executeFontStrategy(
1269
1251
  strategy,
@@ -1272,21 +1254,10 @@ async function init(options) {
1272
1254
  config,
1273
1255
  cwd
1274
1256
  );
1275
- const depsSpinner = createSpinner("Fetching package list");
1276
- let globalsForDeps;
1277
- try {
1278
- globalsForDeps = await fetchRegistryItem("globals");
1279
- } catch {
1280
- depsSpinner.fail("Package list unavailable \u2014 skipping");
1281
- globalsForDeps = null;
1282
- }
1283
- if (globalsForDeps) {
1284
- depsSpinner.succeed("Package list resolved");
1285
- if (globalsForDeps.dependencies) {
1286
- const deps = [...new Set(globalsForDeps.dependencies)].sort();
1287
- console.log("");
1288
- await installDependencies(deps, cwd);
1289
- }
1257
+ if (steraCssResult.dependencies.length > 0) {
1258
+ const deps = [...new Set(steraCssResult.dependencies)].sort();
1259
+ console.log("");
1260
+ await installDependencies(deps, cwd);
1290
1261
  }
1291
1262
  console.log("");
1292
1263
  console.log(" Next steps:");
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { fetchRegistryIndex, fetchRegistryItem, getAllComponents, getComponent, resolveDependencies } from './registry/index.js';
2
2
  export { RegistryFontItem, RegistryIndex, RegistryItem, RegistryItemFile, RegistryItemFont, RegistryItemType, SteraConfig, configSchema, registryIndexSchema, registryItemFileSchema, registryItemFontSchema, registryItemSchema, registryItemTypeSchema } from './schema/index.js';
3
- export { CHECK, CONFIG_FILE, CROSS, LOGO, ProjectInfo, TransformContext, TransformFn, WARN, WriteOptions, WriteResult, applyTransforms, brand, createSpinner, detectAliasPrefix, detectPackageManager, detectProject, dim, findConfigPath, hasGlobalsCss, installDependencies, loadConfig, massageTreeForFonts, resolveOutputPath, transformImports, updateFonts, writeComponentFiles } from './utils/index.js';
3
+ export { CHECK, CONFIG_FILE, CROSS, LOGO, ProjectInfo, TransformContext, TransformFn, WARN, WriteOptions, WriteResult, WriteSteraCssResult, applyTransforms, brand, createSpinner, detectAliasPrefix, detectPackageManager, detectProject, dim, findConfigPath, insertImportLine, installDependencies, loadConfig, massageTreeForFonts, resolveOutputPath, transformImports, updateFonts, writeComponentFiles, writeSteraUiCss } from './utils/index.js';
4
4
  import 'zod';
package/dist/index.js CHANGED
@@ -1,10 +1,3 @@
1
- import {
2
- fetchRegistryIndex,
3
- fetchRegistryItem,
4
- getAllComponents,
5
- getComponent,
6
- resolveDependencies
7
- } from "./chunk-T3QWOVJR.js";
8
1
  import {
9
2
  configSchema,
10
3
  registryIndexSchema,
@@ -27,15 +20,23 @@ import {
27
20
  detectProject,
28
21
  dim,
29
22
  findConfigPath,
30
- hasGlobalsCss,
23
+ insertImportLine,
31
24
  installDependencies,
32
25
  loadConfig,
33
26
  massageTreeForFonts,
34
27
  resolveOutputPath,
35
28
  transformImports,
36
29
  updateFonts,
37
- writeComponentFiles
38
- } from "./chunk-QD7FOI3A.js";
30
+ writeComponentFiles,
31
+ writeSteraUiCss
32
+ } from "./chunk-67ILD3JO.js";
33
+ import {
34
+ fetchRegistryIndex,
35
+ fetchRegistryItem,
36
+ getAllComponents,
37
+ getComponent,
38
+ resolveDependencies
39
+ } from "./chunk-T3QWOVJR.js";
39
40
  export {
40
41
  CHECK,
41
42
  CONFIG_FILE,
@@ -55,7 +56,7 @@ export {
55
56
  findConfigPath,
56
57
  getAllComponents,
57
58
  getComponent,
58
- hasGlobalsCss,
59
+ insertImportLine,
59
60
  installDependencies,
60
61
  loadConfig,
61
62
  massageTreeForFonts,
@@ -68,5 +69,6 @@ export {
68
69
  resolveOutputPath,
69
70
  transformImports,
70
71
  updateFonts,
71
- writeComponentFiles
72
+ writeComponentFiles,
73
+ writeSteraUiCss
72
74
  };
@@ -77,13 +77,36 @@ declare function detectProject(cwd: string): ProjectInfo;
77
77
  */
78
78
  declare function detectAliasPrefix(cwd: string): string | null;
79
79
 
80
+ interface WriteSteraCssResult {
81
+ /** Absolute path to the written `stera-ui.css`. */
82
+ steraUiPath: string;
83
+ /**
84
+ * Non-tailwindcss `@import` statements stripped from the registry content
85
+ * (e.g. `@import "tw-animate-css";`). Callers are expected to ensure these
86
+ * are present in the user's main globals.css.
87
+ */
88
+ extraImports: string[];
89
+ /** npm packages declared by the globals registry item. */
90
+ dependencies: string[];
91
+ }
80
92
  /**
81
- * Check if Stera has already been initialised in the user's project.
82
- * Returns true if either a sibling `stera-ui.css` exists (existing-CSS init
83
- * path) or the user's CSS file itself contains Stera tokens inline
84
- * (no-existing-CSS init path, or a hand-rolled setup).
93
+ * Fetch the `globals` registry item and write `stera-ui.css` into the
94
+ * directory that holds the user's configured globals.css. Overwrites
95
+ * unconditionally — the caller decides when this is appropriate (init or
96
+ * an explicit `add globals` refresh).
97
+ *
98
+ * Does NOT touch the user's globals.css.
99
+ */
100
+ declare function writeSteraUiCss(config: SteraConfig, cwd: string): Promise<WriteSteraCssResult>;
101
+
102
+ /**
103
+ * Insert an `@import` statement into an existing CSS file, placed after the
104
+ * last existing `@import` line. Prepends to the file if none exist.
105
+ *
106
+ * Idempotent: if an `@import` for the same path already exists, no change
107
+ * is made.
85
108
  */
86
- declare function hasGlobalsCss(config: SteraConfig, projectRoot: string): boolean;
109
+ declare function insertImportLine(filePath: string, importStatement: string): Promise<void>;
87
110
 
88
111
  type PackageManager = "pnpm" | "npm" | "yarn" | "bun";
89
112
  declare function detectPackageManager(cwd: string): Promise<PackageManager>;
@@ -143,4 +166,4 @@ declare function updateFonts(fonts: RegistryFontItem[], project: ProjectInfo, cw
143
166
  silent?: boolean;
144
167
  }): Promise<void>;
145
168
 
146
- export { CHECK, CONFIG_FILE, CROSS, LOGO, type ProjectInfo, type TransformContext, type TransformFn, WARN, type WriteOptions, type WriteResult, applyTransforms, brand, createSpinner, detectAliasPrefix, detectPackageManager, detectProject, dim, findConfigPath, hasGlobalsCss, installDependencies, loadConfig, massageTreeForFonts, resolveOutputPath, transformImports, updateFonts, writeComponentFiles };
169
+ export { CHECK, CONFIG_FILE, CROSS, LOGO, type ProjectInfo, type TransformContext, type TransformFn, WARN, type WriteOptions, type WriteResult, type WriteSteraCssResult, applyTransforms, brand, createSpinner, detectAliasPrefix, detectPackageManager, detectProject, dim, findConfigPath, insertImportLine, installDependencies, loadConfig, massageTreeForFonts, resolveOutputPath, transformImports, updateFonts, writeComponentFiles, writeSteraUiCss };
@@ -12,15 +12,17 @@ import {
12
12
  detectProject,
13
13
  dim,
14
14
  findConfigPath,
15
- hasGlobalsCss,
15
+ insertImportLine,
16
16
  installDependencies,
17
17
  loadConfig,
18
18
  massageTreeForFonts,
19
19
  resolveOutputPath,
20
20
  transformImports,
21
21
  updateFonts,
22
- writeComponentFiles
23
- } from "../chunk-QD7FOI3A.js";
22
+ writeComponentFiles,
23
+ writeSteraUiCss
24
+ } from "../chunk-67ILD3JO.js";
25
+ import "../chunk-T3QWOVJR.js";
24
26
  export {
25
27
  CHECK,
26
28
  CONFIG_FILE,
@@ -35,12 +37,13 @@ export {
35
37
  detectProject,
36
38
  dim,
37
39
  findConfigPath,
38
- hasGlobalsCss,
40
+ insertImportLine,
39
41
  installDependencies,
40
42
  loadConfig,
41
43
  massageTreeForFonts,
42
44
  resolveOutputPath,
43
45
  transformImports,
44
46
  updateFonts,
45
- writeComponentFiles
47
+ writeComponentFiles,
48
+ writeSteraUiCss
46
49
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stera-ui",
3
- "version": "0.3.3",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "description": "Add Stera UI components to your project.",
6
6
  "license": "MIT",