comze 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,6 +10,7 @@ A [taze](https://github.com/antfu/taze)-like CLI for updating `composer.json` de
10
10
  - 🎯 Respects `minimum-stability` and `prefer-stable`
11
11
  - 🚫 Major updates hidden by default
12
12
  - ✍️ Interactive selection mode
13
+ - ⚠️ Detects deprecated packages
13
14
 
14
15
  ## Installation
15
16
 
@@ -50,16 +51,16 @@ comze --exclude vendor/package
50
51
 
51
52
  ## Options
52
53
 
53
- | Flag | Description |
54
- |------|-------------|
55
- | `-w, --write` | Write changes to `composer.json` |
56
- | `-i, --install` | Write changes and run `composer update` |
57
- | `-I, --interactive` | Select updates manually |
58
- | `--major` | Include major updates (default: false) |
59
- | `--minor` | Include minor updates (default: true) |
60
- | `--patch` | Include patch updates (default: true) |
61
- | `--exclude <pkgs>` | Exclude packages (comma-separated) |
62
- | `--dry-run` | Preview changes without writing |
54
+ | Flag | Description |
55
+ | ------------------- | --------------------------------------- |
56
+ | `-w, --write` | Write changes to `composer.json` |
57
+ | `-i, --install` | Write changes and run `composer update` |
58
+ | `-I, --interactive` | Select updates manually |
59
+ | `--major` | Include major updates (default: false) |
60
+ | `--minor` | Include minor updates (default: true) |
61
+ | `--patch` | Include patch updates (default: true) |
62
+ | `--exclude <pkgs>` | Exclude packages (comma-separated) |
63
+ | `--dry-run` | Preview changes without writing |
63
64
 
64
65
  ## Composer Stability
65
66
 
@@ -76,15 +77,15 @@ comze reads `minimum-stability` and `prefer-stable` from your `composer.json`:
76
77
 
77
78
  All Composer version constraints are supported:
78
79
 
79
- | Type | Example |
80
- |------|---------|
81
- | Exact | `1.0.2` |
82
- | Caret | `^1.2.3` |
83
- | Tilde | `~1.2` |
84
- | Wildcard | `1.0.*` |
85
- | Range | `>=1.0 <2.0` |
86
- | Hyphenated | `1.0 - 2.0` |
87
- | Dev | `dev-main`, `1.x-dev` |
80
+ | Type | Example |
81
+ | ---------- | --------------------- |
82
+ | Exact | `1.0.2` |
83
+ | Caret | `^1.2.3` |
84
+ | Tilde | `~1.2` |
85
+ | Wildcard | `1.0.*` |
86
+ | Range | `>=1.0 <2.0` |
87
+ | Hyphenated | `1.0 - 2.0` |
88
+ | Dev | `dev-main`, `1.x-dev` |
88
89
 
89
90
  ## Development
90
91
 
package/bin/comze.mjs CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import '../dist/cli.mjs';
2
+ import "../dist/cli.mjs";
package/dist/cli.mjs CHANGED
@@ -7422,11 +7422,23 @@ function parseConstraint(constraint) {
7422
7422
  }
7423
7423
  if (original.startsWith("^")) {
7424
7424
  const version = original.slice(1);
7425
- return { type: "caret", prefix: "^", baseVersion: normalizeVersionString(version), original, isDev: hasDevSuffix(version) };
7425
+ return {
7426
+ type: "caret",
7427
+ prefix: "^",
7428
+ baseVersion: normalizeVersionString(version),
7429
+ original,
7430
+ isDev: hasDevSuffix(version)
7431
+ };
7426
7432
  }
7427
7433
  if (original.startsWith("~")) {
7428
7434
  const version = original.slice(1);
7429
- return { type: "tilde", prefix: "~", baseVersion: normalizeVersionString(version), original, isDev: hasDevSuffix(version) };
7435
+ return {
7436
+ type: "tilde",
7437
+ prefix: "~",
7438
+ baseVersion: normalizeVersionString(version),
7439
+ original,
7440
+ isDev: hasDevSuffix(version)
7441
+ };
7430
7442
  }
7431
7443
  if (original.includes("*")) {
7432
7444
  const basePart = (original.split("*")[0] ?? "").replace(/\.$/, "");
@@ -7434,16 +7446,34 @@ function parseConstraint(constraint) {
7434
7446
  }
7435
7447
  if (original.includes(" - ")) {
7436
7448
  const startVersion = (original.split(" - ")[0] ?? "").trim();
7437
- return { type: "hyphen", prefix: "", baseVersion: normalizeVersionString(startVersion), original, isDev: false };
7449
+ return {
7450
+ type: "hyphen",
7451
+ prefix: "",
7452
+ baseVersion: normalizeVersionString(startVersion),
7453
+ original,
7454
+ isDev: false
7455
+ };
7438
7456
  }
7439
7457
  const rangeMatch = original.match(/^([><!=]+)/);
7440
7458
  if (rangeMatch) {
7441
7459
  const versionMatch = original.match(/([><!=]+)\s*(\d+(?:\.\d+)*(?:-[\w.]+)?)/);
7442
7460
  const version = versionMatch?.[2] ?? original.replace(/[><!=]+/g, "");
7443
7461
  const prefix = rangeMatch[1] ?? ">=";
7444
- return { type: "range", prefix, baseVersion: normalizeVersionString(version), original, isDev: hasDevSuffix(version) };
7462
+ return {
7463
+ type: "range",
7464
+ prefix,
7465
+ baseVersion: normalizeVersionString(version),
7466
+ original,
7467
+ isDev: hasDevSuffix(version)
7468
+ };
7445
7469
  }
7446
- return { type: "exact", prefix: "", baseVersion: normalizeVersionString(original), original, isDev: hasDevSuffix(original) };
7470
+ return {
7471
+ type: "exact",
7472
+ prefix: "",
7473
+ baseVersion: normalizeVersionString(original),
7474
+ original,
7475
+ isDev: hasDevSuffix(original)
7476
+ };
7447
7477
  }
7448
7478
  function hasDevSuffix(version) {
7449
7479
  const lower = version.toLowerCase();
@@ -7521,9 +7551,98 @@ function formatNewVersion(originalConstraint, newVersion) {
7521
7551
  return `${parsed.prefix}${cleaned}`;
7522
7552
  }
7523
7553
 
7554
+ // src/cache.ts
7555
+ import fs from "node:fs/promises";
7556
+ import path from "node:path";
7557
+ import os from "node:os";
7558
+ var CACHE_TTL = 30 * 60 * 1000;
7559
+ function getCacheDir() {
7560
+ const home = os.homedir();
7561
+ const platform = os.platform();
7562
+ const overrideDir = process.env.COMZE_CACHE_DIR?.trim();
7563
+ if (overrideDir) {
7564
+ return overrideDir;
7565
+ }
7566
+ if (platform === "win32") {
7567
+ return path.join(process.env.LOCALAPPDATA || path.join(home, "AppData", "Local"), "comze", "Cache");
7568
+ }
7569
+ if (platform === "darwin") {
7570
+ return path.join(home, "Library", "Caches", "comze");
7571
+ }
7572
+ return path.join(process.env.XDG_CACHE_HOME || path.join(home, ".cache"), "comze");
7573
+ }
7574
+ async function getCache(key) {
7575
+ try {
7576
+ const cacheDir = getCacheDir();
7577
+ const filePath = path.join(cacheDir, `${key}.json`);
7578
+ const content = await fs.readFile(filePath, "utf-8");
7579
+ const data = JSON.parse(content);
7580
+ if (Date.now() - data.timestamp > CACHE_TTL) {
7581
+ fs.unlink(filePath).catch(() => {});
7582
+ return null;
7583
+ }
7584
+ return data.value;
7585
+ } catch {
7586
+ return null;
7587
+ }
7588
+ }
7589
+ async function setCache(key, value) {
7590
+ try {
7591
+ const cacheDir = getCacheDir();
7592
+ await fs.mkdir(cacheDir, { recursive: true });
7593
+ const filePath = path.join(cacheDir, `${key}.json`);
7594
+ const data = {
7595
+ timestamp: Date.now(),
7596
+ value
7597
+ };
7598
+ await fs.writeFile(filePath, JSON.stringify(data), "utf-8");
7599
+ } catch {}
7600
+ }
7601
+
7524
7602
  // src/fetcher.ts
7525
7603
  var PACKAGIST_API = "https://repo.packagist.org/p2";
7526
- async function fetchPackage(packageName, minStability = "stable", preferStable = true, currentVersion, allowMajor = true) {
7604
+ var PACKAGIST_PACKAGE_API = "https://packagist.org/packages";
7605
+ async function fetchDeprecatedInfo(packageName, noCache = false) {
7606
+ const cacheKey = `${packageName.replace("/", "_")}_deprecated`;
7607
+ if (!noCache) {
7608
+ const cached = await getCache(cacheKey);
7609
+ if (cached)
7610
+ return cached;
7611
+ }
7612
+ try {
7613
+ const url = `${PACKAGIST_PACKAGE_API}/${packageName}.json`;
7614
+ const response = await fetch(url);
7615
+ if (!response.ok)
7616
+ return {};
7617
+ const data = await response.json();
7618
+ const abandoned = data.package?.abandoned;
7619
+ if (!abandoned) {
7620
+ const result2 = {};
7621
+ if (!noCache)
7622
+ await setCache(cacheKey, result2);
7623
+ return result2;
7624
+ }
7625
+ if (typeof abandoned === "string") {
7626
+ const result2 = { deprecated: true, replacement: abandoned };
7627
+ if (!noCache)
7628
+ await setCache(cacheKey, result2);
7629
+ return result2;
7630
+ }
7631
+ const result = { deprecated: true };
7632
+ if (!noCache)
7633
+ await setCache(cacheKey, result);
7634
+ return result;
7635
+ } catch {
7636
+ return {};
7637
+ }
7638
+ }
7639
+ async function fetchPackage(packageName, minStability = "stable", preferStable = true, currentVersion, allowMajor = true, noCache = false) {
7640
+ const cacheKey = packageName.replace("/", "_");
7641
+ if (!noCache) {
7642
+ const cached = await getCache(cacheKey);
7643
+ if (cached)
7644
+ return cached;
7645
+ }
7527
7646
  try {
7528
7647
  const url = `${PACKAGIST_API}/${packageName}.json`;
7529
7648
  const response = await fetch(url);
@@ -7580,24 +7699,31 @@ async function fetchPackage(packageName, minStability = "stable", preferStable =
7580
7699
  }
7581
7700
  }
7582
7701
  }
7583
- return {
7702
+ const deprecatedInfo = await fetchDeprecatedInfo(packageName, noCache);
7703
+ const result = {
7584
7704
  latestVersion: selectedVersion.version,
7585
7705
  releaseTime: selectedVersion.time,
7586
7706
  phpRequirement: selectedVersion.require?.php ?? phpRequirement,
7587
- majorVersion: majorDetected
7707
+ majorVersion: majorDetected,
7708
+ deprecated: deprecatedInfo.deprecated,
7709
+ replacement: deprecatedInfo.replacement
7588
7710
  };
7711
+ if (!noCache) {
7712
+ await setCache(cacheKey, result);
7713
+ }
7714
+ return result;
7589
7715
  } catch {
7590
7716
  return null;
7591
7717
  }
7592
7718
  }
7593
- async function fetchAllPackages(packages, minStability = "stable", preferStable = true, allowMajor = true) {
7719
+ async function fetchAllPackages(packages, minStability = "stable", preferStable = true, allowMajor = true, noCache = false) {
7594
7720
  const results = new Map;
7595
7721
  const entries = Object.entries(packages);
7596
7722
  const CONCURRENCY = 5;
7597
7723
  for (let i = 0;i < entries.length; i += CONCURRENCY) {
7598
7724
  const batch = entries.slice(i, i + CONCURRENCY);
7599
7725
  const promises = batch.map(async ([name, version]) => {
7600
- const result = await fetchPackage(name, minStability, preferStable, version, allowMajor);
7726
+ const result = await fetchPackage(name, minStability, preferStable, version, allowMajor, noCache);
7601
7727
  if (result)
7602
7728
  results.set(name, result);
7603
7729
  });
@@ -7712,11 +7838,11 @@ function detectIndent(string) {
7712
7838
  }
7713
7839
 
7714
7840
  // src/writer.ts
7715
- async function readComposerJson(path) {
7716
- if (!existsSync(path))
7841
+ async function readComposerJson(path2) {
7842
+ if (!existsSync(path2))
7717
7843
  return null;
7718
7844
  try {
7719
- const raw = await readFile(path, "utf-8");
7845
+ const raw = await readFile(path2, "utf-8");
7720
7846
  const indent = detectIndent(raw).indent || " ";
7721
7847
  const content = JSON.parse(raw);
7722
7848
  return { content, raw, indent };
@@ -7724,8 +7850,8 @@ async function readComposerJson(path) {
7724
7850
  return null;
7725
7851
  }
7726
7852
  }
7727
- async function writeComposerJson(path, updates, dryRun = false) {
7728
- const result = await readComposerJson(path);
7853
+ async function writeComposerJson(path2, updates, dryRun = false) {
7854
+ const result = await readComposerJson(path2);
7729
7855
  if (!result)
7730
7856
  return false;
7731
7857
  const { content, indent } = result;
@@ -7752,7 +7878,7 @@ async function writeComposerJson(path, updates, dryRun = false) {
7752
7878
  try {
7753
7879
  const newContent = JSON.stringify(content, null, indent) + `
7754
7880
  `;
7755
- await writeFile(path, newContent, "utf-8");
7881
+ await writeFile(path2, newContent, "utf-8");
7756
7882
  return true;
7757
7883
  } catch {
7758
7884
  return false;
@@ -7854,6 +7980,12 @@ function renderTable(packages) {
7854
7980
  if (pkg.phpRequirement) {
7855
7981
  extra += import_picocolors.default.gray(` php ${pkg.phpRequirement}`);
7856
7982
  }
7983
+ if (pkg.deprecated) {
7984
+ extra += import_picocolors.default.red(" deprecated");
7985
+ if (pkg.replacement) {
7986
+ extra += import_picocolors.default.yellow(` → ${pkg.replacement}`);
7987
+ }
7988
+ }
7857
7989
  console.log(` ${name} ${oldVer} ${arrow} ${coloredNewVer} ${diffLabel} ${age}${extra}`);
7858
7990
  }
7859
7991
  console.log("");
@@ -7896,8 +8028,24 @@ function formatPackageChoice(pkg) {
7896
8028
  if (pkg.phpRequirement) {
7897
8029
  extra += import_picocolors.default.gray(` php ${pkg.phpRequirement}`);
7898
8030
  }
8031
+ if (pkg.deprecated) {
8032
+ extra += import_picocolors.default.red(" deprecated");
8033
+ if (pkg.replacement) {
8034
+ extra += import_picocolors.default.yellow(` → ${pkg.replacement}`);
8035
+ }
8036
+ }
7899
8037
  return `${import_picocolors.default.bold(pkg.name)} ${pkg.currentVersion} ${arrow} ${coloredNewVer} ${diffLabel} ${age}${extra}`;
7900
8038
  }
8039
+ function renderDeprecated(packages) {
8040
+ if (packages.length === 0)
8041
+ return;
8042
+ console.log(import_picocolors.default.yellow(" Deprecated packages detected:"));
8043
+ for (const pkg of packages) {
8044
+ const replacement = pkg.replacement ? import_picocolors.default.yellow(` → ${pkg.replacement}`) : "";
8045
+ console.log(` ${import_picocolors.default.red("!")} ${import_picocolors.default.bold(pkg.name)} ${import_picocolors.default.gray(pkg.currentVersion)}${replacement}`);
8046
+ }
8047
+ console.log("");
8048
+ }
7901
8049
 
7902
8050
  // src/interactive.ts
7903
8051
  var import_prompts = __toESM(require_prompts3(), 1);
@@ -7922,11 +8070,62 @@ async function selectPackages(packages) {
7922
8070
  const selectedNames = new Set(response.selected);
7923
8071
  return packages.filter((pkg) => selectedNames.has(pkg.name));
7924
8072
  }
8073
+ // package.json
8074
+ var package_default = {
8075
+ name: "comze",
8076
+ version: "0.3.0",
8077
+ description: "A taze-like CLI for updating composer.json dependencies",
8078
+ author: "qoqn",
8079
+ license: "MIT",
8080
+ keywords: [
8081
+ "composer",
8082
+ "php",
8083
+ "dependencies",
8084
+ "update",
8085
+ "cli",
8086
+ "taze"
8087
+ ],
8088
+ type: "module",
8089
+ main: "./dist/cli.mjs",
8090
+ module: "./dist/cli.mjs",
8091
+ bin: {
8092
+ comze: "bin/comze.mjs"
8093
+ },
8094
+ files: [
8095
+ "dist",
8096
+ "bin"
8097
+ ],
8098
+ scripts: {
8099
+ dev: "bun run src/cli.ts",
8100
+ build: "bun build src/cli.ts --outfile dist/cli.mjs --target node",
8101
+ prepublishOnly: "bun run build",
8102
+ test: "bun test"
8103
+ },
8104
+ engines: {
8105
+ node: ">=18"
8106
+ },
8107
+ repository: {
8108
+ type: "git",
8109
+ url: "git+https://github.com/qoqn/comze.git"
8110
+ },
8111
+ devDependencies: {
8112
+ "@types/bun": "latest",
8113
+ "@types/prompts": "^2.4.9",
8114
+ "@types/semver": "^7.7.1",
8115
+ typescript: "^5"
8116
+ },
8117
+ dependencies: {
8118
+ cac: "^6.7.14",
8119
+ "detect-indent": "^7.0.2",
8120
+ picocolors: "^1.1.1",
8121
+ prompts: "^2.4.2",
8122
+ semver: "^7.7.3"
8123
+ }
8124
+ };
7925
8125
 
7926
8126
  // src/index.ts
7927
- var VERSION = "0.1.0";
7928
8127
  async function run(options) {
7929
- renderHeader(VERSION);
8128
+ renderHeader(package_default.version);
7930
8129
  const composerPath = resolve(process.cwd(), "composer.json");
7931
8130
  const composer = await readComposerJson(composerPath);
7932
8131
  if (!composer) {
@@ -7950,12 +8149,20 @@ async function run(options) {
7950
8149
  console.log(import_picocolors2.default.gray(` Checking ${Object.keys(filteredPackages).length} packages...`));
7951
8150
  console.log(import_picocolors2.default.gray(` Stability: ${minStability}${preferStable ? " (prefer-stable)" : ""}
7952
8151
  `));
7953
- const results = await fetchAllPackages(filteredPackages, minStability, preferStable, options.major);
8152
+ const results = await fetchAllPackages(filteredPackages, minStability, preferStable, options.major, options.noCache);
7954
8153
  const updates = [];
8154
+ const deprecatedPackages = [];
7955
8155
  for (const [name, currentVersion] of Object.entries(filteredPackages)) {
7956
8156
  const result = results.get(name);
7957
8157
  if (!result)
7958
8158
  continue;
8159
+ if (result.deprecated) {
8160
+ deprecatedPackages.push({
8161
+ name,
8162
+ currentVersion,
8163
+ replacement: result.replacement
8164
+ });
8165
+ }
7959
8166
  const diffType = getDiffType(currentVersion, result.latestVersion);
7960
8167
  if (!diffType)
7961
8168
  continue;
@@ -7975,12 +8182,15 @@ async function run(options) {
7975
8182
  age: formatAge(result.releaseTime),
7976
8183
  ageMonths: getAgeMonths(result.releaseTime),
7977
8184
  majorAvailable,
7978
- phpRequirement: result.phpRequirement
8185
+ phpRequirement: result.phpRequirement,
8186
+ deprecated: result.deprecated,
8187
+ replacement: result.replacement
7979
8188
  });
7980
8189
  }
7981
8190
  const order = { major: 0, minor: 1, patch: 2 };
7982
8191
  updates.sort((a, b) => order[a.diffType] - order[b.diffType]);
7983
8192
  renderTable(updates);
8193
+ renderDeprecated(deprecatedPackages);
7984
8194
  if (updates.length === 0)
7985
8195
  return;
7986
8196
  let selectedUpdates = updates;
@@ -8027,9 +8237,13 @@ async function run(options) {
8027
8237
 
8028
8238
  // src/cli.ts
8029
8239
  var cli = dist_default("comze");
8030
- cli.option("-w, --write", "Write changes to composer.json", { default: false }).option("-i, --install", "Write changes and run composer update", { default: false }).option("-I, --interactive", "Select updates manually", { default: false }).option("--major", "Include major updates", { default: false }).option("--minor", "Include minor updates", { default: true }).option("--patch", "Include patch updates", { default: true }).option("--exclude <packages>", "Exclude packages (comma-separated)", { default: "" }).option("--dry-run", "Run without making changes", { default: false });
8240
+ cli.option("-w, --write", "Write changes to composer.json", { default: false }).option("-i, --install", "Write changes and run composer update", {
8241
+ default: false
8242
+ }).option("-I, --interactive", "Select updates manually", { default: false }).option("--major", "Include major updates", { default: false }).option("--minor", "Include minor updates", { default: true }).option("--patch", "Include patch updates", { default: true }).option("--exclude <packages>", "Exclude packages (comma-separated)", {
8243
+ default: ""
8244
+ }).option("--dry-run", "Run without making changes", { default: false }).option("--no-cache", "Bypass cache and force fetch from network", { default: false });
8031
8245
  cli.help();
8032
- cli.version("0.1.0");
8246
+ cli.version(package_default.version);
8033
8247
  cli.command("", "Check for updates in composer.json").action(async (cliOptions) => {
8034
8248
  const options = {
8035
8249
  write: cliOptions.write || cliOptions.install,
@@ -8039,7 +8253,8 @@ cli.command("", "Check for updates in composer.json").action(async (cliOptions)
8039
8253
  minor: cliOptions.minor,
8040
8254
  patch: cliOptions.patch,
8041
8255
  exclude: cliOptions.exclude ? cliOptions.exclude.split(",").map((s) => s.trim()) : [],
8042
- dryRun: cliOptions.dryRun
8256
+ dryRun: cliOptions.dryRun,
8257
+ noCache: cliOptions.noCache
8043
8258
  };
8044
8259
  await run(options);
8045
8260
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "comze",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "A taze-like CLI for updating composer.json dependencies",
5
5
  "author": "qoqn",
6
6
  "license": "MIT",