@frontic/ui 0.3.0 → 0.5.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/api-DmrBFC4C.d.ts +313 -0
- package/dist/api-DmrBFC4C.d.ts.map +1 -0
- package/dist/index-Cd5RBhtD.d.ts +1480 -0
- package/dist/index-Cd5RBhtD.d.ts.map +1 -0
- package/dist/index.d.ts +4 -6
- package/dist/index.js +2411 -555
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.d.ts +46 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +5 -0
- package/dist/mcp-CxUIP4Qu.js +304 -0
- package/dist/mcp-CxUIP4Qu.js.map +1 -0
- package/dist/registry/index.d.ts +113 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +4 -0
- package/dist/registry-CgE-Q6HO.js +2638 -0
- package/dist/registry-CgE-Q6HO.js.map +1 -0
- package/dist/schema/index.d.ts +2 -0
- package/dist/schema/index.js +3 -0
- package/dist/schema-DgHN-d0T.js +155 -0
- package/dist/schema-DgHN-d0T.js.map +1 -0
- package/package.json +68 -14
- package/dist/bin/frontic-ui.js +0 -590
- package/dist/bin/frontic-ui.js.map +0 -1
|
@@ -0,0 +1,2638 @@
|
|
|
1
|
+
import { _ as registrySchema, a as registryBaseColorSchema, b as stylesSchema, c as registryIndexSchema, f as registryItemFileSchema, g as registryResolvedItemsTreeSchema, i as registriesIndexSchema, n as iconsSchema, p as registryItemSchema, r as rawConfigSchema, s as registryConfigSchema, t as configSchema, x as workspaceConfigSchema, y as searchResultsSchema } from "./schema-DgHN-d0T.js";
|
|
2
|
+
import path, { basename } from "pathe";
|
|
3
|
+
import prompts from "prompts";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { existsSync, promises, statSync } from "fs";
|
|
6
|
+
import deepmerge from "deepmerge";
|
|
7
|
+
import fsExtra from "fs-extra";
|
|
8
|
+
import { createPathsMatcher, getTsconfig } from "get-tsconfig";
|
|
9
|
+
import { coerce } from "semver";
|
|
10
|
+
import { glob } from "tinyglobby";
|
|
11
|
+
import { loadConfig } from "c12";
|
|
12
|
+
import { colors } from "consola/utils";
|
|
13
|
+
import consola from "consola";
|
|
14
|
+
import ora from "ora";
|
|
15
|
+
import { homedir, tmpdir } from "os";
|
|
16
|
+
import { Project, QuoteKind, ScriptKind, SyntaxKind } from "ts-morph";
|
|
17
|
+
import { transform } from "vue-metamorph";
|
|
18
|
+
import { transform as transform$1 } from "@unovue/detypes";
|
|
19
|
+
import { ofetch } from "ofetch";
|
|
20
|
+
import { ProxyAgent } from "undici";
|
|
21
|
+
import { createHash } from "crypto";
|
|
22
|
+
import objectToString from "stringify-object";
|
|
23
|
+
import fuzzysort from "fuzzysort";
|
|
24
|
+
|
|
25
|
+
//#region src/utils/frameworks.ts
|
|
26
|
+
const FRAMEWORKS = {
|
|
27
|
+
vite: {
|
|
28
|
+
name: "vite",
|
|
29
|
+
label: "Vite",
|
|
30
|
+
links: {
|
|
31
|
+
installation: "https://shadcn-vue.com/docs/installation/vite",
|
|
32
|
+
tailwind: "https://tailwindcss.com/docs/guides/vite"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
nuxt3: {
|
|
36
|
+
name: "nuxt3",
|
|
37
|
+
label: "Nuxt 3",
|
|
38
|
+
links: {
|
|
39
|
+
installation: "https://shadcn-vue.com/docs/installation/nuxt",
|
|
40
|
+
tailwind: "https://tailwindcss.com/docs/guides/nuxtjs"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
nuxt4: {
|
|
44
|
+
name: "nuxt4",
|
|
45
|
+
label: "Nuxt 4",
|
|
46
|
+
links: {
|
|
47
|
+
installation: "https://shadcn-vue.com/docs/installation/nuxt",
|
|
48
|
+
tailwind: "https://tailwindcss.com/docs/guides/nuxtjs"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
astro: {
|
|
52
|
+
name: "astro",
|
|
53
|
+
label: "Astro",
|
|
54
|
+
links: {
|
|
55
|
+
installation: "https://shadcn-vue.com/docs/installation/astro",
|
|
56
|
+
tailwind: "https://tailwindcss.com/docs/guides/astro"
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
laravel: {
|
|
60
|
+
name: "laravel",
|
|
61
|
+
label: "Laravel",
|
|
62
|
+
links: {
|
|
63
|
+
installation: "https://shadcn-vue.com/docs/installation/laravel",
|
|
64
|
+
tailwind: "https://tailwindcss.com/docs/guides/laravel"
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
manual: {
|
|
68
|
+
name: "manual",
|
|
69
|
+
label: "Manual",
|
|
70
|
+
links: {
|
|
71
|
+
installation: "https://shadcn-vue.com/docs/installation/manual",
|
|
72
|
+
tailwind: "https://tailwindcss.com/docs/installation"
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
inertia: {
|
|
76
|
+
name: "inertia",
|
|
77
|
+
label: "Inertia",
|
|
78
|
+
links: {
|
|
79
|
+
installation: "https://shadcn-vue.com/docs/installation/manual",
|
|
80
|
+
tailwind: "https://tailwindcss.com/docs/installation"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region src/registry/constants.ts
|
|
87
|
+
const REGISTRY_URL = process.env.REGISTRY_URL ?? "https://registry.frontic.io/r";
|
|
88
|
+
const FALLBACK_STYLE = "new-york-v4";
|
|
89
|
+
const BASE_COLORS = [
|
|
90
|
+
{
|
|
91
|
+
name: "neutral",
|
|
92
|
+
label: "Neutral"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: "gray",
|
|
96
|
+
label: "Gray"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: "zinc",
|
|
100
|
+
label: "Zinc"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: "stone",
|
|
104
|
+
label: "Stone"
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: "slate",
|
|
108
|
+
label: "Slate"
|
|
109
|
+
}
|
|
110
|
+
];
|
|
111
|
+
const BUILTIN_REGISTRIES = { "@shadcn": `${REGISTRY_URL}/styles/{style}/{name}.json` };
|
|
112
|
+
const DEPRECATED_COMPONENTS = [{
|
|
113
|
+
name: "toast",
|
|
114
|
+
deprecatedBy: "sonner",
|
|
115
|
+
message: "The toast component is deprecated. Use the sonner component instead."
|
|
116
|
+
}, {
|
|
117
|
+
name: "toaster",
|
|
118
|
+
deprecatedBy: "sonner",
|
|
119
|
+
message: "The toaster component is deprecated. Use the sonner component instead."
|
|
120
|
+
}];
|
|
121
|
+
|
|
122
|
+
//#endregion
|
|
123
|
+
//#region src/utils/highlighter.ts
|
|
124
|
+
const highlighter = {
|
|
125
|
+
error: colors.red,
|
|
126
|
+
warn: colors.yellow,
|
|
127
|
+
info: colors.cyan,
|
|
128
|
+
success: colors.green
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
//#endregion
|
|
132
|
+
//#region src/registry/errors.ts
|
|
133
|
+
const RegistryErrorCode = {
|
|
134
|
+
NETWORK_ERROR: "NETWORK_ERROR",
|
|
135
|
+
NOT_FOUND: "NOT_FOUND",
|
|
136
|
+
UNAUTHORIZED: "UNAUTHORIZED",
|
|
137
|
+
FORBIDDEN: "FORBIDDEN",
|
|
138
|
+
FETCH_ERROR: "FETCH_ERROR",
|
|
139
|
+
NOT_CONFIGURED: "NOT_CONFIGURED",
|
|
140
|
+
INVALID_CONFIG: "INVALID_CONFIG",
|
|
141
|
+
MISSING_ENV_VARS: "MISSING_ENV_VARS",
|
|
142
|
+
LOCAL_FILE_ERROR: "LOCAL_FILE_ERROR",
|
|
143
|
+
PARSE_ERROR: "PARSE_ERROR",
|
|
144
|
+
VALIDATION_ERROR: "VALIDATION_ERROR",
|
|
145
|
+
UNKNOWN_ERROR: "UNKNOWN_ERROR"
|
|
146
|
+
};
|
|
147
|
+
var RegistryError = class extends Error {
|
|
148
|
+
code;
|
|
149
|
+
statusCode;
|
|
150
|
+
context;
|
|
151
|
+
suggestion;
|
|
152
|
+
timestamp;
|
|
153
|
+
cause;
|
|
154
|
+
constructor(message, options = {}) {
|
|
155
|
+
super(message);
|
|
156
|
+
this.name = "RegistryError";
|
|
157
|
+
this.code = options.code || RegistryErrorCode.UNKNOWN_ERROR;
|
|
158
|
+
this.statusCode = options.statusCode;
|
|
159
|
+
this.cause = options.cause;
|
|
160
|
+
this.context = options.context;
|
|
161
|
+
this.suggestion = options.suggestion;
|
|
162
|
+
this.timestamp = /* @__PURE__ */ new Date();
|
|
163
|
+
if (Error.captureStackTrace) Error.captureStackTrace(this, this.constructor);
|
|
164
|
+
}
|
|
165
|
+
toJSON() {
|
|
166
|
+
return {
|
|
167
|
+
name: this.name,
|
|
168
|
+
message: this.message,
|
|
169
|
+
code: this.code,
|
|
170
|
+
statusCode: this.statusCode,
|
|
171
|
+
context: this.context,
|
|
172
|
+
suggestion: this.suggestion,
|
|
173
|
+
timestamp: this.timestamp,
|
|
174
|
+
stack: this.stack
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
var RegistryNotFoundError = class extends RegistryError {
|
|
179
|
+
constructor(url, cause) {
|
|
180
|
+
const message = `The item at ${url} was not found. It may not exist at the registry.`;
|
|
181
|
+
super(message, {
|
|
182
|
+
code: RegistryErrorCode.NOT_FOUND,
|
|
183
|
+
statusCode: 404,
|
|
184
|
+
cause,
|
|
185
|
+
context: { url },
|
|
186
|
+
suggestion: "Check if the item name is correct and the registry URL is accessible."
|
|
187
|
+
});
|
|
188
|
+
this.url = url;
|
|
189
|
+
this.name = "RegistryNotFoundError";
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
var RegistryUnauthorizedError = class extends RegistryError {
|
|
193
|
+
constructor(url, cause) {
|
|
194
|
+
const message = `You are not authorized to access the item at ${url}. If this is a remote registry, you may need to authenticate.`;
|
|
195
|
+
super(message, {
|
|
196
|
+
code: RegistryErrorCode.UNAUTHORIZED,
|
|
197
|
+
statusCode: 401,
|
|
198
|
+
cause,
|
|
199
|
+
context: { url },
|
|
200
|
+
suggestion: "Check your authentication credentials and environment variables."
|
|
201
|
+
});
|
|
202
|
+
this.url = url;
|
|
203
|
+
this.name = "RegistryUnauthorizedError";
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
var RegistryForbiddenError = class extends RegistryError {
|
|
207
|
+
constructor(url, cause) {
|
|
208
|
+
const message = `You are not authorized to access the item at ${url}. If this is a remote registry, you may need to authenticate.`;
|
|
209
|
+
super(message, {
|
|
210
|
+
code: RegistryErrorCode.FORBIDDEN,
|
|
211
|
+
statusCode: 403,
|
|
212
|
+
cause,
|
|
213
|
+
context: { url },
|
|
214
|
+
suggestion: "Check your authentication credentials and environment variables."
|
|
215
|
+
});
|
|
216
|
+
this.url = url;
|
|
217
|
+
this.name = "RegistryForbiddenError";
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
var RegistryFetchError = class extends RegistryError {
|
|
221
|
+
constructor(url, statusCode, responseBody, cause) {
|
|
222
|
+
const baseMessage = statusCode ? `Failed to fetch from registry (${statusCode}): ${url}` : `Failed to fetch from registry: ${url}`;
|
|
223
|
+
const message = typeof cause === "string" && cause ? `${baseMessage} - ${cause}` : baseMessage;
|
|
224
|
+
let suggestion = "Check your network connection and try again.";
|
|
225
|
+
if (statusCode === 404) suggestion = "The requested resource was not found. Check the URL or item name.";
|
|
226
|
+
else if (statusCode === 500) suggestion = "The registry server encountered an error. Try again later.";
|
|
227
|
+
else if (statusCode && statusCode >= 400 && statusCode < 500) suggestion = "There was a client error. Check your request parameters.";
|
|
228
|
+
super(message, {
|
|
229
|
+
code: RegistryErrorCode.FETCH_ERROR,
|
|
230
|
+
statusCode,
|
|
231
|
+
cause,
|
|
232
|
+
context: {
|
|
233
|
+
url,
|
|
234
|
+
responseBody
|
|
235
|
+
},
|
|
236
|
+
suggestion
|
|
237
|
+
});
|
|
238
|
+
this.url = url;
|
|
239
|
+
this.responseBody = responseBody;
|
|
240
|
+
this.name = "RegistryFetchError";
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
var RegistryNotConfiguredError = class extends RegistryError {
|
|
244
|
+
constructor(registryName) {
|
|
245
|
+
const message = registryName ? `Unknown registry "${registryName}". Make sure it is defined in components.json as follows:
|
|
246
|
+
{
|
|
247
|
+
"registries": {
|
|
248
|
+
"${registryName}": "[URL_TO_REGISTRY]"
|
|
249
|
+
}
|
|
250
|
+
}` : "Unknown registry. Make sure it is defined in components.json under \"registries\".";
|
|
251
|
+
super(message, {
|
|
252
|
+
code: RegistryErrorCode.NOT_CONFIGURED,
|
|
253
|
+
context: { registryName },
|
|
254
|
+
suggestion: "Add the registry configuration to your components.json file. Consult the registry documentation for the correct format."
|
|
255
|
+
});
|
|
256
|
+
this.registryName = registryName;
|
|
257
|
+
this.name = "RegistryNotConfiguredError";
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
var RegistryLocalFileError = class extends RegistryError {
|
|
261
|
+
constructor(filePath, cause) {
|
|
262
|
+
super(`Failed to read local registry file: ${filePath}`, {
|
|
263
|
+
code: RegistryErrorCode.LOCAL_FILE_ERROR,
|
|
264
|
+
cause,
|
|
265
|
+
context: { filePath },
|
|
266
|
+
suggestion: "Check if the file exists and you have read permissions."
|
|
267
|
+
});
|
|
268
|
+
this.filePath = filePath;
|
|
269
|
+
this.name = "RegistryLocalFileError";
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
var RegistryParseError = class extends RegistryError {
|
|
273
|
+
parseError;
|
|
274
|
+
constructor(item, parseError) {
|
|
275
|
+
let message = `Failed to parse registry item: ${item}`;
|
|
276
|
+
if (parseError instanceof z.ZodError) message = `Failed to parse registry item: ${item}\n${parseError.errors.map((e) => ` - ${e.path.join(".")}: ${e.message}`).join("\n")}`;
|
|
277
|
+
super(message, {
|
|
278
|
+
code: RegistryErrorCode.PARSE_ERROR,
|
|
279
|
+
cause: parseError,
|
|
280
|
+
context: { item },
|
|
281
|
+
suggestion: "The registry item may be corrupted or have an invalid format. Please make sure it returns a valid JSON object. See https://shadcn-vue.com/schema/registry-item.json."
|
|
282
|
+
});
|
|
283
|
+
this.item = item;
|
|
284
|
+
this.parseError = parseError;
|
|
285
|
+
this.name = "RegistryParseError";
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
var RegistryMissingEnvironmentVariablesError = class extends RegistryError {
|
|
289
|
+
constructor(registryName, missingVars) {
|
|
290
|
+
const message = `Registry "${registryName}" requires the following environment variables:\n\n${missingVars.map((v) => ` • ${v}`).join("\n")}`;
|
|
291
|
+
super(message, {
|
|
292
|
+
code: RegistryErrorCode.MISSING_ENV_VARS,
|
|
293
|
+
context: {
|
|
294
|
+
registryName,
|
|
295
|
+
missingVars
|
|
296
|
+
},
|
|
297
|
+
suggestion: "Set the required environment variables to your .env or .env.local file."
|
|
298
|
+
});
|
|
299
|
+
this.registryName = registryName;
|
|
300
|
+
this.missingVars = missingVars;
|
|
301
|
+
this.name = "RegistryMissingEnvironmentVariablesError";
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
var RegistryInvalidNamespaceError = class extends RegistryError {
|
|
305
|
+
constructor(name) {
|
|
306
|
+
const message = `Invalid registry namespace: "${name}". Registry names must start with @ (e.g., @shadcn, @v0).`;
|
|
307
|
+
super(message, {
|
|
308
|
+
code: RegistryErrorCode.VALIDATION_ERROR,
|
|
309
|
+
context: { name },
|
|
310
|
+
suggestion: "Use a valid registry name starting with @ or provide a direct URL to the registry."
|
|
311
|
+
});
|
|
312
|
+
this.name = name;
|
|
313
|
+
this.name = "RegistryInvalidNamespaceError";
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
var ConfigParseError = class extends RegistryError {
|
|
317
|
+
constructor(cwd, parseError) {
|
|
318
|
+
let message = `Invalid components.json configuration in ${cwd}.`;
|
|
319
|
+
if (parseError instanceof Error && parseError.message.includes("built-in registry and cannot be overridden")) message = `Invalid components.json configuration in ${highlighter.info(`${cwd}/components.json`)}:\n - ${parseError.message}`;
|
|
320
|
+
if (parseError instanceof SyntaxError) message = `Invalid components.json configuration in ${highlighter.info(`${cwd}/components.json`)}:\n - Syntax error: ${parseError.message.replace(`${cwd}/components.json`, "")}`;
|
|
321
|
+
if (parseError instanceof z.ZodError) message = `Invalid components.json configuration in ${highlighter.info(`${cwd}/components.json`)}:\n${parseError.errors.map((e) => ` - ${e.path.join(".")}: ${e.message}`).join("\n")}`;
|
|
322
|
+
super(message, {
|
|
323
|
+
code: RegistryErrorCode.INVALID_CONFIG,
|
|
324
|
+
cause: parseError,
|
|
325
|
+
context: { cwd },
|
|
326
|
+
suggestion: "Check your components.json file for syntax errors or invalid configuration. Run 'npx shadcn@latest init' to regenerate a valid configuration."
|
|
327
|
+
});
|
|
328
|
+
this.cwd = cwd;
|
|
329
|
+
this.name = "ConfigParseError";
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
var RegistriesIndexParseError = class extends RegistryError {
|
|
333
|
+
parseError;
|
|
334
|
+
constructor(parseError) {
|
|
335
|
+
let message = "Failed to parse registries index";
|
|
336
|
+
if (parseError instanceof z.ZodError) {
|
|
337
|
+
const invalidNamespaces = parseError.errors.filter((e) => e.path.length > 0).map((e) => `"${e.path[0]}"`).filter((v, i, arr) => arr.indexOf(v) === i);
|
|
338
|
+
if (invalidNamespaces.length > 0) message = `Failed to parse registries index. Invalid registry namespace(s): ${invalidNamespaces.join(", ")}\n${parseError.errors.map((e) => ` - ${e.path.join(".")}: ${e.message}`).join("\n")}`;
|
|
339
|
+
else message = `Failed to parse registries index:\n${parseError.errors.map((e) => ` - ${e.path.join(".")}: ${e.message}`).join("\n")}`;
|
|
340
|
+
}
|
|
341
|
+
super(message, {
|
|
342
|
+
code: RegistryErrorCode.PARSE_ERROR,
|
|
343
|
+
cause: parseError,
|
|
344
|
+
context: { parseError },
|
|
345
|
+
suggestion: "The registries index may be corrupted or have invalid registry namespace format. Registry names must start with @ (e.g., @shadcn, @example)."
|
|
346
|
+
});
|
|
347
|
+
this.parseError = parseError;
|
|
348
|
+
this.name = "RegistriesIndexParseError";
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
//#endregion
|
|
353
|
+
//#region src/utils/resolve-import.ts
|
|
354
|
+
function resolveImport(importPath, config) {
|
|
355
|
+
const matcher = createPathsMatcher(config);
|
|
356
|
+
if (matcher === null) return;
|
|
357
|
+
return matcher(importPath)[0];
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
//#endregion
|
|
361
|
+
//#region src/utils/get-config.ts
|
|
362
|
+
const DEFAULT_COMPONENTS = "@/components";
|
|
363
|
+
const DEFAULT_UTILS = "@/lib/utils";
|
|
364
|
+
const DEFAULT_TAILWIND_CSS = "assets/css/tailwind.css";
|
|
365
|
+
const DEFAULT_TAILWIND_CONFIG = "tailwind.config.js";
|
|
366
|
+
async function getConfig(cwd) {
|
|
367
|
+
const config = await getRawConfig(cwd);
|
|
368
|
+
if (!config) return null;
|
|
369
|
+
if (!config.iconLibrary) config.iconLibrary = config.style === "new-york" ? "radix" : "lucide";
|
|
370
|
+
return await resolveConfigPaths(cwd, config);
|
|
371
|
+
}
|
|
372
|
+
async function resolveConfigPaths(cwd, config) {
|
|
373
|
+
config.registries = {
|
|
374
|
+
...BUILTIN_REGISTRIES,
|
|
375
|
+
...config.registries || {}
|
|
376
|
+
};
|
|
377
|
+
const detectedFramework = await detectFrameworkConfigFiles(cwd);
|
|
378
|
+
const isTypeScript = await isTypeScriptProject(cwd);
|
|
379
|
+
const tsConfig = await getTsconfig(path.resolve(cwd, detectedFramework?.name === "nuxt4" ? "./.nuxt/tsconfig.app.json" : detectedFramework?.name === "nuxt3" ? "./.nuxt/tsconfig.json" : detectedFramework?.name === "inertia" ? "./inertia/tsconfig.json" : isTypeScript ? "./tsconfig.json" : "./jsconfig.json"), isTypeScript ? void 0 : "jsconfig.json");
|
|
380
|
+
if (tsConfig === null) throw new Error(`Failed to load ${config.typescript ? "tsconfig" : "jsconfig"}.json.`.trim());
|
|
381
|
+
return configSchema.parse({
|
|
382
|
+
...config,
|
|
383
|
+
resolvedPaths: {
|
|
384
|
+
cwd,
|
|
385
|
+
tailwindConfig: config.tailwind.config ? path.resolve(cwd, config.tailwind.config) : "",
|
|
386
|
+
tailwindCss: path.resolve(cwd, config.tailwind.css),
|
|
387
|
+
utils: await resolveImport(config.aliases.utils, tsConfig),
|
|
388
|
+
components: await resolveImport(config.aliases.components, tsConfig),
|
|
389
|
+
ui: config.aliases.ui ? await resolveImport(config.aliases.ui, tsConfig) : path.resolve(await resolveImport(config.aliases.components, tsConfig) ?? cwd, "ui"),
|
|
390
|
+
lib: config.aliases.lib ? await resolveImport(config.aliases.lib, tsConfig) : path.resolve(await resolveImport(config.aliases.utils, tsConfig) ?? cwd, ".."),
|
|
391
|
+
composables: config.aliases.composables ? await resolveImport(config.aliases.composables, tsConfig) : path.resolve(await resolveImport(config.aliases.components, tsConfig) ?? cwd, "..", "composables")
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
async function getRawConfig(cwd) {
|
|
396
|
+
try {
|
|
397
|
+
const configResult = await loadConfig({
|
|
398
|
+
name: "components",
|
|
399
|
+
configFile: "components",
|
|
400
|
+
cwd,
|
|
401
|
+
dotenv: false,
|
|
402
|
+
packageJson: false,
|
|
403
|
+
rcFile: false,
|
|
404
|
+
jitiOptions: {
|
|
405
|
+
rebuildFsCache: true,
|
|
406
|
+
moduleCache: true
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
if (!configResult.config || Object.keys(configResult.config).length === 0) return null;
|
|
410
|
+
const config = rawConfigSchema.parse(configResult.config);
|
|
411
|
+
if (config.registries) {
|
|
412
|
+
for (const registryName of Object.keys(config.registries)) if (registryName in BUILTIN_REGISTRIES) throw new Error(`"${registryName}" is a built-in registry and cannot be overridden.`);
|
|
413
|
+
}
|
|
414
|
+
return config;
|
|
415
|
+
} catch (error) {
|
|
416
|
+
throw new ConfigParseError(cwd, error);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
async function getWorkspaceConfig(config) {
|
|
420
|
+
let resolvedAliases = {};
|
|
421
|
+
for (const key of Object.keys(config.aliases)) {
|
|
422
|
+
if (!isAliasKey(key, config)) continue;
|
|
423
|
+
const resolvedPath = config.resolvedPaths[key];
|
|
424
|
+
const packageRoot = await findPackageRoot(config.resolvedPaths.cwd, resolvedPath);
|
|
425
|
+
if (!packageRoot) {
|
|
426
|
+
resolvedAliases[key] = config;
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
resolvedAliases[key] = await getConfig(packageRoot);
|
|
430
|
+
}
|
|
431
|
+
const result = workspaceConfigSchema.safeParse(resolvedAliases);
|
|
432
|
+
if (!result.success) return null;
|
|
433
|
+
return result.data;
|
|
434
|
+
}
|
|
435
|
+
async function findPackageRoot(cwd, resolvedPath) {
|
|
436
|
+
const commonRoot = findCommonRoot$1(cwd, resolvedPath);
|
|
437
|
+
const relativePath = path.relative(commonRoot, resolvedPath);
|
|
438
|
+
const matchingPackageRoot = (await glob("**/package.json", {
|
|
439
|
+
cwd: commonRoot,
|
|
440
|
+
deep: 3,
|
|
441
|
+
ignore: [
|
|
442
|
+
"**/node_modules/**",
|
|
443
|
+
"**/dist/**",
|
|
444
|
+
"**/build/**",
|
|
445
|
+
"**/public/**"
|
|
446
|
+
]
|
|
447
|
+
})).map((pkgPath) => path.dirname(pkgPath)).find((pkgDir) => relativePath.startsWith(pkgDir));
|
|
448
|
+
return matchingPackageRoot ? path.join(commonRoot, matchingPackageRoot) : null;
|
|
449
|
+
}
|
|
450
|
+
function isAliasKey(key, config) {
|
|
451
|
+
return Object.keys(config.resolvedPaths).filter((key$1) => key$1 !== "utils").includes(key);
|
|
452
|
+
}
|
|
453
|
+
function findCommonRoot$1(cwd, resolvedPath) {
|
|
454
|
+
const parts1 = cwd.split(path.sep);
|
|
455
|
+
const parts2 = resolvedPath.split(path.sep);
|
|
456
|
+
const commonParts = [];
|
|
457
|
+
for (let i = 0; i < Math.min(parts1.length, parts2.length); i++) {
|
|
458
|
+
if (parts1[i] !== parts2[i]) break;
|
|
459
|
+
commonParts.push(parts1[i]);
|
|
460
|
+
}
|
|
461
|
+
return commonParts.join(path.sep);
|
|
462
|
+
}
|
|
463
|
+
async function getTargetStyleFromConfig(cwd, fallback) {
|
|
464
|
+
return (await getProjectInfo(cwd))?.tailwindVersion === "v4" ? "new-york-v4" : fallback;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Creates a config object with sensible defaults.
|
|
468
|
+
* Useful for universal registry items that bypass framework detection.
|
|
469
|
+
*
|
|
470
|
+
* @param partial - Partial config values to override defaults
|
|
471
|
+
* @returns A complete Config object
|
|
472
|
+
*/
|
|
473
|
+
function createConfig(partial) {
|
|
474
|
+
const defaultConfig = {
|
|
475
|
+
typescript: true,
|
|
476
|
+
resolvedPaths: {
|
|
477
|
+
cwd: process.cwd(),
|
|
478
|
+
tailwindConfig: "",
|
|
479
|
+
tailwindCss: "",
|
|
480
|
+
utils: "",
|
|
481
|
+
components: "",
|
|
482
|
+
ui: "",
|
|
483
|
+
lib: "",
|
|
484
|
+
composables: ""
|
|
485
|
+
},
|
|
486
|
+
style: "",
|
|
487
|
+
tailwind: {
|
|
488
|
+
config: "",
|
|
489
|
+
css: "",
|
|
490
|
+
baseColor: "",
|
|
491
|
+
cssVariables: false
|
|
492
|
+
},
|
|
493
|
+
aliases: {
|
|
494
|
+
components: "",
|
|
495
|
+
utils: ""
|
|
496
|
+
},
|
|
497
|
+
registries: { ...BUILTIN_REGISTRIES }
|
|
498
|
+
};
|
|
499
|
+
if (partial) return {
|
|
500
|
+
...defaultConfig,
|
|
501
|
+
...partial,
|
|
502
|
+
resolvedPaths: {
|
|
503
|
+
...defaultConfig.resolvedPaths,
|
|
504
|
+
...partial.resolvedPaths || {}
|
|
505
|
+
},
|
|
506
|
+
tailwind: {
|
|
507
|
+
...defaultConfig.tailwind,
|
|
508
|
+
...partial.tailwind || {}
|
|
509
|
+
},
|
|
510
|
+
aliases: {
|
|
511
|
+
...defaultConfig.aliases,
|
|
512
|
+
...partial.aliases || {}
|
|
513
|
+
},
|
|
514
|
+
registries: {
|
|
515
|
+
...defaultConfig.registries,
|
|
516
|
+
...partial.registries || {}
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
return defaultConfig;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
//#endregion
|
|
523
|
+
//#region src/utils/get-package-info.ts
|
|
524
|
+
function getPackageInfo(cwd = "", shouldThrow = true) {
|
|
525
|
+
const packageJsonPath = path.join(cwd, "package.json");
|
|
526
|
+
return fsExtra.readJSONSync(packageJsonPath, { throws: shouldThrow });
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
//#endregion
|
|
530
|
+
//#region src/utils/get-project-info.ts
|
|
531
|
+
const PROJECT_SHARED_IGNORE = [
|
|
532
|
+
"**/node_modules/**",
|
|
533
|
+
".nuxt",
|
|
534
|
+
"public",
|
|
535
|
+
"dist",
|
|
536
|
+
"build"
|
|
537
|
+
];
|
|
538
|
+
const TS_CONFIG_SCHEMA = z.object({ compilerOptions: z.object({ paths: z.record(z.string().or(z.array(z.string()))) }) });
|
|
539
|
+
async function detectFrameworkConfigFiles(cwd) {
|
|
540
|
+
const packageInfo = await getPackageInfo(cwd, false);
|
|
541
|
+
const configFiles = await glob("**/{nuxt,vite,astro,wxt}.config.*|composer.json", {
|
|
542
|
+
cwd,
|
|
543
|
+
deep: 3,
|
|
544
|
+
ignore: PROJECT_SHARED_IGNORE
|
|
545
|
+
});
|
|
546
|
+
if (configFiles.find((file) => file.startsWith("nuxt.config."))) {
|
|
547
|
+
const nuxtPkg = packageInfo?.dependencies?.nuxt || packageInfo?.devDependencies?.nuxt;
|
|
548
|
+
const nuxtVersion = nuxtPkg && coerce(nuxtPkg)?.version || "4.0.0";
|
|
549
|
+
if (nuxtVersion.startsWith("4")) return FRAMEWORKS.nuxt4;
|
|
550
|
+
else if (nuxtVersion.startsWith("3")) return FRAMEWORKS.nuxt3;
|
|
551
|
+
return null;
|
|
552
|
+
}
|
|
553
|
+
if (configFiles.find((file) => file.startsWith("astro.config."))) return FRAMEWORKS.astro;
|
|
554
|
+
if (configFiles.find((file) => file.startsWith("composer.json"))) return FRAMEWORKS.laravel;
|
|
555
|
+
if (packageInfo?.dependencies?.["@inertiajs/vue3"] || packageInfo?.devDependencies?.["@inertiajs/vue3"] || await fsExtra.pathExists(path.join(cwd, "resources/js"))) return FRAMEWORKS.inertia;
|
|
556
|
+
if (configFiles.find((file) => file.startsWith("wxt.config."))) return FRAMEWORKS.vite;
|
|
557
|
+
if (configFiles.find((file) => file.startsWith("vite.config."))) return FRAMEWORKS.vite;
|
|
558
|
+
return null;
|
|
559
|
+
}
|
|
560
|
+
async function isTypeScriptProject(cwd) {
|
|
561
|
+
return (await glob("tsconfig.*", {
|
|
562
|
+
cwd,
|
|
563
|
+
deep: 1,
|
|
564
|
+
ignore: PROJECT_SHARED_IGNORE
|
|
565
|
+
})).length > 0;
|
|
566
|
+
}
|
|
567
|
+
async function getProjectInfo(cwd) {
|
|
568
|
+
const [detectedFramework, typescript, isSrcDir, tailwindConfigFile, tailwindCssFile, tailwindVersion, aliasPrefix, _packageJson] = await Promise.all([
|
|
569
|
+
detectFrameworkConfigFiles(cwd),
|
|
570
|
+
isTypeScriptProject(cwd),
|
|
571
|
+
fsExtra.pathExists(path.resolve(cwd, "src")),
|
|
572
|
+
getTailwindConfigFile(cwd),
|
|
573
|
+
getTailwindCssFile(cwd),
|
|
574
|
+
getTailwindVersion(cwd),
|
|
575
|
+
getTsConfigAliasPrefix(cwd),
|
|
576
|
+
getPackageInfo(cwd, false)
|
|
577
|
+
]);
|
|
578
|
+
return {
|
|
579
|
+
framework: detectedFramework || FRAMEWORKS.manual,
|
|
580
|
+
typescript,
|
|
581
|
+
isSrcDir,
|
|
582
|
+
tailwindConfigFile,
|
|
583
|
+
tailwindCssFile,
|
|
584
|
+
tailwindVersion,
|
|
585
|
+
aliasPrefix
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
async function getTailwindVersion(cwd) {
|
|
589
|
+
const [packageInfo, config] = await Promise.all([getPackageInfo(cwd, false), getConfig(cwd)]);
|
|
590
|
+
if (config?.tailwind?.config === "") return "v4";
|
|
591
|
+
const hasNuxtTailwind = !!(packageInfo?.dependencies?.["@nuxtjs/tailwindcss"] || packageInfo?.devDependencies?.["@nuxtjs/tailwindcss"]);
|
|
592
|
+
if (!!!(packageInfo?.dependencies?.tailwindcss || packageInfo?.devDependencies?.tailwindcss) && !hasNuxtTailwind) return null;
|
|
593
|
+
if (/^(?:\^|~)?3(?:\.\d+)*(?:-.*)?$/.test(packageInfo?.dependencies?.tailwindcss || packageInfo?.devDependencies?.tailwindcss || "")) return "v3";
|
|
594
|
+
return "v4";
|
|
595
|
+
}
|
|
596
|
+
async function getTailwindCssFile(cwd) {
|
|
597
|
+
const [files, tailwindVersion] = await Promise.all([glob(["**/*.css", "**/*.scss"], {
|
|
598
|
+
cwd,
|
|
599
|
+
deep: 5,
|
|
600
|
+
ignore: PROJECT_SHARED_IGNORE
|
|
601
|
+
}), getTailwindVersion(cwd)]);
|
|
602
|
+
if (!files.length) return null;
|
|
603
|
+
for (const file of files) {
|
|
604
|
+
const contents = await fsExtra.readFile(path.resolve(cwd, file), "utf8");
|
|
605
|
+
if (contents.includes(`@import "tailwindcss"`) || contents.includes(`@import 'tailwindcss'`) || contents.includes(`@tailwind base`)) return file;
|
|
606
|
+
}
|
|
607
|
+
return null;
|
|
608
|
+
}
|
|
609
|
+
async function getTailwindConfigFile(cwd) {
|
|
610
|
+
const files = await glob("tailwind.config.*", {
|
|
611
|
+
cwd,
|
|
612
|
+
deep: 3,
|
|
613
|
+
ignore: PROJECT_SHARED_IGNORE
|
|
614
|
+
});
|
|
615
|
+
if (!files.length) return null;
|
|
616
|
+
return files[0];
|
|
617
|
+
}
|
|
618
|
+
async function getTsConfigAliasPrefix(cwd) {
|
|
619
|
+
const detectedFramework = await detectFrameworkConfigFiles(cwd);
|
|
620
|
+
const isTypeScript = await isTypeScriptProject(cwd);
|
|
621
|
+
const tsConfig = await getTsconfig(cwd, detectedFramework?.name === "nuxt4" ? "./.nuxt/tsconfig.app.json" : detectedFramework?.name === "nuxt3" ? "./.nuxt/tsconfig.json" : detectedFramework?.name === "inertia" ? "./inertia/tsconfig.json" : isTypeScript ? "./tsconfig.json" : "./jsconfig.json");
|
|
622
|
+
if (tsConfig === null || !Object.entries(tsConfig.config.compilerOptions?.paths ?? {}).length) return null;
|
|
623
|
+
const aliasPaths = tsConfig.config.compilerOptions?.paths ?? {};
|
|
624
|
+
for (const [alias, paths] of Object.entries(aliasPaths)) if (paths.includes("./*") || paths.includes("./src/*") || paths.includes("./app/*") || paths.includes("./resources/js/*")) {
|
|
625
|
+
const cleanAlias = alias.replace(/\/\*$/, "") ?? null;
|
|
626
|
+
return cleanAlias === "#build" ? "@" : cleanAlias;
|
|
627
|
+
}
|
|
628
|
+
return Object.keys(aliasPaths)?.[0]?.replace(/\/\*$/, "") ?? null;
|
|
629
|
+
}
|
|
630
|
+
async function getProjectConfig(cwd, defaultProjectInfo = null) {
|
|
631
|
+
const [existingConfig, projectInfo] = await Promise.all([getConfig(cwd), !defaultProjectInfo ? getProjectInfo(cwd) : Promise.resolve(defaultProjectInfo)]);
|
|
632
|
+
if (existingConfig) return existingConfig;
|
|
633
|
+
if (!projectInfo || !projectInfo.tailwindCssFile || projectInfo.tailwindVersion === "v3" && !projectInfo.tailwindConfigFile) return null;
|
|
634
|
+
return await resolveConfigPaths(cwd, {
|
|
635
|
+
$schema: "https://shadcn-vue.com/schema.json",
|
|
636
|
+
typescript: projectInfo.typescript,
|
|
637
|
+
style: "new-york",
|
|
638
|
+
tailwind: {
|
|
639
|
+
config: projectInfo.tailwindConfigFile ?? "",
|
|
640
|
+
baseColor: "zinc",
|
|
641
|
+
css: projectInfo.tailwindCssFile,
|
|
642
|
+
cssVariables: true,
|
|
643
|
+
prefix: ""
|
|
644
|
+
},
|
|
645
|
+
iconLibrary: "lucide",
|
|
646
|
+
aliases: {
|
|
647
|
+
components: `${projectInfo.aliasPrefix}/components`,
|
|
648
|
+
ui: `${projectInfo.aliasPrefix}/components/ui`,
|
|
649
|
+
composables: `${projectInfo.aliasPrefix}/composables`,
|
|
650
|
+
lib: `${projectInfo.aliasPrefix}/lib`,
|
|
651
|
+
utils: `${projectInfo.aliasPrefix}/lib/utils`
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
async function getProjectTailwindVersionFromConfig(config) {
|
|
656
|
+
if (!config.resolvedPaths?.cwd) return "v3";
|
|
657
|
+
const projectInfo = await getProjectInfo(config.resolvedPaths.cwd);
|
|
658
|
+
if (!projectInfo?.tailwindVersion) return null;
|
|
659
|
+
return projectInfo.tailwindVersion;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
//#endregion
|
|
663
|
+
//#region src/utils/logger.ts
|
|
664
|
+
const logger = {
|
|
665
|
+
error(...args) {
|
|
666
|
+
consola.log(highlighter.error(args.join(" ")));
|
|
667
|
+
},
|
|
668
|
+
warn(...args) {
|
|
669
|
+
consola.log(highlighter.warn(args.join(" ")));
|
|
670
|
+
},
|
|
671
|
+
info(...args) {
|
|
672
|
+
consola.log(highlighter.info(args.join(" ")));
|
|
673
|
+
},
|
|
674
|
+
success(...args) {
|
|
675
|
+
consola.log(highlighter.success(args.join(" ")));
|
|
676
|
+
},
|
|
677
|
+
log(...args) {
|
|
678
|
+
consola.log(args.join(" "));
|
|
679
|
+
},
|
|
680
|
+
break() {
|
|
681
|
+
consola.log("");
|
|
682
|
+
}
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
//#endregion
|
|
686
|
+
//#region src/utils/spinner.ts
|
|
687
|
+
function spinner(text, options) {
|
|
688
|
+
return ora({
|
|
689
|
+
text,
|
|
690
|
+
isSilent: options?.silent
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
//#endregion
|
|
695
|
+
//#region src/registry/env.ts
|
|
696
|
+
function expandEnvVars(value) {
|
|
697
|
+
return value.replace(/\$\{(\w+)\}/g, (_match, key) => process.env[key] || "");
|
|
698
|
+
}
|
|
699
|
+
function extractEnvVars(value) {
|
|
700
|
+
const vars = [];
|
|
701
|
+
const regex = /\$\{(\w+)\}/g;
|
|
702
|
+
let match;
|
|
703
|
+
while ((match = regex.exec(value)) !== null) vars.push(match[1]);
|
|
704
|
+
return vars;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
//#endregion
|
|
708
|
+
//#region src/registry/parser.ts
|
|
709
|
+
const REGISTRY_PATTERN = /^(@[a-z0-9](?:[\w-]*[a-z0-9])?)\/(.+)$/i;
|
|
710
|
+
function parseRegistryAndItemFromString(name) {
|
|
711
|
+
if (!name.startsWith("@")) return {
|
|
712
|
+
registry: null,
|
|
713
|
+
item: name
|
|
714
|
+
};
|
|
715
|
+
const match = name.match(REGISTRY_PATTERN);
|
|
716
|
+
if (match) return {
|
|
717
|
+
registry: match[1],
|
|
718
|
+
item: match[2]
|
|
719
|
+
};
|
|
720
|
+
return {
|
|
721
|
+
registry: null,
|
|
722
|
+
item: name
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
//#endregion
|
|
727
|
+
//#region src/utils/compare.ts
|
|
728
|
+
function isContentSame(existingContent, newContent, options = {}) {
|
|
729
|
+
const { ignoreImports = false } = options;
|
|
730
|
+
const normalizedExisting = existingContent.replace(/\r\n/g, "\n").trim();
|
|
731
|
+
const normalizedNew = newContent.replace(/\r\n/g, "\n").trim();
|
|
732
|
+
if (normalizedExisting === normalizedNew) return true;
|
|
733
|
+
if (!ignoreImports) return false;
|
|
734
|
+
const importRegex = /^(import\s+(?:type\s+)?(?:\*\s+as\s+\w+|\{[^}]*\}|\w+)?(?:\s*,\s*(?:\{[^}]*\}|\w+))?\s+from\s+["'])([^"']+)(["'])/gm;
|
|
735
|
+
const normalizeImports = (content) => {
|
|
736
|
+
return content.replace(importRegex, (_match, prefix, importPath, suffix) => {
|
|
737
|
+
if (importPath.startsWith(".")) return `${prefix}${importPath}${suffix}`;
|
|
738
|
+
const parts = importPath.split("/");
|
|
739
|
+
return `${prefix}@normalized/${parts[parts.length - 1]}${suffix}`;
|
|
740
|
+
});
|
|
741
|
+
};
|
|
742
|
+
return normalizeImports(normalizedExisting) === normalizeImports(normalizedNew);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
//#endregion
|
|
746
|
+
//#region src/utils/env-helpers.ts
|
|
747
|
+
function isEnvFile(filePath) {
|
|
748
|
+
const fileName = path.basename(filePath);
|
|
749
|
+
return /^\.env(?:\.|$)/.test(fileName);
|
|
750
|
+
}
|
|
751
|
+
/**
|
|
752
|
+
* Finds a file variant in the project.
|
|
753
|
+
* TODO: abstract this to a more generic function.
|
|
754
|
+
*/
|
|
755
|
+
function findExistingEnvFile(targetDir) {
|
|
756
|
+
for (const variant of [
|
|
757
|
+
".env.local",
|
|
758
|
+
".env",
|
|
759
|
+
".env.development.local",
|
|
760
|
+
".env.development"
|
|
761
|
+
]) {
|
|
762
|
+
const filePath = path.join(targetDir, variant);
|
|
763
|
+
if (existsSync(filePath)) return filePath;
|
|
764
|
+
}
|
|
765
|
+
return null;
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Parse .env content into key-value pairs.
|
|
769
|
+
*/
|
|
770
|
+
function parseEnvContent(content) {
|
|
771
|
+
const lines = content.split("\n");
|
|
772
|
+
const env = {};
|
|
773
|
+
for (const line of lines) {
|
|
774
|
+
const trimmed = line.trim();
|
|
775
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
776
|
+
const equalIndex = trimmed.indexOf("=");
|
|
777
|
+
if (equalIndex === -1) continue;
|
|
778
|
+
const key = trimmed.substring(0, equalIndex).trim();
|
|
779
|
+
const value = trimmed.substring(equalIndex + 1).trim();
|
|
780
|
+
if (key) env[key] = value.replace(/^["']|["']$/g, "");
|
|
781
|
+
}
|
|
782
|
+
return env;
|
|
783
|
+
}
|
|
784
|
+
/**
|
|
785
|
+
* Get the list of new keys that would be added when merging env content.
|
|
786
|
+
*/
|
|
787
|
+
function getNewEnvKeys(existingContent, newContent) {
|
|
788
|
+
const existingEnv = parseEnvContent(existingContent);
|
|
789
|
+
const newEnv = parseEnvContent(newContent);
|
|
790
|
+
const newKeys = [];
|
|
791
|
+
for (const key of Object.keys(newEnv)) if (!(key in existingEnv)) newKeys.push(key);
|
|
792
|
+
return newKeys;
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Merge env content by appending ONLY new keys that don't exist in the existing content.
|
|
796
|
+
* Existing keys are preserved with their original values.
|
|
797
|
+
*/
|
|
798
|
+
function mergeEnvContent(existingContent, newContent) {
|
|
799
|
+
const existingEnv = parseEnvContent(existingContent);
|
|
800
|
+
const newEnv = parseEnvContent(newContent);
|
|
801
|
+
let result = existingContent.trimEnd();
|
|
802
|
+
if (result && !result.endsWith("\n")) result += "\n";
|
|
803
|
+
const newKeys = [];
|
|
804
|
+
for (const [key, value] of Object.entries(newEnv)) if (!(key in existingEnv)) newKeys.push(`${key}=${value}`);
|
|
805
|
+
if (newKeys.length > 0) {
|
|
806
|
+
if (result) result += "\n";
|
|
807
|
+
result += newKeys.join("\n");
|
|
808
|
+
return `${result}\n`;
|
|
809
|
+
}
|
|
810
|
+
if (result && !result.endsWith("\n")) return `${result}\n`;
|
|
811
|
+
return result;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
//#endregion
|
|
815
|
+
//#region src/utils/transformers/transform-css-vars.ts
|
|
816
|
+
function transformCssVars(opts) {
|
|
817
|
+
return {
|
|
818
|
+
type: "codemod",
|
|
819
|
+
name: "add prefix to tailwind classes",
|
|
820
|
+
transform({ scriptASTs, sfcAST, utils: { traverseScriptAST, traverseTemplateAST } }) {
|
|
821
|
+
let transformCount = 0;
|
|
822
|
+
const { baseColor, config } = opts;
|
|
823
|
+
if (config.tailwind?.cssVariables || !baseColor?.inlineColors) return transformCount;
|
|
824
|
+
for (const scriptAST of scriptASTs) traverseScriptAST(scriptAST, { visitLiteral(path$1) {
|
|
825
|
+
if (path$1.parent.value.type !== "ImportDeclaration" && typeof path$1.node.value === "string") {
|
|
826
|
+
const raw = path$1.node.value;
|
|
827
|
+
const mapped = applyColorMapping(raw, baseColor.inlineColors).trim();
|
|
828
|
+
if (mapped !== raw) {
|
|
829
|
+
path$1.node.value = mapped;
|
|
830
|
+
transformCount++;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
return this.traverse(path$1);
|
|
834
|
+
} });
|
|
835
|
+
if (sfcAST) traverseTemplateAST(sfcAST, {
|
|
836
|
+
enterNode(node) {
|
|
837
|
+
if (node.type === "Literal" && typeof node.value === "string") {
|
|
838
|
+
if (!["BinaryExpression", "Property"].includes(node.parent?.type ?? "")) {
|
|
839
|
+
const raw = node.value;
|
|
840
|
+
const mapped = applyColorMapping(raw, baseColor.inlineColors).trim();
|
|
841
|
+
if (mapped !== raw) {
|
|
842
|
+
node.value = mapped;
|
|
843
|
+
transformCount++;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
} else if (node.type === "VLiteral" && typeof node.value === "string") {
|
|
847
|
+
if (node.parent.key.name === "class") {
|
|
848
|
+
const raw = node.value;
|
|
849
|
+
const mapped = applyColorMapping(raw, baseColor.inlineColors).trim();
|
|
850
|
+
if (mapped !== raw) {
|
|
851
|
+
node.value = mapped;
|
|
852
|
+
transformCount++;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
},
|
|
857
|
+
leaveNode() {}
|
|
858
|
+
});
|
|
859
|
+
return transformCount;
|
|
860
|
+
}
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
function splitClassName(className) {
|
|
864
|
+
if (!className.includes("/") && !className.includes(":")) return [
|
|
865
|
+
null,
|
|
866
|
+
className,
|
|
867
|
+
null
|
|
868
|
+
];
|
|
869
|
+
const parts = [];
|
|
870
|
+
const [rest, alpha] = className.split("/");
|
|
871
|
+
if (!rest.includes(":")) return [
|
|
872
|
+
null,
|
|
873
|
+
rest,
|
|
874
|
+
alpha
|
|
875
|
+
];
|
|
876
|
+
const split = rest.split(":");
|
|
877
|
+
const name = split.pop();
|
|
878
|
+
const variant = split.join(":");
|
|
879
|
+
parts.push(variant ?? null, name ?? null, alpha ?? null);
|
|
880
|
+
return parts;
|
|
881
|
+
}
|
|
882
|
+
const PREFIXES = [
|
|
883
|
+
"bg-",
|
|
884
|
+
"text-",
|
|
885
|
+
"border-",
|
|
886
|
+
"ring-offset-",
|
|
887
|
+
"ring-"
|
|
888
|
+
];
|
|
889
|
+
function applyColorMapping(input, mapping) {
|
|
890
|
+
if (input.includes(" border ")) input = input.replace(" border ", " border border-border ");
|
|
891
|
+
const classNames = input.split(" ");
|
|
892
|
+
const lightMode = /* @__PURE__ */ new Set();
|
|
893
|
+
const darkMode = /* @__PURE__ */ new Set();
|
|
894
|
+
for (const className of classNames) {
|
|
895
|
+
const [variant, value, modifier] = splitClassName(className);
|
|
896
|
+
const prefix = PREFIXES.find((prefix$1) => value?.startsWith(prefix$1));
|
|
897
|
+
if (!prefix) {
|
|
898
|
+
if (!lightMode.has(className)) lightMode.add(className);
|
|
899
|
+
continue;
|
|
900
|
+
}
|
|
901
|
+
const needle = value?.replace(prefix, "");
|
|
902
|
+
if (needle && needle in mapping.light) {
|
|
903
|
+
lightMode.add([variant, `${prefix}${mapping.light[needle]}`].filter(Boolean).join(":") + (modifier ? `/${modifier}` : ""));
|
|
904
|
+
darkMode.add([
|
|
905
|
+
"dark",
|
|
906
|
+
variant,
|
|
907
|
+
`${prefix}${mapping.dark[needle]}`
|
|
908
|
+
].filter(Boolean).join(":") + (modifier ? `/${modifier}` : ""));
|
|
909
|
+
continue;
|
|
910
|
+
}
|
|
911
|
+
if (!lightMode.has(className)) lightMode.add(className);
|
|
912
|
+
}
|
|
913
|
+
return [...Array.from(lightMode), ...Array.from(darkMode)].join(" ").trim();
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
//#endregion
|
|
917
|
+
//#region src/utils/transformers/transform-import.ts
|
|
918
|
+
function transformImport(opts) {
|
|
919
|
+
return {
|
|
920
|
+
type: "codemod",
|
|
921
|
+
name: "modify import based on user config",
|
|
922
|
+
transform({ scriptASTs, utils: { traverseScriptAST } }) {
|
|
923
|
+
let transformCount = 0;
|
|
924
|
+
const { config, isRemote } = opts;
|
|
925
|
+
const utilsAlias = config.aliases?.utils;
|
|
926
|
+
const utilsImport = `${typeof utilsAlias === "string" && utilsAlias.includes("/") ? utilsAlias.split("/")[0] : "@"}/lib/utils`;
|
|
927
|
+
for (const scriptAST of scriptASTs) traverseScriptAST(scriptAST, { visitLiteral(path$1) {
|
|
928
|
+
if (typeof path$1.node.value === "string") {
|
|
929
|
+
const parent = path$1.parent.value;
|
|
930
|
+
if (parent.type === "ImportDeclaration" || parent.type === "CallExpression" && parent.callee?.name === "import") {
|
|
931
|
+
const sourcePath = path$1.node.value;
|
|
932
|
+
const updatedImport = updateImportAliases(sourcePath, config, isRemote);
|
|
933
|
+
if (updatedImport !== sourcePath) {
|
|
934
|
+
path$1.node.value = updatedImport;
|
|
935
|
+
transformCount++;
|
|
936
|
+
}
|
|
937
|
+
if (utilsImport === updatedImport || updatedImport === "@/lib/utils") {
|
|
938
|
+
if (parent.type === "ImportDeclaration") {
|
|
939
|
+
if ((parent.specifiers?.map((node) => node.local?.name ?? "") ?? []).find((i) => i === "cn") && config.aliases.utils) {
|
|
940
|
+
path$1.node.value = utilsImport === updatedImport ? updatedImport.replace(utilsImport, config.aliases.utils) : config.aliases.utils;
|
|
941
|
+
transformCount++;
|
|
942
|
+
}
|
|
943
|
+
} else if (parent.type === "CallExpression") {
|
|
944
|
+
const grandParent = path$1.parent.parent?.value;
|
|
945
|
+
if (grandParent?.type === "VariableDeclarator" && grandParent.id?.type === "ObjectPattern") {
|
|
946
|
+
if (grandParent.id.properties?.some((prop) => prop.key?.name === "cn") && config.aliases.utils) {
|
|
947
|
+
path$1.node.value = utilsImport === updatedImport ? updatedImport.replace(utilsImport, config.aliases.utils) : config.aliases.utils;
|
|
948
|
+
transformCount++;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
return this.traverse(path$1);
|
|
956
|
+
} });
|
|
957
|
+
return transformCount;
|
|
958
|
+
}
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
function updateImportAliases(moduleSpecifier, config, isRemote = false) {
|
|
962
|
+
if (!moduleSpecifier.startsWith("@/") && !isRemote) return moduleSpecifier;
|
|
963
|
+
if (isRemote && moduleSpecifier.startsWith("@/")) moduleSpecifier = moduleSpecifier.replace(/^@\//, `@/registry/new-york/`);
|
|
964
|
+
if (!moduleSpecifier.startsWith("@/registry/")) {
|
|
965
|
+
const alias = config.aliases.components.split("/")[0];
|
|
966
|
+
return moduleSpecifier.replace(/^@\//, `${alias}/`);
|
|
967
|
+
}
|
|
968
|
+
if (moduleSpecifier.match(/^@\/registry\/(.+)\/ui/)) return moduleSpecifier.replace(/^@\/registry\/(.+)\/ui/, config.aliases.ui ?? `${config.aliases.components}/ui`);
|
|
969
|
+
if (config.aliases.components && moduleSpecifier.match(/^@\/registry\/(.+)\/components/)) return moduleSpecifier.replace(/^@\/registry\/(.+)\/components/, config.aliases.components);
|
|
970
|
+
if (config.aliases.lib && moduleSpecifier.match(/^@\/registry\/(.+)\/lib/)) return moduleSpecifier.replace(/^@\/registry\/(.+)\/lib/, config.aliases.lib);
|
|
971
|
+
if (config.aliases.composables && moduleSpecifier.match(/^@\/registry\/(.+)\/composables/)) return moduleSpecifier.replace(/^@\/registry\/(.+)\/composables/, config.aliases.composables);
|
|
972
|
+
return moduleSpecifier.replace(/^@\/registry\/[^/]+/, config.aliases.components);
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
//#endregion
|
|
976
|
+
//#region src/utils/transformers/transform-sfc.ts
|
|
977
|
+
async function transformSFC(opts) {
|
|
978
|
+
if (opts.config?.typescript) return opts.raw;
|
|
979
|
+
return await transformByDetype(opts.raw, opts.filename).then((res) => res);
|
|
980
|
+
}
|
|
981
|
+
async function transformByDetype(content, filename) {
|
|
982
|
+
return await transform$1(content, filename, {
|
|
983
|
+
removeTsComments: true,
|
|
984
|
+
prettierOptions: { proseWrap: "never" }
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
//#endregion
|
|
989
|
+
//#region src/utils/transformers/transform-tw-prefix.ts
|
|
990
|
+
async function transformTwPrefix(opts) {
|
|
991
|
+
const tailwindVersion = await getProjectTailwindVersionFromConfig(opts.config);
|
|
992
|
+
return {
|
|
993
|
+
type: "codemod",
|
|
994
|
+
name: "add prefix to tailwind classes",
|
|
995
|
+
transform({ scriptASTs, sfcAST, utils: { traverseScriptAST, traverseTemplateAST, astHelpers } }) {
|
|
996
|
+
let transformCount = 0;
|
|
997
|
+
const { config } = opts;
|
|
998
|
+
if (!config.tailwind?.prefix) return transformCount;
|
|
999
|
+
const addPrefix = (input) => {
|
|
1000
|
+
const result = applyPrefix(input, config.tailwind.prefix, tailwindVersion);
|
|
1001
|
+
transformCount++;
|
|
1002
|
+
return result;
|
|
1003
|
+
};
|
|
1004
|
+
function isVariantProperty(node) {
|
|
1005
|
+
if (node.type === "Property") {
|
|
1006
|
+
if (node.key?.type === "Identifier") {
|
|
1007
|
+
const keyName = node.key.name;
|
|
1008
|
+
return [
|
|
1009
|
+
"variant",
|
|
1010
|
+
"size",
|
|
1011
|
+
"color",
|
|
1012
|
+
"type",
|
|
1013
|
+
"state"
|
|
1014
|
+
].includes(keyName);
|
|
1015
|
+
}
|
|
1016
|
+
if (node.key?.type === "Literal" && typeof node.key.value === "string") {
|
|
1017
|
+
const keyName = node.key.value;
|
|
1018
|
+
return [
|
|
1019
|
+
"variant",
|
|
1020
|
+
"size",
|
|
1021
|
+
"color",
|
|
1022
|
+
"type",
|
|
1023
|
+
"state"
|
|
1024
|
+
].includes(keyName);
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
return false;
|
|
1028
|
+
}
|
|
1029
|
+
function traverseExpression(expression) {
|
|
1030
|
+
if (expression.type === "CallExpression" && expression.callee?.type === "Identifier" && expression.callee.name === "cn") expression.arguments.forEach((arg) => {
|
|
1031
|
+
if (arg.type === "Literal" && typeof arg.value === "string") arg.value = addPrefix(arg.value);
|
|
1032
|
+
else if (arg.type === "ConditionalExpression") {
|
|
1033
|
+
if (arg.consequent?.type === "Literal" && typeof arg.consequent.value === "string") arg.consequent.value = addPrefix(arg.consequent.value);
|
|
1034
|
+
if (arg.alternate?.type === "Literal" && typeof arg.alternate.value === "string") arg.alternate.value = addPrefix(arg.alternate.value);
|
|
1035
|
+
} else if (arg.type === "BinaryExpression") {
|
|
1036
|
+
if (arg.right?.type === "Literal" && typeof arg.right.value === "string") arg.right.value = addPrefix(arg.right.value);
|
|
1037
|
+
} else if (arg.type === "ObjectExpression") arg.properties.forEach((prop) => {
|
|
1038
|
+
if (prop.type === "Property" && prop.value?.type === "Literal" && typeof prop.value.value === "string") {
|
|
1039
|
+
if (!isVariantProperty(prop)) prop.value.value = addPrefix(prop.value.value);
|
|
1040
|
+
}
|
|
1041
|
+
});
|
|
1042
|
+
else astHelpers.findAll(arg, { type: "Literal" }).forEach((literal) => {
|
|
1043
|
+
if (typeof literal.value === "string") {
|
|
1044
|
+
let shouldTransform = true;
|
|
1045
|
+
let parent = literal.parent;
|
|
1046
|
+
while (parent) {
|
|
1047
|
+
if (isVariantProperty(parent)) {
|
|
1048
|
+
shouldTransform = false;
|
|
1049
|
+
break;
|
|
1050
|
+
}
|
|
1051
|
+
parent = parent.parent;
|
|
1052
|
+
}
|
|
1053
|
+
if (shouldTransform) literal.value = addPrefix(literal.value);
|
|
1054
|
+
}
|
|
1055
|
+
});
|
|
1056
|
+
});
|
|
1057
|
+
else if (expression.type === "ConditionalExpression") {
|
|
1058
|
+
if (expression.consequent) traverseExpression(expression.consequent);
|
|
1059
|
+
if (expression.alternate) traverseExpression(expression.alternate);
|
|
1060
|
+
} else if (expression.type === "BinaryExpression") {
|
|
1061
|
+
if (expression.left) traverseExpression(expression.left);
|
|
1062
|
+
if (expression.right) traverseExpression(expression.right);
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
for (const scriptAST of scriptASTs) traverseScriptAST(scriptAST, { visitCallExpression(path$1) {
|
|
1066
|
+
if (path$1.node.callee.type === "Identifier" && path$1.node.callee.name === "cva") {
|
|
1067
|
+
const args = path$1.node.arguments;
|
|
1068
|
+
if (args[0]?.type === "Literal" && typeof args[0].value === "string") args[0].value = addPrefix(args[0].value);
|
|
1069
|
+
if (args[1]?.type === "ObjectExpression") {
|
|
1070
|
+
const variantsProperty = args[1].properties.find((prop) => prop.type === "Property" && prop.key.type === "Identifier" && prop.key.name === "variants");
|
|
1071
|
+
if (variantsProperty && variantsProperty.type === "Property" && variantsProperty.value.type === "ObjectExpression") astHelpers.findAll(variantsProperty.value, { type: "Property" }).forEach((prop) => {
|
|
1072
|
+
if (prop.value?.type === "Literal" && typeof prop.value.value === "string") prop.value.value = addPrefix(prop.value.value);
|
|
1073
|
+
else if (prop.value?.type === "ArrayExpression") prop.value.elements.forEach((element) => {
|
|
1074
|
+
if (element?.type === "Literal" && typeof element.value === "string") element.value = addPrefix(element.value);
|
|
1075
|
+
});
|
|
1076
|
+
});
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
if (path$1.node.callee.type === "Identifier" && path$1.node.callee.name === "cn") path$1.node.arguments.forEach((arg) => {
|
|
1080
|
+
if (arg.type === "Literal" && typeof arg.value === "string") arg.value = addPrefix(arg.value);
|
|
1081
|
+
else if (arg.type === "ConditionalExpression") {
|
|
1082
|
+
if (arg.consequent?.type === "Literal" && typeof arg.consequent.value === "string") arg.consequent.value = addPrefix(arg.consequent.value);
|
|
1083
|
+
if (arg.alternate?.type === "Literal" && typeof arg.alternate.value === "string") arg.alternate.value = addPrefix(arg.alternate.value);
|
|
1084
|
+
} else if (arg.type === "BinaryExpression") {
|
|
1085
|
+
if (arg.right?.type === "Literal" && typeof arg.right.value === "string") arg.right.value = addPrefix(arg.right.value);
|
|
1086
|
+
} else if (arg.type === "ObjectExpression") arg.properties.forEach((prop) => {
|
|
1087
|
+
if (prop.type === "Property" && prop.value?.type === "Literal" && typeof prop.value.value === "string") {
|
|
1088
|
+
if (!isVariantProperty(prop)) prop.value.value = addPrefix(prop.value.value);
|
|
1089
|
+
}
|
|
1090
|
+
});
|
|
1091
|
+
else astHelpers.findAll(arg, { type: "Literal" }).forEach((literal) => {
|
|
1092
|
+
if (typeof literal.value === "string") {
|
|
1093
|
+
let shouldTransform = true;
|
|
1094
|
+
let parent = literal.parent;
|
|
1095
|
+
while (parent) {
|
|
1096
|
+
if (isVariantProperty(parent)) {
|
|
1097
|
+
shouldTransform = false;
|
|
1098
|
+
break;
|
|
1099
|
+
}
|
|
1100
|
+
parent = parent.parent;
|
|
1101
|
+
}
|
|
1102
|
+
if (shouldTransform) literal.value = addPrefix(literal.value);
|
|
1103
|
+
}
|
|
1104
|
+
});
|
|
1105
|
+
});
|
|
1106
|
+
return this.traverse(path$1);
|
|
1107
|
+
} });
|
|
1108
|
+
if (sfcAST) traverseTemplateAST(sfcAST, {
|
|
1109
|
+
enterNode(node) {
|
|
1110
|
+
if (node.type === "VAttribute" && node.key.type === "VDirectiveKey") {
|
|
1111
|
+
if (node.key.argument?.type === "VIdentifier") {
|
|
1112
|
+
const argName = node.key.argument.name;
|
|
1113
|
+
if ([
|
|
1114
|
+
"class",
|
|
1115
|
+
"className",
|
|
1116
|
+
"classes",
|
|
1117
|
+
"classNames"
|
|
1118
|
+
].includes(argName)) {
|
|
1119
|
+
if (node.value?.type === "VExpressionContainer" && node.value.expression) traverseExpression(node.value.expression);
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
} else if (node.type === "VLiteral" && typeof node.value === "string") {
|
|
1123
|
+
if (node.parent?.type === "VAttribute" && node.parent.key?.type === "VIdentifier" && [
|
|
1124
|
+
"class",
|
|
1125
|
+
"className",
|
|
1126
|
+
"classes",
|
|
1127
|
+
"classNames"
|
|
1128
|
+
].includes(node.parent.key.name)) node.value = `"${addPrefix(node.value.replace(/"/g, ""))}"`;
|
|
1129
|
+
}
|
|
1130
|
+
},
|
|
1131
|
+
leaveNode() {}
|
|
1132
|
+
});
|
|
1133
|
+
return transformCount;
|
|
1134
|
+
}
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
function applyPrefix(input, prefix = "", tailwindVersion) {
|
|
1138
|
+
if (tailwindVersion === "v3") return input.split(" ").map((className) => {
|
|
1139
|
+
const [variant, value, modifier] = splitClassName(className);
|
|
1140
|
+
if (variant) return modifier ? `${variant}:${prefix}${value}/${modifier}` : `${variant}:${prefix}${value}`;
|
|
1141
|
+
else return modifier ? `${prefix}${value}/${modifier}` : `${prefix}${value}`;
|
|
1142
|
+
}).join(" ");
|
|
1143
|
+
return input.split(" ").map((className) => className.indexOf(`${prefix}:`) === 0 ? className : `${prefix}:${className.trim()}`).join(" ");
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
//#endregion
|
|
1147
|
+
//#region src/utils/icon-libraries.ts
|
|
1148
|
+
const ICON_LIBRARIES = {
|
|
1149
|
+
lucide: {
|
|
1150
|
+
name: "lucide-vue-next",
|
|
1151
|
+
package: "lucide-vue-next",
|
|
1152
|
+
import: "lucide-vue-next"
|
|
1153
|
+
},
|
|
1154
|
+
radix: {
|
|
1155
|
+
name: "@radix-icons/vue",
|
|
1156
|
+
package: "@radix-icons/vue",
|
|
1157
|
+
import: "@radix-icons/vue"
|
|
1158
|
+
},
|
|
1159
|
+
tabler: {
|
|
1160
|
+
name: "@tabler/icons-vue",
|
|
1161
|
+
package: "@tabler/icons-vue",
|
|
1162
|
+
import: "@tabler/icons-vue"
|
|
1163
|
+
},
|
|
1164
|
+
phosphor: {
|
|
1165
|
+
name: "@phosphor-icons/vue",
|
|
1166
|
+
package: "@phosphor-icons/vue",
|
|
1167
|
+
import: "@phosphor-icons/vue"
|
|
1168
|
+
}
|
|
1169
|
+
};
|
|
1170
|
+
|
|
1171
|
+
//#endregion
|
|
1172
|
+
//#region src/utils/transformers/transform-icons.ts
|
|
1173
|
+
const SOURCE_LIBRARY = "lucide";
|
|
1174
|
+
const ICON_LIBRARY_IMPORTS = new Set(Object.values(ICON_LIBRARIES).map((l) => l.import).filter(Boolean));
|
|
1175
|
+
function transformIcons(opts, registryIcons) {
|
|
1176
|
+
return {
|
|
1177
|
+
type: "codemod",
|
|
1178
|
+
name: "modify import of icon library on user config",
|
|
1179
|
+
transform({ scriptASTs, sfcAST, utils: { traverseScriptAST, traverseTemplateAST } }) {
|
|
1180
|
+
let transformCount = 0;
|
|
1181
|
+
const { config } = opts;
|
|
1182
|
+
if (!config.iconLibrary || !(config.iconLibrary in ICON_LIBRARIES)) return transformCount;
|
|
1183
|
+
const sourceLibrary = SOURCE_LIBRARY;
|
|
1184
|
+
const targetLibrary = config.iconLibrary;
|
|
1185
|
+
if (sourceLibrary === targetLibrary) return transformCount;
|
|
1186
|
+
const targetedIconsMap = /* @__PURE__ */ new Map();
|
|
1187
|
+
for (const scriptAST of scriptASTs) traverseScriptAST(scriptAST, { visitImportDeclaration(path$1) {
|
|
1188
|
+
const source = String(path$1.node.source.value);
|
|
1189
|
+
if (![...ICON_LIBRARY_IMPORTS].some((prefix) => source.startsWith(prefix))) return this.traverse(path$1);
|
|
1190
|
+
let hasChanges = false;
|
|
1191
|
+
for (const specifier of path$1.node.specifiers ?? []) if (specifier.type === "ImportSpecifier") {
|
|
1192
|
+
const iconName = specifier.imported.name;
|
|
1193
|
+
const targetedIcon = registryIcons[iconName]?.[targetLibrary];
|
|
1194
|
+
if (!targetedIcon || targetedIconsMap.has(iconName)) continue;
|
|
1195
|
+
targetedIconsMap.set(iconName, targetedIcon);
|
|
1196
|
+
specifier.imported.name = targetedIcon;
|
|
1197
|
+
hasChanges = true;
|
|
1198
|
+
}
|
|
1199
|
+
if (hasChanges) {
|
|
1200
|
+
path$1.node.source.value = ICON_LIBRARIES[targetLibrary].import;
|
|
1201
|
+
transformCount++;
|
|
1202
|
+
}
|
|
1203
|
+
return this.traverse(path$1);
|
|
1204
|
+
} });
|
|
1205
|
+
if (sfcAST && targetedIconsMap.size > 0) traverseTemplateAST(sfcAST, { enterNode(node) {
|
|
1206
|
+
if (node.type === "VElement" && targetedIconsMap.has(node.rawName)) {
|
|
1207
|
+
node.rawName = targetedIconsMap.get(node.rawName) ?? "";
|
|
1208
|
+
transformCount++;
|
|
1209
|
+
}
|
|
1210
|
+
} });
|
|
1211
|
+
return transformCount;
|
|
1212
|
+
}
|
|
1213
|
+
};
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
//#endregion
|
|
1217
|
+
//#region src/utils/transformers/index.ts
|
|
1218
|
+
async function transform$2(opts) {
|
|
1219
|
+
const source = await transformSFC(opts);
|
|
1220
|
+
const registryIcons = await getRegistryIcons();
|
|
1221
|
+
return transform(source, opts.filename, [
|
|
1222
|
+
transformImport(opts),
|
|
1223
|
+
transformCssVars(opts),
|
|
1224
|
+
await transformTwPrefix(opts),
|
|
1225
|
+
transformIcons(opts, registryIcons)
|
|
1226
|
+
]).code;
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
//#endregion
|
|
1230
|
+
//#region src/utils/updaters/update-files.ts
|
|
1231
|
+
async function updateFiles(files, config, options) {
|
|
1232
|
+
if (!files?.length) return {
|
|
1233
|
+
filesCreated: [],
|
|
1234
|
+
filesUpdated: [],
|
|
1235
|
+
filesSkipped: []
|
|
1236
|
+
};
|
|
1237
|
+
options = {
|
|
1238
|
+
overwrite: false,
|
|
1239
|
+
force: false,
|
|
1240
|
+
silent: false,
|
|
1241
|
+
isRemote: false,
|
|
1242
|
+
isWorkspace: false,
|
|
1243
|
+
...options
|
|
1244
|
+
};
|
|
1245
|
+
const filesCreatedSpinner = spinner(`Updating files.`, { silent: options.silent })?.start();
|
|
1246
|
+
const [projectInfo, baseColor] = await Promise.all([getProjectInfo(config.resolvedPaths.cwd), config.tailwind.baseColor ? getRegistryBaseColor(config.tailwind.baseColor) : Promise.resolve(void 0)]);
|
|
1247
|
+
let filesCreated = [];
|
|
1248
|
+
let filesUpdated = [];
|
|
1249
|
+
let filesSkipped = [];
|
|
1250
|
+
let envVarsAdded = [];
|
|
1251
|
+
let envFile = null;
|
|
1252
|
+
for (let index = 0; index < files.length; index++) {
|
|
1253
|
+
const file = files[index];
|
|
1254
|
+
if (!file.content) continue;
|
|
1255
|
+
let filePath = resolveFilePath(file, config, {
|
|
1256
|
+
framework: projectInfo?.framework.name,
|
|
1257
|
+
commonRoot: findCommonRoot(files.map((f) => f.path), file.path),
|
|
1258
|
+
path: options.path,
|
|
1259
|
+
fileIndex: index
|
|
1260
|
+
});
|
|
1261
|
+
if (!filePath) continue;
|
|
1262
|
+
basename(file.path);
|
|
1263
|
+
const targetDir = path.dirname(filePath);
|
|
1264
|
+
if (!config.typescript) filePath = filePath.replace(/\.ts?$/, (_match) => ".js");
|
|
1265
|
+
if (isEnvFile(filePath) && !existsSync(filePath)) {
|
|
1266
|
+
const alternativeEnvFile = findExistingEnvFile(targetDir);
|
|
1267
|
+
if (alternativeEnvFile) filePath = alternativeEnvFile;
|
|
1268
|
+
}
|
|
1269
|
+
const existingFile = existsSync(filePath);
|
|
1270
|
+
if (existingFile && statSync(filePath).isDirectory()) throw new Error(`Cannot write to ${filePath}: path exists and is a directory. Please provide a file path instead.`);
|
|
1271
|
+
const content = isEnvFile(filePath) ? file.content : await transform$2({
|
|
1272
|
+
filename: file.path,
|
|
1273
|
+
raw: file.content,
|
|
1274
|
+
config,
|
|
1275
|
+
baseColor,
|
|
1276
|
+
isRemote: options.isRemote
|
|
1277
|
+
});
|
|
1278
|
+
if (existingFile && !isEnvFile(filePath)) {
|
|
1279
|
+
if (isContentSame(await promises.readFile(filePath, "utf-8"), content, { ignoreImports: options.isWorkspace })) {
|
|
1280
|
+
filesSkipped.push(path.relative(config.resolvedPaths.cwd, filePath));
|
|
1281
|
+
continue;
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
if (existingFile && !options.overwrite && !isEnvFile(filePath)) {
|
|
1285
|
+
filesCreatedSpinner.stop();
|
|
1286
|
+
if (options.rootSpinner) options.rootSpinner.stop();
|
|
1287
|
+
const { overwrite } = await prompts({
|
|
1288
|
+
type: "confirm",
|
|
1289
|
+
name: "overwrite",
|
|
1290
|
+
message: `The file ${highlighter.info(path.relative(config.resolvedPaths.ui, filePath))} already exists. Would you like to overwrite?`,
|
|
1291
|
+
initial: false
|
|
1292
|
+
});
|
|
1293
|
+
if (!overwrite) {
|
|
1294
|
+
filesSkipped.push(path.relative(config.resolvedPaths.cwd, filePath));
|
|
1295
|
+
if (options.rootSpinner) options.rootSpinner.start();
|
|
1296
|
+
continue;
|
|
1297
|
+
}
|
|
1298
|
+
filesCreatedSpinner?.start();
|
|
1299
|
+
if (options.rootSpinner) options.rootSpinner.start();
|
|
1300
|
+
}
|
|
1301
|
+
if (!existsSync(targetDir)) await promises.mkdir(targetDir, { recursive: true });
|
|
1302
|
+
if (isEnvFile(filePath) && existingFile) {
|
|
1303
|
+
const existingFileContent = await promises.readFile(filePath, "utf-8");
|
|
1304
|
+
const mergedContent = mergeEnvContent(existingFileContent, content);
|
|
1305
|
+
envVarsAdded = getNewEnvKeys(existingFileContent, content);
|
|
1306
|
+
envFile = path.relative(config.resolvedPaths.cwd, filePath);
|
|
1307
|
+
if (!envVarsAdded.length) {
|
|
1308
|
+
filesSkipped.push(path.relative(config.resolvedPaths.cwd, filePath));
|
|
1309
|
+
continue;
|
|
1310
|
+
}
|
|
1311
|
+
await promises.writeFile(filePath, mergedContent, "utf-8");
|
|
1312
|
+
filesUpdated.push(path.relative(config.resolvedPaths.cwd, filePath));
|
|
1313
|
+
continue;
|
|
1314
|
+
}
|
|
1315
|
+
await promises.writeFile(filePath, content, "utf-8");
|
|
1316
|
+
if (!existingFile) {
|
|
1317
|
+
filesCreated.push(path.relative(config.resolvedPaths.cwd, filePath));
|
|
1318
|
+
if (isEnvFile(filePath)) {
|
|
1319
|
+
envVarsAdded = Object.keys(parseEnvContent(content));
|
|
1320
|
+
envFile = path.relative(config.resolvedPaths.cwd, filePath);
|
|
1321
|
+
}
|
|
1322
|
+
} else filesUpdated.push(path.relative(config.resolvedPaths.cwd, filePath));
|
|
1323
|
+
}
|
|
1324
|
+
const updatedFiles = await resolveImports([
|
|
1325
|
+
...filesCreated,
|
|
1326
|
+
...filesUpdated,
|
|
1327
|
+
...filesSkipped
|
|
1328
|
+
], config);
|
|
1329
|
+
filesUpdated.push(...updatedFiles);
|
|
1330
|
+
filesUpdated = filesUpdated.filter((file) => !filesCreated.includes(file));
|
|
1331
|
+
if (!(filesCreated.length || filesUpdated.length) && !filesSkipped.length) filesCreatedSpinner?.info("No files updated.");
|
|
1332
|
+
filesCreated = Array.from(new Set(filesCreated));
|
|
1333
|
+
filesUpdated = Array.from(new Set(filesUpdated));
|
|
1334
|
+
filesSkipped = Array.from(new Set(filesSkipped));
|
|
1335
|
+
if (filesCreated.length) {
|
|
1336
|
+
filesCreatedSpinner?.succeed(`Created ${filesCreated.length} ${filesCreated.length === 1 ? "file" : "files"}:`);
|
|
1337
|
+
if (!options.silent) for (const file of filesCreated) logger.log(` - ${file}`);
|
|
1338
|
+
} else filesCreatedSpinner?.stop();
|
|
1339
|
+
if (filesUpdated.length) {
|
|
1340
|
+
spinner(`Updated ${filesUpdated.length} ${filesUpdated.length === 1 ? "file" : "files"}:`, { silent: options.silent })?.info();
|
|
1341
|
+
if (!options.silent) for (const file of filesUpdated) logger.log(` - ${file}`);
|
|
1342
|
+
}
|
|
1343
|
+
if (filesSkipped.length) {
|
|
1344
|
+
spinner(`Skipped ${filesSkipped.length} ${filesUpdated.length === 1 ? "file" : "files"}: (files might be identical, use --overwrite to overwrite)`, { silent: options.silent })?.info();
|
|
1345
|
+
if (!options.silent) for (const file of filesSkipped) logger.log(` - ${file}`);
|
|
1346
|
+
}
|
|
1347
|
+
if (envVarsAdded.length && envFile) {
|
|
1348
|
+
spinner(`Added the following variables to ${highlighter.info(envFile)}:`)?.info();
|
|
1349
|
+
if (!options.silent) for (const key of envVarsAdded) logger.log(` ${highlighter.success("+")} ${key}`);
|
|
1350
|
+
}
|
|
1351
|
+
if (!options.silent) logger.break();
|
|
1352
|
+
return {
|
|
1353
|
+
filesCreated,
|
|
1354
|
+
filesUpdated,
|
|
1355
|
+
filesSkipped
|
|
1356
|
+
};
|
|
1357
|
+
}
|
|
1358
|
+
function resolveFilePath(file, config, options) {
|
|
1359
|
+
if (options.path) {
|
|
1360
|
+
const resolvedPath = path.isAbsolute(options.path) ? options.path : path.join(config.resolvedPaths.cwd, options.path);
|
|
1361
|
+
if (/\.[^/\\]+$/.test(resolvedPath)) {
|
|
1362
|
+
if (options.fileIndex === 0) return resolvedPath;
|
|
1363
|
+
} else {
|
|
1364
|
+
const fileName = path.basename(file.path);
|
|
1365
|
+
return path.join(resolvedPath, fileName);
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
if (file.target) {
|
|
1369
|
+
if (file.target.startsWith("~/")) return path.join(config.resolvedPaths.cwd, file.target.replace("~/", ""));
|
|
1370
|
+
let target = file.target;
|
|
1371
|
+
if (file.type === "registry:page") {
|
|
1372
|
+
target = resolvePageTarget(target, options.framework);
|
|
1373
|
+
if (!target) return "";
|
|
1374
|
+
}
|
|
1375
|
+
return path.join(config.resolvedPaths.cwd, target.replace("src/", ""));
|
|
1376
|
+
}
|
|
1377
|
+
const targetDir = resolveFileTargetDirectory(file, config);
|
|
1378
|
+
const relativePath = resolveNestedFilePath(file.path, options.commonRoot, config);
|
|
1379
|
+
return path.join(targetDir, relativePath);
|
|
1380
|
+
}
|
|
1381
|
+
function resolveFileTargetDirectory(file, config) {
|
|
1382
|
+
if (file.type === "registry:ui") return config.resolvedPaths.ui;
|
|
1383
|
+
if (file.type === "registry:lib") return config.resolvedPaths.lib;
|
|
1384
|
+
if (file.type === "registry:block" || file.type === "registry:component") return config.resolvedPaths.components;
|
|
1385
|
+
if (file.type === "registry:hook" || file.type === "registry:composable") return config.resolvedPaths.composables;
|
|
1386
|
+
return config.resolvedPaths.components;
|
|
1387
|
+
}
|
|
1388
|
+
function findCommonRoot(paths, needle) {
|
|
1389
|
+
const normalizedPaths = paths.map((p) => p.replace(/^\//, ""));
|
|
1390
|
+
const normalizedNeedle = needle.replace(/^\//, "");
|
|
1391
|
+
const needleDir = normalizedNeedle.split("/").slice(0, -1).join("/");
|
|
1392
|
+
if (!needleDir) return "";
|
|
1393
|
+
const needleSegments = needleDir.split("/");
|
|
1394
|
+
for (let i = needleSegments.length; i > 0; i--) {
|
|
1395
|
+
const testPath = needleSegments.slice(0, i).join("/");
|
|
1396
|
+
if (normalizedPaths.some((path$1) => path$1 !== normalizedNeedle && path$1.startsWith(`${testPath}/`))) return `/${testPath}`;
|
|
1397
|
+
}
|
|
1398
|
+
return `/${needleDir}`;
|
|
1399
|
+
}
|
|
1400
|
+
function resolveNestedFilePath(filePath, commonRoot, config) {
|
|
1401
|
+
const normalizedFilePath = filePath.replace(/^\/|\/$/g, "");
|
|
1402
|
+
const normalizedCommonRoot = commonRoot.replace(/^\/|\/$/g, "");
|
|
1403
|
+
const aliases = Object.values(config.aliases).map((alias) => alias.replace(/^@\//, "").replace(/^\/|\/$/g, "")).sort((a, b) => b.length - a.length);
|
|
1404
|
+
for (const alias of aliases) if (normalizedCommonRoot.includes(alias)) {
|
|
1405
|
+
const aliasEndIndex = normalizedFilePath.indexOf(alias) + alias.length;
|
|
1406
|
+
return normalizedFilePath.substring(aliasEndIndex).replace(/^\//, "");
|
|
1407
|
+
}
|
|
1408
|
+
return normalizedCommonRoot.split("/").pop() + normalizedFilePath.replace(normalizedCommonRoot, "");
|
|
1409
|
+
}
|
|
1410
|
+
function resolvePageTarget(target, framework) {
|
|
1411
|
+
if (!framework) return "";
|
|
1412
|
+
if (framework === "nuxt3" || framework === "nuxt4") return target;
|
|
1413
|
+
if (framework === "laravel") {
|
|
1414
|
+
let result = target.replace(/^app\//, "resources/js/pages/");
|
|
1415
|
+
result = result.replace(/\/page(\.[jt]sx?)$/, "$1");
|
|
1416
|
+
return result;
|
|
1417
|
+
}
|
|
1418
|
+
return "";
|
|
1419
|
+
}
|
|
1420
|
+
async function resolveImports(filePaths, config) {
|
|
1421
|
+
const projectInfo = await getProjectInfo(config.resolvedPaths.cwd);
|
|
1422
|
+
const tsConfig = getTsconfig(config.resolvedPaths.cwd);
|
|
1423
|
+
const updatedFiles = [];
|
|
1424
|
+
if (!projectInfo || tsConfig === null) return [];
|
|
1425
|
+
for (const filepath of filePaths) {
|
|
1426
|
+
const resolvedPath = path.resolve(config.resolvedPaths.cwd, filepath);
|
|
1427
|
+
if (!existsSync(resolvedPath)) continue;
|
|
1428
|
+
const content = await promises.readFile(resolvedPath, "utf-8");
|
|
1429
|
+
try {
|
|
1430
|
+
const importResolverTransformer = (_opts) => ({
|
|
1431
|
+
name: "import-resolver",
|
|
1432
|
+
transform(node) {
|
|
1433
|
+
if (node.type === "ImportDeclaration" && node.source?.value) {
|
|
1434
|
+
const moduleSpecifier = node.source.value;
|
|
1435
|
+
if (projectInfo?.aliasPrefix && !moduleSpecifier.startsWith(`${projectInfo.aliasPrefix}/`)) return;
|
|
1436
|
+
const probableImportFilePath = resolveImport(moduleSpecifier, tsConfig);
|
|
1437
|
+
if (!probableImportFilePath) return;
|
|
1438
|
+
const resolvedImportFilePath = resolveModuleByProbablePath(probableImportFilePath, filePaths, config);
|
|
1439
|
+
if (!resolvedImportFilePath) return;
|
|
1440
|
+
const newImport = toAliasedImport(resolvedImportFilePath, config, projectInfo);
|
|
1441
|
+
if (!newImport || newImport === moduleSpecifier) return;
|
|
1442
|
+
node.source.value = newImport;
|
|
1443
|
+
node.source.raw = `'${newImport}'`;
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
});
|
|
1447
|
+
const result = transform(content, resolvedPath, [importResolverTransformer({})]);
|
|
1448
|
+
if (result.code !== content) {
|
|
1449
|
+
await promises.writeFile(resolvedPath, result.code, "utf-8");
|
|
1450
|
+
updatedFiles.push(filepath);
|
|
1451
|
+
}
|
|
1452
|
+
} catch (error) {
|
|
1453
|
+
console.warn(`Failed to transform imports in ${filepath}:`, error);
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
return updatedFiles;
|
|
1457
|
+
}
|
|
1458
|
+
/**
|
|
1459
|
+
* Given an absolute "probable" import path (no ext),
|
|
1460
|
+
* plus an array of absolute file paths you already know about,
|
|
1461
|
+
* return 0–N matches (best match first), and also check disk for any missing ones.
|
|
1462
|
+
*/
|
|
1463
|
+
function resolveModuleByProbablePath(probableImportFilePath, files, config, extensions = [
|
|
1464
|
+
".vue",
|
|
1465
|
+
".ts",
|
|
1466
|
+
".js",
|
|
1467
|
+
".tsx",
|
|
1468
|
+
".jsx",
|
|
1469
|
+
".css"
|
|
1470
|
+
]) {
|
|
1471
|
+
const cwd = path.normalize(config.resolvedPaths.cwd);
|
|
1472
|
+
const relativeFiles = files.map((f) => f.split(path.sep).join(path.posix.sep));
|
|
1473
|
+
const fileSet = new Set(relativeFiles);
|
|
1474
|
+
const extInPath = path.extname(probableImportFilePath);
|
|
1475
|
+
const hasExt = extInPath !== "";
|
|
1476
|
+
const absBase = hasExt ? probableImportFilePath.slice(0, -extInPath.length) : probableImportFilePath;
|
|
1477
|
+
const relBase = path.relative(cwd, absBase).split(path.sep).join(path.posix.sep);
|
|
1478
|
+
const tryExts = hasExt ? [extInPath] : extensions;
|
|
1479
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
1480
|
+
for (const e of tryExts) {
|
|
1481
|
+
const absCand = absBase + e;
|
|
1482
|
+
const relCand = path.posix.normalize(path.relative(cwd, absCand));
|
|
1483
|
+
if (fileSet.has(relCand) || existsSync(absCand)) candidates.add(relCand);
|
|
1484
|
+
const absIdx = path.join(absBase, `index${e}`);
|
|
1485
|
+
const relIdx = path.posix.normalize(path.relative(cwd, absIdx));
|
|
1486
|
+
if (fileSet.has(relIdx) || existsSync(absIdx)) candidates.add(relIdx);
|
|
1487
|
+
}
|
|
1488
|
+
const name = path.basename(absBase);
|
|
1489
|
+
for (const f of relativeFiles) if (tryExts.some((e) => f.endsWith(`/${name}${e}`))) candidates.add(f);
|
|
1490
|
+
if (candidates.size === 0) return null;
|
|
1491
|
+
return Array.from(candidates).sort((a, b) => {
|
|
1492
|
+
const aExt = path.posix.extname(a);
|
|
1493
|
+
const bExt = path.posix.extname(b);
|
|
1494
|
+
const ord = tryExts.indexOf(aExt) - tryExts.indexOf(bExt);
|
|
1495
|
+
if (ord !== 0) return ord;
|
|
1496
|
+
return (relBase && a.startsWith(relBase) ? -1 : 1) - (relBase && b.startsWith(relBase) ? -1 : 1);
|
|
1497
|
+
})[0];
|
|
1498
|
+
}
|
|
1499
|
+
function toAliasedImport(filePath, config, projectInfo) {
|
|
1500
|
+
const abs = path.normalize(path.join(config.resolvedPaths.cwd, filePath));
|
|
1501
|
+
const matches = Object.entries(config.resolvedPaths).filter(([, root]) => root && abs.startsWith(path.normalize(root + path.sep))).sort((a, b) => b[1].length - a[1].length);
|
|
1502
|
+
if (matches.length === 0) return null;
|
|
1503
|
+
const [aliasKey, rootDir] = matches[0];
|
|
1504
|
+
let rel = path.relative(rootDir, abs);
|
|
1505
|
+
rel = rel.split(path.sep).join("/");
|
|
1506
|
+
const ext = path.posix.extname(rel);
|
|
1507
|
+
const keepExt = [
|
|
1508
|
+
".vue",
|
|
1509
|
+
".ts",
|
|
1510
|
+
".tsx",
|
|
1511
|
+
".js",
|
|
1512
|
+
".jsx"
|
|
1513
|
+
].includes(ext) ? "" : ext;
|
|
1514
|
+
let noExt = rel.slice(0, rel.length - ext.length);
|
|
1515
|
+
if (noExt.endsWith("/index")) noExt = noExt.slice(0, -6);
|
|
1516
|
+
const aliasBase = aliasKey === "cwd" ? projectInfo.aliasPrefix : config.aliases[aliasKey];
|
|
1517
|
+
if (!aliasBase) return null;
|
|
1518
|
+
let suffix = noExt === "" ? "" : `/${noExt}`;
|
|
1519
|
+
suffix = suffix.replace("/src", "");
|
|
1520
|
+
return `${aliasBase}${suffix}${keepExt}`;
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
//#endregion
|
|
1524
|
+
//#region src/registry/utils.ts
|
|
1525
|
+
const project = new Project({ compilerOptions: {} });
|
|
1526
|
+
function isUrl$1(path$1) {
|
|
1527
|
+
try {
|
|
1528
|
+
new URL(path$1);
|
|
1529
|
+
return true;
|
|
1530
|
+
} catch (_error) {
|
|
1531
|
+
return false;
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
function isLocalFile(path$1) {
|
|
1535
|
+
return path$1.endsWith(".json") && !isUrl$1(path$1);
|
|
1536
|
+
}
|
|
1537
|
+
/**
|
|
1538
|
+
* Check if a registry item is universal (framework-agnostic).
|
|
1539
|
+
* A universal registry item must:
|
|
1540
|
+
* 1. Have type "registry:item" or "registry:file"
|
|
1541
|
+
* 2. If it has files, all files must have explicit targets and be type "registry:file" or "registry:item"
|
|
1542
|
+
* It can be installed without framework detection or components.json.
|
|
1543
|
+
*/
|
|
1544
|
+
function isUniversalRegistryItem(registryItem) {
|
|
1545
|
+
if (!registryItem) return false;
|
|
1546
|
+
if (registryItem.type !== "registry:item" && registryItem.type !== "registry:file") return false;
|
|
1547
|
+
return (registryItem.files ?? []).every((file) => !!file.target && (file.type === "registry:file" || file.type === "registry:item"));
|
|
1548
|
+
}
|
|
1549
|
+
async function deduplicateFilesByTarget(filesArrays, config) {
|
|
1550
|
+
if (!canDeduplicateFiles(config)) return z.array(registryItemFileSchema).parse(filesArrays.flat().filter(Boolean));
|
|
1551
|
+
const projectInfo = await getProjectInfo(config.resolvedPaths.cwd);
|
|
1552
|
+
const targetMap = /* @__PURE__ */ new Map();
|
|
1553
|
+
const allFiles = z.array(registryItemFileSchema).parse(filesArrays.flat().filter(Boolean));
|
|
1554
|
+
allFiles.forEach((file) => {
|
|
1555
|
+
const resolvedPath = resolveFilePath(file, config, {
|
|
1556
|
+
framework: projectInfo?.framework.name,
|
|
1557
|
+
commonRoot: findCommonRoot(allFiles.map((f) => f.path), file.path)
|
|
1558
|
+
});
|
|
1559
|
+
if (resolvedPath) targetMap.set(resolvedPath, file);
|
|
1560
|
+
});
|
|
1561
|
+
return Array.from(targetMap.values());
|
|
1562
|
+
}
|
|
1563
|
+
function canDeduplicateFiles(config) {
|
|
1564
|
+
return !!(config?.resolvedPaths?.cwd && (config?.resolvedPaths?.ui || config?.resolvedPaths?.lib || config?.resolvedPaths?.components || config?.resolvedPaths?.composables));
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
//#endregion
|
|
1568
|
+
//#region src/registry/config.ts
|
|
1569
|
+
function resolveStyleFromConfig(config) {
|
|
1570
|
+
if (!config.style) return FALLBACK_STYLE;
|
|
1571
|
+
if (config.style === "new-york" && config.tailwind?.config === "") return FALLBACK_STYLE;
|
|
1572
|
+
return config.style;
|
|
1573
|
+
}
|
|
1574
|
+
function configWithDefaults(config) {
|
|
1575
|
+
const baseConfig = createConfig({
|
|
1576
|
+
style: FALLBACK_STYLE,
|
|
1577
|
+
registries: BUILTIN_REGISTRIES
|
|
1578
|
+
});
|
|
1579
|
+
if (!config) return baseConfig;
|
|
1580
|
+
return configSchema.parse(deepmerge(baseConfig, {
|
|
1581
|
+
...config,
|
|
1582
|
+
style: resolveStyleFromConfig(config),
|
|
1583
|
+
registries: {
|
|
1584
|
+
...BUILTIN_REGISTRIES,
|
|
1585
|
+
...config.registries
|
|
1586
|
+
}
|
|
1587
|
+
}));
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
//#endregion
|
|
1591
|
+
//#region src/registry/context.ts
|
|
1592
|
+
const context = { headers: {} };
|
|
1593
|
+
function setRegistryHeaders(headers) {
|
|
1594
|
+
context.headers = {
|
|
1595
|
+
...context.headers,
|
|
1596
|
+
...headers
|
|
1597
|
+
};
|
|
1598
|
+
}
|
|
1599
|
+
function getRegistryHeadersFromContext(url) {
|
|
1600
|
+
return context.headers[url] || {};
|
|
1601
|
+
}
|
|
1602
|
+
function clearRegistryContext() {
|
|
1603
|
+
context.headers = {};
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
//#endregion
|
|
1607
|
+
//#region src/registry/validator.ts
|
|
1608
|
+
function extractEnvVarsFromRegistryConfig(config) {
|
|
1609
|
+
const vars = /* @__PURE__ */ new Set();
|
|
1610
|
+
if (typeof config === "string") extractEnvVars(config).forEach((v) => vars.add(v));
|
|
1611
|
+
else {
|
|
1612
|
+
extractEnvVars(config.url).forEach((v) => vars.add(v));
|
|
1613
|
+
if (config.params) Object.values(config.params).forEach((value) => {
|
|
1614
|
+
extractEnvVars(value).forEach((v) => vars.add(v));
|
|
1615
|
+
});
|
|
1616
|
+
if (config.headers) Object.values(config.headers).forEach((value) => {
|
|
1617
|
+
extractEnvVars(value).forEach((v) => vars.add(v));
|
|
1618
|
+
});
|
|
1619
|
+
}
|
|
1620
|
+
return Array.from(vars);
|
|
1621
|
+
}
|
|
1622
|
+
function validateRegistryConfig(registryName, config) {
|
|
1623
|
+
const missing = extractEnvVarsFromRegistryConfig(config).filter((v) => !process.env[v]);
|
|
1624
|
+
if (missing.length > 0) throw new RegistryMissingEnvironmentVariablesError(registryName, missing);
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
//#endregion
|
|
1628
|
+
//#region src/registry/builder.ts
|
|
1629
|
+
const NAME_PLACEHOLDER = "{name}";
|
|
1630
|
+
const STYLE_PLACEHOLDER = "{style}";
|
|
1631
|
+
const ENV_VAR_PATTERN = /\$\{(\w+)\}/g;
|
|
1632
|
+
const QUERY_PARAM_SEPARATOR = "?";
|
|
1633
|
+
const QUERY_PARAM_DELIMITER = "&";
|
|
1634
|
+
function buildUrlAndHeadersForRegistryItem(name, config) {
|
|
1635
|
+
const { registry, item } = parseRegistryAndItemFromString(name);
|
|
1636
|
+
if (!registry) return null;
|
|
1637
|
+
const registryConfig = (config?.registries || {})[registry];
|
|
1638
|
+
if (!registryConfig) throw new RegistryNotConfiguredError(registry);
|
|
1639
|
+
validateRegistryConfig(registry, registryConfig);
|
|
1640
|
+
return {
|
|
1641
|
+
url: buildUrlFromRegistryConfig(item, registryConfig, config),
|
|
1642
|
+
headers: buildHeadersFromRegistryConfig(registryConfig)
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1645
|
+
function buildUrlFromRegistryConfig(item, registryConfig, config) {
|
|
1646
|
+
if (typeof registryConfig === "string") {
|
|
1647
|
+
let url = registryConfig.replace(NAME_PLACEHOLDER, item);
|
|
1648
|
+
if (config?.style && url.includes(STYLE_PLACEHOLDER)) url = url.replace(STYLE_PLACEHOLDER, config.style);
|
|
1649
|
+
return expandEnvVars(url);
|
|
1650
|
+
}
|
|
1651
|
+
let baseUrl = registryConfig.url.replace(NAME_PLACEHOLDER, item);
|
|
1652
|
+
if (config?.style && baseUrl.includes(STYLE_PLACEHOLDER)) baseUrl = baseUrl.replace(STYLE_PLACEHOLDER, config.style);
|
|
1653
|
+
baseUrl = expandEnvVars(baseUrl);
|
|
1654
|
+
if (!registryConfig.params) return baseUrl;
|
|
1655
|
+
return appendQueryParams(baseUrl, registryConfig.params);
|
|
1656
|
+
}
|
|
1657
|
+
function buildHeadersFromRegistryConfig(config) {
|
|
1658
|
+
if (typeof config === "string" || !config.headers) return {};
|
|
1659
|
+
const headers = {};
|
|
1660
|
+
for (const [key, value] of Object.entries(config.headers)) {
|
|
1661
|
+
const expandedValue = expandEnvVars(value);
|
|
1662
|
+
if (shouldIncludeHeader(value, expandedValue)) headers[key] = expandedValue;
|
|
1663
|
+
}
|
|
1664
|
+
return headers;
|
|
1665
|
+
}
|
|
1666
|
+
function appendQueryParams(baseUrl, params) {
|
|
1667
|
+
const urlParams = new URLSearchParams();
|
|
1668
|
+
for (const [key, value] of Object.entries(params)) {
|
|
1669
|
+
const expandedValue = expandEnvVars(value);
|
|
1670
|
+
if (expandedValue) urlParams.append(key, expandedValue);
|
|
1671
|
+
}
|
|
1672
|
+
const queryString = urlParams.toString();
|
|
1673
|
+
if (!queryString) return baseUrl;
|
|
1674
|
+
return `${baseUrl}${baseUrl.includes(QUERY_PARAM_SEPARATOR) ? QUERY_PARAM_DELIMITER : QUERY_PARAM_SEPARATOR}${queryString}`;
|
|
1675
|
+
}
|
|
1676
|
+
function shouldIncludeHeader(originalValue, expandedValue) {
|
|
1677
|
+
const trimmedExpanded = expandedValue.trim();
|
|
1678
|
+
if (!trimmedExpanded) return false;
|
|
1679
|
+
if (originalValue.includes("${")) {
|
|
1680
|
+
if (originalValue.match(ENV_VAR_PATTERN)) return trimmedExpanded !== originalValue.replace(ENV_VAR_PATTERN, "").trim();
|
|
1681
|
+
}
|
|
1682
|
+
return true;
|
|
1683
|
+
}
|
|
1684
|
+
/**
|
|
1685
|
+
* Resolves a registry URL from a path or URL string.
|
|
1686
|
+
* Handles special cases like v0 registry URLs that need /json suffix.
|
|
1687
|
+
*
|
|
1688
|
+
* @param pathOrUrl - Either a relative path or a full URL
|
|
1689
|
+
* @returns The resolved registry URL
|
|
1690
|
+
*/
|
|
1691
|
+
function resolveRegistryUrl(pathOrUrl) {
|
|
1692
|
+
if (isUrl$1(pathOrUrl)) {
|
|
1693
|
+
const url = new URL(pathOrUrl);
|
|
1694
|
+
if (url.pathname.match(/\/chat\/b\//) && !url.pathname.endsWith("/json")) url.pathname = `${url.pathname}/json`;
|
|
1695
|
+
return url.toString();
|
|
1696
|
+
}
|
|
1697
|
+
return `${REGISTRY_URL}/${pathOrUrl}`;
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
//#endregion
|
|
1701
|
+
//#region src/registry/fetcher.ts
|
|
1702
|
+
const agent = process.env.https_proxy ? new ProxyAgent(process.env.https_proxy) : void 0;
|
|
1703
|
+
const registryCache = /* @__PURE__ */ new Map();
|
|
1704
|
+
async function fetchRegistry(paths, options = {}) {
|
|
1705
|
+
options = {
|
|
1706
|
+
useCache: true,
|
|
1707
|
+
...options
|
|
1708
|
+
};
|
|
1709
|
+
try {
|
|
1710
|
+
return await Promise.all(paths.map(async (path$1) => {
|
|
1711
|
+
const url = resolveRegistryUrl(path$1);
|
|
1712
|
+
if (options.useCache && registryCache.has(url)) return registryCache.get(url);
|
|
1713
|
+
const fetchPromise = (async () => {
|
|
1714
|
+
const headers = getRegistryHeadersFromContext(url);
|
|
1715
|
+
return (await ofetch.raw(url, {
|
|
1716
|
+
agent,
|
|
1717
|
+
dispatcher: agent,
|
|
1718
|
+
parseResponse: JSON.parse,
|
|
1719
|
+
headers: { ...headers }
|
|
1720
|
+
}).catch(async (error) => {
|
|
1721
|
+
if (!error.response) throw new RegistryFetchError(url, void 0, error.message);
|
|
1722
|
+
const response = error.response;
|
|
1723
|
+
let messageFromServer;
|
|
1724
|
+
if (response.headers.get("content-type")?.includes("application/json")) {
|
|
1725
|
+
const json = await response._data;
|
|
1726
|
+
const parsed = z.object({
|
|
1727
|
+
detail: z.string().optional(),
|
|
1728
|
+
title: z.string().optional(),
|
|
1729
|
+
message: z.string().optional(),
|
|
1730
|
+
error: z.string().optional()
|
|
1731
|
+
}).safeParse(json);
|
|
1732
|
+
if (parsed.success) {
|
|
1733
|
+
messageFromServer = parsed.data.detail || parsed.data.message;
|
|
1734
|
+
if (parsed.data.error) messageFromServer = `[${parsed.data.error}] ${messageFromServer}`;
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
if (response.status === 401) throw new RegistryUnauthorizedError(url, messageFromServer);
|
|
1738
|
+
if (response.status === 404) throw new RegistryNotFoundError(url, messageFromServer);
|
|
1739
|
+
if (response.status === 403) throw new RegistryForbiddenError(url, messageFromServer);
|
|
1740
|
+
throw new RegistryFetchError(url, response.status, messageFromServer);
|
|
1741
|
+
}))._data;
|
|
1742
|
+
})();
|
|
1743
|
+
if (options.useCache) registryCache.set(url, fetchPromise);
|
|
1744
|
+
return fetchPromise;
|
|
1745
|
+
}));
|
|
1746
|
+
} catch (error) {
|
|
1747
|
+
throw error;
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
async function fetchRegistryLocal(filePath) {
|
|
1751
|
+
try {
|
|
1752
|
+
let expandedPath = filePath;
|
|
1753
|
+
if (filePath.startsWith("~/")) expandedPath = path.join(homedir(), filePath.slice(2));
|
|
1754
|
+
const resolvedPath = path.resolve(expandedPath);
|
|
1755
|
+
const content = await promises.readFile(resolvedPath, "utf8");
|
|
1756
|
+
const parsed = JSON.parse(content);
|
|
1757
|
+
try {
|
|
1758
|
+
return registryItemSchema.parse(parsed);
|
|
1759
|
+
} catch (error) {
|
|
1760
|
+
throw new RegistryParseError(filePath, error);
|
|
1761
|
+
}
|
|
1762
|
+
} catch (error) {
|
|
1763
|
+
if (error instanceof Error && (error.message.includes("ENOENT") || error.message.includes("no such file"))) throw new RegistryLocalFileError(filePath, error);
|
|
1764
|
+
if (error instanceof RegistryParseError) throw error;
|
|
1765
|
+
throw new RegistryLocalFileError(filePath, error);
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
//#endregion
|
|
1770
|
+
//#region src/utils/handle-error.ts
|
|
1771
|
+
function handleError(error) {
|
|
1772
|
+
logger.break();
|
|
1773
|
+
logger.error(`Something went wrong. Please check the error below for more details.`);
|
|
1774
|
+
logger.error(`If the problem persists, please open an issue on GitHub.`);
|
|
1775
|
+
logger.error("");
|
|
1776
|
+
if (typeof error === "string") {
|
|
1777
|
+
logger.error(error);
|
|
1778
|
+
logger.break();
|
|
1779
|
+
process.exit(1);
|
|
1780
|
+
}
|
|
1781
|
+
if (error instanceof RegistryError) {
|
|
1782
|
+
if (error.message) {
|
|
1783
|
+
logger.error(error.cause ? "Error:" : "Message:");
|
|
1784
|
+
logger.error(error.message);
|
|
1785
|
+
}
|
|
1786
|
+
if (error.cause) {
|
|
1787
|
+
logger.error("\nMessage:");
|
|
1788
|
+
logger.error(error.cause);
|
|
1789
|
+
}
|
|
1790
|
+
if (error.suggestion) {
|
|
1791
|
+
logger.error("\nSuggestion:");
|
|
1792
|
+
logger.error(error.suggestion);
|
|
1793
|
+
}
|
|
1794
|
+
logger.break();
|
|
1795
|
+
process.exit(1);
|
|
1796
|
+
}
|
|
1797
|
+
if (error instanceof z.ZodError) {
|
|
1798
|
+
logger.error("Validation failed:");
|
|
1799
|
+
for (const [key, value] of Object.entries(error.flatten().fieldErrors)) logger.error(`- ${highlighter.info(key)}: ${value}`);
|
|
1800
|
+
logger.break();
|
|
1801
|
+
process.exit(1);
|
|
1802
|
+
}
|
|
1803
|
+
if (error instanceof Error) {
|
|
1804
|
+
logger.error(error.message);
|
|
1805
|
+
logger.break();
|
|
1806
|
+
process.exit(1);
|
|
1807
|
+
}
|
|
1808
|
+
logger.break();
|
|
1809
|
+
process.exit(1);
|
|
1810
|
+
}
|
|
1811
|
+
|
|
1812
|
+
//#endregion
|
|
1813
|
+
//#region src/utils/updaters/update-tailwind-config.ts
|
|
1814
|
+
async function updateTailwindConfig(tailwindConfig, config, options) {
|
|
1815
|
+
if (!tailwindConfig) return;
|
|
1816
|
+
options = {
|
|
1817
|
+
silent: false,
|
|
1818
|
+
tailwindVersion: "v3",
|
|
1819
|
+
...options
|
|
1820
|
+
};
|
|
1821
|
+
if (options.tailwindVersion === "v4") return;
|
|
1822
|
+
const tailwindFileRelativePath = path.relative(config.resolvedPaths.cwd, config.resolvedPaths.tailwindConfig);
|
|
1823
|
+
const tailwindSpinner = spinner(`Updating ${highlighter.info(tailwindFileRelativePath)}`, { silent: options.silent }).start();
|
|
1824
|
+
const output = await transformTailwindConfig(await promises.readFile(config.resolvedPaths.tailwindConfig, "utf8"), tailwindConfig, config);
|
|
1825
|
+
await promises.writeFile(config.resolvedPaths.tailwindConfig, output, "utf8");
|
|
1826
|
+
tailwindSpinner?.succeed();
|
|
1827
|
+
}
|
|
1828
|
+
async function transformTailwindConfig(input, tailwindConfig, config) {
|
|
1829
|
+
const sourceFile = await _createSourceFile(input, config);
|
|
1830
|
+
const configObject = sourceFile.getDescendantsOfKind(SyntaxKind.ObjectLiteralExpression).find((node) => node.getProperties().some((property) => property.isKind(SyntaxKind.PropertyAssignment) && property.getName() === "content"));
|
|
1831
|
+
if (!configObject) return input;
|
|
1832
|
+
addTailwindConfigProperty(configObject, {
|
|
1833
|
+
name: "darkMode",
|
|
1834
|
+
value: "class"
|
|
1835
|
+
}, { quoteChar: _getQuoteChar(configObject) });
|
|
1836
|
+
tailwindConfig.plugins?.forEach((plugin) => {
|
|
1837
|
+
addTailwindConfigPlugin(configObject, plugin);
|
|
1838
|
+
});
|
|
1839
|
+
if (tailwindConfig.theme) await addTailwindConfigTheme(configObject, tailwindConfig.theme);
|
|
1840
|
+
return sourceFile.getFullText();
|
|
1841
|
+
}
|
|
1842
|
+
function addTailwindConfigProperty(configObject, property, { quoteChar }) {
|
|
1843
|
+
const existingProperty = configObject.getProperty("darkMode");
|
|
1844
|
+
if (!existingProperty) {
|
|
1845
|
+
const newProperty = {
|
|
1846
|
+
name: property.name,
|
|
1847
|
+
initializer: `[${quoteChar}${property.value}${quoteChar}]`
|
|
1848
|
+
};
|
|
1849
|
+
if (property.name === "darkMode") {
|
|
1850
|
+
configObject.insertPropertyAssignment(0, newProperty);
|
|
1851
|
+
return configObject;
|
|
1852
|
+
}
|
|
1853
|
+
configObject.addPropertyAssignment(newProperty);
|
|
1854
|
+
return configObject;
|
|
1855
|
+
}
|
|
1856
|
+
if (existingProperty.isKind(SyntaxKind.PropertyAssignment)) {
|
|
1857
|
+
const initializer = existingProperty.getInitializer();
|
|
1858
|
+
const newValue = `${quoteChar}${property.value}${quoteChar}`;
|
|
1859
|
+
if (initializer?.isKind(SyntaxKind.StringLiteral)) {
|
|
1860
|
+
const initializerText = initializer.getText();
|
|
1861
|
+
initializer.replaceWithText(`[${initializerText}, ${newValue}]`);
|
|
1862
|
+
return configObject;
|
|
1863
|
+
}
|
|
1864
|
+
if (initializer?.isKind(SyntaxKind.ArrayLiteralExpression)) {
|
|
1865
|
+
if (initializer.getElements().map((element) => element.getText()).includes(newValue)) return configObject;
|
|
1866
|
+
initializer.addElement(newValue);
|
|
1867
|
+
}
|
|
1868
|
+
return configObject;
|
|
1869
|
+
}
|
|
1870
|
+
return configObject;
|
|
1871
|
+
}
|
|
1872
|
+
async function addTailwindConfigTheme(configObject, theme) {
|
|
1873
|
+
if (!configObject.getProperty("theme")) configObject.addPropertyAssignment({
|
|
1874
|
+
name: "theme",
|
|
1875
|
+
initializer: "{}"
|
|
1876
|
+
});
|
|
1877
|
+
nestSpreadProperties(configObject);
|
|
1878
|
+
const themeInitializer = (configObject.getPropertyOrThrow("theme")?.asKindOrThrow(SyntaxKind.PropertyAssignment)).getInitializer();
|
|
1879
|
+
if (themeInitializer?.isKind(SyntaxKind.ObjectLiteralExpression)) {
|
|
1880
|
+
const resultString = objectToString(deepmerge(await parseObjectLiteral(themeInitializer.getText()), theme, { arrayMerge: (dst, src) => src })).replace(/'\.\.\.(.*)'/g, "...$1").replace(/'"/g, "'").replace(/"'/g, "'").replace(/'\[/g, "[").replace(/\]'/g, "]").replace(/'\\'/g, "'").replace(/\\'/g, "'").replace(/\\''/g, "'").replace(/''/g, "'");
|
|
1881
|
+
themeInitializer.replaceWithText(resultString);
|
|
1882
|
+
}
|
|
1883
|
+
unnestSpreadProperties(configObject);
|
|
1884
|
+
}
|
|
1885
|
+
function addTailwindConfigPlugin(configObject, plugin) {
|
|
1886
|
+
const existingPlugins = configObject.getProperty("plugins");
|
|
1887
|
+
if (!existingPlugins) {
|
|
1888
|
+
configObject.addPropertyAssignment({
|
|
1889
|
+
name: "plugins",
|
|
1890
|
+
initializer: `[${plugin}]`
|
|
1891
|
+
});
|
|
1892
|
+
return configObject;
|
|
1893
|
+
}
|
|
1894
|
+
if (existingPlugins.isKind(SyntaxKind.PropertyAssignment)) {
|
|
1895
|
+
const initializer = existingPlugins.getInitializer();
|
|
1896
|
+
if (initializer?.isKind(SyntaxKind.ArrayLiteralExpression)) {
|
|
1897
|
+
if (initializer.getElements().map((element) => {
|
|
1898
|
+
return element.getText().replace(/["']/g, "");
|
|
1899
|
+
}).includes(plugin.replace(/["']/g, ""))) return configObject;
|
|
1900
|
+
initializer.addElement(plugin);
|
|
1901
|
+
}
|
|
1902
|
+
return configObject;
|
|
1903
|
+
}
|
|
1904
|
+
return configObject;
|
|
1905
|
+
}
|
|
1906
|
+
async function _createSourceFile(input, config) {
|
|
1907
|
+
const dir = await promises.mkdtemp(path.join(tmpdir(), "shadcn-"));
|
|
1908
|
+
const resolvedPath = config?.resolvedPaths?.tailwindConfig || "tailwind.config.ts";
|
|
1909
|
+
const tempFile = path.join(dir, `shadcn-${path.basename(resolvedPath)}`);
|
|
1910
|
+
return new Project({ compilerOptions: {} }).createSourceFile(tempFile, input, { scriptKind: path.extname(resolvedPath) === ".ts" ? ScriptKind.TS : ScriptKind.JS });
|
|
1911
|
+
}
|
|
1912
|
+
function _getQuoteChar(configObject) {
|
|
1913
|
+
return configObject.getFirstDescendantByKind(SyntaxKind.StringLiteral)?.getQuoteKind() === QuoteKind.Single ? "'" : "\"";
|
|
1914
|
+
}
|
|
1915
|
+
function nestSpreadProperties(obj) {
|
|
1916
|
+
const properties = obj.getProperties();
|
|
1917
|
+
for (let i = 0; i < properties.length; i++) {
|
|
1918
|
+
const prop = properties[i];
|
|
1919
|
+
if (prop.isKind(SyntaxKind.SpreadAssignment)) {
|
|
1920
|
+
const spreadAssignment = prop.asKindOrThrow(SyntaxKind.SpreadAssignment);
|
|
1921
|
+
const spreadText = spreadAssignment.getExpression().getText();
|
|
1922
|
+
obj.insertPropertyAssignment(i, {
|
|
1923
|
+
name: `"___${spreadText.replace(/^\.\.\./, "")}"`,
|
|
1924
|
+
initializer: `"...${spreadText.replace(/^\.\.\./, "")}"`
|
|
1925
|
+
});
|
|
1926
|
+
spreadAssignment.remove();
|
|
1927
|
+
} else if (prop.isKind(SyntaxKind.PropertyAssignment)) {
|
|
1928
|
+
const initializer = prop.asKindOrThrow(SyntaxKind.PropertyAssignment).getInitializer();
|
|
1929
|
+
if (initializer && initializer.isKind(SyntaxKind.ObjectLiteralExpression)) nestSpreadProperties(initializer.asKindOrThrow(SyntaxKind.ObjectLiteralExpression));
|
|
1930
|
+
else if (initializer && initializer.isKind(SyntaxKind.ArrayLiteralExpression)) nestSpreadElements(initializer.asKindOrThrow(SyntaxKind.ArrayLiteralExpression));
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
function nestSpreadElements(arr) {
|
|
1935
|
+
const elements = arr.getElements();
|
|
1936
|
+
for (let j = 0; j < elements.length; j++) {
|
|
1937
|
+
const element = elements[j];
|
|
1938
|
+
if (element.isKind(SyntaxKind.ObjectLiteralExpression)) nestSpreadProperties(element.asKindOrThrow(SyntaxKind.ObjectLiteralExpression));
|
|
1939
|
+
else if (element.isKind(SyntaxKind.ArrayLiteralExpression)) nestSpreadElements(element.asKindOrThrow(SyntaxKind.ArrayLiteralExpression));
|
|
1940
|
+
else if (element.isKind(SyntaxKind.SpreadElement)) {
|
|
1941
|
+
const spreadText = element.getText();
|
|
1942
|
+
arr.removeElement(j);
|
|
1943
|
+
arr.insertElement(j, `"${spreadText}"`);
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
function unnestSpreadProperties(obj) {
|
|
1948
|
+
const properties = obj.getProperties();
|
|
1949
|
+
for (let i = 0; i < properties.length; i++) {
|
|
1950
|
+
const prop = properties[i];
|
|
1951
|
+
if (prop.isKind(SyntaxKind.PropertyAssignment)) {
|
|
1952
|
+
const propAssignment = prop;
|
|
1953
|
+
const initializer = propAssignment.getInitializer();
|
|
1954
|
+
if (initializer && initializer.isKind(SyntaxKind.StringLiteral)) {
|
|
1955
|
+
const value = initializer.asKindOrThrow(SyntaxKind.StringLiteral).getLiteralValue();
|
|
1956
|
+
if (value.startsWith("...")) {
|
|
1957
|
+
obj.insertSpreadAssignment(i, { expression: value.slice(3) });
|
|
1958
|
+
propAssignment.remove();
|
|
1959
|
+
}
|
|
1960
|
+
} else if (initializer?.isKind(SyntaxKind.ObjectLiteralExpression)) unnestSpreadProperties(initializer);
|
|
1961
|
+
else if (initializer && initializer.isKind(SyntaxKind.ArrayLiteralExpression)) unsetSpreadElements(initializer.asKindOrThrow(SyntaxKind.ArrayLiteralExpression));
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
function unsetSpreadElements(arr) {
|
|
1966
|
+
const elements = arr.getElements();
|
|
1967
|
+
for (let j = 0; j < elements.length; j++) {
|
|
1968
|
+
const element = elements[j];
|
|
1969
|
+
if (element.isKind(SyntaxKind.ObjectLiteralExpression)) unnestSpreadProperties(element.asKindOrThrow(SyntaxKind.ObjectLiteralExpression));
|
|
1970
|
+
else if (element.isKind(SyntaxKind.ArrayLiteralExpression)) unsetSpreadElements(element.asKindOrThrow(SyntaxKind.ArrayLiteralExpression));
|
|
1971
|
+
else if (element.isKind(SyntaxKind.StringLiteral)) {
|
|
1972
|
+
const spreadText = element.getText();
|
|
1973
|
+
const spreadTest = /^['"](\.\.\..*)['"]$/g;
|
|
1974
|
+
if (spreadTest.test(spreadText)) {
|
|
1975
|
+
arr.removeElement(j);
|
|
1976
|
+
arr.insertElement(j, spreadText.replace(spreadTest, "$1"));
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
async function parseObjectLiteral(objectLiteralString) {
|
|
1982
|
+
const statement = (await _createSourceFile(`const theme = ${objectLiteralString}`, null)).getStatements()[0];
|
|
1983
|
+
if (statement?.getKind() === SyntaxKind.VariableStatement) {
|
|
1984
|
+
const initializer = (statement.getDeclarationList()?.getDeclarations()[0]).getInitializer();
|
|
1985
|
+
if (initializer?.isKind(SyntaxKind.ObjectLiteralExpression)) return await parseObjectLiteralExpression(initializer);
|
|
1986
|
+
}
|
|
1987
|
+
throw new Error("Invalid input: not an object literal");
|
|
1988
|
+
}
|
|
1989
|
+
function parseObjectLiteralExpression(node) {
|
|
1990
|
+
const result = {};
|
|
1991
|
+
for (const property of node.getProperties()) if (property.isKind(SyntaxKind.PropertyAssignment)) {
|
|
1992
|
+
const name = property.getName().replace(/'/g, "");
|
|
1993
|
+
if (property.getInitializer()?.isKind(SyntaxKind.ObjectLiteralExpression)) result[name] = parseObjectLiteralExpression(property.getInitializer());
|
|
1994
|
+
else if (property.getInitializer()?.isKind(SyntaxKind.ArrayLiteralExpression)) result[name] = parseArrayLiteralExpression(property.getInitializer());
|
|
1995
|
+
else result[name] = parseValue(property.getInitializer());
|
|
1996
|
+
}
|
|
1997
|
+
return result;
|
|
1998
|
+
}
|
|
1999
|
+
function parseArrayLiteralExpression(node) {
|
|
2000
|
+
const result = [];
|
|
2001
|
+
for (const element of node.getElements()) if (element.isKind(SyntaxKind.ObjectLiteralExpression)) result.push(parseObjectLiteralExpression(element.asKindOrThrow(SyntaxKind.ObjectLiteralExpression)));
|
|
2002
|
+
else if (element.isKind(SyntaxKind.ArrayLiteralExpression)) result.push(parseArrayLiteralExpression(element.asKindOrThrow(SyntaxKind.ArrayLiteralExpression)));
|
|
2003
|
+
else result.push(parseValue(element));
|
|
2004
|
+
return result;
|
|
2005
|
+
}
|
|
2006
|
+
function parseValue(node) {
|
|
2007
|
+
switch (node.getKind()) {
|
|
2008
|
+
case SyntaxKind.StringLiteral: return node.getText();
|
|
2009
|
+
case SyntaxKind.NumericLiteral: return Number(node.getText());
|
|
2010
|
+
case SyntaxKind.TrueKeyword: return true;
|
|
2011
|
+
case SyntaxKind.FalseKeyword: return false;
|
|
2012
|
+
case SyntaxKind.NullKeyword: return null;
|
|
2013
|
+
case SyntaxKind.ArrayLiteralExpression: return node.getElements().map(parseValue);
|
|
2014
|
+
case SyntaxKind.ObjectLiteralExpression: return parseObjectLiteralExpression(node);
|
|
2015
|
+
default: return node.getText();
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
function buildTailwindThemeColorsFromCssVars(cssVars) {
|
|
2019
|
+
const result = {};
|
|
2020
|
+
for (const key of Object.keys(cssVars)) {
|
|
2021
|
+
const parts = key.split("-");
|
|
2022
|
+
const colorName = parts[0];
|
|
2023
|
+
const subType = parts.slice(1).join("-");
|
|
2024
|
+
if (subType === "") if (typeof result[colorName] === "object") result[colorName].DEFAULT = `hsl(var(--${key}))`;
|
|
2025
|
+
else result[colorName] = `hsl(var(--${key}))`;
|
|
2026
|
+
else {
|
|
2027
|
+
if (typeof result[colorName] !== "object") result[colorName] = { DEFAULT: `hsl(var(--${colorName}))` };
|
|
2028
|
+
result[colorName][subType] = `hsl(var(--${key}))`;
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
for (const [colorName, value] of Object.entries(result)) if (typeof value === "object" && value.DEFAULT === `hsl(var(--${colorName}))` && !(colorName in cssVars)) delete value.DEFAULT;
|
|
2032
|
+
return result;
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
//#endregion
|
|
2036
|
+
//#region src/registry/resolver.ts
|
|
2037
|
+
function resolveRegistryItemsFromRegistries(items, config) {
|
|
2038
|
+
const registryHeaders = {};
|
|
2039
|
+
const resolvedItems = [...items];
|
|
2040
|
+
if (!config?.registries) {
|
|
2041
|
+
setRegistryHeaders({});
|
|
2042
|
+
return resolvedItems;
|
|
2043
|
+
}
|
|
2044
|
+
for (let i = 0; i < resolvedItems.length; i++) {
|
|
2045
|
+
const resolved = buildUrlAndHeadersForRegistryItem(resolvedItems[i], config);
|
|
2046
|
+
if (resolved) {
|
|
2047
|
+
resolvedItems[i] = resolved.url;
|
|
2048
|
+
if (Object.keys(resolved.headers).length > 0) registryHeaders[resolved.url] = resolved.headers;
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
setRegistryHeaders(registryHeaders);
|
|
2052
|
+
return resolvedItems;
|
|
2053
|
+
}
|
|
2054
|
+
async function fetchRegistryItems(items, config, options = {}) {
|
|
2055
|
+
return await Promise.all(items.map(async (item) => {
|
|
2056
|
+
if (isLocalFile(item)) return fetchRegistryLocal(item);
|
|
2057
|
+
if (isUrl$1(item)) {
|
|
2058
|
+
const [result$1] = await fetchRegistry([item], options);
|
|
2059
|
+
try {
|
|
2060
|
+
return registryItemSchema.parse(result$1);
|
|
2061
|
+
} catch (error) {
|
|
2062
|
+
throw new RegistryParseError(item, error);
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
if (item.startsWith("@") && config?.registries) {
|
|
2066
|
+
const [result$1] = await fetchRegistry(resolveRegistryItemsFromRegistries([item], config), options);
|
|
2067
|
+
try {
|
|
2068
|
+
return registryItemSchema.parse(result$1);
|
|
2069
|
+
} catch (error) {
|
|
2070
|
+
throw new RegistryParseError(item, error);
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
const [result] = await fetchRegistry([`styles/${config?.style ?? "new-york-v4"}/${item}.json`], options);
|
|
2074
|
+
try {
|
|
2075
|
+
return registryItemSchema.parse(result);
|
|
2076
|
+
} catch (error) {
|
|
2077
|
+
throw new RegistryParseError(item, error);
|
|
2078
|
+
}
|
|
2079
|
+
}));
|
|
2080
|
+
}
|
|
2081
|
+
registryItemSchema.extend({ _source: z.string().optional() });
|
|
2082
|
+
async function resolveRegistryTree(names, config, options = {}) {
|
|
2083
|
+
try {
|
|
2084
|
+
options = {
|
|
2085
|
+
useCache: true,
|
|
2086
|
+
...options
|
|
2087
|
+
};
|
|
2088
|
+
let payload = [];
|
|
2089
|
+
const allDependencyItems = [];
|
|
2090
|
+
const allDependencyRegistryNames = [];
|
|
2091
|
+
const uniqueNames = Array.from(new Set(names));
|
|
2092
|
+
const results = await fetchRegistryItems(uniqueNames, config, options);
|
|
2093
|
+
const resultMap = /* @__PURE__ */ new Map();
|
|
2094
|
+
for (let i = 0; i < results.length; i++) if (results[i]) resultMap.set(uniqueNames[i], results[i]);
|
|
2095
|
+
for (const [sourceName, item] of Array.from(resultMap.entries())) {
|
|
2096
|
+
const itemWithSource = {
|
|
2097
|
+
...item,
|
|
2098
|
+
_source: sourceName
|
|
2099
|
+
};
|
|
2100
|
+
payload.push(itemWithSource);
|
|
2101
|
+
if (item.registryDependencies) {
|
|
2102
|
+
let resolvedDependencies = item.registryDependencies;
|
|
2103
|
+
if (!config?.registries) {
|
|
2104
|
+
const namespacedDeps = item.registryDependencies.filter((dep) => dep.startsWith("@"));
|
|
2105
|
+
if (namespacedDeps.length > 0) {
|
|
2106
|
+
const { registry } = parseRegistryAndItemFromString(namespacedDeps[0]);
|
|
2107
|
+
throw new RegistryNotConfiguredError(registry);
|
|
2108
|
+
}
|
|
2109
|
+
} else resolvedDependencies = resolveRegistryItemsFromRegistries(item.registryDependencies, config);
|
|
2110
|
+
const { items, registryNames } = await resolveDependenciesRecursively(resolvedDependencies, config, options, new Set(uniqueNames));
|
|
2111
|
+
allDependencyItems.push(...items);
|
|
2112
|
+
allDependencyRegistryNames.push(...registryNames);
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
payload.push(...allDependencyItems);
|
|
2116
|
+
if (allDependencyRegistryNames.length > 0) {
|
|
2117
|
+
const uniqueRegistryNames = Array.from(new Set(allDependencyRegistryNames));
|
|
2118
|
+
const nonNamespacedItems = uniqueRegistryNames.filter((name) => !name.startsWith("@"));
|
|
2119
|
+
const namespacedDepItems = uniqueRegistryNames.filter((name) => name.startsWith("@"));
|
|
2120
|
+
if (namespacedDepItems.length > 0) {
|
|
2121
|
+
const depResults = await fetchRegistryItems(namespacedDepItems, config, options);
|
|
2122
|
+
for (let i = 0; i < depResults.length; i++) {
|
|
2123
|
+
const itemWithSource = {
|
|
2124
|
+
...depResults[i],
|
|
2125
|
+
_source: namespacedDepItems[i]
|
|
2126
|
+
};
|
|
2127
|
+
payload.push(itemWithSource);
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
if (nonNamespacedItems.length > 0) {
|
|
2131
|
+
const index = await getShadcnRegistryIndex();
|
|
2132
|
+
if (!index && payload.length === 0) return null;
|
|
2133
|
+
if (index) {
|
|
2134
|
+
if (nonNamespacedItems.includes("index")) nonNamespacedItems.unshift("index");
|
|
2135
|
+
const registryUrls = [];
|
|
2136
|
+
for (const name of nonNamespacedItems) {
|
|
2137
|
+
const itemDependencies = await resolveRegistryDependencies(name, config, options);
|
|
2138
|
+
registryUrls.push(...itemDependencies);
|
|
2139
|
+
}
|
|
2140
|
+
const result = await fetchRegistry(Array.from(new Set(registryUrls)), options);
|
|
2141
|
+
const registryPayload = z.array(registryItemSchema).parse(result);
|
|
2142
|
+
payload.push(...registryPayload);
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
}
|
|
2146
|
+
if (!payload.length) return null;
|
|
2147
|
+
if (uniqueNames.includes("index") || allDependencyRegistryNames.includes("index")) {
|
|
2148
|
+
if (config.tailwind.baseColor) {
|
|
2149
|
+
const theme = await registryGetTheme(config.tailwind.baseColor, config);
|
|
2150
|
+
if (theme) payload.unshift(theme);
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
const sourceMap = /* @__PURE__ */ new Map();
|
|
2154
|
+
payload.forEach((item) => {
|
|
2155
|
+
const source = item._source || item.name;
|
|
2156
|
+
sourceMap.set(item, source);
|
|
2157
|
+
});
|
|
2158
|
+
payload = topologicalSortRegistryItems(payload, sourceMap);
|
|
2159
|
+
payload.sort((a, b) => {
|
|
2160
|
+
if (a.type === "registry:theme" && b.type !== "registry:theme") return -1;
|
|
2161
|
+
if (a.type !== "registry:theme" && b.type === "registry:theme") return 1;
|
|
2162
|
+
return 0;
|
|
2163
|
+
});
|
|
2164
|
+
let tailwind = {};
|
|
2165
|
+
payload.forEach((item) => {
|
|
2166
|
+
tailwind = deepmerge(tailwind, item.tailwind ?? {});
|
|
2167
|
+
});
|
|
2168
|
+
let cssVars = {};
|
|
2169
|
+
payload.forEach((item) => {
|
|
2170
|
+
cssVars = deepmerge(cssVars, item.cssVars ?? {});
|
|
2171
|
+
});
|
|
2172
|
+
let css = {};
|
|
2173
|
+
payload.forEach((item) => {
|
|
2174
|
+
css = deepmerge(css, item.css ?? {});
|
|
2175
|
+
});
|
|
2176
|
+
let docs = "";
|
|
2177
|
+
payload.forEach((item) => {
|
|
2178
|
+
if (item.docs) docs += `${item.docs}\n`;
|
|
2179
|
+
});
|
|
2180
|
+
let envVars = {};
|
|
2181
|
+
payload.forEach((item) => {
|
|
2182
|
+
envVars = deepmerge(envVars, item.envVars ?? {});
|
|
2183
|
+
});
|
|
2184
|
+
const deduplicatedFiles = await deduplicateFilesByTarget(payload.map((item) => item.files ?? []), config);
|
|
2185
|
+
const parsed = registryResolvedItemsTreeSchema.parse({
|
|
2186
|
+
dependencies: deepmerge.all(payload.map((item) => item.dependencies ?? [])),
|
|
2187
|
+
devDependencies: deepmerge.all(payload.map((item) => item.devDependencies ?? [])),
|
|
2188
|
+
files: deduplicatedFiles,
|
|
2189
|
+
tailwind,
|
|
2190
|
+
cssVars,
|
|
2191
|
+
css,
|
|
2192
|
+
docs
|
|
2193
|
+
});
|
|
2194
|
+
if (Object.keys(envVars).length > 0) parsed.envVars = envVars;
|
|
2195
|
+
return parsed;
|
|
2196
|
+
} catch (error) {
|
|
2197
|
+
handleError(error);
|
|
2198
|
+
return null;
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
async function resolveDependenciesRecursively(dependencies, config, options = {}, visited = /* @__PURE__ */ new Set()) {
|
|
2202
|
+
const items = [];
|
|
2203
|
+
const registryNames = [];
|
|
2204
|
+
for (const dep of dependencies) {
|
|
2205
|
+
if (visited.has(dep)) continue;
|
|
2206
|
+
visited.add(dep);
|
|
2207
|
+
if (isUrl$1(dep) || isLocalFile(dep)) {
|
|
2208
|
+
const [item] = await fetchRegistryItems([dep], config, options);
|
|
2209
|
+
if (item) {
|
|
2210
|
+
items.push(item);
|
|
2211
|
+
if (item.registryDependencies) {
|
|
2212
|
+
const nested = await resolveDependenciesRecursively(config?.registries ? resolveRegistryItemsFromRegistries(item.registryDependencies, config) : item.registryDependencies, config, options, visited);
|
|
2213
|
+
items.push(...nested.items);
|
|
2214
|
+
registryNames.push(...nested.registryNames);
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
} else if (dep.startsWith("@") && config?.registries) {
|
|
2218
|
+
const { registry } = parseRegistryAndItemFromString(dep);
|
|
2219
|
+
if (registry && !(registry in config.registries)) throw new RegistryNotConfiguredError(registry);
|
|
2220
|
+
const [item] = await fetchRegistryItems([dep], config, options);
|
|
2221
|
+
if (item) {
|
|
2222
|
+
items.push(item);
|
|
2223
|
+
if (item.registryDependencies) {
|
|
2224
|
+
const nested = await resolveDependenciesRecursively(config?.registries ? resolveRegistryItemsFromRegistries(item.registryDependencies, config) : item.registryDependencies, config, options, visited);
|
|
2225
|
+
items.push(...nested.items);
|
|
2226
|
+
registryNames.push(...nested.registryNames);
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
} else {
|
|
2230
|
+
registryNames.push(dep);
|
|
2231
|
+
if (config) try {
|
|
2232
|
+
const [item] = await fetchRegistryItems([dep], config, options);
|
|
2233
|
+
if (item && item.registryDependencies) {
|
|
2234
|
+
const nested = await resolveDependenciesRecursively(config?.registries ? resolveRegistryItemsFromRegistries(item.registryDependencies, config) : item.registryDependencies, config, options, visited);
|
|
2235
|
+
items.push(...nested.items);
|
|
2236
|
+
registryNames.push(...nested.registryNames);
|
|
2237
|
+
}
|
|
2238
|
+
} catch (error) {}
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
return {
|
|
2242
|
+
items,
|
|
2243
|
+
registryNames
|
|
2244
|
+
};
|
|
2245
|
+
}
|
|
2246
|
+
async function resolveRegistryDependencies(url, config, options = {}) {
|
|
2247
|
+
if (isUrl$1(url)) return [url];
|
|
2248
|
+
const { registryNames } = await resolveDependenciesRecursively([url], config, options, /* @__PURE__ */ new Set());
|
|
2249
|
+
const style = config.resolvedPaths?.cwd ? await getTargetStyleFromConfig(config.resolvedPaths.cwd, config.style) : config.style;
|
|
2250
|
+
const urls = registryNames.map((name) => resolveRegistryUrl(isUrl$1(name) ? name : `styles/${style}/${name}.json`));
|
|
2251
|
+
return Array.from(new Set(urls));
|
|
2252
|
+
}
|
|
2253
|
+
async function registryGetTheme(name, config) {
|
|
2254
|
+
const [baseColor, tailwindVersion] = await Promise.all([getRegistryBaseColor(name), getProjectTailwindVersionFromConfig(config)]);
|
|
2255
|
+
if (!baseColor) return null;
|
|
2256
|
+
const { brand, brandForeground } = config.tailwind;
|
|
2257
|
+
const theme = {
|
|
2258
|
+
name,
|
|
2259
|
+
type: "registry:theme",
|
|
2260
|
+
tailwind: { config: { theme: { extend: {
|
|
2261
|
+
borderRadius: {
|
|
2262
|
+
lg: "var(--radius)",
|
|
2263
|
+
md: "calc(var(--radius) - 2px)",
|
|
2264
|
+
sm: "calc(var(--radius) - 4px)"
|
|
2265
|
+
},
|
|
2266
|
+
colors: {}
|
|
2267
|
+
} } } },
|
|
2268
|
+
cssVars: {
|
|
2269
|
+
theme: {},
|
|
2270
|
+
light: {
|
|
2271
|
+
radius: tailwindVersion === "v4" ? "0.625rem" : "0.5rem",
|
|
2272
|
+
...brand && { brand },
|
|
2273
|
+
...brandForeground && { "brand-foreground": brandForeground }
|
|
2274
|
+
},
|
|
2275
|
+
dark: {}
|
|
2276
|
+
}
|
|
2277
|
+
};
|
|
2278
|
+
if (config.tailwind.cssVariables) {
|
|
2279
|
+
theme.tailwind.config.theme.extend.colors = {
|
|
2280
|
+
...theme.tailwind.config.theme.extend.colors,
|
|
2281
|
+
...buildTailwindThemeColorsFromCssVars(baseColor.cssVars.dark ?? {}),
|
|
2282
|
+
...brand && {
|
|
2283
|
+
brand: "hsl(var(--brand))",
|
|
2284
|
+
"brand-foreground": "hsl(var(--brand-foreground))"
|
|
2285
|
+
}
|
|
2286
|
+
};
|
|
2287
|
+
theme.cssVars = {
|
|
2288
|
+
theme: {
|
|
2289
|
+
...baseColor.cssVars.theme,
|
|
2290
|
+
...theme.cssVars.theme
|
|
2291
|
+
},
|
|
2292
|
+
light: {
|
|
2293
|
+
...baseColor.cssVars.light,
|
|
2294
|
+
...theme.cssVars.light
|
|
2295
|
+
},
|
|
2296
|
+
dark: {
|
|
2297
|
+
...baseColor.cssVars.dark,
|
|
2298
|
+
...theme.cssVars.dark
|
|
2299
|
+
}
|
|
2300
|
+
};
|
|
2301
|
+
if (tailwindVersion === "v4" && baseColor.cssVarsV4) theme.cssVars = {
|
|
2302
|
+
theme: {
|
|
2303
|
+
...baseColor.cssVarsV4.theme,
|
|
2304
|
+
...theme.cssVars.theme
|
|
2305
|
+
},
|
|
2306
|
+
light: {
|
|
2307
|
+
radius: "0.625rem",
|
|
2308
|
+
...brand && { brand },
|
|
2309
|
+
...brandForeground && { "brand-foreground": brandForeground },
|
|
2310
|
+
...baseColor.cssVarsV4.light
|
|
2311
|
+
},
|
|
2312
|
+
dark: { ...baseColor.cssVarsV4.dark }
|
|
2313
|
+
};
|
|
2314
|
+
}
|
|
2315
|
+
return theme;
|
|
2316
|
+
}
|
|
2317
|
+
function computeItemHash(item, source) {
|
|
2318
|
+
const identifier = source || item.name;
|
|
2319
|
+
const hash = createHash("sha256").update(identifier).digest("hex").substring(0, 8);
|
|
2320
|
+
return `${item.name}::${hash}`;
|
|
2321
|
+
}
|
|
2322
|
+
function extractItemIdentifierFromDependency(dependency) {
|
|
2323
|
+
if (isUrl$1(dependency)) {
|
|
2324
|
+
const pathname = new URL(dependency).pathname;
|
|
2325
|
+
const match = pathname.match(/\/([^/]+)\.json$/);
|
|
2326
|
+
const name = match ? match[1] : path.basename(pathname, ".json");
|
|
2327
|
+
return {
|
|
2328
|
+
name,
|
|
2329
|
+
hash: computeItemHash({ name }, dependency)
|
|
2330
|
+
};
|
|
2331
|
+
}
|
|
2332
|
+
if (isLocalFile(dependency)) {
|
|
2333
|
+
const match = dependency.match(/\/([^/]+)\.json$/);
|
|
2334
|
+
const name = match ? match[1] : path.basename(dependency, ".json");
|
|
2335
|
+
return {
|
|
2336
|
+
name,
|
|
2337
|
+
hash: computeItemHash({ name }, dependency)
|
|
2338
|
+
};
|
|
2339
|
+
}
|
|
2340
|
+
const { item } = parseRegistryAndItemFromString(dependency);
|
|
2341
|
+
return {
|
|
2342
|
+
name: item,
|
|
2343
|
+
hash: computeItemHash({ name: item }, dependency)
|
|
2344
|
+
};
|
|
2345
|
+
}
|
|
2346
|
+
function topologicalSortRegistryItems(items, sourceMap) {
|
|
2347
|
+
const itemMap = /* @__PURE__ */ new Map();
|
|
2348
|
+
const hashToItem = /* @__PURE__ */ new Map();
|
|
2349
|
+
const inDegree = /* @__PURE__ */ new Map();
|
|
2350
|
+
const adjacencyList = /* @__PURE__ */ new Map();
|
|
2351
|
+
items.forEach((item) => {
|
|
2352
|
+
const hash = computeItemHash(item, sourceMap.get(item) || item.name);
|
|
2353
|
+
itemMap.set(hash, item);
|
|
2354
|
+
hashToItem.set(hash, item);
|
|
2355
|
+
inDegree.set(hash, 0);
|
|
2356
|
+
adjacencyList.set(hash, []);
|
|
2357
|
+
});
|
|
2358
|
+
const depToHashes = /* @__PURE__ */ new Map();
|
|
2359
|
+
items.forEach((item) => {
|
|
2360
|
+
const source = sourceMap.get(item) || item.name;
|
|
2361
|
+
const hash = computeItemHash(item, source);
|
|
2362
|
+
if (!depToHashes.has(item.name)) depToHashes.set(item.name, []);
|
|
2363
|
+
depToHashes.get(item.name).push(hash);
|
|
2364
|
+
if (source !== item.name) {
|
|
2365
|
+
if (!depToHashes.has(source)) depToHashes.set(source, []);
|
|
2366
|
+
depToHashes.get(source).push(hash);
|
|
2367
|
+
}
|
|
2368
|
+
});
|
|
2369
|
+
items.forEach((item) => {
|
|
2370
|
+
const itemHash = computeItemHash(item, sourceMap.get(item) || item.name);
|
|
2371
|
+
if (item.registryDependencies) item.registryDependencies.forEach((dep) => {
|
|
2372
|
+
let depHash;
|
|
2373
|
+
const exactMatches = depToHashes.get(dep) || [];
|
|
2374
|
+
if (exactMatches.length === 1) depHash = exactMatches[0];
|
|
2375
|
+
else if (exactMatches.length > 1) depHash = exactMatches[0];
|
|
2376
|
+
else {
|
|
2377
|
+
const { name } = extractItemIdentifierFromDependency(dep);
|
|
2378
|
+
const nameMatches = depToHashes.get(name) || [];
|
|
2379
|
+
if (nameMatches.length > 0) depHash = nameMatches[0];
|
|
2380
|
+
}
|
|
2381
|
+
if (depHash && itemMap.has(depHash)) {
|
|
2382
|
+
adjacencyList.get(depHash).push(itemHash);
|
|
2383
|
+
inDegree.set(itemHash, inDegree.get(itemHash) + 1);
|
|
2384
|
+
}
|
|
2385
|
+
});
|
|
2386
|
+
});
|
|
2387
|
+
const queue = [];
|
|
2388
|
+
const sorted = [];
|
|
2389
|
+
inDegree.forEach((degree, hash) => {
|
|
2390
|
+
if (degree === 0) queue.push(hash);
|
|
2391
|
+
});
|
|
2392
|
+
while (queue.length > 0) {
|
|
2393
|
+
const currentHash = queue.shift();
|
|
2394
|
+
const item = itemMap.get(currentHash);
|
|
2395
|
+
sorted.push(item);
|
|
2396
|
+
adjacencyList.get(currentHash).forEach((dependentHash) => {
|
|
2397
|
+
const newDegree = inDegree.get(dependentHash) - 1;
|
|
2398
|
+
inDegree.set(dependentHash, newDegree);
|
|
2399
|
+
if (newDegree === 0) queue.push(dependentHash);
|
|
2400
|
+
});
|
|
2401
|
+
}
|
|
2402
|
+
if (sorted.length !== items.length) {
|
|
2403
|
+
console.warn("Circular dependency detected in registry items");
|
|
2404
|
+
const sortedHashes = new Set(sorted.map((item) => {
|
|
2405
|
+
return computeItemHash(item, sourceMap.get(item) || item.name);
|
|
2406
|
+
}));
|
|
2407
|
+
items.forEach((item) => {
|
|
2408
|
+
const hash = computeItemHash(item, sourceMap.get(item) || item.name);
|
|
2409
|
+
if (!sortedHashes.has(hash)) sorted.push(item);
|
|
2410
|
+
});
|
|
2411
|
+
}
|
|
2412
|
+
return sorted;
|
|
2413
|
+
}
|
|
2414
|
+
|
|
2415
|
+
//#endregion
|
|
2416
|
+
//#region src/registry/api.ts
|
|
2417
|
+
async function getRegistry(name, options) {
|
|
2418
|
+
const { config, useCache } = options || {};
|
|
2419
|
+
if (isUrl$1(name)) {
|
|
2420
|
+
const [result$1] = await fetchRegistry([name], { useCache });
|
|
2421
|
+
try {
|
|
2422
|
+
return registrySchema.parse(result$1);
|
|
2423
|
+
} catch (error) {
|
|
2424
|
+
throw new RegistryParseError(name, error);
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
if (!name.startsWith("@")) throw new RegistryInvalidNamespaceError(name);
|
|
2428
|
+
let registryName = name;
|
|
2429
|
+
if (!registryName.endsWith("/registry")) registryName = `${registryName}/registry`;
|
|
2430
|
+
const urlAndHeaders = buildUrlAndHeadersForRegistryItem(registryName, configWithDefaults(config));
|
|
2431
|
+
if (!urlAndHeaders?.url) throw new RegistryNotFoundError(registryName);
|
|
2432
|
+
if (urlAndHeaders.headers && Object.keys(urlAndHeaders.headers).length > 0) setRegistryHeaders({ [urlAndHeaders.url]: urlAndHeaders.headers });
|
|
2433
|
+
const [result] = await fetchRegistry([urlAndHeaders.url], { useCache });
|
|
2434
|
+
try {
|
|
2435
|
+
return registrySchema.parse(result);
|
|
2436
|
+
} catch (error) {
|
|
2437
|
+
throw new RegistryParseError(registryName, error);
|
|
2438
|
+
}
|
|
2439
|
+
}
|
|
2440
|
+
async function getRegistryItems(items, options) {
|
|
2441
|
+
const { config, useCache = false } = options || {};
|
|
2442
|
+
clearRegistryContext();
|
|
2443
|
+
return fetchRegistryItems(items, configWithDefaults(config), { useCache });
|
|
2444
|
+
}
|
|
2445
|
+
async function resolveRegistryItems(items, options) {
|
|
2446
|
+
const { config, useCache = false } = options || {};
|
|
2447
|
+
clearRegistryContext();
|
|
2448
|
+
return resolveRegistryTree(items, configWithDefaults(config), { useCache });
|
|
2449
|
+
}
|
|
2450
|
+
async function getRegistriesConfig(cwd) {
|
|
2451
|
+
const configResult = await getRawConfig(cwd);
|
|
2452
|
+
if (!configResult) return { registries: BUILTIN_REGISTRIES };
|
|
2453
|
+
const registriesConfig = z.object({ registries: registryConfigSchema.optional() }).safeParse(configResult);
|
|
2454
|
+
if (!registriesConfig.success) throw new ConfigParseError(cwd, registriesConfig.error);
|
|
2455
|
+
return { registries: {
|
|
2456
|
+
...BUILTIN_REGISTRIES,
|
|
2457
|
+
...registriesConfig.data.registries || {}
|
|
2458
|
+
} };
|
|
2459
|
+
}
|
|
2460
|
+
async function getShadcnRegistryIndex() {
|
|
2461
|
+
try {
|
|
2462
|
+
const [result] = await fetchRegistry(["index.json"]);
|
|
2463
|
+
return registryIndexSchema.parse(result);
|
|
2464
|
+
} catch (error) {
|
|
2465
|
+
logger.error("\n");
|
|
2466
|
+
handleError(error);
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2469
|
+
async function getRegistryStyles() {
|
|
2470
|
+
try {
|
|
2471
|
+
const [result] = await fetchRegistry(["styles/index.json"]);
|
|
2472
|
+
return stylesSchema.parse(result);
|
|
2473
|
+
} catch (error) {
|
|
2474
|
+
logger.error("\n");
|
|
2475
|
+
handleError(error);
|
|
2476
|
+
return [];
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2479
|
+
async function getRegistryIcons() {
|
|
2480
|
+
try {
|
|
2481
|
+
const [result] = await fetchRegistry(["icons/index.json"]);
|
|
2482
|
+
return iconsSchema.parse(result);
|
|
2483
|
+
} catch (error) {
|
|
2484
|
+
handleError(error);
|
|
2485
|
+
return {};
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
async function getRegistryBaseColors() {
|
|
2489
|
+
return BASE_COLORS;
|
|
2490
|
+
}
|
|
2491
|
+
async function getRegistryBaseColor(baseColor) {
|
|
2492
|
+
try {
|
|
2493
|
+
const [result] = await fetchRegistry([`colors/${baseColor}.json`]);
|
|
2494
|
+
return registryBaseColorSchema.parse(result);
|
|
2495
|
+
} catch (error) {
|
|
2496
|
+
handleError(error);
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
/**
|
|
2500
|
+
* @deprecated This function is deprecated and will be removed in a future version.
|
|
2501
|
+
*/
|
|
2502
|
+
async function resolveTree(index, names) {
|
|
2503
|
+
const tree = [];
|
|
2504
|
+
for (const name of names) {
|
|
2505
|
+
const entry = index.find((entry$1) => entry$1.name === name);
|
|
2506
|
+
if (!entry) continue;
|
|
2507
|
+
tree.push(entry);
|
|
2508
|
+
if (entry.registryDependencies) {
|
|
2509
|
+
const dependencies = await resolveTree(index, entry.registryDependencies);
|
|
2510
|
+
tree.push(...dependencies);
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
return tree.filter((component, index$1, self) => self.findIndex((c) => c.name === component.name) === index$1);
|
|
2514
|
+
}
|
|
2515
|
+
/**
|
|
2516
|
+
* @deprecated This function is deprecated and will be removed in a future version.
|
|
2517
|
+
*/
|
|
2518
|
+
async function fetchTree(style, tree) {
|
|
2519
|
+
try {
|
|
2520
|
+
return (await fetchRegistry(tree.map((item) => `styles/${style}/${item.name}.json`))).map((result) => registryItemSchema.parse(result));
|
|
2521
|
+
} catch (error) {
|
|
2522
|
+
handleError(error);
|
|
2523
|
+
return [];
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
/**
|
|
2527
|
+
* @deprecated This function is deprecated and will be removed in a future version.
|
|
2528
|
+
*/
|
|
2529
|
+
async function getItemTargetPath(config, item, override) {
|
|
2530
|
+
if (override) return override;
|
|
2531
|
+
if (item.type === "registry:ui") return config.resolvedPaths.ui ?? config.resolvedPaths.components;
|
|
2532
|
+
const [parent, type] = item.type?.split(":") ?? [];
|
|
2533
|
+
if (!(parent in config.resolvedPaths)) return null;
|
|
2534
|
+
return path.join(config.resolvedPaths[parent], type);
|
|
2535
|
+
}
|
|
2536
|
+
async function getRegistriesIndex(options) {
|
|
2537
|
+
options = {
|
|
2538
|
+
useCache: true,
|
|
2539
|
+
...options
|
|
2540
|
+
};
|
|
2541
|
+
const [data] = await fetchRegistry([`${REGISTRY_URL}/registries.json`], { useCache: options.useCache });
|
|
2542
|
+
try {
|
|
2543
|
+
return registriesIndexSchema.parse(data);
|
|
2544
|
+
} catch (error) {
|
|
2545
|
+
if (error instanceof z.ZodError) throw new RegistriesIndexParseError(error);
|
|
2546
|
+
throw error;
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
|
|
2550
|
+
//#endregion
|
|
2551
|
+
//#region src/registry/search.ts
|
|
2552
|
+
async function searchRegistries(registries, options) {
|
|
2553
|
+
const { query, limit, offset, config, useCache } = options || {};
|
|
2554
|
+
let allItems = [];
|
|
2555
|
+
for (const registry of registries) {
|
|
2556
|
+
const itemsWithRegistry = ((await getRegistry(registry, {
|
|
2557
|
+
config,
|
|
2558
|
+
useCache
|
|
2559
|
+
})).items || []).map((item) => ({
|
|
2560
|
+
name: item.name,
|
|
2561
|
+
type: item.type,
|
|
2562
|
+
description: item.description,
|
|
2563
|
+
registry,
|
|
2564
|
+
addCommandArgument: buildRegistryItemNameFromRegistry(item.name, registry)
|
|
2565
|
+
}));
|
|
2566
|
+
allItems = allItems.concat(itemsWithRegistry);
|
|
2567
|
+
}
|
|
2568
|
+
if (query) allItems = searchItems(allItems, {
|
|
2569
|
+
query,
|
|
2570
|
+
limit: allItems.length,
|
|
2571
|
+
keys: ["name", "description"]
|
|
2572
|
+
});
|
|
2573
|
+
const paginationOffset = offset || 0;
|
|
2574
|
+
const paginationLimit = limit || allItems.length;
|
|
2575
|
+
const totalItems = allItems.length;
|
|
2576
|
+
const result = {
|
|
2577
|
+
pagination: {
|
|
2578
|
+
total: totalItems,
|
|
2579
|
+
offset: paginationOffset,
|
|
2580
|
+
limit: paginationLimit,
|
|
2581
|
+
hasMore: paginationOffset + paginationLimit < totalItems
|
|
2582
|
+
},
|
|
2583
|
+
items: allItems.slice(paginationOffset, paginationOffset + paginationLimit)
|
|
2584
|
+
};
|
|
2585
|
+
return searchResultsSchema.parse(result);
|
|
2586
|
+
}
|
|
2587
|
+
const searchableItemSchema = z.object({
|
|
2588
|
+
name: z.string(),
|
|
2589
|
+
type: z.string().optional(),
|
|
2590
|
+
description: z.string().optional(),
|
|
2591
|
+
registry: z.string().optional(),
|
|
2592
|
+
addCommandArgument: z.string().optional()
|
|
2593
|
+
}).passthrough();
|
|
2594
|
+
function searchItems(items, options) {
|
|
2595
|
+
options = {
|
|
2596
|
+
limit: 100,
|
|
2597
|
+
threshold: -1e4,
|
|
2598
|
+
...options
|
|
2599
|
+
};
|
|
2600
|
+
const results = fuzzysort.go(options.query, items, {
|
|
2601
|
+
keys: options.keys,
|
|
2602
|
+
threshold: options.threshold,
|
|
2603
|
+
limit: options.limit
|
|
2604
|
+
}).map((result) => result.obj);
|
|
2605
|
+
return z.array(searchableItemSchema).parse(results);
|
|
2606
|
+
}
|
|
2607
|
+
function isUrl(string) {
|
|
2608
|
+
try {
|
|
2609
|
+
new URL(string);
|
|
2610
|
+
return true;
|
|
2611
|
+
} catch {
|
|
2612
|
+
return false;
|
|
2613
|
+
}
|
|
2614
|
+
}
|
|
2615
|
+
function buildRegistryItemNameFromRegistry(name, registry) {
|
|
2616
|
+
if (!isUrl(registry)) return `${registry}/${name}`;
|
|
2617
|
+
const protocolEnd = registry.indexOf("://") + 3;
|
|
2618
|
+
const hostEnd = registry.indexOf("/", protocolEnd);
|
|
2619
|
+
if (hostEnd === -1) {
|
|
2620
|
+
const queryStart = registry.indexOf("?", protocolEnd);
|
|
2621
|
+
if (queryStart !== -1) return registry.substring(0, queryStart) + registry.substring(queryStart).replace(/\bregistry\b/g, name);
|
|
2622
|
+
return registry;
|
|
2623
|
+
}
|
|
2624
|
+
const hostPart = registry.substring(0, hostEnd);
|
|
2625
|
+
const pathAndQuery = registry.substring(hostEnd);
|
|
2626
|
+
const pathEnd = pathAndQuery.includes("?") ? pathAndQuery.indexOf("?") : pathAndQuery.length;
|
|
2627
|
+
const pathOnly = pathAndQuery.substring(0, pathEnd);
|
|
2628
|
+
const queryAndAfter = pathAndQuery.substring(pathEnd);
|
|
2629
|
+
const lastIndex = pathOnly.lastIndexOf("registry");
|
|
2630
|
+
let updatedPath = pathOnly;
|
|
2631
|
+
if (lastIndex !== -1) updatedPath = pathOnly.substring(0, lastIndex) + name + pathOnly.substring(lastIndex + 8);
|
|
2632
|
+
const updatedQuery = queryAndAfter.replace(/\bregistry\b/g, name);
|
|
2633
|
+
return hostPart + updatedPath + updatedQuery;
|
|
2634
|
+
}
|
|
2635
|
+
|
|
2636
|
+
//#endregion
|
|
2637
|
+
export { RegistryLocalFileError as $, mergeEnvContent as A, DEFAULT_TAILWIND_CSS as B, configWithDefaults as C, ICON_LIBRARIES as D, transform$2 as E, getProjectInfo as F, getConfig as G, createConfig as H, getProjectTailwindVersionFromConfig as I, RegistriesIndexParseError as J, getWorkspaceConfig as K, getPackageInfo as L, spinner as M, logger as N, findExistingEnvFile as O, getProjectConfig as P, RegistryInvalidNamespaceError as Q, DEFAULT_COMPONENTS as R, clearRegistryContext as S, updateFiles as T, findCommonRoot$1 as U, DEFAULT_UTILS as V, findPackageRoot as W, RegistryFetchError as X, RegistryError as Y, RegistryForbiddenError as Z, _createSourceFile as _, getRegistriesIndex as a, highlighter as at, handleError as b, getRegistryBaseColors as c, DEPRECATED_COMPONENTS as ct, getRegistryStyles as d, RegistryMissingEnvironmentVariablesError as et, getShadcnRegistryIndex as f, resolveRegistryTree as g, fetchRegistryItems as h, getRegistriesConfig as i, RegistryUnauthorizedError as it, parseRegistryAndItemFromString as j, getNewEnvKeys as k, getRegistryIcons as l, REGISTRY_URL as lt, resolveTree as m, fetchTree as n, RegistryNotFoundError as nt, getRegistry as o, BASE_COLORS as ot, resolveRegistryItems as p, resolveConfigPaths as q, getItemTargetPath as r, RegistryParseError as rt, getRegistryBaseColor as s, BUILTIN_REGISTRIES as st, searchRegistries as t, RegistryNotConfiguredError as tt, getRegistryItems as u, _getQuoteChar as v, isUniversalRegistryItem as w, buildUrlAndHeadersForRegistryItem as x, updateTailwindConfig as y, DEFAULT_TAILWIND_CONFIG as z };
|
|
2638
|
+
//# sourceMappingURL=registry-CgE-Q6HO.js.map
|