@tinacms/cli 2.2.5 → 2.2.6

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
@@ -32,11 +32,31 @@ Commands:
32
32
  schema:types [options] Generate a GraphQL query for your site's schema, (and optionally Typescript types)
33
33
  init [options] Add TinaCloud to an existing project
34
34
  audit [options] Audit your schema and the files to check for errors
35
+ doctor [options] Check direct TinaCMS dependencies against npm latest
35
36
  help [command] display help for command
36
37
  ```
37
38
 
38
39
  [See our docs](https://tina.io/docs/cli-overview/) for more information about the commands.
39
40
 
41
+ ### `doctor`
42
+
43
+ Use `tinacms doctor` to check whether the Tina packages installed in your
44
+ project are current against the npm `latest` dist-tag.
45
+
46
+ The command reads your `package.json`, resolves installed versions from
47
+ `node_modules` or the project lockfile, filters to Tina package names
48
+ (`tinacms`, `@tinacms/*`, `tinacms-*`, `next-tinacms-*`, and
49
+ `create-tina-app`), then prints the installed version next to the latest
50
+ published version.
51
+
52
+ ```bash
53
+ pnpm tinacms doctor
54
+ ```
55
+
56
+ Use `--json` for machine-readable output. The command exits with code `1` if a
57
+ Tina package is outdated or if a package version cannot be checked, which makes
58
+ it suitable for CI.
59
+
40
60
  ## Getting started
41
61
 
42
62
  The simplest way to get started is to add a `.tina/schema.ts` file
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { Cli, Builtins } from "clipanion";
3
3
 
4
4
  // package.json
5
- var version = "2.2.5";
5
+ var version = "2.2.6";
6
6
 
7
7
  // src/next/commands/dev-command/index.ts
8
8
  import path9 from "path";
@@ -691,7 +691,7 @@ var Codegen = class {
691
691
  import { resolve } from "@tinacms/datalayer";
692
692
  import type { TinaClient } from "tinacms/dist/client";
693
693
 
694
- import { queries } from "./types";
694
+ import { queries } from "${this.configManager.isUsingTs() ? "./types.ts" : "./types.js"}";
695
695
  import database from "../database";
696
696
 
697
697
  export async function databaseRequest({ query, variables, user }) {
@@ -757,7 +757,7 @@ export default databaseClient;
757
757
  const errorPolicy = this.configManager.config?.client?.errorPolicy;
758
758
  const apiURL = this.getApiURL();
759
759
  const clientString = `import { createClient } from "tinacms/dist/client";
760
- import { queries } from "./types";
760
+ import { queries } from "${this.configManager.isUsingTs() ? "./types.ts" : "./types.js"}";
761
761
  export const client = createClient({ ${this.noClientBuildCache === false ? `cacheDir: '${normalizePath(
762
762
  this.configManager.generatedCachePath
763
763
  )}', ` : ""}url: ${this.localContentBuild ? `process.env.TINA_LOCAL_URL || '${apiURL}'` : `'${apiURL}'`}, token: '${token}', queries, ${errorPolicy ? `errorPolicy: '${errorPolicy}'` : ""} });
@@ -894,6 +894,7 @@ var ConfigManager = class {
894
894
  rootPath;
895
895
  tinaFolderPath;
896
896
  isUsingLegacyFolder;
897
+ hasWarnedLegacyFolder = false;
897
898
  tinaConfigFilePath;
898
899
  tinaSpaPackagePath;
899
900
  contentRootPath;
@@ -957,6 +958,14 @@ var ConfigManager = class {
957
958
  async processConfig() {
958
959
  const require2 = createRequire(import.meta.url);
959
960
  this.tinaFolderPath = await this.getTinaFolderPath(this.rootPath);
961
+ if (this.isUsingLegacyFolder && !this.hasWarnedLegacyFolder) {
962
+ this.hasWarnedLegacyFolder = true;
963
+ logger.warn(
964
+ warnText(
965
+ "WARN: Detected legacy `.tina/` config folder. `tina-lock.json` is only generated for the new `tina/` layout, and TinaCloud requires it to index your schema. Migrate by renaming `.tina/` to `tina/` (the contents stay the same). See https://tina.io/docs/tina-folder/overview/."
966
+ )
967
+ );
968
+ }
960
969
  this.envFilePath = path4.resolve(
961
970
  path4.join(this.tinaFolderPath, "..", ".env")
962
971
  );
@@ -1129,28 +1138,41 @@ var ConfigManager = class {
1129
1138
  }
1130
1139
  printGeneratedClientFilePath() {
1131
1140
  if (this.isUsingTs()) {
1132
- return this.generatedClientTSFilePath.replace(`${this.rootPath}/`, "");
1141
+ return normalizePath2(
1142
+ path4.relative(this.rootPath, this.generatedClientTSFilePath)
1143
+ );
1133
1144
  }
1134
- return this.generatedClientJSFilePath.replace(`${this.rootPath}/`, "");
1145
+ return normalizePath2(
1146
+ path4.relative(this.rootPath, this.generatedClientJSFilePath)
1147
+ );
1135
1148
  }
1136
1149
  printGeneratedTypesFilePath() {
1137
- return this.generatedTypesTSFilePath.replace(`${this.rootPath}/`, "");
1150
+ return normalizePath2(
1151
+ path4.relative(this.rootPath, this.generatedTypesTSFilePath)
1152
+ );
1138
1153
  }
1139
1154
  printoutputHTMLFilePath() {
1140
- return this.outputHTMLFilePath.replace(`${this.publicFolderPath}/`, "");
1155
+ return normalizePath2(
1156
+ path4.relative(this.publicFolderPath, this.outputHTMLFilePath)
1157
+ );
1141
1158
  }
1142
1159
  printRelativePath(filename) {
1143
1160
  if (filename) {
1144
- return filename.replace(/\\/g, "/").replace(`${this.rootPath}/`, "");
1161
+ return normalizePath2(path4.relative(this.rootPath, filename));
1145
1162
  }
1146
1163
  throw `No path provided to print`;
1147
1164
  }
1148
1165
  printPrebuildFilePath() {
1149
- return this.prebuildFilePath.replace(/\\/g, "/").replace(`${this.rootPath}/${this.tinaFolderPath}/`, "");
1166
+ return normalizePath2(
1167
+ path4.relative(
1168
+ path4.join(this.rootPath, this.tinaFolderPath),
1169
+ this.prebuildFilePath
1170
+ )
1171
+ );
1150
1172
  }
1151
1173
  printContentRelativePath(filename) {
1152
1174
  if (filename) {
1153
- return filename.replace(/\\/g, "/").replace(`${this.contentRootPath}/`, "");
1175
+ return normalizePath2(path4.relative(this.contentRootPath, filename));
1154
1176
  }
1155
1177
  throw `No path provided to print`;
1156
1178
  }
@@ -1499,7 +1521,7 @@ var BaseCommand = class extends Command {
1499
1521
  pathFilter
1500
1522
  });
1501
1523
  const tinaPathUpdates = modified.filter(
1502
- (path16) => path16.startsWith(".tina/__generated__/_schema.json") || path16.startsWith("tina/tina-lock.json")
1524
+ (path17) => path17.startsWith(".tina/__generated__/_schema.json") || path17.startsWith("tina/tina-lock.json")
1503
1525
  );
1504
1526
  if (tinaPathUpdates.length > 0) {
1505
1527
  res = await database.indexContent({
@@ -6711,22 +6733,22 @@ var writeGeneratedFile = async ({
6711
6733
  content,
6712
6734
  typescript
6713
6735
  }) => {
6714
- const { exists, path: path16, parentPath } = generatedFile.resolve(typescript);
6736
+ const { exists, path: path17, parentPath } = generatedFile.resolve(typescript);
6715
6737
  if (exists) {
6716
6738
  if (overwrite) {
6717
- logger.info(`Overwriting file at ${path16}... \u2705`);
6718
- fs15.outputFileSync(path16, content);
6739
+ logger.info(`Overwriting file at ${path17}... \u2705`);
6740
+ fs15.outputFileSync(path17, content);
6719
6741
  } else {
6720
- logger.info(`Not overwriting file at ${path16}.`);
6742
+ logger.info(`Not overwriting file at ${path17}.`);
6721
6743
  logger.info(
6722
- logText(`Please add the following to ${path16}:
6744
+ logText(`Please add the following to ${path17}:
6723
6745
  ${indentText(content)}}`)
6724
6746
  );
6725
6747
  }
6726
6748
  } else {
6727
- logger.info(`Adding file at ${path16}... \u2705`);
6749
+ logger.info(`Adding file at ${path17}... \u2705`);
6728
6750
  await fs15.ensureDir(parentPath);
6729
- fs15.outputFileSync(path16, content);
6751
+ fs15.outputFileSync(path17, content);
6730
6752
  }
6731
6753
  };
6732
6754
  var addConfigFile = async ({
@@ -7121,6 +7143,356 @@ var SearchIndexCommand = class extends Command7 {
7121
7143
  }
7122
7144
  };
7123
7145
 
7146
+ // src/next/commands/doctor-command/index.ts
7147
+ import { Command as Command8, Option as Option8 } from "clipanion";
7148
+
7149
+ // src/next/commands/doctor-command/doctor.ts
7150
+ import path16 from "path";
7151
+ import fs16 from "fs-extra";
7152
+ import yaml3 from "js-yaml";
7153
+ var DEPENDENCY_TYPES = [
7154
+ "dependencies",
7155
+ "devDependencies",
7156
+ "optionalDependencies",
7157
+ "peerDependencies"
7158
+ ];
7159
+ var LOCAL_REFERENCE_PREFIXES = [
7160
+ "file:",
7161
+ "link:",
7162
+ "patch:",
7163
+ "portal:",
7164
+ "workspace:"
7165
+ ];
7166
+ function isTinaPackage(name2) {
7167
+ return name2 === "tinacms" || name2 === "create-tina-app" || name2.startsWith("@tinacms/") || name2.startsWith("tinacms-") || name2.startsWith("next-tinacms-");
7168
+ }
7169
+ function getTinaDependencies(packageJson) {
7170
+ const dependencies = /* @__PURE__ */ new Map();
7171
+ for (const dependencyType of DEPENDENCY_TYPES) {
7172
+ for (const [name2, declared] of Object.entries(
7173
+ packageJson[dependencyType] || {}
7174
+ )) {
7175
+ if (!isTinaPackage(name2) || dependencies.has(name2)) continue;
7176
+ dependencies.set(name2, { name: name2, declared, dependencyType });
7177
+ }
7178
+ }
7179
+ return [...dependencies.values()].sort(
7180
+ (a, b) => a.name.localeCompare(b.name)
7181
+ );
7182
+ }
7183
+ async function readProjectPackageJson(rootPath) {
7184
+ const packageJsonPath = path16.join(rootPath, "package.json");
7185
+ if (!await fs16.pathExists(packageJsonPath)) {
7186
+ throw new Error(`No package.json found at ${packageJsonPath}`);
7187
+ }
7188
+ return fs16.readJSON(packageJsonPath);
7189
+ }
7190
+ async function resolveInstalledVersions({
7191
+ rootPath,
7192
+ dependencies
7193
+ }) {
7194
+ const lockfileVersions = await readLockfileVersions(rootPath);
7195
+ return Promise.all(
7196
+ dependencies.map(async (dependency) => {
7197
+ const installed = await readNodeModulesVersion(rootPath, dependency.name) || lockfileVersions.get(dependency.name);
7198
+ return { ...dependency, installed };
7199
+ })
7200
+ );
7201
+ }
7202
+ async function fetchLatestVersion(packageName, timeoutMs) {
7203
+ const controller = new AbortController();
7204
+ const timeout2 = setTimeout(() => controller.abort(), timeoutMs);
7205
+ try {
7206
+ const response = await fetch(
7207
+ `https://registry.npmjs.org/${encodeURIComponent(packageName)}`,
7208
+ { signal: controller.signal }
7209
+ );
7210
+ if (!response.ok) {
7211
+ throw new Error(`npm registry returned ${response.status}`);
7212
+ }
7213
+ const body = await response.json();
7214
+ const latest = body?.["dist-tags"]?.latest;
7215
+ if (typeof latest !== "string" || latest.length === 0) {
7216
+ throw new Error("npm registry response did not include dist-tags.latest");
7217
+ }
7218
+ return latest;
7219
+ } finally {
7220
+ clearTimeout(timeout2);
7221
+ }
7222
+ }
7223
+ function classifyDependency(dependency, latest, error) {
7224
+ if (!dependency.installed) {
7225
+ return {
7226
+ ...dependency,
7227
+ latest,
7228
+ status: "unknown",
7229
+ error: "Installed version could not be resolved from node_modules or lockfile"
7230
+ };
7231
+ }
7232
+ if (isLocalReference(dependency.installed)) {
7233
+ return {
7234
+ ...dependency,
7235
+ latest,
7236
+ status: "local",
7237
+ error
7238
+ };
7239
+ }
7240
+ if (!latest || error) {
7241
+ return {
7242
+ ...dependency,
7243
+ latest,
7244
+ status: "unknown",
7245
+ error
7246
+ };
7247
+ }
7248
+ return {
7249
+ ...dependency,
7250
+ latest,
7251
+ status: normalizeVersion(dependency.installed) === normalizeVersion(latest) ? "current" : "outdated"
7252
+ };
7253
+ }
7254
+ async function checkTinaDependencies({
7255
+ dependencies,
7256
+ fetchLatest
7257
+ }) {
7258
+ return Promise.all(
7259
+ dependencies.map(async (dependency) => {
7260
+ try {
7261
+ const latest = await fetchLatest(dependency.name);
7262
+ return classifyDependency(dependency, latest);
7263
+ } catch (error) {
7264
+ return classifyDependency(
7265
+ dependency,
7266
+ void 0,
7267
+ error instanceof Error ? error.message : String(error)
7268
+ );
7269
+ }
7270
+ })
7271
+ );
7272
+ }
7273
+ function formatDoctorTable(results) {
7274
+ const rows = [
7275
+ ["Package", "Type", "Declared", "Installed", "Latest", "Status"],
7276
+ ...results.map((result) => [
7277
+ result.name,
7278
+ result.dependencyType,
7279
+ result.declared,
7280
+ result.installed || "-",
7281
+ result.latest || "-",
7282
+ formatStatus(result)
7283
+ ])
7284
+ ];
7285
+ const widths = rows[0].map(
7286
+ (_, column) => Math.max(...rows.map((row) => row[column].length))
7287
+ );
7288
+ return rows.map((row, rowIndex) => {
7289
+ const line = row.map((cell, column) => cell.padEnd(widths[column])).join(" ");
7290
+ return rowIndex === 0 ? `${line}
7291
+ ${widths.map((w) => "-".repeat(w)).join(" ")}` : line;
7292
+ }).join("\n");
7293
+ }
7294
+ function formatStatus(result) {
7295
+ if (result.status === "outdated") return "OUTDATED";
7296
+ if (result.status === "local") return "LOCAL";
7297
+ if (result.status === "unknown")
7298
+ return `UNKNOWN${result.error ? ` (${result.error})` : ""}`;
7299
+ return "CURRENT";
7300
+ }
7301
+ function normalizeVersion(version2) {
7302
+ return version2.trim().replace(/^v/, "").split("(")[0].trim();
7303
+ }
7304
+ function isLocalReference(version2) {
7305
+ const normalized = version2.trim();
7306
+ return LOCAL_REFERENCE_PREFIXES.some(
7307
+ (prefix) => normalized.startsWith(prefix)
7308
+ );
7309
+ }
7310
+ async function readNodeModulesVersion(rootPath, packageName) {
7311
+ const packageJsonPath = path16.join(
7312
+ rootPath,
7313
+ "node_modules",
7314
+ ...packageName.split("/"),
7315
+ "package.json"
7316
+ );
7317
+ if (!await fs16.pathExists(packageJsonPath)) return void 0;
7318
+ const packageJson = await fs16.readJSON(packageJsonPath);
7319
+ return typeof packageJson.version === "string" ? packageJson.version : void 0;
7320
+ }
7321
+ async function readLockfileVersions(rootPath) {
7322
+ const readers = [
7323
+ readPackageLockVersions,
7324
+ readPnpmLockVersions,
7325
+ readYarnLockVersions
7326
+ ];
7327
+ for (const reader of readers) {
7328
+ const versions = await reader(rootPath);
7329
+ if (versions.size > 0) return versions;
7330
+ }
7331
+ return /* @__PURE__ */ new Map();
7332
+ }
7333
+ async function readPackageLockVersions(rootPath) {
7334
+ const lockfilePath = path16.join(rootPath, "package-lock.json");
7335
+ const versions = /* @__PURE__ */ new Map();
7336
+ if (!await fs16.pathExists(lockfilePath)) return versions;
7337
+ const lockfile = await fs16.readJSON(lockfilePath);
7338
+ for (const [key, value] of Object.entries(lockfile.packages || {})) {
7339
+ if (!key.startsWith("node_modules/")) continue;
7340
+ const name2 = key.replace(/^node_modules\//, "");
7341
+ const version2 = value.version;
7342
+ if (isTinaPackage(name2) && typeof version2 === "string") {
7343
+ versions.set(name2, version2);
7344
+ }
7345
+ }
7346
+ for (const [name2, value] of Object.entries(lockfile.dependencies || {})) {
7347
+ const version2 = value.version;
7348
+ if (isTinaPackage(name2) && typeof version2 === "string") {
7349
+ versions.set(name2, version2);
7350
+ }
7351
+ }
7352
+ return versions;
7353
+ }
7354
+ async function readPnpmLockVersions(rootPath) {
7355
+ const lockfilePath = path16.join(rootPath, "pnpm-lock.yaml");
7356
+ const versions = /* @__PURE__ */ new Map();
7357
+ if (!await fs16.pathExists(lockfilePath)) return versions;
7358
+ const lockfile = yaml3.load(await fs16.readFile(lockfilePath, "utf8"));
7359
+ const rootImporter = lockfile?.importers?.["."];
7360
+ for (const dependencyType of DEPENDENCY_TYPES) {
7361
+ for (const [name2, value] of Object.entries(
7362
+ rootImporter?.[dependencyType] || {}
7363
+ )) {
7364
+ if (!isTinaPackage(name2)) continue;
7365
+ const version2 = typeof value === "string" ? value : typeof value.version === "string" ? value.version : void 0;
7366
+ if (version2) versions.set(name2, normalizeInstalledVersion(version2));
7367
+ }
7368
+ }
7369
+ for (const key of Object.keys(lockfile?.packages || {})) {
7370
+ const parsed = parsePnpmPackageKey(key);
7371
+ if (parsed && isTinaPackage(parsed.name) && !versions.has(parsed.name)) {
7372
+ versions.set(parsed.name, parsed.version);
7373
+ }
7374
+ }
7375
+ return versions;
7376
+ }
7377
+ async function readYarnLockVersions(rootPath) {
7378
+ const lockfilePath = path16.join(rootPath, "yarn.lock");
7379
+ const versions = /* @__PURE__ */ new Map();
7380
+ if (!await fs16.pathExists(lockfilePath)) return versions;
7381
+ const contents = await fs16.readFile(lockfilePath, "utf8");
7382
+ if (contents.includes("__metadata:")) {
7383
+ return readYarnBerryLockVersions(contents);
7384
+ }
7385
+ const lines = contents.split("\n");
7386
+ let activeNames = [];
7387
+ for (const line of lines) {
7388
+ if (line.length > 0 && !line.startsWith(" ") && line.includes("@")) {
7389
+ activeNames = extractYarnPackageNames(line);
7390
+ continue;
7391
+ }
7392
+ const version2 = line.match(/^\s+version\s+"([^"]+)"/)?.[1];
7393
+ if (!version2) continue;
7394
+ for (const name2 of activeNames) {
7395
+ if (isTinaPackage(name2) && !versions.has(name2)) {
7396
+ versions.set(name2, version2);
7397
+ }
7398
+ }
7399
+ }
7400
+ return versions;
7401
+ }
7402
+ function readYarnBerryLockVersions(contents) {
7403
+ const versions = /* @__PURE__ */ new Map();
7404
+ const lockfile = yaml3.load(contents);
7405
+ for (const [descriptor, value] of Object.entries(lockfile || {})) {
7406
+ if (descriptor === "__metadata") continue;
7407
+ const name2 = extractYarnBerryPackageName(descriptor);
7408
+ const version2 = getYarnBerryInstalledVersion(descriptor, value?.version);
7409
+ if (name2 && isTinaPackage(name2) && version2) {
7410
+ versions.set(name2, version2);
7411
+ }
7412
+ }
7413
+ return versions;
7414
+ }
7415
+ function parsePnpmPackageKey(key) {
7416
+ const normalized = key.replace(/^\//, "");
7417
+ const match = normalized.match(/^(@[^/]+\/[^@]+|[^@/]+)@([^(/]+)/);
7418
+ if (!match) return void 0;
7419
+ return { name: match[1], version: normalizeVersion(match[2]) };
7420
+ }
7421
+ function normalizeInstalledVersion(version2) {
7422
+ return isLocalReference(version2) ? version2 : normalizeVersion(version2);
7423
+ }
7424
+ function extractYarnPackageNames(line) {
7425
+ return line.replace(/:$/, "").split(/,\s*/).map((descriptor) => descriptor.trim().replace(/^"|"$/g, "")).map((descriptor) => {
7426
+ if (descriptor.startsWith("@")) {
7427
+ const [, scope, name2] = descriptor.match(/^(@[^/]+)\/([^@]+)/) || [];
7428
+ return scope && name2 ? `${scope}/${name2}` : descriptor;
7429
+ }
7430
+ return descriptor.split("@")[0];
7431
+ }).filter(Boolean);
7432
+ }
7433
+ function extractYarnBerryPackageName(descriptor) {
7434
+ if (descriptor.startsWith("@")) {
7435
+ return descriptor.match(/^(@[^/]+\/[^@]+)/)?.[1];
7436
+ }
7437
+ return descriptor.split("@")[0] || void 0;
7438
+ }
7439
+ function getYarnBerryInstalledVersion(descriptor, version2) {
7440
+ const name2 = extractYarnBerryPackageName(descriptor);
7441
+ const reference = name2 ? descriptor.slice(name2.length + 1) : void 0;
7442
+ if (reference && isLocalReference(reference)) return reference;
7443
+ return typeof version2 === "string" ? normalizeInstalledVersion(version2) : void 0;
7444
+ }
7445
+
7446
+ // src/next/commands/doctor-command/index.ts
7447
+ var DoctorCommand = class extends Command8 {
7448
+ static paths = [["doctor"]];
7449
+ rootPath = Option8.String("--rootPath", {
7450
+ description: "Specify the root directory to inspect (defaults to current working directory)"
7451
+ });
7452
+ json = Option8.Boolean("--json", false, {
7453
+ description: "Print machine-readable JSON output"
7454
+ });
7455
+ timeout = Option8.String("--timeout", "5000", {
7456
+ description: "npm registry request timeout in milliseconds"
7457
+ });
7458
+ static usage = Command8.Usage({
7459
+ category: "Commands",
7460
+ description: "Check direct TinaCMS dependencies against npm latest"
7461
+ });
7462
+ async catch(error) {
7463
+ const message = error instanceof Error ? error.message : String(error);
7464
+ logger.error(`Error occurred during tinacms doctor: ${message}`);
7465
+ process.exit(1);
7466
+ }
7467
+ async execute() {
7468
+ const rootPath = this.rootPath || process.cwd();
7469
+ const timeoutMs = Number(this.timeout);
7470
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
7471
+ logger.error("--timeout must be a positive number");
7472
+ return 1;
7473
+ }
7474
+ const packageJson = await readProjectPackageJson(rootPath);
7475
+ const dependencies = await resolveInstalledVersions({
7476
+ rootPath,
7477
+ dependencies: getTinaDependencies(packageJson)
7478
+ });
7479
+ const results = await checkTinaDependencies({
7480
+ dependencies,
7481
+ fetchLatest: (name2) => fetchLatestVersion(name2, timeoutMs)
7482
+ });
7483
+ if (this.json) {
7484
+ logger.info(JSON.stringify({ rootPath, results }, null, 2));
7485
+ } else if (results.length === 0) {
7486
+ logger.info("No direct TinaCMS dependencies found.");
7487
+ } else {
7488
+ logger.info(formatDoctorTable(results));
7489
+ }
7490
+ const hasOutdated = results.some((result) => result.status === "outdated");
7491
+ const hasUnknown = results.some((result) => result.status === "unknown");
7492
+ return hasOutdated || hasUnknown ? 1 : 0;
7493
+ }
7494
+ };
7495
+
7124
7496
  // src/index.ts
7125
7497
  var cli = new Cli({
7126
7498
  binaryName: `tinacms`,
@@ -7133,6 +7505,7 @@ cli.register(AuditCommand);
7133
7505
  cli.register(InitCommand);
7134
7506
  cli.register(CodemodCommand);
7135
7507
  cli.register(SearchIndexCommand);
7508
+ cli.register(DoctorCommand);
7136
7509
  cli.register(Builtins.DefinitionsCommand);
7137
7510
  cli.register(Builtins.HelpCommand);
7138
7511
  cli.register(Builtins.VersionCommand);
@@ -0,0 +1,34 @@
1
+ export type DoctorStatus = 'current' | 'local' | 'outdated' | 'unknown';
2
+ export type TinaDependency = {
3
+ name: string;
4
+ declared: string;
5
+ dependencyType: DependencyType;
6
+ installed?: string;
7
+ };
8
+ export type DoctorResult = TinaDependency & {
9
+ latest?: string;
10
+ status: DoctorStatus;
11
+ error?: string;
12
+ };
13
+ type PackageJson = {
14
+ dependencies?: Record<string, string>;
15
+ devDependencies?: Record<string, string>;
16
+ optionalDependencies?: Record<string, string>;
17
+ peerDependencies?: Record<string, string>;
18
+ };
19
+ type DependencyType = 'dependencies' | 'devDependencies' | 'optionalDependencies' | 'peerDependencies';
20
+ export declare function isTinaPackage(name: string): boolean;
21
+ export declare function getTinaDependencies(packageJson: PackageJson): TinaDependency[];
22
+ export declare function readProjectPackageJson(rootPath: string): Promise<any>;
23
+ export declare function resolveInstalledVersions({ rootPath, dependencies, }: {
24
+ rootPath: string;
25
+ dependencies: TinaDependency[];
26
+ }): Promise<TinaDependency[]>;
27
+ export declare function fetchLatestVersion(packageName: string, timeoutMs: number): Promise<string>;
28
+ export declare function classifyDependency(dependency: TinaDependency, latest?: string, error?: string): DoctorResult;
29
+ export declare function checkTinaDependencies({ dependencies, fetchLatest, }: {
30
+ dependencies: TinaDependency[];
31
+ fetchLatest: (name: string) => Promise<string>;
32
+ }): Promise<DoctorResult[]>;
33
+ export declare function formatDoctorTable(results: DoctorResult[]): string;
34
+ export {};
@@ -0,0 +1,10 @@
1
+ import { Command } from 'clipanion';
2
+ export declare class DoctorCommand extends Command {
3
+ static paths: string[][];
4
+ rootPath: string;
5
+ json: boolean;
6
+ timeout: string;
7
+ static usage: import("clipanion").Usage;
8
+ catch(error: unknown): Promise<void>;
9
+ execute(): Promise<number | void>;
10
+ }
@@ -8,6 +8,7 @@ export declare class ConfigManager {
8
8
  rootPath: string;
9
9
  tinaFolderPath: string;
10
10
  isUsingLegacyFolder: boolean;
11
+ private hasWarnedLegacyFolder;
11
12
  tinaConfigFilePath: string;
12
13
  tinaSpaPackagePath: string;
13
14
  contentRootPath?: string;
@@ -63,12 +64,12 @@ export declare class ConfigManager {
63
64
  minor: string;
64
65
  patch: string;
65
66
  };
66
- printGeneratedClientFilePath(): string;
67
- printGeneratedTypesFilePath(): string;
68
- printoutputHTMLFilePath(): string;
69
- printRelativePath(filename: string): string;
70
- printPrebuildFilePath(): string;
71
- printContentRelativePath(filename: string): string;
67
+ printGeneratedClientFilePath(): any;
68
+ printGeneratedTypesFilePath(): any;
69
+ printoutputHTMLFilePath(): any;
70
+ printRelativePath(filename: string): any;
71
+ printPrebuildFilePath(): any;
72
+ printContentRelativePath(filename: string): any;
72
73
  /**
73
74
  * Given a filepath without an extension, find the first match (eg. tsx, ts, jsx, js)
74
75
  */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tinacms/cli",
3
3
  "type": "module",
4
- "version": "2.2.5",
4
+ "version": "2.2.6",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
7
7
  "files": [
@@ -41,7 +41,7 @@
41
41
  "@types/progress": "^2.0.7",
42
42
  "@types/prompts": "^2.4.9",
43
43
  "jest": "^29.7.0",
44
- "@tinacms/scripts": "1.6.0"
44
+ "@tinacms/scripts": "1.6.1"
45
45
  },
46
46
  "dependencies": {
47
47
  "@graphql-codegen/core": "^2.6.8",
@@ -88,12 +88,12 @@
88
88
  "vite": "^4.5.9",
89
89
  "yup": "^1.6.1",
90
90
  "zod": "^3.24.2",
91
- "@tinacms/app": "2.4.5",
92
- "@tinacms/graphql": "2.3.0",
93
- "@tinacms/schema-tools": "2.7.3",
91
+ "@tinacms/app": "2.4.6",
92
+ "@tinacms/graphql": "2.3.1",
94
93
  "@tinacms/metrics": "2.0.1",
95
- "@tinacms/search": "1.2.12",
96
- "tinacms": "3.7.5"
94
+ "@tinacms/search": "1.2.13",
95
+ "tinacms": "3.7.6",
96
+ "@tinacms/schema-tools": "2.7.4"
97
97
  },
98
98
  "publishConfig": {
99
99
  "registry": "https://registry.npmjs.org"