@ts-dev-tools/core
Version:
TS dev tools Core
172 lines (171 loc) • 8.14 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.PeerDependenciesService = void 0;
const node_fs_1 = require("node:fs");
const node_path_1 = require("node:path");
const PackageJson_1 = require("./PackageJson");
const PluginService_1 = require("./PluginService");
class PeerDependenciesService {
constructor() { }
static DEPENDENCIES_FOLDER = "node_modules";
static async executeResolution(absoluteProjectDir) {
console.info(`Resolving peer dependencies...`);
const installedPlugins = PluginService_1.PluginService.getInstalledPlugins(absoluteProjectDir);
const missingPeers = [];
const packagesToInspect = PeerDependenciesService.getPackagesToInspect(absoluteProjectDir, installedPlugins);
for (const packageToInspect of packagesToInspect) {
const peers = PeerDependenciesService.getPackagePeerDependencies(packageToInspect.path);
for (const peer of peers) {
const isResolvable = PeerDependenciesService.canResolveFromConsumerRoot(absoluteProjectDir, peer.name);
if (!isResolvable) {
const sourcePath = PeerDependenciesService.resolvePeerSourcePath(absoluteProjectDir, packageToInspect.path, peer.name);
if (peer.optional && !(0, node_fs_1.existsSync)(sourcePath)) {
continue;
}
missingPeers.push({
...peer,
requiredBy: packageToInspect.name,
sourcePath,
});
}
}
}
if (missingPeers.length > 0) {
PeerDependenciesService.symlinkMissingPeers(absoluteProjectDir, missingPeers);
}
console.info(`Resolving peer dependencies done!`);
}
static getPackagePeerDependencies(packageDirPath) {
const packageJson = PackageJson_1.PackageJson.fromDirPath(packageDirPath);
const content = packageJson.getContent();
const peerDependencies = content.peerDependencies || {};
const peerDependenciesMeta = content.peerDependenciesMeta || {};
return Object.keys(peerDependencies).map((name) => ({
name,
optional: peerDependenciesMeta[name]?.optional ||
false,
}));
}
static getPackagesToInspect(absoluteProjectDir, installedPlugins) {
const packages = new Map();
for (const plugin of installedPlugins) {
packages.set(plugin.fullname, {
name: plugin.fullname,
path: plugin.path,
});
const pluginDependencies = PackageJson_1.PackageJson.fromDirPath(plugin.path).getDependenciesPackageNames();
for (const dependencyName of pluginDependencies) {
const dependencyPath = PeerDependenciesService.resolveDependencyPath(absoluteProjectDir, plugin.path, dependencyName);
if (!dependencyPath) {
continue;
}
if (!packages.has(dependencyName)) {
packages.set(dependencyName, {
name: dependencyName,
path: dependencyPath,
});
}
}
}
return Array.from(packages.values());
}
static resolveDependencyPath(absoluteProjectDir, pluginPath, dependencyName) {
const candidatePaths = [
(0, node_path_1.join)(absoluteProjectDir, PeerDependenciesService.DEPENDENCIES_FOLDER, dependencyName),
(0, node_path_1.join)(pluginPath, PeerDependenciesService.DEPENDENCIES_FOLDER, dependencyName),
];
for (const candidatePath of candidatePaths) {
if ((0, node_fs_1.existsSync)((0, node_path_1.join)(candidatePath, "package.json"))) {
return candidatePath;
}
}
return undefined;
}
static resolvePeerSourcePath(absoluteProjectDir, requiredByPath, peerName) {
const projectNodeModulesPath = (0, node_path_1.join)(absoluteProjectDir, PeerDependenciesService.DEPENDENCIES_FOLDER);
const directResolution = PeerDependenciesService.resolvePackagePathFrom(requiredByPath, peerName);
if (directResolution) {
return directResolution;
}
const projectResolution = PeerDependenciesService.resolvePackagePathFrom(projectNodeModulesPath, peerName);
if (projectResolution) {
return projectResolution;
}
const nestedResolution = PeerDependenciesService.resolveFromNestedNodeModules(projectNodeModulesPath, peerName);
if (nestedResolution) {
return nestedResolution;
}
return (0, node_path_1.join)(projectNodeModulesPath, peerName);
}
static resolveFromNestedNodeModules(projectNodeModulesPath, peerName) {
const packageDirs = PeerDependenciesService.getTopLevelPackageDirs(projectNodeModulesPath);
for (const packageDir of packageDirs) {
const resolvedPath = PeerDependenciesService.resolvePackagePathFrom(packageDir, peerName);
if (resolvedPath) {
return resolvedPath;
}
}
return undefined;
}
static getTopLevelPackageDirs(nodeModulesPath) {
if (!(0, node_fs_1.existsSync)(nodeModulesPath)) {
return [];
}
const packageDirs = [];
const entries = (0, node_fs_1.readdirSync)(nodeModulesPath);
for (const entry of entries) {
const entryPath = (0, node_path_1.join)(nodeModulesPath, entry);
if (!entry.startsWith("@")) {
packageDirs.push(entryPath);
continue;
}
const scopedEntries = (0, node_fs_1.readdirSync)(entryPath);
for (const scopedEntry of scopedEntries) {
packageDirs.push((0, node_path_1.join)(entryPath, scopedEntry));
}
}
return packageDirs;
}
static resolvePackagePathFrom(fromPath, packageName) {
try {
const packageJsonPath = require.resolve((0, node_path_1.join)(packageName, "package.json"), {
paths: [fromPath],
});
return (0, node_path_1.dirname)(packageJsonPath);
}
catch {
return undefined;
}
}
static canResolveFromConsumerRoot(absoluteProjectDir, packageName) {
const packagePath = (0, node_path_1.join)(absoluteProjectDir, PeerDependenciesService.DEPENDENCIES_FOLDER, packageName);
return (0, node_fs_1.existsSync)(packagePath);
}
static symlinkMissingPeers(absoluteProjectDir, missingPeers) {
const projectDependencyPath = (0, node_path_1.join)(absoluteProjectDir, PeerDependenciesService.DEPENDENCIES_FOLDER);
const uniqueMissingPeers = new Map();
for (const peer of missingPeers) {
if (!uniqueMissingPeers.has(peer.name)) {
uniqueMissingPeers.set(peer.name, peer);
}
}
for (const peer of Array.from(uniqueMissingPeers.values())) {
const peerSourcePath = peer.sourcePath;
if (!(0, node_fs_1.existsSync)(peerSourcePath)) {
console.warn(`- Peer dependency "${peer.name}" required by "${peer.requiredBy}" not found in plugin node_modules`);
continue;
}
const peerTargetPath = (0, node_path_1.join)(projectDependencyPath, peer.name);
console.info(`- Symlinking peer dependency "${peer.name}" required by "${peer.requiredBy}"`);
PeerDependenciesService.symlinkPeer(peerSourcePath, peerTargetPath);
}
}
static symlinkPeer(sourcePath, targetPath) {
const targetParentFolder = (0, node_path_1.join)(targetPath, "..");
if (!(0, node_fs_1.existsSync)(targetParentFolder)) {
(0, node_fs_1.mkdirSync)(targetParentFolder, { recursive: true });
}
(0, node_fs_1.symlinkSync)(sourcePath, targetPath);
}
}
exports.PeerDependenciesService = PeerDependenciesService;