@tokiui/cli 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +291 -43
- package/package.json +4 -1
package/dist/index.js
CHANGED
|
@@ -33,6 +33,7 @@ var import_fs_extra2 = __toESM(require("fs-extra"));
|
|
|
33
33
|
var import_prompts = __toESM(require("prompts"));
|
|
34
34
|
var import_kleur = __toESM(require("kleur"));
|
|
35
35
|
var import_ora = __toESM(require("ora"));
|
|
36
|
+
var import_execa = require("execa");
|
|
36
37
|
|
|
37
38
|
// src/utils/config.ts
|
|
38
39
|
var import_path = __toESM(require("path"));
|
|
@@ -55,51 +56,192 @@ function detectPackageManager(cwd) {
|
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
// src/commands/init.ts
|
|
59
|
+
var GLOBALS_CANDIDATES = [
|
|
60
|
+
"src/app/globals.css",
|
|
61
|
+
"src/styles/globals.css",
|
|
62
|
+
"src/index.css",
|
|
63
|
+
"app/globals.css",
|
|
64
|
+
"styles/globals.css",
|
|
65
|
+
"index.css"
|
|
66
|
+
];
|
|
67
|
+
var POSTCSS_CANDIDATES = [
|
|
68
|
+
"postcss.config.mjs",
|
|
69
|
+
"postcss.config.js",
|
|
70
|
+
"postcss.config.cjs"
|
|
71
|
+
];
|
|
72
|
+
function detectGlobalsCss(cwd) {
|
|
73
|
+
return GLOBALS_CANDIDATES.find((c) => import_fs_extra2.default.existsSync(import_path2.default.join(cwd, c)));
|
|
74
|
+
}
|
|
75
|
+
function detectPostcss(cwd) {
|
|
76
|
+
return POSTCSS_CANDIDATES.find((f) => import_fs_extra2.default.existsSync(import_path2.default.join(cwd, f)));
|
|
77
|
+
}
|
|
78
|
+
function hasTailwindV3(cwd) {
|
|
79
|
+
return import_fs_extra2.default.existsSync(import_path2.default.join(cwd, "tailwind.config.ts")) || import_fs_extra2.default.existsSync(import_path2.default.join(cwd, "tailwind.config.js"));
|
|
80
|
+
}
|
|
58
81
|
var initCommand = new import_commander.Command("init").description("Initialize tokiui in your project").action(async () => {
|
|
59
82
|
const cwd = process.cwd();
|
|
60
|
-
console.log(import_kleur.default.bold("\
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
83
|
+
console.log(import_kleur.default.bold("\n tokiui \u2014 setup\n"));
|
|
84
|
+
const hasSrc = import_fs_extra2.default.existsSync(import_path2.default.join(cwd, "src"));
|
|
85
|
+
const isNext = import_fs_extra2.default.existsSync(import_path2.default.join(cwd, "next.config.ts")) || import_fs_extra2.default.existsSync(import_path2.default.join(cwd, "next.config.js")) || import_fs_extra2.default.existsSync(import_path2.default.join(cwd, "next.config.mjs"));
|
|
86
|
+
const detectedGlobals = detectGlobalsCss(cwd);
|
|
87
|
+
if (hasTailwindV3(cwd)) {
|
|
88
|
+
console.log(
|
|
89
|
+
import_kleur.default.yellow(
|
|
90
|
+
" \u26A0 tailwind.config.ts detected \u2014 tokiui requires Tailwind CSS v4.\n Migrate first: https://tailwindcss.com/docs/upgrade-guide\n"
|
|
91
|
+
)
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
const answers = await (0, import_prompts.default)(
|
|
95
|
+
[
|
|
96
|
+
{
|
|
97
|
+
type: "text",
|
|
98
|
+
name: "componentsDir",
|
|
99
|
+
message: "Where should components be installed?",
|
|
100
|
+
initial: hasSrc ? "src/components/ui" : "components/ui"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
type: "text",
|
|
104
|
+
name: "libDir",
|
|
105
|
+
message: "Where should utility files be placed?",
|
|
106
|
+
initial: hasSrc ? "src/lib" : "lib"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
type: "text",
|
|
110
|
+
name: "globalsPath",
|
|
111
|
+
message: "Where is your global CSS file?",
|
|
112
|
+
initial: detectedGlobals ?? (hasSrc ? "src/app/globals.css" : "app/globals.css")
|
|
113
|
+
}
|
|
114
|
+
],
|
|
68
115
|
{
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
116
|
+
onCancel() {
|
|
117
|
+
console.log(import_kleur.default.red("\n Aborted.\n"));
|
|
118
|
+
process.exit(0);
|
|
119
|
+
}
|
|
73
120
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
121
|
+
);
|
|
122
|
+
const pm = detectPackageManager(cwd);
|
|
123
|
+
const installCmd = pm === "npm" ? "install" : "add";
|
|
124
|
+
const libAlias = hasSrc ? "@/" + answers.libDir.replace(/^src\//, "") : "@/" + answers.libDir;
|
|
125
|
+
const spinner = (0, import_ora.default)();
|
|
126
|
+
spinner.start("Creating directories");
|
|
80
127
|
await import_fs_extra2.default.ensureDir(import_path2.default.join(cwd, answers.componentsDir));
|
|
81
128
|
await import_fs_extra2.default.ensureDir(import_path2.default.join(cwd, answers.libDir));
|
|
129
|
+
spinner.succeed("Directories ready");
|
|
130
|
+
spinner.start(`Installing packages with ${pm}`);
|
|
131
|
+
try {
|
|
132
|
+
await (0, import_execa.execa)(
|
|
133
|
+
pm,
|
|
134
|
+
[installCmd, "@tokiui/ui", "tailwindcss", "@tailwindcss/postcss", "clsx", "tailwind-merge"],
|
|
135
|
+
{ cwd }
|
|
136
|
+
);
|
|
137
|
+
spinner.succeed("Packages installed");
|
|
138
|
+
} catch {
|
|
139
|
+
spinner.fail("Package install failed \u2014 run manually:");
|
|
140
|
+
console.log(
|
|
141
|
+
import_kleur.default.dim(
|
|
142
|
+
`
|
|
143
|
+
${pm} ${installCmd} @tokiui/ui tailwindcss @tailwindcss/postcss clsx tailwind-merge
|
|
144
|
+
`
|
|
145
|
+
)
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
spinner.start("Writing utility functions");
|
|
82
149
|
const cnPath = import_path2.default.join(cwd, answers.libDir, "utils.ts");
|
|
83
150
|
if (!import_fs_extra2.default.existsSync(cnPath)) {
|
|
84
151
|
await import_fs_extra2.default.writeFile(
|
|
85
152
|
cnPath,
|
|
86
|
-
|
|
87
|
-
import {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
153
|
+
[
|
|
154
|
+
`import { clsx, type ClassValue } from 'clsx'`,
|
|
155
|
+
`import { twMerge } from 'tailwind-merge'`,
|
|
156
|
+
``,
|
|
157
|
+
`export function cn(...inputs: ClassValue[]) {`,
|
|
158
|
+
` return twMerge(clsx(inputs))`,
|
|
159
|
+
`}`
|
|
160
|
+
].join("\n") + "\n"
|
|
161
|
+
);
|
|
162
|
+
spinner.succeed("utils.ts written");
|
|
163
|
+
} else {
|
|
164
|
+
spinner.succeed("utils.ts already exists \u2014 skipped");
|
|
165
|
+
}
|
|
166
|
+
spinner.start("Configuring PostCSS");
|
|
167
|
+
const existingPostcss = detectPostcss(cwd);
|
|
168
|
+
if (!existingPostcss) {
|
|
169
|
+
await import_fs_extra2.default.writeFile(
|
|
170
|
+
import_path2.default.join(cwd, "postcss.config.mjs"),
|
|
171
|
+
`export default {
|
|
172
|
+
plugins: {
|
|
173
|
+
'@tailwindcss/postcss': {},
|
|
174
|
+
},
|
|
91
175
|
}
|
|
92
176
|
`
|
|
93
177
|
);
|
|
178
|
+
spinner.succeed("postcss.config.mjs created");
|
|
179
|
+
} else {
|
|
180
|
+
const content = await import_fs_extra2.default.readFile(import_path2.default.join(cwd, existingPostcss), "utf-8");
|
|
181
|
+
if (content.includes("@tailwindcss/postcss") || content.includes("tailwindcss")) {
|
|
182
|
+
spinner.succeed(`PostCSS already configured (${existingPostcss})`);
|
|
183
|
+
} else {
|
|
184
|
+
spinner.warn(
|
|
185
|
+
`${existingPostcss} exists but has no Tailwind plugin \u2014 add '@tailwindcss/postcss' manually`
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
spinner.start("Setting up global CSS");
|
|
190
|
+
const globalsAbsPath = import_path2.default.join(cwd, answers.globalsPath);
|
|
191
|
+
await import_fs_extra2.default.ensureDir(import_path2.default.dirname(globalsAbsPath));
|
|
192
|
+
let css = import_fs_extra2.default.existsSync(globalsAbsPath) ? await import_fs_extra2.default.readFile(globalsAbsPath, "utf-8") : "";
|
|
193
|
+
const prepend = [];
|
|
194
|
+
if (!css.includes('@import "tailwindcss"') && !css.includes("@import 'tailwindcss'")) {
|
|
195
|
+
prepend.push('@import "tailwindcss";');
|
|
196
|
+
}
|
|
197
|
+
if (!css.includes("@tokiui/ui/styles.css")) {
|
|
198
|
+
prepend.push('@import "@tokiui/ui/styles.css";');
|
|
199
|
+
}
|
|
200
|
+
if (prepend.length > 0) {
|
|
201
|
+
css = prepend.join("\n") + "\n\n" + css;
|
|
202
|
+
await import_fs_extra2.default.writeFile(globalsAbsPath, css);
|
|
203
|
+
spinner.succeed(`globals.css updated (${answers.globalsPath})`);
|
|
204
|
+
} else {
|
|
205
|
+
spinner.succeed("globals.css already configured");
|
|
206
|
+
}
|
|
207
|
+
spinner.start("Checking TypeScript path aliases");
|
|
208
|
+
const tsconfigPath = import_path2.default.join(cwd, "tsconfig.json");
|
|
209
|
+
if (import_fs_extra2.default.existsSync(tsconfigPath)) {
|
|
210
|
+
const tsconfig = await import_fs_extra2.default.readJson(tsconfigPath);
|
|
211
|
+
const co = tsconfig.compilerOptions ?? {};
|
|
212
|
+
const paths = co.paths ?? {};
|
|
213
|
+
if (!paths["@/*"]) {
|
|
214
|
+
tsconfig.compilerOptions = {
|
|
215
|
+
...co,
|
|
216
|
+
baseUrl: ".",
|
|
217
|
+
paths: { ...paths, "@/*": [hasSrc ? "./src/*" : "./*"] }
|
|
218
|
+
};
|
|
219
|
+
await import_fs_extra2.default.writeJson(tsconfigPath, tsconfig, { spaces: 2 });
|
|
220
|
+
spinner.succeed(`tsconfig.json \u2014 added "@/*": ["${hasSrc ? "./src/*" : "./*"}"]`);
|
|
221
|
+
} else {
|
|
222
|
+
spinner.succeed("tsconfig.json already has @/* paths");
|
|
223
|
+
}
|
|
224
|
+
} else {
|
|
225
|
+
spinner.warn("No tsconfig.json found \u2014 skipping path alias setup");
|
|
94
226
|
}
|
|
95
227
|
writeConfig(cwd, {
|
|
96
228
|
componentsDir: answers.componentsDir,
|
|
97
229
|
libDir: answers.libDir,
|
|
230
|
+
libAlias,
|
|
98
231
|
style: "default"
|
|
99
232
|
});
|
|
100
|
-
|
|
101
|
-
console.log(import_kleur.default.
|
|
102
|
-
|
|
233
|
+
console.log(import_kleur.default.green("\n \u2713 tokiui initialized\n"));
|
|
234
|
+
console.log(` ${import_kleur.default.bold("Add your first component:")}
|
|
235
|
+
`);
|
|
236
|
+
console.log(` ${import_kleur.default.cyan("npx @tokiui/cli add button")}
|
|
237
|
+
`);
|
|
238
|
+
if (!isNext) {
|
|
239
|
+
console.log(
|
|
240
|
+
import_kleur.default.dim(
|
|
241
|
+
" Tip: make sure your framework imports globals.css so the\n Tailwind and tokiui styles are loaded.\n"
|
|
242
|
+
)
|
|
243
|
+
);
|
|
244
|
+
}
|
|
103
245
|
});
|
|
104
246
|
|
|
105
247
|
// src/commands/add.ts
|
|
@@ -109,24 +251,110 @@ var import_fs_extra3 = __toESM(require("fs-extra"));
|
|
|
109
251
|
var import_prompts2 = __toESM(require("prompts"));
|
|
110
252
|
var import_kleur2 = __toESM(require("kleur"));
|
|
111
253
|
var import_ora2 = __toESM(require("ora"));
|
|
112
|
-
var
|
|
254
|
+
var import_execa2 = require("execa");
|
|
113
255
|
|
|
114
256
|
// src/utils/registry.ts
|
|
115
|
-
var
|
|
257
|
+
var ALLOWED_FETCH_ORIGIN = "https://raw.githubusercontent.com/TopherGacad/tokiui/";
|
|
258
|
+
var SAFE_REF_RE = /^[a-zA-Z0-9._/@-]+$/;
|
|
259
|
+
function resolveRegistryRef() {
|
|
260
|
+
const envRef = process.env.TOKIUI_REGISTRY_REF;
|
|
261
|
+
if (envRef !== void 0) {
|
|
262
|
+
if (!SAFE_REF_RE.test(envRef) || envRef.includes("..")) {
|
|
263
|
+
throw new Error(
|
|
264
|
+
`TOKIUI_REGISTRY_REF contains invalid characters: "${envRef}". Only alphanumeric characters, hyphens, dots, slashes, and @ are allowed.`
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
return envRef;
|
|
268
|
+
}
|
|
269
|
+
return `cli-v${"0.2.0"}`;
|
|
270
|
+
}
|
|
271
|
+
var REGISTRY_REF = resolveRegistryRef();
|
|
272
|
+
var REGISTRY_BASE = `${ALLOWED_FETCH_ORIGIN}${REGISTRY_REF}/packages/registry`;
|
|
273
|
+
var SOURCE_BASE = `${ALLOWED_FETCH_ORIGIN}${REGISTRY_REF}/packages/ui/src`;
|
|
274
|
+
async function safeFetch(url) {
|
|
275
|
+
if (!url.startsWith(ALLOWED_FETCH_ORIGIN)) {
|
|
276
|
+
throw new Error(
|
|
277
|
+
`Security: refusing to fetch from unexpected origin.
|
|
278
|
+
Expected: ${ALLOWED_FETCH_ORIGIN}...
|
|
279
|
+
Got: ${url}`
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
return fetch(url);
|
|
283
|
+
}
|
|
284
|
+
var ALLOWED_SCOPES = /* @__PURE__ */ new Set(["@radix-ui"]);
|
|
285
|
+
var ALLOWED_PACKAGES = /* @__PURE__ */ new Set([
|
|
286
|
+
"class-variance-authority",
|
|
287
|
+
"clsx",
|
|
288
|
+
"tailwind-merge",
|
|
289
|
+
"sonner",
|
|
290
|
+
"lucide-react"
|
|
291
|
+
]);
|
|
292
|
+
var PACKAGE_NAME_RE = /^(@[a-z0-9][a-z0-9-._]*\/[a-z0-9][a-z0-9-._]*)(@[^\s]+)?$|^([a-z0-9][a-z0-9-._]*)(@[^\s]+)?$/;
|
|
293
|
+
function assertSafeDependency(dep) {
|
|
294
|
+
if (!PACKAGE_NAME_RE.test(dep)) {
|
|
295
|
+
throw new Error(
|
|
296
|
+
`Registry returned a malformed dependency name: "${dep}". Aborting install.`
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
if (dep.startsWith("@")) {
|
|
300
|
+
const scope = dep.slice(0, dep.indexOf("/"));
|
|
301
|
+
if (!ALLOWED_SCOPES.has(scope)) {
|
|
302
|
+
throw new Error(
|
|
303
|
+
`Dependency scope "${scope}" is not in the approved list. Refusing to install "${dep}". If this is legitimate, open an issue on GitHub.`
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
} else {
|
|
307
|
+
const name = dep.split("@")[0];
|
|
308
|
+
if (!ALLOWED_PACKAGES.has(name)) {
|
|
309
|
+
throw new Error(
|
|
310
|
+
`Package "${name}" is not in the approved dependency list. Refusing to install. If this is legitimate, open an issue on GitHub.`
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
function assertSafeFilePath(filePath) {
|
|
316
|
+
if (filePath.includes("..") || filePath.startsWith("/") || /^[A-Za-z]:/.test(filePath) || filePath.includes("\0") || filePath.includes("\r") || filePath.includes("\n")) {
|
|
317
|
+
throw new Error(`Registry returned an unsafe file path: "${filePath}". Aborting.`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
function assertSafeComponentName(name) {
|
|
321
|
+
if (!/^[a-z][a-z0-9-]*$/.test(name)) {
|
|
322
|
+
throw new Error(`Invalid component name in registry: "${name}". Aborting.`);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
function assertRegistryIndex(data) {
|
|
326
|
+
if (typeof data !== "object" || data === null || !Array.isArray(data.components)) {
|
|
327
|
+
throw new Error("Received an invalid registry index from the server.");
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
function assertRegistryComponent(data) {
|
|
331
|
+
const obj = data;
|
|
332
|
+
const valid = typeof data === "object" && data !== null && typeof obj.name === "string" && Array.isArray(obj.files) && Array.isArray(obj.dependencies) && Array.isArray(obj.devDependencies) && Array.isArray(obj.registryDependencies) && obj.files.every((f) => typeof f === "string") && obj.dependencies.every((d) => typeof d === "string") && obj.registryDependencies.every((d) => typeof d === "string");
|
|
333
|
+
if (!valid) {
|
|
334
|
+
throw new Error(
|
|
335
|
+
`Received an invalid component definition for "${String(obj.name ?? "unknown")}" from the registry.`
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
116
339
|
async function fetchRegistryIndex() {
|
|
117
|
-
const res = await
|
|
340
|
+
const res = await safeFetch(`${REGISTRY_BASE}/index.json`);
|
|
118
341
|
if (!res.ok) throw new Error(`Failed to fetch registry index: ${res.statusText}`);
|
|
119
|
-
|
|
342
|
+
const data = await res.json();
|
|
343
|
+
assertRegistryIndex(data);
|
|
344
|
+
return data;
|
|
120
345
|
}
|
|
121
346
|
async function fetchComponent(name) {
|
|
122
|
-
|
|
347
|
+
assertSafeComponentName(name);
|
|
348
|
+
const res = await safeFetch(`${REGISTRY_BASE}/components/${name}.json`);
|
|
123
349
|
if (!res.ok) throw new Error(`Component "${name}" not found in registry`);
|
|
124
|
-
|
|
350
|
+
const data = await res.json();
|
|
351
|
+
assertRegistryComponent(data);
|
|
352
|
+
return data;
|
|
125
353
|
}
|
|
126
354
|
async function fetchComponentSource(name, file) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
);
|
|
355
|
+
assertSafeComponentName(name);
|
|
356
|
+
assertSafeFilePath(file);
|
|
357
|
+
const res = await safeFetch(`${SOURCE_BASE}/${file}`);
|
|
130
358
|
if (!res.ok) throw new Error(`Failed to fetch source for ${name}/${file}`);
|
|
131
359
|
return res.text();
|
|
132
360
|
}
|
|
@@ -146,6 +374,7 @@ var addCommand = new import_commander2.Command("add").description("Add a compone
|
|
|
146
374
|
);
|
|
147
375
|
process.exit(1);
|
|
148
376
|
}
|
|
377
|
+
const libAlias = config.libAlias ?? `@/${config.libDir}`;
|
|
149
378
|
let componentName = componentArg;
|
|
150
379
|
if (!componentName) {
|
|
151
380
|
const spinner = (0, import_ora2.default)("Fetching component list...").start();
|
|
@@ -165,25 +394,35 @@ var addCommand = new import_commander2.Command("add").description("Add a compone
|
|
|
165
394
|
console.log(import_kleur2.default.red("Aborted."));
|
|
166
395
|
process.exit(0);
|
|
167
396
|
}
|
|
397
|
+
const visited = /* @__PURE__ */ new Set();
|
|
168
398
|
for (const name of answer.components) {
|
|
169
|
-
await installComponent(name, cwd, config.componentsDir,
|
|
399
|
+
await installComponent(name, cwd, config.componentsDir, libAlias, visited);
|
|
170
400
|
}
|
|
171
401
|
return;
|
|
172
402
|
}
|
|
173
|
-
await installComponent(componentName, cwd, config.componentsDir,
|
|
403
|
+
await installComponent(componentName, cwd, config.componentsDir, libAlias, /* @__PURE__ */ new Set());
|
|
174
404
|
});
|
|
175
|
-
async function installComponent(name, cwd, componentsDir,
|
|
405
|
+
async function installComponent(name, cwd, componentsDir, libAlias, visited) {
|
|
406
|
+
if (visited.has(name)) return;
|
|
407
|
+
visited.add(name);
|
|
408
|
+
assertSafeComponentName(name);
|
|
176
409
|
const spinner = (0, import_ora2.default)(`Adding ${name}...`).start();
|
|
177
|
-
const meta = await fetchComponent(name).catch(() => {
|
|
178
|
-
spinner.fail(
|
|
410
|
+
const meta = await fetchComponent(name).catch((err) => {
|
|
411
|
+
spinner.fail(`${err instanceof Error ? err.message : String(err)}`);
|
|
179
412
|
process.exit(1);
|
|
180
413
|
});
|
|
181
414
|
for (const dep of meta.registryDependencies) {
|
|
182
|
-
await installComponent(dep, cwd, componentsDir,
|
|
415
|
+
await installComponent(dep, cwd, componentsDir, libAlias, visited);
|
|
183
416
|
}
|
|
184
417
|
for (const file of meta.files) {
|
|
418
|
+
try {
|
|
419
|
+
assertSafeFilePath(file);
|
|
420
|
+
} catch (err) {
|
|
421
|
+
spinner.fail(`Security: ${err instanceof Error ? err.message : String(err)}`);
|
|
422
|
+
process.exit(1);
|
|
423
|
+
}
|
|
185
424
|
const source = await fetchComponentSource(name, file);
|
|
186
|
-
const transformed = transformImports(source,
|
|
425
|
+
const transformed = transformImports(source, libAlias);
|
|
187
426
|
const destPath = import_path3.default.join(cwd, componentsDir, import_path3.default.basename(file));
|
|
188
427
|
if (import_fs_extra3.default.existsSync(destPath)) {
|
|
189
428
|
spinner.stop();
|
|
@@ -203,9 +442,18 @@ async function installComponent(name, cwd, componentsDir, libDir) {
|
|
|
203
442
|
await import_fs_extra3.default.writeFile(destPath, transformed);
|
|
204
443
|
}
|
|
205
444
|
if (meta.dependencies.length > 0) {
|
|
445
|
+
for (const dep of meta.dependencies) {
|
|
446
|
+
try {
|
|
447
|
+
assertSafeDependency(dep);
|
|
448
|
+
} catch (err) {
|
|
449
|
+
spinner.fail(`Security: ${err instanceof Error ? err.message : String(err)}`);
|
|
450
|
+
process.exit(1);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
console.log(import_kleur2.default.dim(` Installing: ${meta.dependencies.join(", ")}`));
|
|
206
454
|
const pm = detectPackageManager(cwd);
|
|
207
455
|
const installCmd = pm === "npm" ? "install" : "add";
|
|
208
|
-
await (0,
|
|
456
|
+
await (0, import_execa2.execa)(pm, [installCmd, ...meta.dependencies], { cwd });
|
|
209
457
|
}
|
|
210
458
|
spinner.succeed(`Added ${import_kleur2.default.bold(name)}`);
|
|
211
459
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokiui/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "CLI for adding tokiui components to your project",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Topher Gacad",
|
|
@@ -21,6 +21,9 @@
|
|
|
21
21
|
"bin": {
|
|
22
22
|
"tokiui": "./dist/index.js"
|
|
23
23
|
},
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
24
27
|
"files": [
|
|
25
28
|
"dist"
|
|
26
29
|
],
|