@sanity/cli 3.93.1-next.9.198ab74452 → 3.94.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.
Files changed (149) hide show
  1. package/lib/_chunks-cjs/cli.js +1 -1
  2. package/lib/_chunks-cjs/cli.js.map +1 -1
  3. package/package.json +10 -11
  4. package/templates/blog/README.md +1 -1
  5. package/templates/clean/README.md +1 -1
  6. package/templates/ecommerce/README.md +1 -1
  7. package/templates/get-started/README.md +1 -1
  8. package/templates/moviedb/README.md +1 -1
  9. package/templates/quickstart/README.md +1 -1
  10. package/src/CommandRunner.ts +0 -219
  11. package/src/__telemetry__/cli.telemetry.ts +0 -29
  12. package/src/__telemetry__/init.telemetry.ts +0 -95
  13. package/src/actions/codemod/codemodAction.ts +0 -109
  14. package/src/actions/codemod/mods/deskRename.ts +0 -19
  15. package/src/actions/codemod/mods/index.ts +0 -9
  16. package/src/actions/codemod/mods/partsTypeDirective.ts +0 -28
  17. package/src/actions/codemod/mods/reactIconsV3.ts +0 -54
  18. package/src/actions/codemod/types.ts +0 -10
  19. package/src/actions/init-plugin/bootstrapFromTemplate.ts +0 -196
  20. package/src/actions/init-project/bootstrapLocalTemplate.ts +0 -201
  21. package/src/actions/init-project/bootstrapRemoteTemplate.ts +0 -118
  22. package/src/actions/init-project/createAppCliConfig.ts +0 -24
  23. package/src/actions/init-project/createCliConfig.ts +0 -31
  24. package/src/actions/init-project/createPackageManifest.ts +0 -102
  25. package/src/actions/init-project/createStudioConfig.ts +0 -53
  26. package/src/actions/init-project/determineAppTemplate.ts +0 -13
  27. package/src/actions/init-project/fsUtils.ts +0 -40
  28. package/src/actions/init-project/git.ts +0 -59
  29. package/src/actions/init-project/initProject.ts +0 -1708
  30. package/src/actions/init-project/processTemplate.ts +0 -55
  31. package/src/actions/init-project/promptForDatasetName.ts +0 -68
  32. package/src/actions/init-project/prompts/index.ts +0 -51
  33. package/src/actions/init-project/prompts/nextjs.ts +0 -59
  34. package/src/actions/init-project/readPackageJson.ts +0 -17
  35. package/src/actions/init-project/templates/appQuickstart.ts +0 -23
  36. package/src/actions/init-project/templates/appSanityUi.ts +0 -25
  37. package/src/actions/init-project/templates/blog.ts +0 -5
  38. package/src/actions/init-project/templates/clean.ts +0 -5
  39. package/src/actions/init-project/templates/getStarted.ts +0 -37
  40. package/src/actions/init-project/templates/index.ts +0 -24
  41. package/src/actions/init-project/templates/moviedb.ts +0 -35
  42. package/src/actions/init-project/templates/nextjs/index.ts +0 -238
  43. package/src/actions/init-project/templates/nextjs/schemaTypes/blog.ts +0 -252
  44. package/src/actions/init-project/templates/quickstart.ts +0 -5
  45. package/src/actions/init-project/templates/shopify.ts +0 -78
  46. package/src/actions/init-project/templates/shopifyOnline.ts +0 -50
  47. package/src/actions/init-project/updateInitialTemplateMetadata.ts +0 -24
  48. package/src/actions/login/login.telemetry.ts +0 -18
  49. package/src/actions/login/login.ts +0 -350
  50. package/src/actions/login/types.ts +0 -19
  51. package/src/actions/project/createProject.ts +0 -36
  52. package/src/actions/telemetry/setTelemetryConsent.ts +0 -101
  53. package/src/actions/typegen/generate.telemetry.ts +0 -20
  54. package/src/actions/typegen/generateAction.ts +0 -210
  55. package/src/actions/versions/findSanityModuleVersions.ts +0 -185
  56. package/src/actions/yarn/events.ts +0 -73
  57. package/src/cli.ts +0 -350
  58. package/src/cliClient.ts +0 -62
  59. package/src/commands/blueprints/addBlueprintsCommand.ts +0 -155
  60. package/src/commands/blueprints/blueprintsGroup.ts +0 -10
  61. package/src/commands/blueprints/configBlueprintsCommand.ts +0 -86
  62. package/src/commands/blueprints/deployBlueprintsCommand.ts +0 -63
  63. package/src/commands/blueprints/destroyBlueprintsCommand.ts +0 -78
  64. package/src/commands/blueprints/infoBlueprintsCommand.ts +0 -55
  65. package/src/commands/blueprints/initBlueprintsCommand.ts +0 -99
  66. package/src/commands/blueprints/logsBlueprintsCommand.ts +0 -66
  67. package/src/commands/blueprints/planBlueprintsCommand.ts +0 -51
  68. package/src/commands/blueprints/stacksBlueprintsCommand.ts +0 -66
  69. package/src/commands/codemod/codemodCommand.ts +0 -32
  70. package/src/commands/debug/debugCommand.ts +0 -26
  71. package/src/commands/debug/printDebugInfo.ts +0 -243
  72. package/src/commands/docs/docsCommand.ts +0 -20
  73. package/src/commands/functions/devFunctionsCommand.ts +0 -73
  74. package/src/commands/functions/envFunctionsCommand.ts +0 -105
  75. package/src/commands/functions/functionsGroup.ts +0 -10
  76. package/src/commands/functions/logsFunctionsCommand.ts +0 -94
  77. package/src/commands/functions/testFunctionsCommand.ts +0 -93
  78. package/src/commands/help/helpCommand.ts +0 -21
  79. package/src/commands/help/showHelp.ts +0 -58
  80. package/src/commands/index.ts +0 -72
  81. package/src/commands/init/initCommand.ts +0 -123
  82. package/src/commands/install/installCommand.ts +0 -38
  83. package/src/commands/learn/learnCommand.ts +0 -20
  84. package/src/commands/login/loginCommand.ts +0 -29
  85. package/src/commands/logout/logoutCommand.ts +0 -52
  86. package/src/commands/manage/manageCommand.ts +0 -24
  87. package/src/commands/projects/listProjectsCommand.ts +0 -62
  88. package/src/commands/projects/projectsGroup.ts +0 -10
  89. package/src/commands/telemetry/disableTelemetryCommand.ts +0 -19
  90. package/src/commands/telemetry/enableTelemetryCommand.ts +0 -19
  91. package/src/commands/telemetry/telemetryGroup.ts +0 -10
  92. package/src/commands/telemetry/telemetryStatusCommand.ts +0 -89
  93. package/src/commands/typegen/generateTypesCommand.ts +0 -56
  94. package/src/commands/typegen/typegenGroup.ts +0 -10
  95. package/src/commands/versions/printVersionResult.ts +0 -51
  96. package/src/commands/versions/versionsCommand.ts +0 -13
  97. package/src/config.ts +0 -14
  98. package/src/debug.ts +0 -3
  99. package/src/index.ts +0 -6
  100. package/src/outputters/cliOutputter.ts +0 -47
  101. package/src/packageManager/getInstallCommand.ts +0 -26
  102. package/src/packageManager/getUpgradeCommand.ts +0 -52
  103. package/src/packageManager/index.ts +0 -33
  104. package/src/packageManager/installPackages.ts +0 -99
  105. package/src/packageManager/packageManagerChoice.ts +0 -204
  106. package/src/packageManager/uninstallPackages.ts +0 -44
  107. package/src/packageManager/yarnStub.ts +0 -22
  108. package/src/prompters/cliPrompter.ts +0 -9
  109. package/src/run.ts +0 -8
  110. package/src/studioDependencies.ts +0 -25
  111. package/src/types.ts +0 -387
  112. package/src/util/canLaunchBrowser.ts +0 -12
  113. package/src/util/cliWorker.ts +0 -34
  114. package/src/util/clientWrapper.ts +0 -119
  115. package/src/util/copy.ts +0 -50
  116. package/src/util/createExpiringConfig.ts +0 -80
  117. package/src/util/createTelemetryStore.ts +0 -204
  118. package/src/util/detectRuntime.ts +0 -12
  119. package/src/util/dynamicRequire.ts +0 -17
  120. package/src/util/frameworkPort.ts +0 -63
  121. package/src/util/generateCommandsDocumentation.ts +0 -62
  122. package/src/util/getCliConfig.ts +0 -172
  123. package/src/util/getCliVersion.ts +0 -22
  124. package/src/util/getLocalVersion.ts +0 -35
  125. package/src/util/getProjectDefaults.ts +0 -103
  126. package/src/util/getProviderName.ts +0 -9
  127. package/src/util/getUserConfig.ts +0 -15
  128. package/src/util/isCi.ts +0 -7
  129. package/src/util/isCommandGroup.ts +0 -7
  130. package/src/util/isInteractive.ts +0 -3
  131. package/src/util/isTrueish.ts +0 -8
  132. package/src/util/journeyConfig.ts +0 -280
  133. package/src/util/loadEnv.ts +0 -110
  134. package/src/util/mergeCommands.ts +0 -78
  135. package/src/util/neatStack.ts +0 -30
  136. package/src/util/noSuchCommandText.ts +0 -91
  137. package/src/util/parseArguments.ts +0 -76
  138. package/src/util/pathExists.ts +0 -14
  139. package/src/util/readJson.ts +0 -6
  140. package/src/util/readdirRecursive.ts +0 -26
  141. package/src/util/remoteTemplate.ts +0 -320
  142. package/src/util/resolveLatestVersions.ts +0 -24
  143. package/src/util/resolveRootDir.ts +0 -59
  144. package/src/util/telemetryDisclosure.ts +0 -45
  145. package/src/util/updateNotifier.ts +0 -158
  146. package/src/util/writeJson.ts +0 -6
  147. package/src/workers/getAndWriteJourneySchema.ts +0 -7
  148. package/src/workers/getCliConfig.ts +0 -14
  149. package/src/workers/typegenGenerate.ts +0 -228
