UNPKG

@baseplate-dev/core-generators

Version:

Core generators for Baseplate

142 lines 5.46 kB
import semver from 'semver'; /** * Container for managing Node.js dependencies with specific merging rules */ export class NodePackageDependenciesContainer { _value; /** * Peer dependencies are tracked in a separate map from dev/prod since a * package (e.g. `react`) is commonly both a peer dependency (for consumers) * and a dev dependency (needed locally to build/test) at once - something * the single dev/prod map can't represent since it allows only one type per * package name. */ _peerValue; getDynamicSource; constructor(options) { this._value = new Map(); this._peerValue = new Map(); this.getDynamicSource = options?.getDynamicSource; } /** * Resolves the version to use when merging a new version in for an existing * entry, keeping whichever of the two semver ranges is narrower/more specific. * @param name - The name of the dependency (used in the error message) * @param oldVersion - The existing version * @param newVersion - The version being merged in */ resolveMergedVersion(name, oldVersion, newVersion) { if (newVersion === oldVersion || semver.subset(oldVersion, newVersion) // Proposed version is broader or equal ) { return oldVersion; // Keep the existing, more specific or equal version } if (semver.subset(newVersion, oldVersion)) { // Proposed version is narrower return newVersion; // Use the new, more specific version } // Versions are incompatible ranges or values throw new Error(`Could not merge incompatible versions for dependency "${name}": existing "${oldVersion}"`); } /** * Add a dependency to the container * @param name - The name of the dependency * @param version - The version of the dependency * @param type - The type of the dependency (dev or prod) */ add(name, version, type) { const existingEntry = this._value.get(name); if (existingEntry) { const newVersion = this.resolveMergedVersion(name, existingEntry.version, version); // Determine the final type: 'normal' takes precedence over 'dev' const finalType = existingEntry.type === 'prod' || type === 'prod' ? 'prod' : 'dev'; this._value.set(name, { name, version: newVersion, type: finalType, source: this.getDynamicSource?.(), }); } else { // Dependency doesn't exist, add it this._value.set(name, { name, version, type, source: this.getDynamicSource?.(), }); } } /** * Add production dependencies to the container * @param packages - The dependencies to add */ addProdPackages(packages) { for (const [name, version] of Object.entries(packages)) { this.add(name, version, 'prod'); } } /** * Add development dependencies to the container * @param packages - The dependencies to add */ addDevPackages(packages) { for (const [name, version] of Object.entries(packages)) { this.add(name, version, 'dev'); } } /** * Add peer dependencies to the container. Peer entries are tracked * independently of dev/prod, so a package can be both a peer dependency * and a dev/prod dependency at the same time. * @param packages - The dependencies to add */ addPeerPackages(packages) { for (const [name, version] of Object.entries(packages)) { const existingEntry = this._peerValue.get(name); const newVersion = existingEntry ? this.resolveMergedVersion(name, existingEntry.version, version) : version; this._peerValue.set(name, { name, version: newVersion, source: this.getDynamicSource?.(), }); } } /** * Add multiple dependencies to the container * @param packages - The dependencies to add * @param type - The type of the dependencies (dev, prod, or peer) */ addPackages(packages) { for (const [name, version] of Object.entries(packages.prod ?? {})) { this.add(name, version, 'prod'); } for (const [name, version] of Object.entries(packages.dev ?? {})) { this.add(name, version, 'dev'); } this.addPeerPackages(packages.peer ?? {}); } getValue() { const value = this._value; function getPackageType(type) { return Object.fromEntries([...value.entries()] .filter(([, info]) => info.type === type) .map(([name, info]) => [name, info.version])); } return { dev: getPackageType('dev'), prod: getPackageType('prod'), peer: Object.fromEntries([...this._peerValue.entries()].map(([name, info]) => [ name, info.version, ])), }; } } // Helper function to create the container easily in the schema builder export function createNodePackageDependenciesContainer(options) { return new NodePackageDependenciesContainer(options); } //# sourceMappingURL=package-dependencies-container.js.map