create-tina-app 2.1.5 → 2.1.7

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
@@ -9,6 +9,19 @@ import path from "path";
9
9
 
10
10
  // src/util/textstyles.ts
11
11
  import chalk from "chalk";
12
+ var stripAnsi = (str) => str.replace(/\]8;;[^]*/g, "").replace(/\[[0-9;]*m/g, "");
13
+ var box = (lines, color = chalk.hex("#EC4816")) => {
14
+ const pad = 1;
15
+ const width = Math.max(...lines.map((l) => stripAnsi(l).length));
16
+ const rule = color("\u2500".repeat(width + pad * 2));
17
+ const top = `${color("\u250C")}${rule}${color("\u2510")}`;
18
+ const bottom = `${color("\u2514")}${rule}${color("\u2518")}`;
19
+ const body = lines.map((line) => {
20
+ const trailing = " ".repeat(width - stripAnsi(line).length);
21
+ return `${color("\u2502")}${" ".repeat(pad)}${line}${trailing}${" ".repeat(pad)}${color("\u2502")}`;
22
+ });
23
+ return [top, ...body, bottom].join("\n");
24
+ };
12
25
  var TextStyles = {
13
26
  tinaOrange: chalk.hex("#EC4816"),
14
27
  link: (url) => `\x1B]8;;${url}\x07${chalk.cyan.underline(url)}\x1B]8;;\x07`,
@@ -143,18 +156,36 @@ async function updateTelemetryConfig(dir, mode) {
143
156
 
144
157
  // src/util/install.ts
145
158
  import spawn from "cross-spawn";
159
+ var MAX_OUTPUT_CHARS = 4e3;
146
160
  function install(packageManager, verboseOutput) {
161
+ const command = `${packageManager} install`;
147
162
  return new Promise((resolve, reject) => {
148
163
  const child = spawn(packageManager, ["install"], {
149
- stdio: verboseOutput ? "inherit" : "ignore",
164
+ stdio: verboseOutput ? "inherit" : ["ignore", "ignore", "pipe"],
150
165
  env: { ...process.env, ADBLOCK: "1", DISABLE_OPENCOLLECTIVE: "1" }
151
166
  });
167
+ let captured = "";
168
+ child.stderr?.on("data", (chunk) => {
169
+ captured = (captured + chunk.toString()).slice(-MAX_OUTPUT_CHARS);
170
+ });
171
+ child.on("error", (err) => {
172
+ reject(
173
+ new Error(`Failed to run "${command}": ${err.message}`)
174
+ );
175
+ });
152
176
  child.on("close", (code) => {
153
- if (code !== 0) {
154
- reject({ command: `${packageManager} install` });
177
+ if (code === 0) {
178
+ resolve();
155
179
  return;
156
180
  }
157
- resolve();
181
+ const details = captured.trim();
182
+ let message = `"${command}" exited with code ${code ?? "unknown"}.`;
183
+ if (details) {
184
+ message += `
185
+
186
+ ${details}`;
187
+ }
188
+ reject(new Error(message));
158
189
  });
159
190
  });
160
191
  }
@@ -422,18 +453,31 @@ async function downloadTemplate(template, root, spinner) {
422
453
  }
423
454
 
424
455
  // src/util/preRunChecks.ts
425
- var SUPPORTED_NODE_VERSION_BOUNDS = { oldest: 20, latest: 22 };
426
- var SUPPORTED_NODE_VERSION_RANGE = [
427
- ...Array(SUPPORTED_NODE_VERSION_BOUNDS.latest).keys()
428
- ].map((i) => i + SUPPORTED_NODE_VERSION_BOUNDS.oldest);
429
- var isSupported = SUPPORTED_NODE_VERSION_RANGE.some(
430
- (version2) => process.version.startsWith(`v${version2}`)
431
- );
456
+ import { readFileSync } from "node:fs";
457
+ import { dirname, join } from "node:path";
458
+ import { fileURLToPath } from "node:url";
459
+ function getSupportedMajors() {
460
+ const here = dirname(fileURLToPath(import.meta.url));
461
+ try {
462
+ const { engines } = JSON.parse(
463
+ readFileSync(join(here, "../package.json"), "utf8")
464
+ );
465
+ return [...String(engines?.node ?? "").matchAll(/(\d+)\.x/g)].map(
466
+ (m) => Number(m[1])
467
+ );
468
+ } catch {
469
+ return [];
470
+ }
471
+ }
432
472
  function preRunChecks(spinner) {
433
473
  spinner.start("Running pre-run checks...");
474
+ const supported = getSupportedMajors();
475
+ const currentMajor = Number(process.versions.node.split(".")[0]);
476
+ const isSupported = supported.includes(currentMajor);
434
477
  if (!isSupported) {
478
+ const range = supported.length ? supported.map((v) => `v${v}`).join(" or ") : "a supported LTS version";
435
479
  spinner.warn(
436
- `Node ${process.version} is not supported by create-tina-app. Please update to be within v${SUPPORTED_NODE_VERSION_BOUNDS.oldest}-v${SUPPORTED_NODE_VERSION_BOUNDS.latest}. See https://nodejs.org/en/download/ for more details.`
480
+ `Node ${process.version} is not supported by create-tina-app. Please use ${range}. See https://nodejs.org/en/download/ for more details.`
437
481
  );
438
482
  } else {
439
483
  spinner.succeed(`Node ${process.version} is supported.`);
@@ -466,7 +510,7 @@ import { Command } from "commander";
466
510
 
467
511
  // package.json
468
512
  var name = "create-tina-app";
469
- var version = "2.1.5";
513
+ var version = "2.1.7";
470
514
 
471
515
  // src/util/packageManagers.ts
472
516
  var PKG_MANAGERS = ["npm", "yarn", "pnpm", "bun"];
@@ -689,6 +733,7 @@ var ERROR_CODES = {
689
733
  ERR_VAL_INVALID_TEMPLATE: "ERR_VAL_INVALID_TEMPLATE",
690
734
  ERR_VAL_INVALID_PKG_MANAGER: "ERR_VAL_INVALID_PKG_MANAGER",
691
735
  ERR_VAL_INVALID_PROJECT_NAME: "ERR_VAL_INVALID_PROJECT_NAME",
736
+ ERR_VAL_INVALID_THEME: "ERR_VAL_INVALID_THEME",
692
737
  ERR_VAL_UNSUPPORTED_NODE: "ERR_VAL_UNSUPPORTED_NODE",
693
738
  ERR_VAL_NO_PKG_MANAGERS: "ERR_VAL_NO_PKG_MANAGERS",
694
739
  // File System Errors (FS_*)
@@ -879,6 +924,8 @@ async function fetchPostHogConfig(endpointUrl) {
879
924
 
880
925
  // src/index.ts
881
926
  import { osInfo as getOsSystemInfo } from "systeminformation";
927
+ var DISCORD_SUPPORT_URL = "https://discord.com/invite/zumN63Ybpf";
928
+ var FAQ_URL = "https://tina.io/docs/faq";
882
929
  var posthogClient = null;
883
930
  async function initializePostHog(configEndpoint, disableGeoip) {
884
931
  let apiKey;
@@ -963,6 +1010,18 @@ ${TextStylesBold.bold("Telemetry Notice")}`);
963
1010
  }
964
1011
  const spinner = ora();
965
1012
  preRunChecks(spinner);
1013
+ const fatalExit = async (message, error, context) => {
1014
+ spinner.fail(
1015
+ `${message}
1016
+
1017
+ Need more help? Reach out to the TinaCMS community at ${TextStyles.link(
1018
+ DISCORD_SUPPORT_URL
1019
+ )}`
1020
+ );
1021
+ postHogCaptureError(posthogClient, userId, sessionId, error, context);
1022
+ if (posthogClient) await posthogClient.shutdown();
1023
+ exit(1);
1024
+ };
966
1025
  postHogCapture(
967
1026
  posthogClient,
968
1027
  userId,
@@ -974,15 +1033,10 @@ ${TextStylesBold.bold("Telemetry Notice")}`);
974
1033
  if (opts.template) {
975
1034
  template = TEMPLATES.find((_template) => _template.value === opts.template);
976
1035
  if (!template) {
977
- spinner.fail(
1036
+ await fatalExit(
978
1037
  `The provided template '${opts.template}' is invalid. Please provide one of the following: ${TEMPLATES.map(
979
1038
  (x2) => x2.value
980
- )}`
981
- );
982
- postHogCaptureError(
983
- posthogClient,
984
- userId,
985
- sessionId,
1039
+ )}`,
986
1040
  new Error(`Invalid template: ${opts.template}`),
987
1041
  {
988
1042
  errorCode: ERROR_CODES.ERR_VAL_INVALID_TEMPLATE,
@@ -992,17 +1046,13 @@ ${TextStylesBold.bold("Telemetry Notice")}`);
992
1046
  additionalProperties: { ...telemetryData }
993
1047
  }
994
1048
  );
995
- if (posthogClient) await posthogClient.shutdown();
996
- exit(1);
997
1049
  }
998
1050
  }
999
1051
  let pkgManager = opts.pkgManager;
1000
1052
  if (pkgManager) {
1001
1053
  if (!PKG_MANAGERS.find((_pkgManager) => _pkgManager === pkgManager)) {
1002
- postHogCaptureError(
1003
- posthogClient,
1004
- userId,
1005
- sessionId,
1054
+ await fatalExit(
1055
+ `The provided package manager '${opts.pkgManager}' is not supported. Please provide one of the following: ${PKG_MANAGERS}`,
1006
1056
  new Error(`Invalid package manager: ${opts.pkgManager}`),
1007
1057
  {
1008
1058
  errorCode: ERROR_CODES.ERR_VAL_INVALID_PKG_MANAGER,
@@ -1012,22 +1062,12 @@ ${TextStylesBold.bold("Telemetry Notice")}`);
1012
1062
  additionalProperties: { ...telemetryData }
1013
1063
  }
1014
1064
  );
1015
- if (posthogClient) await posthogClient.shutdown();
1016
- spinner.fail(
1017
- `The provided package manager '${opts.pkgManager}' is not supported. Please provide one of the following: ${PKG_MANAGERS}`
1018
- );
1019
- exit(1);
1020
1065
  }
1021
1066
  }
1022
1067
  if (!pkgManager) {
1023
1068
  if (installedPkgManagers.length === 0) {
1024
- spinner.fail(
1025
- `You have no supported package managers installed. Please install one of the following: ${PKG_MANAGERS}`
1026
- );
1027
- postHogCaptureError(
1028
- posthogClient,
1029
- userId,
1030
- sessionId,
1069
+ await fatalExit(
1070
+ `You have no supported package managers installed. Please install one of the following: ${PKG_MANAGERS}`,
1031
1071
  new Error("No supported package managers installed"),
1032
1072
  {
1033
1073
  errorCode: ERROR_CODES.ERR_VAL_NO_PKG_MANAGERS,
@@ -1037,8 +1077,6 @@ ${TextStylesBold.bold("Telemetry Notice")}`);
1037
1077
  additionalProperties: telemetryData
1038
1078
  }
1039
1079
  );
1040
- if (posthogClient) await posthogClient.shutdown();
1041
- exit(1);
1042
1080
  }
1043
1081
  const res = await prompts({
1044
1082
  message: "Which package manager would you like to use?",
@@ -1134,10 +1172,20 @@ ${TextStylesBold.bold("Telemetry Notice")}`);
1134
1172
  if (opts.theme) {
1135
1173
  const validThemes = THEMES.map((t) => t.value);
1136
1174
  if (!validThemes.includes(opts.theme)) {
1137
- console.error(
1138
- `Invalid theme "${opts.theme}". Valid options are: ${validThemes.join(", ")}`
1175
+ await fatalExit(
1176
+ `Invalid theme "${opts.theme}". Valid options are: ${validThemes.join(", ")}`,
1177
+ new Error(`Invalid theme: ${opts.theme}`),
1178
+ {
1179
+ errorCode: ERROR_CODES.ERR_VAL_INVALID_THEME,
1180
+ errorCategory: "validation",
1181
+ step: TRACKING_STEPS.THEME_SELECT,
1182
+ fatal: true,
1183
+ additionalProperties: {
1184
+ ...telemetryData,
1185
+ template: template.value
1186
+ }
1187
+ }
1139
1188
  );
1140
- exit(1);
1141
1189
  }
1142
1190
  themeChoice = opts.theme;
1143
1191
  } else {
@@ -1172,13 +1220,8 @@ ${TextStylesBold.bold("Telemetry Notice")}`);
1172
1220
  }
1173
1221
  const rootDir = path3.join(process.cwd(), projectName);
1174
1222
  if (!await isWriteable(path3.dirname(rootDir))) {
1175
- spinner.fail(
1176
- "The application path is not writable, please check folder permissions and try again. It is likely you do not have write permissions for this folder."
1177
- );
1178
- postHogCaptureError(
1179
- posthogClient,
1180
- userId,
1181
- sessionId,
1223
+ await fatalExit(
1224
+ "The application path is not writable, please check folder permissions and try again. It is likely you do not have write permissions for this folder.",
1182
1225
  new Error("Directory not writable"),
1183
1226
  {
1184
1227
  errorCode: ERROR_CODES.ERR_FS_NOT_WRITABLE,
@@ -1188,8 +1231,6 @@ ${TextStylesBold.bold("Telemetry Notice")}`);
1188
1231
  additionalProperties: { ...telemetryData }
1189
1232
  }
1190
1233
  );
1191
- if (posthogClient) await posthogClient.shutdown();
1192
- process.exit(1);
1193
1234
  }
1194
1235
  let appName;
1195
1236
  try {
@@ -1197,16 +1238,13 @@ ${TextStylesBold.bold("Telemetry Notice")}`);
1197
1238
  telemetryData["app-name"] = appName;
1198
1239
  } catch (err) {
1199
1240
  const error = err;
1200
- spinner.fail(error.message);
1201
- postHogCaptureError(posthogClient, userId, sessionId, error, {
1241
+ await fatalExit(error.message, error, {
1202
1242
  errorCode: ERROR_CODES.ERR_FS_MKDIR_FAILED,
1203
1243
  errorCategory: "filesystem",
1204
1244
  step: TRACKING_STEPS.DIRECTORY_SETUP,
1205
1245
  fatal: true,
1206
1246
  additionalProperties: { ...telemetryData }
1207
1247
  });
1208
- if (posthogClient) await posthogClient.shutdown();
1209
- exit(1);
1210
1248
  }
1211
1249
  try {
1212
1250
  if (themeChoice) {
@@ -1225,24 +1263,30 @@ ${TextStylesBold.bold("Telemetry Notice")}`);
1225
1263
  spinner.succeed();
1226
1264
  } catch (err) {
1227
1265
  const error = err;
1228
- spinner.fail(`Failed to download template: ${error.message}`);
1229
- postHogCaptureError(posthogClient, userId, sessionId, error, {
1266
+ await fatalExit(`Failed to download template: ${error.message}`, error, {
1230
1267
  errorCode: ERROR_CODES.ERR_TPL_DOWNLOAD_FAILED,
1231
1268
  errorCategory: "template",
1232
1269
  step: TRACKING_STEPS.DOWNLOADING_TEMPLATE,
1233
1270
  fatal: true,
1234
1271
  additionalProperties: { ...telemetryData }
1235
1272
  });
1236
- if (posthogClient) await posthogClient.shutdown();
1237
- exit(1);
1238
1273
  }
1239
1274
  spinner.start("Installing packages.");
1240
1275
  try {
1241
1276
  await install(pkgManager, opts.verbose);
1242
1277
  spinner.succeed();
1243
1278
  } catch (err) {
1244
- const error = err;
1245
- spinner.fail(`Failed to install packages: ${error.message}`);
1279
+ const error = err instanceof Error ? err : new Error(String(err));
1280
+ const reason = error.message || String(err);
1281
+ spinner.fail(`Failed to install packages: ${reason}`);
1282
+ console.log(
1283
+ `
1284
+ ${box([
1285
+ `${TextStyles.bold("Stuck?")} Check the TinaCMS FAQ for common install issues:`,
1286
+ TextStyles.link(FAQ_URL)
1287
+ ])}
1288
+ `
1289
+ );
1246
1290
  packageManagerInstallationHadError = true;
1247
1291
  postHogCaptureError(posthogClient, userId, sessionId, error, {
1248
1292
  errorCode: ERROR_CODES.ERR_INSTALL_PKG_MANAGER_FAILED,
@@ -1317,12 +1361,21 @@ ${TextStylesBold.bold("Telemetry Notice")}`);
1317
1361
  "https://tina.io/docs/r/what-is-tinacloud"
1318
1362
  )}`
1319
1363
  );
1364
+ console.log(
1365
+ ` \u2022 \u{1F4AC} Reach out for support: ${TextStyles.link(DISCORD_SUPPORT_URL)}`
1366
+ );
1320
1367
  }
1321
1368
  run().catch(async (error) => {
1322
1369
  if (process.stdout.columns >= 60) {
1323
1370
  console.log(TextStyles.tinaOrange(`${errorArt}`));
1324
1371
  }
1325
1372
  console.error("Error running create-tina-app:", error);
1373
+ console.error(
1374
+ `
1375
+ \u{1F4AC} Need more help? Reach out to the TinaCMS community at ${TextStyles.link(
1376
+ DISCORD_SUPPORT_URL
1377
+ )}`
1378
+ );
1326
1379
  const sessionId = generateSessionId();
1327
1380
  const userId = await getAnonymousUserId();
1328
1381
  postHogCaptureError(posthogClient, userId, sessionId, error, {
@@ -3,5 +3,7 @@ import { PackageManager } from './packageManagers';
3
3
  * Spawn a package manager installation.
4
4
  *
5
5
  * @returns A Promise that resolves once the installation is finished.
6
+ * On failure it rejects with an `Error` whose message includes the command,
7
+ * the exit code, and the tail of the captured output.
6
8
  */
7
9
  export declare function install(packageManager: PackageManager, verboseOutput: boolean): Promise<void>;
@@ -35,6 +35,7 @@ export declare const ERROR_CODES: {
35
35
  readonly ERR_VAL_INVALID_TEMPLATE: "ERR_VAL_INVALID_TEMPLATE";
36
36
  readonly ERR_VAL_INVALID_PKG_MANAGER: "ERR_VAL_INVALID_PKG_MANAGER";
37
37
  readonly ERR_VAL_INVALID_PROJECT_NAME: "ERR_VAL_INVALID_PROJECT_NAME";
38
+ readonly ERR_VAL_INVALID_THEME: "ERR_VAL_INVALID_THEME";
38
39
  readonly ERR_VAL_UNSUPPORTED_NODE: "ERR_VAL_UNSUPPORTED_NODE";
39
40
  readonly ERR_VAL_NO_PKG_MANAGERS: "ERR_VAL_NO_PKG_MANAGERS";
40
41
  readonly ERR_FS_NOT_WRITABLE: "ERR_FS_NOT_WRITABLE";
@@ -1,3 +1,8 @@
1
+ /**
2
+ * Draw a bordered box around one or more lines so a message stands out from the
3
+ * surrounding log output.
4
+ */
5
+ export declare const box: (lines: string[], color?: (s: string) => string) => string;
1
6
  export declare const TextStyles: {
2
7
  tinaOrange: import("chalk").ChalkInstance;
3
8
  link: (url: string) => string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-tina-app",
3
- "version": "2.1.5",
3
+ "version": "2.1.7",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "files": [
@@ -20,7 +20,7 @@
20
20
  ]
21
21
  },
22
22
  "engines": {
23
- "node": ">=18.18.0"
23
+ "node": "22.x || 24.x"
24
24
  },
25
25
  "publishConfig": {
26
26
  "registry": "https://registry.npmjs.org"