@@ -1,56 +0,0 @@
1
- import {type CliCommandDefinition} from '../../types'
2
-
3
- const description = 'Generates TypeScript types from schema types and GROQ queries'
4
-
5
- const helpText = `
6
- Sanity TypeGen (Beta)
7
- This command is currently in beta and may undergo significant changes. Feedback is welcome!
8
-
9
- Usage
10
- sanity typegen generate [options]
11
-
12
- Options:
13
- --config-path <path>
14
- Specifies the path to the typegen configuration file. This file should be a JSON file that contains settings for the type generation process.
15
- Default: "sanity-typegen.json"
16
-
17
- --help, -h
18
- Displays this help message, providing information on command usage and options.
19
-
20
- Examples:
21
- Generate TypeScript type definitions from a Sanity Studio schema extracted using the \`sanity schema extract\` command.
22
- $ sanity typegen generate
23
-
24
- Configuration:
25
- This command can utilize configuration settings defined in a \`sanity-typegen.json\` file. These settings include:
26
-
27
- - "path": Specifies a glob pattern to locate your TypeScript or JavaScript files.
28
- Default: "./src/**/*.{ts,tsx,js,jsx}"
29
-
30
- - "schema": Defines the path to your Sanity schema file. This file should be generated using the \`sanity schema extract\` command.
31
- Default: "schema.json"
32
-
33
- - "generates": Indicates the path where the generated TypeScript type definitions will be saved.
34
- Default: "./sanity.types.ts"
35
-
36
- The default configuration values listed above are used if not overridden in your \`sanity-typegen.json\` configuration file. To customize the behavior of the type generation, adjust these properties in the configuration file according to your project's needs.
37
-
38
- Note:
39
- - The \`sanity schema extract\` command is a prerequisite for extracting your Sanity Studio schema into a \`schema.json\` file, which is then used by the \`sanity typegen generate\` command to generate type definitions.
40
- - While this tool is in beta, we encourage you to experiment with these configurations and provide feedback to help improve its functionality and usability.
41
- `
42
-
43
- const generateTypegenCommand: CliCommandDefinition = {
44
- name: 'generate',
45
- group: 'typegen',
46
- signature: '',
47
- description,
48
- helpText,
49
- action: async (args, context) => {
50
- const mod = await import('../../actions/typegen/generateAction')
51
-
52
- return mod.default(args, context)
53
- },
54
- } satisfies CliCommandDefinition
55
-
56
- export default generateTypegenCommand
@@ -1,10 +0,0 @@
1
- import {type CliCommandGroupDefinition} from '../../types'
2
-
3
- const typegenGroup: CliCommandGroupDefinition = {
4
- name: 'typegen',
5
- signature: '[COMMAND]',
6
- isGroupRoot: true,
7
- description: 'Beta: Generate TypeScript types for schema and GROQ',
8
- }
9
-
10
- export default typegenGroup
@@ -1,51 +0,0 @@
1
- import chalk from 'chalk'
2
- import {padEnd, padStart} from 'lodash'
3
-
4
- import {
5
- findSanityModuleVersions,
6
- type ModuleVersionResult,
7
- } from '../../actions/versions/findSanityModuleVersions'
8
- import {type CliCommandAction} from '../../types'
9
-
10
- const printVersionResultCommand: CliCommandAction = async (args, context) => {
11
- const versions = await findSanityModuleVersions(context, {target: 'latest'})
12
- printResult(versions, context.output.print)
13
- }
14
-
15
- export default printVersionResultCommand
16
-
17
- export function printResult(versions: ModuleVersionResult[], print: (msg: string) => void): void {
18
- const {versionLength, formatName} = getFormatters(versions)
19
- versions.forEach((mod) => {
20
- const version = padStart(mod.installed || '<missing>', versionLength)
21
- const latest =
22
- mod.installed === mod.latest
23
- ? chalk.green('(up to date)')
24
- : `(latest: ${chalk.yellow(mod.latest)})`
25
-
26
- print(`${formatName(getDisplayName(mod))} ${version} ${latest}`)
27
- })
28
- }
29
-
30
- export function getFormatters(versions: ModuleVersionResult[]): {
31
- nameLength: number
32
- versionLength: number
33
- formatName: (name: string) => string
34
- } {
35
- const nameLength = versions.reduce((max, mod) => Math.max(max, getDisplayName(mod).length), 0)
36
- const versionLength = versions.reduce(
37
- (max, mod) => Math.max(max, (mod.installed || '<missing>').length),
38
- 0,
39
- )
40
-
41
- const formatName = (name: string): string =>
42
- padEnd(name, nameLength + 1)
43
- .replace(/^@sanity\/(.*?)(\s|$)/, `${chalk.yellow('@sanity/')}${chalk.cyan('$1')}$2`)
44
- .replace(/^sanity(\s|$)/, `${chalk.yellow('sanity')}$1`)
45
-
46
- return {nameLength, versionLength, formatName}
47
- }
48
-
49
- function getDisplayName(mod: ModuleVersionResult): string {
50
- return mod.isGlobal ? `${mod.name} (global)` : mod.name
51
- }
@@ -1,13 +0,0 @@
1
- import printVersionResult from './printVersionResult'
2
-
3
- const help = `
4
- Shows a list of installed Sanity modules and their respective versions, and
5
- checks the npm registry for the latest available versions.`
6
-
7
- export default {
8
- name: 'versions',
9
- signature: '',
10
- description: 'Shows installed versions of Sanity Studio and components',
11
- helpText: help,
12
- action: printVersionResult,
13
- }
package/src/config.ts DELETED
@@ -1,14 +0,0 @@
1
- import {type CliConfig} from './types'
2
-
3
- /** @beta */
4
- export function defineCliConfig(config: CliConfig): CliConfig {
5
- return config
6
- }
7
-
8
- /**
9
- * @deprecated Use `defineCliConfig` instead
10
- * @beta
11
- */
12
- export function createCliConfig(config: CliConfig): CliConfig {
13
- return config
14
- }
package/src/debug.ts DELETED
@@ -1,3 +0,0 @@
1
- import debugIt from 'debug'
2
-
3
- export const debug = debugIt('sanity:cli')
package/src/index.ts DELETED
@@ -1,6 +0,0 @@
1
- export type {CliClientOptions} from './cliClient'
2
- export {getCliClient} from './cliClient'
3
- export * from './config'
4
- export * from './types'
5
- export {loadEnv} from './util/loadEnv'
6
- export type {SanityClient} from '@sanity/client'
@@ -1,47 +0,0 @@
1
- /* eslint-disable no-console */
2
- import chalk from 'chalk'
3
- import ora, {type Options, type Ora} from 'ora'
4
-
5
- const SYMBOL_CHECK = chalk.green('✓')
6
- const SYMBOL_WARN = chalk.yellow('⚠')
7
- const SYMBOL_FAIL = chalk.red('✗')
8
-
9
- let isFirstClear = true
10
-
11
- export default {
12
- print(...args: unknown[]): void {
13
- console.log(...args)
14
- },
15
-
16
- success(firstPartOfMessage: unknown, ...args: unknown[]): void {
17
- console.log(`${SYMBOL_CHECK} ${firstPartOfMessage}`, ...args)
18
- },
19
-
20
- warn(firstPartOfMessage: unknown, ...args: unknown[]): void {
21
- console.warn(`${SYMBOL_WARN} ${firstPartOfMessage}`, ...args)
22
- },
23
-
24
- error(firstPartOfMessage: unknown, ...args: unknown[]): void {
25
- if (firstPartOfMessage instanceof Error) {
26
- console.error(`${SYMBOL_FAIL} ${chalk.red(firstPartOfMessage.stack)}`)
27
- } else {
28
- console.error(`${SYMBOL_FAIL} ${firstPartOfMessage}`, ...args)
29
- }
30
- },
31
-
32
- clear: (): void => {
33
- // On first run, clear completely so it doesn't show half screen on Windows.
34
- // On next runs, use a different sequence that properly scrolls back.
35
- process.stdout.write(isFirstClear ? '\x1bc' : '\x1b[2J\x1b[0f')
36
- isFirstClear = false
37
- },
38
-
39
- spinner(options: Options): Ora {
40
- const spinner = ora(options)
41
- // Override the default status methods to use custom symbols instead of emojis
42
- spinner.succeed = (text?: string) => spinner.stopAndPersist({text, symbol: SYMBOL_CHECK})
43
- spinner.warn = (text?: string) => spinner.stopAndPersist({text, symbol: SYMBOL_WARN})
44
- spinner.fail = (text?: string) => spinner.stopAndPersist({text, symbol: SYMBOL_FAIL})
45
- return spinner
46
- },
47
- }
@@ -1,26 +0,0 @@
1
- import {getPackageManagerChoice} from './packageManagerChoice'
2
-
3
- export async function getInstallCommand(options: {
4
- workDir: string
5
- pkgNames?: string[]
6
- depType?: 'dev' | 'prod' | 'peer'
7
- }): Promise<string> {
8
- const {workDir, depType = 'prod', pkgNames} = options
9
- const {chosen} = await getPackageManagerChoice(workDir, {interactive: false})
10
-
11
- // eg `npm install`, `yarn install`, `pnpm install`
12
- if (!pkgNames || pkgNames.length === 0) {
13
- return `${chosen} install`
14
- }
15
-
16
- const pkgNameString = pkgNames.join(' ')
17
-
18
- if (chosen === 'yarn') {
19
- const flag = depType === 'dev' || depType === 'peer' ? ` --${depType}` : ''
20
- return `yarn add ${pkgNameString}${flag}`
21
- } else if (chosen === 'pnpm') {
22
- return `pnpm add ${pkgNameString} --save-${depType}`
23
- }
24
-
25
- return `npm install ${pkgNameString} --save-${depType}`
26
- }
@@ -1,52 +0,0 @@
1
- import path from 'node:path'
2
-
3
- import isInstalledGlobally from 'is-installed-globally'
4
-
5
- import {debug} from '../debug'
6
- import {getPackageManagerChoice} from './packageManagerChoice'
7
-
8
- const cliPkgName = '@sanity/cli'
9
-
10
- interface Options {
11
- cwd?: string
12
- workDir?: string
13
- }
14
-
15
- export async function getCliUpgradeCommand(options: Options = {}): Promise<string> {
16
- let {cwd, workDir} = options
17
- cwd = path.resolve(cwd || process.cwd())
18
- workDir = path.resolve(workDir || cwd)
19
-
20
- if (isInstalledGlobally && isInstalledUsingYarn()) {
21
- debug('CLI is installed globally with yarn')
22
- return `yarn global add ${cliPkgName}`
23
- }
24
-
25
- if (isInstalledGlobally) {
26
- debug('CLI is installed globally with npm')
27
- return `npm install -g ${cliPkgName}`
28
- }
29
-
30
- const cmds = cwd === workDir ? [] : [`cd ${path.relative(cwd, workDir)}`]
31
-
32
- const {chosen} = await getPackageManagerChoice(workDir, {interactive: false})
33
-
34
- if (chosen === 'yarn') {
35
- cmds.push(`yarn upgrade ${cliPkgName}`)
36
- } else if (chosen === 'pnpm') {
37
- cmds.push(`pnpm update ${cliPkgName}`)
38
- } else {
39
- cmds.push(`npm update ${cliPkgName}`)
40
- }
41
-
42
- return cmds.join(' && ')
43
- }
44
-
45
- function isInstalledUsingYarn() {
46
- const isWindows = process.platform === 'win32'
47
- const yarnPath = isWindows
48
- ? path.join('Yarn', 'config', 'global')
49
- : path.join('.config', 'yarn', 'global')
50
-
51
- return __dirname.includes(yarnPath)
52
- }
@@ -1,33 +0,0 @@
1
- import {getInstallCommand} from './getInstallCommand'
2
- import {getCliUpgradeCommand} from './getUpgradeCommand'
3
- import {installDeclaredPackages, installNewPackages} from './installPackages'
4
- import {getPackageManagerChoice} from './packageManagerChoice'
5
- import {uninstallPackages} from './uninstallPackages'
6
- import {getYarnStub} from './yarnStub'
7
-
8
- // Exported for internal CLI usage
9
- export {
10
- getCliUpgradeCommand,
11
- getInstallCommand,
12
- getPackageManagerChoice,
13
- getYarnStub,
14
- installDeclaredPackages,
15
- installNewPackages,
16
- uninstallPackages,
17
- }
18
-
19
- // Exported for use in `sanity` (formerly `@sanity/core`)
20
-
21
- /**
22
- * @internal
23
- */
24
- export const cliPackageManager = {
25
- getInstallCommand,
26
- getPackageManagerChoice,
27
- installNewPackages,
28
- }
29
-
30
- /**
31
- * @internal
32
- */
33
- export type CliPackageManager = typeof cliPackageManager
@@ -1,99 +0,0 @@
1
- import execa, {type CommonOptions, type ExecaReturnValue} from 'execa'
2
-
3
- import {type CliCommandContext} from '../types'
4
- import {getPartialEnvWithNpmPath, type PackageManager} from './packageManagerChoice'
5
-
6
- export interface InstallOptions {
7
- packageManager: PackageManager
8
- packages: string[]
9
- }
10
-
11
- export async function installDeclaredPackages(
12
- cwd: string,
13
- packageManager: PackageManager,
14
- context: Pick<CliCommandContext, 'output'>,
15
- ): Promise<void> {
16
- const {output} = context
17
- const execOptions: CommonOptions<'utf8'> = {
18
- encoding: 'utf8',
19
- env: getPartialEnvWithNpmPath(cwd),
20
- cwd,
21
- stdio: 'pipe',
22
- }
23
-
24
- // results of running execa with the selected package manager
25
- let result: ExecaReturnValue<string> | undefined
26
-
27
- type PackageManagerLibs = Exclude<PackageManager, 'manual'>
28
- type InstallerArgs = {[key in PackageManagerLibs]: string[]}
29
-
30
- const installerArgs: InstallerArgs = {
31
- npm: ['install', '--legacy-peer-deps'],
32
- yarn: ['install'],
33
- pnpm: ['install'],
34
- bun: ['install'],
35
- }
36
-
37
- async function handleInstall(cmd: PackageManager, args: InstallerArgs[PackageManagerLibs]) {
38
- // Start a spinner for the install process
39
- const progress = output.spinner(`Running ${cmd} ${args.join(' ')}\n`).start()
40
-
41
- // Perform the install command with execa
42
- result = await execa(cmd, args, execOptions)
43
-
44
- // If the install fails, log execa's stdout and throw…
45
- if (result?.exitCode || result?.failed) {
46
- progress.fail()
47
- output.print(result.stdout)
48
- throw new Error('Dependency installation failed')
49
- } else {
50
- // …otherwise, just mark the install as successful
51
- progress.succeed()
52
- }
53
- }
54
-
55
- if (packageManager === 'manual') {
56
- output.print(`Manual installation selected — run 'npm ${installerArgs.npm} or equivalent'`)
57
- } else {
58
- await handleInstall(packageManager, installerArgs[packageManager])
59
- }
60
- }
61
-
62
- export async function installNewPackages(
63
- options: InstallOptions,
64
- context: Pick<CliCommandContext, 'output' | 'workDir'>,
65
- ): Promise<void> {
66
- const {packageManager, packages} = options
67
- const {output, workDir} = context
68
- const execOptions: CommonOptions<'utf8'> = {
69
- encoding: 'utf8',
70
- env: getPartialEnvWithNpmPath(workDir),
71
- cwd: workDir,
72
- stdio: 'inherit',
73
- }
74
-
75
- const npmArgs = ['install', '--legacy-peer-deps', '--save', ...packages]
76
- let result: ExecaReturnValue<string> | undefined
77
- if (packageManager === 'npm') {
78
- output.print(`Running 'npm ${npmArgs.join(' ')}'`)
79
- result = await execa('npm', npmArgs, execOptions)
80
- } else if (packageManager === 'yarn') {
81
- const yarnArgs = ['add', ...packages]
82
- output.print(`Running 'yarn ${yarnArgs.join(' ')}'`)
83
- result = await execa('yarn', yarnArgs, execOptions)
84
- } else if (packageManager === 'pnpm') {
85
- const pnpmArgs = ['add', '--save-prod', ...packages]
86
- output.print(`Running 'pnpm ${pnpmArgs.join(' ')}'`)
87
- result = await execa('pnpm', pnpmArgs, execOptions)
88
- } else if (packageManager === 'bun') {
89
- const bunArgs = ['add', ...packages]
90
- output.print(`Running 'bun ${bunArgs.join(' ')}'`)
91
- result = await execa('bun', bunArgs, execOptions)
92
- } else if (packageManager === 'manual') {
93
- output.print(`Manual installation selected - run 'npm ${npmArgs.join(' ')}' or equivalent`)
94
- }
95
-
96
- if (result?.exitCode || result?.failed) {
97
- throw new Error('Package installation failed')
98
- }
99
- }
@@ -1,204 +0,0 @@
1
- /* eslint-disable no-process-env */
2
- import path from 'node:path'
3
-
4
- import preferredPM from 'preferred-pm'
5
- import which from 'which'
6
-
7
- import {type CliPrompter} from '../types'
8
- import {isInteractive} from '../util/isInteractive'
9
-
10
- export type PackageManager = 'npm' | 'yarn' | 'pnpm' | 'bun' | 'manual'
11
-
12
- export const ALLOWED_PACKAGE_MANAGERS: PackageManager[] = ['npm', 'yarn', 'pnpm', 'bun', 'manual']
13
- export const allowedPackageManagersString = ALLOWED_PACKAGE_MANAGERS.map((pm) => `"${pm}"`).join(
14
- ' | ',
15
- )
16
-
17
- const EXPERIMENTAL = ['bun']
18
-
19
- /**
20
- * Attempts to resolve the most optimal package manager to use to install/upgrade
21
- * packages/dependencies at a given path. It does so by looking for package manager
22
- * specific lockfiles. If it finds a lockfile belonging to a certain package manager,
23
- * it prioritizes this one. However, if that package manager is not installed, it will
24
- * prompt the user for which one they want to use and hint at the most optimal one
25
- * not being installed.
26
- *
27
- * Note that this function also takes local npm binary paths into account - for instance,
28
- * `yarn` can be installed as a dependency of the project instead of globally, and it
29
- * will use that is available.
30
- *
31
- * The user can also select 'manual' to skip the process and run their preferred package
32
- * manager manually. Commands using this function must take this `manual` choice into
33
- * account and act accordingly if chosen.
34
- *
35
- * @param workDir - The working directory where a lockfile is most likely to be present
36
- * @param options - Pass `interactive: false` to fall back to npm if most optimal is
37
- * not available, instead of prompting
38
- * @returns Object of `chosen` and, if a lockfile is found, the `mostOptimal` choice
39
- */
40
- export async function getPackageManagerChoice(
41
- workDir: string,
42
- options: {interactive: false} | {interactive?: true; prompt: CliPrompter},
43
- ): Promise<{chosen: PackageManager; mostOptimal?: PackageManager}> {
44
- const rootDir = workDir || process.cwd()
45
- const preferred = (await preferredPM(rootDir))?.name
46
-
47
- if (preferred && (await hasCommand(preferred, rootDir))) {
48
- // There is an optimal/preferred package manager, and the user has it installed!
49
- return {chosen: preferred, mostOptimal: preferred}
50
- }
51
-
52
- const mostLikelyPM = await getMostLikelyInstalledPackageManager(rootDir)
53
- const interactive = typeof options.interactive === 'boolean' ? options.interactive : isInteractive
54
- if (!interactive) {
55
- // We can't ask the user for their preference, so fall back to either the one that is being run
56
- // or whatever is installed on the system (npm being the preferred choice).
57
- // Note that the most optimal choice is already picked above if available.
58
- return {chosen: mostLikelyPM || (await getFallback(rootDir)), mostOptimal: preferred}
59
- }
60
-
61
- if (!('prompt' in options)) {
62
- throw new Error('Must pass `prompt` when in interactive mode')
63
- }
64
-
65
- // We can ask the user for their preference, hurray!
66
- const messageSuffix = preferred ? ` (preferred is ${preferred}, but is not installed)` : ''
67
- const installed = await getAvailablePackageManagers(rootDir)
68
- const chosen = await options.prompt.single<PackageManager>({
69
- type: 'list',
70
- choices: installed.map((pm) => ({
71
- value: pm,
72
- name: EXPERIMENTAL.includes(pm) ? `${pm} (experimental)` : pm,
73
- })),
74
- default: preferred || mostLikelyPM,
75
- message: `Package manager to use for installing dependencies?${messageSuffix}`,
76
- })
77
-
78
- return {chosen, mostOptimal: preferred}
79
- }
80
-
81
- async function getFallback(cwd: string): Promise<PackageManager> {
82
- if (await hasNpmInstalled(cwd)) {
83
- return 'npm'
84
- }
85
-
86
- if (await hasYarnInstalled(cwd)) {
87
- return 'yarn'
88
- }
89
-
90
- if (await hasPnpmInstalled(cwd)) {
91
- return 'pnpm'
92
- }
93
-
94
- if (await hasBunInstalled(cwd)) {
95
- return 'bun'
96
- }
97
-
98
- return 'manual'
99
- }
100
-
101
- async function getAvailablePackageManagers(cwd: string): Promise<PackageManager[]> {
102
- const [npm, yarn, pnpm, bun] = await Promise.all([
103
- hasNpmInstalled(cwd),
104
- hasYarnInstalled(cwd),
105
- hasPnpmInstalled(cwd),
106
- hasBunInstalled(cwd),
107
- ])
108
-
109
- const choices = [npm && 'npm', yarn && 'yarn', pnpm && 'pnpm', bun && 'bun', 'manual']
110
- return choices.filter((pm): pm is PackageManager => pm !== false)
111
- }
112
-
113
- export function hasNpmInstalled(cwd?: string): Promise<boolean> {
114
- return hasCommand('npm', cwd)
115
- }
116
-
117
- export function hasYarnInstalled(cwd?: string): Promise<boolean> {
118
- return hasCommand('yarn', cwd)
119
- }
120
-
121
- export function hasPnpmInstalled(cwd?: string): Promise<boolean> {
122
- return hasCommand('pnpm', cwd)
123
- }
124
-
125
- export function hasBunInstalled(cwd?: string): Promise<boolean> {
126
- return hasCommand('bun', cwd)
127
- }
128
-
129
- export function getNpmRunPath(cwd: string): string {
130
- let previous
131
- let cwdPath = path.resolve(cwd)
132
- const result: string[] = []
133
-
134
- while (previous !== cwdPath) {
135
- result.push(path.join(cwdPath, 'node_modules', '.bin'))
136
- previous = cwdPath
137
- cwdPath = path.resolve(cwdPath, '..')
138
- }
139
-
140
- result.push(path.resolve(cwd, process.execPath, '..'))
141
-
142
- const pathEnv = process.env[getPathEnvVarKey()]
143
- return [...result, pathEnv].join(path.delimiter)
144
- }
145
-
146
- export function getPartialEnvWithNpmPath(cwd: string): NodeJS.ProcessEnv {
147
- const key = getPathEnvVarKey()
148
- return {[key]: getNpmRunPath(cwd)}
149
- }
150
-
151
- function getPathEnvVarKey(): string {
152
- if (process.platform !== 'win32') {
153
- return 'PATH'
154
- }
155
-
156
- return (
157
- Object.keys(process.env)
158
- .reverse()
159
- .find((key) => key.toUpperCase() === 'PATH') || 'Path'
160
- )
161
- }
162
-
163
- function getCommandPath(cmd: string, cwd?: string): Promise<string | null> {
164
- const options = cwd ? {path: getNpmRunPath(cwd)} : {}
165
- return which(cmd, options).catch(() => null)
166
- }
167
-
168
- function hasCommand(cmd: string, cwd?: string): Promise<boolean> {
169
- return getCommandPath(cmd, cwd).then((cmdPath) => cmdPath !== null)
170
- }
171
-
172
- async function getMostLikelyInstalledPackageManager(
173
- rootDir: string,
174
- ): Promise<PackageManager | undefined> {
175
- const installed = await getAvailablePackageManagers(rootDir)
176
- const running = getRunningPackageManager()
177
- return running && installed.includes(running) ? running : undefined
178
- }
179
-
180
- function getRunningPackageManager(): PackageManager | undefined {
181
- // Yes, the env var is lowercase - it is set by the package managers themselves
182
- const agent = process.env.npm_config_user_agent || ''
183
-
184
- if (agent.includes('yarn')) {
185
- return 'yarn'
186
- }
187
-
188
- if (agent.includes('pnpm')) {
189
- return 'pnpm'
190
- }
191
-
192
- if (agent.includes('bun')) {
193
- return 'bun'
194
- }
195
-
196
- // Both yarn and pnpm does a `npm/?` thing, thus the slightly different match here
197
- // Theoretically not needed since we check for yarn/pnpm above, but in case other
198
- // package managers do the same thing, we'll (hopefully) catch them here.
199
- if (/^npm\/\d/.test(agent)) {
200
- return 'npm'
201
- }
202
-
203
- return undefined
204
- }
@@ -1,44 +0,0 @@
1
- import execa, {type CommonOptions, type ExecaReturnValue} from 'execa'
2
-
3
- import {type CliCommandContext} from '../types'
4
- import {getPartialEnvWithNpmPath, type PackageManager} from './packageManagerChoice'
5
-
6
- export interface UninstallOptions {
7
- packageManager: PackageManager
8
- packages: string[]
9
- }
10
-
11
- export async function uninstallPackages(
12
- options: UninstallOptions,
13
- context: Pick<CliCommandContext, 'output' | 'workDir'>,
14
- ): Promise<void> {
15
- const {packageManager, packages} = options
16
- const {output, workDir} = context
17
- const execOptions: CommonOptions<'utf8'> = {
18
- encoding: 'utf8',
19
- env: getPartialEnvWithNpmPath(workDir),
20
- cwd: workDir,
21
- stdio: 'inherit',
22
- }
23
-
24
- const npmArgs = ['uninstall', ...packages]
25
- let result: ExecaReturnValue<string> | undefined
26
- if (packageManager === 'npm') {
27
- output.print(`Running 'npm ${npmArgs.join(' ')}'`)
28
- result = await execa('npm', npmArgs, execOptions)
29
- } else if (packageManager === 'yarn') {
30
- const yarnArgs = ['remove', ...packages]
31
- output.print(`Running 'yarn ${yarnArgs.join(' ')}'`)
32
- result = await execa('yarn', yarnArgs, execOptions)
33
- } else if (packageManager === 'pnpm') {
34
- const pnpmArgs = ['remove', ...packages]
35
- output.print(`Running 'pnpm ${pnpmArgs.join(' ')}'`)
36
- result = await execa('pnpm', pnpmArgs, execOptions)
37
- } else if (packageManager === 'manual') {
38
- output.print(`Manual installation selected - run 'npm ${npmArgs.join(' ')}' or equivalent`)
39
- }
40
-
41
- if (result?.exitCode || result?.failed) {
42
- throw new Error('Package installation failed')
43
- }
44
- }