UNPKG

@strapi/strapi

Version:

An open source headless CMS solution to create and manage your own API. It provides a powerful dashboard and features to make your life easier. Databases supported: MySQL, MariaDB, PostgreSQL, SQLite

231 lines (225 loc) 9.65 kB
'use strict'; var os = require('node:os'); var fs = require('node:fs/promises'); var path = require('node:path'); var crypto = require('node:crypto'); var semver = require('semver'); var resolveFrom = require('resolve-from'); var execa = require('execa'); var readPkgUp = require('read-pkg-up'); var managers = require('./managers.js'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } var os__default = /*#__PURE__*/_interopDefault(os); var fs__default = /*#__PURE__*/_interopDefault(fs); var path__default = /*#__PURE__*/_interopDefault(path); var crypto__default = /*#__PURE__*/_interopDefault(crypto); var semver__default = /*#__PURE__*/_interopDefault(semver); var resolveFrom__default = /*#__PURE__*/_interopDefault(resolveFrom); var execa__default = /*#__PURE__*/_interopDefault(execa); var readPkgUp__default = /*#__PURE__*/_interopDefault(readPkgUp); const CACHE_PATH = path__default.default.join('node_modules', '.strapi', 'deps-check.hash'); const hashPackageJson = async (cwd)=>{ try { const content = await fs__default.default.readFile(path__default.default.join(cwd, 'package.json'), 'utf8'); return crypto__default.default.createHash('sha1').update(content).digest('hex'); } catch { return null; } }; const readCachedHash = (cwd)=>fs__default.default.readFile(path__default.default.join(cwd, CACHE_PATH), 'utf8').catch(()=>null); const writeCachedHash = async (cwd, hash)=>{ try { await fs__default.default.mkdir(path__default.default.dirname(path__default.default.join(cwd, CACHE_PATH)), { recursive: true }); await fs__default.default.writeFile(path__default.default.join(cwd, CACHE_PATH), hash, 'utf8'); } catch { // best-effort cache write — silently ignore } }; /** * From V5 this will be imported from the package.json of `@strapi/strapi`. */ const ADMIN_PEER_DEPS = { react: '^18.0.0', 'react-dom': '^18.0.0', 'react-router-dom': '^6.0.0', 'styled-components': '^6.0.0' }; class MissingAdminPeerDepsError extends Error { constructor(missing){ super('Missing required dependencies. Please install them and re-run this command.'); this.name = 'MissingAdminPeerDepsError'; this.missing = missing; } } const loadProjectPackageJson = async (cwd)=>{ const pkg = await readPkgUp__default.default({ cwd }); if (!pkg) { throw new Error(`Could not find package.json at path: ${cwd}`); } if (!pkg.packageJson.dependencies) { throw new Error(`Could not find dependencies in package.json at path: ${cwd}`); } return pkg; }; /** * Returns admin peer dependencies that are not declared in the project's package.json. */ const findUndeclaredAdminPeerDeps = async (cwd)=>{ const pkg = await loadProjectPackageJson(cwd); return Object.entries(ADMIN_PEER_DEPS).reduce((missing, [name, version])=>{ const declaredVersion = pkg.packageJson.dependencies?.[name] ?? pkg.packageJson.devDependencies?.[name]; if (!declaredVersion) { missing.push({ name, wantedVersion: version }); } return missing; }, []); }; const getDeclaredAdminPeerDeps = (packageJson)=>{ return Object.entries(ADMIN_PEER_DEPS).reduce((declared, [name, version])=>{ const declaredVersion = packageJson.dependencies?.[name] ?? packageJson.devDependencies?.[name]; if (declaredVersion) { declared.push({ name, wantedVersion: version, declaredVersion }); } return declared; }, []); }; const getInstallCommandHint = (missing)=>{ const packages = missing.map(({ name, wantedVersion })=>`${name}@${wantedVersion}`).join(' '); const packageManager = managers.getPackageManager(); if (packageManager === 'yarn') { return `yarn add ${packages}`; } if (packageManager === 'pnpm') { return `pnpm add --save-prod ${packages}`; } return `npm install --legacy-peer-deps --save ${packages}`; }; const reportMissingAdminPeerDeps = (logger, missing)=>{ logger.error('The Strapi admin is missing required dependencies:', os__default.default.EOL, missing.map(({ name, wantedVersion })=>` - ${name}@${wantedVersion}`).join(os__default.default.EOL)); logger.error('Please install them manually before re-running this command:', os__default.default.EOL, ` ${getInstallCommandHint(missing)}`); }; /** * Validates declared admin peer dependencies (versions and node_modules presence). */ const validateDeclaredAdminPeerDeps = async (cwd, logger)=>{ const pkg = await loadProjectPackageJson(cwd); const declared = getDeclaredAdminPeerDeps(pkg.packageJson); if (!declared.length) { return; } logger.debug('Loaded package.json:', os__default.default.EOL, pkg.packageJson); const errors = []; for (const dep of declared){ let minDeclaredVersion = null; try { minDeclaredVersion = semver__default.default.minVersion(dep.declaredVersion); } catch (err) { // Intentional fall-through (variable will be left as null, throwing below) } if (!minDeclaredVersion) { errors.push(`The declared dependency, ${dep.name} has an invalid version in package.json: ${dep.declaredVersion}`); } else if (!semver__default.default.satisfies(minDeclaredVersion, dep.wantedVersion)) { logger.warn([ `Declared version of ${dep.name} (${minDeclaredVersion}) is not compatible with the version required by Strapi (${dep.wantedVersion}).`, 'You may experience issues, we recommend you change this.' ].join(os__default.default.EOL)); } const installedVersion = await getModuleVersion(dep.name, cwd); if (!installedVersion) { errors.push(`The declared dependency, ${dep.name} is not installed. You should install before re-running this command`); } else if (!semver__default.default.satisfies(installedVersion, dep.wantedVersion)) { logger.warn([ `Declared version of ${dep.name} (${installedVersion}) is not compatible with the version required by Strapi (${dep.wantedVersion}).`, 'You may experience issues, we recommend you change this.' ].join(os__default.default.EOL)); } } if (errors.length > 0 && process.env.NODE_ENV === 'development') { throw new Error(`${os__default.default.EOL}- ${errors.join(`${os__default.default.EOL}- `)}`); } }; const installAdminPeerDeps = async (missing, { cwd, logger })=>{ const packageManager = managers.getPackageManager(); if (!packageManager) { logger.error('Could not find a supported package manager, please install the dependencies manually.'); process.exit(1); } const execOptions = { encoding: 'utf8', cwd, stdio: 'inherit' }; const packages = missing.map(({ name, wantedVersion })=>`${name}@${wantedVersion}`); let result; if (packageManager === 'npm') { const npmArgs = [ 'install', '--legacy-peer-deps', '--save', ...packages ]; logger.info(`Running 'npm ${npmArgs.join(' ')}'`); result = await execa__default.default('npm', npmArgs, execOptions); } else if (packageManager === 'yarn') { const yarnArgs = [ 'add', ...packages ]; logger.info(`Running 'yarn ${yarnArgs.join(' ')}'`); result = await execa__default.default('yarn', yarnArgs, execOptions); } else if (packageManager === 'pnpm') { const pnpmArgs = [ 'add', '--save-prod', ...packages ]; logger.info(`Running 'pnpm ${pnpmArgs.join(' ')}'`); result = await execa__default.default('pnpm', pnpmArgs, execOptions); } if (result?.exitCode || result?.failed) { throw new Error('Package installation failed'); } }; const reexecCurrentCommand = async (cwd)=>{ const [file, ...args] = process.argv; /** * Re-run the same command after installation e.g. strapi build because the yarn.lock might * not be the same and could break installations. It's not the best solution, but it works. */ await execa__default.default(file, args, { cwd, stdio: 'inherit' }); }; const getModule = async (name, cwd)=>{ const modulePackagePath = resolveFrom__default.default.silent(cwd, path__default.default.join(name, 'package.json')); if (!modulePackagePath) { return null; } const file = await fs__default.default.readFile(modulePackagePath, 'utf8').then((res)=>JSON.parse(res)); return file; }; const getModuleVersion = async (name, cwd)=>{ const pkg = await getModule(name, cwd); return pkg?.version || null; }; exports.ADMIN_PEER_DEPS = ADMIN_PEER_DEPS; exports.MissingAdminPeerDepsError = MissingAdminPeerDepsError; exports.findUndeclaredAdminPeerDeps = findUndeclaredAdminPeerDeps; exports.getInstallCommandHint = getInstallCommandHint; exports.getModule = getModule; exports.hashPackageJson = hashPackageJson; exports.installAdminPeerDeps = installAdminPeerDeps; exports.readCachedHash = readCachedHash; exports.reexecCurrentCommand = reexecCurrentCommand; exports.reportMissingAdminPeerDeps = reportMissingAdminPeerDeps; exports.validateDeclaredAdminPeerDeps = validateDeclaredAdminPeerDeps; exports.writeCachedHash = writeCachedHash; //# sourceMappingURL=dependencies.js.map