@drmhse/authos-cli 0.1.0 → 0.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.
package/dist/index.js CHANGED
@@ -70,6 +70,18 @@ function getAdapterPackage(framework) {
70
70
  return null;
71
71
  }
72
72
  }
73
+ function readPackageJson(cwd) {
74
+ const packageJsonPath = path2__namespace.join(cwd, "package.json");
75
+ if (!fs__namespace.existsSync(packageJsonPath)) {
76
+ return null;
77
+ }
78
+ try {
79
+ const content = fs__namespace.readFileSync(packageJsonPath, "utf8");
80
+ return JSON.parse(content);
81
+ } catch {
82
+ return null;
83
+ }
84
+ }
73
85
  function writeFile(filePath, content) {
74
86
  const dir = path2__namespace.dirname(filePath);
75
87
  if (!fs__namespace.existsSync(dir)) {
@@ -137,6 +149,31 @@ function getFrameworkName(framework) {
137
149
  return "Unknown";
138
150
  }
139
151
  }
152
+ function detectTailwind(cwd) {
153
+ const packageJson = readPackageJson(cwd);
154
+ if (!packageJson) return false;
155
+ const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
156
+ return !!(deps["tailwindcss"] || deps["@tailwindcss/vite"] || deps["@tailwindcss/typography"]);
157
+ }
158
+ function hasTailwindConfig(cwd) {
159
+ const configFiles = [
160
+ "tailwind.config.js",
161
+ "tailwind.config.ts",
162
+ "tailwind.config.cjs",
163
+ "tailwind.config.mjs",
164
+ "tailwind.config.cts"
165
+ ];
166
+ return configFiles.some((file) => fs__namespace.existsSync(path2__namespace.join(cwd, file)));
167
+ }
168
+ function isTailwindConfigured(cwd) {
169
+ return detectTailwind(cwd) || hasTailwindConfig(cwd);
170
+ }
171
+ function detectStyling(cwd) {
172
+ if (isTailwindConfigured(cwd)) {
173
+ return "tailwind";
174
+ }
175
+ return "unknown";
176
+ }
140
177
 
141
178
  // src/commands/init.ts
142
179
  async function initCommand(options = {}) {
@@ -995,6 +1032,510 @@ async function handleSignOut() {
995
1032
  </div>
996
1033
  </template>
997
1034
  `;
1035
+ var loginFormReactCssModules = `"use client";
1036
+
1037
+ import { useState } from "react";
1038
+ import { useAuthOS } from "@drmhse/authos-react";
1039
+ import { AuthErrorCodes } from "@drmhse/sso-sdk";
1040
+ import styles from "./LoginForm.module.css";
1041
+
1042
+ interface LoginFormProps {
1043
+ onSuccess?: () => void;
1044
+ redirectTo?: string;
1045
+ }
1046
+
1047
+ export function LoginForm({ onSuccess, redirectTo }: LoginFormProps) {
1048
+ const { client } = useAuthOS();
1049
+ const [email, setEmail] = useState("");
1050
+ const [password, setPassword] = useState("");
1051
+ const [error, setError] = useState<string | null>(null);
1052
+ const [loading, setLoading] = useState(false);
1053
+ const [mfaRequired, setMfaRequired] = useState(false);
1054
+ const [mfaCode, setMfaCode] = useState("");
1055
+ const [mfaToken, setMfaToken] = useState<string | null>(null);
1056
+
1057
+ const handleSubmit = async (e: React.FormEvent) => {
1058
+ e.preventDefault();
1059
+ setError(null);
1060
+ setLoading(true);
1061
+
1062
+ try {
1063
+ const response = await client.auth.login({ email, password });
1064
+
1065
+ if (response.mfa_required) {
1066
+ setMfaRequired(true);
1067
+ setMfaToken(response.mfa_token || null);
1068
+ } else {
1069
+ onSuccess?.();
1070
+ if (redirectTo) {
1071
+ window.location.href = redirectTo;
1072
+ }
1073
+ }
1074
+ } catch (err: unknown) {
1075
+ if (err && typeof err === "object" && "errorCode" in err) {
1076
+ const apiError = err as { errorCode: string; message: string };
1077
+ if (apiError.errorCode === AuthErrorCodes.MFA_REQUIRED) {
1078
+ setMfaRequired(true);
1079
+ } else {
1080
+ setError(apiError.message || "Login failed");
1081
+ }
1082
+ } else {
1083
+ setError("An unexpected error occurred");
1084
+ }
1085
+ } finally {
1086
+ setLoading(false);
1087
+ }
1088
+ };
1089
+
1090
+ const handleMfaSubmit = async (e: React.FormEvent) => {
1091
+ e.preventDefault();
1092
+ setError(null);
1093
+ setLoading(true);
1094
+
1095
+ try {
1096
+ await client.auth.verifyMfa({ code: mfaCode, mfa_token: mfaToken! });
1097
+ onSuccess?.();
1098
+ if (redirectTo) {
1099
+ window.location.href = redirectTo;
1100
+ }
1101
+ } catch (err: unknown) {
1102
+ if (err && typeof err === "object" && "message" in err) {
1103
+ setError((err as { message: string }).message || "Invalid MFA code");
1104
+ } else {
1105
+ setError("Invalid MFA code");
1106
+ }
1107
+ } finally {
1108
+ setLoading(false);
1109
+ }
1110
+ };
1111
+
1112
+ if (mfaRequired) {
1113
+ return (
1114
+ <form onSubmit={handleMfaSubmit} className={styles.form}>
1115
+ <div className={styles.header}>
1116
+ <h2 className={styles.title}>Two-Factor Authentication</h2>
1117
+ <p className={styles.subtitle}>Enter the code from your authenticator app</p>
1118
+ </div>
1119
+
1120
+ {error && <div className={styles.error}>{error}</div>}
1121
+
1122
+ <div className={styles.field}>
1123
+ <label htmlFor="mfa-code" className={styles.label}>Verification Code</label>
1124
+ <input
1125
+ id="mfa-code"
1126
+ type="text"
1127
+ inputMode="numeric"
1128
+ autoComplete="one-time-code"
1129
+ value={mfaCode}
1130
+ onChange={(e) => setMfaCode(e.target.value)}
1131
+ className={styles.input}
1132
+ placeholder="000000"
1133
+ maxLength={6}
1134
+ required
1135
+ />
1136
+ </div>
1137
+
1138
+ <button type="submit" disabled={loading} className={styles.button}>
1139
+ {loading ? "Verifying..." : "Verify"}
1140
+ </button>
1141
+
1142
+ <button type="button" onClick={() => setMfaRequired(false)} className={styles.link}>
1143
+ Back to login
1144
+ </button>
1145
+ </form>
1146
+ );
1147
+ }
1148
+
1149
+ return (
1150
+ <form onSubmit={handleSubmit} className={styles.form}>
1151
+ <div className={styles.header}>
1152
+ <h2 className={styles.title}>Sign In</h2>
1153
+ <p className={styles.subtitle}>Enter your credentials to continue</p>
1154
+ </div>
1155
+
1156
+ {error && <div className={styles.error}>{error}</div>}
1157
+
1158
+ <div className={styles.field}>
1159
+ <label htmlFor="email" className={styles.label}>Email</label>
1160
+ <input
1161
+ id="email"
1162
+ type="email"
1163
+ autoComplete="email"
1164
+ value={email}
1165
+ onChange={(e) => setEmail(e.target.value)}
1166
+ className={styles.input}
1167
+ placeholder="you@example.com"
1168
+ required
1169
+ />
1170
+ </div>
1171
+
1172
+ <div className={styles.field}>
1173
+ <label htmlFor="password" className={styles.label}>Password</label>
1174
+ <input
1175
+ id="password"
1176
+ type="password"
1177
+ autoComplete="current-password"
1178
+ value={password}
1179
+ onChange={(e) => setPassword(e.target.value)}
1180
+ className={styles.input}
1181
+ placeholder="Your password"
1182
+ required
1183
+ />
1184
+ </div>
1185
+
1186
+ <button type="submit" disabled={loading} className={styles.button}>
1187
+ {loading ? "Signing in..." : "Sign In"}
1188
+ </button>
1189
+ </form>
1190
+ );
1191
+ }
1192
+ `;
1193
+ var loginFormCssModulesStyles = `.form {
1194
+ display: flex;
1195
+ flex-direction: column;
1196
+ gap: 1rem;
1197
+ width: 100%;
1198
+ max-width: 24rem;
1199
+ }
1200
+
1201
+ .header {
1202
+ text-align: center;
1203
+ margin-bottom: 1.5rem;
1204
+ }
1205
+
1206
+ .title {
1207
+ font-size: 1.25rem;
1208
+ font-weight: 600;
1209
+ margin: 0;
1210
+ }
1211
+
1212
+ .subtitle {
1213
+ color: #6b7280;
1214
+ font-size: 0.875rem;
1215
+ margin-top: 0.25rem;
1216
+ }
1217
+
1218
+ .field {
1219
+ display: flex;
1220
+ flex-direction: column;
1221
+ }
1222
+
1223
+ .label {
1224
+ font-size: 0.875rem;
1225
+ font-weight: 500;
1226
+ margin-bottom: 0.25rem;
1227
+ }
1228
+
1229
+ .input {
1230
+ width: 100%;
1231
+ padding: 0.5rem 0.75rem;
1232
+ border: 1px solid #e2e8f0;
1233
+ border-radius: 0.375rem;
1234
+ font-size: 1rem;
1235
+ }
1236
+
1237
+ .input:focus {
1238
+ outline: none;
1239
+ border-color: #3b82f6;
1240
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
1241
+ }
1242
+
1243
+ .button {
1244
+ width: 100%;
1245
+ padding: 0.5rem 1rem;
1246
+ background-color: #2563eb;
1247
+ color: white;
1248
+ border: none;
1249
+ border-radius: 0.375rem;
1250
+ font-weight: 500;
1251
+ cursor: pointer;
1252
+ font-size: 1rem;
1253
+ }
1254
+
1255
+ .button:hover {
1256
+ background-color: #1d4ed8;
1257
+ }
1258
+
1259
+ .button:disabled {
1260
+ opacity: 0.5;
1261
+ cursor: not-allowed;
1262
+ }
1263
+
1264
+ .error {
1265
+ background-color: #fef2f2;
1266
+ color: #dc2626;
1267
+ padding: 0.5rem 1rem;
1268
+ border-radius: 0.375rem;
1269
+ font-size: 0.875rem;
1270
+ }
1271
+
1272
+ .link {
1273
+ background: none;
1274
+ border: none;
1275
+ color: #6b7280;
1276
+ font-size: 0.875rem;
1277
+ cursor: pointer;
1278
+ text-align: center;
1279
+ }
1280
+
1281
+ .link:hover {
1282
+ text-decoration: underline;
1283
+ }
1284
+ `;
1285
+ var loginFormVueScoped = `<script setup lang="ts">
1286
+ import { ref } from "vue";
1287
+ import { useAuthOS } from "@drmhse/authos-vue";
1288
+ import { AuthErrorCodes } from "@drmhse/sso-sdk";
1289
+
1290
+ interface Props {
1291
+ redirectTo?: string;
1292
+ }
1293
+
1294
+ const props = defineProps<Props>();
1295
+ const emit = defineEmits<{
1296
+ success: [];
1297
+ }>();
1298
+
1299
+ const { client } = useAuthOS();
1300
+
1301
+ const email = ref("");
1302
+ const password = ref("");
1303
+ const error = ref<string | null>(null);
1304
+ const loading = ref(false);
1305
+ const mfaRequired = ref(false);
1306
+ const mfaCode = ref("");
1307
+ const mfaToken = ref<string | null>(null);
1308
+
1309
+ async function handleSubmit() {
1310
+ error.value = null;
1311
+ loading.value = true;
1312
+
1313
+ try {
1314
+ const response = await client.auth.login({
1315
+ email: email.value,
1316
+ password: password.value,
1317
+ });
1318
+
1319
+ if (response.mfa_required) {
1320
+ mfaRequired.value = true;
1321
+ mfaToken.value = response.mfa_token || null;
1322
+ } else {
1323
+ emit("success");
1324
+ if (props.redirectTo) {
1325
+ window.location.href = props.redirectTo;
1326
+ }
1327
+ }
1328
+ } catch (err: unknown) {
1329
+ if (err && typeof err === "object" && "errorCode" in err) {
1330
+ const apiError = err as { errorCode: string; message: string };
1331
+ if (apiError.errorCode === AuthErrorCodes.MFA_REQUIRED) {
1332
+ mfaRequired.value = true;
1333
+ } else {
1334
+ error.value = apiError.message || "Login failed";
1335
+ }
1336
+ } else {
1337
+ error.value = "An unexpected error occurred";
1338
+ }
1339
+ } finally {
1340
+ loading.value = false;
1341
+ }
1342
+ }
1343
+
1344
+ async function handleMfaSubmit() {
1345
+ error.value = null;
1346
+ loading.value = true;
1347
+
1348
+ try {
1349
+ await client.auth.verifyMfa({
1350
+ code: mfaCode.value,
1351
+ mfa_token: mfaToken.value!,
1352
+ });
1353
+ emit("success");
1354
+ if (props.redirectTo) {
1355
+ window.location.href = props.redirectTo;
1356
+ }
1357
+ } catch (err: unknown) {
1358
+ if (err && typeof err === "object" && "message" in err) {
1359
+ error.value = (err as { message: string }).message || "Invalid MFA code";
1360
+ } else {
1361
+ error.value = "Invalid MFA code";
1362
+ }
1363
+ } finally {
1364
+ loading.value = false;
1365
+ }
1366
+ }
1367
+
1368
+ function backToLogin() {
1369
+ mfaRequired.value = false;
1370
+ mfaCode.value = "";
1371
+ error.value = null;
1372
+ }
1373
+ </script>
1374
+
1375
+ <template>
1376
+ <form v-if="mfaRequired" @submit.prevent="handleMfaSubmit" class="form">
1377
+ <div class="header">
1378
+ <h2 class="title">Two-Factor Authentication</h2>
1379
+ <p class="subtitle">Enter the code from your authenticator app</p>
1380
+ </div>
1381
+
1382
+ <div v-if="error" class="error">{{ error }}</div>
1383
+
1384
+ <div class="field">
1385
+ <label for="mfa-code" class="label">Verification Code</label>
1386
+ <input
1387
+ id="mfa-code"
1388
+ v-model="mfaCode"
1389
+ type="text"
1390
+ inputmode="numeric"
1391
+ autocomplete="one-time-code"
1392
+ class="input"
1393
+ placeholder="000000"
1394
+ maxlength="6"
1395
+ required
1396
+ />
1397
+ </div>
1398
+
1399
+ <button type="submit" :disabled="loading" class="button">
1400
+ {{ loading ? "Verifying..." : "Verify" }}
1401
+ </button>
1402
+
1403
+ <button type="button" @click="backToLogin" class="link">Back to login</button>
1404
+ </form>
1405
+
1406
+ <form v-else @submit.prevent="handleSubmit" class="form">
1407
+ <div class="header">
1408
+ <h2 class="title">Sign In</h2>
1409
+ <p class="subtitle">Enter your credentials to continue</p>
1410
+ </div>
1411
+
1412
+ <div v-if="error" class="error">{{ error }}</div>
1413
+
1414
+ <div class="field">
1415
+ <label for="email" class="label">Email</label>
1416
+ <input
1417
+ id="email"
1418
+ v-model="email"
1419
+ type="email"
1420
+ autocomplete="email"
1421
+ class="input"
1422
+ placeholder="you@example.com"
1423
+ required
1424
+ />
1425
+ </div>
1426
+
1427
+ <div class="field">
1428
+ <label for="password" class="label">Password</label>
1429
+ <input
1430
+ id="password"
1431
+ v-model="password"
1432
+ type="password"
1433
+ autocomplete="current-password"
1434
+ class="input"
1435
+ placeholder="Your password"
1436
+ required
1437
+ />
1438
+ </div>
1439
+
1440
+ <button type="submit" :disabled="loading" class="button">
1441
+ {{ loading ? "Signing in..." : "Sign In" }}
1442
+ </button>
1443
+ </form>
1444
+ </template>
1445
+
1446
+ <style scoped>
1447
+ .form {
1448
+ display: flex;
1449
+ flex-direction: column;
1450
+ gap: 1rem;
1451
+ width: 100%;
1452
+ max-width: 24rem;
1453
+ }
1454
+
1455
+ .header {
1456
+ text-align: center;
1457
+ margin-bottom: 1.5rem;
1458
+ }
1459
+
1460
+ .title {
1461
+ font-size: 1.25rem;
1462
+ font-weight: 600;
1463
+ margin: 0;
1464
+ }
1465
+
1466
+ .subtitle {
1467
+ color: #6b7280;
1468
+ font-size: 0.875rem;
1469
+ margin-top: 0.25rem;
1470
+ }
1471
+
1472
+ .field {
1473
+ display: flex;
1474
+ flex-direction: column;
1475
+ }
1476
+
1477
+ .label {
1478
+ font-size: 0.875rem;
1479
+ font-weight: 500;
1480
+ margin-bottom: 0.25rem;
1481
+ }
1482
+
1483
+ .input {
1484
+ width: 100%;
1485
+ padding: 0.5rem 0.75rem;
1486
+ border: 1px solid #e2e8f0;
1487
+ border-radius: 0.375rem;
1488
+ font-size: 1rem;
1489
+ }
1490
+
1491
+ .input:focus {
1492
+ outline: none;
1493
+ border-color: #3b82f6;
1494
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
1495
+ }
1496
+
1497
+ .button {
1498
+ width: 100%;
1499
+ padding: 0.5rem 1rem;
1500
+ background-color: #2563eb;
1501
+ color: white;
1502
+ border: none;
1503
+ border-radius: 0.375rem;
1504
+ font-weight: 500;
1505
+ cursor: pointer;
1506
+ }
1507
+
1508
+ .button:hover {
1509
+ background-color: #1d4ed8;
1510
+ }
1511
+
1512
+ .button:disabled {
1513
+ opacity: 0.5;
1514
+ cursor: not-allowed;
1515
+ }
1516
+
1517
+ .error {
1518
+ background-color: #fef2f2;
1519
+ color: #dc2626;
1520
+ padding: 0.5rem 1rem;
1521
+ border-radius: 0.375rem;
1522
+ font-size: 0.875rem;
1523
+ }
1524
+
1525
+ .link {
1526
+ background: none;
1527
+ border: none;
1528
+ color: #6b7280;
1529
+ font-size: 0.875rem;
1530
+ cursor: pointer;
1531
+ text-align: center;
1532
+ }
1533
+
1534
+ .link:hover {
1535
+ text-decoration: underline;
1536
+ }
1537
+ </style>
1538
+ `;
998
1539
  var templates = {
999
1540
  "login-form": {
1000
1541
  name: "Login Form",
@@ -1003,11 +1544,33 @@ var templates = {
1003
1544
  {
1004
1545
  name: "LoginForm",
1005
1546
  content: {
1006
- react: loginFormReact,
1007
- next: loginFormReact,
1008
- vue: loginFormVue,
1009
- nuxt: loginFormVue,
1010
- unknown: loginFormReact
1547
+ tailwind: {
1548
+ react: loginFormReact,
1549
+ next: loginFormReact,
1550
+ vue: loginFormVue,
1551
+ nuxt: loginFormVue,
1552
+ unknown: loginFormReact
1553
+ },
1554
+ "css-modules": {
1555
+ react: loginFormReactCssModules,
1556
+ next: loginFormReactCssModules,
1557
+ vue: loginFormVueScoped,
1558
+ nuxt: loginFormVueScoped,
1559
+ unknown: loginFormReactCssModules
1560
+ },
1561
+ none: {
1562
+ react: loginFormReact,
1563
+ // Fallback to tailwind for now
1564
+ next: loginFormReact,
1565
+ vue: loginFormVue,
1566
+ nuxt: loginFormVue,
1567
+ unknown: loginFormReact
1568
+ }
1569
+ },
1570
+ extraFiles: {
1571
+ "css-modules": [
1572
+ { name: "LoginForm.module.css", content: loginFormCssModulesStyles }
1573
+ ]
1011
1574
  }
1012
1575
  }
1013
1576
  ]
@@ -1019,11 +1582,28 @@ var templates = {
1019
1582
  {
1020
1583
  name: "OrganizationSwitcher",
1021
1584
  content: {
1022
- react: orgSwitcherReact,
1023
- next: orgSwitcherReact,
1024
- vue: orgSwitcherVue,
1025
- nuxt: orgSwitcherVue,
1026
- unknown: orgSwitcherReact
1585
+ tailwind: {
1586
+ react: orgSwitcherReact,
1587
+ next: orgSwitcherReact,
1588
+ vue: orgSwitcherVue,
1589
+ nuxt: orgSwitcherVue,
1590
+ unknown: orgSwitcherReact
1591
+ },
1592
+ "css-modules": {
1593
+ react: orgSwitcherReact,
1594
+ // Will add CSS modules variant later
1595
+ next: orgSwitcherReact,
1596
+ vue: orgSwitcherVue,
1597
+ nuxt: orgSwitcherVue,
1598
+ unknown: orgSwitcherReact
1599
+ },
1600
+ none: {
1601
+ react: orgSwitcherReact,
1602
+ next: orgSwitcherReact,
1603
+ vue: orgSwitcherVue,
1604
+ nuxt: orgSwitcherVue,
1605
+ unknown: orgSwitcherReact
1606
+ }
1027
1607
  }
1028
1608
  }
1029
1609
  ]
@@ -1035,11 +1615,28 @@ var templates = {
1035
1615
  {
1036
1616
  name: "UserProfile",
1037
1617
  content: {
1038
- react: userProfileReact,
1039
- next: userProfileReact,
1040
- vue: userProfileVue,
1041
- nuxt: userProfileVue,
1042
- unknown: userProfileReact
1618
+ tailwind: {
1619
+ react: userProfileReact,
1620
+ next: userProfileReact,
1621
+ vue: userProfileVue,
1622
+ nuxt: userProfileVue,
1623
+ unknown: userProfileReact
1624
+ },
1625
+ "css-modules": {
1626
+ react: userProfileReact,
1627
+ // Will add CSS modules variant later
1628
+ next: userProfileReact,
1629
+ vue: userProfileVue,
1630
+ nuxt: userProfileVue,
1631
+ unknown: userProfileReact
1632
+ },
1633
+ none: {
1634
+ react: userProfileReact,
1635
+ next: userProfileReact,
1636
+ vue: userProfileVue,
1637
+ nuxt: userProfileVue,
1638
+ unknown: userProfileReact
1639
+ }
1043
1640
  }
1044
1641
  }
1045
1642
  ]
@@ -1077,6 +1674,26 @@ async function addCommand(templateName, options = {}) {
1077
1674
  } else {
1078
1675
  log.info(`Detected ${getFrameworkName(framework)} project`);
1079
1676
  }
1677
+ let styling = detectStyling(cwd);
1678
+ if (styling === "unknown") {
1679
+ const response = await prompts2__default.default({
1680
+ type: "select",
1681
+ name: "styling",
1682
+ message: "How do you want to style this component?",
1683
+ choices: [
1684
+ { title: "Tailwind CSS (utility classes)", value: "tailwind" },
1685
+ { title: "CSS Modules (.module.css)", value: "css-modules" },
1686
+ { title: "Unstyled (headless)", value: "none" }
1687
+ ]
1688
+ });
1689
+ if (!response.styling) {
1690
+ log.error("Command cancelled.");
1691
+ process.exit(1);
1692
+ }
1693
+ styling = response.styling;
1694
+ } else {
1695
+ log.info(`Using Tailwind CSS styling (detected)`);
1696
+ }
1080
1697
  if (!templateName) {
1081
1698
  const availableTemplates = getAvailableTemplates();
1082
1699
  const choices = availableTemplates.map((name) => {
@@ -1127,9 +1744,29 @@ async function addCommand(templateName, options = {}) {
1127
1744
  continue;
1128
1745
  }
1129
1746
  }
1130
- const content = file.content[framework];
1747
+ const content = file.content[styling]?.[framework] ?? file.content["none"]?.[framework] ?? "";
1131
1748
  writeFile(filePath, content);
1132
1749
  log.success(`Created ${path2__namespace.relative(cwd, filePath)}`);
1750
+ const extraFiles = file.extraFiles?.[styling];
1751
+ if (extraFiles) {
1752
+ for (const extra of extraFiles) {
1753
+ const extraPath = path2__namespace.join(componentsDir, extra.name);
1754
+ if (fileExists(extraPath) && !options.force) {
1755
+ const response = await prompts2__default.default({
1756
+ type: "confirm",
1757
+ name: "overwrite",
1758
+ message: `${extra.name} already exists. Overwrite?`,
1759
+ initial: false
1760
+ });
1761
+ if (!response.overwrite) {
1762
+ log.warn(`Skipped ${extra.name}`);
1763
+ continue;
1764
+ }
1765
+ }
1766
+ writeFile(extraPath, extra.content);
1767
+ log.success(`Created ${path2__namespace.relative(cwd, extraPath)}`);
1768
+ }
1769
+ }
1133
1770
  }
1134
1771
  console.log("\n");
1135
1772
  log.success(`Added ${template.name} component!`);