Skip to content

Commit 2658c1b

Browse files
authored
Check node types match node version (#14357)
The TS types for the Node APIs come from the "types/node" package, published from the Definitely Typed project. Both Node and the types package use semver versions ("major.minor.patch"). The types package for a particular Node version will have matching "major" and "minor" numbers, but not necessarily a matching "patch" number: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/README.md#how-do-definitely-typed-package-versions-relate-to-versions-of-the-corresponding-library We should ensure that the types package we install matches the Node version. We have a `check-node-versions` script to ensure this in other places (riff-raff config, for example), but it currently only supports matching exact versions (major, minor and patch). This change updates the script to allow specifying a "level" to which a given Node version should be matched, and adds the DCAR and AR types packages to these checks. Note: the "match level" for the types package should be "minor", but there isn't yet a package available for the version of Node that we're currently on (v22.18.0, latest types package is v22.17.x). Therefore this change sets the "level" to "major", to at least ensure that we're always on the correct major version. This can be updated once a new types package is released.
1 parent 9fd0fa5 commit 2658c1b

File tree

1 file changed

+79
-12
lines changed

1 file changed

+79
-12
lines changed

dotcom-rendering/scripts/check-node-versions.mjs

Lines changed: 79 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { readFile } from 'node:fs/promises';
44
import { dirname, resolve } from 'node:path';
55
import { fileURLToPath } from 'node:url';
66
import { log, warn } from '../../scripts/log.js';
7+
import semverParse from 'semver/functions/parse.js';
8+
import semverSatisfies from 'semver/functions/satisfies.js';
79

810
const __dirname = dirname(fileURLToPath(import.meta.url));
911

@@ -27,36 +29,101 @@ if (!nodeVersion) {
2729
log(`Found node version ${nodeVersion} in \`.nvmrc\``);
2830
}
2931

32+
/**
33+
* @typedef {'major' | 'minor' | 'patch'} MatchLevel
34+
*/
3035
const requiredNodeVersionMatches =
31-
/** @type {const} @satisfies {ReadonlyArray<{filepath: string, pattern: RegExp}>}*/ ([
36+
/** @type {const} @satisfies {ReadonlyArray<{filepath: string, pattern: RegExp, matchLevel: MatchLevel}>}*/ ([
3237
{
3338
filepath: 'Containerfile',
3439
pattern: /^FROM node:(.+)-alpine$/m,
40+
matchLevel: 'patch',
3541
},
3642
{
3743
filepath: 'scripts/deploy/riff-raff.yaml',
3844
pattern: /^ +Recipe: dotcom-rendering.*-node-(\d+\.\d+\.\d+).*?$/m,
45+
matchLevel: 'patch',
3946
},
4047
{
4148
filepath: '../apps-rendering/riff-raff.yaml',
4249
pattern: /^ +Recipe: apps-rendering.*-node-(\d+\.\d+\.\d+).*?$/m,
50+
matchLevel: 'patch',
51+
},
52+
{
53+
filepath: 'package.json',
54+
pattern: /^\t+"@types\/node"\: "(.+)",$/m,
55+
/*
56+
Definitely Typed packages only match the major and minor
57+
versions of the corresponding library/node release.
58+
https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/README.md#how-do-definitely-typed-package-versions-relate-to-versions-of-the-corresponding-library
59+
60+
Note: Given this rule, this should be set to 'minor'. It's currently
61+
set to 'major' because the latest node release doesn't yet have
62+
types available for it (v22.18.0, latest types package is v22.17.x).
63+
*/
64+
matchLevel: 'major',
65+
},
66+
{
67+
filepath: '../apps-rendering/package.json',
68+
pattern: /^\t+"@types\/node"\: "(.+)",$/m,
69+
/*
70+
Definitely Typed packages only match the major and minor
71+
versions of the corresponding library/node release.
72+
https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/README.md#how-do-definitely-typed-package-versions-relate-to-versions-of-the-corresponding-library
73+
74+
Note: Given this rule, this should be set to 'minor'. It's currently
75+
set to 'major' because the latest node release doesn't yet have
76+
types available for it (v22.18.0, latest types package is v22.17.x).
77+
*/
78+
matchLevel: 'major',
4379
},
4480
]);
4581

82+
/**
83+
*
84+
* @param {string} a
85+
* @param {string} b
86+
* @param {MatchLevel} matchLevel
87+
* @returns boolean
88+
*/
89+
const versionMatches = (a, b, matchLevel) => {
90+
const semverA = semverParse(a);
91+
92+
switch (matchLevel) {
93+
case 'major':
94+
return semverSatisfies(b, `${semverA?.major}.x.x`);
95+
case 'minor':
96+
return semverSatisfies(b, `${semverA?.major}.${semverA?.minor}.x`);
97+
case 'patch':
98+
return semverSatisfies(b, a);
99+
}
100+
};
101+
46102
const problems = (
47103
await Promise.all(
48-
requiredNodeVersionMatches.map(async ({ filepath, pattern }) => {
49-
const fileContents = await readFile(
50-
resolve(...filepath.split('/')),
51-
'utf-8',
52-
);
53-
const foundNodeVersion =
54-
fileContents.match(pattern)?.[1] ?? undefined;
104+
requiredNodeVersionMatches.map(
105+
async ({ filepath, pattern, matchLevel }) => {
106+
const fileContents = await readFile(
107+
resolve(...filepath.split('/')),
108+
'utf-8',
109+
);
110+
const foundNodeVersion =
111+
fileContents.match(pattern)?.[1] ?? undefined;
112+
113+
const matches =
114+
foundNodeVersion === undefined
115+
? false
116+
: versionMatches(
117+
nodeVersion,
118+
foundNodeVersion,
119+
matchLevel,
120+
);
55121

56-
return foundNodeVersion === nodeVersion
57-
? undefined
58-
: `Node version in ${filepath} (${foundNodeVersion}) does not match \`.nvmrc\` (${nodeVersion})`;
59-
}),
122+
return matches
123+
? undefined
124+
: `Node version in ${filepath} (${foundNodeVersion}) does not match \`.nvmrc\` (${nodeVersion}), expected them to match versions to the ${matchLevel} level`;
125+
},
126+
),
60127
)
61128
).filter(
62129
/** @type {(problem?: string) => problem is string} */

0 commit comments

Comments
 (0)