|
|
|
|
@@ -167,6 +167,10 @@ class PackageManager {
|
|
|
|
|
downloadResult = await this.installFromFile(parsed);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 'workspace': {
|
|
|
|
|
downloadResult = await this.installFromWorkspace(parsed);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 'registry':
|
|
|
|
|
default: {
|
|
|
|
|
// Resolve version first
|
|
|
|
|
@@ -789,6 +793,11 @@ class PackageManager {
|
|
|
|
|
return this.parseFileSpec(spec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handle workspace dependencies
|
|
|
|
|
if (this.isWorkspaceSpec(spec)) {
|
|
|
|
|
return this.parseWorkspaceSpec(spec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handle standard npm packages
|
|
|
|
|
const match = spec.match(/^(@?[^@]+)(?:@(.+))?$/);
|
|
|
|
|
return {
|
|
|
|
|
@@ -808,6 +817,16 @@ class PackageManager {
|
|
|
|
|
spec.includes('git@');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
isWorkspaceSpec(spec) {
|
|
|
|
|
// Check if it's a workspace dependency
|
|
|
|
|
const match = spec.match(/^(@?[^@]+)@(.+)$/);
|
|
|
|
|
if (match) {
|
|
|
|
|
const version = match[2];
|
|
|
|
|
return version.startsWith('workspace:');
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
parseGitSpec(gitSpec) {
|
|
|
|
|
let url = gitSpec;
|
|
|
|
|
let ref = 'main'; // Changed from 'master' to 'main' (modern default)
|
|
|
|
|
@@ -847,6 +866,23 @@ class PackageManager {
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
parseWorkspaceSpec(spec) {
|
|
|
|
|
const match = spec.match(/^(@?[^@]+)@workspace:(.*)$/);
|
|
|
|
|
if (!match) {
|
|
|
|
|
throw new Error(`Invalid workspace spec: ${spec}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const name = match[1];
|
|
|
|
|
const workspaceVersion = match[2]; // Could be *, ^1.0.0, ~1.0.0, etc.
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
name: name,
|
|
|
|
|
version: workspaceVersion,
|
|
|
|
|
source: 'workspace',
|
|
|
|
|
originalSpec: spec
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
parseFileSpec(fileSpec) {
|
|
|
|
|
const cleanPath = fileSpec.replace('file:', '');
|
|
|
|
|
const name = path.basename(cleanPath);
|
|
|
|
|
@@ -1225,6 +1261,139 @@ class PackageManager {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async installFromWorkspace(workspaceSpec) {
|
|
|
|
|
try {
|
|
|
|
|
const packageName = workspaceSpec.name;
|
|
|
|
|
const workspaceVersion = workspaceSpec.version;
|
|
|
|
|
|
|
|
|
|
// Find the workspace package
|
|
|
|
|
const workspacePackage = await this.findWorkspacePackage(packageName, workspaceVersion);
|
|
|
|
|
|
|
|
|
|
if (!workspacePackage) {
|
|
|
|
|
throw new Error(`Workspace package ${packageName} not found`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Install from the workspace directory
|
|
|
|
|
const fileSpec = {
|
|
|
|
|
name: packageName,
|
|
|
|
|
path: workspacePackage.path,
|
|
|
|
|
source: 'file'
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return await this.installFromFile(fileSpec);
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
throw new Error(`Failed to install workspace package ${workspaceSpec.name}: ${error.message}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async findWorkspacePackage(packageName, workspaceVersion) {
|
|
|
|
|
try {
|
|
|
|
|
// Read the workspace root package.json to find workspace configuration
|
|
|
|
|
const rootPackageJsonPath = path.join(this.projectRoot, 'package.json');
|
|
|
|
|
|
|
|
|
|
if (!await fs.pathExists(rootPackageJsonPath)) {
|
|
|
|
|
throw new Error('No package.json found in project root');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const rootPackageJson = await fs.readJson(rootPackageJsonPath);
|
|
|
|
|
|
|
|
|
|
// Check for workspace configuration
|
|
|
|
|
let workspacePatterns = [];
|
|
|
|
|
|
|
|
|
|
// Handle different workspace configuration formats
|
|
|
|
|
if (rootPackageJson.workspaces) {
|
|
|
|
|
if (Array.isArray(rootPackageJson.workspaces)) {
|
|
|
|
|
workspacePatterns = rootPackageJson.workspaces;
|
|
|
|
|
} else if (rootPackageJson.workspaces.packages) {
|
|
|
|
|
workspacePatterns = rootPackageJson.workspaces.packages;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (workspacePatterns.length === 0) {
|
|
|
|
|
throw new Error('No workspace configuration found');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Search for the package in workspace directories
|
|
|
|
|
for (const pattern of workspacePatterns) {
|
|
|
|
|
const workspaceDirs = await this.expandWorkspacePattern(pattern);
|
|
|
|
|
|
|
|
|
|
for (const workspaceDir of workspaceDirs) {
|
|
|
|
|
const packageJsonPath = path.join(this.projectRoot, workspaceDir, 'package.json');
|
|
|
|
|
|
|
|
|
|
if (await fs.pathExists(packageJsonPath)) {
|
|
|
|
|
const packageJson = await fs.readJson(packageJsonPath);
|
|
|
|
|
|
|
|
|
|
if (packageJson.name === packageName) {
|
|
|
|
|
// Check if version matches workspace requirement
|
|
|
|
|
if (this.matchesWorkspaceVersion(packageJson.version, workspaceVersion)) {
|
|
|
|
|
return {
|
|
|
|
|
name: packageName,
|
|
|
|
|
version: packageJson.version,
|
|
|
|
|
path: path.join(this.projectRoot, workspaceDir),
|
|
|
|
|
packageJson: packageJson
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
throw new Error(`Failed to find workspace package ${packageName}: ${error.message}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async expandWorkspacePattern(pattern) {
|
|
|
|
|
const glob = require('glob');
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// Use glob to expand workspace patterns
|
|
|
|
|
const matches = await new Promise((resolve, reject) => {
|
|
|
|
|
glob(pattern, { cwd: this.projectRoot }, (err, files) => {
|
|
|
|
|
if (err) reject(err);
|
|
|
|
|
else resolve(files);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Filter to only directories that contain package.json
|
|
|
|
|
const workspaceDirs = [];
|
|
|
|
|
for (const match of matches) {
|
|
|
|
|
const packageJsonPath = path.join(this.projectRoot, match, 'package.json');
|
|
|
|
|
if (await fs.pathExists(packageJsonPath)) {
|
|
|
|
|
workspaceDirs.push(match);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return workspaceDirs;
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.warn(chalk.yellow(`Warning: Failed to expand workspace pattern ${pattern}: ${error.message}`));
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
matchesWorkspaceVersion(packageVersion, workspaceVersion) {
|
|
|
|
|
// Handle different workspace version formats
|
|
|
|
|
if (workspaceVersion === '*') {
|
|
|
|
|
return true; // Any version matches
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (workspaceVersion.startsWith('^') || workspaceVersion.startsWith('~')) {
|
|
|
|
|
// Use semver to check if version satisfies range
|
|
|
|
|
return semver.satisfies(packageVersion, workspaceVersion);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (workspaceVersion === packageVersion) {
|
|
|
|
|
return true; // Exact match
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Default to true for other cases (workspace should generally match)
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async runPostInstallScripts(packageName, packageDir) {
|
|
|
|
|
try {
|
|
|
|
|
const packageJsonPath = path.join(packageDir, 'package.json');
|
|
|
|
|
|