create-tina-app 0.0.0-d80714b-20241205222511 → 0.0.0-d813ac1-20251210222143

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/README.md CHANGED
@@ -6,6 +6,6 @@ Create Tina App is a powerful command-line interface (CLI) tool designed to kick
6
6
 
7
7
  To get started, you need to first build the code - see [contributing](https://github.com/tinacms/tinacms/blob/main/CONTRIBUTING.md).
8
8
 
9
- 1. run `pnpm link create-tina-app`
9
+ 1. run `pnpm link --global`
10
10
  1. Test your changes by running `npx create-tina-app`
11
11
  1. Run `pnpm unlink` when done to unlink the local build
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- require('../dist')
3
+ import('../dist/index.js');
package/dist/index.d.ts CHANGED
@@ -1,2 +1 @@
1
- export declare const PKG_MANAGERS: string[];
2
1
  export declare function run(): Promise<void>;
package/dist/index.js CHANGED
@@ -1,87 +1,30 @@
1
- var __create = Object.create;
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __getProtoOf = Object.getPrototypeOf;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __export = (target, all) => {
8
- for (var name2 in all)
9
- __defProp(target, name2, { get: all[name2], enumerable: true });
10
- };
11
- var __copyProps = (to, from, except, desc) => {
12
- if (from && typeof from === "object" || typeof from === "function") {
13
- for (let key of __getOwnPropNames(from))
14
- if (!__hasOwnProp.call(to, key) && key !== except)
15
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
- }
17
- return to;
18
- };
19
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
21
- mod
22
- ));
23
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
24
-
25
1
  // src/index.ts
26
- var src_exports = {};
27
- __export(src_exports, {
28
- PKG_MANAGERS: () => PKG_MANAGERS,
29
- run: () => run
30
- });
31
- module.exports = __toCommonJS(src_exports);
32
- var import_metrics = require("@tinacms/metrics");
33
- var import_commander = require("commander");
34
- var import_prompts = __toESM(require("prompts"));
35
- var import_node_path = __toESM(require("path"));
36
-
37
- // package.json
38
- var name = "create-tina-app";
39
- var version = "1.2.4";
2
+ import { Telemetry } from "@tinacms/metrics";
3
+ import prompts from "prompts";
4
+ import path4 from "node:path";
5
+ import { createRequire } from "node:module";
40
6
 
41
7
  // src/util/fileUtil.ts
42
- var import_fs_extra = __toESM(require("fs-extra"));
43
- var import_path = __toESM(require("path"));
8
+ import fs from "fs-extra";
9
+ import path from "path";
44
10
 
45
- // src/util/logger.ts
46
- var import_chalk = __toESM(require("chalk"));
11
+ // src/util/textstyles.ts
12
+ import chalk from "chalk";
47
13
  var TextStyles = {
48
- link: import_chalk.default.bold.cyan,
49
- cmd: import_chalk.default.inverse,
50
- info: import_chalk.default.blue,
51
- success: import_chalk.default.green,
52
- warn: import_chalk.default.yellow,
53
- err: import_chalk.default.red,
54
- bold: import_chalk.default.bold
55
- };
56
- var Logger = class {
57
- log(message) {
58
- console.info(message);
59
- }
60
- debug(message) {
61
- console.debug(TextStyles.info(`[DEBUG] ${message}`));
62
- }
63
- info(message) {
64
- console.info(TextStyles.info(`[INFO] ${message}`));
65
- }
66
- success(message) {
67
- console.log(TextStyles.success(`[SUCCESS] ${message}`));
68
- }
69
- cmd(message) {
70
- console.log(TextStyles.cmd(message));
71
- }
72
- warn(message) {
73
- console.warn(TextStyles.warn(`[WARNING] ${message}`));
74
- }
75
- err(message) {
76
- console.error(TextStyles.err(`[ERROR] ${message}`));
77
- }
14
+ tinaOrange: chalk.hex("#EC4816"),
15
+ link: (url) => `\x1B]8;;${url}\x07${chalk.cyan.underline(url)}\x1B]8;;\x07`,
16
+ cmd: chalk.bgBlackBright.bold.white,
17
+ info: chalk.blue,
18
+ success: chalk.green,
19
+ warn: chalk.yellow,
20
+ err: chalk.red,
21
+ bold: chalk.bold
78
22
  };
79
- var log = new Logger();
80
23
 
81
24
  // src/util/fileUtil.ts
