@mui/internal-code-infra 0.0.4-canary.100 → 0.0.4-canary.101
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/build/utils/pnpm.d.mts +50 -3
- package/package.json +4 -2
- package/src/cli/cmdBuild.mjs +2 -3
- package/src/cli/cmdSetVersionOverrides.mjs +32 -25
- package/src/utils/pnpm.mjs +196 -11
package/build/utils/pnpm.d.mts
CHANGED
|
@@ -263,18 +263,65 @@ export declare function writeOverridesToWorkspace(workspaceDir: string, override
|
|
|
263
263
|
*/
|
|
264
264
|
export declare function renameWorkspaceScope(packages: (PublicPackage | PrivatePackage)[], fromScope: string, toScope: string): Promise<Map<string, string>>;
|
|
265
265
|
/**
|
|
266
|
-
*
|
|
266
|
+
* Pick the version to pin for a specifier whose newest match is too recent to
|
|
267
|
+
* satisfy a `minimumReleaseAge` cooldown.
|
|
268
|
+
*
|
|
269
|
+
* Only the three specifier shapes this tool is given are handled. An exact
|
|
270
|
+
* version is not a choice, so it stands and the install reports it. A range
|
|
271
|
+
* resolves within itself. A dist-tag repoints to the highest aged-in version
|
|
272
|
+
* sharing the major and prerelease-ness — except `latest`, which pnpm allows to
|
|
273
|
+
* cross majors. Unlike pnpm, a deprecated version is not passed over: `pnpm info`
|
|
274
|
+
* lists bare version strings, so that flag would cost a request per candidate.
|
|
275
|
+
*
|
|
276
|
+
* @param {string} requested - The specifier as given: a dist-tag, range or exact version
|
|
277
|
+
* @param {string} resolvedVersion - Version the specifier resolves to today
|
|
278
|
+
* @param {string[]} versions - Every published version, from `pnpm info <pkg> versions`
|
|
279
|
+
* @param {Record<string, string>} publishTimes - Version to ISO publish date, from `pnpm info <pkg> time`
|
|
280
|
+
* @param {number} cutoff - Epoch ms; versions published after this are too recent
|
|
281
|
+
* @param {string[]} [exemptVersions] - Versions `minimumReleaseAgeExclude` installs regardless of age
|
|
282
|
+
* @returns {string | null} Version to pin, or null when nothing qualifies
|
|
283
|
+
* @internal exported for unit tests
|
|
284
|
+
*/
|
|
285
|
+
export declare function selectAgedVersion(requested: string, resolvedVersion: string, versions: string[], publishTimes: Record<string, string>, cutoff: number, exemptVersions?: string[]): string | null;
|
|
286
|
+
/**
|
|
287
|
+
* Read the effective pnpm configuration, including everything resolved from
|
|
288
|
+
* pnpm-workspace.yaml. `config list` types the values and omits unset keys,
|
|
289
|
+
* where `config get` stringifies everything and prints `undefined`.
|
|
290
|
+
*
|
|
291
|
+
* @returns {Promise<Record<string, any>>} Parsed configuration
|
|
292
|
+
*/
|
|
293
|
+
export declare function readPnpmConfig(): Promise<Record<string, any>>;
|
|
294
|
+
/**
|
|
295
|
+
* Read the registry cooldown from pnpm config, so resolution stays in step with
|
|
296
|
+
* what the subsequent install will enforce.
|
|
297
|
+
*
|
|
298
|
+
* @returns {Promise<import('@pnpm/config.version-policy').PublishedByPolicy>} Cutoff date and exemption policy, both undefined when no cooldown is configured
|
|
299
|
+
*/
|
|
300
|
+
export declare function getMinimumReleaseAgePolicy(): Promise<import('@pnpm/config.version-policy').PublishedByPolicy>;
|
|
301
|
+
/**
|
|
302
|
+
* Resolve a package@version specifier to an exact version.
|
|
303
|
+
*
|
|
304
|
+
* Given a cooldown policy, the result is the newest version the install will
|
|
305
|
+
* actually accept — a dist-tag on a daily channel always points at a build too
|
|
306
|
+
* recent to install under one. See {@link selectAgedVersion}.
|
|
307
|
+
*
|
|
267
308
|
* @param {string} packageSpec - Package specifier in format "package@version"
|
|
309
|
+
* @param {import('@pnpm/config.version-policy').PublishedByPolicy} policy - Cooldown from {@link getMinimumReleaseAgePolicy}
|
|
268
310
|
* @returns {Promise<string>} Exact version string
|
|
269
311
|
*/
|
|
270
|
-
export declare function resolveVersion(packageSpec: string): Promise<string>;
|
|
312
|
+
export declare function resolveVersion(packageSpec: string, policy: import('@pnpm/config.version-policy').PublishedByPolicy): Promise<string>;
|
|
271
313
|
/**
|
|
272
314
|
* Find the version of a dependency for a specific package@version
|
|
315
|
+
*
|
|
316
|
+
* The parent's spec is often a range, so the cooldown applies here too. Stepping
|
|
317
|
+
* back stays within that range and so never contradicts the parent.
|
|
318
|
+
*
|
|
273
319
|
* @param {string} packageSpec - Package specifier in format "package@version"
|
|
274
320
|
* @param {string} dependency - Dependency name to look up
|
|
321
|
+
* @param {import('@pnpm/config.version-policy').PublishedByPolicy} policy - Registry cooldown to resolve within
|
|
275
322
|
* @returns {Promise<string>} Exact version string of the dependency
|
|
276
323
|
*/
|
|
277
|
-
export declare function findDependencyVersionFromSpec(packageSpec: string, dependency: string): Promise<string>;
|
|
324
|
+
export declare function findDependencyVersionFromSpec(packageSpec: string, dependency: string, policy: import('@pnpm/config.version-policy').PublishedByPolicy): Promise<string>;
|
|
278
325
|
/**
|
|
279
326
|
* Get the maximum semver version between two versions
|
|
280
327
|
* @param {string} a
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mui/internal-code-infra",
|
|
3
|
-
"version": "0.0.4-canary.
|
|
3
|
+
"version": "0.0.4-canary.101",
|
|
4
4
|
"author": "MUI Team",
|
|
5
5
|
"description": "Infra scripts and configs to be used across MUI repos.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -71,7 +71,9 @@
|
|
|
71
71
|
"@octokit/auth-action": "^6.0.2",
|
|
72
72
|
"@octokit/oauth-methods": "^6.0.2",
|
|
73
73
|
"@octokit/rest": "^22.0.1",
|
|
74
|
+
"@pnpm/config.version-policy": "^1100.1.9",
|
|
74
75
|
"@pnpm/find-workspace-dir": "^1000.1.5",
|
|
76
|
+
"@pnpm/parse-wanted-dependency": "^1001.0.0",
|
|
75
77
|
"@typescript-eslint/types": "^8.64.0",
|
|
76
78
|
"@typescript-eslint/utils": "^8.64.0",
|
|
77
79
|
"@vitest/eslint-plugin": "^1.6.23",
|
|
@@ -188,7 +190,7 @@
|
|
|
188
190
|
"publishConfig": {
|
|
189
191
|
"access": "public"
|
|
190
192
|
},
|
|
191
|
-
"gitSha": "
|
|
193
|
+
"gitSha": "cbb48ed31d032fb3e0f5b02d9eaf72fcd5828376",
|
|
192
194
|
"scripts": {
|
|
193
195
|
"build": "tsgo -p tsconfig.build.json",
|
|
194
196
|
"typescript": "tsgo -noEmit",
|
package/src/cli/cmdBuild.mjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
2
|
import { findWorkspaceDir } from '@pnpm/find-workspace-dir';
|
|
3
|
-
import { $ } from 'execa';
|
|
4
3
|
import { globby } from 'globby';
|
|
5
4
|
import * as fs from 'node:fs/promises';
|
|
6
5
|
import * as path from 'node:path';
|
|
@@ -15,6 +14,7 @@ import {
|
|
|
15
14
|
mapConcurrently,
|
|
16
15
|
validatePkgJson,
|
|
17
16
|
} from '../utils/build.mjs';
|
|
17
|
+
import { readPnpmConfig } from '../utils/pnpm.mjs';
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* @typedef {Object} Args
|
|
@@ -279,8 +279,7 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
|
|
|
279
279
|
if (babelRuntimeVersion === 'catalog:') {
|
|
280
280
|
// resolve the version from the given package
|
|
281
281
|
// outputs the pnpm-workspace.yaml config as json
|
|
282
|
-
const
|
|
283
|
-
const pnpmWorkspaceConfig = JSON.parse(configStdout);
|
|
282
|
+
const pnpmWorkspaceConfig = await readPnpmConfig();
|
|
284
283
|
babelRuntimeVersion = pnpmWorkspaceConfig.catalog['@babel/runtime'];
|
|
285
284
|
}
|
|
286
285
|
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
import * as semver from 'semver';
|
|
4
4
|
import { $ } from 'execa';
|
|
5
5
|
import { findWorkspaceDir } from '@pnpm/find-workspace-dir';
|
|
6
|
+
import { parseWantedDependency } from '@pnpm/parse-wanted-dependency';
|
|
6
7
|
import {
|
|
8
|
+
getMinimumReleaseAgePolicy,
|
|
7
9
|
resolveVersion,
|
|
8
10
|
findDependencyVersionFromSpec,
|
|
9
11
|
writeOverridesToWorkspace,
|
|
@@ -17,61 +19,62 @@ import {
|
|
|
17
19
|
/**
|
|
18
20
|
* Process a single package override
|
|
19
21
|
* @param {string} packageSpec - Package specifier in format "package@version"
|
|
22
|
+
* @param {import('@pnpm/config.version-policy').PublishedByPolicy} policy - Registry cooldown to resolve within
|
|
20
23
|
* @returns {Promise<Record<string, string>>} Overrides object for this package
|
|
21
24
|
*/
|
|
22
|
-
async function processPackageOverride(packageSpec) {
|
|
25
|
+
async function processPackageOverride(packageSpec, policy) {
|
|
23
26
|
/** @type {Record<string, string>} */
|
|
24
27
|
const overrides = {};
|
|
25
28
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if (lastAtIndex === -1) {
|
|
29
|
+
const { alias: packageName, bareSpecifier: version } = parseWantedDependency(packageSpec);
|
|
30
|
+
if (!packageName || version === undefined) {
|
|
29
31
|
throw new Error(`Invalid package specifier: ${packageSpec}`);
|
|
30
32
|
}
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (!
|
|
34
|
+
// An empty version is distinct from a missing one: CI interpolates a matrix
|
|
35
|
+
// value into `--pkg pkg@<version>`, and the leg that leaves it unset must skip
|
|
36
|
+
// the override rather than fail the job.
|
|
37
|
+
if (!version || version === 'stable') {
|
|
36
38
|
return overrides;
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
// eslint-disable-next-line no-console
|
|
40
42
|
console.log(`Resolving overrides for ${packageName} version: ${version}`);
|
|
41
43
|
|
|
44
|
+
/** @param {string} spec */
|
|
45
|
+
const resolve = (spec) => resolveVersion(spec, policy);
|
|
46
|
+
|
|
42
47
|
if (packageName === 'react') {
|
|
43
48
|
// Special case for React - also override related packages
|
|
44
|
-
overrides.react = await
|
|
45
|
-
overrides['react-dom'] = await
|
|
46
|
-
overrides['react-is'] = await
|
|
49
|
+
overrides.react = await resolve(packageSpec);
|
|
50
|
+
overrides['react-dom'] = await resolve(`react-dom@${version}`);
|
|
51
|
+
overrides['react-is'] = await resolve(`react-is@${version}`);
|
|
47
52
|
overrides.scheduler = await findDependencyVersionFromSpec(
|
|
48
53
|
`react-dom@${overrides['react-dom']}`,
|
|
49
54
|
'scheduler',
|
|
55
|
+
policy,
|
|
50
56
|
);
|
|
51
57
|
|
|
52
58
|
const reactMajor = semver.major(overrides.react);
|
|
53
59
|
if (reactMajor === 17) {
|
|
54
|
-
overrides['@testing-library/react'] = await
|
|
60
|
+
overrides['@testing-library/react'] = await resolve('@testing-library/react@^12.1.0');
|
|
55
61
|
}
|
|
56
62
|
} else if (packageName === '@mui/material') {
|
|
57
63
|
// Special case for MUI - also override related packages
|
|
58
|
-
overrides['@mui/material'] = await
|
|
59
|
-
overrides['@mui/system'] = await
|
|
60
|
-
overrides['@mui/icons-material'] = await
|
|
61
|
-
overrides['@mui/utils'] = await
|
|
62
|
-
overrides['@mui/material-nextjs'] = await
|
|
64
|
+
overrides['@mui/material'] = await resolve(`@mui/material@${version}`);
|
|
65
|
+
overrides['@mui/system'] = await resolve(`@mui/system@${version}`);
|
|
66
|
+
overrides['@mui/icons-material'] = await resolve(`@mui/icons-material@${version}`);
|
|
67
|
+
overrides['@mui/utils'] = await resolve(`@mui/utils@${version}`);
|
|
68
|
+
overrides['@mui/material-nextjs'] = await resolve(`@mui/material-nextjs@${version}`);
|
|
63
69
|
|
|
64
|
-
const latest = await
|
|
70
|
+
const latest = await resolve(`@mui/material@latest`);
|
|
65
71
|
const latestMajor = semver.major(latest);
|
|
66
72
|
const muiMajor = semver.major(overrides['@mui/material']);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
} else {
|
|
70
|
-
overrides['@mui/lab'] = await resolveVersion(`@mui/lab@latest`);
|
|
71
|
-
}
|
|
73
|
+
const labTag = muiMajor < latestMajor ? `latest-v${muiMajor}` : 'latest';
|
|
74
|
+
overrides['@mui/lab'] = await resolve(`@mui/lab@${labTag}`);
|
|
72
75
|
} else {
|
|
73
76
|
// Generic case for other packages
|
|
74
|
-
overrides[packageName] = await
|
|
77
|
+
overrides[packageName] = await resolve(packageSpec);
|
|
75
78
|
}
|
|
76
79
|
|
|
77
80
|
return overrides;
|
|
@@ -89,8 +92,12 @@ async function handler(args) {
|
|
|
89
92
|
return;
|
|
90
93
|
}
|
|
91
94
|
|
|
95
|
+
// Read once and thread through: every override must land on a version that
|
|
96
|
+
// clears the same cooldown the following `pnpm dedupe` enforces.
|
|
97
|
+
const policy = await getMinimumReleaseAgePolicy();
|
|
98
|
+
|
|
92
99
|
const packageOverridePromises = args.pkg.map((packageSpec) =>
|
|
93
|
-
processPackageOverride(packageSpec),
|
|
100
|
+
processPackageOverride(packageSpec, policy),
|
|
94
101
|
);
|
|
95
102
|
const packageOverrideResults = await Promise.all(packageOverridePromises);
|
|
96
103
|
|
package/src/utils/pnpm.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import { getPublishedByPolicy } from '@pnpm/config.version-policy';
|
|
4
|
+
import { parseWantedDependency } from '@pnpm/parse-wanted-dependency';
|
|
3
5
|
import { findWorkspaceDir } from '@pnpm/find-workspace-dir';
|
|
4
6
|
import { $ } from 'execa';
|
|
5
7
|
import * as fs from 'node:fs/promises';
|
|
@@ -328,10 +330,10 @@ function aliasTarget(spec) {
|
|
|
328
330
|
if (!spec.startsWith('workspace:')) {
|
|
329
331
|
return null;
|
|
330
332
|
}
|
|
331
|
-
const
|
|
332
|
-
//
|
|
333
|
-
|
|
334
|
-
return
|
|
333
|
+
const { alias, bareSpecifier } = parseWantedDependency(spec.slice('workspace:'.length));
|
|
334
|
+
// A plain range parses as one half or the other (`*` as an alias, `^1.0.0` as a
|
|
335
|
+
// specifier); only an aliased spec carries both.
|
|
336
|
+
return bareSpecifier === undefined ? null : (alias ?? null);
|
|
335
337
|
}
|
|
336
338
|
|
|
337
339
|
/**
|
|
@@ -720,26 +722,209 @@ export async function renameWorkspaceScope(packages, fromScope, toScope) {
|
|
|
720
722
|
}
|
|
721
723
|
|
|
722
724
|
/**
|
|
723
|
-
*
|
|
725
|
+
* The versions a `minimumReleaseAgeExclude` policy exempts from the cooldown. A
|
|
726
|
+
* policy answers `true` for a package that is exempt at any version, or the list
|
|
727
|
+
* of versions exempted individually.
|
|
728
|
+
*
|
|
729
|
+
* @param {import('@pnpm/config.version-policy').PublishedByPolicy} policy - Policy from {@link getMinimumReleaseAgePolicy}
|
|
730
|
+
* @param {string} name - Package name
|
|
731
|
+
* @returns {true | string[]} `true` when every version is exempt, otherwise the exempt versions
|
|
732
|
+
*/
|
|
733
|
+
function cooldownExemptions(policy, name) {
|
|
734
|
+
const exemption = policy.publishedByExclude?.(name);
|
|
735
|
+
if (exemption === true) {
|
|
736
|
+
return true;
|
|
737
|
+
}
|
|
738
|
+
return Array.isArray(exemption) ? exemption : [];
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
/**
|
|
742
|
+
* Pick the version to pin for a specifier whose newest match is too recent to
|
|
743
|
+
* satisfy a `minimumReleaseAge` cooldown.
|
|
744
|
+
*
|
|
745
|
+
* Only the three specifier shapes this tool is given are handled. An exact
|
|
746
|
+
* version is not a choice, so it stands and the install reports it. A range
|
|
747
|
+
* resolves within itself. A dist-tag repoints to the highest aged-in version
|
|
748
|
+
* sharing the major and prerelease-ness — except `latest`, which pnpm allows to
|
|
749
|
+
* cross majors. Unlike pnpm, a deprecated version is not passed over: `pnpm info`
|
|
750
|
+
* lists bare version strings, so that flag would cost a request per candidate.
|
|
751
|
+
*
|
|
752
|
+
* @param {string} requested - The specifier as given: a dist-tag, range or exact version
|
|
753
|
+
* @param {string} resolvedVersion - Version the specifier resolves to today
|
|
754
|
+
* @param {string[]} versions - Every published version, from `pnpm info <pkg> versions`
|
|
755
|
+
* @param {Record<string, string>} publishTimes - Version to ISO publish date, from `pnpm info <pkg> time`
|
|
756
|
+
* @param {number} cutoff - Epoch ms; versions published after this are too recent
|
|
757
|
+
* @param {string[]} [exemptVersions] - Versions `minimumReleaseAgeExclude` installs regardless of age
|
|
758
|
+
* @returns {string | null} Version to pin, or null when nothing qualifies
|
|
759
|
+
* @internal exported for unit tests
|
|
760
|
+
*/
|
|
761
|
+
export function selectAgedVersion(
|
|
762
|
+
requested,
|
|
763
|
+
resolvedVersion,
|
|
764
|
+
versions,
|
|
765
|
+
publishTimes,
|
|
766
|
+
cutoff,
|
|
767
|
+
exemptVersions = [],
|
|
768
|
+
) {
|
|
769
|
+
const exempt = new Set(exemptVersions);
|
|
770
|
+
|
|
771
|
+
/**
|
|
772
|
+
* Whether the install would accept this version: old enough, or excluded from
|
|
773
|
+
* the cooldown altogether.
|
|
774
|
+
*
|
|
775
|
+
* @param {string} version
|
|
776
|
+
* @returns {boolean}
|
|
777
|
+
*/
|
|
778
|
+
const isInstallable = (version) => {
|
|
779
|
+
if (exempt.has(version)) {
|
|
780
|
+
return true;
|
|
781
|
+
}
|
|
782
|
+
const published = Date.parse(publishTimes[version]);
|
|
783
|
+
return Number.isFinite(published) && published <= cutoff;
|
|
784
|
+
};
|
|
785
|
+
|
|
786
|
+
// An unknown publish date leaves nothing to compare against, so the
|
|
787
|
+
// resolution stands rather than being swapped for another build.
|
|
788
|
+
if (!Object.hasOwn(publishTimes, resolvedVersion) || isInstallable(resolvedVersion)) {
|
|
789
|
+
return resolvedVersion;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
if (semver.valid(requested)) {
|
|
793
|
+
return resolvedVersion;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// `time` keeps an entry for a version after it is unpublished, so candidates
|
|
797
|
+
// come from the version list rather than from the dates.
|
|
798
|
+
const candidates = versions.filter(isInstallable);
|
|
799
|
+
|
|
800
|
+
if (semver.validRange(requested)) {
|
|
801
|
+
return semver.maxSatisfying(candidates, requested, true);
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
const resolved = semver.parse(resolvedVersion, true);
|
|
805
|
+
if (!resolved) {
|
|
806
|
+
return resolvedVersion;
|
|
807
|
+
}
|
|
808
|
+
const resolvedIsPrerelease = resolved.prerelease.length > 0;
|
|
809
|
+
|
|
810
|
+
let best = null;
|
|
811
|
+
for (const version of candidates) {
|
|
812
|
+
const parsed = semver.parse(version, true);
|
|
813
|
+
if (
|
|
814
|
+
!parsed ||
|
|
815
|
+
(requested !== 'latest' && parsed.major !== resolved.major) ||
|
|
816
|
+
parsed.prerelease.length > 0 !== resolvedIsPrerelease
|
|
817
|
+
) {
|
|
818
|
+
continue;
|
|
819
|
+
}
|
|
820
|
+
if (!best || semver.gt(version, best, true)) {
|
|
821
|
+
best = version;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
return best;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
/**
|
|
828
|
+
* Read the effective pnpm configuration, including everything resolved from
|
|
829
|
+
* pnpm-workspace.yaml. `config list` types the values and omits unset keys,
|
|
830
|
+
* where `config get` stringifies everything and prints `undefined`.
|
|
831
|
+
*
|
|
832
|
+
* @returns {Promise<Record<string, any>>} Parsed configuration
|
|
833
|
+
*/
|
|
834
|
+
export async function readPnpmConfig() {
|
|
835
|
+
const result = await $`pnpm config list --json`;
|
|
836
|
+
return JSON.parse(result.stdout);
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
/**
|
|
840
|
+
* Read the registry cooldown from pnpm config, so resolution stays in step with
|
|
841
|
+
* what the subsequent install will enforce.
|
|
842
|
+
*
|
|
843
|
+
* @returns {Promise<import('@pnpm/config.version-policy').PublishedByPolicy>} Cutoff date and exemption policy, both undefined when no cooldown is configured
|
|
844
|
+
*/
|
|
845
|
+
export async function getMinimumReleaseAgePolicy() {
|
|
846
|
+
const config = await readPnpmConfig();
|
|
847
|
+
// pnpm's own parser, so exclude entries keep their full grammar — wildcards,
|
|
848
|
+
// version unions (`pkg@1.0.0 || 2.0.0`) and `!` negation.
|
|
849
|
+
return getPublishedByPolicy({
|
|
850
|
+
minimumReleaseAge: config.minimumReleaseAge,
|
|
851
|
+
minimumReleaseAgeExclude: config.minimumReleaseAgeExclude,
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
/**
|
|
856
|
+
* Resolve a package@version specifier to an exact version.
|
|
857
|
+
*
|
|
858
|
+
* Given a cooldown policy, the result is the newest version the install will
|
|
859
|
+
* actually accept — a dist-tag on a daily channel always points at a build too
|
|
860
|
+
* recent to install under one. See {@link selectAgedVersion}.
|
|
861
|
+
*
|
|
724
862
|
* @param {string} packageSpec - Package specifier in format "package@version"
|
|
863
|
+
* @param {import('@pnpm/config.version-policy').PublishedByPolicy} policy - Cooldown from {@link getMinimumReleaseAgePolicy}
|
|
725
864
|
* @returns {Promise<string>} Exact version string
|
|
726
865
|
*/
|
|
727
|
-
export async function resolveVersion(packageSpec) {
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
866
|
+
export async function resolveVersion(packageSpec, policy) {
|
|
867
|
+
// The unprojected document carries both the resolved version and every
|
|
868
|
+
// publish date, so honouring the cooldown costs no extra round-trip.
|
|
869
|
+
const info = JSON.parse((await $`pnpm info ${packageSpec} --json`).stdout);
|
|
870
|
+
const manifest = Array.isArray(info) ? info[info.length - 1] : info;
|
|
871
|
+
const { name, version: exactVersion, time: publishTimes = {}, versions = [] } = manifest;
|
|
872
|
+
|
|
873
|
+
if (!policy.publishedBy) {
|
|
874
|
+
return exactVersion;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
const exemptVersions = cooldownExemptions(policy, name);
|
|
878
|
+
if (exemptVersions === true || exemptVersions.includes(exactVersion)) {
|
|
879
|
+
return exactVersion;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
const cutoff = policy.publishedBy.toISOString();
|
|
883
|
+
const { bareSpecifier: requested } = parseWantedDependency(packageSpec);
|
|
884
|
+
const agedVersion = selectAgedVersion(
|
|
885
|
+
requested ?? '',
|
|
886
|
+
exactVersion,
|
|
887
|
+
versions,
|
|
888
|
+
publishTimes,
|
|
889
|
+
policy.publishedBy.getTime(),
|
|
890
|
+
exemptVersions,
|
|
891
|
+
);
|
|
892
|
+
|
|
893
|
+
if (!agedVersion) {
|
|
894
|
+
throw new Error(
|
|
895
|
+
`No version matching ${packageSpec} was published before the minimumReleaseAge cutoff ` +
|
|
896
|
+
`(${cutoff}). The newest match, ${exactVersion}, was published at ` +
|
|
897
|
+
`${publishTimes[exactVersion]}, and no earlier match qualifies. Lower ` +
|
|
898
|
+
`minimumReleaseAge, add ${name} to minimumReleaseAgeExclude, or wait for ${exactVersion} ` +
|
|
899
|
+
`to age in.`,
|
|
900
|
+
);
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
if (agedVersion !== exactVersion) {
|
|
904
|
+
// eslint-disable-next-line no-console
|
|
905
|
+
console.log(
|
|
906
|
+
`Resolved ${packageSpec} to ${agedVersion} rather than ${exactVersion}, which was published after the minimumReleaseAge cutoff (${cutoff}).`,
|
|
907
|
+
);
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
return agedVersion;
|
|
731
911
|
}
|
|
732
912
|
|
|
733
913
|
/**
|
|
734
914
|
* Find the version of a dependency for a specific package@version
|
|
915
|
+
*
|
|
916
|
+
* The parent's spec is often a range, so the cooldown applies here too. Stepping
|
|
917
|
+
* back stays within that range and so never contradicts the parent.
|
|
918
|
+
*
|
|
735
919
|
* @param {string} packageSpec - Package specifier in format "package@version"
|
|
736
920
|
* @param {string} dependency - Dependency name to look up
|
|
921
|
+
* @param {import('@pnpm/config.version-policy').PublishedByPolicy} policy - Registry cooldown to resolve within
|
|
737
922
|
* @returns {Promise<string>} Exact version string of the dependency
|
|
738
923
|
*/
|
|
739
|
-
export async function findDependencyVersionFromSpec(packageSpec, dependency) {
|
|
924
|
+
export async function findDependencyVersionFromSpec(packageSpec, dependency, policy) {
|
|
740
925
|
const result = await $`pnpm info ${packageSpec} dependencies.${dependency}`;
|
|
741
926
|
const spec = result.stdout.trim();
|
|
742
|
-
return resolveVersion(`${dependency}@${spec}
|
|
927
|
+
return resolveVersion(`${dependency}@${spec}`, policy);
|
|
743
928
|
}
|
|
744
929
|
|
|
745
930
|
/**
|