@jskit-ai/release 0.1.4 → 0.1.6

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/release.mjs +35 -28
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/release",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Selective npm release preparation, publication and dependency pinning for package repositories.",
5
5
  "type": "module",
6
6
  "files": [
package/src/release.mjs CHANGED
@@ -59,12 +59,8 @@ function parseArgs(argv = []) {
59
59
  else options.from.push(path.resolve(value));
60
60
  continue;
61
61
  }
62
- if (argument === "--registry") {
63
- options.registry = normalizeRegistryUrl(args.shift());
64
- continue;
65
- }
66
- if (argument.startsWith("--registry=")) {
67
- options.registry = normalizeRegistryUrl(argument.slice("--registry=".length));
62
+ if (name === "--registry") {
63
+ options.registry = normalizeRegistryUrl(inline ?? args.shift() ?? "");
68
64
  continue;
69
65
  }
70
66
  throw new Error(`Unknown argument: ${argument}`);
@@ -76,7 +72,7 @@ function parseArgs(argv = []) {
76
72
  return options;
77
73
  }
78
74
 
79
- function normalizeRegistryUrl(value = DEFAULT_REGISTRY) {
75
+ function normalizeRegistryUrl(value) {
80
76
  const registry = String(value || "").trim();
81
77
  if (!registry) {
82
78
  throw new Error("--registry requires a URL.");
@@ -178,8 +174,10 @@ async function discoverReleasePackages(repoRoot) {
178
174
  const name = String(manifest.packageJson.name || "").trim();
179
175
  if (!name || manifest.packageJson.private === true) continue;
180
176
  if (records.some(record => record.name === name)) throw new Error(`${name} is declared by more than one package directory.`);
181
- records.push({ name, ...manifest, localDependencies: new Set() });
177
+ records.push({ name, ...manifest });
182
178
  }
179
+ const localNames = new Set(records.map(record => record.name));
180
+ for (const record of records) record.localDependencies = collectPublishDependencies(record.packageJson, localNames);
183
181
  return records.sort((left, right) => left.name.localeCompare(right.name));
184
182
  }
185
183
 
@@ -203,13 +201,6 @@ function collectPublishDependencies(packageJson, localNames) {
203
201
  return dependencies;
204
202
  }
205
203
 
206
- function hydrateLocalDependencies(records) {
207
- const localNames = new Set(records.map((record) => record.name));
208
- for (const record of records) {
209
- record.localDependencies = collectPublishDependencies(record.packageJson, localNames);
210
- }
211
- }
212
-
213
204
  function currentVersionMap(records) {
214
205
  return new Map(records.map((record) => {
215
206
  const version = String(record.packageJson.version || "").trim();
@@ -497,7 +488,6 @@ async function prepareRelease({ cwd: repoRoot, dryRun, registry, report = false
497
488
  }
498
489
  }
499
490
  if (!staged.length) throw new Error(`No publishable packages found in ${repoRoot}.`);
500
- hydrateLocalDependencies(staged);
501
491
  topologicalPublishOrder(staged);
502
492
  const current = currentVersionMap(staged);
503
493
  const versions = new Map(current);
@@ -660,23 +650,20 @@ function releaseCommitMessage(entries) {
660
650
  return `Release ${subject}${names.length > 4 ? `\n\n${names.join("\n")}` : ""}`;
661
651
  }
662
652
 
663
- async function publishRelease(records, { cwd: repoRoot, dryRun, registry }) {
664
- if (!dryRun) {
665
- assertMainReleaseCheckout(repoRoot);
666
- }
653
+ async function publishRelease(records, { cwd: repoRoot, registry }) {
654
+ assertMainReleaseCheckout(repoRoot);
667
655
  await validateReleaseState(records, repoRoot);
668
656
  const order = topologicalPublishOrder(records);
669
657
  const recordsByName = new Map(records.map((record) => [record.name, record]));
670
658
  const directory = await mkdtemp(path.join(tmpdir(), "jskit-release-"));
671
659
  try {
672
- const configPath = !dryRun || String(process.env.NPM_TOKEN || "").trim()
673
- ? await createNpmUserConfig(registry, directory, records) : "";
674
- if (!dryRun) await runCommand("npm", ["whoami", "--registry", registry, "--userconfig", configPath]);
660
+ const configPath = await createNpmUserConfig(registry, directory, records);
661
+ await runCommand("npm", ["whoami", "--registry", registry, "--userconfig", configPath]);
675
662
  const inspection = createPublicationInspection({ directory, registry, configPath });
676
663
  await assertNoNewerVersions(records, inspection);
677
664
  const plan = await planPublication(order.map(name => recordsByName.get(name)), { inspection });
678
665
  for (const entry of plan) {
679
- if (dryRun || entry.state === "skip") continue;
666
+ if (entry.state === "skip") continue;
680
667
  const args = [
681
668
  "publish",
682
669
  entry.tarball,
@@ -694,7 +681,7 @@ async function publishRelease(records, { cwd: repoRoot, dryRun, registry }) {
694
681
  } finally {
695
682
  await rm(directory, { recursive: true, force: true });
696
683
  }
697
- process.stdout.write(dryRun ? "Publish dry-run complete.\n" : "Publish complete.\n");
684
+ process.stdout.write("Publish complete.\n");
698
685
  }
699
686
 
700
687
  // publish: prepare the clean, current main checkout, commit and push the
@@ -708,9 +695,28 @@ async function publishCommand(options) {
708
695
  assertMainReleaseCheckout(options.cwd);
709
696
  const { plan } = await prepareRelease(options);
710
697
  commitAndPush(options.cwd, releaseCommitMessage(plan));
711
- const records = await discoverReleasePackages(options.cwd);
712
- hydrateLocalDependencies(records);
713
- await publishRelease(records, options);
698
+ await publishRelease(await discoverReleasePackages(options.cwd), options);
699
+ }
700
+
701
+ // npm cannot move exactly pinned packages that require each other in
702
+ // lockstep (assistant-core and assistant-runtime, say): the locked old
703
+ // versions still demand the old peers, and install stops with ERESOLVE.
704
+ // Forgetting only the pinned packages' lock entries lets npm resolve them
705
+ // afresh while every other locked dependency stays as it was.
706
+ async function forgetPinnedLockEntries(repoRoot, names) {
707
+ const lockPath = path.join(repoRoot, "package-lock.json");
708
+ let lock;
709
+ try {
710
+ lock = JSON.parse(await readFile(lockPath, "utf8"));
711
+ } catch (error) {
712
+ if (error.code === "ENOENT") return;
713
+ throw error;
714
+ }
715
+ for (const key of Object.keys(lock.packages || {})) {
716
+ const name = key.split("node_modules/").at(-1);
717
+ if (key.includes("node_modules/") && names.has(name)) delete lock.packages[key];
718
+ }
719
+ await writeFile(lockPath, serializeJson(lock), "utf8");
714
720
  }
715
721
 
716
722
  // npm can take minutes to serve a just-published version everywhere.
@@ -760,6 +766,7 @@ async function pinCommand({ cwd: repoRoot, from, dryRun, noCommit }) {
760
766
  process.stdout.write("Pin dry-run complete.\n");
761
767
  return;
762
768
  }
769
+ await forgetPinnedLockEntries(repoRoot, new Set(versions.keys()));
763
770
  await installReleasedPins(repoRoot);
764
771
  if (noCommit) {
765
772
  process.stdout.write(`${path.basename(repoRoot)}: pins installed; not committed\n`);