@jsenv/monorepo 0.4.5 → 0.4.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/monorepo",
3
- "version": "0.4.5",
3
+ "version": "0.4.7",
4
4
  "type": "module",
5
5
  "description": "Helpers to manage packages in a monorepo",
6
6
  "repository": {
@@ -23,10 +23,10 @@
23
23
  "/src/"
24
24
  ],
25
25
  "dependencies": {
26
- "@jsenv/filesystem": "4.15.21",
27
- "@jsenv/humanize": "1.8.0",
28
- "@jsenv/package-publish": "1.11.52",
29
- "@jsenv/urls": "2.9.11",
26
+ "@jsenv/filesystem": "4.15.22",
27
+ "@jsenv/humanize": "1.8.1",
28
+ "@jsenv/package-publish": "1.11.53",
29
+ "@jsenv/urls": "2.9.12",
30
30
  "semver": "7.8.5"
31
31
  },
32
32
  "publishConfig": {
@@ -6,7 +6,7 @@ export const fetchWorkspaceLatests = async (workspacePackages) => {
6
6
  let done = 0;
7
7
  const total = packageNames.length;
8
8
  const latestVersions = {};
9
- const fetchTask = createTaskLog(`fetch latest versions`);
9
+ const fetchTask = createTaskLog(`fetch latest versions of workspace packages`);
10
10
  try {
11
11
  await Promise.all(
12
12
  packageNames.map(async (packageName) => {
@@ -1,24 +1,29 @@
1
- import { createLogger, UNICODE } from "@jsenv/humanize";
1
+ import { createLogger, createTaskLog, UNICODE } from "@jsenv/humanize";
2
2
  import { publish } from "@jsenv/package-publish/src/internal/publish.js";
3
- import { collectWorkspacePackages } from "./internal/collect_workspace_packages.js";
3
+ import {
4
+ checkVersionStatusInRegistry,
5
+ VERSION_STATUS,
6
+ waitForStagedVersionToLand,
7
+ } from "@jsenv/package-publish/src/internal/staged_version.js";
4
8
  import {
5
9
  compareTwoPackageVersions,
6
10
  VERSION_COMPARE_RESULTS,
7
11
  } from "./internal/compare_two_package_versions.js";
8
- import { fetchWorkspaceLatests } from "./internal/fetch_workspace_latests.js";
9
12
  import { syncPackagesVersions } from "./sync_packages_versions.js";
10
13
 
14
+ const REGISTRY_URL = "https://registry.npmjs.org";
15
+
11
16
  export const publishPackages = async ({ directoryUrl, packagesRelations }) => {
12
- const versionsInSync = await ensureVersionsAreInSync({
17
+ // the sync check already read the package.json files and the registry;
18
+ // it hands both back so that publishing does not do it a second time
19
+ const syncResult = await ensureVersionsAreInSync({
13
20
  directoryUrl,
14
21
  packagesRelations,
15
22
  });
16
- if (!versionsInSync) {
23
+ if (!syncResult) {
17
24
  return;
18
25
  }
19
-
20
- const workspacePackages = await collectWorkspacePackages({ directoryUrl });
21
- const registryLatestVersions = await fetchWorkspaceLatests(workspacePackages);
26
+ const { workspacePackages, registryLatestVersions } = syncResult;
22
27
  const toPublishPackageNames = Object.keys(workspacePackages).filter(
23
28
  (packageName) => {
24
29
  const workspacePackage = workspacePackages[packageName];
@@ -41,31 +46,75 @@ export const publishPackages = async ({ directoryUrl, packagesRelations }) => {
41
46
  return;
42
47
  }
43
48
 
44
- const packageSlugs = toPublishPackageNames.map(
45
- (name) => `${name}@${workspacePackages[name].packageObject.version}`,
49
+ const token = process.env.NPM_TOKEN;
50
+ const statusTask = createTaskLog(`check versions on registry`);
51
+ let packageInfos;
52
+ try {
53
+ packageInfos = await Promise.all(
54
+ toPublishPackageNames.map(async (packageName) => {
55
+ const workspacePackage = workspacePackages[packageName];
56
+ const packageVersion = workspacePackage.packageObject.version;
57
+ const versionStatus = await checkVersionStatusInRegistry({
58
+ registryUrl: REGISTRY_URL,
59
+ packageName,
60
+ packageVersion,
61
+ token,
62
+ });
63
+ return {
64
+ packageName,
65
+ packageVersion,
66
+ packageSlug: `${packageName}@${packageVersion}`,
67
+ rootDirectoryUrl: new URL("./", workspacePackage.packageUrl),
68
+ versionStatus,
69
+ };
70
+ }),
71
+ );
72
+ statusTask.done();
73
+ } catch (e) {
74
+ statusTask.fail();
75
+ throw e;
76
+ }
77
+ const packagesToPublish = packageInfos.filter(
78
+ ({ versionStatus }) => versionStatus === VERSION_STATUS.ABSENT,
79
+ );
80
+ const stagedPackages = packageInfos.filter(
81
+ ({ versionStatus }) => versionStatus === VERSION_STATUS.STAGED,
46
82
  );
47
83
 
48
- console.log(`${UNICODE.INFO} ${
49
- toPublishPackageNames.length
50
- } packages to publish
51
- - ${packageSlugs.join(`
84
+ if (packagesToPublish.length === 0 && stagedPackages.length === 0) {
85
+ console.log(`${UNICODE.OK} packages are published on registry`);
86
+ return;
87
+ }
88
+ if (packagesToPublish.length) {
89
+ console.log(`${UNICODE.INFO} ${packagesToPublish.length} packages to publish
90
+ - ${packagesToPublish.map(({ packageSlug }) => packageSlug).join(`
52
91
  - `)}`);
53
- await toPublishPackageNames.reduce(
54
- async (previous, toPublishPackageName, index) => {
55
- await previous;
56
- await publish({
57
- logger: createLogger({ logLevel: "info" }),
58
- packageSlug: packageSlugs[index],
59
- rootDirectoryUrl: new URL(
60
- "./",
61
- workspacePackages[toPublishPackageName].packageUrl,
62
- ),
63
- registryUrl: "https://registry.npmjs.org",
64
- token: process.env.NPM_TOKEN,
65
- });
66
- },
67
- Promise.resolve(),
68
- );
92
+ }
93
+ if (stagedPackages.length) {
94
+ console.log(`${UNICODE.INFO} ${stagedPackages.length} packages staged on registry, waiting for it to publish them
95
+ - ${stagedPackages.map(({ packageSlug }) => packageSlug).join(`
96
+ - `)}`);
97
+ }
98
+
99
+ // a staged version cannot be published again, the registry only needs time to
100
+ // expose it; the others go through "npm publish"
101
+ for (const { packageName, packageVersion } of stagedPackages) {
102
+ await waitForStagedVersionToLand({
103
+ registryUrl: REGISTRY_URL,
104
+ packageName,
105
+ packageVersion,
106
+ token,
107
+ });
108
+ }
109
+ for (const { packageSlug, rootDirectoryUrl } of packagesToPublish) {
110
+ await publish({
111
+ logger: createLogger({ logLevel: "info" }),
112
+ packageSlug,
113
+ rootDirectoryUrl,
114
+ registryUrl: REGISTRY_URL,
115
+ token,
116
+ });
117
+ }
69
118
  };
70
119
 
71
120
  /*
@@ -74,13 +123,18 @@ export const publishPackages = async ({ directoryUrl, packagesRelations }) => {
74
123
  * When versions are not in sync we ask permission to sync them before publishing.
75
124
  */
76
125
  const ensureVersionsAreInSync = async ({ directoryUrl, packagesRelations }) => {
77
- const { outdatedPackageNames, versionUpdates, dependencyUpdates } =
78
- await syncPackagesVersions({
79
- logs: false,
80
- dryRun: true,
81
- directoryUrl,
82
- packagesRelations,
83
- });
126
+ const {
127
+ outdatedPackageNames,
128
+ versionUpdates,
129
+ dependencyUpdates,
130
+ workspacePackages,
131
+ registryLatestVersions,
132
+ } = await syncPackagesVersions({
133
+ logs: false,
134
+ dryRun: true,
135
+ directoryUrl,
136
+ packagesRelations,
137
+ });
84
138
  if (outdatedPackageNames.length) {
85
139
  console.warn(
86
140
  `${UNICODE.FAILURE} ${outdatedPackageNames.length} packages have a version older than the one published on registry
@@ -88,11 +142,11 @@ const ensureVersionsAreInSync = async ({ directoryUrl, packagesRelations }) => {
88
142
  - `)}
89
143
  Run "npm run monorepo:sync_versions" and review the changes before publishing`,
90
144
  );
91
- return false;
145
+ return null;
92
146
  }
93
147
  const outOfSyncCount = versionUpdates.length + dependencyUpdates.length;
94
148
  if (outOfSyncCount === 0) {
95
- return true;
149
+ return { workspacePackages, registryLatestVersions };
96
150
  }
97
151
  console.warn(`${UNICODE.WARNING} versions are not in sync, ${outOfSyncCount} things must be updated in package.json files
98
152
  - ${[
@@ -108,10 +162,20 @@ Run "npm run monorepo:sync_versions" and review the changes before publishing`,
108
162
  const confirmed = await confirm(`Sync versions and publish?`);
109
163
  if (!confirmed) {
110
164
  console.log(`${UNICODE.INFO} publish aborted, versions are not in sync`);
111
- return false;
165
+ return null;
112
166
  }
113
- await syncPackagesVersions({ directoryUrl, packagesRelations });
114
- return true;
167
+ // the dry run has mutated its own copy of the package objects; sync again on
168
+ // freshly read files (but with what the registry already told us) so that the
169
+ // updates are actually written and the package objects match the files
170
+ const syncResult = await syncPackagesVersions({
171
+ directoryUrl,
172
+ packagesRelations,
173
+ registryLatestVersions,
174
+ });
175
+ return {
176
+ workspacePackages: syncResult.workspacePackages,
177
+ registryLatestVersions,
178
+ };
115
179
  };
116
180
 
117
181
  const confirm = async (question) => {
@@ -17,9 +17,12 @@ export const syncPackagesVersions = async ({
17
17
  dryRun = false,
18
18
  directoryUrl,
19
19
  packagesRelations = {},
20
+ // when the caller already knows what is on the registry it can pass it here
21
+ // so that a single command does not fetch the same data twice
22
+ registryLatestVersions,
20
23
  }) => {
21
24
  const workspacePackages = await collectWorkspacePackages({ directoryUrl });
22
- const registryLatestVersions = await fetchWorkspaceLatests(workspacePackages);
25
+ registryLatestVersions ??= await fetchWorkspaceLatests(workspacePackages);
23
26
 
24
27
  const outdatedPackageNames = [];
25
28
  const toPublishPackageNames = [];
@@ -68,6 +71,8 @@ Use a tool like "git diff" to see the new versions and ensure this is what you w
68
71
  );
69
72
  }
70
73
  return {
74
+ workspacePackages,
75
+ registryLatestVersions,
71
76
  outdatedPackageNames,
72
77
  versionUpdates,
73
78
  dependencyUpdates,
@@ -240,6 +245,8 @@ Use a tool like "git diff" to see the new versions and ensure this is what you w
240
245
  }
241
246
 
242
247
  return {
248
+ workspacePackages,
249
+ registryLatestVersions,
243
250
  outdatedPackageNames,
244
251
  versionUpdates,
245
252
  dependencyUpdates,
@@ -93,7 +93,7 @@ export const upgradeExternalVersions = async ({
93
93
  fetch_latest_versions: {
94
94
  let done = 0;
95
95
  const total = externalPackageNames.length;
96
- const fetchTask = createTaskLog(`fetch latest versions`);
96
+ const fetchTask = createTaskLog(`fetch latest versions of external packages`);
97
97
  try {
98
98
  await Promise.all(
99
99
  externalPackageNames.map(async (externalPackageName) => {