@sanity/cli 6.3.2 → 6.4.0
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 +16 -10
- package/dist/actions/init/bootstrapLocalTemplate.js +16 -1
- package/dist/actions/init/bootstrapLocalTemplate.js.map +1 -1
- package/dist/actions/init/initApp.js +72 -0
- package/dist/actions/init/initApp.js.map +1 -0
- package/dist/actions/init/initHelpers.js +37 -0
- package/dist/actions/init/initHelpers.js.map +1 -0
- package/dist/actions/init/initNextJs.js +246 -0
- package/dist/actions/init/initNextJs.js.map +1 -0
- package/dist/actions/init/initStudio.js +127 -0
- package/dist/actions/init/initStudio.js.map +1 -0
- package/dist/actions/init/scaffoldTemplate.js +114 -0
- package/dist/actions/init/scaffoldTemplate.js.map +1 -0
- package/dist/actions/init/templates/appQuickstart.js +2 -1
- package/dist/actions/init/templates/appQuickstart.js.map +1 -1
- package/dist/actions/init/templates/appSanityUi.js +2 -1
- package/dist/actions/init/templates/appSanityUi.js.map +1 -1
- package/dist/actions/init/templates/shopify.js +6 -6
- package/dist/actions/init/templates/shopify.js.map +1 -1
- package/dist/actions/init/templates/shopifyOnline.js +2 -2
- package/dist/actions/init/templates/shopifyOnline.js.map +1 -1
- package/dist/actions/mcp/detectAvailableEditors.js +16 -3
- package/dist/actions/mcp/detectAvailableEditors.js.map +1 -1
- package/dist/actions/mcp/editorConfigs.js +192 -132
- package/dist/actions/mcp/editorConfigs.js.map +1 -1
- package/dist/actions/mcp/setupMCP.js +4 -1
- package/dist/actions/mcp/setupMCP.js.map +1 -1
- package/dist/actions/mcp/writeMCPConfig.js +2 -2
- package/dist/actions/mcp/writeMCPConfig.js.map +1 -1
- package/dist/actions/schema/extractSchema.js +5 -7
- package/dist/actions/schema/extractSchema.js.map +1 -1
- package/dist/commands/datasets/copy.js +14 -0
- package/dist/commands/datasets/copy.js.map +1 -1
- package/dist/commands/init.js +149 -482
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/mcp/configure.js +1 -1
- package/dist/commands/mcp/configure.js.map +1 -1
- package/dist/hooks/prerun/injectEnvVariables.js +3 -5
- package/dist/hooks/prerun/injectEnvVariables.js.map +1 -1
- package/dist/services/datasets.js +2 -1
- package/dist/services/datasets.js.map +1 -1
- package/dist/telemetry/init.telemetry.js.map +1 -1
- package/dist/util/packageManager/installationInfo/detectPackages.js +13 -7
- package/dist/util/packageManager/installationInfo/detectPackages.js.map +1 -1
- package/dist/util/update/fetchUpdateInfo.js +40 -0
- package/dist/util/update/fetchUpdateInfo.js.map +1 -0
- package/dist/util/update/fetchUpdateInfo.worker.js +19 -0
- package/dist/util/update/fetchUpdateInfo.worker.js.map +1 -0
- package/dist/util/update/getRunnerUpdateCommand.js +33 -0
- package/dist/util/update/getRunnerUpdateCommand.js.map +1 -0
- package/dist/util/update/getUpdateCommand.js +6 -7
- package/dist/util/update/getUpdateCommand.js.map +1 -1
- package/dist/util/update/packageRunner.js +10 -0
- package/dist/util/update/packageRunner.js.map +1 -0
- package/dist/util/update/resolveRunnerPackage.js +45 -0
- package/dist/util/update/resolveRunnerPackage.js.map +1 -0
- package/dist/util/update/resolveUpdateTarget.js +31 -0
- package/dist/util/update/resolveUpdateTarget.js.map +1 -0
- package/dist/util/update/showNotificationUpdate.js +8 -6
- package/dist/util/update/showNotificationUpdate.js.map +1 -1
- package/dist/util/update/updateChecker.js +73 -38
- package/dist/util/update/updateChecker.js.map +1 -1
- package/oclif.manifest.json +540 -525
- package/package.json +6 -6
- package/templates/app-quickstart/src/App.tsx +2 -2
- package/templates/app-sanity-ui/src/App.tsx +2 -2
- package/templates/shopify/schemaTypes/objects/hotspot/imageWithProductHotspotsType.ts +1 -1
- package/dist/util/update/fetchLatestVersion.js +0 -21
- package/dist/util/update/fetchLatestVersion.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/telemetry/init.telemetry.ts"],"sourcesContent":["import {defineTrace} from '@sanity/telemetry'\n\nimport {type EditorName} from '../actions/mcp/editorConfigs.js'\n\ninterface StartStep {\n flags: Record<string, boolean | number | string | undefined>\n step: 'start'\n}\n\ninterface LoginStep {\n step: 'login'\n\n alreadyLoggedIn?: boolean\n}\n\ninterface FetchJourneyConfigStep {\n datasetName: string\n displayName: string\n isFirstProject: boolean\n projectId: string\n step: 'fetchJourneyConfig'\n}\n\ninterface CreateOrSelectProjectStep {\n projectId: string\n selectedOption: 'create' | 'none' | 'select'\n step: 'createOrSelectProject'\n}\n\ninterface CreateOrSelectDatasetStep {\n datasetName: string\n selectedOption: 'create' | 'none' | 'select'\n step: 'createOrSelectDataset'\n visibility: 'private' | 'public'\n}\n\ninterface UseDefaultPlanCoupon {\n selectedOption: 'no' | 'yes'\n step: 'useDefaultPlanCoupon'\n\n coupon?: string\n}\n\ninterface UseDefaultPlanId {\n selectedOption: 'no' | 'yes'\n step: 'useDefaultPlanId'\n\n planId?: string\n}\n\ninterface UseDetectedFrameworkStep {\n selectedOption: 'no' | 'yes'\n step: 'useDetectedFramework'\n\n detectedFramework?: string\n}\n\ninterface UseTypeScriptStep {\n selectedOption: 'no' | 'yes'\n step: 'useTypeScript'\n}\n\ninterface SelectTemplateStep {\n selectedOption: string\n step: 'selectProjectTemplate'\n}\ninterface ImportTemplateDatasetStep {\n selectedOption: 'no' | 'yes'\n step: 'importTemplateDataset'\n}\n\ninterface SendCommunityInviteStep {\n selectedOption: 'no' | 'yes'\n step: 'sendCommunityInvite'\n}\n\ninterface SelectPackageManagerStep {\n selectedOption: string\n step: 'selectPackageManager'\n}\n\ninterface MCPSetupStep {\n configuredEditors: EditorName[]\n detectedEditors: EditorName[]\n skipped: boolean\n step: 'mcpSetup'\n}\n\nexport type InitStepResult =\n | CreateOrSelectDatasetStep\n | CreateOrSelectProjectStep\n | FetchJourneyConfigStep\n | ImportTemplateDatasetStep\n | LoginStep\n | MCPSetupStep\n | SelectPackageManagerStep\n | SelectTemplateStep\n | SendCommunityInviteStep\n | StartStep\n | UseDefaultPlanCoupon\n | UseDefaultPlanId\n | UseDetectedFrameworkStep\n | UseTypeScriptStep\n\nexport const CLIInitStepCompleted = defineTrace<InitStepResult>({\n description: 'User completed a step in the CLI init flow',\n name: 'CLI Init Step Completed',\n version: 1,\n})\n"],"names":["defineTrace","CLIInitStepCompleted","description","name","version"],"mappings":"AAAA,SAAQA,WAAW,QAAO,oBAAmB;
|
|
1
|
+
{"version":3,"sources":["../../src/telemetry/init.telemetry.ts"],"sourcesContent":["import {defineTrace} from '@sanity/telemetry'\n\nimport {type EditorName} from '../actions/mcp/editorConfigs.js'\n\ninterface StartStep {\n flags: Record<string, boolean | number | string | undefined>\n step: 'start'\n}\n\ninterface LoginStep {\n step: 'login'\n\n alreadyLoggedIn?: boolean\n}\n\ninterface FetchJourneyConfigStep {\n datasetName: string\n displayName: string\n isFirstProject: boolean\n projectId: string\n step: 'fetchJourneyConfig'\n}\n\ninterface CreateOrSelectProjectStep {\n projectId: string\n selectedOption: 'create' | 'none' | 'select'\n step: 'createOrSelectProject'\n}\n\ninterface CreateOrSelectDatasetStep {\n datasetName: string\n selectedOption: 'create' | 'none' | 'select'\n step: 'createOrSelectDataset'\n visibility: 'private' | 'public'\n}\n\ninterface UseDefaultPlanCoupon {\n selectedOption: 'no' | 'yes'\n step: 'useDefaultPlanCoupon'\n\n coupon?: string\n}\n\ninterface UseDefaultPlanId {\n selectedOption: 'no' | 'yes'\n step: 'useDefaultPlanId'\n\n planId?: string\n}\n\ninterface UseDetectedFrameworkStep {\n selectedOption: 'no' | 'yes'\n step: 'useDetectedFramework'\n\n detectedFramework?: string\n}\n\ninterface UseTypeScriptStep {\n selectedOption: 'no' | 'yes'\n step: 'useTypeScript'\n}\n\ninterface SelectTemplateStep {\n selectedOption: string\n step: 'selectProjectTemplate'\n}\ninterface ImportTemplateDatasetStep {\n selectedOption: 'no' | 'yes'\n step: 'importTemplateDataset'\n}\n\ninterface SendCommunityInviteStep {\n selectedOption: 'no' | 'yes'\n step: 'sendCommunityInvite'\n}\n\ninterface SelectPackageManagerStep {\n selectedOption: string\n step: 'selectPackageManager'\n}\n\ninterface MCPSetupStep {\n configuredEditors: EditorName[]\n detectedEditors: EditorName[]\n skipped: boolean\n step: 'mcpSetup'\n}\n\ninterface ConfigureAppProjectStep {\n selectedOption: 'create' | 'existing' | 'skip'\n step: 'configureAppProject'\n}\n\nexport type InitStepResult =\n | ConfigureAppProjectStep\n | CreateOrSelectDatasetStep\n | CreateOrSelectProjectStep\n | FetchJourneyConfigStep\n | ImportTemplateDatasetStep\n | LoginStep\n | MCPSetupStep\n | SelectPackageManagerStep\n | SelectTemplateStep\n | SendCommunityInviteStep\n | StartStep\n | UseDefaultPlanCoupon\n | UseDefaultPlanId\n | UseDetectedFrameworkStep\n | UseTypeScriptStep\n\nexport const CLIInitStepCompleted = defineTrace<InitStepResult>({\n description: 'User completed a step in the CLI init flow',\n name: 'CLI Init Step Completed',\n version: 1,\n})\n"],"names":["defineTrace","CLIInitStepCompleted","description","name","version"],"mappings":"AAAA,SAAQA,WAAW,QAAO,oBAAmB;AA8G7C,OAAO,MAAMC,uBAAuBD,YAA4B;IAC9DE,aAAa;IACbC,MAAM;IACNC,SAAS;AACX,GAAE"}
|
|
@@ -6,11 +6,14 @@ import { readJsonFile } from './readJsonFile.js';
|
|
|
6
6
|
import { resolveVersionRange } from './resolveVersionRange.js';
|
|
7
7
|
/**
|
|
8
8
|
* Finds where a package is declared in the workspace, walking up from startDir.
|
|
9
|
-
* Resolves catalog: protocol if used.
|
|
9
|
+
* Resolves catalog: protocol if used (requires workspaceInfo).
|
|
10
|
+
*
|
|
11
|
+
* When workspaceInfo is omitted, walks to the filesystem root instead of stopping
|
|
12
|
+
* at the workspace root, and returns the raw declared range without catalog resolution.
|
|
10
13
|
*/ export async function findPackageDeclaration(packageName, startDir, workspaceInfo) {
|
|
11
14
|
let currentDir = path.resolve(startDir);
|
|
12
15
|
const fsRoot = path.parse(currentDir).root;
|
|
13
|
-
// Walk up until we pass the workspace root
|
|
16
|
+
// Walk up until we pass the workspace root (or filesystem root if no workspace info)
|
|
14
17
|
while(currentDir !== fsRoot){
|
|
15
18
|
const packageJsonPath = path.join(currentDir, 'package.json');
|
|
16
19
|
const packageJson = await readJsonFile(packageJsonPath);
|
|
@@ -27,7 +30,7 @@ import { resolveVersionRange } from './resolveVersionRange.js';
|
|
|
27
30
|
const deps = packageJson[depType];
|
|
28
31
|
if (deps && packageName in deps) {
|
|
29
32
|
const declaredVersionRange = deps[packageName];
|
|
30
|
-
const versionRange = await resolveVersionRange(declaredVersionRange, packageName, workspaceInfo);
|
|
33
|
+
const versionRange = workspaceInfo ? await resolveVersionRange(declaredVersionRange, packageName, workspaceInfo) : declaredVersionRange;
|
|
31
34
|
return {
|
|
32
35
|
declaredVersionRange,
|
|
33
36
|
dependencyType: depType,
|
|
@@ -37,8 +40,8 @@ import { resolveVersionRange } from './resolveVersionRange.js';
|
|
|
37
40
|
}
|
|
38
41
|
}
|
|
39
42
|
}
|
|
40
|
-
// Stop at workspace root
|
|
41
|
-
if (currentDir === workspaceInfo.root) {
|
|
43
|
+
// Stop at workspace root if provided, otherwise continue to filesystem root
|
|
44
|
+
if (workspaceInfo && currentDir === workspaceInfo.root) {
|
|
42
45
|
break;
|
|
43
46
|
}
|
|
44
47
|
currentDir = path.dirname(currentDir);
|
|
@@ -85,6 +88,9 @@ import { resolveVersionRange } from './resolveVersionRange.js';
|
|
|
85
88
|
* Also extracts \@sanity/cli dependency range from sanity package if applicable.
|
|
86
89
|
*
|
|
87
90
|
* Handles both hoisted (npm/yarn) and nested (pnpm) node_modules structures.
|
|
91
|
+
*
|
|
92
|
+
* When workspaceRoot is omitted, walks to the filesystem root instead of stopping
|
|
93
|
+
* at the workspace root.
|
|
88
94
|
*/ export async function findInstalledPackage(packageName, startDir, workspaceRoot) {
|
|
89
95
|
let currentDir = path.resolve(startDir);
|
|
90
96
|
const fsRoot = path.parse(currentDir).root;
|
|
@@ -105,8 +111,8 @@ import { resolveVersionRange } from './resolveVersionRange.js';
|
|
|
105
111
|
}
|
|
106
112
|
}
|
|
107
113
|
}
|
|
108
|
-
// Stop at workspace root
|
|
109
|
-
if (currentDir === workspaceRoot) {
|
|
114
|
+
// Stop at workspace root if provided, otherwise continue to filesystem root
|
|
115
|
+
if (workspaceRoot && currentDir === workspaceRoot) {
|
|
110
116
|
break;
|
|
111
117
|
}
|
|
112
118
|
currentDir = path.dirname(currentDir);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/util/packageManager/installationInfo/detectPackages.ts"],"sourcesContent":["import fs from 'node:fs/promises'\nimport path from 'node:path'\nimport {fileURLToPath, pathToFileURL} from 'node:url'\n\nimport {resolve as resolveImport} from 'import-meta-resolve'\n\nimport {readJsonFile} from './readJsonFile.js'\nimport {resolveVersionRange} from './resolveVersionRange.js'\nimport {\n type InstalledPackage,\n type PackageDeclaration,\n type PackageInfo,\n type PackageOverride,\n type SanityPackage,\n type WorkspaceInfo,\n} from './types.js'\n\ninterface PackageJson {\n dependencies?: Record<string, string>\n devDependencies?: Record<string, string>\n name?: string\n overrides?: Record<string, string>\n pnpm?: {\n overrides?: Record<string, string>\n }\n resolutions?: Record<string, string>\n version?: string\n}\n\n/**\n * Finds where a package is declared in the workspace, walking up from startDir.\n * Resolves catalog: protocol if used.\n */\nexport async function findPackageDeclaration(\n packageName: SanityPackage,\n startDir: string,\n workspaceInfo: WorkspaceInfo,\n): Promise<PackageDeclaration | null> {\n let currentDir = path.resolve(startDir)\n const fsRoot = path.parse(currentDir).root\n\n // Walk up until we pass the workspace root\n while (currentDir !== fsRoot) {\n const packageJsonPath = path.join(currentDir, 'package.json')\n const packageJson = await readJsonFile<PackageJson>(packageJsonPath)\n\n if (packageJson) {\n // Check dependencies and devDependencies only.\n // peerDependencies are excluded because they are not auto-installed —\n // flagging them as declared-not-installed would be a false positive\n // (common in Sanity plugins that list sanity as a peer dep).\n const depTypes = ['dependencies', 'devDependencies'] as const\n for (const depType of depTypes) {\n const deps = packageJson[depType]\n if (deps && packageName in deps) {\n const declaredVersionRange = deps[packageName]\n const versionRange = await resolveVersionRange(\n declaredVersionRange,\n packageName,\n workspaceInfo,\n )\n\n return {\n declaredVersionRange,\n dependencyType: depType,\n packageJsonPath,\n versionRange,\n }\n }\n }\n }\n\n // Stop at workspace root\n if (currentDir === workspaceInfo.root) {\n break\n }\n\n currentDir = path.dirname(currentDir)\n }\n\n return null\n}\n\n/**\n * Finds if a package has an override/resolution defined in the workspace root.\n * Checks npm overrides, yarn resolutions, and pnpm overrides.\n */\nexport async function findPackageOverride(\n packageName: SanityPackage,\n workspaceInfo: WorkspaceInfo,\n): Promise<PackageOverride | null> {\n const rootPackageJsonPath = path.join(workspaceInfo.root, 'package.json')\n const packageJson = await readJsonFile<PackageJson>(rootPackageJsonPath)\n\n if (!packageJson) {\n return null\n }\n\n // Check npm/pnpm overrides\n if (packageJson.overrides && packageName in packageJson.overrides) {\n return {\n mechanism: 'npm-overrides',\n packageJsonPath: rootPackageJsonPath,\n versionRange: packageJson.overrides[packageName],\n }\n }\n\n // Check pnpm.overrides (alternative location)\n if (packageJson.pnpm?.overrides && packageName in packageJson.pnpm.overrides) {\n return {\n mechanism: 'pnpm-overrides',\n packageJsonPath: rootPackageJsonPath,\n versionRange: packageJson.pnpm.overrides[packageName],\n }\n }\n\n // Check yarn resolutions\n if (packageJson.resolutions && packageName in packageJson.resolutions) {\n return {\n mechanism: 'yarn-resolutions',\n packageJsonPath: rootPackageJsonPath,\n versionRange: packageJson.resolutions[packageName],\n }\n }\n\n return null\n}\n\n/**\n * Finds installed package in node_modules, walking up from startDir.\n * Also extracts \\@sanity/cli dependency range from sanity package if applicable.\n *\n * Handles both hoisted (npm/yarn) and nested (pnpm) node_modules structures.\n */\nexport async function findInstalledPackage(\n packageName: SanityPackage,\n startDir: string,\n workspaceRoot: string,\n): Promise<InstalledPackage | null> {\n let currentDir = path.resolve(startDir)\n const fsRoot = path.parse(currentDir).root\n\n while (currentDir !== fsRoot) {\n // First, check the top-level node_modules (works for npm, yarn, and hoisted pnpm)\n const result = await findPackageInNodeModules(\n packageName,\n path.join(currentDir, 'node_modules'),\n )\n if (result) {\n return result\n }\n\n // For @sanity/cli, use Node's module resolution from sanity's location.\n // This handles all package manager layouts (pnpm nested deps, hoisting, etc.)\n if (packageName === '@sanity/cli') {\n const sanityPath = await resolvePackagePath(path.join(currentDir, 'node_modules', 'sanity'))\n if (sanityPath) {\n const nestedResult = await resolveCliFromSanity(sanityPath)\n if (nestedResult) {\n return nestedResult\n }\n }\n }\n\n // Stop at workspace root\n if (currentDir === workspaceRoot) {\n break\n }\n\n currentDir = path.dirname(currentDir)\n }\n\n return null\n}\n\n/**\n * Looks for a package in a specific node_modules directory.\n */\nasync function findPackageInNodeModules(\n packageName: SanityPackage,\n nodeModulesDir: string,\n): Promise<InstalledPackage | null> {\n const packagePath = path.join(nodeModulesDir, packageName)\n const resolvedPath = await resolvePackagePath(packagePath)\n\n if (!resolvedPath) {\n return null\n }\n\n const packageJsonPath = path.join(resolvedPath, 'package.json')\n const packageJson = await readJsonFile<PackageJson>(packageJsonPath)\n\n if (!packageJson?.version) {\n return null\n }\n\n let cliDependencyRange: string | null = null\n\n // If this is the sanity package, extract @sanity/cli dependency\n if (packageName === 'sanity') {\n cliDependencyRange = packageJson.dependencies?.['@sanity/cli'] ?? null\n }\n\n return {\n cliDependencyRange,\n path: resolvedPath,\n version: packageJson.version,\n }\n}\n\n/**\n * Resolves a package path, following symlinks (pnpm uses symlinks).\n * Returns null if the path doesn't exist.\n */\nasync function resolvePackagePath(packagePath: string): Promise<string | null> {\n try {\n // Use realpath to follow symlinks (important for pnpm)\n return await fs.realpath(packagePath)\n } catch {\n return null\n }\n}\n\n/**\n * Uses Node's module resolution to find \\@sanity/cli from sanity's location.\n * This is more robust than manually traversing directories as it handles\n * all package manager layouts (pnpm nested deps, hoisting variations, etc.)\n */\nasync function resolveCliFromSanity(sanityPath: string): Promise<InstalledPackage | null> {\n try {\n // Resolve @sanity/cli/package.json from sanity's perspective\n const sanityPkgUrl = pathToFileURL(path.join(sanityPath, 'package.json')).href\n const cliPkgUrl = resolveImport('@sanity/cli/package.json', sanityPkgUrl)\n const cliPkgPath = fileURLToPath(cliPkgUrl)\n const cliPath = path.dirname(cliPkgPath)\n\n const packageJson = await readJsonFile<PackageJson>(cliPkgPath)\n if (!packageJson?.version) {\n return null\n }\n\n return {\n cliDependencyRange: null,\n path: cliPath,\n version: packageJson.version,\n }\n } catch {\n return null\n }\n}\n\n/**\n * Collects all package info (declaration, override, installed) for a package.\n */\nexport async function collectPackageInfo(\n packageName: SanityPackage,\n startDir: string,\n workspaceInfo: WorkspaceInfo,\n): Promise<PackageInfo> {\n const [declared, override, installed] = await Promise.all([\n findPackageDeclaration(packageName, startDir, workspaceInfo),\n findPackageOverride(packageName, workspaceInfo),\n findInstalledPackage(packageName, startDir, workspaceInfo.root),\n ])\n\n return {declared, installed, override}\n}\n"],"names":["fs","path","fileURLToPath","pathToFileURL","resolve","resolveImport","readJsonFile","resolveVersionRange","findPackageDeclaration","packageName","startDir","workspaceInfo","currentDir","fsRoot","parse","root","packageJsonPath","join","packageJson","depTypes","depType","deps","declaredVersionRange","versionRange","dependencyType","dirname","findPackageOverride","rootPackageJsonPath","overrides","mechanism","pnpm","resolutions","findInstalledPackage","workspaceRoot","result","findPackageInNodeModules","sanityPath","resolvePackagePath","nestedResult","resolveCliFromSanity","nodeModulesDir","packagePath","resolvedPath","version","cliDependencyRange","dependencies","realpath","sanityPkgUrl","href","cliPkgUrl","cliPkgPath","cliPath","collectPackageInfo","declared","override","installed","Promise","all"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../src/util/packageManager/installationInfo/detectPackages.ts"],"sourcesContent":["import fs from 'node:fs/promises'\nimport path from 'node:path'\nimport {fileURLToPath, pathToFileURL} from 'node:url'\n\nimport {resolve as resolveImport} from 'import-meta-resolve'\n\nimport {readJsonFile} from './readJsonFile.js'\nimport {resolveVersionRange} from './resolveVersionRange.js'\nimport {\n type InstalledPackage,\n type PackageDeclaration,\n type PackageInfo,\n type PackageOverride,\n type SanityPackage,\n type WorkspaceInfo,\n} from './types.js'\n\ninterface PackageJson {\n dependencies?: Record<string, string>\n devDependencies?: Record<string, string>\n name?: string\n overrides?: Record<string, string>\n pnpm?: {\n overrides?: Record<string, string>\n }\n resolutions?: Record<string, string>\n version?: string\n}\n\n/**\n * Finds where a package is declared in the workspace, walking up from startDir.\n * Resolves catalog: protocol if used (requires workspaceInfo).\n *\n * When workspaceInfo is omitted, walks to the filesystem root instead of stopping\n * at the workspace root, and returns the raw declared range without catalog resolution.\n */\nexport async function findPackageDeclaration(\n packageName: SanityPackage,\n startDir: string,\n workspaceInfo?: WorkspaceInfo,\n): Promise<PackageDeclaration | null> {\n let currentDir = path.resolve(startDir)\n const fsRoot = path.parse(currentDir).root\n\n // Walk up until we pass the workspace root (or filesystem root if no workspace info)\n while (currentDir !== fsRoot) {\n const packageJsonPath = path.join(currentDir, 'package.json')\n const packageJson = await readJsonFile<PackageJson>(packageJsonPath)\n\n if (packageJson) {\n // Check dependencies and devDependencies only.\n // peerDependencies are excluded because they are not auto-installed —\n // flagging them as declared-not-installed would be a false positive\n // (common in Sanity plugins that list sanity as a peer dep).\n const depTypes = ['dependencies', 'devDependencies'] as const\n for (const depType of depTypes) {\n const deps = packageJson[depType]\n if (deps && packageName in deps) {\n const declaredVersionRange = deps[packageName]\n const versionRange = workspaceInfo\n ? await resolveVersionRange(declaredVersionRange, packageName, workspaceInfo)\n : declaredVersionRange\n\n return {\n declaredVersionRange,\n dependencyType: depType,\n packageJsonPath,\n versionRange,\n }\n }\n }\n }\n\n // Stop at workspace root if provided, otherwise continue to filesystem root\n if (workspaceInfo && currentDir === workspaceInfo.root) {\n break\n }\n\n currentDir = path.dirname(currentDir)\n }\n\n return null\n}\n\n/**\n * Finds if a package has an override/resolution defined in the workspace root.\n * Checks npm overrides, yarn resolutions, and pnpm overrides.\n */\nexport async function findPackageOverride(\n packageName: SanityPackage,\n workspaceInfo: WorkspaceInfo,\n): Promise<PackageOverride | null> {\n const rootPackageJsonPath = path.join(workspaceInfo.root, 'package.json')\n const packageJson = await readJsonFile<PackageJson>(rootPackageJsonPath)\n\n if (!packageJson) {\n return null\n }\n\n // Check npm/pnpm overrides\n if (packageJson.overrides && packageName in packageJson.overrides) {\n return {\n mechanism: 'npm-overrides',\n packageJsonPath: rootPackageJsonPath,\n versionRange: packageJson.overrides[packageName],\n }\n }\n\n // Check pnpm.overrides (alternative location)\n if (packageJson.pnpm?.overrides && packageName in packageJson.pnpm.overrides) {\n return {\n mechanism: 'pnpm-overrides',\n packageJsonPath: rootPackageJsonPath,\n versionRange: packageJson.pnpm.overrides[packageName],\n }\n }\n\n // Check yarn resolutions\n if (packageJson.resolutions && packageName in packageJson.resolutions) {\n return {\n mechanism: 'yarn-resolutions',\n packageJsonPath: rootPackageJsonPath,\n versionRange: packageJson.resolutions[packageName],\n }\n }\n\n return null\n}\n\n/**\n * Finds installed package in node_modules, walking up from startDir.\n * Also extracts \\@sanity/cli dependency range from sanity package if applicable.\n *\n * Handles both hoisted (npm/yarn) and nested (pnpm) node_modules structures.\n *\n * When workspaceRoot is omitted, walks to the filesystem root instead of stopping\n * at the workspace root.\n */\nexport async function findInstalledPackage(\n packageName: SanityPackage,\n startDir: string,\n workspaceRoot?: string,\n): Promise<InstalledPackage | null> {\n let currentDir = path.resolve(startDir)\n const fsRoot = path.parse(currentDir).root\n\n while (currentDir !== fsRoot) {\n // First, check the top-level node_modules (works for npm, yarn, and hoisted pnpm)\n const result = await findPackageInNodeModules(\n packageName,\n path.join(currentDir, 'node_modules'),\n )\n if (result) {\n return result\n }\n\n // For @sanity/cli, use Node's module resolution from sanity's location.\n // This handles all package manager layouts (pnpm nested deps, hoisting, etc.)\n if (packageName === '@sanity/cli') {\n const sanityPath = await resolvePackagePath(path.join(currentDir, 'node_modules', 'sanity'))\n if (sanityPath) {\n const nestedResult = await resolveCliFromSanity(sanityPath)\n if (nestedResult) {\n return nestedResult\n }\n }\n }\n\n // Stop at workspace root if provided, otherwise continue to filesystem root\n if (workspaceRoot && currentDir === workspaceRoot) {\n break\n }\n\n currentDir = path.dirname(currentDir)\n }\n\n return null\n}\n\n/**\n * Looks for a package in a specific node_modules directory.\n */\nasync function findPackageInNodeModules(\n packageName: SanityPackage,\n nodeModulesDir: string,\n): Promise<InstalledPackage | null> {\n const packagePath = path.join(nodeModulesDir, packageName)\n const resolvedPath = await resolvePackagePath(packagePath)\n\n if (!resolvedPath) {\n return null\n }\n\n const packageJsonPath = path.join(resolvedPath, 'package.json')\n const packageJson = await readJsonFile<PackageJson>(packageJsonPath)\n\n if (!packageJson?.version) {\n return null\n }\n\n let cliDependencyRange: string | null = null\n\n // If this is the sanity package, extract @sanity/cli dependency\n if (packageName === 'sanity') {\n cliDependencyRange = packageJson.dependencies?.['@sanity/cli'] ?? null\n }\n\n return {\n cliDependencyRange,\n path: resolvedPath,\n version: packageJson.version,\n }\n}\n\n/**\n * Resolves a package path, following symlinks (pnpm uses symlinks).\n * Returns null if the path doesn't exist.\n */\nasync function resolvePackagePath(packagePath: string): Promise<string | null> {\n try {\n // Use realpath to follow symlinks (important for pnpm)\n return await fs.realpath(packagePath)\n } catch {\n return null\n }\n}\n\n/**\n * Uses Node's module resolution to find \\@sanity/cli from sanity's location.\n * This is more robust than manually traversing directories as it handles\n * all package manager layouts (pnpm nested deps, hoisting variations, etc.)\n */\nasync function resolveCliFromSanity(sanityPath: string): Promise<InstalledPackage | null> {\n try {\n // Resolve @sanity/cli/package.json from sanity's perspective\n const sanityPkgUrl = pathToFileURL(path.join(sanityPath, 'package.json')).href\n const cliPkgUrl = resolveImport('@sanity/cli/package.json', sanityPkgUrl)\n const cliPkgPath = fileURLToPath(cliPkgUrl)\n const cliPath = path.dirname(cliPkgPath)\n\n const packageJson = await readJsonFile<PackageJson>(cliPkgPath)\n if (!packageJson?.version) {\n return null\n }\n\n return {\n cliDependencyRange: null,\n path: cliPath,\n version: packageJson.version,\n }\n } catch {\n return null\n }\n}\n\n/**\n * Collects all package info (declaration, override, installed) for a package.\n */\nexport async function collectPackageInfo(\n packageName: SanityPackage,\n startDir: string,\n workspaceInfo: WorkspaceInfo,\n): Promise<PackageInfo> {\n const [declared, override, installed] = await Promise.all([\n findPackageDeclaration(packageName, startDir, workspaceInfo),\n findPackageOverride(packageName, workspaceInfo),\n findInstalledPackage(packageName, startDir, workspaceInfo.root),\n ])\n\n return {declared, installed, override}\n}\n"],"names":["fs","path","fileURLToPath","pathToFileURL","resolve","resolveImport","readJsonFile","resolveVersionRange","findPackageDeclaration","packageName","startDir","workspaceInfo","currentDir","fsRoot","parse","root","packageJsonPath","join","packageJson","depTypes","depType","deps","declaredVersionRange","versionRange","dependencyType","dirname","findPackageOverride","rootPackageJsonPath","overrides","mechanism","pnpm","resolutions","findInstalledPackage","workspaceRoot","result","findPackageInNodeModules","sanityPath","resolvePackagePath","nestedResult","resolveCliFromSanity","nodeModulesDir","packagePath","resolvedPath","version","cliDependencyRange","dependencies","realpath","sanityPkgUrl","href","cliPkgUrl","cliPkgPath","cliPath","collectPackageInfo","declared","override","installed","Promise","all"],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { getUserConfig, subdebug } from '@sanity/cli-core';
|
|
2
|
+
import { getLatestVersion } from 'get-latest-version';
|
|
3
|
+
import { promiseRaceWithTimeout } from '../promiseRaceWithTimeout.js';
|
|
4
|
+
import { resolveUpdateTarget } from './resolveUpdateTarget.js';
|
|
5
|
+
const debug = subdebug('updateChecker');
|
|
6
|
+
const FETCH_TIMEOUT = 15_000;
|
|
7
|
+
/**
|
|
8
|
+
* Fetch the latest version of the update target package and write it to the config cache.
|
|
9
|
+
* Designed to run in a detached child process so it never blocks the main CLI.
|
|
10
|
+
*
|
|
11
|
+
* When `packageOverride` is given, the cwd-based resolver is skipped — used by
|
|
12
|
+
* the main process to pin the worker to the same package it already resolved
|
|
13
|
+
* (e.g. via a runner's symlinked install) so cache reads and writes align.
|
|
14
|
+
*/ export async function fetchUpdateInfo(cwd, cliVersion, packageOverride) {
|
|
15
|
+
const { packageName } = packageOverride ? {
|
|
16
|
+
packageName: packageOverride
|
|
17
|
+
} : await resolveUpdateTarget(cwd, cliVersion);
|
|
18
|
+
debug('Worker: fetching latest version of %s', packageName);
|
|
19
|
+
let latestVersion;
|
|
20
|
+
try {
|
|
21
|
+
latestVersion = await promiseRaceWithTimeout(getLatestVersion(packageName), FETCH_TIMEOUT);
|
|
22
|
+
} catch (err) {
|
|
23
|
+
debug('Worker: failed to fetch latest version of %s from npm: %s', packageName, err instanceof Error ? err.message : String(err));
|
|
24
|
+
throw err;
|
|
25
|
+
}
|
|
26
|
+
if (latestVersion === null) {
|
|
27
|
+
debug('Worker: fetch timed out after %dms', FETCH_TIMEOUT);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
debug('Worker: latest %s version is %s', packageName, latestVersion);
|
|
31
|
+
const store = getUserConfig();
|
|
32
|
+
const cacheKey = `latestVersion:${packageName}`;
|
|
33
|
+
store.set(cacheKey, {
|
|
34
|
+
updatedAt: Date.now(),
|
|
35
|
+
value: latestVersion
|
|
36
|
+
});
|
|
37
|
+
debug('Worker: cached result to %s', cacheKey);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
//# sourceMappingURL=fetchUpdateInfo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/util/update/fetchUpdateInfo.ts"],"sourcesContent":["import {getUserConfig, subdebug} from '@sanity/cli-core'\nimport {getLatestVersion} from 'get-latest-version'\n\nimport {type SanityPackage} from '../packageManager/installationInfo/types.js'\nimport {promiseRaceWithTimeout} from '../promiseRaceWithTimeout.js'\nimport {resolveUpdateTarget} from './resolveUpdateTarget.js'\n\nconst debug = subdebug('updateChecker')\n\nconst FETCH_TIMEOUT = 15_000\n\n/**\n * Fetch the latest version of the update target package and write it to the config cache.\n * Designed to run in a detached child process so it never blocks the main CLI.\n *\n * When `packageOverride` is given, the cwd-based resolver is skipped — used by\n * the main process to pin the worker to the same package it already resolved\n * (e.g. via a runner's symlinked install) so cache reads and writes align.\n */\nexport async function fetchUpdateInfo(\n cwd: string,\n cliVersion: string,\n packageOverride?: SanityPackage,\n): Promise<void> {\n const {packageName} = packageOverride\n ? {packageName: packageOverride}\n : await resolveUpdateTarget(cwd, cliVersion)\n debug('Worker: fetching latest version of %s', packageName)\n\n let latestVersion: string | null | undefined\n try {\n latestVersion = await promiseRaceWithTimeout(getLatestVersion(packageName), FETCH_TIMEOUT)\n } catch (err) {\n debug(\n 'Worker: failed to fetch latest version of %s from npm: %s',\n packageName,\n err instanceof Error ? err.message : String(err),\n )\n throw err\n }\n\n if (latestVersion === null) {\n debug('Worker: fetch timed out after %dms', FETCH_TIMEOUT)\n return\n }\n\n debug('Worker: latest %s version is %s', packageName, latestVersion)\n\n const store = getUserConfig()\n const cacheKey = `latestVersion:${packageName}`\n\n store.set(cacheKey, {\n updatedAt: Date.now(),\n value: latestVersion,\n })\n\n debug('Worker: cached result to %s', cacheKey)\n}\n"],"names":["getUserConfig","subdebug","getLatestVersion","promiseRaceWithTimeout","resolveUpdateTarget","debug","FETCH_TIMEOUT","fetchUpdateInfo","cwd","cliVersion","packageOverride","packageName","latestVersion","err","Error","message","String","store","cacheKey","set","updatedAt","Date","now","value"],"mappings":"AAAA,SAAQA,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AACxD,SAAQC,gBAAgB,QAAO,qBAAoB;AAGnD,SAAQC,sBAAsB,QAAO,+BAA8B;AACnE,SAAQC,mBAAmB,QAAO,2BAA0B;AAE5D,MAAMC,QAAQJ,SAAS;AAEvB,MAAMK,gBAAgB;AAEtB;;;;;;;CAOC,GACD,OAAO,eAAeC,gBACpBC,GAAW,EACXC,UAAkB,EAClBC,eAA+B;IAE/B,MAAM,EAACC,WAAW,EAAC,GAAGD,kBAClB;QAACC,aAAaD;IAAe,IAC7B,MAAMN,oBAAoBI,KAAKC;IACnCJ,MAAM,yCAAyCM;IAE/C,IAAIC;IACJ,IAAI;QACFA,gBAAgB,MAAMT,uBAAuBD,iBAAiBS,cAAcL;IAC9E,EAAE,OAAOO,KAAK;QACZR,MACE,6DACAM,aACAE,eAAeC,QAAQD,IAAIE,OAAO,GAAGC,OAAOH;QAE9C,MAAMA;IACR;IAEA,IAAID,kBAAkB,MAAM;QAC1BP,MAAM,sCAAsCC;QAC5C;IACF;IAEAD,MAAM,mCAAmCM,aAAaC;IAEtD,MAAMK,QAAQjB;IACd,MAAMkB,WAAW,CAAC,cAAc,EAAEP,aAAa;IAE/CM,MAAME,GAAG,CAACD,UAAU;QAClBE,WAAWC,KAAKC,GAAG;QACnBC,OAAOX;IACT;IAEAP,MAAM,+BAA+Ba;AACvC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { pathToFileURL } from 'node:url';
|
|
3
|
+
import { fetchUpdateInfo } from './fetchUpdateInfo.js';
|
|
4
|
+
// Only run if executed directly (not imported)
|
|
5
|
+
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
6
|
+
const cwd = process.env.SANITY_UPDATE_CHECK_CWD || process.cwd();
|
|
7
|
+
const cliVersion = process.env.SANITY_UPDATE_CHECK_CLI_VERSION || '0.0.0';
|
|
8
|
+
const rawPackage = process.env.SANITY_UPDATE_CHECK_PACKAGE;
|
|
9
|
+
const packageOverride = rawPackage === 'sanity' || rawPackage === '@sanity/cli' ? rawPackage : undefined;
|
|
10
|
+
try {
|
|
11
|
+
await fetchUpdateInfo(cwd, cliVersion, packageOverride);
|
|
12
|
+
process.exit(0);
|
|
13
|
+
} catch {
|
|
14
|
+
// Silently exit - don't leave zombie processes
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
//# sourceMappingURL=fetchUpdateInfo.worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/util/update/fetchUpdateInfo.worker.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport {pathToFileURL} from 'node:url'\n\nimport {type SanityPackage} from '../packageManager/installationInfo/types.js'\nimport {fetchUpdateInfo} from './fetchUpdateInfo.js'\n\n// Only run if executed directly (not imported)\nif (import.meta.url === pathToFileURL(process.argv[1]).href) {\n const cwd = process.env.SANITY_UPDATE_CHECK_CWD || process.cwd()\n const cliVersion = process.env.SANITY_UPDATE_CHECK_CLI_VERSION || '0.0.0'\n const rawPackage = process.env.SANITY_UPDATE_CHECK_PACKAGE\n const packageOverride: SanityPackage | undefined =\n rawPackage === 'sanity' || rawPackage === '@sanity/cli' ? rawPackage : undefined\n\n try {\n await fetchUpdateInfo(cwd, cliVersion, packageOverride)\n process.exit(0)\n } catch {\n // Silently exit - don't leave zombie processes\n process.exit(1)\n }\n}\n"],"names":["pathToFileURL","fetchUpdateInfo","url","process","argv","href","cwd","env","SANITY_UPDATE_CHECK_CWD","cliVersion","SANITY_UPDATE_CHECK_CLI_VERSION","rawPackage","SANITY_UPDATE_CHECK_PACKAGE","packageOverride","undefined","exit"],"mappings":";AAEA,SAAQA,aAAa,QAAO,WAAU;AAGtC,SAAQC,eAAe,QAAO,uBAAsB;AAEpD,+CAA+C;AAC/C,IAAI,YAAYC,GAAG,KAAKF,cAAcG,QAAQC,IAAI,CAAC,EAAE,EAAEC,IAAI,EAAE;IAC3D,MAAMC,MAAMH,QAAQI,GAAG,CAACC,uBAAuB,IAAIL,QAAQG,GAAG;IAC9D,MAAMG,aAAaN,QAAQI,GAAG,CAACG,+BAA+B,IAAI;IAClE,MAAMC,aAAaR,QAAQI,GAAG,CAACK,2BAA2B;IAC1D,MAAMC,kBACJF,eAAe,YAAYA,eAAe,gBAAgBA,aAAaG;IAEzE,IAAI;QACF,MAAMb,gBAAgBK,KAAKG,YAAYI;QACvCV,QAAQY,IAAI,CAAC;IACf,EAAE,OAAM;QACN,+CAA+C;QAC/CZ,QAAQY,IAAI,CAAC;IACf;AACF"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const BIN_NAMES = {
|
|
2
|
+
'@sanity/cli': 'sanity',
|
|
3
|
+
sanity: 'sanity'
|
|
4
|
+
};
|
|
5
|
+
export function getRunnerUpdateCommand(runner, packageName) {
|
|
6
|
+
const binName = BIN_NAMES[packageName];
|
|
7
|
+
switch(runner){
|
|
8
|
+
case 'bunx':
|
|
9
|
+
{
|
|
10
|
+
return `bunx ${packageName}@latest`;
|
|
11
|
+
}
|
|
12
|
+
case 'npx':
|
|
13
|
+
{
|
|
14
|
+
return `npx --yes ${packageName}@latest`;
|
|
15
|
+
}
|
|
16
|
+
case 'pnpm-dlx':
|
|
17
|
+
{
|
|
18
|
+
return `pnpm dlx ${packageName}@latest`;
|
|
19
|
+
}
|
|
20
|
+
case 'yarn-dlx':
|
|
21
|
+
{
|
|
22
|
+
// yarn dlx only needs `-p` when the package name differs from the bin name
|
|
23
|
+
return binName === packageName ? `yarn dlx ${packageName}@latest` : `yarn dlx -p ${packageName}@latest ${binName}`;
|
|
24
|
+
}
|
|
25
|
+
default:
|
|
26
|
+
{
|
|
27
|
+
const _exhaustive = runner;
|
|
28
|
+
throw new Error(`Unknown runner: ${_exhaustive}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
//# sourceMappingURL=getRunnerUpdateCommand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/util/update/getRunnerUpdateCommand.ts"],"sourcesContent":["import {type SanityPackage} from '../packageManager/installationInfo/types.js'\nimport {type PackageRunner} from './packageRunner.js'\n\nconst BIN_NAMES: Record<SanityPackage, string> = {\n '@sanity/cli': 'sanity',\n sanity: 'sanity',\n}\n\nexport function getRunnerUpdateCommand(runner: PackageRunner, packageName: SanityPackage): string {\n const binName = BIN_NAMES[packageName]\n\n switch (runner) {\n case 'bunx': {\n return `bunx ${packageName}@latest`\n }\n case 'npx': {\n return `npx --yes ${packageName}@latest`\n }\n case 'pnpm-dlx': {\n return `pnpm dlx ${packageName}@latest`\n }\n case 'yarn-dlx': {\n // yarn dlx only needs `-p` when the package name differs from the bin name\n return binName === packageName\n ? `yarn dlx ${packageName}@latest`\n : `yarn dlx -p ${packageName}@latest ${binName}`\n }\n default: {\n const _exhaustive: never = runner\n throw new Error(`Unknown runner: ${_exhaustive as string}`)\n }\n }\n}\n"],"names":["BIN_NAMES","sanity","getRunnerUpdateCommand","runner","packageName","binName","_exhaustive","Error"],"mappings":"AAGA,MAAMA,YAA2C;IAC/C,eAAe;IACfC,QAAQ;AACV;AAEA,OAAO,SAASC,uBAAuBC,MAAqB,EAAEC,WAA0B;IACtF,MAAMC,UAAUL,SAAS,CAACI,YAAY;IAEtC,OAAQD;QACN,KAAK;YAAQ;gBACX,OAAO,CAAC,KAAK,EAAEC,YAAY,OAAO,CAAC;YACrC;QACA,KAAK;YAAO;gBACV,OAAO,CAAC,UAAU,EAAEA,YAAY,OAAO,CAAC;YAC1C;QACA,KAAK;YAAY;gBACf,OAAO,CAAC,SAAS,EAAEA,YAAY,OAAO,CAAC;YACzC;QACA,KAAK;YAAY;gBACf,2EAA2E;gBAC3E,OAAOC,YAAYD,cACf,CAAC,SAAS,EAAEA,YAAY,OAAO,CAAC,GAChC,CAAC,YAAY,EAAEA,YAAY,QAAQ,EAAEC,SAAS;YACpD;QACA;YAAS;gBACP,MAAMC,cAAqBH;gBAC3B,MAAM,IAAII,MAAM,CAAC,gBAAgB,EAAED,aAAuB;YAC5D;IACF;AACF"}
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
import { getYarnMajorVersion } from '@sanity/cli-core/package-manager';
|
|
2
|
-
export const cliPkgName = 'sanity';
|
|
3
2
|
/**
|
|
4
3
|
* Get the appropriate update command for the package manager
|
|
5
|
-
*/ export function getUpdateCommand(pm) {
|
|
4
|
+
*/ export function getUpdateCommand(pm, packageName) {
|
|
6
5
|
if (pm === 'yarn') {
|
|
7
6
|
const yarnMajor = getYarnMajorVersion();
|
|
8
7
|
const cmd = yarnMajor !== undefined && yarnMajor >= 2 ? 'up' : 'upgrade';
|
|
9
|
-
return `yarn ${cmd} ${
|
|
8
|
+
return `yarn ${cmd} ${packageName}`;
|
|
10
9
|
}
|
|
11
10
|
const localCommands = {
|
|
12
|
-
bun: `bun update ${
|
|
13
|
-
manual: `npm update ${
|
|
14
|
-
npm: `npm update ${
|
|
15
|
-
pnpm: `pnpm update ${
|
|
11
|
+
bun: `bun update ${packageName}`,
|
|
12
|
+
manual: `npm update ${packageName}`,
|
|
13
|
+
npm: `npm update ${packageName}`,
|
|
14
|
+
pnpm: `pnpm update ${packageName}`
|
|
16
15
|
};
|
|
17
16
|
return localCommands[pm];
|
|
18
17
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/util/update/getUpdateCommand.ts"],"sourcesContent":["import {getYarnMajorVersion} from '@sanity/cli-core/package-manager'\n\nimport {type
|
|
1
|
+
{"version":3,"sources":["../../../src/util/update/getUpdateCommand.ts"],"sourcesContent":["import {getYarnMajorVersion} from '@sanity/cli-core/package-manager'\n\nimport {type SanityPackage} from '../packageManager/installationInfo/types.js'\nimport {type PackageManager} from '../packageManager/packageManagerChoice.js'\n\n/**\n * Get the appropriate update command for the package manager\n */\nexport function getUpdateCommand(pm: PackageManager, packageName: SanityPackage): string {\n if (pm === 'yarn') {\n const yarnMajor = getYarnMajorVersion()\n const cmd = yarnMajor !== undefined && yarnMajor >= 2 ? 'up' : 'upgrade'\n return `yarn ${cmd} ${packageName}`\n }\n\n const localCommands: Record<Exclude<PackageManager, 'yarn'>, string> = {\n bun: `bun update ${packageName}`,\n manual: `npm update ${packageName}`,\n npm: `npm update ${packageName}`,\n pnpm: `pnpm update ${packageName}`,\n }\n return localCommands[pm]\n}\n"],"names":["getYarnMajorVersion","getUpdateCommand","pm","packageName","yarnMajor","cmd","undefined","localCommands","bun","manual","npm","pnpm"],"mappings":"AAAA,SAAQA,mBAAmB,QAAO,mCAAkC;AAKpE;;CAEC,GACD,OAAO,SAASC,iBAAiBC,EAAkB,EAAEC,WAA0B;IAC7E,IAAID,OAAO,QAAQ;QACjB,MAAME,YAAYJ;QAClB,MAAMK,MAAMD,cAAcE,aAAaF,aAAa,IAAI,OAAO;QAC/D,OAAO,CAAC,KAAK,EAAEC,IAAI,CAAC,EAAEF,aAAa;IACrC;IAEA,MAAMI,gBAAiE;QACrEC,KAAK,CAAC,WAAW,EAAEL,aAAa;QAChCM,QAAQ,CAAC,WAAW,EAAEN,aAAa;QACnCO,KAAK,CAAC,WAAW,EAAEP,aAAa;QAChCQ,MAAM,CAAC,YAAY,EAAER,aAAa;IACpC;IACA,OAAOI,aAAa,CAACL,GAAG;AAC1B"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function detectPackageRunner(binaryPath = process.argv[1] ?? '') {
|
|
2
|
+
const normalized = binaryPath.replaceAll('\\', '/');
|
|
3
|
+
if (normalized.includes('/_npx/')) return 'npx';
|
|
4
|
+
if (normalized.includes('/pnpm/dlx/')) return 'pnpm-dlx';
|
|
5
|
+
if (normalized.includes('/xfs-') && normalized.includes('/dlx-')) return 'yarn-dlx';
|
|
6
|
+
if (/\/bunx-\d+-/.test(normalized)) return 'bunx';
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
//# sourceMappingURL=packageRunner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/util/update/packageRunner.ts"],"sourcesContent":["export type PackageRunner = 'bunx' | 'npx' | 'pnpm-dlx' | 'yarn-dlx'\n\nexport function detectPackageRunner(\n binaryPath: string = process.argv[1] ?? '',\n): PackageRunner | null {\n const normalized = binaryPath.replaceAll('\\\\', '/')\n\n if (normalized.includes('/_npx/')) return 'npx'\n if (normalized.includes('/pnpm/dlx/')) return 'pnpm-dlx'\n if (normalized.includes('/xfs-') && normalized.includes('/dlx-')) return 'yarn-dlx'\n if (/\\/bunx-\\d+-/.test(normalized)) return 'bunx'\n\n return null\n}\n"],"names":["detectPackageRunner","binaryPath","process","argv","normalized","replaceAll","includes","test"],"mappings":"AAEA,OAAO,SAASA,oBACdC,aAAqBC,QAAQC,IAAI,CAAC,EAAE,IAAI,EAAE;IAE1C,MAAMC,aAAaH,WAAWI,UAAU,CAAC,MAAM;IAE/C,IAAID,WAAWE,QAAQ,CAAC,WAAW,OAAO;IAC1C,IAAIF,WAAWE,QAAQ,CAAC,eAAe,OAAO;IAC9C,IAAIF,WAAWE,QAAQ,CAAC,YAAYF,WAAWE,QAAQ,CAAC,UAAU,OAAO;IACzE,IAAI,cAAcC,IAAI,CAACH,aAAa,OAAO;IAE3C,OAAO;AACT"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { readFile, realpath } from 'node:fs/promises';
|
|
2
|
+
import { dirname, resolve } from 'node:path';
|
|
3
|
+
import { subdebug } from '@sanity/cli-core';
|
|
4
|
+
const debug = subdebug('updateChecker');
|
|
5
|
+
const KNOWN_PACKAGES = new Set([
|
|
6
|
+
'@sanity/cli',
|
|
7
|
+
'sanity'
|
|
8
|
+
]);
|
|
9
|
+
const MAX_WALK_ITERATIONS = 25;
|
|
10
|
+
/**
|
|
11
|
+
* Resolve the Sanity package name + installed version from a runner install.
|
|
12
|
+
* Falls back to `sanity` + `fallbackVersion` when the walk can't determine them.
|
|
13
|
+
*/ export async function resolveRunnerPackage(binaryPath = process.argv[1] ?? '', fallbackVersion = '') {
|
|
14
|
+
try {
|
|
15
|
+
// Follow the runner's .bin/sanity symlink to the real bin file, then walk
|
|
16
|
+
// up until we hit a package.json for a known Sanity package.
|
|
17
|
+
let dir = dirname(await realpath(binaryPath));
|
|
18
|
+
for(let i = 0; i < MAX_WALK_ITERATIONS && dir !== resolve(dir, '..'); i++){
|
|
19
|
+
try {
|
|
20
|
+
const pkg = JSON.parse(await readFile(resolve(dir, 'package.json'), 'utf8'));
|
|
21
|
+
if (typeof pkg.name === 'string' && typeof pkg.version === 'string' && isKnownSanityPackage(pkg.name)) {
|
|
22
|
+
return {
|
|
23
|
+
installedVersion: pkg.version,
|
|
24
|
+
packageName: pkg.name
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
} catch {
|
|
28
|
+
// ignore missing/malformed package.json and keep walking
|
|
29
|
+
}
|
|
30
|
+
dir = dirname(dir);
|
|
31
|
+
}
|
|
32
|
+
debug('resolveRunnerPackage: walk exhausted without finding a known Sanity package');
|
|
33
|
+
} catch (err) {
|
|
34
|
+
debug('resolveRunnerPackage: realpath failed for %s (%s)', binaryPath, err);
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
installedVersion: fallbackVersion,
|
|
38
|
+
packageName: 'sanity'
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function isKnownSanityPackage(name) {
|
|
42
|
+
return KNOWN_PACKAGES.has(name);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
//# sourceMappingURL=resolveRunnerPackage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/util/update/resolveRunnerPackage.ts"],"sourcesContent":["import {readFile, realpath} from 'node:fs/promises'\nimport {dirname, resolve} from 'node:path'\n\nimport {subdebug} from '@sanity/cli-core'\n\nimport {type SanityPackage} from '../packageManager/installationInfo/types.js'\n\nconst debug = subdebug('updateChecker')\n\nconst KNOWN_PACKAGES = new Set<SanityPackage>(['@sanity/cli', 'sanity'])\nconst MAX_WALK_ITERATIONS = 25\n\ninterface RunnerPackage {\n installedVersion: string\n packageName: SanityPackage\n}\n\n/**\n * Resolve the Sanity package name + installed version from a runner install.\n * Falls back to `sanity` + `fallbackVersion` when the walk can't determine them.\n */\nexport async function resolveRunnerPackage(\n binaryPath: string = process.argv[1] ?? '',\n fallbackVersion = '',\n): Promise<RunnerPackage> {\n try {\n // Follow the runner's .bin/sanity symlink to the real bin file, then walk\n // up until we hit a package.json for a known Sanity package.\n let dir = dirname(await realpath(binaryPath))\n for (let i = 0; i < MAX_WALK_ITERATIONS && dir !== resolve(dir, '..'); i++) {\n try {\n const pkg = JSON.parse(await readFile(resolve(dir, 'package.json'), 'utf8'))\n if (\n typeof pkg.name === 'string' &&\n typeof pkg.version === 'string' &&\n isKnownSanityPackage(pkg.name)\n ) {\n return {installedVersion: pkg.version, packageName: pkg.name}\n }\n } catch {\n // ignore missing/malformed package.json and keep walking\n }\n dir = dirname(dir)\n }\n debug('resolveRunnerPackage: walk exhausted without finding a known Sanity package')\n } catch (err) {\n debug('resolveRunnerPackage: realpath failed for %s (%s)', binaryPath, err)\n }\n\n return {installedVersion: fallbackVersion, packageName: 'sanity'}\n}\n\nfunction isKnownSanityPackage(name: string): name is SanityPackage {\n return KNOWN_PACKAGES.has(name as SanityPackage)\n}\n"],"names":["readFile","realpath","dirname","resolve","subdebug","debug","KNOWN_PACKAGES","Set","MAX_WALK_ITERATIONS","resolveRunnerPackage","binaryPath","process","argv","fallbackVersion","dir","i","pkg","JSON","parse","name","version","isKnownSanityPackage","installedVersion","packageName","err","has"],"mappings":"AAAA,SAAQA,QAAQ,EAAEC,QAAQ,QAAO,mBAAkB;AACnD,SAAQC,OAAO,EAAEC,OAAO,QAAO,YAAW;AAE1C,SAAQC,QAAQ,QAAO,mBAAkB;AAIzC,MAAMC,QAAQD,SAAS;AAEvB,MAAME,iBAAiB,IAAIC,IAAmB;IAAC;IAAe;CAAS;AACvE,MAAMC,sBAAsB;AAO5B;;;CAGC,GACD,OAAO,eAAeC,qBACpBC,aAAqBC,QAAQC,IAAI,CAAC,EAAE,IAAI,EAAE,EAC1CC,kBAAkB,EAAE;IAEpB,IAAI;QACF,0EAA0E;QAC1E,6DAA6D;QAC7D,IAAIC,MAAMZ,QAAQ,MAAMD,SAASS;QACjC,IAAK,IAAIK,IAAI,GAAGA,IAAIP,uBAAuBM,QAAQX,QAAQW,KAAK,OAAOC,IAAK;YAC1E,IAAI;gBACF,MAAMC,MAAMC,KAAKC,KAAK,CAAC,MAAMlB,SAASG,QAAQW,KAAK,iBAAiB;gBACpE,IACE,OAAOE,IAAIG,IAAI,KAAK,YACpB,OAAOH,IAAII,OAAO,KAAK,YACvBC,qBAAqBL,IAAIG,IAAI,GAC7B;oBACA,OAAO;wBAACG,kBAAkBN,IAAII,OAAO;wBAAEG,aAAaP,IAAIG,IAAI;oBAAA;gBAC9D;YACF,EAAE,OAAM;YACN,yDAAyD;YAC3D;YACAL,MAAMZ,QAAQY;QAChB;QACAT,MAAM;IACR,EAAE,OAAOmB,KAAK;QACZnB,MAAM,qDAAqDK,YAAYc;IACzE;IAEA,OAAO;QAACF,kBAAkBT;QAAiBU,aAAa;IAAQ;AAClE;AAEA,SAASF,qBAAqBF,IAAY;IACxC,OAAOb,eAAemB,GAAG,CAACN;AAC5B"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { subdebug } from '@sanity/cli-core';
|
|
2
|
+
import { findInstalledPackage, findPackageDeclaration } from '../packageManager/installationInfo/detectPackages.js';
|
|
3
|
+
const debug = subdebug('updateChecker');
|
|
4
|
+
/**
|
|
5
|
+
* Determine which package to check for updates and what version is currently installed.
|
|
6
|
+
*
|
|
7
|
+
* If the user's project declares `sanity` as a dependency and it's installed,
|
|
8
|
+
* we check `sanity` (since that's what the user manages). Otherwise, we fall back
|
|
9
|
+
* to `@sanity/cli` (the currently running CLI binary).
|
|
10
|
+
*/ export async function resolveUpdateTarget(cwd, cliVersion) {
|
|
11
|
+
// Check if `sanity` is a dependency in the local project
|
|
12
|
+
const sanityDeclaration = await findPackageDeclaration('sanity', cwd);
|
|
13
|
+
if (sanityDeclaration) {
|
|
14
|
+
debug('Project declares sanity as a dependency, checking installed version');
|
|
15
|
+
const sanityInstalled = await findInstalledPackage('sanity', cwd);
|
|
16
|
+
if (sanityInstalled) {
|
|
17
|
+
debug('Installed sanity version: %s', sanityInstalled.version);
|
|
18
|
+
return {
|
|
19
|
+
installedVersion: sanityInstalled.version,
|
|
20
|
+
packageName: 'sanity'
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
debug('sanity is declared but not installed, falling back to @sanity/cli');
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
installedVersion: cliVersion,
|
|
27
|
+
packageName: '@sanity/cli'
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=resolveUpdateTarget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/util/update/resolveUpdateTarget.ts"],"sourcesContent":["import {subdebug} from '@sanity/cli-core'\n\nimport {\n findInstalledPackage,\n findPackageDeclaration,\n} from '../packageManager/installationInfo/detectPackages.js'\nimport {type SanityPackage} from '../packageManager/installationInfo/types.js'\n\nconst debug = subdebug('updateChecker')\n\ninterface UpdateTarget {\n installedVersion: string\n packageName: SanityPackage\n}\n\n/**\n * Determine which package to check for updates and what version is currently installed.\n *\n * If the user's project declares `sanity` as a dependency and it's installed,\n * we check `sanity` (since that's what the user manages). Otherwise, we fall back\n * to `@sanity/cli` (the currently running CLI binary).\n */\nexport async function resolveUpdateTarget(cwd: string, cliVersion: string): Promise<UpdateTarget> {\n // Check if `sanity` is a dependency in the local project\n const sanityDeclaration = await findPackageDeclaration('sanity', cwd)\n\n if (sanityDeclaration) {\n debug('Project declares sanity as a dependency, checking installed version')\n const sanityInstalled = await findInstalledPackage('sanity', cwd)\n\n if (sanityInstalled) {\n debug('Installed sanity version: %s', sanityInstalled.version)\n return {installedVersion: sanityInstalled.version, packageName: 'sanity'}\n }\n\n debug('sanity is declared but not installed, falling back to @sanity/cli')\n }\n\n return {installedVersion: cliVersion, packageName: '@sanity/cli'}\n}\n"],"names":["subdebug","findInstalledPackage","findPackageDeclaration","debug","resolveUpdateTarget","cwd","cliVersion","sanityDeclaration","sanityInstalled","version","installedVersion","packageName"],"mappings":"AAAA,SAAQA,QAAQ,QAAO,mBAAkB;AAEzC,SACEC,oBAAoB,EACpBC,sBAAsB,QACjB,uDAAsD;AAG7D,MAAMC,QAAQH,SAAS;AAOvB;;;;;;CAMC,GACD,OAAO,eAAeI,oBAAoBC,GAAW,EAAEC,UAAkB;IACvE,yDAAyD;IACzD,MAAMC,oBAAoB,MAAML,uBAAuB,UAAUG;IAEjE,IAAIE,mBAAmB;QACrBJ,MAAM;QACN,MAAMK,kBAAkB,MAAMP,qBAAqB,UAAUI;QAE7D,IAAIG,iBAAiB;YACnBL,MAAM,gCAAgCK,gBAAgBC,OAAO;YAC7D,OAAO;gBAACC,kBAAkBF,gBAAgBC,OAAO;gBAAEE,aAAa;YAAQ;QAC1E;QAEAR,MAAM;IACR;IAEA,OAAO;QAACO,kBAAkBJ;QAAYK,aAAa;IAAa;AAClE"}
|
|
@@ -3,20 +3,22 @@ import { ux } from '@oclif/core';
|
|
|
3
3
|
import { boxen } from '@sanity/cli-core/ux';
|
|
4
4
|
import isInstalledGlobally from 'is-installed-globally';
|
|
5
5
|
import { getPackageManagerChoice } from '../packageManager/packageManagerChoice.js';
|
|
6
|
-
import {
|
|
6
|
+
import { getRunnerUpdateCommand } from './getRunnerUpdateCommand.js';
|
|
7
|
+
import { getUpdateCommand } from './getUpdateCommand.js';
|
|
7
8
|
import { isInstalledUsingYarn } from './isInstalledUsingYarn.js';
|
|
8
9
|
/**
|
|
9
10
|
* Show a boxed notification about the available update
|
|
10
|
-
*/ export async function showUpdateNotification(currentVersion, latestVersion) {
|
|
11
|
+
*/ export async function showUpdateNotification(currentVersion, latestVersion, packageName, runner = null) {
|
|
11
12
|
let command;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
if (runner) {
|
|
14
|
+
command = getRunnerUpdateCommand(runner, packageName);
|
|
15
|
+
} else if (isInstalledGlobally) {
|
|
16
|
+
command = isInstalledUsingYarn() ? `yarn global add ${packageName}` : `npm install -g ${packageName}`;
|
|
15
17
|
} else {
|
|
16
18
|
const { chosen } = await getPackageManagerChoice(process.cwd(), {
|
|
17
19
|
interactive: false
|
|
18
20
|
});
|
|
19
|
-
command = getUpdateCommand(chosen);
|
|
21
|
+
command = getUpdateCommand(chosen, packageName);
|
|
20
22
|
}
|
|
21
23
|
const message = `Update available: ${styleText('dim', currentVersion)} → ${styleText('green', latestVersion)}\n\nRun ${styleText('cyan', command)} to update`;
|
|
22
24
|
const boxed = boxen(message, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/util/update/showNotificationUpdate.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {ux} from '@oclif/core'\nimport {boxen} from '@sanity/cli-core/ux'\nimport isInstalledGlobally from 'is-installed-globally'\n\nimport {getPackageManagerChoice} from '../packageManager/packageManagerChoice.js'\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../src/util/update/showNotificationUpdate.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {ux} from '@oclif/core'\nimport {boxen} from '@sanity/cli-core/ux'\nimport isInstalledGlobally from 'is-installed-globally'\n\nimport {type SanityPackage} from '../packageManager/installationInfo/types.js'\nimport {getPackageManagerChoice} from '../packageManager/packageManagerChoice.js'\nimport {getRunnerUpdateCommand} from './getRunnerUpdateCommand.js'\nimport {getUpdateCommand} from './getUpdateCommand.js'\nimport {isInstalledUsingYarn} from './isInstalledUsingYarn.js'\nimport {type PackageRunner} from './packageRunner.js'\n\n/**\n * Show a boxed notification about the available update\n */\nexport async function showUpdateNotification(\n currentVersion: string,\n latestVersion: string,\n packageName: SanityPackage,\n runner: PackageRunner | null = null,\n): Promise<void> {\n let command\n\n if (runner) {\n command = getRunnerUpdateCommand(runner, packageName)\n } else if (isInstalledGlobally) {\n command = isInstalledUsingYarn()\n ? `yarn global add ${packageName}`\n : `npm install -g ${packageName}`\n } else {\n const {chosen} = await getPackageManagerChoice(process.cwd(), {interactive: false})\n command = getUpdateCommand(chosen, packageName)\n }\n\n const message = `Update available: ${styleText('dim', currentVersion)} → ${styleText('green', latestVersion)}\\n\\nRun ${styleText('cyan', command)} to update`\n\n const boxed = boxen(message, {\n borderColor: 'yellow',\n borderStyle: 'round',\n margin: 1,\n padding: 1,\n })\n\n ux.stderr('\\n' + boxed + '\\n')\n}\n"],"names":["styleText","ux","boxen","isInstalledGlobally","getPackageManagerChoice","getRunnerUpdateCommand","getUpdateCommand","isInstalledUsingYarn","showUpdateNotification","currentVersion","latestVersion","packageName","runner","command","chosen","process","cwd","interactive","message","boxed","borderColor","borderStyle","margin","padding","stderr"],"mappings":"AAAA,SAAQA,SAAS,QAAO,YAAW;AAEnC,SAAQC,EAAE,QAAO,cAAa;AAC9B,SAAQC,KAAK,QAAO,sBAAqB;AACzC,OAAOC,yBAAyB,wBAAuB;AAGvD,SAAQC,uBAAuB,QAAO,4CAA2C;AACjF,SAAQC,sBAAsB,QAAO,8BAA6B;AAClE,SAAQC,gBAAgB,QAAO,wBAAuB;AACtD,SAAQC,oBAAoB,QAAO,4BAA2B;AAG9D;;CAEC,GACD,OAAO,eAAeC,uBACpBC,cAAsB,EACtBC,aAAqB,EACrBC,WAA0B,EAC1BC,SAA+B,IAAI;IAEnC,IAAIC;IAEJ,IAAID,QAAQ;QACVC,UAAUR,uBAAuBO,QAAQD;IAC3C,OAAO,IAAIR,qBAAqB;QAC9BU,UAAUN,yBACN,CAAC,gBAAgB,EAAEI,aAAa,GAChC,CAAC,eAAe,EAAEA,aAAa;IACrC,OAAO;QACL,MAAM,EAACG,MAAM,EAAC,GAAG,MAAMV,wBAAwBW,QAAQC,GAAG,IAAI;YAACC,aAAa;QAAK;QACjFJ,UAAUP,iBAAiBQ,QAAQH;IACrC;IAEA,MAAMO,UAAU,CAAC,kBAAkB,EAAElB,UAAU,OAAOS,gBAAgB,GAAG,EAAET,UAAU,SAASU,eAAe,QAAQ,EAAEV,UAAU,QAAQa,SAAS,UAAU,CAAC;IAE7J,MAAMM,QAAQjB,MAAMgB,SAAS;QAC3BE,aAAa;QACbC,aAAa;QACbC,QAAQ;QACRC,SAAS;IACX;IAEAtB,GAAGuB,MAAM,CAAC,OAAOL,QAAQ;AAC3B"}
|
|
@@ -1,17 +1,21 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
1
3
|
import { getUserConfig, isCi, subdebug } from '@sanity/cli-core';
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
4
|
+
import { gt as semverGt } from 'semver';
|
|
5
|
+
import { detectPackageRunner } from './packageRunner.js';
|
|
6
|
+
import { resolveRunnerPackage } from './resolveRunnerPackage.js';
|
|
7
|
+
import { resolveUpdateTarget } from './resolveUpdateTarget.js';
|
|
5
8
|
import { showUpdateNotification } from './showNotificationUpdate.js';
|
|
6
9
|
const debug = subdebug('updateChecker');
|
|
7
|
-
const TWELVE_HOURS = 12 * 60 * 60 * 1000
|
|
8
|
-
;
|
|
9
|
-
const CHECK_TIMEOUT = 300;
|
|
10
|
+
const TWELVE_HOURS = 12 * 60 * 60 * 1000;
|
|
10
11
|
/**
|
|
11
12
|
* Check for CLI updates and notify the user if a new version is available.
|
|
12
|
-
* This is designed to be non-blocking and will silently fail if anything goes wrong.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
14
|
+
* The main process resolves the local update target (which package and installed version),
|
|
15
|
+
* then reads the latest version from the config cache. It never makes network requests.
|
|
16
|
+
* If the cache is empty or expired, a detached worker process is spawned to fetch the
|
|
17
|
+
* latest version from npm and write it to the cache. The notification is shown on the
|
|
18
|
+
* next CLI invocation when the cached result is available instantly.
|
|
15
19
|
*/ export async function updateChecker(config) {
|
|
16
20
|
debug(`Installed CLI version is ${config.version}`);
|
|
17
21
|
// Skip in CI or if disabled
|
|
@@ -22,39 +26,70 @@ const CHECK_TIMEOUT = 300;
|
|
|
22
26
|
if (!process.stdout.isTTY) {
|
|
23
27
|
return;
|
|
24
28
|
}
|
|
29
|
+
const runner = detectPackageRunner();
|
|
30
|
+
const { installedVersion, packageName } = runner ? await resolveRunnerPackage(process.argv[1] ?? '', config.version) : await resolveUpdateTarget(process.cwd(), config.version);
|
|
31
|
+
debug('Update target: %s@%s%s', packageName, installedVersion, runner ? ` via ${runner}` : '');
|
|
25
32
|
const store = getUserConfig();
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (comparison === 0) {
|
|
51
|
-
debug('No update found');
|
|
52
|
-
return;
|
|
33
|
+
const cacheKey = `latestVersion:${packageName}`;
|
|
34
|
+
const cached = readCachedLatestVersion(store, cacheKey);
|
|
35
|
+
if (cached) {
|
|
36
|
+
const { expired, latestVersion, updatedAt } = cached;
|
|
37
|
+
debug('Cache %s for %s: installed=%s, latest=%s', expired ? 'expired' : 'hit', packageName, installedVersion, latestVersion);
|
|
38
|
+
if (semverGt(latestVersion, installedVersion)) {
|
|
39
|
+
const notifiedKey = `notifiedAt:${packageName}`;
|
|
40
|
+
if (store.get(notifiedKey) === updatedAt) {
|
|
41
|
+
debug('Update is available (%s), already notified for this cache cycle', latestVersion);
|
|
42
|
+
} else {
|
|
43
|
+
debug('Update is available (%s)', latestVersion);
|
|
44
|
+
await showUpdateNotification(installedVersion, latestVersion, packageName, runner);
|
|
45
|
+
store.set(notifiedKey, updatedAt);
|
|
46
|
+
}
|
|
47
|
+
} else {
|
|
48
|
+
debug('No update found');
|
|
49
|
+
}
|
|
50
|
+
if (expired) {
|
|
51
|
+
debug('Cache expired, spawning worker to refresh');
|
|
52
|
+
spawnFetchWorker(config.version, packageName);
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
debug('No cached update info, spawning worker to fetch');
|
|
56
|
+
spawnFetchWorker(config.version, packageName);
|
|
53
57
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Read and validate the cached latest version for a specific package.
|
|
61
|
+
* The cache only stores the latest npm version (globally valid) - the installed
|
|
62
|
+
* version is always resolved locally to avoid cross-project confusion.
|
|
63
|
+
*/ function readCachedLatestVersion(store, cacheKey) {
|
|
64
|
+
const stored = store.get(cacheKey);
|
|
65
|
+
if (!stored || typeof stored !== 'object' || !('updatedAt' in stored) || typeof stored.updatedAt !== 'number' || !('value' in stored) || typeof stored.value !== 'string') {
|
|
66
|
+
return null;
|
|
57
67
|
}
|
|
68
|
+
const expired = Date.now() - stored.updatedAt > TWELVE_HOURS;
|
|
69
|
+
return {
|
|
70
|
+
expired,
|
|
71
|
+
latestVersion: stored.value,
|
|
72
|
+
updatedAt: stored.updatedAt
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Spawn a detached worker process to fetch the latest version and update the cache.
|
|
77
|
+
* The worker is unref'd so the parent CLI can exit immediately.
|
|
78
|
+
*/ function spawnFetchWorker(cliVersion, packageName) {
|
|
79
|
+
const workerPath = fileURLToPath(new URL('fetchUpdateInfo.worker.js', import.meta.url));
|
|
80
|
+
debug(`Spawning update check worker: ${process.execPath} ${workerPath}`);
|
|
81
|
+
spawn(process.execPath, [
|
|
82
|
+
workerPath
|
|
83
|
+
], {
|
|
84
|
+
detached: true,
|
|
85
|
+
env: {
|
|
86
|
+
...process.env,
|
|
87
|
+
SANITY_UPDATE_CHECK_CLI_VERSION: cliVersion,
|
|
88
|
+
SANITY_UPDATE_CHECK_CWD: process.cwd(),
|
|
89
|
+
SANITY_UPDATE_CHECK_PACKAGE: packageName
|
|
90
|
+
},
|
|
91
|
+
stdio: debug.enabled ? 'inherit' : 'ignore'
|
|
92
|
+
}).unref();
|
|
58
93
|
}
|
|
59
94
|
|
|
60
95
|
//# sourceMappingURL=updateChecker.js.map
|