@juit/check-updates
Version:
Small and fast utility to update package dependencies
296 lines (294 loc) • 12.8 kB
JavaScript
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
;