tweenlabs 0.1.1 → 0.1.3
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/bin/cli.js +214 -84
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const fs = require("fs");
|
|
4
|
-
const path = require("path");
|
|
5
|
-
const { execSync } = require("child_process");
|
|
6
|
-
const https = require("https");
|
|
7
|
-
const readline = require("readline");
|
|
3
|
+
const fs = require("node:fs");
|
|
4
|
+
const path = require("node:path");
|
|
5
|
+
const { execSync } = require("node:child_process");
|
|
6
|
+
const https = require("node:https");
|
|
7
|
+
const readline = require("node:readline");
|
|
8
8
|
|
|
9
9
|
// Simple Terminal Colors
|
|
10
10
|
const colors = {
|
|
@@ -24,40 +24,44 @@ ${colors.bold}${colors.cyan}TweenLabs CLI${colors.reset} - Install premium GSAP
|
|
|
24
24
|
${colors.bold}Usage:${colors.reset}
|
|
25
25
|
npx tweenlabs list List all available components
|
|
26
26
|
npx tweenlabs add <component-slug> Install a specific component
|
|
27
|
-
npx tweenlabs add all Install all components
|
|
28
|
-
|
|
29
|
-
${colors.bold}Examples:${colors.reset}
|
|
30
|
-
npx tweenlabs list
|
|
31
|
-
npx tweenlabs add 11-magnetic-dock
|
|
32
|
-
npx tweenlabs add magnetic-dock
|
|
33
|
-
npx tweenlabs add all
|
|
34
27
|
|
|
35
28
|
${colors.bold}Options:${colors.reset}
|
|
36
|
-
|
|
37
|
-
|
|
29
|
+
-y, --yes Skip all prompts (auto-accept defaults & install dependencies)
|
|
30
|
+
-p, --path <path> Specify a custom directory to install the component
|
|
31
|
+
-o, --overwrite Overwrite existing component files without prompting
|
|
32
|
+
-h, --help Show this help message
|
|
33
|
+
-v, --version Show version
|
|
38
34
|
`;
|
|
39
35
|
|
|
40
36
|
// Helper to make GET requests without external dependencies
|
|
41
37
|
function fetchJson(url) {
|
|
42
38
|
return new Promise((resolve, reject) => {
|
|
43
|
-
const client = url.startsWith("https") ? https : require("http");
|
|
44
|
-
client
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
39
|
+
const client = url.startsWith("https") ? https : require("node:http");
|
|
40
|
+
client
|
|
41
|
+
.get(
|
|
42
|
+
url,
|
|
43
|
+
{
|
|
44
|
+
headers: { "User-Agent": "tweenlabs-cli" },
|
|
45
|
+
},
|
|
46
|
+
(res) => {
|
|
47
|
+
if (res.statusCode !== 200) {
|
|
48
|
+
reject(new Error(`Server returned status code ${res.statusCode}`));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
let data = "";
|
|
52
|
+
res.on("data", (chunk) => {
|
|
53
|
+
data += chunk;
|
|
54
|
+
});
|
|
55
|
+
res.on("end", () => {
|
|
56
|
+
try {
|
|
57
|
+
resolve(JSON.parse(data));
|
|
58
|
+
} catch (err) {
|
|
59
|
+
reject(err);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
},
|
|
63
|
+
)
|
|
64
|
+
.on("error", reject);
|
|
61
65
|
});
|
|
62
66
|
}
|
|
63
67
|
|
|
@@ -71,7 +75,7 @@ function askQuestion(query) {
|
|
|
71
75
|
rl.question(query, (ans) => {
|
|
72
76
|
rl.close();
|
|
73
77
|
resolve(ans.trim());
|
|
74
|
-
})
|
|
78
|
+
}),
|
|
75
79
|
);
|
|
76
80
|
}
|
|
77
81
|
|
|
@@ -86,50 +90,108 @@ function detectPackageManager() {
|
|
|
86
90
|
async function main() {
|
|
87
91
|
const args = process.argv.slice(2);
|
|
88
92
|
|
|
89
|
-
if (args.
|
|
93
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
90
94
|
console.log(helpText);
|
|
91
95
|
process.exit(0);
|
|
92
96
|
}
|
|
93
97
|
|
|
94
98
|
if (args.includes("--version") || args.includes("-v")) {
|
|
95
|
-
|
|
99
|
+
try {
|
|
100
|
+
const pkg = require("../package.json");
|
|
101
|
+
console.log(pkg.version);
|
|
102
|
+
} catch (_err) {
|
|
103
|
+
console.log("0.1.1");
|
|
104
|
+
}
|
|
105
|
+
process.exit(0);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Parse flags
|
|
109
|
+
const isYes = args.includes("-y") || args.includes("--yes");
|
|
110
|
+
const isOverwrite = args.includes("-o") || args.includes("--overwrite");
|
|
111
|
+
|
|
112
|
+
// Parse path option
|
|
113
|
+
let customPath = null;
|
|
114
|
+
const pathIdx = args.findIndex((arg) => arg === "-p" || arg === "--path");
|
|
115
|
+
if (pathIdx !== -1 && pathIdx + 1 < args.length) {
|
|
116
|
+
customPath = args[pathIdx + 1];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Filter out options and their arguments from cleanArgs
|
|
120
|
+
const cleanArgs = [];
|
|
121
|
+
for (let i = 0; i < args.length; i++) {
|
|
122
|
+
if (
|
|
123
|
+
args[i] === "-y" ||
|
|
124
|
+
args[i] === "--yes" ||
|
|
125
|
+
args[i] === "-o" ||
|
|
126
|
+
args[i] === "--overwrite"
|
|
127
|
+
) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (args[i] === "-p" || args[i] === "--path") {
|
|
131
|
+
i++; // skip next arg (the path value)
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
cleanArgs.push(args[i]);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (cleanArgs.length === 0) {
|
|
138
|
+
console.log(helpText);
|
|
96
139
|
process.exit(0);
|
|
97
140
|
}
|
|
98
141
|
|
|
99
|
-
if (
|
|
100
|
-
console.log(
|
|
101
|
-
|
|
142
|
+
if (cleanArgs[0] === "list") {
|
|
143
|
+
console.log(
|
|
144
|
+
`\n${colors.cyan}🔍 Fetching available components list...${colors.reset}`,
|
|
145
|
+
);
|
|
146
|
+
const domain =
|
|
147
|
+
process.env.TWEENLABS_REGISTRY_URL || "https://tweenlabs.xyz";
|
|
102
148
|
const url = `${domain}/api/registry/list`;
|
|
103
149
|
try {
|
|
104
150
|
const data = await fetchJson(url);
|
|
105
|
-
console.log(
|
|
151
|
+
console.log(
|
|
152
|
+
`\n${colors.bold}${colors.green}Available TweenLabs Components:${colors.reset}\n`,
|
|
153
|
+
);
|
|
106
154
|
for (const comp of data.components) {
|
|
107
|
-
console.log(
|
|
155
|
+
console.log(
|
|
156
|
+
` ${colors.bold}${colors.cyan}* ${comp.name}${colors.reset}`,
|
|
157
|
+
);
|
|
108
158
|
console.log(` Slug: ${comp.slug} (or ${comp.cleanSlug})`);
|
|
109
159
|
console.log(` Desc: ${comp.description}\n`);
|
|
110
160
|
}
|
|
111
161
|
process.exit(0);
|
|
112
162
|
} catch (err) {
|
|
113
|
-
console.error(
|
|
114
|
-
|
|
163
|
+
console.error(
|
|
164
|
+
`\n${colors.red}❌ Failed to fetch components list.${colors.reset}`,
|
|
165
|
+
);
|
|
166
|
+
console.error(`${colors.gray}Details: ${err.message}${colors.reset}`);
|
|
115
167
|
process.exit(1);
|
|
116
168
|
}
|
|
117
169
|
}
|
|
118
170
|
|
|
119
|
-
if (
|
|
120
|
-
console.log(
|
|
171
|
+
if (cleanArgs[0] !== "add") {
|
|
172
|
+
console.log(
|
|
173
|
+
`${colors.red}Error: Unknown command "${cleanArgs[0]}". Did you mean "add" or "list"?${colors.reset}`,
|
|
174
|
+
);
|
|
121
175
|
console.log(helpText);
|
|
122
176
|
process.exit(1);
|
|
123
177
|
}
|
|
124
178
|
|
|
125
|
-
const componentSlug =
|
|
179
|
+
const componentSlug = cleanArgs[1];
|
|
126
180
|
if (!componentSlug) {
|
|
127
|
-
console.log(
|
|
128
|
-
|
|
181
|
+
console.log(
|
|
182
|
+
`${colors.red}Error: Please specify a component slug to add.${colors.reset}`,
|
|
183
|
+
);
|
|
184
|
+
console.log(
|
|
185
|
+
colors.gray +
|
|
186
|
+
"Example: npx tweenlabs add 11-magnetic-dock" +
|
|
187
|
+
colors.reset,
|
|
188
|
+
);
|
|
129
189
|
process.exit(1);
|
|
130
190
|
}
|
|
131
191
|
|
|
132
|
-
console.log(
|
|
192
|
+
console.log(
|
|
193
|
+
`\n${colors.cyan}🔍 Fetching ${colors.bold}${componentSlug}${colors.reset}${colors.cyan} registry data...${colors.reset}`,
|
|
194
|
+
);
|
|
133
195
|
|
|
134
196
|
// Determine registry domain (allow local testing via env variable)
|
|
135
197
|
const domain = process.env.TWEENLABS_REGISTRY_URL || "https://tweenlabs.xyz";
|
|
@@ -139,24 +201,85 @@ async function main() {
|
|
|
139
201
|
try {
|
|
140
202
|
componentData = await fetchJson(url);
|
|
141
203
|
} catch (err) {
|
|
142
|
-
console.error(
|
|
143
|
-
|
|
204
|
+
console.error(
|
|
205
|
+
`\n${colors.red}❌ Failed to fetch component. Make sure the slug is correct and the server is running.${colors.reset}`,
|
|
206
|
+
);
|
|
207
|
+
console.error(`${colors.gray}Details: ${err.message}${colors.reset}`);
|
|
144
208
|
process.exit(1);
|
|
145
209
|
}
|
|
146
210
|
|
|
147
|
-
console.log(
|
|
211
|
+
console.log(
|
|
212
|
+
`${colors.green}✓ Component found: ${colors.bold}${componentData.className}${colors.reset}`,
|
|
213
|
+
);
|
|
148
214
|
|
|
149
215
|
// Resolve target directory
|
|
150
|
-
let targetDir =
|
|
151
|
-
if (
|
|
152
|
-
targetDir = path.
|
|
216
|
+
let targetDir = "";
|
|
217
|
+
if (customPath) {
|
|
218
|
+
targetDir = path.resolve(process.cwd(), customPath);
|
|
219
|
+
} else {
|
|
220
|
+
// 1. Try to read components.json (shadcn configuration)
|
|
221
|
+
const componentsJsonPath = path.join(process.cwd(), "components.json");
|
|
222
|
+
if (fs.existsSync(componentsJsonPath)) {
|
|
223
|
+
try {
|
|
224
|
+
const config = JSON.parse(fs.readFileSync(componentsJsonPath, "utf-8"));
|
|
225
|
+
const compAlias = config.aliases?.components;
|
|
226
|
+
if (compAlias) {
|
|
227
|
+
const cleanAlias = compAlias.replace(/^[@~]\//, "");
|
|
228
|
+
if (
|
|
229
|
+
fs.existsSync(path.join(process.cwd(), "src")) &&
|
|
230
|
+
!cleanAlias.startsWith("src/")
|
|
231
|
+
) {
|
|
232
|
+
targetDir = path.join(
|
|
233
|
+
process.cwd(),
|
|
234
|
+
"src",
|
|
235
|
+
cleanAlias,
|
|
236
|
+
"tweenlabs",
|
|
237
|
+
);
|
|
238
|
+
} else {
|
|
239
|
+
targetDir = path.join(process.cwd(), cleanAlias, "tweenlabs");
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
} catch (_err) {
|
|
243
|
+
// Ignore JSON parse errors
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// 2. Fallback if targetDir is still not resolved
|
|
248
|
+
if (!targetDir) {
|
|
249
|
+
if (fs.existsSync(path.join(process.cwd(), "src"))) {
|
|
250
|
+
targetDir = path.join(process.cwd(), "src", "components", "tweenlabs");
|
|
251
|
+
} else {
|
|
252
|
+
targetDir = path.join(process.cwd(), "components", "tweenlabs");
|
|
253
|
+
}
|
|
254
|
+
}
|
|
153
255
|
}
|
|
154
256
|
|
|
155
|
-
|
|
156
|
-
`📁
|
|
257
|
+
console.log(
|
|
258
|
+
`📁 Target directory: ${colors.bold}${path.relative(process.cwd(), targetDir)}${colors.reset}`,
|
|
157
259
|
);
|
|
158
|
-
|
|
159
|
-
|
|
260
|
+
|
|
261
|
+
// Check if any files already exist
|
|
262
|
+
let hasExistingFiles = false;
|
|
263
|
+
for (const file of componentData.files) {
|
|
264
|
+
const filePath = path.join(targetDir, file.name);
|
|
265
|
+
if (fs.existsSync(filePath)) {
|
|
266
|
+
hasExistingFiles = true;
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (hasExistingFiles && !isOverwrite && !isYes) {
|
|
272
|
+
const overwriteConfirm = await askQuestion(
|
|
273
|
+
`⚠️ Component files already exist in ${colors.bold}${path.relative(process.cwd(), targetDir)}${colors.reset}. Overwrite? (y/n) ${colors.gray}[y]${colors.reset}: `,
|
|
274
|
+
);
|
|
275
|
+
if (
|
|
276
|
+
overwriteConfirm &&
|
|
277
|
+
overwriteConfirm.toLowerCase() !== "y" &&
|
|
278
|
+
overwriteConfirm.toLowerCase() !== "yes"
|
|
279
|
+
) {
|
|
280
|
+
console.log(`${colors.yellow}Installation cancelled.${colors.reset}`);
|
|
281
|
+
process.exit(0);
|
|
282
|
+
}
|
|
160
283
|
}
|
|
161
284
|
|
|
162
285
|
// Create target directory if it doesn't exist
|
|
@@ -169,7 +292,9 @@ async function main() {
|
|
|
169
292
|
for (const file of componentData.files) {
|
|
170
293
|
const filePath = path.join(targetDir, file.name);
|
|
171
294
|
fs.writeFileSync(filePath, file.content, "utf-8");
|
|
172
|
-
console.log(
|
|
295
|
+
console.log(
|
|
296
|
+
`${colors.green} → Created: ${colors.bold}${path.relative(process.cwd(), filePath)}${colors.reset}`,
|
|
297
|
+
);
|
|
173
298
|
}
|
|
174
299
|
|
|
175
300
|
// Check and install dependencies
|
|
@@ -178,8 +303,10 @@ async function main() {
|
|
|
178
303
|
// Read package.json to see if dependencies are already installed
|
|
179
304
|
let pkgJson = {};
|
|
180
305
|
try {
|
|
181
|
-
pkgJson = JSON.parse(
|
|
182
|
-
|
|
306
|
+
pkgJson = JSON.parse(
|
|
307
|
+
fs.readFileSync(path.join(process.cwd(), "package.json"), "utf-8"),
|
|
308
|
+
);
|
|
309
|
+
} catch (_err) {
|
|
183
310
|
// Ignore
|
|
184
311
|
}
|
|
185
312
|
|
|
@@ -188,36 +315,39 @@ async function main() {
|
|
|
188
315
|
|
|
189
316
|
if (missingDeps.length > 0) {
|
|
190
317
|
const pm = detectPackageManager();
|
|
191
|
-
console.log(
|
|
192
|
-
|
|
193
|
-
const installConfirm = await askQuestion(
|
|
194
|
-
`Do you want to install them using ${colors.bold}${pm}${colors.reset}? (y/n) ${colors.gray}[y]${colors.reset}: `
|
|
318
|
+
console.log(
|
|
319
|
+
`\n${colors.cyan}📦 Installing missing dependencies: ${colors.bold}${missingDeps.join(", ")}${colors.reset} using ${pm}...`,
|
|
195
320
|
);
|
|
196
321
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
console.log(`${colors.yellow}⚠️ Skipping installation. Please make sure to install ${missingDeps.join(", ")} manually.${colors.reset}`);
|
|
322
|
+
let installCmd = "";
|
|
323
|
+
if (pm === "pnpm") installCmd = `pnpm add ${missingDeps.join(" ")}`;
|
|
324
|
+
else if (pm === "yarn") installCmd = `yarn add ${missingDeps.join(" ")}`;
|
|
325
|
+
else if (pm === "bun") installCmd = `bun add ${missingDeps.join(" ")}`;
|
|
326
|
+
else installCmd = `npm install ${missingDeps.join(" ")}`;
|
|
327
|
+
|
|
328
|
+
try {
|
|
329
|
+
execSync(installCmd, { stdio: "inherit" });
|
|
330
|
+
console.log(
|
|
331
|
+
`${colors.green}✓ Dependencies installed successfully!${colors.reset}`,
|
|
332
|
+
);
|
|
333
|
+
} catch (_err) {
|
|
334
|
+
console.error(
|
|
335
|
+
`${colors.red}❌ Failed to install dependencies. Please run "${installCmd}" manually.${colors.reset}`,
|
|
336
|
+
);
|
|
213
337
|
}
|
|
214
338
|
} else {
|
|
215
|
-
console.log(
|
|
339
|
+
console.log(
|
|
340
|
+
`\n${colors.green}✓ All dependencies (${dependencies.join(", ")}) already installed.${colors.reset}`,
|
|
341
|
+
);
|
|
216
342
|
}
|
|
217
343
|
}
|
|
218
344
|
|
|
219
|
-
console.log(
|
|
220
|
-
|
|
345
|
+
console.log(
|
|
346
|
+
`\n${colors.green}${colors.bold}🎉 Installation complete!${colors.reset}`,
|
|
347
|
+
);
|
|
348
|
+
console.log(
|
|
349
|
+
`You can now import and use the ${colors.bold}${componentData.className}${colors.reset} component in your project.\n`,
|
|
350
|
+
);
|
|
221
351
|
}
|
|
222
352
|
|
|
223
353
|
main().catch((err) => {
|