create-nx-workspace
Version:
247 lines (246 loc) • 9.55 kB
JavaScript
;
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';
}