82
25
  async function isWriteable(directory) {
83
26
  try {
84
- await import_fs_extra.default.promises.access(directory, (import_fs_extra.default.constants || import_fs_extra.default).W_OK);
27
+ await fs.promises.access(directory, (fs.constants || fs).W_OK);
85
28
  return true;
86
29
  } catch (err) {
87
30
  return false;
@@ -108,112 +51,71 @@ function folderContainsInstallConflicts(root) {
108
51
  "yarn-debug.log",
109
52
  "yarn-error.log"
110
53
  ];
111
- const conflicts = import_fs_extra.default.readdirSync(root).filter((file) => !validFiles.includes(file)).filter((file) => !/\.iml$/.test(file));
54
+ const conflicts = fs.readdirSync(root).filter((file) => !validFiles.includes(file)).filter((file) => !/\.iml$/.test(file));
112
55
  return conflicts;
113
56
  }
114
57
  async function setupProjectDirectory(dir) {
115
- const appName = import_path.default.basename(dir);
116
- await import_fs_extra.default.mkdirp(dir);
58
+ const appName = path.basename(dir);
59
+ await fs.mkdirp(dir);
117
60
  process.chdir(dir);
118
61
  const conflicts = folderContainsInstallConflicts(dir);
119
62
  if (conflicts.length > 0) {
120
- log.err(
121
- `The directory '${TextStyles.bold(
122
- appName
123
- )}' contains files that could conflict. Below is a list of conflicts, please remove them and try again.`
124
- );
63
+ const errorMessageLines = [
64
+ `The directory '${TextStyles.bold(appName)}' contains files that could conflict. Below is a list of conflicts, please remove them and try again.`
65
+ ];
125
66
  for (const file of conflicts) {
126
67
  try {
127
- const stats = import_fs_extra.default.lstatSync(import_path.default.join(dir, file));
68
+ const stats = fs.lstatSync(path.join(dir, file));
128
69
  if (stats.isDirectory()) {
129
- log.log(`- ${TextStyles.info(file)}/`);
70
+ errorMessageLines.push(` - ${TextStyles.info(file)}/`);
130
71
  } else {
131
- log.log(`- ${file}`);
72
+ errorMessageLines.push(` - ${file}`);
132
73
  }
133
74
  } catch {
134
- log.log(`- ${file}`);
75
+ errorMessageLines.push(` - ${file}`);
135
76
  }
136
77
  }
137
- process.exit(1);
78
+ throw new Error(errorMessageLines.join("\n"));
138
79
  }
139
80
  return appName;
140
81
  }
141
82
  function updateProjectPackageName(dir, name2) {
142
- const packageJsonPath = import_path.default.join(dir, "package.json");
143
- const packageJson = JSON.parse(import_fs_extra.default.readFileSync(packageJsonPath, "utf8"));
83
+ const packageJsonPath = path.join(dir, "package.json");
84
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
144
85
  packageJson.name = name2;
145
- import_fs_extra.default.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
86
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
146
87
  }
147
88
  function updateProjectPackageVersion(dir, version2) {
148
- const packageJsonPath = import_path.default.join(dir, "package.json");
149
- const packageJson = JSON.parse(import_fs_extra.default.readFileSync(packageJsonPath, "utf8"));
89
+ const packageJsonPath = path.join(dir, "package.json");
90
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
150
91
  packageJson.version = version2;
151
- import_fs_extra.default.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
92
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
93
+ }
94
+ async function updateThemeSettings(dir, selectedTheme) {
95
+ const settingsDir = path.join(dir, "content", "settings");
96
+ const configPath = path.join(settingsDir, "config.json");
97
+ await fs.mkdirp(settingsDir);
98
+ let config = {};
99
+ try {
100
+ const existingConfig = await fs.readFile(configPath, "utf8");
101
+ config = JSON.parse(existingConfig);
102
+ } catch (error) {
103
+ }
104
+ config.selectedTheme = selectedTheme;
105
+ await fs.writeFile(configPath, JSON.stringify(config, null, 2));
152
106
  }
153
107
 
154
108
  // src/util/install.ts
155
- var import_cross_spawn = __toESM(require("cross-spawn"));
156
- function install(root, dependencies, { packageManager, isOnline, devDependencies }) {
157
- const npmFlags = [];
158
- const yarnFlags = [];
159
- const pnpmFlags = [];
109
+ import spawn from "cross-spawn";
110
+ function install(packageManager, verboseOutput) {
160
111
  return new Promise((resolve, reject) => {
161
- let args;
162
- const command = packageManager;
163
- if (dependencies == null ? void 0 : dependencies.length) {
164
- switch (packageManager) {
165
- case "yarn":
166
- args = ["add", "--exact"];
167
- if (!isOnline)
168
- args.push("--offline");
169
- args.push("--cwd", root);
170
- if (devDependencies)
171
- args.push("--dev");
172
- args.push(...dependencies);
173
- break;
174
- case "npm":
175
- args = ["install", "--save-exact"];
176
- args.push(devDependencies ? "--save-dev" : "--save");
177
- args.push(...dependencies);
178
- break;
179
- case "pnpm":
180
- args = ["add"];
181
- if (!isOnline)
182
- args.push("--offline");
183
- args.push("--save-exact");
184
- if (devDependencies)
185
- args.push("-D");
186
- args.push(...dependencies);
187
- break;
188
- }
189
- } else {
190
- args = ["install"];
191
- if (!isOnline) {
192
- log.warn("You appear to be offline.");
193
- if (packageManager === "yarn") {
194
- log.warn("Falling back to the local Yarn cache.");
195
- args.push("--offline");
196
- }
197
- }
198
- }
199
- switch (packageManager) {
200
- case "yarn":
201
- args.push(...yarnFlags);
202
- break;
203
- case "npm":
204
- args.push(...npmFlags);
205
- break;
206
- case "pnpm":
207
- args.push(...pnpmFlags);
208
- break;
209
- }
210
- const child = (0, import_cross_spawn.default)(command, args, {
211
- stdio: "inherit",
112
+ const child = spawn(packageManager, ["install"], {
113
+ stdio: verboseOutput ? "inherit" : "ignore",
212
114
  env: { ...process.env, ADBLOCK: "1", DISABLE_OPENCOLLECTIVE: "1" }
213
115
  });
214
116
  child.on("close", (code) => {
215
117
  if (code !== 0) {
216
- reject({ command: `${command} ${args.join(" ")}` });
118
+ reject({ command: `${packageManager} install` });
217
119
  return;
218
120
  }
219
121
  resolve();
@@ -222,12 +124,12 @@ function install(root, dependencies, { packageManager, isOnline, devDependencies
222
124
  }
223
125
 
224
126
  // src/util/git.ts
225
- var import_child_process = require("child_process");
226
- var import_path2 = __toESM(require("path"));
227
- var import_fs_extra2 = __toESM(require("fs-extra"));
127
+ import { execSync } from "child_process";
128
+ import path2 from "path";
129
+ import fs2 from "fs-extra";
228
130
  function isInGitRepository() {
229
131
  try {
230
- (0, import_child_process.execSync)("git rev-parse --is-inside-work-tree", { stdio: "ignore" });
132
+ execSync("git rev-parse --is-inside-work-tree", { stdio: "ignore" });
231
133
  return true;
232
134
  } catch (_) {
233
135
  }
@@ -235,7 +137,7 @@ function isInGitRepository() {
235
137
  }
236
138
  function isInMercurialRepository() {
237
139
  try {
238
- (0, import_child_process.execSync)("hg --cwd . root", { stdio: "ignore" });
140
+ execSync("hg --cwd . root", { stdio: "ignore" });
239
141
  return true;
240
142
  } catch (_) {
241
143
  }
@@ -243,25 +145,27 @@ function isInMercurialRepository() {
243
145
  }
244
146
  function makeFirstCommit(root) {
245
147
  try {
246
- (0, import_child_process.execSync)("git checkout -b main", { stdio: "ignore" });
247
- (0, import_child_process.execSync)("git add -A", { stdio: "ignore" });
248
- (0, import_child_process.execSync)('git commit -m "Initial commit from Create Tina App"', {
148
+ execSync("git checkout -b main", { stdio: "ignore" });
149
+ execSync("git add -A", { stdio: "ignore" });
150
+ execSync('git commit -m "Initial commit from Create Tina App"', {
249
151
  stdio: "ignore"
250
152
  });
251
153
  } catch (err) {
252
- import_fs_extra2.default.removeSync(import_path2.default.join(root, ".git"));
154
+ fs2.removeSync(path2.join(root, ".git"));
253
155
  throw err;
254
156
  }
255
157
  }
256
- function initializeGit() {
257
- (0, import_child_process.execSync)("git --version", { stdio: "ignore" });
158
+ function initializeGit(spinner) {
159
+ execSync("git --version", { stdio: "ignore" });
258
160
  if (isInGitRepository() || isInMercurialRepository()) {
259
- log.warn("Already in a Git repository, skipping.");
161
+ spinner.warn("Already in a Git repository, skipping.");
260
162
  return false;
261
163
  }
262
- if (!import_fs_extra2.default.existsSync(".gitignore")) {
263
- log.warn("There is no .gitignore file in this repository, creating one...");
264
- import_fs_extra2.default.writeFileSync(
164
+ if (!fs2.existsSync(".gitignore")) {
165
+ spinner.warn(
166
+ "There is no .gitignore file in this repository, creating one..."
167
+ );
168
+ fs2.writeFileSync(
265
169
  ".gitignore",
266
170
  `node_modules
267
171
  .yarn/*
@@ -271,18 +175,25 @@ function initializeGit() {
271
175
  `
272
176
  );
273
177
  }
274
- (0, import_child_process.execSync)("git init", { stdio: "ignore" });
178
+ execSync("git init", { stdio: "ignore" });
275
179
  return true;
276
180
  }
277
181
 
278
182
  // src/util/examples.ts
279
- var import_node_stream = require("stream");
280
- var import_promises = require("stream/promises");
281
- var import_tar = require("tar");
183
+ import { Readable } from "node:stream";
184
+ import { pipeline } from "node:stream/promises";
185
+ import { x } from "tar";
282
186
  async function getRepoInfo(url, examplePath) {
283
187
  const [, username, name2, t, _branch, ...file] = url.pathname.split("/");
284
188
  const filePath = examplePath ? examplePath.replace(/^\//, "") : file.join("/");
285
- if (t === void 0 || t === "" && _branch === void 0) {
189
+ if (
190
+ // Support repos whose entire purpose is to be a Next.js example, e.g.
191
+ // https://github.com/:username/:my-cool-nextjs-example-repo-name.
192
+ t === void 0 || // Support GitHub URL that ends with a trailing slash, e.g.
193
+ // https://github.com/:username/:my-cool-nextjs-example-repo-name/
194
+ // In this case "t" will be an empty string while the next part "_branch" will be undefined
195
+ t === "" && _branch === void 0
196
+ ) {
286
197
  try {
287
198
  const infoResponse = await fetch(
288
199
  `https://api.github.com/repos/${username}/${name2}`
@@ -306,14 +217,14 @@ async function downloadTarStream(url) {
306
217
  if (!res.body) {
307
218
  throw new Error(`Failed to download: ${url}`);
308
219
  }
309
- return import_node_stream.Readable.fromWeb(res.body);
220
+ return Readable.fromWeb(res.body);
310
221
  }
311
222
  async function downloadAndExtractRepo(root, { username, name: name2, branch, filePath }) {
312
- await (0, import_promises.pipeline)(
223
+ await pipeline(
313
224
  await downloadTarStream(
314
225
  `https://codeload.github.com/${username}/${name2}/tar.gz/${branch}`
315
226
  ),
316
- (0, import_tar.x)({
227
+ x({
317
228
  cwd: root,
318
229
  strip: filePath ? filePath.split("/").length + 1 : 1,
319
230
  filter: (p) => p.startsWith(
@@ -324,85 +235,206 @@ async function downloadAndExtractRepo(root, { username, name: name2, branch, fil
324
235
  }
325
236
 
326
237
  // src/templates.ts
327
- var import_fs_extra3 = require("fs-extra");
328
- var import_path3 = __toESM(require("path"));
238
+ import { copy } from "fs-extra";
239
+ import path3 from "path";
329
240
  var TEMPLATES = [
330
241
  {
331
242
  title: "\u2B50 NextJS starter",
332
- description: "Kickstart your project with NextJS \u2013 our top recommendation for a seamless, performant, and versatile web experience.",
333
- value: "tina-cloud-starter",
243
+ description: "Kickstart your project with Next.js \u2013 our top recommendation for a seamless, performant, and versatile web experience.",
244
+ value: "tina-nextjs-starter",
334
245
  isInternal: false,
335
- gitURL: "https://github.com/tinacms/tina-cloud-starter"
246
+ features: [
247
+ {
248
+ name: "Visual Editing",
249
+ description: "\u2705"
250
+ },
251
+ {
252
+ name: "ISR",
253
+ description: "\u2705"
254
+ },
255
+ {
256
+ name: "SSG",
257
+ description: "\u2705"
258
+ }
259
+ ],
260
+ gitURL: "https://github.com/tinacms/tina-nextjs-starter",
261
+ devUrl: "http://localhost:3000"
262
+ },
263
+ {
264
+ title: "\u2B50\uFE0F TinaDocs",
265
+ description: "Get your documentation site up and running with TinaCMS and Next.js in minutes.",
266
+ value: "tina-docs",
267
+ isInternal: false,
268
+ features: [
269
+ {
270
+ name: "Visual Editing",
271
+ description: "\u2705"
272
+ },
273
+ {
274
+ name: "ISR",
275
+ description: "\u2705"
276
+ },
277
+ {
278
+ name: "SSG",
279
+ description: "\u2705"
280
+ }
281
+ ],
282
+ gitURL: "https://github.com/tinacms/tina-docs",
283
+ devUrl: "http://localhost:3000"
284
+ },
285
+ {
286
+ title: "Astro Starter",
287
+ description: "Get started with Astro - a modern static site generator designed for fast, lightweight, and flexible web projects.",
288
+ value: "tina-astro-starter",
289
+ isInternal: false,
290
+ features: [
291
+ {
292
+ name: "Visual Editing",
293
+ description: "\u274C"
294
+ },
295
+ {
296
+ name: "ISR",
297
+ description: "\u274C"
298
+ },
299
+ {
300
+ name: "SSG",
301
+ description: "\u2705"
302
+ }
303
+ ],
304
+ gitURL: "https://github.com/tinacms/tina-astro-starter",
305
+ devUrl: "http://localhost:4321"
336
306
  },
337
307
  {
338
308
  title: "Hugo Starter",
339
309
  description: "With Hugo, you wield the power of lightning-fast site generation, crafting web experiences at the speed of thought.",
340
310
  value: "tina-hugo-starter",
341
311
  isInternal: false,
342
- gitURL: "https://github.com/tinacms/tina-hugo-starter"
312
+ features: [
313
+ {
314
+ name: "Visual Editing",
315
+ description: "\u274C"
316
+ },
317
+ {
318
+ name: "ISR",
319
+ description: "\u274C"
320
+ },
321
+ {
322
+ name: "SSG",
323
+ description: "\u2705"
324
+ }
325
+ ],
326
+ gitURL: "https://github.com/tinacms/tina-hugo-starter",
327
+ devUrl: "http://localhost:1313"
343
328
  },
344
329
  {
345
330
  title: "Remix Starter",
346
331
  description: "Dive into Remix to orchestrate seamless, interactive user journeys like a maestro of the web.",
347
332
  value: "tina-remix-starter",
348
333
  isInternal: false,
349
- gitURL: "https://github.com/tinacms/tina-remix-starter"
334
+ features: [
335
+ {
336
+ name: "Visual Editing",
337
+ description: "\u274C"
338
+ },
339
+ {
340
+ name: "ISR",
341
+ description: "\u274C"
342
+ },
343
+ {
344
+ name: "SSG",
345
+ description: "\u26A0\uFE0F Requires adapter"
346
+ }
347
+ ],
348
+ gitURL: "https://github.com/tinacms/tina-remix-starter",
349
+ devUrl: "http://localhost:3000"
350
350
  },
351
351
  {
352
352
  title: "Docusaurus Starter",
353
353
  description: "Docusaurus empowers you to build and evolve documentation like crafting a living, breathing knowledge repository.",
354
354
  value: "tinasaurus",
355
355
  isInternal: false,
356
- gitURL: "https://github.com/tinacms/tinasaurus"
356
+ features: [
357
+ {
358
+ name: "Visual Editing",
359
+ description: "\u274C"
360
+ },
361
+ {
362
+ name: "ISR",
363
+ description: "\u274C"
364
+ },
365
+ {
366
+ name: "SSR",
367
+ description: "\u2705"
368
+ }
369
+ ],
370
+ gitURL: "https://github.com/tinacms/tinasaurus",
371
+ devUrl: "http://localhost:3000"
357
372
  },
358
373
  {
359
374
  title: "Bare bones starter",
360
- description: "Stripped down to essentials, this starter is the canvas for pure, unadulterated code creativity.",
375
+ description: "Stripped down to essentials, this starter is the canvas for pure, unadulterated code creativity. Built with Next.js.",
361
376
  value: "basic",
362
377
  isInternal: false,
363
- gitURL: "https://github.com/tinacms/tina-barebones-starter"
378
+ features: [
379
+ {
380
+ name: "Visual Editing",
381
+ description: "\u2705"
382
+ },
383
+ {
384
+ name: "ISR",
385
+ description: "\u2705"
386
+ },
387
+ {
388
+ name: "SSG",
389
+ description: "\u2705"
390
+ }
391
+ ],
392
+ gitURL: "https://github.com/tinacms/tina-barebones-starter",
393
+ devUrl: "http://localhost:3000"
364
394
  }
365
395
  ];
366
- async function downloadTemplate(template, root) {
396
+ async function downloadTemplate(template, root, spinner) {
367
397
  if (template.isInternal === false) {
368
398
  const repoURL = new URL(template.gitURL);
369
399
  const repoInfo = await getRepoInfo(repoURL);
370
400
  if (!repoInfo) {
371
401
  throw new Error("Repository information not found.");
372
402
  }
373
- log.info(
374
- `Downloading files from repo ${TextStyles.link(
375
- `${repoInfo == null ? void 0 : repoInfo.username}/${repoInfo == null ? void 0 : repoInfo.name}`
376
- )}.`
377
- );
403
+ spinner.text = `Downloading files from repo ${TextStyles.tinaOrange(
404
+ `${repoInfo?.username}/${repoInfo?.name}`
405
+ )}`;
378
406
  await downloadAndExtractRepo(root, repoInfo);
379
407
  } else {
380
- const templateFile = import_path3.default.join(__dirname, "..", "examples", template.value);
381
- await (0, import_fs_extra3.copy)(`${templateFile}/`, "./");
408
+ const templateFile = path3.join(__dirname, "..", "examples", template.value);
409
+ await copy(`${templateFile}/`, "./");
382
410
  }
383
411
  }
384
412
 
385
413
  // src/util/preRunChecks.ts
386
- var SUPPORTED_NODE_VERSIONS = ["18", "20", "22"];
387
- function preRunChecks() {
388
- checkSupportedNodeVersion();
389
- }
390
- function checkSupportedNodeVersion() {
391
- if (!SUPPORTED_NODE_VERSIONS.some(
392
- (version2) => process.version.startsWith(`v${version2}`)
393
- )) {
394
- log.warn(
395
- `Version ${process.version} of Node.js is not supported in create-tina-app, please update to the latest LTS version. See https://nodejs.org/en/download/ for more details.`
414
+ var SUPPORTED_NODE_VERSION_BOUNDS = { oldest: 20, latest: 22 };
415
+ var SUPPORTED_NODE_VERSION_RANGE = [
416
+ ...Array(SUPPORTED_NODE_VERSION_BOUNDS.latest).keys()
417
+ ].map((i) => i + SUPPORTED_NODE_VERSION_BOUNDS.oldest);
418
+ var isSupported = SUPPORTED_NODE_VERSION_RANGE.some(
419
+ (version2) => process.version.startsWith(`v${version2}`)
420
+ );
421
+ function preRunChecks(spinner) {
422
+ spinner.start("Running pre-run checks...");
423
+ if (!isSupported) {
424
+ spinner.warn(
425
+ `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.`
396
426
  );
427
+ } else {
428
+ spinner.succeed(`Node ${process.version} is supported.`);
397
429
  }
398
430
  }
399
431
 
400
432
  // src/util/checkPkgManagers.ts
401
- var import_child_process2 = require("child_process");
433
+ import { exec } from "child_process";
402
434
  async function checkPackageExists(name2) {
403
435
  try {
404
436
  await new Promise((resolve, reject) => {
405
- (0, import_child_process2.exec)(`${name2} -v`, (error, stdout, stderr) => {
437
+ exec(`${name2} -v`, (error, stdout, stderr) => {
406
438
  if (error) {
407
439
  reject(stderr);
408
440
  }
@@ -416,13 +448,22 @@ async function checkPackageExists(name2) {
416
448
  }
417
449
 
418
450
  // src/index.ts
419
- var import_node_process = require("process");
420
- var import_validate_npm_package_name = __toESM(require("validate-npm-package-name"));
421
- var PKG_MANAGERS = ["npm", "yarn", "pnpm"];
422
- async function run() {
423
- preRunChecks();
451
+ import { exit } from "node:process";
452
+
453
+ // src/util/options.ts
454
+ import { Command } from "commander";
455
+
456
+ // package.json
457
+ var name = "create-tina-app";
458
+ var version = "2.0.0";
459
+
460
+ // src/util/packageManagers.ts
461
+ var PKG_MANAGERS = ["npm", "yarn", "pnpm", "bun"];
462
+
463
+ // src/util/options.ts
464
+ function extractOptions(args) {
424
465
  let projectName = "";
425
- const program = new import_commander.Command(name);
466
+ const program = new Command(name);
426
467
  program.version(version).option(
427
468
  "-t, --template <template>",
428
469
  `Choose which template to start from. Valid templates are: ${TEMPLATES.map(
@@ -434,34 +475,212 @@ async function run() {
434
475
  ).option(
435
476
  "-d, --dir <dir>",
436
477
  "Choose which directory to run this script from."
437
- ).option("--noTelemetry", "Disable anonymous telemetry that is collected.").arguments("[project-directory]").usage(`${TextStyles.success("<project-directory>")} [options]`).action((name2) => {
478
+ ).option("-v, --verbose", "Enable verbose output.").option("--noTelemetry", "Disable anonymous telemetry that is collected.").arguments("[project-directory]").usage(`${TextStyles.success("<project-directory>")} [options]`).action((name2) => {
438
479
  projectName = name2;
439
480
  });
440
- program.parse(process.argv);
481
+ program.parse(args);
441
482
  const opts = program.opts();
442
483
  if (opts.dir) {
443
484
  process.chdir(opts.dir);
444
485
  }
445
- const telemetry = new import_metrics.Telemetry({ disabled: opts == null ? void 0 : opts.noTelemetry });
446
- let template = opts.template;
447
- if (template) {
448
- template = TEMPLATES.find((_template) => _template.value === template);
486
+ if (projectName) {
487
+ opts.projectName = projectName;
488
+ }
489
+ return opts;
490
+ }
491
+
492
+ // src/util/isNpm.js
493
+ import { builtinModules as builtins } from "node:module";
494
+ function validate(name2) {
495
+ if (name2 === null) {
496
+ return {
497
+ message: "name cannot be null",
498
+ isError: true
499
+ };
500
+ }
501
+ if (name2 === void 0) {
502
+ return {
503
+ message: "name cannot be undefined",
504
+ isError: true
505
+ };
506
+ }
507
+ if (typeof name2 !== "string") {
508
+ return {
509
+ message: "name must be a string",
510
+ isError: true
511
+ };
512
+ }
513
+ if (!name2.length) {
514
+ return {
515
+ message: "name length must be greater than zero",
516
+ isError: true
517
+ };
518
+ }
519
+ if (name2.startsWith(".")) {
520
+ return {
521
+ message: "name cannot start with a period",
522
+ isError: true
523
+ };
524
+ }
525
+ if (name2.match(/^_/)) {
526
+ return {
527
+ message: "name cannot start with an underscore",
528
+ isError: true
529
+ };
530
+ }
531
+ if (name2.trim() !== name2) {
532
+ return {
533
+ message: "name cannot contain leading or trailing spaces",
534
+ isError: true
535
+ };
536
+ }
537
+ const exclusionList = ["node_modules", "favicon.ico"];
538
+ exclusionList.forEach(function(excludedName) {
539
+ if (name2.toLowerCase() === excludedName) {
540
+ return {
541
+ message: excludedName + " is not a valid package name",
542
+ isError: true
543
+ };
544
+ }
545
+ });
546
+ if (builtins.includes(name2.toLowerCase())) {
547
+ return {
548
+ message: name2 + " is a core module name",
549
+ isError: true
550
+ };
551
+ }
552
+ if (name2.length > 214) {
553
+ return {
554
+ message: "name can no longer contain more than 214 characters",
555
+ isError: true
556
+ };
557
+ }
558
+ if (name2.toLowerCase() !== name2) {
559
+ return {
560
+ message: "name can no longer contain capital letters",
561
+ isError: true
562
+ };
563
+ }
564
+ if (/[~'!()*]/.test(name2.split("/").slice(-1)[0])) {
565
+ return {
566
+ message: `name can no longer contain special characters ("~'!()*")`,
567
+ isError: true
568
+ };
569
+ }
570
+ if (encodeURIComponent(name2) !== name2) {
571
+ const scopedPackagePattern = new RegExp("^(?:@([^/]+?)[/])?([^/]+?)$");
572
+ const nameMatch = name2.match(scopedPackagePattern);
573
+ if (nameMatch) {
574
+ const user = nameMatch[1];
575
+ const pkg = nameMatch[2];
576
+ if (pkg.startsWith(".")) {
577
+ return {
578
+ message: "name cannot start with a period",
579
+ isError: true
580
+ };
581
+ }
582
+ if (encodeURIComponent(user) === user && encodeURIComponent(pkg) === pkg) {
583
+ return { message: null, isError: false };
584
+ }
585
+ }
586
+ return {
587
+ message: "name can only contain URL-friendly characters",
588
+ isError: true
589
+ };
590
+ }
591
+ return { message: null, isError: false };
592
+ }
593
+
594
+ // src/util/asciiArt.ts
595
+ var llama = " :--=: \n :-===- \n -=====- \n -=======. \n .=========-. \n :===========--:\n -=============.\n .==========-:. \n :=========-. \n -=========- \n .==========- \n -==========- \n :===========- \n -=============. \n :==============: \n :===============- \n .:-================- \n ..::---==================== \n ....::::::::::-------============================. \n .---=================================================: \n .-=====================================================- \n:=======================================================. \n .-====================================================. \n .-=================================================. \n :=============================================- \n -============================================. \n .============-:. -==========- \n :=========-: .. -==========. \n -========: :-=- -=========- \n .========. .-==== :=========: \n -=======: :=====. -========: \n -======- -====- -=======: \n -=====: -====: :======. \n .=====. -====. .-====- \n :==== -===- -====: \n -==- :===- :====. ";
596
+ var errorArt = " ++++++++++++++\u2260 \n +++++++++\u2248 \u03C0+++++++++\u2260 \n ++++ ++++ \n +++ +++\u2248 \n +++ +++ \n \u03C0++ +++ \n ++ ++ \n +- +\u03C0 \n ++\xD7 +++++++++ \u2260+++++++++ \u221A+++++++++ ++ \n + ++ \u2248+++ ++ ++ ++++ +\u221E \n ++ ++ ++ ++ ++ ++ + \n ++ ++ ++ ++ ++ ++ ++ \n ++ ++-+++++++- ++ ++ +++++++- + \n \u221A+ ++\xF7+++++ ++ ++++++++ ++ \n ++ ++ \u221E++ ++ ++ ++ \n ++ ++ +++ ++ ++ ++ \n ++ ++ +++ \u2260+++++++++ ++ \u2260+ \n ++ ++ ++ +\u2260\u221E\u221A\u221A\u2260\xD7+\u03C0 ++ \u2260+ \n ++ \u2260+ \n ++ \u2260+ \n ++ +++ \u2260+ \n ++ ++++ \u2260+ \n ++ ++++++++ \u2260+ \n ++ ++++++++++ =+ \n ++ ++++++ =+ \n ++ \u2248++++++ =+ \n ++ +++++++ \u2260+ \n ++ +++++++ \u2260+ \n ++ +++++++ =+ \n ++ ++++++++ =+ \n ++ \u221E++++++++++++ \u2260+ \n ++ \u221E++++++++++++++++++++++++++++++- =+ \n ++ +++++++++++++++++++++++++++++++++ \u2260+ \n ++ +++++++++++++++++++++++++++++++++ \u2260+ \n ++ ++++++++++++++++++++++++++++++ \u2260+ \n ++ +++++++++++++++++++++++++++++ \u2260+ \n ++ ++++++++++++++++++++++++++++ \u2260+ \n ++ ++++++++\u2260\u2260++++++=\u221A ++++++++ \u2260+ \n ++ ++++++ + +++++++ \u2260+ \n ++ +++++ \u221E+++ \xF7++++++ \u2260+ \n ++ ++++ +++ +++++\u221A \u2260+ \n ++ +++= +++\u221A ++++ \u2260+ \n ++ ++\u221E =++ +++ \u2260+ \n ++ ++\u2248 +++ ++\xF7 =+ \n ++ +++ +++\u2260 +++ =+ \n ++ \u2260+ \n ++ + \n \u2248+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ \n ++ +\u2260 \n ++ +++++++++ ++++++++ +++++++ ++++++ ++++++++ +\u2260 \n ++ ++ ++ ++ + ++ ++ ++ ++ ++ +\u2260 \n ++ ++ ++ ++ + ++ ++ ++ ++ ++ +\u2260 \n ++ ++++++++ ++++++++ ++++++++ ++ ++ ++++++++ +\u2260 \n ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +\u2260 \n ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +\u2260 \n ++ +++++++++ ++ ++ ++ ++ +++++++ ++ ++ +\u2260 \n ++ +\u2260 \n ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ \n \n ";
597
+ var tinaCms = "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\n\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\n \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2554\u2588\u2588\u2588\u2588\u2554\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\n \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551\n \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u255A\u2550\u255D \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\n \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D";
598
+
599
+ // src/themes.ts
600
+ var THEMES = [
601
+ {
602
+ title: "Default",
603
+ description: "The default monochromatic theme for your documentation site",
604
+ value: "default"
605
+ },
606
+ {
607
+ title: "Tina",
608
+ description: "The warm color scheme of TinaCMS for your documentation",
609
+ value: "tina"
610
+ },
611
+ {
612
+ title: "Blossom",
613
+ value: "blossom",
614
+ description: "A Blossom theme for your project"
615
+ },
616
+ {
617
+ title: "Lake",
618
+ value: "lake",
619
+ description: "A Lake theme for your project"
620
+ },
621
+ {
622
+ title: "Pine",
623
+ value: "pine",
624
+ description: "A Pine theme for your project"
625
+ },
626
+ {
627
+ title: "Indigo",
628
+ value: "indigo",
629
+ description: "An Indigo theme for your project"
630
+ }
631
+ ];
632
+
633
+ // src/index.ts
634
+ function formatTemplateChoice(template) {
635
+ let description = template.description || "";
636
+ if (template.features && template.features.length > 0) {
637
+ const featuresText = template.features.map((feature) => ` \u2022 ${feature.name}: ${feature.description}`).join("\n");
638
+ description = `${description}
639
+
640
+ Features:
641
+ ${featuresText}`;
642
+ }
643
+ return {
644
+ title: template.title,
645
+ value: template.value,
646
+ description
647
+ };
648
+ }
649
+ async function run() {
650
+ const ora = (await import("ora")).default;
651
+ let packageManagerInstallationHadError = false;
652
+ if (process.stdout.columns >= 60) {
653
+ console.log(TextStyles.tinaOrange(`${llama}`));
654
+ console.log(TextStyles.tinaOrange(`${tinaCms}`));
655
+ } else {
656
+ console.log(TextStyles.tinaOrange(`\u{1F999} TinaCMS`));
657
+ }
658
+ const require2 = createRequire(import.meta.url);
659
+ const version2 = require2("../package.json").version;
660
+ console.log(`Create Tina App v${version2}`);
661
+ const spinner = ora();
662
+ preRunChecks(spinner);
663
+ const opts = extractOptions(process.argv);
664
+ const telemetry = new Telemetry({ disabled: opts?.noTelemetry });
665
+ let template = null;
666
+ if (opts.template) {
667
+ template = TEMPLATES.find((_template) => _template.value === opts.template);
449
668
  if (!template) {
450
- log.err(
669
+ spinner.fail(
451
670
  `The provided template '${opts.template}' is invalid. Please provide one of the following: ${TEMPLATES.map(
452
671
  (x2) => x2.value
453
672
  )}`
454
673
  );
455
- (0, import_node_process.exit)(1);
674
+ exit(1);
456
675
  }
457
676
  }
458
677
  let pkgManager = opts.pkgManager;
459
678
  if (pkgManager) {
460
679
  if (!PKG_MANAGERS.find((_pkgManager) => _pkgManager === pkgManager)) {
461
- log.err(
680
+ spinner.fail(
462
681
  `The provided package manager '${opts.pkgManager}' is not supported. Please provide one of the following: ${PKG_MANAGERS}`
463
682
  );
464
- (0, import_node_process.exit)(1);
683
+ exit(1);
465
684
  }
466
685
  }
467
686
  if (!pkgManager) {
@@ -472,12 +691,12 @@ async function run() {
472
691
  }
473
692
  }
474
693
  if (installedPkgManagers.length === 0) {
475
- log.err(
694
+ spinner.fail(
476
695
  `You have no supported package managers installed. Please install one of the following: ${PKG_MANAGERS}`
477
696
  );
478
- (0, import_node_process.exit)(1);
697
+ exit(1);
479
698
  }
480
- const res = await (0, import_prompts.default)({
699
+ const res = await prompts({
481
700
  message: "Which package manager would you like to use?",
482
701
  name: "packageManager",
483
702
  type: "select",
@@ -485,95 +704,151 @@ async function run() {
485
704
  return { title: manager, value: manager };
486
705
  })
487
706
  });
488
- if (!Object.hasOwn(res, "packageManager"))
489
- (0, import_node_process.exit)(1);
707
+ if (!Object.hasOwn(res, "packageManager")) exit(1);
490
708
  pkgManager = res.packageManager;
491
709
  }
710
+ let projectName = opts.projectName;
492
711
  if (!projectName) {
493
- const res = await (0, import_prompts.default)({
712
+ const res = await prompts({
494
713
  name: "name",
495
714
  type: "text",
496
715
  message: "What is your project named?",
497
716
  initial: "my-tina-app",
498
717
  validate: (name2) => {
499
- const { validForNewPackages, errors } = (0, import_validate_npm_package_name.default)(
500
- import_node_path.default.basename(import_node_path.default.resolve(name2))
718
+ const { message, isError } = validate(
719
+ path4.basename(path4.resolve(name2))
501
720
  );
502
- if (validForNewPackages)
503
- return true;
504
- return "Invalid project name: " + errors[0];
721
+ if (isError) return `Invalid project name: ${message}`;
722
+ return true;
505
723
  }
506
724
  });
507
- if (!Object.hasOwn(res, "name"))
508
- (0, import_node_process.exit)(1);
725
+ if (!Object.hasOwn(res, "name")) exit(1);
509
726
  projectName = res.name;
510
727
  }
511
728
  if (!template) {
512
- const res = await (0, import_prompts.default)({
729
+ const res = await prompts({
513
730
  name: "template",
514
731
  type: "select",
515
732
  message: "What starter code would you like to use?",
516
- choices: TEMPLATES
733
+ choices: TEMPLATES.map(formatTemplateChoice)
517
734
  });
518
- if (!Object.hasOwn(res, "template"))
519
- (0, import_node_process.exit)(1);
735
+ if (!Object.hasOwn(res, "template")) exit(1);
520
736
  template = TEMPLATES.find((_template) => _template.value === res.template);
521
737
  }
738
+ let themeChoice;
739
+ if (template.value === "tina-docs") {
740
+ const res = await prompts({
741
+ name: "theme",
742
+ type: "select",
743
+ message: "What theme would you like to use?",
744
+ choices: THEMES
745
+ });
746
+ if (!Object.hasOwn(res, "theme")) exit(1);
747
+ themeChoice = res.theme;
748
+ }
522
749
  await telemetry.submitRecord({
523
750
  event: {
524
751
  name: "create-tina-app:invoke",
525
- template,
752
+ template: template.value,
526
753
  pkgManager
527
754
  }
528
755
  });
529
- const rootDir = import_node_path.default.join(process.cwd(), projectName);
530
- if (!await isWriteable(import_node_path.default.dirname(rootDir))) {
531
- log.err(
756
+ const rootDir = path4.join(process.cwd(), projectName);
757
+ if (!await isWriteable(path4.dirname(rootDir))) {
758
+ spinner.fail(
532
759
  "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."
533
760
  );
534
761
  process.exit(1);
535
762
  }
536
- const appName = await setupProjectDirectory(rootDir);
763
+ let appName;
537
764
  try {
538
- await downloadTemplate(template, rootDir);
765
+ appName = await setupProjectDirectory(rootDir);
766
+ } catch (err) {
767
+ spinner.fail(err.message);
768
+ exit(1);
769
+ }
770
+ try {
771
+ await downloadTemplate(template, rootDir, spinner);
772
+ if (themeChoice) {
773
+ await updateThemeSettings(rootDir, themeChoice);
774
+ }
775
+ spinner.start("Downloading template...");
776
+ await downloadTemplate(template, rootDir, spinner);
777
+ spinner.succeed();
778
+ spinner.start("Updating project metadata...");
539
779
  updateProjectPackageName(rootDir, projectName);
540
780
  updateProjectPackageVersion(rootDir, "0.0.1");
781
+ spinner.succeed();
541
782
  } catch (err) {
542
- log.err(`Failed to download template: ${err.message}`);
543
- (0, import_node_process.exit)(1);
783
+ spinner.fail(`Failed to download template: ${err.message}`);
784
+ exit(1);
544
785
  }
545
- log.info("Installing packages.");
546
- await install(rootDir, null, { packageManager: pkgManager, isOnline: true });
547
- log.info("Initializing git repository.");
786
+ spinner.start("Installing packages.");
548
787
  try {
549
- if (initializeGit()) {
788
+ await install(pkgManager, opts.verbose);
789
+ spinner.succeed();
790
+ } catch (err) {
791
+ spinner.fail(`Failed to install packages: ${err.message}`);
792
+ packageManagerInstallationHadError = true;
793
+ }
794
+ spinner.start("Initializing git repository.");
795
+ try {
796
+ if (initializeGit(spinner)) {
550
797
  makeFirstCommit(rootDir);
551
- log.info("Initialized git repository.");
798
+ spinner.succeed();
552
799
  }
553
800
  } catch (err) {
554
- log.err("Failed to initialize Git repository, skipping.");
555
- }
556
- log.success("Starter successfully created!");
557
- log.log(TextStyles.bold("\nTo launch your app, run:\n"));
558
- log.cmd(`cd ${appName}
559
- ${pkgManager} run dev`);
560
- log.log(`
561
- Next steps:
562
- \u2022 \u{1F4DD} Edit some content on ${TextStyles.link(
563
- "http://localhost:3000"
564
- )} (See ${TextStyles.link("https://tina.io/docs/using-tina-editor")})
565
- \u2022 \u{1F4D6} Learn the basics: ${TextStyles.link("https://tina.io/docs/schema/")}
566
- \u2022 \u{1F58C}\uFE0F Extend Tina with custom field components: ${TextStyles.link(
567
- "https://tina.io/docs/advanced/extending-tina/"
568
- )}
569
- \u2022 \u{1F680} Deploy to Production: ${TextStyles.link(
570
- "https://tina.io/docs/tina-cloud/"
571
- )}
572
- `);
801
+ spinner.fail("Failed to initialize Git repository, skipping.");
802
+ }
803
+ spinner.succeed(`Created ${TextStyles.tinaOrange(appName)}
804
+ `);
805
+ if (template.value === "tina-hugo-starter") {
806
+ spinner.warn(
807
+ `Hugo is required for this starter. Install it via ${TextStyles.link(
808
+ "https://gohugo.io/installation/"
809
+ )}
810
+ `
811
+ );
812
+ }
813
+ const padCommand = (cmd, width = 20) => TextStyles.cmd(cmd) + " ".repeat(Math.max(0, width - cmd.length));
814
+ spinner.info(`${TextStyles.bold("To get started:")}
815
+
816
+ ${padCommand(`cd ${appName}`)}# move into your project directory${packageManagerInstallationHadError ? `
817
+ ${padCommand(`${pkgManager} install`)}# install dependencies` : ""}
818
+ ${padCommand(
819
+ `${pkgManager} run dev`
820
+ )}# start the dev server ${TextStyles.link(template.devUrl)}
821
+ ${padCommand(`${pkgManager} run build`)}# build the app for production
822
+ `);
823
+ console.log("Next steps:");
824
+ console.log(
825
+ ` \u2022 \u{1F4DD} Edit some content: ${TextStyles.link(
826
+ "https://tina.io/docs/using-tina-editor"
827
+ )}`
828
+ );
829
+ console.log(
830
+ ` \u2022 \u{1F4D6} Learn the basics: ${TextStyles.link(
831
+ "https://tina.io/docs/schema/"
832
+ )}`
833
+ );
834
+ console.log(
835
+ ` \u2022 \u{1F58C}\uFE0F Extend Tina with custom field components: ${TextStyles.link(
836
+ "https://tina.io/docs/advanced/extending-tina/"
837
+ )}`
838
+ );
839
+ console.log(
840
+ ` \u2022 \u{1F680} Deploy to Production: ${TextStyles.link(
841
+ "https://tina.io/docs/tinacloud/"
842
+ )}`
843
+ );
573
844
  }
574
- run();
575
- // Annotate the CommonJS export names for ESM import in node:
576
- 0 && (module.exports = {
577
- PKG_MANAGERS,
578
- run
845
+ run().catch((error) => {
846
+ if (process.stdout.columns >= 60) {
847
+ console.log(TextStyles.tinaOrange(`${errorArt}`));
848
+ }
849
+ console.error("Error running create-tina-app: \n", error);
850
+ process.exit(1);
579
851
  });
852
+ export {
853
+ run
854
+ };
@@ -1,7 +1,14 @@
1
- type BaseExample = {
1
+ import { Ora } from 'ora';
2
+ type Feature = {
3
+ name: string;
4
+ description: string;
5
+ };
6
+ export type BaseExample = {
2
7
  title: string;
3
8
  description?: string;
9
+ features?: Feature[];
4
10
  value: string;
11
+ devUrl: string;
5
12
  };
6
13
  export type InternalTemplate = BaseExample & {
7
14
  isInternal: true;
@@ -12,5 +19,5 @@ export type ExternalTemplate = BaseExample & {
12
19
  };
13
20
  export type Template = InternalTemplate | ExternalTemplate;
14
21
  export declare const TEMPLATES: Template[];
15
- export declare function downloadTemplate(template: Template, root: string): Promise<void>;
22
+ export declare function downloadTemplate(template: Template, root: string, spinner: Ora): Promise<void>;
16
23
  export {};
@@ -0,0 +1,7 @@
1
+ type TinaDocsTheme = {
2
+ title: string;
3
+ value: string;
4
+ description: string;
5
+ };
6
+ export declare const THEMES: TinaDocsTheme[];
7
+ export {};
@@ -0,0 +1,3 @@
1
+ export declare const llama: string;
2
+ export declare const errorArt: string;
3
+ export declare const tinaCms: string;
@@ -1,2 +1,2 @@
1
- import { PKG_MANAGERS } from '..';
2
- export declare function checkPackageExists(name: (typeof PKG_MANAGERS)[number]): Promise<boolean>;
1
+ import { PackageManager } from './packageManagers';
2
+ export declare function checkPackageExists(name: PackageManager): Promise<boolean>;
@@ -3,3 +3,4 @@ export declare function folderContainsInstallConflicts(root: string): string[];
3
3
  export declare function setupProjectDirectory(dir: string): Promise<string>;
4
4
  export declare function updateProjectPackageName(dir: string, name: string): void;
5
5
  export declare function updateProjectPackageVersion(dir: string, version: string): void;
6
+ export declare function updateThemeSettings(dir: string, selectedTheme: string): Promise<void>;
@@ -1,2 +1,3 @@
1
+ import { Ora } from 'ora';
1
2
  export declare function makeFirstCommit(root: string): void;
2
- export declare function initializeGit(): boolean;
3
+ export declare function initializeGit(spinner: Ora): boolean;
@@ -1,21 +1,7 @@
1
- interface InstallArgs {
2
- /**
3
- * The package manager to use (yarn, npm, pnpm).
4
- */
5
- packageManager: 'yarn' | 'npm' | 'pnpm';
6
- /**
7
- * Indicate whether there is an active Internet connection.
8
- */
9
- isOnline: boolean;
10
- /**
11
- * Indicate whether the given dependencies are devDependencies.
12
- */
13
- devDependencies?: boolean;
14
- }
1
+ import { PackageManager } from './packageManagers';
15
2
  /**
16
- * Spawn a package manager installation with Yarn, NPM, or PNPM.
3
+ * Spawn a package manager installation.
17
4
  *
18
5
  * @returns A Promise that resolves once the installation is finished.
19
6
  */
20
- export declare function install(root: string, dependencies: string[] | null, { packageManager, isOnline, devDependencies }: InstallArgs): Promise<void>;
21
- export {};
7
+ export declare function install(packageManager: PackageManager, verboseOutput: boolean): Promise<void>;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @typedef {Object} ValidationResult
3
+ * @property {string | null} message
4
+ * @property {boolean} isError
5
+ */
6
+ /**
7
+ * Validates whether the provided name is valid on NPM.
8
+ *
9
+ * @param {string} name
10
+ * @returns {ValidationResult}
11
+ */
12
+ export default function validate(name: string): ValidationResult;
13
+ export type ValidationResult = {
14
+ message: string | null;
15
+ isError: boolean;
16
+ };
@@ -0,0 +1,9 @@
1
+ export interface CreateOptions {
2
+ template: string;
3
+ pkgManager: string;
4
+ dir: string;
5
+ noTelemetry: boolean;
6
+ projectName: string;
7
+ verbose: boolean;
8
+ }
9
+ export declare function extractOptions(args: string[]): CreateOptions;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The available package managers a user can use.
3
+ * To add a new supported package manager, add the usage command to this list.
4
+ * The `PackageManager` type will be automatically updated as a result.
5
+ */
6
+ export declare const PKG_MANAGERS: readonly ["npm", "yarn", "pnpm", "bun"];
7
+ export type PackageManager = (typeof PKG_MANAGERS)[number];
@@ -1,2 +1,2 @@
1
- export declare const SUPPORTED_NODE_VERSIONS: string[];
2
- export declare function preRunChecks(): void;
1
+ import { Ora } from 'ora';
2
+ export declare function preRunChecks(spinner: Ora): void;
@@ -0,0 +1,10 @@
1
+ export declare const TextStyles: {
2
+ tinaOrange: import("chalk").ChalkInstance;
3
+ link: (url: string) => string;
4
+ cmd: import("chalk").ChalkInstance;
5
+ info: import("chalk").ChalkInstance;
6
+ success: import("chalk").ChalkInstance;
7
+ warn: import("chalk").ChalkInstance;
8
+ err: import("chalk").ChalkInstance;
9
+ bold: import("chalk").ChalkInstance;
10
+ };
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "create-tina-app",
3
- "version": "0.0.0-d80714b-20241205222511",
3
+ "version": "0.0.0-d813ac1-20251210222143",
4
+ "type": "module",
4
5
  "main": "dist/index.js",
5
6
  "files": [
6
7
  "dist",
@@ -31,21 +32,21 @@
31
32
  "devDependencies": {
32
33
  "@types/cross-spawn": "^6.0.6",
33
34
  "@types/fs-extra": "^11.0.4",
34
- "@types/node": "^22.9.0",
35
+ "@types/node": "^22.13.1",
35
36
  "@types/prompts": "^2.4.9",
36
37
  "@types/tar": "6.1.13",
37
- "typescript": "^5.6.3",
38
- "@tinacms/scripts": "1.3.1"
38
+ "typescript": "^5.7.3",
39
+ "@tinacms/scripts": "1.4.2"
39
40
  },
40
41
  "dependencies": {
41
- "chalk": "4.1.2",
42
+ "chalk": "^5.4.1",
42
43
  "commander": "^12.1.0",
43
- "cross-spawn": "^7.0.5",
44
- "fs-extra": "^11.2.0",
44
+ "cross-spawn": "^7.0.6",
45
+ "fs-extra": "^11.3.0",
46
+ "ora": "^8.2.0",
45
47
  "prompts": "^2.4.2",
46
48
  "tar": "7.4.0",
47
- "validate-npm-package-name": "^5.0.1",
48
- "@tinacms/metrics": "1.0.8"
49
+ "@tinacms/metrics": "2.0.1"
49
50
  },
50
51
  "scripts": {
51
52
  "types": "pnpm tsc",
@@ -1,20 +0,0 @@
1
- import chalk from 'chalk';
2
- export declare const TextStyles: {
3
- link: chalk.Chalk;
4
- cmd: chalk.Chalk;
5
- info: chalk.Chalk;
6
- success: chalk.Chalk;
7
- warn: chalk.Chalk;
8
- err: chalk.Chalk;
9
- bold: chalk.Chalk;
10
- };
11
- export declare class Logger {
12
- log(message: string): void;
13
- debug(message: string): void;
14
- info(message: string): void;
15
- success(message: string): void;
16
- cmd(message: string): void;
17
- warn(message: string): void;
18
- err(message: string): void;
19
- }
20
- export declare const log: Logger;