-
Notifications
You must be signed in to change notification settings - Fork 86
/
Copy pathnext-version-helpers.mjs
124 lines (105 loc) · 4.5 KB
/
next-version-helpers.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// @ts-check
import { readFile, writeFile } from 'node:fs/promises'
import fg from 'fast-glob'
import { gte, satisfies, valid } from 'semver'
import { execaCommand } from 'execa'
const FUTURE_NEXT_PATCH_VERSION = '14.999.0'
const NEXT_VERSION_REQUIRES_REACT_19 = '14.3.0-canary.45'
const REACT_18_VERSION = '18.2.0'
/**
* Check if current next version satisfies a semver constraint
* @param {string} condition Semver constraint
* @returns {boolean} True if condition constraint is satisfied
*/
export function nextVersionSatisfies(condition) {
const version = process.env.NEXT_RESOLVED_VERSION ?? process.env.NEXT_VERSION ?? 'latest'
const isSemverVersion = valid(version)
const checkVersion = isSemverVersion ? version : FUTURE_NEXT_PATCH_VERSION
return satisfies(checkVersion, condition) || version === condition
}
/**
* Check if current next version requires React 19
* @param {string} version Next version
* @returns {boolean} True if current next version requires React 19
*/
export function nextVersionRequiresReact19(version) {
// @ts-expect-error Mistake in semver types
return gte(version, NEXT_VERSION_REQUIRES_REACT_19, { includePrerelease: true })
}
/**
* Finds all package.json in fixture directory and updates 'next' version to a given version
* If there is test.dependencies.next, it will only update if the version satisfies the constraint in that field
* @param {string} cwd Directory of a fixture
* @param {string} version Version to which update 'next' version
* @param {Object} [options] Update options
* @param {string} [options.logPrefix] Text to prefix logs with
* @param {'update' | 'revert'} [options.operation] This just informs log output wording, otherwise it has no effect
* @param {boolean} [options.silent] Doesn't produce any logs if truthy
* @param {boolean} [options.updateReact] Update React version to match Next version
* @returns {Promise<void>}
*/
export async function setNextVersionInFixture(
cwd,
version,
{ logPrefix = '', operation = 'update', silent = false, updateReact = true } = {},
) {
// use NEXT_RESOLVED_VERSION env var if exists and if passed version matches version from NEXT_VERSION
// otherwise use whatever is passed
const resolvedVersion =
process.env.NEXT_RESOLVED_VERSION && (process.env.NEXT_VERSION ?? 'latest') === version
? process.env.NEXT_RESOLVED_VERSION
: version
// if resolved version is different from version, we add it to the log to provide additional details
const nextVersionForLogs = `next@${version}${resolvedVersion !== version ? ` (${resolvedVersion})` : ``}`
if (!silent) {
console.log(
`${logPrefix}▲ ${operation === 'revert' ? 'Reverting' : 'Updating'} to ${nextVersionForLogs}...`,
)
}
const packageJsons = await fg.glob(['**/package.json', '!**/node_modules'], {
cwd,
absolute: true,
})
const isSemverVersion = valid(resolvedVersion)
await Promise.all(
packageJsons.map(async (packageJsonPath) => {
const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf8'))
if (packageJson.dependencies?.next) {
const versionConstraint = packageJson.test?.dependencies?.next
// We can't use semver to check "canary" or "latest", so we use a fake future minor version
const checkVersion = isSemverVersion ? resolvedVersion : FUTURE_NEXT_PATCH_VERSION
if (
operation === 'update' &&
versionConstraint &&
!satisfies(checkVersion, versionConstraint) &&
version !== versionConstraint
) {
if (!silent) {
console.log(
`${logPrefix}⏩ Skipping '${packageJson.name}' because it requires next@${versionConstraint}`,
)
}
return
}
packageJson.dependencies.next = version
const { stdout } = await execaCommand(
`npm info next@${resolvedVersion} peerDependencies --json`,
{ cwd },
)
const nextPeerDependencies = JSON.parse(stdout)
if (updateReact && nextVersionRequiresReact19(checkVersion)) {
const reactVersion =
operation === 'update' ? nextPeerDependencies['react'] : REACT_18_VERSION
packageJson.dependencies.react = reactVersion
packageJson.dependencies['react-dom'] = reactVersion
}
await writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n')
}
}),
)
if (!silent) {
console.log(
`${logPrefix}▲ ${operation === 'revert' ? 'Reverted' : 'Updated'} to ${nextVersionForLogs}`,
)
}
}