ensure support of yarn when install tool deps

This commit is contained in:
Nacho Codoñer
2025-09-08 10:17:39 +02:00
parent ec8849f1cb
commit 303d2294c4
3 changed files with 128 additions and 3 deletions

View File

@@ -81,6 +81,9 @@ async function ensureDependenciesInstalled(dependencies, globalStateKey, package
logInfo(`${dep}`);
});
// Check if this is a Yarn project
const isYarnProj = process.env.YARN_ENABLED === 'true';
// Install dev dependencies
const devDepsToInstall = allDepsToInstall.filter(dep => dep.dev === true || dep.dev == null);
if (devDepsToInstall.length > 0) {
@@ -88,6 +91,7 @@ async function ensureDependenciesInstalled(dependencies, globalStateKey, package
success = await installNpmDependency(devDepsStrings, {
cwd: appDir,
dev: true,
yarn: isYarnProj,
});
}
@@ -95,22 +99,30 @@ async function ensureDependenciesInstalled(dependencies, globalStateKey, package
const depsToInstall = allDepsToInstall.filter(dep => dep.dev === false);
if (depsToInstall.length > 0) {
const depsStrings = depsToInstall.map(dep => `${dep.name}@${dep.version}`);
const depsSuccess = await installNpmDependency(depsStrings, {
let depsSuccess;
depsSuccess = await installNpmDependency(depsStrings, {
cwd: appDir,
dev: false,
yarn: isYarnProj,
});
success = success && depsSuccess;
}
if (!success) {
const isYarnProj = process.env.YARN_ENABLED === 'true';
const installCommand = isYarnProj
? `yarn add --dev ${joinWithAnd(dependencyStrings)}`
: `meteor npm install -D ${joinWithAnd(dependencyStrings)}`;
logError(`\n┌─────────────────────────────────────────────────`);
logError(`│ ❌ ${packageName} Installation Failed`);
logError(`└─────────────────────────────────────────────────`);
logError(`Run: meteor npm install -D ${joinWithAnd(dependencyStrings)}`);
logError(`Run: ${installCommand}`);
throw new Error(
`Failed to install ${packageName} dependencies. Please install them manually with: meteor npm install -D ${joinWithAnd(dependencyStrings)}`
`Failed to install ${packageName} dependencies. Please install them manually with: ${installCommand}`
);
}

View File

@@ -79,17 +79,29 @@ const {
const {
getNpxCommand,
getNpmCommand,
getYarnCommand,
isYarnProject,
} = require('meteor/tools-core/lib/npm');
if (isMeteorAppRun() || isMeteorAppBuild() || isMeteorAppTest()) {
// Get entry points from Meteor configuration
setGlobalState(GLOBAL_STATE_KEYS.INITIAL_ENTRYPONTS, getMeteorAppEntrypoints());
let isYarnProj = process.env.YARN_ENABLED === 'true';
console.log("--> (rspack_plugin.js-Line: 91)\n process.env.YARN_ENABLED: ", process.env.YARN_ENABLED);
// Main entry point - using top-level await
try {
// Check if the project is a Yarn project and store the result in environment variable if not already set
if (process.env.YARN_ENABLED === undefined) {
isYarnProj = isYarnProject();
process.env.YARN_ENABLED = isYarnProj ? 'true' : 'false';
}
if (isMeteorAppDebug() || isMeteorAppConfigModernVerbose()) {
logInfo(`[i] Meteor Npx prefix: ${getNpxCommand([])?.prefix}`);
logInfo(`[i] Meteor Npm prefix: ${getNpmCommand([])?.prefix}`);
if (isYarnProj) {
logInfo(`[i] Meteor Yarn prefix: ${getYarnCommand([])?.prefix}`);
}
}
// Clean build context files only if they haven't been cleaned yet

View File

@@ -136,6 +136,37 @@ function buildNpmInstallArgs(dependencies, options = {}) {
return args;
}
/**
* Builds yarn install arguments based on options and dependencies
*
* @param {string|string[]} dependencies - The npm dependency or dependencies to install
* @param {Object} [options] - Options for the installation
* @param {boolean} [options.dev=false] - If true, install as a dev dependency
* @param {boolean} [options.exact=false] - If true, install with exact version
* @returns {string[]} Array of arguments for the yarn add command
*/
function buildYarnInstallArgs(dependencies, options = {}) {
const args = ['add'];
// Add flags based on options
if (options.dev) {
args.push('--dev');
}
if (options.exact) {
args.push('--exact');
}
// Add dependencies to the command
if (Array.isArray(dependencies)) {
args.push(...dependencies);
} else {
args.push(dependencies);
}
return args;
}
/**
* Executes a command and returns a promise that resolves to true if successful
*
@@ -161,17 +192,26 @@ function executeCommand(command, args, options) {
/**
* Installs a npm dependency using direct npm binary if available, otherwise falls back to `meteor npm install`.
* If yarn option is true, uses yarn instead.
*
* @param {string|string[]} dependencies - The npm dependency or dependencies to install
* @param {Object} [options] - Options for the installation
* @param {string} [options.cwd] - Current working directory (defaults to process.cwd())
* @param {boolean} [options.dev=false] - If true, install as a dev dependency
* @param {boolean} [options.exact=false] - If true, install with exact version
* @param {boolean} [options.yarn=false] - If true, use yarn instead of npm
* @returns {Promise<boolean>} A promise that resolves to true if installation succeeded, false otherwise
*/
export function installNpmDependency(dependencies, options = {}) {
const cwd = options.cwd || process.cwd();
// If yarn option is true, use yarn
if (options.yarn) {
const { command, args: baseArgs } = getYarnCommand([]);
const args = buildYarnInstallArgs(dependencies, options);
return executeCommand(command, [...baseArgs, ...args], { cwd });
}
// Try to get the npm binary path
const npmBinaryPath = getNodeBinaryPath('npm');
@@ -319,3 +359,64 @@ export function getNpxCommand(args) {
prefix: `meteor npx`,
};
}
/**
* Checks if the current project is a Yarn project.
* Looks for yarn.lock file in the current working directory and checks packageManager in package.json.
*
* @param {Object} [options] - Options for the check
* @param {string} [options.cwd] - Current working directory (defaults to process.cwd())
* @returns {boolean} True if it's a Yarn project, false otherwise
*/
export function isYarnProject(options = {}) {
const cwd = options.cwd || process.cwd();
// Check if yarn.lock exists
const yarnLockPath = path.join(cwd, 'yarn.lock');
if (fs.existsSync(yarnLockPath)) {
return true;
}
// Check packageManager field in package.json
try {
const packageJsonPath = path.join(cwd, 'package.json');
if (fs.existsSync(packageJsonPath)) {
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
// Check if packageManager contains "yarn"
if (packageJson.packageManager && packageJson.packageManager.includes('yarn')) {
return true;
}
}
} catch (error) {
// If there's an error reading or parsing package.json, continue
}
return false;
}
/**
* Gets the yarn command and arguments
* @param {string[]} args - The arguments to pass to yarn
* @returns {Object} An object with command, args, and base properties
*/
export function getYarnCommand(args) {
// Try to get the yarn binary path
const yarnBinaryPath = getNodeBinaryPath('yarn');
// If we have a direct path to yarn, use it
if (yarnBinaryPath && fs.existsSync(yarnBinaryPath)) {
return {
command: yarnBinaryPath,
args,
prefix: `${yarnBinaryPath}`,
};
}
// Fall back to using 'yarn' directly
return {
command: 'yarn',
args,
prefix: `yarn`,
};
}