@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
|
+
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
|
|
1141
|
+
return normalizePath2(
|
|
1142
|
+
path4.relative(this.rootPath, this.generatedClientTSFilePath)
|
|
1143
|
+
);
|
|
1133
1144
|
}
|
|
1134
|
-
return
|
|
1145
|
+
return normalizePath2(
|
|
1146
|
+
path4.relative(this.rootPath, this.generatedClientJSFilePath)
|
|
1147
|
+
);
|
|
1135
1148
|
}
|
|
1136
1149
|
printGeneratedTypesFilePath() {
|
|
1137
|
-
return
|
|
1150
|
+
return normalizePath2(
|
|
1151
|
+
path4.relative(this.rootPath, this.generatedTypesTSFilePath)
|
|
1152
|
+
);
|
|
1138
1153
|
}
|
|
1139
1154
|
printoutputHTMLFilePath() {
|
|
1140
|
-
return
|
|
1155
|
+
return normalizePath2(
|
|
1156
|
+
path4.relative(this.publicFolderPath, this.outputHTMLFilePath)
|
|
1157
|
+
);
|
|
1141
1158
|
}
|
|
1142
1159
|
printRelativePath(filename) {
|
|
1143
1160
|
if (filename) {
|
|
1144
|
-
return
|
|
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
|
|
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
|
|
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
|
-
(
|
|
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:
|
|
6736
|
+
const { exists, path: path17, parentPath } = generatedFile.resolve(typescript);
|
|
6715
6737
|
if (exists) {
|
|
6716
6738
|
if (overwrite) {
|
|
6717
|
-
logger.info(`Overwriting file at ${
|
|
6718
|
-
fs15.outputFileSync(
|
|
6739
|
+
logger.info(`Overwriting file at ${path17}... \u2705`);
|
|
6740
|
+
fs15.outputFileSync(path17, content);
|
|
6719
6741
|
} else {
|
|
6720
|
-
logger.info(`Not overwriting file at ${
|
|
6742
|
+
logger.info(`Not overwriting file at ${path17}.`);
|
|
6721
6743
|
logger.info(
|
|
6722
|
-
logText(`Please add the following to ${
|
|
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 ${
|
|
6749
|
+
logger.info(`Adding file at ${path17}... \u2705`);
|
|
6728
6750
|
await fs15.ensureDir(parentPath);
|
|
6729
|
-
fs15.outputFileSync(
|
|
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():
|
|
67
|
-
printGeneratedTypesFilePath():
|
|
68
|
-
printoutputHTMLFilePath():
|
|
69
|
-
printRelativePath(filename: string):
|
|
70
|
-
printPrebuildFilePath():
|
|
71
|
-
printContentRelativePath(filename: 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.
|
|
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.
|
|
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.
|
|
92
|
-
"@tinacms/graphql": "2.3.
|
|
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.
|
|
96
|
-
"tinacms": "3.7.
|
|
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"
|