stera-ui 0.8.1 → 0.9.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/add-JTKZDMNI.js +2 -0
- package/dist/chunk-42DL2YAQ.js +2 -0
- package/dist/chunk-FZGIVCWE.js +42175 -0
- package/dist/chunk-LUUYMQIF.js +97 -0
- package/dist/chunk-MBBLZJIK.js +2 -0
- package/dist/chunk-OPVLMYW5.js +2 -0
- package/dist/chunk-OSMC63WJ.js +2 -0
- package/dist/chunk-P2O7D5BG.js +2 -0
- package/dist/chunk-UVHQPGZ3.js +7 -0
- package/dist/chunk-XJ6VM67K.js +2 -0
- package/dist/cli.js +29 -32
- package/dist/index.js +2 -86
- package/dist/init-XAKB27HW.js +12 -0
- package/dist/list-W7ECLPKW.js +5 -0
- package/dist/registry/index.d.ts +3 -1
- package/dist/registry/index.js +2 -17
- package/dist/schema/index.js +2 -18
- package/dist/utils/index.d.ts +26 -3
- package/dist/utils/index.js +2 -58
- package/package.json +6 -8
- package/dist/add-DBC33IWM.js +0 -201
- package/dist/chunk-4E46BULJ.js +0 -79
- package/dist/chunk-BJNLGEZJ.js +0 -100
- package/dist/chunk-KGMF6UW4.js +0 -21
- package/dist/chunk-KKUJRJ2N.js +0 -209
- package/dist/chunk-RQ7T7RF5.js +0 -880
- package/dist/chunk-UKHKFX2A.js +0 -376
- package/dist/init-EQHYR7MG.js +0 -763
- package/dist/list-M73QTIWR.js +0 -44
package/dist/chunk-RQ7T7RF5.js
DELETED
|
@@ -1,880 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
fetchRegistryItem
|
|
3
|
-
} from "./chunk-BJNLGEZJ.js";
|
|
4
|
-
|
|
5
|
-
// src/utils/transform.ts
|
|
6
|
-
function applyTransforms(content, ctx, transforms) {
|
|
7
|
-
return transforms.reduce((acc, fn) => fn(acc, ctx), content);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
// src/utils/transform-imports.ts
|
|
11
|
-
var DEFAULT_ALIASES = {
|
|
12
|
-
utils: "@/lib/utils",
|
|
13
|
-
ui: "@/components/ui",
|
|
14
|
-
components: "@/components",
|
|
15
|
-
lib: "@/lib",
|
|
16
|
-
hooks: "@/hooks"
|
|
17
|
-
};
|
|
18
|
-
var transformImports = (content, ctx) => {
|
|
19
|
-
if (!ctx.filename.endsWith(".ts") && !ctx.filename.endsWith(".tsx")) {
|
|
20
|
-
return content;
|
|
21
|
-
}
|
|
22
|
-
const { aliases } = ctx.config;
|
|
23
|
-
const rules = [];
|
|
24
|
-
if (aliases.utils !== DEFAULT_ALIASES.utils) {
|
|
25
|
-
rules.push({
|
|
26
|
-
type: "exact",
|
|
27
|
-
from: DEFAULT_ALIASES.utils,
|
|
28
|
-
to: aliases.utils
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
if (aliases.ui !== DEFAULT_ALIASES.ui) {
|
|
32
|
-
rules.push({ type: "prefix", from: DEFAULT_ALIASES.ui, to: aliases.ui });
|
|
33
|
-
}
|
|
34
|
-
if (aliases.components !== DEFAULT_ALIASES.components) {
|
|
35
|
-
rules.push({
|
|
36
|
-
type: "prefix",
|
|
37
|
-
from: DEFAULT_ALIASES.components,
|
|
38
|
-
to: aliases.components
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
if (aliases.lib !== DEFAULT_ALIASES.lib) {
|
|
42
|
-
rules.push({ type: "prefix", from: DEFAULT_ALIASES.lib, to: aliases.lib });
|
|
43
|
-
}
|
|
44
|
-
if (aliases.hooks !== DEFAULT_ALIASES.hooks) {
|
|
45
|
-
rules.push({
|
|
46
|
-
type: "prefix",
|
|
47
|
-
from: DEFAULT_ALIASES.hooks,
|
|
48
|
-
to: aliases.hooks
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
if (rules.length === 0) {
|
|
52
|
-
return content;
|
|
53
|
-
}
|
|
54
|
-
return content.replace(
|
|
55
|
-
/from\s+(["'])([^"']+)\1/g,
|
|
56
|
-
(match, quote, importPath) => {
|
|
57
|
-
for (const rule of rules) {
|
|
58
|
-
if (rule.type === "exact" && importPath === rule.from) {
|
|
59
|
-
return `from ${quote}${rule.to}${quote}`;
|
|
60
|
-
}
|
|
61
|
-
if (rule.type === "prefix" && importPath.startsWith(rule.from + "/")) {
|
|
62
|
-
const rest = importPath.slice(rule.from.length);
|
|
63
|
-
return `from ${quote}${rule.to}${rest}${quote}`;
|
|
64
|
-
}
|
|
65
|
-
if (rule.type === "prefix" && importPath === rule.from) {
|
|
66
|
-
return `from ${quote}${rule.to}${quote}`;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return match;
|
|
70
|
-
}
|
|
71
|
-
);
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
// src/utils/resolve-paths.ts
|
|
75
|
-
import path from "path";
|
|
76
|
-
import { loadConfig, createMatchPath } from "tsconfig-paths";
|
|
77
|
-
var TYPE_TO_ALIAS = {
|
|
78
|
-
"registry:ui": "ui",
|
|
79
|
-
"registry:lib": "lib",
|
|
80
|
-
"registry:hook": "hooks",
|
|
81
|
-
"registry:component": "components"
|
|
82
|
-
};
|
|
83
|
-
var matcherCache = /* @__PURE__ */ new Map();
|
|
84
|
-
function getTsConfigMatcher(projectRoot) {
|
|
85
|
-
let matcher = matcherCache.get(projectRoot);
|
|
86
|
-
if (matcher !== void 0) return matcher;
|
|
87
|
-
const tsConfig = loadConfig(projectRoot);
|
|
88
|
-
matcher = tsConfig.resultType === "success" && Object.keys(tsConfig.paths).length > 0 ? {
|
|
89
|
-
paths: tsConfig.paths,
|
|
90
|
-
match: createMatchPath(tsConfig.absoluteBaseUrl, tsConfig.paths)
|
|
91
|
-
} : null;
|
|
92
|
-
matcherCache.set(projectRoot, matcher);
|
|
93
|
-
return matcher;
|
|
94
|
-
}
|
|
95
|
-
function resolveAlias(alias, projectRoot) {
|
|
96
|
-
const matcher = getTsConfigMatcher(projectRoot);
|
|
97
|
-
if (matcher) {
|
|
98
|
-
const hasMatchingPrefix = Object.keys(matcher.paths).some((key) => {
|
|
99
|
-
const prefix = key.replace(/\/\*$/, "");
|
|
100
|
-
return alias === prefix || alias.startsWith(`${prefix}/`);
|
|
101
|
-
});
|
|
102
|
-
if (hasMatchingPrefix) {
|
|
103
|
-
const resolved = matcher.match(alias, void 0, () => true, [
|
|
104
|
-
".ts",
|
|
105
|
-
".tsx"
|
|
106
|
-
]);
|
|
107
|
-
if (resolved) {
|
|
108
|
-
return path.relative(projectRoot, resolved);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
throw new Error(
|
|
113
|
-
`Could not resolve alias "${alias}" from tsconfig.json. Ensure compilerOptions.paths defines a mapping for it (e.g. "${alias}/*"), then re-run. See https://ui.stera.sh/docs/installation.`
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
function resolveOutputPath(config, projectRoot, file) {
|
|
117
|
-
const alias = TYPE_TO_ALIAS[file.type];
|
|
118
|
-
if (alias) {
|
|
119
|
-
const dir = resolveAlias(config.aliases[alias], projectRoot);
|
|
120
|
-
const filename = path.basename(file.path);
|
|
121
|
-
return path.join(projectRoot, dir, filename);
|
|
122
|
-
}
|
|
123
|
-
if (file.type === "registry:style") {
|
|
124
|
-
const cssDir = path.dirname(config.css);
|
|
125
|
-
const filename = path.basename(file.path);
|
|
126
|
-
return path.join(projectRoot, cssDir, filename);
|
|
127
|
-
}
|
|
128
|
-
return path.join(projectRoot, file.path);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// src/utils/resolve-config.ts
|
|
132
|
-
import fs from "fs";
|
|
133
|
-
import path2 from "path";
|
|
134
|
-
var CONFIG_FILE = "stera.config.json";
|
|
135
|
-
var LEGACY_CONFIG_FILE = "components.json";
|
|
136
|
-
function migrateConfig(raw) {
|
|
137
|
-
if (!("version" in raw) && "tailwind" in raw) {
|
|
138
|
-
const legacy = raw;
|
|
139
|
-
return {
|
|
140
|
-
version: 1,
|
|
141
|
-
css: legacy.tailwind.css || "src/styles/globals.css",
|
|
142
|
-
aliases: legacy.aliases
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
return raw;
|
|
146
|
-
}
|
|
147
|
-
var warnedLegacyFilename = false;
|
|
148
|
-
function warnLegacyFilenameOnce(legacyPath) {
|
|
149
|
-
if (warnedLegacyFilename) return;
|
|
150
|
-
warnedLegacyFilename = true;
|
|
151
|
-
console.warn(
|
|
152
|
-
` \u26A0 Found ${LEGACY_CONFIG_FILE}. Rename to ${CONFIG_FILE} \u2014 legacy filename support will be removed in a future release.
|
|
153
|
-
(${legacyPath})`
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
function findConfigPath(cwd) {
|
|
157
|
-
let dir = path2.resolve(cwd);
|
|
158
|
-
while (true) {
|
|
159
|
-
const configPath = path2.join(dir, CONFIG_FILE);
|
|
160
|
-
if (fs.existsSync(configPath)) return configPath;
|
|
161
|
-
const legacyPath = path2.join(dir, LEGACY_CONFIG_FILE);
|
|
162
|
-
if (fs.existsSync(legacyPath)) return legacyPath;
|
|
163
|
-
const parent = path2.dirname(dir);
|
|
164
|
-
if (parent === dir) return null;
|
|
165
|
-
dir = parent;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
function loadConfig2(cwd) {
|
|
169
|
-
const configPath = findConfigPath(cwd);
|
|
170
|
-
if (!configPath) return null;
|
|
171
|
-
if (path2.basename(configPath) === LEGACY_CONFIG_FILE) {
|
|
172
|
-
warnLegacyFilenameOnce(configPath);
|
|
173
|
-
}
|
|
174
|
-
try {
|
|
175
|
-
const raw = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
176
|
-
return migrateConfig(raw);
|
|
177
|
-
} catch {
|
|
178
|
-
return null;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// src/utils/detect-project.ts
|
|
183
|
-
import fs2 from "fs";
|
|
184
|
-
import path3 from "path";
|
|
185
|
-
import { loadConfig as loadConfig3 } from "tsconfig-paths";
|
|
186
|
-
function hasFile(cwd, filenames) {
|
|
187
|
-
return filenames.some((f) => fs2.existsSync(path3.join(cwd, f)));
|
|
188
|
-
}
|
|
189
|
-
function findTailwindCssFile(cwd) {
|
|
190
|
-
const candidates = [
|
|
191
|
-
"src/app/globals.css",
|
|
192
|
-
"src/styles/globals.css",
|
|
193
|
-
"src/index.css",
|
|
194
|
-
"app/globals.css",
|
|
195
|
-
"styles/globals.css",
|
|
196
|
-
"index.css"
|
|
197
|
-
];
|
|
198
|
-
for (const candidate of candidates) {
|
|
199
|
-
const fullPath = path3.join(cwd, candidate);
|
|
200
|
-
if (!fs2.existsSync(fullPath)) continue;
|
|
201
|
-
const content = fs2.readFileSync(fullPath, "utf-8");
|
|
202
|
-
if (content.includes('@import "tailwindcss"') || content.includes("@import 'tailwindcss'") || content.includes("@tailwind base")) {
|
|
203
|
-
return candidate;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
return null;
|
|
207
|
-
}
|
|
208
|
-
function detectProject(cwd) {
|
|
209
|
-
const hasSrc = fs2.existsSync(path3.join(cwd, "src"));
|
|
210
|
-
let framework = "other";
|
|
211
|
-
if (hasFile(cwd, ["next.config.ts", "next.config.js", "next.config.mjs"])) {
|
|
212
|
-
framework = "next";
|
|
213
|
-
} else if (hasFile(cwd, ["vite.config.ts", "vite.config.js", "vite.config.mjs"])) {
|
|
214
|
-
framework = "vite";
|
|
215
|
-
}
|
|
216
|
-
let rsc = false;
|
|
217
|
-
if (framework === "next") {
|
|
218
|
-
const appDir = hasSrc ? "src/app" : "app";
|
|
219
|
-
rsc = fs2.existsSync(path3.join(cwd, appDir));
|
|
220
|
-
}
|
|
221
|
-
const existingCssFile = findTailwindCssFile(cwd);
|
|
222
|
-
let layoutFile = null;
|
|
223
|
-
if (framework === "next") {
|
|
224
|
-
const appDir = hasSrc ? "src/app" : "app";
|
|
225
|
-
for (const ext of ["tsx", "jsx", "ts", "js"]) {
|
|
226
|
-
const candidate = `${appDir}/layout.${ext}`;
|
|
227
|
-
if (fs2.existsSync(path3.join(cwd, candidate))) {
|
|
228
|
-
layoutFile = candidate;
|
|
229
|
-
break;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
} else if (framework === "vite") {
|
|
233
|
-
if (fs2.existsSync(path3.join(cwd, "index.html"))) {
|
|
234
|
-
layoutFile = "index.html";
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
return { hasSrc, framework, rsc, existingCssFile, layoutFile };
|
|
238
|
-
}
|
|
239
|
-
function detectAliasPrefix(cwd) {
|
|
240
|
-
let tsConfig;
|
|
241
|
-
try {
|
|
242
|
-
tsConfig = loadConfig3(cwd);
|
|
243
|
-
} catch {
|
|
244
|
-
return null;
|
|
245
|
-
}
|
|
246
|
-
if (tsConfig.resultType !== "success" || Object.keys(tsConfig.paths).length === 0) {
|
|
247
|
-
return null;
|
|
248
|
-
}
|
|
249
|
-
for (const [alias, targets] of Object.entries(tsConfig.paths)) {
|
|
250
|
-
if (targets.includes("./*") || targets.includes("./src/*") || targets.includes("./app/*")) {
|
|
251
|
-
return alias.replace(/\/\*$/, "");
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
const first = Object.keys(tsConfig.paths)[0];
|
|
255
|
-
return first ? first.replace(/\/\*$/, "") : null;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// src/utils/write-styles.ts
|
|
259
|
-
import fs6 from "fs/promises";
|
|
260
|
-
import { existsSync } from "fs";
|
|
261
|
-
import path5 from "path";
|
|
262
|
-
|
|
263
|
-
// src/utils/css-imports.ts
|
|
264
|
-
import fs3 from "fs/promises";
|
|
265
|
-
async function insertImportLine(filePath, importStatement) {
|
|
266
|
-
const content = await fs3.readFile(filePath, "utf-8");
|
|
267
|
-
const importPath = importStatement.match(/["']([^"']+)["']/)?.[1];
|
|
268
|
-
if (importPath) {
|
|
269
|
-
const escaped = importPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
270
|
-
if (new RegExp(`@import\\s+["']${escaped}["']`).test(content)) return;
|
|
271
|
-
}
|
|
272
|
-
const lines = content.split("\n");
|
|
273
|
-
let lastImportIndex = -1;
|
|
274
|
-
for (let i = 0; i < lines.length; i++) {
|
|
275
|
-
if (lines[i].trim().startsWith("@import")) {
|
|
276
|
-
lastImportIndex = i;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
if (lastImportIndex >= 0) {
|
|
280
|
-
lines.splice(lastImportIndex + 1, 0, importStatement);
|
|
281
|
-
} else {
|
|
282
|
-
lines.unshift(importStatement);
|
|
283
|
-
}
|
|
284
|
-
await fs3.writeFile(filePath, lines.join("\n"), "utf-8");
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// src/utils/patch-css-vars.ts
|
|
288
|
-
import fs4 from "fs";
|
|
289
|
-
function removeCssImport(filePath, importPath) {
|
|
290
|
-
const content = fs4.readFileSync(filePath, "utf-8");
|
|
291
|
-
const pattern = new RegExp(
|
|
292
|
-
`@import\\s+['"]${escapeRegex(importPath)}['"]\\s*;?\\n?`
|
|
293
|
-
);
|
|
294
|
-
if (!pattern.test(content)) return false;
|
|
295
|
-
fs4.writeFileSync(filePath, content.replace(pattern, ""), "utf-8");
|
|
296
|
-
return true;
|
|
297
|
-
}
|
|
298
|
-
function escapeRegex(str) {
|
|
299
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// src/utils/write-files.ts
|
|
303
|
-
import fs5 from "fs";
|
|
304
|
-
import path4 from "path";
|
|
305
|
-
import { confirm } from "@inquirer/prompts";
|
|
306
|
-
async function confirmOverwrite(relativePath) {
|
|
307
|
-
if (!process.stdin.isTTY) {
|
|
308
|
-
console.log(
|
|
309
|
-
` Skipping ${relativePath} (already exists, use --overwrite to replace)`
|
|
310
|
-
);
|
|
311
|
-
return false;
|
|
312
|
-
}
|
|
313
|
-
return confirm({
|
|
314
|
-
message: `${relativePath} already exists. Overwrite?`,
|
|
315
|
-
default: false
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
|
-
async function writeComponentFiles(items, config, projectRoot, options = {}) {
|
|
319
|
-
const written = [];
|
|
320
|
-
const skipped = [];
|
|
321
|
-
for (const item of items) {
|
|
322
|
-
for (const file of item.files ?? []) {
|
|
323
|
-
const outputPath = resolveOutputPath(config, projectRoot, file);
|
|
324
|
-
const relativePath = path4.relative(projectRoot, outputPath);
|
|
325
|
-
if (fs5.existsSync(outputPath)) {
|
|
326
|
-
const existing = fs5.readFileSync(outputPath, "utf-8");
|
|
327
|
-
if (existing === file.content) {
|
|
328
|
-
skipped.push({ path: relativePath, reason: "unchanged" });
|
|
329
|
-
continue;
|
|
330
|
-
}
|
|
331
|
-
if (!options.overwrite) {
|
|
332
|
-
const confirmed = await confirmOverwrite(relativePath);
|
|
333
|
-
if (!confirmed) {
|
|
334
|
-
skipped.push({ path: relativePath, reason: "declined" });
|
|
335
|
-
continue;
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
const dir = path4.dirname(outputPath);
|
|
340
|
-
fs5.mkdirSync(dir, { recursive: true });
|
|
341
|
-
fs5.writeFileSync(outputPath, file.content ?? "", "utf-8");
|
|
342
|
-
written.push(relativePath);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
return { written, skipped };
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
// src/utils/write-styles.ts
|
|
349
|
-
var INDEX_TARGET = "ui/index.css";
|
|
350
|
-
var TYPOGRAPHY_TARGET = "ui/typography.css";
|
|
351
|
-
var PROTECTED_TARGETS = /* @__PURE__ */ new Set([TYPOGRAPHY_TARGET, "ui/colors.css"]);
|
|
352
|
-
var LEGACY_FILENAME = "stera-ui.css";
|
|
353
|
-
var LEGACY_IMPORT = "./stera-ui.css";
|
|
354
|
-
function partitionImports(content) {
|
|
355
|
-
const lines = content.split("\n");
|
|
356
|
-
const hoisted = [];
|
|
357
|
-
const kept = [];
|
|
358
|
-
for (const line of lines) {
|
|
359
|
-
const specifier = line.trim().match(/^@import\s+["'](.+?)["']/)?.[1];
|
|
360
|
-
if (specifier === void 0) {
|
|
361
|
-
kept.push(line);
|
|
362
|
-
} else if (specifier.startsWith("./") || specifier.startsWith("../")) {
|
|
363
|
-
kept.push(line);
|
|
364
|
-
} else if (specifier !== "tailwindcss") {
|
|
365
|
-
hoisted.push(line.trim());
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
const cleaned = kept.join("\n").replace(/\n{3,}/g, "\n\n").replace(/^\n+/, "");
|
|
369
|
-
return { content: cleaned, hoisted };
|
|
370
|
-
}
|
|
371
|
-
async function migrateLegacyStyles(config, cwd) {
|
|
372
|
-
const globalsPath = path5.resolve(cwd, config.css);
|
|
373
|
-
const legacyPath = path5.join(path5.dirname(globalsPath), LEGACY_FILENAME);
|
|
374
|
-
if (!existsSync(legacyPath)) return null;
|
|
375
|
-
await fs6.rm(legacyPath);
|
|
376
|
-
if (existsSync(globalsPath)) {
|
|
377
|
-
removeCssImport(globalsPath, LEGACY_IMPORT);
|
|
378
|
-
await insertImportLine(globalsPath, `@import "./${INDEX_TARGET}";`);
|
|
379
|
-
}
|
|
380
|
-
return legacyPath;
|
|
381
|
-
}
|
|
382
|
-
async function writeStyles(config, cwd, options = {}) {
|
|
383
|
-
const item = await fetchRegistryItem("globals");
|
|
384
|
-
const cssDir = path5.dirname(path5.resolve(cwd, config.css));
|
|
385
|
-
const written = [];
|
|
386
|
-
const skipped = [];
|
|
387
|
-
let extraImports = [];
|
|
388
|
-
for (const file of item.files ?? []) {
|
|
389
|
-
const target = file.target ?? path5.basename(file.path);
|
|
390
|
-
const outputPath = path5.join(cssDir, target);
|
|
391
|
-
let content = file.content ?? "";
|
|
392
|
-
if (target === INDEX_TARGET) {
|
|
393
|
-
const partitioned = partitionImports(content);
|
|
394
|
-
content = partitioned.content;
|
|
395
|
-
extraImports = partitioned.hoisted;
|
|
396
|
-
}
|
|
397
|
-
if (PROTECTED_TARGETS.has(target) && existsSync(outputPath)) {
|
|
398
|
-
const existing = await fs6.readFile(outputPath, "utf-8");
|
|
399
|
-
if (existing === content) {
|
|
400
|
-
skipped.push({ path: target, reason: "unchanged" });
|
|
401
|
-
continue;
|
|
402
|
-
}
|
|
403
|
-
if (!options.overwrite && !await confirmOverwrite(target)) {
|
|
404
|
-
skipped.push({ path: target, reason: "declined" });
|
|
405
|
-
continue;
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
await fs6.mkdir(path5.dirname(outputPath), { recursive: true });
|
|
409
|
-
await fs6.writeFile(outputPath, content, "utf-8");
|
|
410
|
-
written.push(target);
|
|
411
|
-
}
|
|
412
|
-
return {
|
|
413
|
-
indexPath: path5.join(cssDir, INDEX_TARGET),
|
|
414
|
-
typographyPath: path5.join(cssDir, TYPOGRAPHY_TARGET),
|
|
415
|
-
extraImports,
|
|
416
|
-
dependencies: item.dependencies ?? [],
|
|
417
|
-
written,
|
|
418
|
-
skipped
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// src/utils/install-deps.ts
|
|
423
|
-
import fs7 from "fs";
|
|
424
|
-
import path6 from "path";
|
|
425
|
-
import { detect } from "@antfu/ni";
|
|
426
|
-
import { execa } from "execa";
|
|
427
|
-
|
|
428
|
-
// src/utils/format.ts
|
|
429
|
-
var BRAND = "\x1B[38;2;166;244;48m";
|
|
430
|
-
var DIM = "\x1B[2m";
|
|
431
|
-
var RESET = "\x1B[0m";
|
|
432
|
-
var enabled = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
433
|
-
var wrap = (code, text) => enabled ? `${code}${text}${RESET}` : text;
|
|
434
|
-
var brand = (text) => wrap(BRAND, text);
|
|
435
|
-
var dim = (text) => wrap(DIM, text);
|
|
436
|
-
var LOGO = brand("\u273B");
|
|
437
|
-
var CHECK = brand("\u2713");
|
|
438
|
-
var CROSS = "\u2717";
|
|
439
|
-
var WARN = "!";
|
|
440
|
-
|
|
441
|
-
// src/utils/spinner.ts
|
|
442
|
-
var FRAMES = ["\u2022", "\u2726", "\u2736", "\u273B"].map(brand);
|
|
443
|
-
var INTERVAL_MS = 150;
|
|
444
|
-
function createSpinner(text) {
|
|
445
|
-
const isTTY = process.stdout.isTTY;
|
|
446
|
-
if (!isTTY) {
|
|
447
|
-
process.stdout.write(` ${text}...
|
|
448
|
-
`);
|
|
449
|
-
return {
|
|
450
|
-
succeed(msg) {
|
|
451
|
-
process.stdout.write(` ${CHECK} ${msg}
|
|
452
|
-
`);
|
|
453
|
-
},
|
|
454
|
-
fail(msg) {
|
|
455
|
-
process.stdout.write(` ${CROSS} ${msg}
|
|
456
|
-
`);
|
|
457
|
-
}
|
|
458
|
-
};
|
|
459
|
-
}
|
|
460
|
-
let frameIndex = 0;
|
|
461
|
-
const interval = setInterval(() => {
|
|
462
|
-
process.stdout.write(`\r ${FRAMES[frameIndex % FRAMES.length]} ${text}`);
|
|
463
|
-
frameIndex++;
|
|
464
|
-
}, INTERVAL_MS);
|
|
465
|
-
const clear = () => {
|
|
466
|
-
clearInterval(interval);
|
|
467
|
-
process.stdout.write("\r" + " ".repeat(text.length + 6) + "\r");
|
|
468
|
-
};
|
|
469
|
-
return {
|
|
470
|
-
succeed(msg) {
|
|
471
|
-
clear();
|
|
472
|
-
process.stdout.write(` ${CHECK} ${msg}
|
|
473
|
-
`);
|
|
474
|
-
},
|
|
475
|
-
fail(msg) {
|
|
476
|
-
clear();
|
|
477
|
-
process.stdout.write(` ${CROSS} ${msg}
|
|
478
|
-
`);
|
|
479
|
-
}
|
|
480
|
-
};
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
// src/utils/install-deps.ts
|
|
484
|
-
async function detectPackageManager(cwd) {
|
|
485
|
-
const detected = await detect({ programmatic: true, cwd });
|
|
486
|
-
if (detected === "pnpm@6" || detected === "pnpm") return "pnpm";
|
|
487
|
-
if (detected === "yarn@berry" || detected === "yarn") return "yarn";
|
|
488
|
-
if (detected === "bun") return "bun";
|
|
489
|
-
if (detected === "npm") return "npm";
|
|
490
|
-
const userAgent = process.env.npm_config_user_agent || "";
|
|
491
|
-
if (userAgent.startsWith("pnpm")) return "pnpm";
|
|
492
|
-
if (userAgent.startsWith("yarn")) return "yarn";
|
|
493
|
-
if (userAgent.startsWith("bun")) return "bun";
|
|
494
|
-
return "npm";
|
|
495
|
-
}
|
|
496
|
-
function getInstallArgs(pm, deps) {
|
|
497
|
-
switch (pm) {
|
|
498
|
-
case "pnpm":
|
|
499
|
-
return ["pnpm", ["add", ...deps]];
|
|
500
|
-
case "yarn":
|
|
501
|
-
return ["yarn", ["add", ...deps]];
|
|
502
|
-
case "bun":
|
|
503
|
-
return ["bun", ["add", ...deps]];
|
|
504
|
-
default:
|
|
505
|
-
return ["npm", ["install", ...deps]];
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
async function installDependencies(deps, cwd) {
|
|
509
|
-
if (deps.length === 0) return;
|
|
510
|
-
const pkgPath = path6.join(cwd, "package.json");
|
|
511
|
-
let existing = /* @__PURE__ */ new Set();
|
|
512
|
-
if (fs7.existsSync(pkgPath)) {
|
|
513
|
-
try {
|
|
514
|
-
const pkg = JSON.parse(fs7.readFileSync(pkgPath, "utf-8"));
|
|
515
|
-
existing = new Set(
|
|
516
|
-
Object.keys({ ...pkg.dependencies, ...pkg.devDependencies })
|
|
517
|
-
);
|
|
518
|
-
} catch {
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
const toInstall = deps.filter((dep) => !existing.has(dep));
|
|
522
|
-
if (toInstall.length === 0) return;
|
|
523
|
-
const pm = await detectPackageManager(cwd);
|
|
524
|
-
const [cmd, args] = getInstallArgs(pm, toInstall);
|
|
525
|
-
const spinner = createSpinner(`Installing with ${pm}`);
|
|
526
|
-
try {
|
|
527
|
-
await execa(cmd, args, { cwd });
|
|
528
|
-
spinner.succeed(`Installed ${toInstall.join(", ")} with ${pm}`);
|
|
529
|
-
} catch (error) {
|
|
530
|
-
spinner.fail(`Install failed`);
|
|
531
|
-
if (error instanceof Error && "stdout" in error && typeof error.stdout === "string" && error.stdout.includes("ERR_PNPM_ADDING_TO_ROOT")) {
|
|
532
|
-
throw new Error(
|
|
533
|
-
`Cannot install dependencies at a pnpm workspace root.
|
|
534
|
-
Run stera-ui init from within a specific workspace package instead,
|
|
535
|
-
or use "pnpm add -w <package>" manually to install at the root.`
|
|
536
|
-
);
|
|
537
|
-
}
|
|
538
|
-
throw error;
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
// src/utils/update-fonts.ts
|
|
543
|
-
import { existsSync as existsSync2, promises as fs8 } from "fs";
|
|
544
|
-
import path7 from "path";
|
|
545
|
-
import {
|
|
546
|
-
Project,
|
|
547
|
-
ScriptKind,
|
|
548
|
-
SyntaxKind,
|
|
549
|
-
VariableDeclarationKind
|
|
550
|
-
} from "ts-morph";
|
|
551
|
-
var ROOT_FONT_VARIABLES = /* @__PURE__ */ new Set([
|
|
552
|
-
"--font-sans",
|
|
553
|
-
"--font-serif",
|
|
554
|
-
"--font-mono"
|
|
555
|
-
]);
|
|
556
|
-
function massageTreeForFonts(fonts, project) {
|
|
557
|
-
const result = {
|
|
558
|
-
cssVars: {},
|
|
559
|
-
dependencies: [],
|
|
560
|
-
cssImports: []
|
|
561
|
-
};
|
|
562
|
-
if (fonts.length === 0) return result;
|
|
563
|
-
const isNext = project.framework === "next";
|
|
564
|
-
for (const font of fonts) {
|
|
565
|
-
if (isNext) {
|
|
566
|
-
result.cssVars[font.font.variable] = `var(${font.font.variable})`;
|
|
567
|
-
} else {
|
|
568
|
-
const fontName = font.name.replace(/^font-/, "");
|
|
569
|
-
const fontSourceDep = font.font.dependency ?? `@fontsource-variable/${fontName}`;
|
|
570
|
-
result.dependencies.push(fontSourceDep);
|
|
571
|
-
result.cssImports.push(`@import "${fontSourceDep}";`);
|
|
572
|
-
result.cssVars[font.font.variable] = font.font.family;
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
return result;
|
|
576
|
-
}
|
|
577
|
-
async function updateFonts(fonts, project, cwd, options = {}) {
|
|
578
|
-
if (fonts.length === 0) return;
|
|
579
|
-
if (project.framework !== "next") return;
|
|
580
|
-
const layoutPath = findLayoutFile(project, cwd);
|
|
581
|
-
if (!layoutPath) return;
|
|
582
|
-
if (options.silent) {
|
|
583
|
-
try {
|
|
584
|
-
const layoutContent = await fs8.readFile(layoutPath, "utf-8");
|
|
585
|
-
const updatedContent = transformLayoutFonts(layoutContent, fonts);
|
|
586
|
-
if (updatedContent !== layoutContent) {
|
|
587
|
-
await fs8.writeFile(layoutPath, updatedContent, "utf-8");
|
|
588
|
-
}
|
|
589
|
-
} catch {
|
|
590
|
-
}
|
|
591
|
-
return;
|
|
592
|
-
}
|
|
593
|
-
const spinner = createSpinner("Updating fonts");
|
|
594
|
-
try {
|
|
595
|
-
const layoutContent = await fs8.readFile(layoutPath, "utf-8");
|
|
596
|
-
const updatedContent = transformLayoutFonts(layoutContent, fonts);
|
|
597
|
-
if (updatedContent !== layoutContent) {
|
|
598
|
-
await fs8.writeFile(layoutPath, updatedContent, "utf-8");
|
|
599
|
-
}
|
|
600
|
-
spinner.succeed("Updated layout with font imports");
|
|
601
|
-
} catch {
|
|
602
|
-
spinner.fail("Failed to update fonts");
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
function findLayoutFile(project, cwd) {
|
|
606
|
-
if (!project.layoutFile) return null;
|
|
607
|
-
const fullPath = path7.join(cwd, project.layoutFile);
|
|
608
|
-
return existsSync2(fullPath) ? fullPath : null;
|
|
609
|
-
}
|
|
610
|
-
function transformLayoutFonts(input, fonts) {
|
|
611
|
-
const project = new Project({ compilerOptions: {} });
|
|
612
|
-
const sourceFile = project.createSourceFile("layout.tsx", input, {
|
|
613
|
-
scriptKind: ScriptKind.TSX
|
|
614
|
-
});
|
|
615
|
-
const googleFonts = fonts.filter((f) => f.font.provider === "google");
|
|
616
|
-
const fontVariableNames = [];
|
|
617
|
-
const fontUtilityClasses = [];
|
|
618
|
-
for (const font of googleFonts) {
|
|
619
|
-
const importName = font.font.import;
|
|
620
|
-
if (!importName) continue;
|
|
621
|
-
const existingImport = sourceFile.getImportDeclaration(
|
|
622
|
-
(decl) => decl.getModuleSpecifierValue() === "next/font/google"
|
|
623
|
-
);
|
|
624
|
-
let hasExistingImport = false;
|
|
625
|
-
if (existingImport) {
|
|
626
|
-
const namedImports = existingImport.getNamedImports();
|
|
627
|
-
hasExistingImport = namedImports.some(
|
|
628
|
-
(imp) => imp.getName() === importName
|
|
629
|
-
);
|
|
630
|
-
if (!hasExistingImport) {
|
|
631
|
-
existingImport.addNamedImport(importName);
|
|
632
|
-
}
|
|
633
|
-
} else {
|
|
634
|
-
sourceFile.addImportDeclaration({
|
|
635
|
-
moduleSpecifier: "next/font/google",
|
|
636
|
-
namedImports: [importName]
|
|
637
|
-
});
|
|
638
|
-
}
|
|
639
|
-
const varName = getFontVariableName(importName, font.font.variable);
|
|
640
|
-
const fontOptions = buildFontOptions(font);
|
|
641
|
-
const existingVarDecl = findFontVariableDeclaration(
|
|
642
|
-
sourceFile,
|
|
643
|
-
font.font.variable
|
|
644
|
-
);
|
|
645
|
-
if (hasExistingImport && !existingVarDecl && isRootFontVariable(font.font.variable)) {
|
|
646
|
-
continue;
|
|
647
|
-
}
|
|
648
|
-
if (existingVarDecl) {
|
|
649
|
-
existingVarDecl.setInitializer(`${importName}(${fontOptions})`);
|
|
650
|
-
if (existingVarDecl.getName() !== varName) {
|
|
651
|
-
existingVarDecl.rename(varName);
|
|
652
|
-
}
|
|
653
|
-
} else {
|
|
654
|
-
const insertPosition = findInsertPosition(sourceFile);
|
|
655
|
-
const statement = sourceFile.insertVariableStatement(insertPosition, {
|
|
656
|
-
declarationKind: VariableDeclarationKind.Const,
|
|
657
|
-
declarations: [
|
|
658
|
-
{
|
|
659
|
-
name: varName,
|
|
660
|
-
initializer: `${importName}(${fontOptions})`
|
|
661
|
-
}
|
|
662
|
-
]
|
|
663
|
-
});
|
|
664
|
-
statement.appendWhitespace("\n");
|
|
665
|
-
}
|
|
666
|
-
fontVariableNames.push(varName);
|
|
667
|
-
if (shouldApplyFontUtilityToHtml(font)) {
|
|
668
|
-
fontUtilityClasses.push(font.font.variable.replace("--", ""));
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
const fontFamilyClasses = /* @__PURE__ */ new Set(["font-sans", "font-serif", "font-mono"]);
|
|
672
|
-
const lastFontFamilyClass = [...fontUtilityClasses].reverse().find((cls) => fontFamilyClasses.has(cls));
|
|
673
|
-
const filteredUtilityClasses = fontUtilityClasses.filter(
|
|
674
|
-
(cls) => !fontFamilyClasses.has(cls)
|
|
675
|
-
);
|
|
676
|
-
if (lastFontFamilyClass) {
|
|
677
|
-
filteredUtilityClasses.unshift(lastFontFamilyClass);
|
|
678
|
-
}
|
|
679
|
-
if (fontVariableNames.length > 0) {
|
|
680
|
-
updateHtmlClassName(sourceFile, fontVariableNames, filteredUtilityClasses);
|
|
681
|
-
}
|
|
682
|
-
return sourceFile.getFullText();
|
|
683
|
-
}
|
|
684
|
-
function buildFontOptions(font) {
|
|
685
|
-
const options = {};
|
|
686
|
-
if (font.font.subsets?.length) options.subsets = font.font.subsets;
|
|
687
|
-
if (font.font.weight?.length) options.weight = font.font.weight;
|
|
688
|
-
options.variable = font.font.variable;
|
|
689
|
-
return JSON.stringify(options).replace(/"([^"]+)":/g, "$1:").replace(/"/g, "'");
|
|
690
|
-
}
|
|
691
|
-
function isRootFontVariable(variable) {
|
|
692
|
-
return ROOT_FONT_VARIABLES.has(variable);
|
|
693
|
-
}
|
|
694
|
-
function shouldApplyFontUtilityToHtml(font) {
|
|
695
|
-
return !font.font.selector && isRootFontVariable(font.font.variable);
|
|
696
|
-
}
|
|
697
|
-
function getFontVariableName(importName, variable) {
|
|
698
|
-
const baseName = toCamelCase(importName);
|
|
699
|
-
if (isRootFontVariable(variable)) return baseName;
|
|
700
|
-
return `${baseName}${toPascalCase(variable.replace(/^--font-/, ""))}`;
|
|
701
|
-
}
|
|
702
|
-
function toCamelCase(str) {
|
|
703
|
-
return str.split("_").map(
|
|
704
|
-
(part, index) => index === 0 ? part.toLowerCase() : part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()
|
|
705
|
-
).join("");
|
|
706
|
-
}
|
|
707
|
-
function toPascalCase(str) {
|
|
708
|
-
return str.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
709
|
-
}
|
|
710
|
-
function findFontVariableDeclaration(sourceFile, variable) {
|
|
711
|
-
for (const statement of sourceFile.getVariableStatements()) {
|
|
712
|
-
for (const declaration of statement.getDeclarations()) {
|
|
713
|
-
const initializer = declaration.getInitializer();
|
|
714
|
-
if (!initializer || initializer.getKind() !== SyntaxKind.CallExpression)
|
|
715
|
-
continue;
|
|
716
|
-
const args = initializer.getArguments();
|
|
717
|
-
if (args.length === 0) continue;
|
|
718
|
-
const argText = args[0].getText();
|
|
719
|
-
if (argText.includes("variable:") && argText.includes(variable)) {
|
|
720
|
-
return declaration;
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
return null;
|
|
725
|
-
}
|
|
726
|
-
function findInsertPosition(sourceFile) {
|
|
727
|
-
const imports = sourceFile.getImportDeclarations();
|
|
728
|
-
if (imports.length > 0) {
|
|
729
|
-
return imports[imports.length - 1].getChildIndex() + 1;
|
|
730
|
-
}
|
|
731
|
-
return 0;
|
|
732
|
-
}
|
|
733
|
-
function updateHtmlClassName(sourceFile, fontVariableNames, fontUtilityClasses) {
|
|
734
|
-
const jsxElements = sourceFile.getDescendantsOfKind(
|
|
735
|
-
SyntaxKind.JsxOpeningElement
|
|
736
|
-
);
|
|
737
|
-
for (const element of jsxElements) {
|
|
738
|
-
if (element.getTagNameNode().getText() !== "html") continue;
|
|
739
|
-
const newUtilityClasses = fontUtilityClasses.map((cls) => `"${cls}"`);
|
|
740
|
-
const newVarExpressions = fontVariableNames.map(
|
|
741
|
-
(name) => `${name}.variable`
|
|
742
|
-
);
|
|
743
|
-
const allNewArgs = [...newUtilityClasses, ...newVarExpressions];
|
|
744
|
-
const classNameAttr = element.getAttribute("className");
|
|
745
|
-
if (!classNameAttr) {
|
|
746
|
-
element.addAttribute({
|
|
747
|
-
name: "className",
|
|
748
|
-
initializer: `{cn(${allNewArgs.join(", ")})}`
|
|
749
|
-
});
|
|
750
|
-
ensureCnImport(sourceFile);
|
|
751
|
-
return;
|
|
752
|
-
}
|
|
753
|
-
if (classNameAttr.getKind() !== SyntaxKind.JsxAttribute) return;
|
|
754
|
-
const jsxAttr = classNameAttr.asKindOrThrow(SyntaxKind.JsxAttribute);
|
|
755
|
-
const initializer = jsxAttr.getInitializer();
|
|
756
|
-
if (!initializer) return;
|
|
757
|
-
if (initializer.getKind() === SyntaxKind.StringLiteral) {
|
|
758
|
-
const currentValue = initializer.getText().slice(1, -1);
|
|
759
|
-
ensureCnImport(sourceFile);
|
|
760
|
-
jsxAttr.setInitializer(
|
|
761
|
-
`{cn("${currentValue}", ${allNewArgs.join(", ")})}`
|
|
762
|
-
);
|
|
763
|
-
} else if (initializer.getKind() === SyntaxKind.JsxExpression) {
|
|
764
|
-
const jsxExpr = initializer.asKindOrThrow(SyntaxKind.JsxExpression);
|
|
765
|
-
const expr = jsxExpr.getExpression();
|
|
766
|
-
if (!expr) return;
|
|
767
|
-
const exprText = expr.getText();
|
|
768
|
-
if (exprText.startsWith("cn(")) {
|
|
769
|
-
const hasAllFontVars = newVarExpressions.every(
|
|
770
|
-
(v) => exprText.includes(v)
|
|
771
|
-
);
|
|
772
|
-
const hasAllUtilityClasses = fontUtilityClasses.every(
|
|
773
|
-
(cls) => exprText.includes(`"${cls}"`)
|
|
774
|
-
);
|
|
775
|
-
if (hasAllFontVars && hasAllUtilityClasses) continue;
|
|
776
|
-
let cleanedExpr = exprText;
|
|
777
|
-
for (const varExpr of newVarExpressions) {
|
|
778
|
-
cleanedExpr = cleanedExpr.replace(
|
|
779
|
-
new RegExp(`,?\\s*${varExpr.replace(".", "\\.")}`, "g"),
|
|
780
|
-
""
|
|
781
|
-
);
|
|
782
|
-
}
|
|
783
|
-
cleanedExpr = cleanedExpr.replace(/cn\(\s*,/, "cn(");
|
|
784
|
-
const newExpr = cleanedExpr.replace(
|
|
785
|
-
/\)$/,
|
|
786
|
-
`, ${allNewArgs.join(", ")})`
|
|
787
|
-
);
|
|
788
|
-
jsxExpr.replaceWithText(`{${newExpr}}`);
|
|
789
|
-
} else if (/^\w+\.variable$/.test(exprText)) {
|
|
790
|
-
ensureCnImport(sourceFile);
|
|
791
|
-
jsxExpr.replaceWithText(`{cn(${allNewArgs.join(", ")})}`);
|
|
792
|
-
} else if (exprText.startsWith("`") && exprText.endsWith("`")) {
|
|
793
|
-
const cnArgs = parseTemplateLiteralToCnArgs(exprText);
|
|
794
|
-
ensureCnImport(sourceFile);
|
|
795
|
-
const allNewArgsSet = new Set(allNewArgs);
|
|
796
|
-
const fontFamilyLiterals = new Set(
|
|
797
|
-
["font-sans", "font-serif", "font-mono"].map((c) => `"${c}"`)
|
|
798
|
-
);
|
|
799
|
-
const cleanedCnArgs = cnArgs.filter(
|
|
800
|
-
(arg) => !allNewArgsSet.has(arg) && !fontFamilyLiterals.has(arg)
|
|
801
|
-
);
|
|
802
|
-
jsxExpr.replaceWithText(
|
|
803
|
-
`{cn(${[...cleanedCnArgs, ...allNewArgs].join(", ")})}`
|
|
804
|
-
);
|
|
805
|
-
} else {
|
|
806
|
-
ensureCnImport(sourceFile);
|
|
807
|
-
jsxExpr.replaceWithText(`{cn(${exprText}, ${allNewArgs.join(", ")})}`);
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
function ensureCnImport(sourceFile) {
|
|
813
|
-
const existingImport = sourceFile.getImportDeclaration(
|
|
814
|
-
(decl) => decl.getNamedImports().some((imp) => imp.getName() === "cn")
|
|
815
|
-
);
|
|
816
|
-
if (!existingImport) {
|
|
817
|
-
const utilsImport = sourceFile.getImportDeclaration(
|
|
818
|
-
(decl) => decl.getModuleSpecifierValue().includes("/lib/utils")
|
|
819
|
-
);
|
|
820
|
-
if (utilsImport) {
|
|
821
|
-
const namedImports = utilsImport.getNamedImports();
|
|
822
|
-
if (!namedImports.some((imp) => imp.getName() === "cn")) {
|
|
823
|
-
utilsImport.addNamedImport("cn");
|
|
824
|
-
}
|
|
825
|
-
} else {
|
|
826
|
-
sourceFile.addImportDeclaration({
|
|
827
|
-
moduleSpecifier: "@/lib/utils",
|
|
828
|
-
namedImports: ["cn"]
|
|
829
|
-
});
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
function parseTemplateLiteralToCnArgs(templateLiteral) {
|
|
834
|
-
const staticArgs = [];
|
|
835
|
-
const variableArgs = [];
|
|
836
|
-
const content = templateLiteral.slice(1, -1);
|
|
837
|
-
const parts = content.split(/(\$\{[^}]+\})/);
|
|
838
|
-
for (const part of parts) {
|
|
839
|
-
if (!part) continue;
|
|
840
|
-
if (part.startsWith("${") && part.endsWith("}")) {
|
|
841
|
-
const expr = part.slice(2, -1).trim();
|
|
842
|
-
if (expr) variableArgs.push(expr);
|
|
843
|
-
} else {
|
|
844
|
-
const staticParts = part.trim().split(/\s+/).filter(Boolean);
|
|
845
|
-
for (const staticPart of staticParts) {
|
|
846
|
-
staticArgs.push(`"${staticPart}"`);
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
return [...staticArgs, ...variableArgs];
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
export {
|
|
854
|
-
applyTransforms,
|
|
855
|
-
transformImports,
|
|
856
|
-
resolveOutputPath,
|
|
857
|
-
CONFIG_FILE,
|
|
858
|
-
LEGACY_CONFIG_FILE,
|
|
859
|
-
findConfigPath,
|
|
860
|
-
loadConfig2 as loadConfig,
|
|
861
|
-
detectProject,
|
|
862
|
-
detectAliasPrefix,
|
|
863
|
-
insertImportLine,
|
|
864
|
-
writeComponentFiles,
|
|
865
|
-
INDEX_TARGET,
|
|
866
|
-
TYPOGRAPHY_TARGET,
|
|
867
|
-
migrateLegacyStyles,
|
|
868
|
-
writeStyles,
|
|
869
|
-
brand,
|
|
870
|
-
dim,
|
|
871
|
-
LOGO,
|
|
872
|
-
CHECK,
|
|
873
|
-
CROSS,
|
|
874
|
-
WARN,
|
|
875
|
-
createSpinner,
|
|
876
|
-
detectPackageManager,
|
|
877
|
-
installDependencies,
|
|
878
|
-
massageTreeForFonts,
|
|
879
|
-
updateFonts
|
|
880
|
-
};
|