{"version":3,"file":"dependencies.mjs","sources":["../../../../src/node/core/dependencies.ts"],"sourcesContent":["import os from 'node:os';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport semver, { SemVer } from 'semver';\nimport resolveFrom from 'resolve-from';\nimport execa, { CommonOptions, ExecaReturnValue } from 'execa';\nimport readPkgUp, { PackageJson } from 'read-pkg-up';\nimport type { BuildOptions } from '../build';\nimport { getPackageManager } from './managers';\n\n/**\n * From V5 this will be imported from the package.json of `@strapi/strapi`.\n */\nconst PEER_DEPS = {\n  react: '^18.0.0',\n  'react-dom': '^18.0.0',\n  'react-router-dom': '^6.0.0',\n  'styled-components': '^6.0.0',\n};\n\ninterface CheckRequiredDependenciesResult {\n  didInstall: boolean;\n}\n\ninterface DepToInstall {\n  name: string;\n  wantedVersion: string;\n  declaredVersion?: never;\n}\n\n/**\n * Checks the user's project that it has declared and installed the required dependencies\n * needed by the Strapi admin project. Whilst generally speaking most modules will be\n * declared by the actual packages there are some packages where you only really want one of\n * and thus they are declared as peer dependencies – react / styled-components / etc.\n *\n * If these deps are not installed or declared, then we prompt the user to correct this. In\n * V4 this is not a hard requirement, but in V5 it will be. Might as well get people started now.\n */\nconst checkRequiredDependencies = async ({\n  cwd,\n  logger,\n}: Pick<BuildOptions, 'cwd' | 'logger'>): Promise<CheckRequiredDependenciesResult> => {\n  /**\n   * This enables us to use experimental deps for libraries like\n   * react or styled-components. This is useful for testing against.\n   */\n  if (process.env.USE_EXPERIMENTAL_DEPENDENCIES === 'true') {\n    logger.warn('You are using experimental dependencies that may not be compatible with Strapi.');\n    return { didInstall: false };\n  }\n\n  const pkg = await readPkgUp({ cwd });\n\n  if (!pkg) {\n    throw new Error(`Could not find package.json at path: ${cwd}`);\n  }\n\n  logger.debug('Loaded package.json:', os.EOL, pkg.packageJson);\n\n  interface DepToReview {\n    name: string;\n    wantedVersion: string;\n    declaredVersion: string;\n  }\n\n  /**\n   * Run through each of the peer deps and figure out if they need to be\n   * installed or they need their version checked against.\n   */\n  const { install, review } = Object.entries(PEER_DEPS).reduce<{\n    install: DepToInstall[];\n    review: DepToReview[];\n  }>(\n    (acc, [name, version]) => {\n      if (!pkg.packageJson.dependencies) {\n        throw new Error(`Could not find dependencies in package.json at path: ${cwd}`);\n      }\n\n      const declaredVersion = pkg.packageJson.dependencies[name];\n\n      if (!declaredVersion) {\n        acc.install.push({\n          name,\n          wantedVersion: version,\n        });\n      } else {\n        acc.review.push({\n          name,\n          wantedVersion: version,\n          declaredVersion,\n        });\n      }\n\n      return acc;\n    },\n    {\n      install: [],\n      review: [],\n    }\n  );\n\n  if (install.length > 0) {\n    logger.info(\n      'The Strapi admin needs to install the following dependencies:',\n      os.EOL,\n      install.map(({ name, wantedVersion }) => `  - ${name}@${wantedVersion}`).join(os.EOL)\n    );\n\n    await installDependencies(install, {\n      cwd,\n      logger,\n    });\n\n    const [file, ...args] = process.argv;\n\n    /**\n     * Re-run the same command after installation e.g. strapi build because the yarn.lock might\n     * not be the same and could break installations. It's not the best solution, but it works.\n     */\n    await execa(file, args, { cwd, stdio: 'inherit' });\n    return { didInstall: true };\n  }\n\n  if (review.length) {\n    const errors: string[] = [];\n\n    for (const dep of review) {\n      // The version specified in package.json could be incorrect, eg `foo`\n      let minDeclaredVersion: SemVer | null = null;\n      try {\n        minDeclaredVersion = semver.minVersion(dep.declaredVersion);\n      } catch (err) {\n        // Intentional fall-through (variable will be left as null, throwing below)\n      }\n\n      if (!minDeclaredVersion) {\n        errors.push(\n          `The declared dependency, ${dep.name} has an invalid version in package.json: ${dep.declaredVersion}`\n        );\n      } else if (!semver.satisfies(minDeclaredVersion, dep.wantedVersion)) {\n        /**\n         * The delcared version should be semver compatible with our required version\n         * of the dependency. If it's not, we should advise the user to change it.\n         */\n        logger.warn(\n          [\n            `Declared version of ${dep.name} (${minDeclaredVersion}) is not compatible with the version required by Strapi (${dep.wantedVersion}).`,\n            'You may experience issues, we recommend you change this.',\n          ].join(os.EOL)\n        );\n      }\n\n      const installedVersion = await getModuleVersion(dep.name, cwd);\n\n      if (!installedVersion) {\n        /**\n         * TODO: when we know the packageManager we can advise the actual install command.\n         */\n        errors.push(\n          `The declared dependency, ${dep.name} is not installed. You should install before re-running this command`\n        );\n      } else if (!semver.satisfies(installedVersion, dep.wantedVersion)) {\n        logger.warn(\n          [\n            `Declared version of ${dep.name} (${installedVersion}) is not compatible with the version required by Strapi (${dep.wantedVersion}).`,\n            'You may experience issues, we recommend you change this.',\n          ].join(os.EOL)\n        );\n      }\n    }\n\n    if (errors.length > 0 && process.env.NODE_ENV === 'development') {\n      throw new Error(`${os.EOL}- ${errors.join(`${os.EOL}- `)}`);\n    }\n  }\n\n  return { didInstall: false };\n};\n\nconst getModule = async (name: string, cwd: string): Promise<PackageJson | null> => {\n  const modulePackagePath = resolveFrom.silent(cwd, path.join(name, 'package.json'));\n  if (!modulePackagePath) {\n    return null;\n  }\n  const file = await fs.readFile(modulePackagePath, 'utf8').then((res) => JSON.parse(res));\n\n  return file;\n};\n\nconst getModuleVersion = async (name: string, cwd: string): Promise<string | null> => {\n  const pkg = await getModule(name, cwd);\n\n  return pkg?.version || null;\n};\n\nconst installDependencies = async (\n  install: DepToInstall[],\n  { cwd, logger }: Pick<BuildOptions, 'cwd' | 'logger'>\n) => {\n  const packageManager = getPackageManager();\n\n  if (!packageManager) {\n    logger.error(\n      'Could not find a supported package manager, please install the dependencies manually.'\n    );\n    process.exit(1);\n  }\n\n  const execOptions: CommonOptions<'utf8'> = {\n    encoding: 'utf8',\n    cwd,\n    stdio: 'inherit',\n  };\n\n  const packages = install.map(({ name, wantedVersion }) => `${name}@${wantedVersion}`);\n\n  let result: ExecaReturnValue<string> | undefined;\n\n  if (packageManager === 'npm') {\n    const npmArgs = ['install', '--legacy-peer-deps', '--save', ...packages];\n    logger.info(`Running 'npm ${npmArgs.join(' ')}'`);\n    result = await execa('npm', npmArgs, execOptions);\n  } else if (packageManager === 'yarn') {\n    const yarnArgs = ['add', ...packages];\n    logger.info(`Running 'yarn ${yarnArgs.join(' ')}'`);\n    result = await execa('yarn', yarnArgs, execOptions);\n  } else if (packageManager === 'pnpm') {\n    const pnpmArgs = ['add', '--save-prod', ...packages];\n    logger.info(`Running 'pnpm ${pnpmArgs.join(' ')}'`);\n    result = await execa('pnpm', pnpmArgs, execOptions);\n  }\n\n  if (result?.exitCode || result?.failed) {\n    throw new Error('Package installation failed');\n  }\n};\n\nexport { checkRequiredDependencies, getModule };\nexport type { CheckRequiredDependenciesResult, PackageJson };\n"],"names":["PEER_DEPS","react","checkRequiredDependencies","cwd","logger","process","env","USE_EXPERIMENTAL_DEPENDENCIES","warn","didInstall","pkg","readPkgUp","Error","debug","os","EOL","packageJson","install","review","Object","entries","reduce","acc","name","version","dependencies","declaredVersion","push","wantedVersion","length","info","map","join","installDependencies","file","args","argv","execa","stdio","errors","dep","minDeclaredVersion","semver","minVersion","err","satisfies","installedVersion","getModuleVersion","NODE_ENV","getModule","modulePackagePath","resolveFrom","silent","path","fs","readFile","then","res","JSON","parse","packageManager","getPackageManager","error","exit","execOptions","encoding","packages","result","npmArgs","yarnArgs","pnpmArgs","exitCode","failed"],"mappings":"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"}