@sublay/cli 7.0.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.d.ts +1 -0
- package/dist/index.js +506 -0
- package/package.json +42 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
|
|
6
|
+
// src/commands/init.ts
|
|
7
|
+
import prompts from "prompts";
|
|
8
|
+
import fs2 from "fs-extra";
|
|
9
|
+
import path2 from "path";
|
|
10
|
+
import chalk from "chalk";
|
|
11
|
+
|
|
12
|
+
// src/utils/detect.ts
|
|
13
|
+
import fs from "fs-extra";
|
|
14
|
+
import path from "path";
|
|
15
|
+
async function detectProjectType() {
|
|
16
|
+
try {
|
|
17
|
+
const packageJsonPath = path.join(process.cwd(), "package.json");
|
|
18
|
+
if (!await fs.pathExists(packageJsonPath)) {
|
|
19
|
+
return "unknown";
|
|
20
|
+
}
|
|
21
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
22
|
+
const deps = {
|
|
23
|
+
...packageJson.dependencies,
|
|
24
|
+
...packageJson.devDependencies
|
|
25
|
+
};
|
|
26
|
+
if (deps["react-native"] || deps["expo"]) {
|
|
27
|
+
return "react-native";
|
|
28
|
+
}
|
|
29
|
+
if (deps["react"]) {
|
|
30
|
+
return "react";
|
|
31
|
+
}
|
|
32
|
+
return "unknown";
|
|
33
|
+
} catch (error) {
|
|
34
|
+
return "unknown";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async function detectTypeScript() {
|
|
38
|
+
try {
|
|
39
|
+
const tsconfigPath = path.join(process.cwd(), "tsconfig.json");
|
|
40
|
+
if (await fs.pathExists(tsconfigPath)) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
const packageJsonPath = path.join(process.cwd(), "package.json");
|
|
44
|
+
if (await fs.pathExists(packageJsonPath)) {
|
|
45
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
46
|
+
const deps = {
|
|
47
|
+
...packageJson.dependencies,
|
|
48
|
+
...packageJson.devDependencies
|
|
49
|
+
};
|
|
50
|
+
if (deps["typescript"] || deps["@types/react"]) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
} catch (error) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// src/commands/init.ts
|
|
61
|
+
async function init() {
|
|
62
|
+
console.log(chalk.bold("\n\u{1F680} Welcome to Sublay CLI\n"));
|
|
63
|
+
const projectType = await detectProjectType();
|
|
64
|
+
const hasTypeScript = await detectTypeScript();
|
|
65
|
+
const answers = await prompts([
|
|
66
|
+
{
|
|
67
|
+
type: "select",
|
|
68
|
+
name: "platform",
|
|
69
|
+
message: "Which platform are you using?",
|
|
70
|
+
choices: [
|
|
71
|
+
{ title: "React (Web)", value: "react" },
|
|
72
|
+
{ title: "React Native", value: "react-native" },
|
|
73
|
+
{ title: "Expo", value: "expo" }
|
|
74
|
+
],
|
|
75
|
+
initial: projectType === "react-native" ? 1 : 0
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
type: "select",
|
|
79
|
+
name: "style",
|
|
80
|
+
message: "Which styling approach?",
|
|
81
|
+
choices: [
|
|
82
|
+
{ title: "Tailwind CSS", value: "tailwind" },
|
|
83
|
+
{ title: "Inline Styles", value: "styled" }
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
type: "text",
|
|
88
|
+
name: "componentsPath",
|
|
89
|
+
message: "Where should components be installed?",
|
|
90
|
+
initial: "src/components"
|
|
91
|
+
}
|
|
92
|
+
]);
|
|
93
|
+
if (!answers.platform) {
|
|
94
|
+
console.log(chalk.yellow("\n\u26A0\uFE0F Setup cancelled"));
|
|
95
|
+
process.exit(0);
|
|
96
|
+
}
|
|
97
|
+
const config = {
|
|
98
|
+
platform: answers.platform,
|
|
99
|
+
style: answers.style || "styled",
|
|
100
|
+
typescript: hasTypeScript,
|
|
101
|
+
paths: {
|
|
102
|
+
components: answers.componentsPath || "src/components"
|
|
103
|
+
},
|
|
104
|
+
aliases: {
|
|
105
|
+
"@/components": `./src/components`
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
const configPath = path2.join(process.cwd(), "sublay.json");
|
|
109
|
+
await fs2.writeJson(configPath, config, { spaces: 2 });
|
|
110
|
+
console.log(chalk.green("\n\u2705 Configuration saved to sublay.json"));
|
|
111
|
+
console.log(chalk.bold("\n\u{1F389} Sublay CLI initialized successfully!\n"));
|
|
112
|
+
console.log(chalk.dim("Next steps:"));
|
|
113
|
+
console.log(chalk.dim(" 1. Run: npx @sublay/cli add <component-name>"));
|
|
114
|
+
console.log(chalk.dim(" Available: comments-threaded, comments-social, notifications-control"));
|
|
115
|
+
console.log(chalk.dim(" 2. Import components in your app"));
|
|
116
|
+
console.log(chalk.dim(" 3. Customize styles directly in the component files\n"));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// src/commands/add.ts
|
|
120
|
+
import fs5 from "fs-extra";
|
|
121
|
+
import path5 from "path";
|
|
122
|
+
import chalk3 from "chalk";
|
|
123
|
+
import ora from "ora";
|
|
124
|
+
|
|
125
|
+
// src/utils/registry.ts
|
|
126
|
+
import fs3 from "fs-extra";
|
|
127
|
+
import path3 from "path";
|
|
128
|
+
import { fileURLToPath } from "url";
|
|
129
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
130
|
+
var __dirname = path3.dirname(__filename);
|
|
131
|
+
var LOCAL_REGISTRY_PATH = path3.resolve(__dirname, "..", "..", "..", "registry");
|
|
132
|
+
async function fetchRegistry(componentName, config) {
|
|
133
|
+
try {
|
|
134
|
+
const registryPlatform = config.platform === "expo" ? "react-native" : config.platform;
|
|
135
|
+
const registryPath = path3.join(
|
|
136
|
+
LOCAL_REGISTRY_PATH,
|
|
137
|
+
registryPlatform,
|
|
138
|
+
componentName,
|
|
139
|
+
config.style,
|
|
140
|
+
"registry.json"
|
|
141
|
+
);
|
|
142
|
+
const localExists = await fs3.pathExists(registryPath);
|
|
143
|
+
if (localExists) {
|
|
144
|
+
return await fs3.readJson(registryPath);
|
|
145
|
+
}
|
|
146
|
+
const url = `https://raw.githubusercontent.com/sublay-io/cli/main/registry/${registryPlatform}/${componentName}/${config.style}/registry.json`;
|
|
147
|
+
try {
|
|
148
|
+
const response = await fetch(url);
|
|
149
|
+
if (!response.ok) {
|
|
150
|
+
if (response.status === 404) {
|
|
151
|
+
console.error(`Component "${componentName}" with style "${config.style}" not found in registry.`);
|
|
152
|
+
} else {
|
|
153
|
+
console.error(`Failed to fetch registry from ${url}: ${response.status} ${response.statusText}`);
|
|
154
|
+
}
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
return await response.json();
|
|
158
|
+
} catch (fetchError) {
|
|
159
|
+
console.error(`Error fetching from GitHub (${url}):`, fetchError);
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
} catch (error) {
|
|
163
|
+
console.error("Error fetching registry:", error);
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async function fetchFile(registryUrl, filePath) {
|
|
168
|
+
try {
|
|
169
|
+
const urlParts = registryUrl.split("/");
|
|
170
|
+
const style = urlParts[urlParts.length - 1];
|
|
171
|
+
const componentName = urlParts[urlParts.length - 2];
|
|
172
|
+
const platform = urlParts[urlParts.length - 3];
|
|
173
|
+
const localPath = path3.join(
|
|
174
|
+
LOCAL_REGISTRY_PATH,
|
|
175
|
+
platform,
|
|
176
|
+
componentName,
|
|
177
|
+
style,
|
|
178
|
+
filePath
|
|
179
|
+
);
|
|
180
|
+
const localExists = await fs3.pathExists(localPath);
|
|
181
|
+
if (localExists) {
|
|
182
|
+
return await fs3.readFile(localPath, "utf-8");
|
|
183
|
+
}
|
|
184
|
+
const fileUrl = `${registryUrl}/${filePath}`;
|
|
185
|
+
try {
|
|
186
|
+
const response = await fetch(fileUrl);
|
|
187
|
+
if (!response.ok) {
|
|
188
|
+
console.error(`Failed to fetch file from ${fileUrl}: ${response.status} ${response.statusText}`);
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
return await response.text();
|
|
192
|
+
} catch (fetchError) {
|
|
193
|
+
console.error(`Error fetching file from GitHub (${fileUrl}):`, fetchError);
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
} catch (error) {
|
|
197
|
+
console.error(`Error fetching file ${filePath}:`, error);
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// src/utils/transform.ts
|
|
203
|
+
function transformImports(content, config) {
|
|
204
|
+
let transformed = content;
|
|
205
|
+
transformed = transformed.replace(/from\s+['"]\.\.\/files\//g, 'from "../components/');
|
|
206
|
+
transformed = transformed.replace(/import\s+['"]\.\.\/files\//g, 'import "../components/');
|
|
207
|
+
transformed = transformed.replace(/import\(['"]\.\.\/files\//g, 'import("../components/');
|
|
208
|
+
if (config.platform === "expo") {
|
|
209
|
+
transformed = transformed.replace(
|
|
210
|
+
/from\s+["']@sublay\/react-native["']/g,
|
|
211
|
+
'from "@sublay/expo"'
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
if (config.aliases["@/components"]) {
|
|
215
|
+
}
|
|
216
|
+
return transformed;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// src/utils/dependencies.ts
|
|
220
|
+
import fs4 from "fs-extra";
|
|
221
|
+
import path4 from "path";
|
|
222
|
+
import chalk2 from "chalk";
|
|
223
|
+
import prompts2 from "prompts";
|
|
224
|
+
import { execa } from "execa";
|
|
225
|
+
async function installDependencies(deps) {
|
|
226
|
+
try {
|
|
227
|
+
console.log(chalk2.blue(`
|
|
228
|
+
\u{1F4E6} Installing ${deps.join(", ")}...
|
|
229
|
+
`));
|
|
230
|
+
const packageManager = await detectPackageManager();
|
|
231
|
+
await execa(packageManager, ["install", ...deps], {
|
|
232
|
+
stdio: "inherit",
|
|
233
|
+
cwd: process.cwd()
|
|
234
|
+
});
|
|
235
|
+
console.log(chalk2.green("\n\u2705 Dependencies installed successfully"));
|
|
236
|
+
} catch (error) {
|
|
237
|
+
console.error(chalk2.red("\n\u274C Failed to install dependencies"));
|
|
238
|
+
console.error(error);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
async function detectPackageManager() {
|
|
242
|
+
const lockFiles = {
|
|
243
|
+
"pnpm-lock.yaml": "pnpm",
|
|
244
|
+
"yarn.lock": "yarn",
|
|
245
|
+
"package-lock.json": "npm"
|
|
246
|
+
};
|
|
247
|
+
for (const [lockFile, pm] of Object.entries(lockFiles)) {
|
|
248
|
+
if (await fs4.pathExists(path4.join(process.cwd(), lockFile))) {
|
|
249
|
+
return pm;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return "npm";
|
|
253
|
+
}
|
|
254
|
+
async function checkComponentDependencies(dependencies) {
|
|
255
|
+
try {
|
|
256
|
+
const packageJsonPath = path4.join(process.cwd(), "package.json");
|
|
257
|
+
if (!await fs4.pathExists(packageJsonPath)) {
|
|
258
|
+
console.log(chalk2.yellow("\n\u26A0\uFE0F No package.json found"));
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
const packageJson = await fs4.readJson(packageJsonPath);
|
|
262
|
+
const allDeps = {
|
|
263
|
+
...packageJson.dependencies,
|
|
264
|
+
...packageJson.devDependencies
|
|
265
|
+
};
|
|
266
|
+
const requiredDeps = dependencies.map((dep) => {
|
|
267
|
+
const parts = dep.split("@").filter(Boolean);
|
|
268
|
+
if (dep.startsWith("@")) {
|
|
269
|
+
return `@${parts[0]}`;
|
|
270
|
+
} else {
|
|
271
|
+
return parts[0];
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
const missingDeps = requiredDeps.filter((dep) => !allDeps[dep]);
|
|
275
|
+
if (missingDeps.length === 0) {
|
|
276
|
+
console.log(chalk2.green("\n\u2705 All required dependencies are installed"));
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
console.log(chalk2.yellow("\n\u26A0\uFE0F Missing required dependencies:"));
|
|
280
|
+
const missingDepsWithVersions = dependencies.filter((dep) => {
|
|
281
|
+
const parts = dep.split("@").filter(Boolean);
|
|
282
|
+
const pkgName = dep.startsWith("@") ? `@${parts[0]}` : parts[0];
|
|
283
|
+
return missingDeps.includes(pkgName);
|
|
284
|
+
});
|
|
285
|
+
missingDepsWithVersions.forEach((dep) => console.log(chalk2.dim(` - ${dep}`)));
|
|
286
|
+
const { install } = await prompts2({
|
|
287
|
+
type: "confirm",
|
|
288
|
+
name: "install",
|
|
289
|
+
message: "Would you like to install them now?",
|
|
290
|
+
initial: true
|
|
291
|
+
});
|
|
292
|
+
if (install) {
|
|
293
|
+
await installDependencies(missingDepsWithVersions);
|
|
294
|
+
} else {
|
|
295
|
+
console.log(chalk2.dim("\nYou can install them later with:"));
|
|
296
|
+
const packageManager = await detectPackageManager();
|
|
297
|
+
const installCmd = packageManager === "npm" ? "npm install" : packageManager === "yarn" ? "yarn add" : "pnpm add";
|
|
298
|
+
console.log(chalk2.dim(` ${installCmd} ${missingDepsWithVersions.join(" ")}
|
|
299
|
+
`));
|
|
300
|
+
}
|
|
301
|
+
} catch (error) {
|
|
302
|
+
console.error(chalk2.red("\n\u274C Error checking dependencies:"), error);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// src/utils/strip-types.ts
|
|
307
|
+
async function stripTypes(code, filePath) {
|
|
308
|
+
try {
|
|
309
|
+
const { transform } = await import("@babel/core");
|
|
310
|
+
const presetTypescript = (await import("@babel/preset-typescript")).default;
|
|
311
|
+
const pluginSyntaxJsx = (await import("@babel/plugin-syntax-jsx")).default;
|
|
312
|
+
const result = transform(code, {
|
|
313
|
+
filename: filePath,
|
|
314
|
+
presets: [
|
|
315
|
+
[presetTypescript, {
|
|
316
|
+
isTSX: true,
|
|
317
|
+
// Enable TSX parsing
|
|
318
|
+
allExtensions: true
|
|
319
|
+
// Parse all files as TS/TSX
|
|
320
|
+
}]
|
|
321
|
+
],
|
|
322
|
+
plugins: [pluginSyntaxJsx],
|
|
323
|
+
// Pass the actual plugin function
|
|
324
|
+
configFile: false,
|
|
325
|
+
// Don't look for babel.config.js
|
|
326
|
+
babelrc: false,
|
|
327
|
+
// Don't look for .babelrc
|
|
328
|
+
sourceMaps: false,
|
|
329
|
+
// No source maps needed
|
|
330
|
+
compact: false,
|
|
331
|
+
// Don't minimize output
|
|
332
|
+
retainLines: true
|
|
333
|
+
// Try to keep line numbers similar
|
|
334
|
+
});
|
|
335
|
+
if (!result || !result.code) {
|
|
336
|
+
throw new Error("Babel transformation returned no code");
|
|
337
|
+
}
|
|
338
|
+
return result.code;
|
|
339
|
+
} catch (error) {
|
|
340
|
+
throw new Error(
|
|
341
|
+
`Failed to strip types from ${filePath}: ${error instanceof Error ? error.message : String(error)}`
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
function convertFileExtension(filePath) {
|
|
346
|
+
return filePath.replace(/\.tsx?$/, (match) => {
|
|
347
|
+
return match === ".tsx" ? ".jsx" : ".js";
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// src/commands/add.ts
|
|
352
|
+
async function add(componentName) {
|
|
353
|
+
const spinner = ora("Initializing...").start();
|
|
354
|
+
try {
|
|
355
|
+
const configPath = path5.join(process.cwd(), "sublay.json");
|
|
356
|
+
if (!await fs5.pathExists(configPath)) {
|
|
357
|
+
spinner.fail("No sublay.json found");
|
|
358
|
+
console.log(chalk3.yellow("\n\u26A0\uFE0F Please run: npx @sublay/cli init"));
|
|
359
|
+
process.exit(1);
|
|
360
|
+
}
|
|
361
|
+
const config = await fs5.readJson(configPath);
|
|
362
|
+
spinner.text = `Fetching ${componentName}...`;
|
|
363
|
+
const registry = await fetchRegistry(componentName, config);
|
|
364
|
+
if (!registry) {
|
|
365
|
+
spinner.fail(`Component "${componentName}" not found`);
|
|
366
|
+
process.exit(1);
|
|
367
|
+
}
|
|
368
|
+
if (config.platform === "expo") {
|
|
369
|
+
registry.dependencies = registry.dependencies.map(
|
|
370
|
+
(dep) => dep.startsWith("@sublay/react-native") ? dep.replace("@sublay/react-native", "@sublay/expo") : dep
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
spinner.text = "Downloading files...";
|
|
374
|
+
let filesInstalled = 0;
|
|
375
|
+
for (const file of registry.files) {
|
|
376
|
+
if (shouldExcludeFile(file.path)) {
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
const fileContent = await fetchFile(registry.registryUrl, file.path);
|
|
380
|
+
if (!fileContent) {
|
|
381
|
+
console.log(chalk3.yellow(`
|
|
382
|
+
\u26A0\uFE0F Could not fetch ${file.path}`));
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
let transformed = transformImports(fileContent, config);
|
|
386
|
+
let finalFilePath = file.path;
|
|
387
|
+
if (!config.typescript && (file.path.endsWith(".ts") || file.path.endsWith(".tsx"))) {
|
|
388
|
+
transformed = await stripTypes(transformed, file.path);
|
|
389
|
+
finalFilePath = convertFileExtension(file.path);
|
|
390
|
+
}
|
|
391
|
+
const targetPath = getTargetPath(finalFilePath, config, componentName);
|
|
392
|
+
const fullPath = path5.join(process.cwd(), targetPath);
|
|
393
|
+
await fs5.ensureDir(path5.dirname(fullPath));
|
|
394
|
+
await fs5.writeFile(fullPath, transformed, "utf-8");
|
|
395
|
+
filesInstalled++;
|
|
396
|
+
}
|
|
397
|
+
spinner.text = "Creating index file...";
|
|
398
|
+
await createIndexFile(componentName, config, registry);
|
|
399
|
+
filesInstalled++;
|
|
400
|
+
spinner.succeed(`Added ${componentName}`);
|
|
401
|
+
console.log(
|
|
402
|
+
chalk3.green(`
|
|
403
|
+
\u2705 Successfully installed ${filesInstalled} files!`)
|
|
404
|
+
);
|
|
405
|
+
console.log(
|
|
406
|
+
chalk3.dim(
|
|
407
|
+
`
|
|
408
|
+
\u{1F4C1} Files added to ${path5.join(
|
|
409
|
+
config.paths.components,
|
|
410
|
+
componentName
|
|
411
|
+
)}`
|
|
412
|
+
)
|
|
413
|
+
);
|
|
414
|
+
await checkComponentDependencies(registry.dependencies);
|
|
415
|
+
const componentInfo = getComponentInfo(registry);
|
|
416
|
+
const relativeImportPath = path5.relative(
|
|
417
|
+
process.cwd(),
|
|
418
|
+
path5.join(config.paths.components, componentName)
|
|
419
|
+
);
|
|
420
|
+
console.log(chalk3.bold("\n\u{1F4D6} Usage:"));
|
|
421
|
+
console.log(
|
|
422
|
+
chalk3.dim(
|
|
423
|
+
` import { ${componentInfo.mainComponent} } from './${relativeImportPath}';`
|
|
424
|
+
)
|
|
425
|
+
);
|
|
426
|
+
console.log(chalk3.dim(` // With types:`));
|
|
427
|
+
console.log(
|
|
428
|
+
chalk3.dim(
|
|
429
|
+
` import { ${componentInfo.mainComponent}, type ${componentInfo.typeExport} } from './${relativeImportPath}';`
|
|
430
|
+
)
|
|
431
|
+
);
|
|
432
|
+
console.log();
|
|
433
|
+
} catch (error) {
|
|
434
|
+
spinner.fail("Failed to add component");
|
|
435
|
+
console.error(chalk3.red("\n\u274C Error:"), error);
|
|
436
|
+
process.exit(1);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
function getTargetPath(filePath, config, componentName) {
|
|
440
|
+
const componentDir = path5.join(config.paths.components, componentName);
|
|
441
|
+
if (filePath.startsWith("hooks/")) {
|
|
442
|
+
return path5.join(componentDir, "hooks", path5.basename(filePath));
|
|
443
|
+
}
|
|
444
|
+
if (filePath.startsWith("utils/")) {
|
|
445
|
+
return path5.join(componentDir, "utils", path5.basename(filePath));
|
|
446
|
+
}
|
|
447
|
+
if (filePath.startsWith("context/")) {
|
|
448
|
+
return path5.join(componentDir, "context", path5.basename(filePath));
|
|
449
|
+
}
|
|
450
|
+
if (filePath.startsWith("files/")) {
|
|
451
|
+
const normalizedPath = filePath.substring(6);
|
|
452
|
+
return path5.join(componentDir, "components", normalizedPath);
|
|
453
|
+
}
|
|
454
|
+
return path5.join(componentDir, filePath);
|
|
455
|
+
}
|
|
456
|
+
async function createIndexFile(componentName, config, registry) {
|
|
457
|
+
const componentDir = path5.join(
|
|
458
|
+
process.cwd(),
|
|
459
|
+
config.paths.components,
|
|
460
|
+
componentName
|
|
461
|
+
);
|
|
462
|
+
const indexExtension = config.typescript ? "ts" : "js";
|
|
463
|
+
const indexPath = path5.join(componentDir, `index.${indexExtension}`);
|
|
464
|
+
const componentInfo = getComponentInfo(registry);
|
|
465
|
+
const indexContent = `export { default as ${componentInfo.mainComponent} } from './components/${componentInfo.mainFile}';
|
|
466
|
+
export * from './components/${componentInfo.mainFile}';
|
|
467
|
+
`;
|
|
468
|
+
await fs5.writeFile(indexPath, indexContent, "utf-8");
|
|
469
|
+
}
|
|
470
|
+
function getComponentInfo(registry) {
|
|
471
|
+
return {
|
|
472
|
+
mainComponent: registry.exports.mainComponent,
|
|
473
|
+
mainFile: registry.exports.mainFile,
|
|
474
|
+
typeExport: registry.exports.typeExports?.[0] || ""
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
function shouldExcludeFile(filePath) {
|
|
478
|
+
const fileName = path5.basename(filePath);
|
|
479
|
+
const excludedFiles = [
|
|
480
|
+
"package.json",
|
|
481
|
+
"tsconfig.json",
|
|
482
|
+
".gitignore",
|
|
483
|
+
"pnpm-lock.yaml",
|
|
484
|
+
"package-lock.json",
|
|
485
|
+
"yarn.lock",
|
|
486
|
+
".eslintrc",
|
|
487
|
+
".prettierrc"
|
|
488
|
+
];
|
|
489
|
+
if (excludedFiles.includes(fileName)) {
|
|
490
|
+
return true;
|
|
491
|
+
}
|
|
492
|
+
if (filePath.includes("node_modules")) {
|
|
493
|
+
return true;
|
|
494
|
+
}
|
|
495
|
+
if (fileName.startsWith(".")) {
|
|
496
|
+
return true;
|
|
497
|
+
}
|
|
498
|
+
return false;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// src/index.ts
|
|
502
|
+
var program = new Command();
|
|
503
|
+
program.name("sublay").description("CLI for installing Sublay UI components").version("0.1.0");
|
|
504
|
+
program.command("init").description("Initialize Sublay configuration in your project").action(init);
|
|
505
|
+
program.command("add <component>").description("Add a component to your project").action(add);
|
|
506
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sublay/cli",
|
|
3
|
+
"version": "7.0.0",
|
|
4
|
+
"description": "CLI for installing Sublay UI components",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"sublay": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
14
|
+
"dev": "tsup src/index.ts --format esm --watch",
|
|
15
|
+
"typecheck": "tsc --noEmit"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"commander": "^14.0.3",
|
|
19
|
+
"prompts": "^2.4.2",
|
|
20
|
+
"chalk": "^5.6.2",
|
|
21
|
+
"ora": "^9.3.0",
|
|
22
|
+
"fs-extra": "^11.3.4",
|
|
23
|
+
"node-fetch": "^3.3.2",
|
|
24
|
+
"execa": "^9.6.1",
|
|
25
|
+
"@babel/core": "^7.29.0",
|
|
26
|
+
"@babel/preset-typescript": "^7.28.5",
|
|
27
|
+
"@babel/plugin-syntax-jsx": "^7.28.6"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^25.6.0",
|
|
31
|
+
"@types/prompts": "^2.4.9",
|
|
32
|
+
"@types/fs-extra": "^11.0.4",
|
|
33
|
+
"tsup": "^8.5.1",
|
|
34
|
+
"typescript": "^6.0.3"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18.0.0"
|
|
38
|
+
},
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
}
|
|
42
|
+
}
|