UNPKG

@juit/check-updates

Version:

Small and fast utility to update package dependencies

296 lines (294 loc) 12.8 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // updater.ts var updater_exports = {}; __export(updater_exports, { Updater: () => Updater }); module.exports = __toCommonJS(updater_exports); var import_node_assert = __toESM(require("node:assert"), 1); var import_node_events = require("node:events"); var import_promises = require("node:fs/promises"); var import_node_path = require("node:path"); var import_semver = __toESM(require("semver"), 1); var import_debug = require("./debug.cjs"); var import_npmrc = require("./npmrc.cjs"); var dependencyTypes = [ "dependencies", "devDependencies", "peerDependencies", "optionalDependencies" ]; var Workspaces = class { _versions = {}; _emitter = new import_node_events.EventEmitter().setMaxListeners(100); get length() { return Object.entries(this._versions).length; } onUpdate(handler) { this._emitter.on("update", handler); } register(name, version) { (0, import_node_assert.default)(!this._versions[name], `Package "${name}" already registered`); this._versions[name] = version || "0.0.0"; } update(name, version) { (0, import_node_assert.default)(this._versions[name], `Package "${name}" not registered`); const oldVersion = this._versions[name] || "0.0.0"; (0, import_node_assert.default)(import_semver.default.gte(version, oldVersion), `Package "${name}" new version ${version} less than old ${oldVersion}`); if (import_semver.default.eq(version, oldVersion)) return; this._versions[name] = version; this._emitter.emit("update", name, version); } has(name) { return !!this._versions[name]; } *[Symbol.iterator]() { for (const [name, version] of Object.entries(this._versions)) { yield [name, version]; } } }; var Updater = class _Updater { constructor(_packageFile, _options, _cache, _workspaces = new Workspaces()) { this._packageFile = _packageFile; this._options = _options; this._cache = _cache; this._workspaces = _workspaces; this._packageFile = (0, import_node_path.resolve)(_packageFile); this._debug = (0, import_debug.makeDebug)(_options.debug); this._children = []; _workspaces.onUpdate((name, version) => { if (!this._packageData) return; for (const type of dependencyTypes) { const dependencies = this._packageData[type]; if (!dependencies) continue; if (!dependencies[name]) continue; if (dependencies[name] === version) continue; dependencies[name] = version; this._changed = true; this._bump(); } }); } _packageData; _npmRc; _originalVersion; _debug; _children; _changed = false; get name() { (0, import_node_assert.default)(this._packageData, "Updater not initialized"); return this._packageData.name; } get version() { (0, import_node_assert.default)(this._packageData, "Updater not initialized"); return this._packageData.version || "0.0.0"; } set version(version) { (0, import_node_assert.default)(this._originalVersion && this._packageData, "Updater not initialized"); (0, import_node_assert.default)(import_semver.default.lte(this._originalVersion, version), [ `Unable to set version for "${this.packageFile}" to "${version}"`, `as it's less than original version "${this._originalVersion}"` ].join(" ")); if (import_semver.default.eq(this._originalVersion, version)) return; if (this._packageData.version === version) return; this._changed = true; console.log(`Updating ${this._details} version to ${import_debug.Y}${version}${import_debug.X}`); this._packageData.version = version; if (this.name) this._workspaces.update(this.name, version); } get packageFile() { return (0, import_node_path.relative)(process.cwd(), this._packageFile); } get changed() { if (this._changed) return true; return this._children.reduce((changed, child) => changed || child.changed, false); } async init() { this._debug("Reading package file", this.packageFile); const json = await (0, import_promises.readFile)(this._packageFile, "utf8"); const data = this._packageData = JSON.parse(json); (0, import_node_assert.default)( data && typeof data === "object" && !Array.isArray(data), `File ${this.packageFile} is not a valid "pacakge.json" file` ); const npmrc = await (0, import_npmrc.readNpmRc)(this._packageFile); if (data.name) this._workspaces.register(data.name, data.version); this._originalVersion = data.version || "0.0.0"; if (this._options.workspaces && data.workspaces) { for (const path of data.workspaces) { const packageFile = (0, import_node_path.resolve)(this._packageFile, "..", path, "package.json"); const updater = new _Updater(packageFile, this._options, this._cache, this._workspaces); this._children.push(await updater.init()); } } this._packageData = data; this._npmRc = npmrc; return this; } get _details() { let string = `${import_debug.G}${this.packageFile}${import_debug.X}`; if (this.name || this.version) { string += ` [${import_debug.Y}`; if (this.name) string += `${this.name}`; if (this.name && this.version) string += " "; if (this.version) string += `${this.version}`; string += `${import_debug.X}]`; } return string; } _bump() { (0, import_node_assert.default)(this._originalVersion, "Updater not initialized"); if (this._options.bump) { this.version = import_semver.default.inc(this._originalVersion, this._options.bump) || this._originalVersion; } } /** Update a single dependency, returning the highest matching version */ async _updateDependency(name, rangeString) { (0, import_node_assert.default)(this._npmRc, "Updater not initialized"); if (this._workspaces.has(name)) { this._debug(`Not processing workspace package ${import_debug.Y}${name}${import_debug.X}`); return rangeString; } const match = /^\s*([~^])\s*(\d+(\.\d+(\.\d+)?)?)(-(alpha|beta|rc)[.-]\d+)?\s*$/.exec(rangeString); if (!match) { this._debug(`Not processing range ${import_debug.G}${rangeString}${import_debug.X} for ${import_debug.Y}${name}${import_debug.X}`); return rangeString; } const [, specifier = "", version = "", , , label = ""] = match; if (!this._options.strict) { const r = rangeString; rangeString = `>=${version}${label}`; if (specifier === "~") rangeString += ` <${import_semver.default.inc(version, "major")}`; this._debug(`Extending version for ${import_debug.Y}${name}${import_debug.X} from ${import_debug.G}${r}${import_debug.X} to ${import_debug.G}${rangeString}${import_debug.X}`); } const range = new import_semver.default.Range(rangeString); const versions = await this._cache.getVersions(name, this._npmRc, false); for (const v of versions) { if (range.test(v)) return `${specifier}${v}`; } if (label) { const versions2 = await this._cache.getVersions(name, this._npmRc, true); for (const v of versions2) { if (range.test(v)) return `${specifier}${v}`; } } return `${specifier}${version}${label}`; } /** Update a dependencies group, populating the "updated" version field */ async _updateDependenciesGroup(type) { (0, import_node_assert.default)(this._packageData, "Updater not initialized"); const dependencies = this._packageData[type]; if (!dependencies) return []; const promises = Object.entries(dependencies).map(async ([name, declared]) => { const updated = await this._updateDependency(name, declared); if (!this._options.debug) process.stdout.write("."); if (updated === declared) return; dependencies[name] = updated; return { name, declared, updated, type }; }); return (await Promise.all(promises)).filter((change) => !!change); } /** Update dependencies and return the version number of this package */ async update() { (0, import_node_assert.default)(this._packageData, "Updater not initialized"); for (const child of this._children) await child.update(); process.stdout.write(`Processing ${this._details} `); if (this._options.debug) process.stdout.write("\n"); const changes = await this._updateDependenciesGroup("dependencies"); if (changes.length || !this._options.quick) { changes.push(...await this._updateDependenciesGroup("devDependencies")); changes.push(...await this._updateDependenciesGroup("optionalDependencies")); changes.push(...await this._updateDependenciesGroup("peerDependencies")); } if (this._options.debug) process.stdout.write("Updated with"); if (!changes.length) return void console.log(` ${import_debug.R}no changes${import_debug.X}`); this._changed = true; changes.sort(({ name: a }, { name: b }) => a < b ? -1 : a > b ? 1 : 0); console.log(` ${import_debug.R}${changes.length} changes${import_debug.X}`); let lname = 0; let ldeclared = 0; let lupdated = 0; for (const { name, declared, updated } of changes) { lname = lname > name.length ? lname : name.length; ldeclared = ldeclared > declared.length ? ldeclared : declared.length; lupdated = lupdated > updated.length ? lupdated : updated.length; } for (const { name, declared, updated, type } of changes) { const kind = type === "devDependencies" ? "dev" : type === "peerDependencies" ? "peer" : type === "optionalDependencies" ? "optional" : "main"; console.log([ ` * ${import_debug.Y}${name.padEnd(lname)}${import_debug.X}`, ` : ${import_debug.G}${declared.padStart(ldeclared)}${import_debug.X}`, ` -> ${import_debug.G}${updated.padEnd(lupdated)} ${import_debug.B}${kind}${import_debug.X}` ].join("")); } this._bump(); } align(version) { if (this._workspaces.length < 2) { return this._debug(`No workspaces found in ${this._details}`); } if (!version) { let aligned = "0.0.0"; for (const [, version2] of this._workspaces) { if (import_semver.default.gt(version2, aligned)) aligned = version2; } version = aligned; } this.version = version; for (const child of this._children) child.version = version; console.log(`Workspaces versions aligned to ${import_debug.Y}${version}${import_debug.X}`); } /** Write out the new package file */ async write() { (0, import_node_assert.default)(this._packageData, "Updater not initialized"); for (const type of dependencyTypes) { const dependencies = Object.entries(this._packageData[type] || {}); if (dependencies.length) { this._packageData[type] = dependencies.sort(([nameA], [nameB]) => nameA.localeCompare(nameB)).reduce((deps, [name, version]) => { deps[name] = version; return deps; }, {}); } else { delete this._packageData[type]; } } const json = JSON.stringify(this._packageData, null, 2); this._debug(`${import_debug.Y}>>>`, this.packageFile, `<<<${import_debug.X} ${json}`); await (0, import_promises.writeFile)(this.packageFile, json + "\n"); for (const child of this._children) await child.write(); } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { Updater }); //# sourceMappingURL=updater.cjs.map