Skip to content

Commit e02dd34

Browse files
committed
Remove jsTypings/semver.ts, add unit tests and logging
1 parent 79d7f37 commit e02dd34

12 files changed

+207
-151
lines changed

Diff for: src/compiler/diagnosticMessages.json

+8
Original file line numberDiff line numberDiff line change
@@ -3671,6 +3671,14 @@
36713671
"category": "Error",
36723672
"code": 6205
36733673
},
3674+
"'package.json' has invalid version '{0}' in 'typesVersions' field.": {
3675+
"category": "Message",
3676+
"code": 6206
3677+
},
3678+
"'package.json' does not have a 'typesVersions' entry that matches version '{0}'.": {
3679+
"category": "Message",
3680+
"code": 6207
3681+
},
36743682

36753683
"Projects to reference": {
36763684
"category": "Message",

Diff for: src/compiler/moduleNameResolver.ts

+20-7
Original file line numberDiff line numberDiff line change
@@ -127,20 +127,30 @@ namespace ts {
127127

128128
/* @internal */
129129
export function getPackageJsonTypesVersionsOverride(typesVersions: MapLike<string>) {
130-
const typeScriptVersion = Version.parse(versionMajorMinor);
130+
return getPackageJsonTypesVersionsOverrideWithTrace(typesVersions, /*state*/ undefined);
131+
}
132+
133+
let typeScriptVersion: Version | undefined;
134+
135+
function getPackageJsonTypesVersionsOverrideWithTrace(typesVersions: MapLike<string>, state: ModuleResolutionState | undefined) {
136+
if (!typeScriptVersion) typeScriptVersion = new Version(versionMajorMinor);
137+
131138
let bestVersion: Version | undefined;
132139
let bestVersionKey: string | undefined;
133140
for (const key in typesVersions) {
134141
if (!hasProperty(typesVersions, key)) continue;
135142

136143
const keyVersion = Version.tryParse(key);
137144
if (keyVersion === undefined) {
145+
if (state && state.traceEnabled) {
146+
trace(state.host, Diagnostics.package_json_has_invalid_version_0_in_typesVersions_field, key);
147+
}
138148
continue;
139149
}
140150

141151
// match the greatest version less than the current TypeScript version
142-
if (keyVersion.compareTo(typeScriptVersion) <= 0
143-
&& (bestVersion === undefined || keyVersion.compareTo(bestVersion) > 0)) {
152+
if (keyVersion.compareTo(bestVersion) > 0 &&
153+
keyVersion.compareTo(typeScriptVersion) <= 0) {
144154
bestVersion = keyVersion;
145155
bestVersionKey = key;
146156
}
@@ -169,22 +179,25 @@ namespace ts {
169179
return;
170180
}
171181

172-
const result = getPackageJsonTypesVersionsOverride(typesVersions);
182+
const result = getPackageJsonTypesVersionsOverrideWithTrace(typesVersions, state);
173183
if (!result) {
174-
return undefined;
184+
if (state.traceEnabled) {
185+
trace(state.host, Diagnostics.package_json_does_not_have_a_typesVersions_entry_that_matches_version_0, versionMajorMinor);
186+
}
187+
return;
175188
}
176189

177190
const { version: bestVersionKey, directory: bestVersionPath } = result;
178191
if (!isString(bestVersionPath)) {
179192
if (state.traceEnabled) {
180-
trace(state.host, Diagnostics.Expected_type_of_0_field_in_package_json_to_be_1_got_2, `typesVersion['${bestVersionKey}']`, "string", typeof bestVersionPath);
193+
trace(state.host, Diagnostics.Expected_type_of_0_field_in_package_json_to_be_1_got_2, `typesVersions['${bestVersionKey}']`, "string", typeof bestVersionPath);
181194
}
182195
return;
183196
}
184197

185198
if (state.traceEnabled) {
186199
const path = normalizePath(combinePaths(baseDirectory, bestVersionPath));
187-
trace(state.host, Diagnostics.package_json_has_0_field_1_that_references_2, `typesVersion['${bestVersionKey}']`, bestVersionPath, path);
200+
trace(state.host, Diagnostics.package_json_has_0_field_1_that_references_2, `typesVersions['${bestVersionKey}']`, bestVersionPath, path);
188201
}
189202

190203
return bestVersionPath;

Diff for: src/compiler/semver.ts

+62-63
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,49 @@
11
/* @internal */
22
namespace ts {
3-
// Per https://semver.org/#spec-item-2:
4-
//
3+
// https://semver.org/#spec-item-2
54
// > A normal version number MUST take the form X.Y.Z where X, Y, and Z are non-negative
65
// > integers, and MUST NOT contain leading zeroes. X is the major version, Y is the minor
76
// > version, and Z is the patch version. Each element MUST increase numerically.
87
//
98
// NOTE: We differ here in that we allow X and X.Y, with missing parts having the default
109
// value of `0`.
11-
const versionRegExp = /^(0|[1-9]\d*)(?:\.(0|[1-9]\d*)(?:\.(0|[1-9]\d*)(?:-([a-z0-9-.]+))?(?:(\+[a-z0-9-.]+))?)?)?$/i;
10+
const versionRegExp = /^(0|[1-9]\d*)(?:\.(0|[1-9]\d*)(?:\.(0|[1-9]\d*)(?:\-([a-z0-9-.]+))?(?:\+([a-z0-9-.]+))?)?)?$/i;
1211

13-
// Per https://semver.org/#spec-item-9:
14-
//
12+
// https://semver.org/#spec-item-9
1513
// > A pre-release version MAY be denoted by appending a hyphen and a series of dot separated
1614
// > identifiers immediately following the patch version. Identifiers MUST comprise only ASCII
1715
// > alphanumerics and hyphen [0-9A-Za-z-]. Identifiers MUST NOT be empty. Numeric identifiers
1816
// > MUST NOT include leading zeroes.
1917
const prereleaseRegExp = /^(?:0|[1-9]\d*|[a-z-][a-z0-9-]*)(?:\.(?:0|[1-9]\d*|[a-z-][a-z0-9-]*))*$/i;
2018

21-
// Per https://semver.org/#spec-item-10:
22-
//
19+
// https://semver.org/#spec-item-10
2320
// > Build metadata MAY be denoted by appending a plus sign and a series of dot separated
2421
// > identifiers immediately following the patch or pre-release version. Identifiers MUST
2522
// > comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-]. Identifiers MUST NOT be empty.
2623
const buildRegExp = /^[a-z0-9-]+(?:\.[a-z0-9-]+)*$/i;
2724

28-
// Per https://semver.org/#spec-item-9:
29-
//
25+
// https://semver.org/#spec-item-9
3026
// > Numeric identifiers MUST NOT include leading zeroes.
3127
const numericIdentifierRegExp = /^(0|[1-9]\d*)$/;
3228

3329
/**
34-
* Describes a precise semantic version number, per https://semver.org
30+
* Describes a precise semantic version number, https://semver.org
3531
*/
3632
export class Version {
37-
static readonly zero = new Version(0);
38-
3933
readonly major: number;
4034
readonly minor: number;
4135
readonly patch: number;
4236
readonly prerelease: ReadonlyArray<string>;
4337
readonly build: ReadonlyArray<string>;
4438

45-
constructor(major: number, minor = 0, patch = 0, prerelease = "", build = "") {
39+
constructor(text: string);
40+
constructor(major: number, minor?: number, patch?: number, prerelease?: string, build?: string);
41+
constructor(major: number | string, minor = 0, patch = 0, prerelease = "", build = "") {
42+
if (typeof major === "string") {
43+
const result = Debug.assertDefined(tryParseComponents(major), "Invalid version");
44+
({ major, minor, patch, prerelease, build } = result);
45+
}
46+
4647
Debug.assert(major >= 0, "Invalid argument: major");
4748
Debug.assert(minor >= 0, "Invalid argument: minor");
4849
Debug.assert(patch >= 0, "Invalid argument: patch");
@@ -51,104 +52,102 @@ namespace ts {
5152
this.major = major;
5253
this.minor = minor;
5354
this.patch = patch;
54-
this.prerelease = prerelease === "" ? emptyArray : prerelease.split(".");
55-
this.build = build === "" ? emptyArray : build.split(".");
56-
}
57-
58-
static parse(text: string) {
59-
return Debug.assertDefined(this.tryParse(text));
55+
this.prerelease = prerelease ? prerelease.split(".") : emptyArray;
56+
this.build = build ? build.split(".") : emptyArray;
6057
}
6158

6259
static tryParse(text: string) {
63-
const match = versionRegExp.exec(text);
64-
if (!match) return undefined;
60+
const result = tryParseComponents(text);
61+
if (!result) return undefined;
6562

66-
const [, major, minor = 0, patch = 0, prerelease, build] = match;
67-
if (prerelease && !prereleaseRegExp.test(prerelease)) return undefined;
68-
if (build && !buildRegExp.test(build)) return undefined;
69-
return new Version(+major, +minor, +patch, prerelease, build);
63+
const { major, minor, patch, prerelease, build } = result;
64+
return new Version(major, minor, patch, prerelease, build);
7065
}
7166

72-
static compare(left: Version | undefined, right: Version | undefined, compareBuildMetadata?: boolean) {
73-
// Per https://semver.org/#spec-item-11:
74-
//
67+
compareTo(other: Version | undefined) {
68+
// https://semver.org/#spec-item-11
7569
// > Precedence is determined by the first difference when comparing each of these
7670
// > identifiers from left to right as follows: Major, minor, and patch versions are
7771
// > always compared numerically.
7872
//
79-
// > When major, minor, and patch are equal, a pre-release version has lower
80-
// > precedence than a normal version.
73+
// https://semver.org/#spec-item-11
74+
// > Precedence for two pre-release versions with the same major, minor, and patch version
75+
// > MUST be determined by comparing each dot separated identifier from left to right until
76+
// > a difference is found [...]
8177
//
82-
// Per https://semver.org/#spec-item-10:
83-
//
84-
// > Build metadata SHOULD be ignored when determining version precedence.
85-
if (left === right) return Comparison.EqualTo;
86-
if (left === undefined) return Comparison.LessThan;
87-
if (right === undefined) return Comparison.GreaterThan;
88-
return compareValues(left.major, right.major)
89-
|| compareValues(left.minor, right.minor)
90-
|| compareValues(left.patch, right.patch)
91-
|| compareVersionFragments(left.prerelease, right.prerelease, /*compareNumericIdentifiers*/ true)
92-
|| (compareBuildMetadata ? compareVersionFragments(left.build, right.build, /*compareNumericIdentifiers*/ false) : Comparison.EqualTo);
93-
}
94-
95-
compareTo(other: Version, compareBuildMetadata?: boolean) {
96-
return Version.compare(this, other, compareBuildMetadata);
78+
// https://semver.org/#spec-item-11
79+
// > Build metadata does not figure into precedence
80+
if (this === other) return Comparison.EqualTo;
81+
if (other === undefined) return Comparison.GreaterThan;
82+
return compareValues(this.major, other.major)
83+
|| compareValues(this.minor, other.minor)
84+
|| compareValues(this.patch, other.patch)
85+
|| comparePrerelaseIdentifiers(this.prerelease, other.prerelease);
9786
}
9887

9988
toString() {
10089
let result = `${this.major}.${this.minor}.${this.patch}`;
101-
if (this.prerelease) result += `-${this.prerelease.join(".")}`;
102-
if (this.build) result += `+${this.build.join(".")}`;
90+
if (some(this.prerelease)) result += `-${this.prerelease.join(".")}`;
91+
if (some(this.build)) result += `+${this.build.join(".")}`;
10392
return result;
10493
}
10594
}
10695

107-
function compareVersionFragments(left: ReadonlyArray<string>, right: ReadonlyArray<string>, compareNumericIdentifiers: boolean) {
108-
// Per https://semver.org/#spec-item-11:
109-
//
96+
function tryParseComponents(text: string) {
97+
const match = versionRegExp.exec(text);
98+
if (!match) return undefined;
99+
100+
const [, major, minor = "0", patch = "0", prerelease = "", build = ""] = match;
101+
if (prerelease && !prereleaseRegExp.test(prerelease)) return undefined;
102+
if (build && !buildRegExp.test(build)) return undefined;
103+
return {
104+
major: parseInt(major, 10),
105+
minor: parseInt(minor, 10),
106+
patch: parseInt(patch, 10),
107+
prerelease,
108+
build
109+
};
110+
}
111+
112+
function comparePrerelaseIdentifiers(left: ReadonlyArray<string>, right: ReadonlyArray<string>) {
113+
// https://semver.org/#spec-item-11
110114
// > When major, minor, and patch are equal, a pre-release version has lower precedence
111115
// > than a normal version.
112116
if (left === right) return Comparison.EqualTo;
113117
if (left.length === 0) return right.length === 0 ? Comparison.EqualTo : Comparison.GreaterThan;
114118
if (right.length === 0) return Comparison.LessThan;
115119

116-
// Per https://semver.org/#spec-item-11:
117-
//
120+
// https://semver.org/#spec-item-11
118121
// > Precedence for two pre-release versions with the same major, minor, and patch version
119122
// > MUST be determined by comparing each dot separated identifier from left to right until
120-
// > a difference is found
123+
// > a difference is found [...]
121124
const length = Math.min(left.length, right.length);
122125
for (let i = 0; i < length; i++) {
123126
const leftIdentifier = left[i];
124127
const rightIdentifier = right[i];
125128
if (leftIdentifier === rightIdentifier) continue;
126129

127-
const leftIsNumeric = compareNumericIdentifiers && numericIdentifierRegExp.test(leftIdentifier);
128-
const rightIsNumeric = compareNumericIdentifiers && numericIdentifierRegExp.test(rightIdentifier);
130+
const leftIsNumeric = numericIdentifierRegExp.test(leftIdentifier);
131+
const rightIsNumeric = numericIdentifierRegExp.test(rightIdentifier);
129132
if (leftIsNumeric || rightIsNumeric) {
130-
// Per https://semver.org/#spec-item-11:
131-
//
133+
// https://semver.org/#spec-item-11
132134
// > Numeric identifiers always have lower precedence than non-numeric identifiers.
133135
if (leftIsNumeric !== rightIsNumeric) return leftIsNumeric ? Comparison.LessThan : Comparison.GreaterThan;
134136

135-
// Per https://semver.org/#spec-item-11:
136-
//
137+
// https://semver.org/#spec-item-11
137138
// > identifiers consisting of only digits are compared numerically
138139
const result = compareValues(+leftIdentifier, +rightIdentifier);
139140
if (result) return result;
140141
}
141142
else {
142-
// Per https://semver.org/#spec-item-11:
143-
//
143+
// https://semver.org/#spec-item-11
144144
// > identifiers with letters or hyphens are compared lexically in ASCII sort order.
145145
const result = compareStringsCaseSensitive(leftIdentifier, rightIdentifier);
146146
if (result) return result;
147147
}
148148
}
149149

150-
// Per https://semver.org/#spec-item-11:
151-
//
150+
// https://semver.org/#spec-item-11
152151
// > A larger set of pre-release fields has a higher precedence than a smaller set, if all
153152
// > of the preceding identifiers are equal.
154153
return compareValues(left.length, right.length);

Diff for: src/jsTyping/jsTyping.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ namespace ts.JsTyping {
2121

2222
export interface CachedTyping {
2323
typingLocation: string;
24-
version: Semver;
24+
version: Version;
2525
}
2626

2727
/* @internal */
2828
export function isTypingUpToDate(cachedTyping: CachedTyping, availableTypingVersions: MapLike<string>) {
29-
const availableVersion = Semver.parse(getProperty(availableTypingVersions, `ts${versionMajorMinor}`) || getProperty(availableTypingVersions, "latest")!);
30-
return !availableVersion.greaterThan(cachedTyping.version);
29+
const availableVersion = Version.parse(getProperty(availableTypingVersions, `ts${versionMajorMinor}`) || getProperty(availableTypingVersions, "latest")!);
30+
return availableVersion.compareTo(cachedTyping.version) <= 0;
3131
}
3232

3333
/* @internal */

Diff for: src/jsTyping/semver.ts

-61
This file was deleted.

Diff for: src/jsTyping/tsconfig.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
"files": [
1717
"shared.ts",
1818
"types.ts",
19-
"jsTyping.ts",
20-
"semver.ts"
19+
"jsTyping.ts"
2120
]
2221
}

Diff for: src/testRunner/tsconfig.json

+1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
"unittests/publicApi.ts",
7474
"unittests/reuseProgramStructure.ts",
7575
"unittests/session.ts",
76+
"unittests/semver.ts",
7677
"unittests/symbolWalker.ts",
7778
"unittests/telemetry.ts",
7879
"unittests/textChanges.ts",

0 commit comments

Comments
 (0)