@vicket/create-support 1.1.1 → 1.1.2

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.
Files changed (71) hide show
  1. package/bin/create-vicket-support.js +429 -389
  2. package/package.json +1 -1
  3. package/templates/next/src/app/api/vicket/[...path]/route.ts +2 -55
  4. package/templates/next/src/app/components/vicket/ReplyForm.tsx +154 -0
  5. package/templates/next/src/app/components/vicket/SupportContent.tsx +298 -0
  6. package/templates/next/src/app/components/vicket/TicketDialog.tsx +3 -3
  7. package/templates/next/src/app/support/page.tsx +27 -353
  8. package/templates/next/src/app/ticket/page.tsx +110 -325
  9. package/templates/next/src/app/vicket.css +1325 -1325
  10. package/templates/nuxt/app/assets/css/vicket.css +1325 -1325
  11. package/templates/nuxt/app/components/VicketReplyForm.vue +154 -0
  12. package/templates/nuxt/app/components/VicketSupportContent.vue +255 -0
  13. package/templates/nuxt/app/components/VicketTicketDialog.vue +2 -2
  14. package/templates/nuxt/app/pages/support.vue +7 -293
  15. package/templates/nuxt/app/pages/ticket.vue +36 -178
  16. package/templates/nuxt/server/api/vicket/[...path].ts +2 -85
  17. package/templates/sveltekit/src/lib/vicket/ReplyForm.svelte +134 -0
  18. package/templates/sveltekit/src/lib/vicket/SupportContent.svelte +263 -0
  19. package/templates/sveltekit/src/lib/vicket/TicketDialog.svelte +457 -459
  20. package/templates/sveltekit/src/lib/vicket.css +1325 -1325
  21. package/templates/sveltekit/src/routes/api/vicket/[...path]/+server.ts +2 -76
  22. package/templates/sveltekit/src/routes/support/+page.server.ts +13 -0
  23. package/templates/sveltekit/src/routes/support/+page.svelte +3 -312
  24. package/templates/sveltekit/src/routes/ticket/+page.server.ts +19 -0
  25. package/templates/sveltekit/src/routes/ticket/+page.svelte +13 -188
  26. package/templates-tailwind/next/src/app/api/vicket/[...path]/route.ts +6 -0
  27. package/templates-tailwind/next/src/app/support/page.tsx +33 -3
  28. package/templates-tailwind/next/src/app/ticket/page.tsx +249 -6
  29. package/templates-tailwind/next/src/components/vicket/reply-form.tsx +113 -0
  30. package/templates-tailwind/next/src/components/vicket/support-content.tsx +265 -0
  31. package/templates-tailwind/next/src/components/vicket/ticket-dialog.tsx +2 -2
  32. package/templates-tailwind/nuxt/app/components/VicketReplyForm.vue +169 -0
  33. package/templates-tailwind/nuxt/app/components/{VicketSupportPage.vue → VicketSupportContent.vue} +275 -317
  34. package/templates-tailwind/nuxt/app/components/VicketTicketDialog.vue +3 -0
  35. package/templates-tailwind/nuxt/app/pages/support.vue +10 -1
  36. package/templates-tailwind/nuxt/app/pages/ticket.vue +298 -1
  37. package/templates-tailwind/nuxt/server/api/vicket/[...path].ts +2 -0
  38. package/templates-tailwind/sveltekit/src/lib/vicket/ReplyForm.svelte +127 -0
  39. package/templates-tailwind/sveltekit/src/lib/vicket/{SupportPage.svelte → SupportContent.svelte} +9 -71
  40. package/templates-tailwind/sveltekit/src/lib/vicket/TicketDialog.svelte +405 -406
  41. package/templates-tailwind/sveltekit/src/routes/api/vicket/[...path]/+server.ts +3 -0
  42. package/templates-tailwind/sveltekit/src/routes/support/+page.server.ts +13 -0
  43. package/templates-tailwind/sveltekit/src/routes/support/+page.svelte +4 -2
  44. package/templates-tailwind/sveltekit/src/routes/ticket/+page.server.ts +19 -0
  45. package/templates-tailwind/sveltekit/src/routes/ticket/+page.svelte +292 -2
  46. package/templates/next/src/app/utils/vicket/api.ts +0 -149
  47. package/templates/next/src/app/utils/vicket/types.ts +0 -85
  48. package/templates/next/src/app/utils/vicket/utils.ts +0 -49
  49. package/templates/nuxt/app/composables/useVicket.ts +0 -274
  50. package/templates/sveltekit/src/lib/vicket/api.ts +0 -162
  51. package/templates/sveltekit/src/lib/vicket/types.ts +0 -87
  52. package/templates/sveltekit/src/lib/vicket/utils.ts +0 -55
  53. package/templates-tailwind/next/src/app/api/vicket/init/route.ts +0 -24
  54. package/templates-tailwind/next/src/app/api/vicket/messages/route.ts +0 -36
  55. package/templates-tailwind/next/src/app/api/vicket/thread/route.ts +0 -27
  56. package/templates-tailwind/next/src/app/api/vicket/tickets/route.ts +0 -37
  57. package/templates-tailwind/next/src/components/vicket/support-page.tsx +0 -359
  58. package/templates-tailwind/next/src/components/vicket/ticket-page.tsx +0 -425
  59. package/templates-tailwind/next/src/lib/vicket.ts +0 -257
  60. package/templates-tailwind/nuxt/app/components/VicketTicketPage.vue +0 -449
  61. package/templates-tailwind/nuxt/app/composables/use-vicket.ts +0 -249
  62. package/templates-tailwind/nuxt/server/api/vicket/init.get.ts +0 -22
  63. package/templates-tailwind/nuxt/server/api/vicket/messages.post.ts +0 -56
  64. package/templates-tailwind/nuxt/server/api/vicket/thread.get.ts +0 -26
  65. package/templates-tailwind/nuxt/server/api/vicket/tickets.post.ts +0 -53
  66. package/templates-tailwind/sveltekit/src/lib/vicket/TicketPage.svelte +0 -465
  67. package/templates-tailwind/sveltekit/src/lib/vicket/index.ts +0 -257
  68. package/templates-tailwind/sveltekit/src/routes/api/vicket/init/+server.ts +0 -22
  69. package/templates-tailwind/sveltekit/src/routes/api/vicket/messages/+server.ts +0 -40
  70. package/templates-tailwind/sveltekit/src/routes/api/vicket/thread/+server.ts +0 -25
  71. package/templates-tailwind/sveltekit/src/routes/api/vicket/tickets/+server.ts +0 -37
