UNPKG

create-nx-workspace

Version:

Smart Repos · Fast Builds

247 lines (246 loc) • 9.55 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.packageManagerList = void 0; exports.detectPackageManager = detectPackageManager; exports.getPackageManagerCommand = getPackageManagerCommand; exports.generatePackageManagerFiles = generatePackageManagerFiles; exports.workspacesToPnpmYaml = workspacesToPnpmYaml; exports.convertStarToWorkspaceProtocol = convertStarToWorkspaceProtocol; exports.findAllWorkspacePackageJsons = findAllWorkspacePackageJsons; exports.findWorkspacePackages = findWorkspacePackages; exports.getPackageManagerVersion = getPackageManagerVersion; exports.detectInvokedPackageManager = detectInvokedPackageManager; const node_child_process_1 = require("node:child_process"); const node_fs_1 = require("node:fs"); const node_path_1 = require("node:path"); /* * Because we don't want to depend on @nx/workspace (to speed up the workspace creation) * we duplicate the helper functions from @nx/workspace in this file. */ exports.packageManagerList = ['pnpm', 'yarn', 'npm', 'bun']; function detectPackageManager(dir = '') { return (0, node_fs_1.existsSync)((0, node_path_1.join)(dir, 'bun.lockb')) || (0, node_fs_1.existsSync)((0, node_path_1.join)(dir, 'bun.lock')) ? 'bun' : (0, node_fs_1.existsSync)((0, node_path_1.join)(dir, 'yarn.lock')) ? 'yarn' : (0, node_fs_1.existsSync)((0, node_path_1.join)(dir, 'pnpm-lock.yaml')) ? 'pnpm' : 'npm'; } /** * Returns commands for the package manager used in the workspace. * By default, the package manager is derived based on the lock file, * but it can also be passed in explicitly. * * Example: * * ```javascript * execSync(`${getPackageManagerCommand().addDev} my-dev-package`); * ``` * */ function getPackageManagerCommand(packageManager = detectPackageManager()) { const pmVersion = getPackageManagerVersion(packageManager); const [pmMajor, pmMinor] = pmVersion.split('.'); switch (packageManager) { case 'yarn': const useBerry = +pmMajor >= 2; const installCommand = 'yarn install --silent'; return { preInstall: `yarn set version ${pmVersion}`, install: useBerry ? installCommand : `${installCommand} --ignore-scripts`, // using npx is necessary to avoid yarn classic manipulating the version detection when using berry exec: useBerry ? 'npx' : 'yarn', globalAdd: 'yarn global add', getRegistryUrl: useBerry ? 'yarn config get npmRegistryServer' : 'yarn config get registry', }; case 'pnpm': let useExec = false; if ((+pmMajor >= 6 && +pmMinor >= 13) || +pmMajor >= 7) { useExec = true; } return { install: 'pnpm install --no-frozen-lockfile --silent --ignore-scripts', exec: useExec ? 'pnpm exec' : 'pnpx', globalAdd: 'pnpm add -g', getRegistryUrl: 'pnpm config get registry', }; case 'npm': return { install: 'npm install --silent --ignore-scripts', exec: 'npx', globalAdd: 'npm i -g', getRegistryUrl: 'npm config get registry', }; case 'bun': // bun doesn't current support programmatically reading config https://github.com/oven-sh/bun/issues/7140 return { install: 'bun install --silent --ignore-scripts', exec: 'bunx', globalAdd: 'bun install -g', }; } } function generatePackageManagerFiles(root, packageManager = detectPackageManager()) { const [pmMajor] = getPackageManagerVersion(packageManager).split('.'); switch (packageManager) { case 'pnpm': // pnpm doesn't support "workspaces" in package.json convertToWorkspaceYaml(root); convertStarToWorkspaceProtocol(root); break; case 'yarn': if (+pmMajor >= 2) { (0, node_fs_1.writeFileSync)((0, node_path_1.join)(root, '.yarnrc.yml'), 'nodeLinker: node-modules\nenableScripts: false'); // avoids errors when using nested yarn projects (0, node_fs_1.writeFileSync)((0, node_path_1.join)(root, 'yarn.lock'), ''); } convertStarToWorkspaceProtocol(root); break; case 'bun': convertStarToWorkspaceProtocol(root); break; // npm handles "*" natively, no conversion needed } } /** * Converts an array of workspace globs to pnpm-workspace.yaml content. */ function workspacesToPnpmYaml(workspaces) { return `packages:\n${workspaces.map((p) => ` - '${p}'`).join('\n')}\n`; } function convertToWorkspaceYaml(root) { const packageJsonPath = (0, node_path_1.join)(root, 'package.json'); if (!(0, node_fs_1.existsSync)(packageJsonPath)) { return; } const packageJson = JSON.parse((0, node_fs_1.readFileSync)(packageJsonPath, 'utf-8')); const workspaces = packageJson.workspaces; if (!workspaces || workspaces.length === 0) { return; } (0, node_fs_1.writeFileSync)((0, node_path_1.join)(root, 'pnpm-workspace.yaml'), workspacesToPnpmYaml(workspaces)); delete packageJson.workspaces; (0, node_fs_1.writeFileSync)(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n'); } /** * Converts "*" dependencies to "workspace:*" in all workspace package.json files. * This is needed for pnpm, yarn, and bun to properly symlink workspace packages. */ function convertStarToWorkspaceProtocol(root) { for (const pkgJsonPath of findAllWorkspacePackageJsons(root)) { try { const pkgJson = JSON.parse((0, node_fs_1.readFileSync)(pkgJsonPath, 'utf-8')); let updated = false; for (const deps of [pkgJson.dependencies, pkgJson.devDependencies]) { if (!deps) continue; for (const [dep, version] of Object.entries(deps)) { if (version === '*') { deps[dep] = 'workspace:*'; updated = true; } } } if (updated) { (0, node_fs_1.writeFileSync)(pkgJsonPath, JSON.stringify(pkgJson, null, 2) + '\n'); } } catch { // Skip invalid package.json files } } } function findAllWorkspacePackageJsons(root, maxDepth = 2) { const results = []; for (const dir of ['packages', 'libs', 'apps']) { const fullPath = (0, node_path_1.join)(root, dir); if ((0, node_fs_1.existsSync)(fullPath)) { findPackageJsonsRecursive(fullPath, 1, maxDepth, results); } } return results; } function findPackageJsonsRecursive(dir, currentDepth, maxDepth, results) { if (currentDepth > maxDepth) { return; } try { const entries = (0, node_fs_1.readdirSync)(dir, { withFileTypes: true }); for (const entry of entries) { if (!entry.isDirectory()) continue; const entryPath = (0, node_path_1.join)(dir, entry.name); const pkgJsonPath = (0, node_path_1.join)(entryPath, 'package.json'); if ((0, node_fs_1.existsSync)(pkgJsonPath)) { results.push(pkgJsonPath); } if (currentDepth < maxDepth) { findPackageJsonsRecursive(entryPath, currentDepth + 1, maxDepth, results); } } } catch { // Skip unreadable directories } } function findWorkspacePackages(root) { const packages = []; const packageJsonPaths = findAllWorkspacePackageJsons(root); for (const pkgJsonPath of packageJsonPaths) { try { const pkgJson = JSON.parse((0, node_fs_1.readFileSync)(pkgJsonPath, 'utf-8')); if (pkgJson.name) { packages.push(pkgJson.name); } } catch { // Skip invalid package.json files } } return packages.sort(); } const pmVersionCache = new Map(); function getPackageManagerVersion(packageManager, cwd = process.cwd()) { if (pmVersionCache.has(packageManager)) { return pmVersionCache.get(packageManager); } const version = (0, node_child_process_1.execSync)(`${packageManager} --version`, { cwd, encoding: 'utf-8', windowsHide: false, }).trim(); pmVersionCache.set(packageManager, version); return version; } /** * Detects which package manager was used to invoke create-nx-{plugin|workspace} command * based on the main Module process that invokes the command * - npx returns 'npm' * - pnpx returns 'pnpm' * - yarn create returns 'yarn' * - bunx returns 'bun' * * Default to 'npm' */ function detectInvokedPackageManager() { if (process.env.npm_config_user_agent) { for (const pm of exports.packageManagerList) { if (process.env.npm_config_user_agent.startsWith(`${pm}/`)) { return pm; } } } if (process.env.npm_execpath) { for (const pm of exports.packageManagerList) { if (process.env.npm_execpath.split(node_path_1.sep).includes(pm)) { return pm; } } } return 'npm'; }