@@ -1,389 +1,429 @@
1
- #!/usr/bin/env node
2
-
3
- const fs = require("node:fs");
4
- const path = require("node:path");
5
- const { stdin } = require("node:process");
6
-
7
- const PKG_VERSION = require(
8
- path.resolve(__dirname, "..", "package.json"),
9
- ).version;
10
-
11
- const FRAMEWORK_ALIASES = {
12
- next: "next",
13
- nextjs: "next",
14
- nuxt: "nuxt",
15
- svelte: "sveltekit",
16
- sveltekit: "sveltekit",
17
- };
18
-
19
- const FRAMEWORK_CHOICES = [
20
- { value: "next", label: "Next.js (App Router)" },
21
- { value: "nuxt", label: "Nuxt 4 (Vue)" },
22
- { value: "sveltekit", label: "SvelteKit" },
23
- ];
24
-
25
- const DEFAULT_FRAMEWORK = "next";
26
- const STYLING_ALIASES = {
27
- css: "css",
28
- tailwind: "tailwind",
29
- tw: "tailwind",
30
- };
31
- const STYLING_CHOICES = [
32
- { value: "css", label: "CSS file (default)" },
33
- { value: "tailwind", label: "Tailwind CSS (inline utilities)" },
34
- ];
35
- const DEFAULT_STYLING = "css";
36
- let clack = null;
37
-
38
- async function loadClack() {
39
- if (clack) return clack;
40
- const prompts = await import("@clack/prompts");
41
- clack = prompts;
42
- return prompts;
43
- }
44
-
45
- function printHelp() {
46
- console.log(`
47
- Usage:
48
- create-vicket-support [options]
49
-
50
- Options:
51
- -f, --framework <name> Framework: next | nuxt | sveltekit
52
- -s, --styling <name> Styling: css | tailwind (default: css)
53
- --tailwind Shortcut for --styling tailwind
54
- -d, --dir <path> Output directory (default: current directory)
55
- -y, --yes Overwrite existing files without prompt
56
- -v, --version Show version
57
- -h, --help Show help
58
-
59
- Examples:
60
- npx @vicket/create-support --framework next
61
- npx @vicket/create-support --framework next --styling tailwind
62
- npx @vicket/create-support --framework nuxt --dir ./my-app
63
- npx @vicket/create-support -f sveltekit -d . -y --tailwind
64
- `);
65
- }
66
-
67
- function parseArgs(argv) {
68
- const options = {
69
- framework: "",
70
- styling: "",
71
- dir: process.cwd(),
72
- yes: false,
73
- help: false,
74
- version: false,
75
- };
76
-
77
- for (let i = 0; i < argv.length; i += 1) {
78
- const arg = argv[i];
79
- if (arg === "--version" || arg === "-v") {
80
- options.version = true;
81
- continue;
82
- }
83
- if (arg === "--help" || arg === "-h") {
84
- options.help = true;
85
- continue;
86
- }
87
- if (arg === "--framework" || arg === "-f") {
88
- options.framework = argv[i + 1] || "";
89
- i += 1;
90
- continue;
91
- }
92
- if (arg.startsWith("--framework=")) {
93
- options.framework = arg.split("=")[1] || "";
94
- continue;
95
- }
96
- if (arg === "--styling" || arg === "-s") {
97
- options.styling = argv[i + 1] || options.styling;
98
- i += 1;
99
- continue;
100
- }
101
- if (arg.startsWith("--styling=")) {
102
- options.styling = arg.split("=")[1] || options.styling;
103
- continue;
104
- }
105
- if (arg === "--tailwind") {
106
- options.styling = "tailwind";
107
- continue;
108
- }
109
- if (arg === "--dir" || arg === "-d") {
110
- options.dir = argv[i + 1] || options.dir;
111
- i += 1;
112
- continue;
113
- }
114
- if (arg.startsWith("--dir=")) {
115
- options.dir = arg.split("=")[1] || options.dir;
116
- continue;
117
- }
118
- if (arg === "--yes" || arg === "-y") {
119
- options.yes = true;
120
- continue;
121
- }
122
- }
123
-
124
- return options;
125
- }
126
-
127
- function normalizeFramework(input) {
128
- return FRAMEWORK_ALIASES[(input || "").trim().toLowerCase()] || "";
129
- }
130
-
131
- function normalizeStyling(input) {
132
- return STYLING_ALIASES[(input || "").trim().toLowerCase()] || "";
133
- }
134
-
135
- async function askFrameworkSelection() {
136
- if (!stdin.isTTY) {
137
- return "";
138
- }
139
-
140
- const p = await loadClack();
141
- p.intro("Create Vicket Support");
142
- const value = await p.select({
143
- message: "Select a framework",
144
- initialValue: DEFAULT_FRAMEWORK,
145
- options: FRAMEWORK_CHOICES.map((choice) => ({
146
- value: choice.value,
147
- label: choice.label,
148
- })),
149
- });
150
-
151
- if (p.isCancel(value)) {
152
- p.cancel("Operation cancelled.");
153
- return "";
154
- }
155
-
156
- return value;
157
- }
158
-
159
- async function askStylingSelection() {
160
- if (!stdin.isTTY) {
161
- return DEFAULT_STYLING;
162
- }
163
-
164
- const p = await loadClack();
165
- const value = await p.select({
166
- message: "Select a styling mode",
167
- initialValue: DEFAULT_STYLING,
168
- options: STYLING_CHOICES.map((choice) => ({
169
- value: choice.value,
170
- label: choice.label,
171
- })),
172
- });
173
-
174
- if (p.isCancel(value)) {
175
- p.cancel("Operation cancelled.");
176
- return "";
177
- }
178
-
179
- return value;
180
- }
181
-
182
- function collectFiles(baseDir, currentDir = baseDir, acc = []) {
183
- const entries = fs.readdirSync(currentDir, { withFileTypes: true });
184
- for (const entry of entries) {
185
- const absolute = path.join(currentDir, entry.name);
186
- if (entry.isDirectory()) {
187
- collectFiles(baseDir, absolute, acc);
188
- continue;
189
- }
190
-
191
- const relative = path.relative(baseDir, absolute);
192
- acc.push(relative);
193
- }
194
- return acc;
195
- }
196
-
197
- function ensureDirectory(filePath) {
198
- const dir = path.dirname(filePath);
199
- fs.mkdirSync(dir, { recursive: true });
200
- }
201
-
202
- /**
203
- * Detect whether a Next.js project uses the src/ directory layout.
204
- * Returns true if src/app exists, false if app/ exists at root,
205
- * or falls back to checking for a src/ directory.
206
- */
207
- function detectNextSrcLayout(targetRoot) {
208
- if (fs.existsSync(path.join(targetRoot, "src", "app"))) {
209
- return true;
210
- }
211
- if (fs.existsSync(path.join(targetRoot, "app"))) {
212
- return false;
213
- }
214
- if (fs.existsSync(path.join(targetRoot, "src"))) {
215
- return true;
216
- }
217
- return false;
218
- }
219
-
220
- async function confirmOverwrite(existingFiles) {
221
- if (!stdin.isTTY) {
222
- return false;
223
- }
224
-
225
- const p = await loadClack();
226
- p.log.warn("The following files already exist:");
227
- for (const file of existingFiles) {
228
- p.log.message(`- ${file}`);
229
- }
230
-
231
- const value = await p.confirm({
232
- message: "Overwrite these files?",
233
- initialValue: false,
234
- });
235
-
236
- if (p.isCancel(value)) {
237
- p.cancel("Operation cancelled.");
238
- return false;
239
- }
240
-
241
- return Boolean(value);
242
- }
243
-
244
- async function printPostInstall(framework, styling) {
245
- const envLinesByFramework = {
246
- next: [
247
- "- VICKET_API_URL (https://api.vicket.app/api/v1) server-side only",
248
- "- VICKET_API_KEY (website API key) — server-side only, NEVER expose to client",
249
- ],
250
- nuxt: [
251
- "- VICKET_API_URL (https://api.vicket.app/api/v1) — server-side only",
252
- "- VICKET_API_KEY (website API key) — server-side only, NEVER expose to client",
253
- ],
254
- sveltekit: [
255
- "- VICKET_API_URL (https://api.vicket.app/api/v1) — server-side only",
256
- "- VICKET_API_KEY (website API key) — server-side only, NEVER expose to client",
257
- ],
258
- };
259
- const envLines = envLinesByFramework[framework] || envLinesByFramework.next;
260
-
261
- if (stdin.isTTY) {
262
- const p = await loadClack();
263
- p.outro("Scaffold complete.");
264
- p.log.info("Set these environment variables in the target app:");
265
- for (const line of envLines) {
266
- p.log.message(line);
267
- }
268
-
269
- if (framework === "nuxt") {
270
- p.log.info(
271
- "Nuxt note: if you prefer global styles, add ~/assets/css/vicket.css to nuxt.config.ts > css. Files are in the app/ directory (Nuxt 4 convention).",
272
- );
273
- }
274
- if (styling === "tailwind") {
275
- p.log.info(
276
- "Tailwind mode: ensure Tailwind CSS is configured in the target app.",
277
- );
278
- }
279
- return;
280
- }
281
-
282
- console.log("\nScaffold complete.");
283
- console.log("Set these environment variables in the target app:");
284
- for (const line of envLines) {
285
- console.log(line);
286
- }
287
- if (styling === "tailwind") {
288
- console.log(
289
- "Tailwind mode: ensure Tailwind CSS is configured in the target app.",
290
- );
291
- }
292
- }
293
-
294
- async function main() {
295
- const options = parseArgs(process.argv.slice(2));
296
- if (options.version) {
297
- console.log(`@vicket/create-support v${PKG_VERSION}`);
298
- process.exit(0);
299
- }
300
- if (options.help) {
301
- printHelp();
302
- process.exit(0);
303
- }
304
-
305
- let framework = normalizeFramework(options.framework);
306
- const requestedStyling = (options.styling || "").trim();
307
- if (requestedStyling && !normalizeStyling(requestedStyling)) {
308
- console.error("Unsupported styling. Use --styling css|tailwind");
309
- printHelp();
310
- process.exit(1);
311
- }
312
-
313
- let styling = normalizeStyling(options.styling) || DEFAULT_STYLING;
314
-
315
- if (!framework) {
316
- framework = await askFrameworkSelection();
317
- }
318
-
319
- if (!framework) {
320
- console.error(
321
- "Missing or unsupported framework. Use --framework next|nuxt|sveltekit",
322
- );
323
- printHelp();
324
- process.exit(1);
325
- }
326
-
327
- if (
328
- !normalizeStyling(options.styling) &&
329
- stdin.isTTY &&
330
- !process.argv.includes("--tailwind")
331
- ) {
332
- const selectedStyling = await askStylingSelection();
333
- if (!selectedStyling) {
334
- console.log("Aborted. No files were written.");
335
- process.exit(0);
336
- }
337
- styling = selectedStyling;
338
- }
339
-
340
- const packageRoot = path.resolve(__dirname, "..");
341
- const templateDir =
342
- styling === "tailwind" ? "templates-tailwind" : "templates";
343
- const templateRoot = path.join(packageRoot, templateDir, framework);
344
-
345
- if (!fs.existsSync(templateRoot)) {
346
- console.error(`Template not found: ${templateDir}/${framework}`);
347
- process.exit(1);
348
- }
349
-
350
- const targetRoot = path.resolve(options.dir);
351
- const files = collectFiles(templateRoot);
352
-
353
- const stripSrc =
354
- framework === "next" && !detectNextSrcLayout(targetRoot);
355
- const srcPrefix = "src" + path.sep;
356
-
357
- function toDestRelative(relativePath) {
358
- if (stripSrc && relativePath.startsWith(srcPrefix)) {
359
- return relativePath.slice(srcPrefix.length);
360
- }
361
- return relativePath;
362
- }
363
-
364
- const existing = files
365
- .map((f) => toDestRelative(f))
366
- .filter((destPath) => fs.existsSync(path.join(targetRoot, destPath)));
367
-
368
- if (existing.length > 0 && !options.yes) {
369
- const shouldOverwrite = await confirmOverwrite(existing);
370
- if (!shouldOverwrite) {
371
- console.log("Aborted. No files were written.");
372
- process.exit(0);
373
- }
374
- }
375
-
376
- for (const relativePath of files) {
377
- const from = path.join(templateRoot, relativePath);
378
- const to = path.join(targetRoot, toDestRelative(relativePath));
379
- ensureDirectory(to);
380
- fs.copyFileSync(from, to);
381
- }
382
-
383
- await printPostInstall(framework, styling);
384
- }
385
-
386
- main().catch((error) => {
387
- console.error(error);
388
- process.exit(1);
389
- });
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("node:fs");
4
+ const path = require("node:path");
5
+ const { stdin } = require("node:process");
6
+
7
+ const PKG_VERSION = require(
8
+ path.resolve(__dirname, "..", "package.json"),
9
+ ).version;
10
+
11
+ const FRAMEWORK_ALIASES = {
12
+ next: "next",
13
+ nextjs: "next",
14
+ nuxt: "nuxt",
15
+ svelte: "sveltekit",
16
+ sveltekit: "sveltekit",
17
+ };
18
+
19
+ const FRAMEWORK_CHOICES = [
20
+ { value: "next", label: "Next.js (App Router)" },
21
+ { value: "nuxt", label: "Nuxt 4 (Vue)" },
22
+ { value: "sveltekit", label: "SvelteKit" },
23
+ ];
24
+
25
+ const DEFAULT_FRAMEWORK = "next";
26
+ const STYLING_ALIASES = {
27
+ css: "css",
28
+ tailwind: "tailwind",
29
+ tw: "tailwind",
30
+ };
31
+ const STYLING_CHOICES = [
32
+ { value: "css", label: "CSS file (default)" },
33
+ { value: "tailwind", label: "Tailwind CSS (inline utilities)" },
34
+ ];
35
+ const DEFAULT_STYLING = "css";
36
+ let clack = null;
37
+
38
+ async function loadClack() {
39
+ if (clack) return clack;
40
+ const prompts = await import("@clack/prompts");
41
+ clack = prompts;
42
+ return prompts;
43
+ }
44
+
45
+ function printHelp() {
46
+ console.log(`
47
+ Usage:
48
+ create-vicket-support [options]
49
+
50
+ Options:
51
+ -f, --framework <name> Framework: next | nuxt | sveltekit
52
+ -s, --styling <name> Styling: css | tailwind (default: css)
53
+ --tailwind Shortcut for --styling tailwind
54
+ -d, --dir <path> Output directory (default: current directory)
55
+ -y, --yes Overwrite existing files without prompt
56
+ -v, --version Show version
57
+ -h, --help Show help
58
+
59
+ Examples:
60
+ npx @vicket/create-support --framework next
61
+ npx @vicket/create-support --framework next --styling tailwind
62
+ npx @vicket/create-support --framework nuxt --dir ./my-app
63
+ npx @vicket/create-support -f sveltekit -d . -y --tailwind
64
+ `);
65
+ }
66
+
67
+ function parseArgs(argv) {
68
+ const options = {
69
+ framework: "",
70
+ styling: "",
71
+ dir: process.cwd(),
72
+ yes: false,
73
+ help: false,
74
+ version: false,
75
+ };
76
+
77
+ for (let i = 0; i < argv.length; i += 1) {
78
+ const arg = argv[i];
79
+ if (arg === "--version" || arg === "-v") {
80
+ options.version = true;
81
+ continue;
82
+ }
83
+ if (arg === "--help" || arg === "-h") {
84
+ options.help = true;
85
+ continue;
86
+ }
87
+ if (arg === "--framework" || arg === "-f") {
88
+ options.framework = argv[i + 1] || "";
89
+ i += 1;
90
+ continue;
91
+ }
92
+ if (arg.startsWith("--framework=")) {
93
+ options.framework = arg.split("=")[1] || "";
94
+ continue;
95
+ }
96
+ if (arg === "--styling" || arg === "-s") {
97
+ options.styling = argv[i + 1] || options.styling;
98
+ i += 1;
99
+ continue;
100
+ }
101
+ if (arg.startsWith("--styling=")) {
102
+ options.styling = arg.split("=")[1] || options.styling;
103
+ continue;
104
+ }
105
+ if (arg === "--tailwind") {
106
+ options.styling = "tailwind";
107
+ continue;
108
+ }
109
+ if (arg === "--dir" || arg === "-d") {
110
+ options.dir = argv[i + 1] || options.dir;
111
+ i += 1;
112
+ continue;
113
+ }
114
+ if (arg.startsWith("--dir=")) {
115
+ options.dir = arg.split("=")[1] || options.dir;
116
+ continue;
117
+ }
118
+ if (arg === "--yes" || arg === "-y") {
119
+ options.yes = true;
120
+ continue;
121
+ }
122
+ }
123
+
124
+ return options;
125
+ }
126
+
127
+ function normalizeFramework(input) {
128
+ return FRAMEWORK_ALIASES[(input || "").trim().toLowerCase()] || "";
129
+ }
130
+
131
+ function normalizeStyling(input) {
132
+ return STYLING_ALIASES[(input || "").trim().toLowerCase()] || "";
133
+ }
134
+
135
+ async function askFrameworkSelection() {
136
+ if (!stdin.isTTY) {
137
+ return "";
138
+ }
139
+
140
+ const p = await loadClack();
141
+ p.intro("Create Vicket Support");
142
+ const value = await p.select({
143
+ message: "Select a framework",
144
+ initialValue: DEFAULT_FRAMEWORK,
145
+ options: FRAMEWORK_CHOICES.map((choice) => ({
146
+ value: choice.value,
147
+ label: choice.label,
148
+ })),
149
+ });
150
+
151
+ if (p.isCancel(value)) {
152
+ p.cancel("Operation cancelled.");
153
+ return "";
154
+ }
155
+
156
+ return value;
157
+ }
158
+
159
+ async function askStylingSelection() {
160
+ if (!stdin.isTTY) {
161
+ return DEFAULT_STYLING;
162
+ }
163
+
164
+ const p = await loadClack();
165
+ const value = await p.select({
166
+ message: "Select a styling mode",
167
+ initialValue: DEFAULT_STYLING,
168
+ options: STYLING_CHOICES.map((choice) => ({
169
+ value: choice.value,
170
+ label: choice.label,
171
+ })),
172
+ });
173
+
174
+ if (p.isCancel(value)) {
175
+ p.cancel("Operation cancelled.");
176
+ return "";
177
+ }
178
+
179
+ return value;
180
+ }
181
+
182
+ function collectFiles(baseDir, currentDir = baseDir, acc = []) {
183
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
184
+ for (const entry of entries) {
185
+ const absolute = path.join(currentDir, entry.name);
186
+ if (entry.isDirectory()) {
187
+ collectFiles(baseDir, absolute, acc);
188
+ continue;
189
+ }
190
+
191
+ const relative = path.relative(baseDir, absolute);
192
+ acc.push(relative);
193
+ }
194
+ return acc;
195
+ }
196
+
197
+ function ensureDirectory(filePath) {
198
+ const dir = path.dirname(filePath);
199
+ fs.mkdirSync(dir, { recursive: true });
200
+ }
201
+
202
+ /**
203
+ * Detect whether a Next.js project uses the src/ directory layout.
204
+ * Returns true if src/app exists, false if app/ exists at root,
205
+ * or falls back to checking for a src/ directory.
206
+ */
207
+ function detectNextSrcLayout(targetRoot) {
208
+ if (fs.existsSync(path.join(targetRoot, "src", "app"))) {
209
+ return true;
210
+ }
211
+ if (fs.existsSync(path.join(targetRoot, "app"))) {
212
+ return false;
213
+ }
214
+ if (fs.existsSync(path.join(targetRoot, "src"))) {
215
+ return true;
216
+ }
217
+ return false;
218
+ }
219
+
220
+ async function confirmOverwrite(existingFiles) {
221
+ if (!stdin.isTTY) {
222
+ return false;
223
+ }
224
+
225
+ const p = await loadClack();
226
+ p.log.warn("The following files already exist:");
227
+ for (const file of existingFiles) {
228
+ p.log.message(`- ${file}`);
229
+ }
230
+
231
+ const value = await p.confirm({
232
+ message: "Overwrite these files?",
233
+ initialValue: false,
234
+ });
235
+
236
+ if (p.isCancel(value)) {
237
+ p.cancel("Operation cancelled.");
238
+ return false;
239
+ }
240
+
241
+ return Boolean(value);
242
+ }
243
+
244
+ function detectPackageManager(targetRoot) {
245
+ if (fs.existsSync(path.join(targetRoot, "bun.lockb")) || fs.existsSync(path.join(targetRoot, "bun.lock"))) return "bun";
246
+ if (fs.existsSync(path.join(targetRoot, "pnpm-lock.yaml"))) return "pnpm";
247
+ if (fs.existsSync(path.join(targetRoot, "yarn.lock"))) return "yarn";
248
+ return "npm";
249
+ }
250
+
251
+ async function installVicketPackage(targetRoot) {
252
+ const { execSync } = require("node:child_process");
253
+ const pm = detectPackageManager(targetRoot);
254
+
255
+ const installCmd = {
256
+ npm: "npm install vicket",
257
+ pnpm: "pnpm add vicket",
258
+ yarn: "yarn add vicket",
259
+ bun: "bun add vicket",
260
+ }[pm];
261
+
262
+ if (stdin.isTTY) {
263
+ const p = await loadClack();
264
+ const s = p.spinner();
265
+ s.start(`Installing vicket package with ${pm}...`);
266
+ try {
267
+ execSync(installCmd, { cwd: targetRoot, stdio: "pipe" });
268
+ s.stop(`Installed vicket with ${pm}.`);
269
+ } catch {
270
+ s.stop(`Could not auto-install. Run "${installCmd}" manually.`);
271
+ }
272
+ } else {
273
+ console.log(`Installing vicket with ${pm}...`);
274
+ try {
275
+ execSync(installCmd, { cwd: targetRoot, stdio: "pipe" });
276
+ console.log("Installed vicket.");
277
+ } catch {
278
+ console.log(`Could not auto-install. Run "${installCmd}" manually.`);
279
+ }
280
+ }
281
+ }
282
+
283
+ async function printPostInstall(framework, styling) {
284
+ const envLinesByFramework = {
285
+ next: [
286
+ "- VICKET_API_URL (https://api.vicket.app/api/v1) — server-side only",
287
+ "- VICKET_API_KEY (website API key) — server-side only, NEVER expose to client",
288
+ ],
289
+ nuxt: [
290
+ "- VICKET_API_URL (https://api.vicket.app/api/v1) — server-side only",
291
+ "- VICKET_API_KEY (website API key) — server-side only, NEVER expose to client",
292
+ ],
293
+ sveltekit: [
294
+ "- VICKET_API_URL (https://api.vicket.app/api/v1) — server-side only",
295
+ "- VICKET_API_KEY (website API key) — server-side only, NEVER expose to client",
296
+ ],
297
+ };
298
+ const envLines = envLinesByFramework[framework] || envLinesByFramework.next;
299
+
300
+ if (stdin.isTTY) {
301
+ const p = await loadClack();
302
+ p.outro("Scaffold complete.");
303
+ p.log.info("Set these environment variables in the target app:");
304
+ for (const line of envLines) {
305
+ p.log.message(line);
306
+ }
307
+
308
+ if (framework === "nuxt") {
309
+ p.log.info(
310
+ "Nuxt note: if you prefer global styles, add ~/assets/css/vicket.css to nuxt.config.ts > css. Files are in the app/ directory (Nuxt 4 convention).",
311
+ );
312
+ }
313
+ if (styling === "tailwind") {
314
+ p.log.info(
315
+ "Tailwind mode: ensure Tailwind CSS is configured in the target app.",
316
+ );
317
+ }
318
+ return;
319
+ }
320
+
321
+ console.log("\nScaffold complete.");
322
+ console.log("Set these environment variables in the target app:");
323
+ for (const line of envLines) {
324
+ console.log(line);
325
+ }
326
+ if (styling === "tailwind") {
327
+ console.log(
328
+ "Tailwind mode: ensure Tailwind CSS is configured in the target app.",
329
+ );
330
+ }
331
+ }
332
+
333
+ async function main() {
334
+ const options = parseArgs(process.argv.slice(2));
335
+ if (options.version) {
336
+ console.log(`@vicket/create-support v${PKG_VERSION}`);
337
+ process.exit(0);
338
+ }
339
+ if (options.help) {
340
+ printHelp();
341
+ process.exit(0);
342
+ }
343
+
344
+ let framework = normalizeFramework(options.framework);
345
+ const requestedStyling = (options.styling || "").trim();
346
+ if (requestedStyling && !normalizeStyling(requestedStyling)) {
347
+ console.error("Unsupported styling. Use --styling css|tailwind");
348
+ printHelp();
349
+ process.exit(1);
350
+ }
351
+
352
+ let styling = normalizeStyling(options.styling) || DEFAULT_STYLING;
353
+
354
+ if (!framework) {
355
+ framework = await askFrameworkSelection();
356
+ }
357
+
358
+ if (!framework) {
359
+ console.error(
360
+ "Missing or unsupported framework. Use --framework next|nuxt|sveltekit",
361
+ );
362
+ printHelp();
363
+ process.exit(1);
364
+ }
365
+
366
+ if (
367
+ !normalizeStyling(options.styling) &&
368
+ stdin.isTTY &&
369
+ !process.argv.includes("--tailwind")
370
+ ) {
371
+ const selectedStyling = await askStylingSelection();
372
+ if (!selectedStyling) {
373
+ console.log("Aborted. No files were written.");
374
+ process.exit(0);
375
+ }
376
+ styling = selectedStyling;
377
+ }
378
+
379
+ const packageRoot = path.resolve(__dirname, "..");
380
+ const templateDir =
381
+ styling === "tailwind" ? "templates-tailwind" : "templates";
382
+ const templateRoot = path.join(packageRoot, templateDir, framework);
383
+
384
+ if (!fs.existsSync(templateRoot)) {
385
+ console.error(`Template not found: ${templateDir}/${framework}`);
386
+ process.exit(1);
387
+ }
388
+
389
+ const targetRoot = path.resolve(options.dir);
390
+ const files = collectFiles(templateRoot);
391
+
392
+ const stripSrc =
393
+ framework === "next" && !detectNextSrcLayout(targetRoot);
394
+ const srcPrefix = "src" + path.sep;
395
+
396
+ function toDestRelative(relativePath) {
397
+ if (stripSrc && relativePath.startsWith(srcPrefix)) {
398
+ return relativePath.slice(srcPrefix.length);
399
+ }
400
+ return relativePath;
401
+ }
402
+
403
+ const existing = files
404
+ .map((f) => toDestRelative(f))
405
+ .filter((destPath) => fs.existsSync(path.join(targetRoot, destPath)));
406
+
407
+ if (existing.length > 0 && !options.yes) {
408
+ const shouldOverwrite = await confirmOverwrite(existing);
409
+ if (!shouldOverwrite) {
410
+ console.log("Aborted. No files were written.");
411
+ process.exit(0);
412
+ }
413
+ }
414
+
415
+ for (const relativePath of files) {
416
+ const from = path.join(templateRoot, relativePath);
417
+ const to = path.join(targetRoot, toDestRelative(relativePath));
418
+ ensureDirectory(to);
419
+ fs.copyFileSync(from, to);
420
+ }
421
+
422
+ await installVicketPackage(targetRoot);
423
+ await printPostInstall(framework, styling);
424
+ }
425
+
426
+ main().catch((error) => {
427
+ console.error(error);
428
+ process.exit(1);
429
+ });