mirror of
https://github.com/chenasraf/DefinitelyTyped-tools.git
synced 2026-05-18 01:49:03 +00:00
Use valid semvers internally (#436)
* Update test position after upgrading Prettier * Use valid semvers internally
This commit is contained in:
@@ -23,7 +23,8 @@
|
||||
"@definitelytyped/typescript-versions": "^0.0.112-next.9",
|
||||
"@definitelytyped/utils": "^0.0.112-next.9",
|
||||
"@types/node": "^14.14.35",
|
||||
"fs-extra": "^9.1.0"
|
||||
"fs-extra": "^9.1.0",
|
||||
"semver": "^7.3.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/fs-extra": "^9.0.8"
|
||||
|
||||
@@ -2,18 +2,19 @@ import { ParseDefinitionsOptions } from "./get-definitely-typed";
|
||||
import { TypingsData, AllPackages, formatTypingVersion } from "./packages";
|
||||
import {
|
||||
assertDefined,
|
||||
best,
|
||||
mapDefined,
|
||||
nAtATime,
|
||||
FS,
|
||||
logger,
|
||||
writeLog,
|
||||
Logger,
|
||||
Semver,
|
||||
UncachedNpmInfoClient,
|
||||
NpmInfoRawVersions,
|
||||
NpmInfoVersion,
|
||||
max,
|
||||
min,
|
||||
} from "@definitelytyped/utils";
|
||||
import * as semver from "semver";
|
||||
|
||||
export async function checkParseResults(
|
||||
includeNpmChecks: false,
|
||||
@@ -132,21 +133,19 @@ async function checkNpm(
|
||||
}
|
||||
|
||||
const versions = getRegularVersions(info.versions);
|
||||
const firstTypedVersion = best(
|
||||
const firstTypedVersion = min(
|
||||
mapDefined(versions, ({ hasTypes, version }) => (hasTypes ? version : undefined)),
|
||||
(a, b) => b.greaterThan(a)
|
||||
semver.compare
|
||||
);
|
||||
// A package might have added types but removed them later, so check the latest version too
|
||||
if (firstTypedVersion === undefined || !best(versions, (a, b) => a.version.greaterThan(b.version))!.hasTypes) {
|
||||
if (firstTypedVersion === undefined || !max(versions, (a, b) => semver.compare(a.version, b.version))!.hasTypes) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ourVersion = `${major}.${minor}`;
|
||||
|
||||
log("");
|
||||
log(
|
||||
`Typings already defined for ${name} (${libraryName}) as of ${firstTypedVersion.versionString} (our version: ${ourVersion})`
|
||||
);
|
||||
log(`Typings already defined for ${name} (${libraryName}) as of ${firstTypedVersion} (our version: ${ourVersion})`);
|
||||
const contributorUrls = contributors
|
||||
.map((c) => {
|
||||
const gh = "https://github.com/";
|
||||
@@ -155,14 +154,14 @@ async function checkNpm(
|
||||
.join(", ");
|
||||
log(" To fix this:");
|
||||
log(` git checkout -b not-needed-${name}`);
|
||||
const yarnargs = [name, firstTypedVersion.versionString, projectName];
|
||||
const yarnargs = [name, firstTypedVersion, projectName];
|
||||
if (libraryName !== name) {
|
||||
yarnargs.push(JSON.stringify(libraryName));
|
||||
}
|
||||
log(" yarn not-needed " + yarnargs.join(" "));
|
||||
log(` git add --all && git commit -m "${name}: Provides its own types" && git push -u origin not-needed-${name}`);
|
||||
log(` And comment PR: This will deprecate \`@types/${name}\` in favor of just \`${name}\`. CC ${contributorUrls}`);
|
||||
if (new Semver(major, minor, 0).greaterThan(firstTypedVersion)) {
|
||||
if (semver.gt(`${major}.${minor}.0`, firstTypedVersion)) {
|
||||
log(" WARNING: our version is greater!");
|
||||
}
|
||||
if (dependedOn.has(name)) {
|
||||
@@ -177,11 +176,11 @@ export async function packageHasTypes(packageName: string, client: UncachedNpmIn
|
||||
|
||||
function getRegularVersions(
|
||||
versions: NpmInfoRawVersions
|
||||
): readonly { readonly version: Semver; readonly hasTypes: boolean }[] {
|
||||
return mapDefined(Object.entries(versions), ([versionString, info]) => {
|
||||
const version = Semver.tryParse(versionString);
|
||||
return version === undefined ? undefined : { version, hasTypes: versionHasTypes(info) };
|
||||
});
|
||||
): readonly { readonly version: semver.SemVer; readonly hasTypes: boolean }[] {
|
||||
return Object.entries(versions).map(([versionString, info]) => ({
|
||||
version: new semver.SemVer(versionString),
|
||||
hasTypes: versionHasTypes(info),
|
||||
}));
|
||||
}
|
||||
|
||||
function versionHasTypes(info: NpmInfoVersion): boolean {
|
||||
|
||||
@@ -17,10 +17,10 @@ import {
|
||||
FS,
|
||||
consoleLogger,
|
||||
assertDefined,
|
||||
Semver,
|
||||
UncachedNpmInfoClient,
|
||||
NpmInfo,
|
||||
} from "@definitelytyped/utils";
|
||||
import * as semver from "semver";
|
||||
import { getAffectedPackages } from "./get-affected-packages";
|
||||
|
||||
export interface GitDiff {
|
||||
@@ -145,20 +145,18 @@ export function checkNotNeededPackage(
|
||||
Unneeded packages have to be replaced with a package on npm.`
|
||||
);
|
||||
typings = assertDefined(typings, `Unexpected error: @types package not found for ${unneeded.fullNpmName}`);
|
||||
const latestTypings = Semver.parse(
|
||||
assertDefined(
|
||||
typings.distTags.get("latest"),
|
||||
`Unexpected error: ${unneeded.fullNpmName} is missing the "latest" tag.`
|
||||
)
|
||||
const latestTypings = assertDefined(
|
||||
typings.distTags.get("latest"),
|
||||
`Unexpected error: ${unneeded.fullNpmName} is missing the "latest" tag.`
|
||||
);
|
||||
assert(
|
||||
unneeded.version.greaterThan(latestTypings),
|
||||
`The specified version ${unneeded.version.versionString} of ${unneeded.libraryName} must be newer than the version
|
||||
it is supposed to replace, ${latestTypings.versionString} of ${unneeded.fullNpmName}.`
|
||||
semver.gt(unneeded.version, latestTypings),
|
||||
`The specified version ${unneeded.version} of ${unneeded.libraryName} must be newer than the version
|
||||
it is supposed to replace, ${latestTypings} of ${unneeded.fullNpmName}.`
|
||||
);
|
||||
assert(
|
||||
source.versions.has(unneeded.version.versionString),
|
||||
`The specified version ${unneeded.version.versionString} of ${unneeded.libraryName} is not on npm.`
|
||||
source.versions.has(String(unneeded.version)),
|
||||
`The specified version ${unneeded.version} of ${unneeded.libraryName} is not on npm.`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { parseHeaderOrFail } from "@definitelytyped/header-parser";
|
||||
import { Dir, FS, InMemoryFS, mangleScopedPackage, Semver } from "@definitelytyped/utils";
|
||||
import { Dir, FS, InMemoryFS, mangleScopedPackage } from "@definitelytyped/utils";
|
||||
import * as semver from "semver";
|
||||
|
||||
class DTMock {
|
||||
public readonly fs: FS;
|
||||
@@ -43,7 +44,7 @@ class DTMock {
|
||||
const index = latestDir.get("index.d.ts") as string;
|
||||
const latestHeader = parseHeaderOrFail(index);
|
||||
const latestVersion = `${latestHeader.libraryMajorVersion}.${latestHeader.libraryMinorVersion}`;
|
||||
const olderVersionParsed = Semver.parse(olderVersion, true)!;
|
||||
const olderVersionParsed = semver.coerce(olderVersion)!;
|
||||
|
||||
const oldDir = latestDir.subdir(`v${olderVersion}`);
|
||||
const tsconfig = JSON.parse(latestDir.get("tsconfig.json") as string);
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
import assert = require("assert");
|
||||
import { Author } from "@definitelytyped/header-parser";
|
||||
import {
|
||||
FS,
|
||||
mapValues,
|
||||
assertSorted,
|
||||
unmangleScopedPackage,
|
||||
Semver,
|
||||
assertDefined,
|
||||
unique,
|
||||
} from "@definitelytyped/utils";
|
||||
import { FS, mapValues, assertSorted, unmangleScopedPackage, assertDefined, unique } from "@definitelytyped/utils";
|
||||
import { AllTypeScriptVersion, TypeScriptVersion } from "@definitelytyped/typescript-versions";
|
||||
import * as semver from "semver";
|
||||
import { readDataFile } from "./data-file";
|
||||
import { scopeName, typesDirectoryName } from "./lib/settings";
|
||||
import { parseVersionFromDirectoryName } from "./lib/definition-parser";
|
||||
@@ -261,7 +254,7 @@ interface NotNeededPackageRaw extends BaseRaw {
|
||||
}
|
||||
|
||||
export class NotNeededPackage extends PackageBase {
|
||||
readonly version: Semver;
|
||||
readonly version: semver.SemVer;
|
||||
|
||||
get license(): License.MIT {
|
||||
return License.MIT;
|
||||
@@ -286,7 +279,7 @@ export class NotNeededPackage extends PackageBase {
|
||||
constructor(readonly name: string, readonly libraryName: string, asOfVersion: string) {
|
||||
super({ libraryName });
|
||||
assert(libraryName && name && asOfVersion);
|
||||
this.version = Semver.parse(asOfVersion);
|
||||
this.version = new semver.SemVer(asOfVersion);
|
||||
}
|
||||
|
||||
get major(): number {
|
||||
@@ -496,22 +489,20 @@ export function getLicenseFromPackageJson(packageJsonLicense: unknown): License
|
||||
}
|
||||
|
||||
export class TypingsVersions {
|
||||
private readonly map: ReadonlyMap<Semver, TypingsData>;
|
||||
private readonly map: ReadonlyMap<semver.SemVer, TypingsData>;
|
||||
|
||||
/**
|
||||
* Sorted from latest to oldest.
|
||||
*/
|
||||
private readonly versions: Semver[];
|
||||
private readonly versions: semver.SemVer[];
|
||||
|
||||
constructor(data: TypingsVersionsRaw) {
|
||||
/**
|
||||
* Sorted from latest to oldest so that we publish the current version first.
|
||||
* This is important because older versions repeatedly reset the "latest" tag to the current version.
|
||||
*/
|
||||
this.versions = Object.keys(data)
|
||||
.map((key) => Semver.parse(key, true))
|
||||
.sort(Semver.compare)
|
||||
.reverse();
|
||||
this.versions = Object.keys(data).map((key) => new semver.SemVer(`${key}.0`));
|
||||
this.versions.sort(semver.rcompare);
|
||||
|
||||
this.map = new Map(
|
||||
this.versions.map((version, i) => [version, new TypingsData(data[`${version.major}.${version.minor}`], !i)])
|
||||
|
||||
@@ -219,7 +219,7 @@ describe(NotNeededPackage, () => {
|
||||
expect(data.license).toBe(License.MIT);
|
||||
expect(data.name).toBe("types-package");
|
||||
expect(data.libraryName).toBe("real-package");
|
||||
expect(data.version).toEqual({
|
||||
expect(data.version).toMatchObject({
|
||||
major: 1,
|
||||
minor: 0,
|
||||
patch: 0,
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"@octokit/rest": "^16.33.0",
|
||||
"@types/node": "^12.12.29",
|
||||
"markdown-table": "^1.1.3",
|
||||
"semver": "^7.3.7",
|
||||
"typescript": "^4.1.0",
|
||||
"yargs": "^15.4.1"
|
||||
},
|
||||
|
||||
@@ -16,7 +16,9 @@ import {
|
||||
MeasureBatchCompilationChildProcessResult,
|
||||
} from "./measureBatchCompilationWorker";
|
||||
import { AllPackages, HeaderParsedTypingVersion } from "@definitelytyped/definitions-parser";
|
||||
import { runWithListeningChildProcesses, runWithChildProcesses, Semver } from "@definitelytyped/utils";
|
||||
import { TypeScriptVersion } from "@definitelytyped/typescript-versions";
|
||||
import { runWithListeningChildProcesses, runWithChildProcesses } from "@definitelytyped/utils";
|
||||
import * as semver from "semver";
|
||||
|
||||
export interface MeasurePerfOptions {
|
||||
packageName: string;
|
||||
@@ -253,14 +255,12 @@ export async function measurePerf({
|
||||
}
|
||||
|
||||
function getLatestTypesVersionForTypeScriptVersion(
|
||||
typesVersions: readonly string[],
|
||||
typesVersions: readonly TypeScriptVersion[],
|
||||
typeScriptVersion: string
|
||||
): string | undefined {
|
||||
const tsVersion = Semver.parse(typeScriptVersion.replace(/-dev.*$/, ""));
|
||||
const tsVersion = new semver.SemVer(typeScriptVersion);
|
||||
for (let i = typesVersions.length - 1; i > 0; i--) {
|
||||
const [major, minor] = typesVersions[i].split(".").map(Number); // e.g. '3.5'
|
||||
const typesVersion = new Semver(major, minor, 0);
|
||||
if (tsVersion.greaterThan(typesVersion)) {
|
||||
if (semver.gte(tsVersion, `${typesVersions[i]}.0-`)) {
|
||||
return typesVersions[i];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"hh-mm-ss": "^1.2.0",
|
||||
"longjohn": "^0.2.11",
|
||||
"oboe": "^2.1.3",
|
||||
"semver": "^7.3.7",
|
||||
"source-map-support": "^0.4.0",
|
||||
"typescript": "^4.1.0",
|
||||
"yargs": "15.3.1"
|
||||
|
||||
@@ -213,7 +213,7 @@ function dependencySemver(dependency: DependencyVersion): string {
|
||||
export function createNotNeededPackageJSON({ libraryName, license, fullNpmName, version }: NotNeededPackage): string {
|
||||
const out = {
|
||||
name: fullNpmName,
|
||||
version: version.versionString,
|
||||
version: String(version),
|
||||
typings: null, // tslint:disable-line no-null-keyword
|
||||
description: `Stub TypeScript definitions entry for ${libraryName}, which provides its own types definitions`,
|
||||
main: "",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { NotNeededPackage } from "@definitelytyped/definitions-parser";
|
||||
import { Logger, assertDefined, Semver, best, CachedNpmInfoClient } from "@definitelytyped/utils";
|
||||
import { Logger, assertDefined, CachedNpmInfoClient, max } from "@definitelytyped/utils";
|
||||
import * as semver from "semver";
|
||||
|
||||
/**
|
||||
* When we fail to publish a deprecated package, it leaves behind an entry in the time property.
|
||||
@@ -10,27 +11,19 @@ export function skipBadPublishes(pkg: NotNeededPackage, client: CachedNpmInfoCli
|
||||
// because this is called right after isAlreadyDeprecated, we can rely on the cache being up-to-date
|
||||
const info = assertDefined(client.getNpmInfoFromCache(pkg.fullEscapedNpmName));
|
||||
const notNeeded = pkg.version;
|
||||
const latest = Semver.parse(findActualLatest(info.time));
|
||||
if (
|
||||
latest.equals(notNeeded) ||
|
||||
latest.greaterThan(notNeeded) ||
|
||||
(info.versions.has(notNeeded.versionString) &&
|
||||
!assertDefined(info.versions.get(notNeeded.versionString)).deprecated)
|
||||
) {
|
||||
const plusOne = new Semver(latest.major, latest.minor, latest.patch + 1);
|
||||
log(`Deprecation of ${notNeeded.versionString} failed, instead using ${plusOne.versionString}.`);
|
||||
return new NotNeededPackage(pkg.name, pkg.libraryName, plusOne.versionString);
|
||||
const latest = new semver.SemVer(findActualLatest(info.time));
|
||||
if (semver.lte(notNeeded, latest)) {
|
||||
const plusOne = semver.inc(latest, "patch")!;
|
||||
log(`Deprecation of ${notNeeded} failed, instead using ${plusOne}.`);
|
||||
return new NotNeededPackage(pkg.name, pkg.libraryName, plusOne);
|
||||
}
|
||||
return pkg;
|
||||
}
|
||||
|
||||
function findActualLatest(times: Map<string, string>) {
|
||||
const actual = best(times, ([k, v], [bestK, bestV]) =>
|
||||
bestK === "modified" || bestK === "created"
|
||||
? true
|
||||
: k === "modified" || k === "created"
|
||||
? false
|
||||
: new Date(v).getTime() > new Date(bestV).getTime()
|
||||
const actual = max(
|
||||
[...times].filter(([version]) => version !== "modified" && version !== "created"),
|
||||
([, a], [, b]) => (new Date(a) as never) - (new Date(b) as never)
|
||||
);
|
||||
if (!actual) {
|
||||
throw new Error("failed to find actual latest");
|
||||
|
||||
@@ -51,9 +51,9 @@ export async function deprecateNotNeededPackage(
|
||||
): Promise<void> {
|
||||
const name = pkg.fullNpmName;
|
||||
if (dry) {
|
||||
log("(dry) Skip deprecate not needed package " + name + " at " + pkg.version.versionString);
|
||||
log("(dry) Skip deprecate not needed package " + name + " at " + pkg.version);
|
||||
} else {
|
||||
log(`Deprecating ${name} at ${pkg.version.versionString} with message: ${pkg.deprecatedMessage()}.`);
|
||||
await client.deprecate(name, pkg.version.versionString, pkg.deprecatedMessage());
|
||||
log(`Deprecating ${name} at ${pkg.version} with message: ${pkg.deprecatedMessage()}.`);
|
||||
await client.deprecate(name, String(pkg.version), pkg.deprecatedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,12 +13,10 @@ import {
|
||||
} from "@definitelytyped/definitions-parser";
|
||||
import {
|
||||
assertDefined,
|
||||
best,
|
||||
computeHash,
|
||||
execAndThrowErrors,
|
||||
joinPaths,
|
||||
logUncaughtErrors,
|
||||
mapDefined,
|
||||
loggerWithErrors,
|
||||
FS,
|
||||
logger,
|
||||
@@ -29,13 +27,14 @@ import {
|
||||
sleep,
|
||||
npmInstallFlags,
|
||||
readJson,
|
||||
Semver,
|
||||
UncachedNpmInfoClient,
|
||||
withNpmCache,
|
||||
NpmPublishClient,
|
||||
CachedNpmInfoClient,
|
||||
isObject,
|
||||
max,
|
||||
} from "@definitelytyped/utils";
|
||||
import * as semver from "semver";
|
||||
import { getSecret, Secret } from "./lib/secrets";
|
||||
|
||||
const typesRegistry = "types-registry";
|
||||
@@ -75,7 +74,7 @@ export default async function publishRegistry(
|
||||
);
|
||||
const registry = JSON.stringify(registryJsonData);
|
||||
const newContentHash = computeHash(registry);
|
||||
const newVersion = `0.1.${npmVersion.patch + 1}`;
|
||||
const newVersion = semver.inc(npmVersion, "patch")!;
|
||||
const isTimeForNewVersion = isSevenDaysAfter(lastModified);
|
||||
|
||||
await publishToRegistry();
|
||||
@@ -88,13 +87,13 @@ export default async function publishRegistry(
|
||||
const token = await getSecret(Secret.NPM_TOKEN);
|
||||
|
||||
const publishClient = () => NpmPublishClient.create(token, { defaultTag: "next" });
|
||||
if (!highestSemverVersion.equals(npmVersion)) {
|
||||
if (!semver.eq(highestSemverVersion, npmVersion)) {
|
||||
// There was an error in the last publish and types-registry wasn't validated.
|
||||
// This may have just been due to a timeout, so test if types-registry@next is a subset of the one we're about to publish.
|
||||
// If so, we should just update it to "latest" now.
|
||||
log("Old version of types-registry was never tagged latest, so updating");
|
||||
await validateIsSubset(readNotNeededPackages(dt), log);
|
||||
await (await publishClient()).tag(typesRegistry, highestSemverVersion.versionString, "latest", dry, log);
|
||||
await (await publishClient()).tag(typesRegistry, String(highestSemverVersion), "latest", dry, log);
|
||||
} else if (npmContentHash !== newContentHash && isTimeForNewVersion) {
|
||||
log("New packages have been added, so publishing a new registry.");
|
||||
await publish(await publishClient(), typesRegistry, packageJson, newVersion, dry, log);
|
||||
@@ -204,10 +203,9 @@ function assertJsonNewer(newer: { [s: string]: any }, older: { [s: string]: any
|
||||
}
|
||||
switch (typeof newer[key]) {
|
||||
case "string":
|
||||
const newerver = Semver.tryParse(newer[key]);
|
||||
const olderver = Semver.tryParse(older[key]);
|
||||
const condition =
|
||||
newerver && olderver ? newerver.greaterThan(olderver) || newerver.equals(olderver) : newer[key] >= older[key];
|
||||
const newerver = semver.parse(newer[key]);
|
||||
const olderver = semver.parse(older[key]);
|
||||
const condition = newerver && olderver ? semver.gte(newerver, olderver) : newer[key] >= older[key];
|
||||
assert(condition, `${key} in ${parent} did not match: newer[key] (${newer[key]}) < older[key] (${older[key]})`);
|
||||
break;
|
||||
case "number":
|
||||
@@ -281,8 +279,8 @@ async function generateRegistry(typings: readonly TypingsData[], client: CachedN
|
||||
}
|
||||
|
||||
interface ProcessedNpmInfo {
|
||||
readonly npmVersion: Semver;
|
||||
readonly highestSemverVersion: Semver;
|
||||
readonly npmVersion: semver.SemVer;
|
||||
readonly highestSemverVersion: semver.SemVer;
|
||||
readonly npmContentHash: string;
|
||||
readonly lastModified: Date;
|
||||
}
|
||||
@@ -292,16 +290,13 @@ async function fetchAndProcessNpmInfo(
|
||||
client: UncachedNpmInfoClient
|
||||
): Promise<ProcessedNpmInfo> {
|
||||
const info = assertDefined(await client.fetchNpmInfo(escapedPackageName));
|
||||
const npmVersion = Semver.parse(assertDefined(info.distTags.get("latest")));
|
||||
const npmVersion = new semver.SemVer(assertDefined(info.distTags.get("latest")));
|
||||
const { distTags, versions, time } = info;
|
||||
const highestSemverVersion = getLatestVersion(versions.keys());
|
||||
assert.strictEqual(highestSemverVersion.versionString, distTags.get("next"));
|
||||
const npmContentHash = versions.get(npmVersion.versionString)!.typesPublisherContentHash || "";
|
||||
const highestSemverVersion = max(
|
||||
Array.from(versions.keys(), (v) => new semver.SemVer(v)),
|
||||
semver.compare
|
||||
)!;
|
||||
assert.strictEqual(String(highestSemverVersion), distTags.get("next"));
|
||||
const npmContentHash = versions.get(String(npmVersion))!.typesPublisherContentHash || "";
|
||||
return { npmVersion, highestSemverVersion, npmContentHash, lastModified: new Date(time.get("modified")!) };
|
||||
}
|
||||
function getLatestVersion(versions: Iterable<string>): Semver {
|
||||
return best(
|
||||
mapDefined(versions, (v) => Semver.tryParse(v)),
|
||||
(a, b) => a.greaterThan(b)
|
||||
)!;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"@definitelytyped/definitions-parser": "^0.0.112-next.9",
|
||||
"@definitelytyped/typescript-versions": "^0.0.112-next.9",
|
||||
"@definitelytyped/utils": "^0.0.112-next.9",
|
||||
"semver": "^7.3.7",
|
||||
"yargs": "^15.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -15,9 +15,6 @@ import {
|
||||
consoleLogger,
|
||||
NpmInfoVersion,
|
||||
logUncaughtErrors,
|
||||
Semver,
|
||||
best,
|
||||
mapDefined,
|
||||
loggerWithErrors,
|
||||
LoggerWithErrors,
|
||||
nAtATime,
|
||||
@@ -30,6 +27,7 @@ import {
|
||||
parseDefinitions,
|
||||
getDefinitelyTyped,
|
||||
} from "@definitelytyped/definitions-parser";
|
||||
import * as semver from "semver";
|
||||
|
||||
if (!module.parent) {
|
||||
logUncaughtErrors(main);
|
||||
@@ -128,19 +126,17 @@ export async function fetchTypesPackageVersionInfo(
|
||||
): Promise<{ version: string; needsPublish: boolean }> {
|
||||
let info = client.getNpmInfoFromCache(pkg.fullEscapedNpmName);
|
||||
let latestVersion = info && getHighestVersionForMajor(info.versions, pkg);
|
||||
let latestVersionInfo = latestVersion && assertDefined(info!.versions.get(latestVersion.versionString));
|
||||
let latestVersionInfo = latestVersion && assertDefined(info!.versions.get(latestVersion));
|
||||
if (!latestVersionInfo || latestVersionInfo.typesPublisherContentHash !== pkg.contentHash) {
|
||||
if (log) {
|
||||
log.info(
|
||||
`Version info not cached for ${pkg.desc}@${latestVersion ? latestVersion.versionString : "(no latest version)"}`
|
||||
);
|
||||
log.info(`Version info not cached for ${pkg.desc}@${latestVersion || "(no latest version)"}`);
|
||||
}
|
||||
info = await client.fetchAndCacheNpmInfo(pkg.fullEscapedNpmName);
|
||||
latestVersion = info && getHighestVersionForMajor(info.versions, pkg);
|
||||
latestVersionInfo = latestVersion && assertDefined(info!.versions.get(latestVersion.versionString));
|
||||
if (!latestVersionInfo) {
|
||||
return { version: versionString(pkg, /*patch*/ 0), needsPublish: true };
|
||||
if (!latestVersion) {
|
||||
return { version: `${pkg.major}.${pkg.minor}.0`, needsPublish: true };
|
||||
}
|
||||
latestVersionInfo = assertDefined(info!.versions.get(latestVersion));
|
||||
}
|
||||
|
||||
if (latestVersionInfo.deprecated) {
|
||||
@@ -151,39 +147,12 @@ export async function fetchTypesPackageVersionInfo(
|
||||
);
|
||||
}
|
||||
const needsPublish = canPublish && pkg.contentHash !== latestVersionInfo.typesPublisherContentHash;
|
||||
const patch = needsPublish
|
||||
? latestVersion!.minor === pkg.minor
|
||||
? latestVersion!.patch + 1
|
||||
: 0
|
||||
: latestVersion!.patch;
|
||||
return { version: versionString(pkg, patch), needsPublish };
|
||||
}
|
||||
|
||||
function versionString(pkg: TypingsData, patch: number): string {
|
||||
return new Semver(pkg.major, pkg.minor, patch).versionString;
|
||||
return { version: needsPublish ? semver.inc(latestVersion!, "patch")! : `${pkg.major}.${pkg.minor}.0`, needsPublish };
|
||||
}
|
||||
|
||||
function getHighestVersionForMajor(
|
||||
versions: ReadonlyMap<string, NpmInfoVersion>,
|
||||
{ major, minor }: TypingsData
|
||||
): Semver | undefined {
|
||||
const patch = latestPatchMatchingMajorAndMinor(versions.keys(), major, minor);
|
||||
return patch === undefined ? undefined : new Semver(major, minor, patch);
|
||||
}
|
||||
|
||||
/** Finds the version with matching major/minor with the latest patch version. */
|
||||
function latestPatchMatchingMajorAndMinor(
|
||||
versions: Iterable<string>,
|
||||
newMajor: number,
|
||||
newMinor: number
|
||||
): number | undefined {
|
||||
const versionsWithTypings = mapDefined(versions, (v) => {
|
||||
const semver = Semver.tryParse(v);
|
||||
if (!semver) {
|
||||
return undefined;
|
||||
}
|
||||
const { major, minor, patch } = semver;
|
||||
return major === newMajor && minor === newMinor ? patch : undefined;
|
||||
});
|
||||
return best(versionsWithTypings, (a, b) => a > b);
|
||||
): string | null {
|
||||
return semver.maxSatisfying([...versions.keys()], `~${major}.${minor}`);
|
||||
}
|
||||
|
||||
@@ -205,27 +205,26 @@ export function mapToRecord<T, U>(map: Map<string, T>, cb?: (t: T) => U): Record
|
||||
return o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the input that is better than all others, or `undefined` if there are no inputs.
|
||||
* @param isBetter Returns true if `a` should be preferred over `b`.
|
||||
*/
|
||||
export function best<T>(inputs: Iterable<T>, isBetter: (a: T, b: T) => boolean): T | undefined {
|
||||
const iter = inputs[Symbol.iterator]();
|
||||
|
||||
const first = iter.next();
|
||||
if (first.done) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let res = first.value;
|
||||
while (true) {
|
||||
const { value, done } = iter.next();
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
if (isBetter(value, res)) {
|
||||
res = value;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
export function min<T>(array: readonly [T, ...(T | undefined)[]]): T;
|
||||
export function min<T>(array: readonly T[], compare?: (a: T, b: T) => number): T | undefined;
|
||||
export function min<T>(array: readonly T[], compare?: (a: T, b: T) => number) {
|
||||
return array.length === 0
|
||||
? undefined
|
||||
: array.reduce((previousValue, currentValue) =>
|
||||
(compare ? compare(currentValue, previousValue) < 0 : currentValue < previousValue)
|
||||
? currentValue
|
||||
: previousValue
|
||||
);
|
||||
}
|
||||
|
||||
export function max<T>(array: readonly [T, ...(T | undefined)[]]): T;
|
||||
export function max<T>(array: readonly T[], compare?: (a: T, b: T) => number): T | undefined;
|
||||
export function max<T>(array: readonly T[], compare?: (a: T, b: T) => number) {
|
||||
return array.length === 0
|
||||
? undefined
|
||||
: array.reduce((previousValue, currentValue) =>
|
||||
(compare ? compare(currentValue, previousValue) > 0 : currentValue > previousValue)
|
||||
? currentValue
|
||||
: previousValue
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,5 +8,4 @@ export * from "./miscellany";
|
||||
export * from "./npm";
|
||||
export * from "./process";
|
||||
export * from "./progress";
|
||||
export * from "./semver";
|
||||
export * from "./typescript-installer";
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
/** Version of a package published to NPM. */
|
||||
export class Semver {
|
||||
static parse(semver: string, coerce?: boolean): Semver {
|
||||
const result = Semver.tryParse(semver, coerce);
|
||||
if (!result) {
|
||||
throw new Error(`Unexpected semver: ${semver}`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static fromRaw({ major, minor, patch }: { major: number; minor: number; patch: number }): Semver {
|
||||
return new Semver(major, minor, patch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 0 if equal, 1 if x > y, -1 if x < y
|
||||
*/
|
||||
static compare(x: Semver, y: Semver) {
|
||||
const versions: [number, number][] = [
|
||||
[x.major, y.major],
|
||||
[x.minor, y.minor],
|
||||
[x.patch, y.patch],
|
||||
];
|
||||
for (const [componentX, componentY] of versions) {
|
||||
if (componentX > componentY) {
|
||||
return 1;
|
||||
}
|
||||
if (componentX < componentY) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Per the semver spec <http://semver.org/#spec-item-2>:
|
||||
*
|
||||
* A normal version number MUST take the form X.Y.Z where X, Y, and Z are non-negative integers, and MUST NOT contain leading zeroes.
|
||||
*
|
||||
* @note This must parse the output of `versionString`.
|
||||
*
|
||||
* @param semver The version string.
|
||||
* @param coerce Without this optional parameter the version MUST follow the above semver spec. However, when set to `true` components after the
|
||||
* major version may be omitted. I.e. `1` equals `1.0` and `1.0.0`.
|
||||
*/
|
||||
static tryParse(semver: string, coerce?: boolean): Semver | undefined {
|
||||
const rgx = /^(\d+)(\.(\d+))?(\.(\d+))?$/;
|
||||
const match = rgx.exec(semver);
|
||||
if (match) {
|
||||
const { 1: major, 3: minor, 5: patch } = match;
|
||||
if ((minor !== undefined && patch !== undefined) || coerce) {
|
||||
// tslint:disable-line:strict-type-predicates
|
||||
return new Semver(intOfString(major), intOfString(minor || "0"), intOfString(patch || "0"));
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
constructor(readonly major: number, readonly minor: number, readonly patch: number) {}
|
||||
|
||||
get versionString(): string {
|
||||
const { major, minor, patch } = this;
|
||||
return `${major}.${minor}.${patch}`;
|
||||
}
|
||||
|
||||
equals(other: Semver): boolean {
|
||||
return Semver.compare(this, other) === 0;
|
||||
}
|
||||
|
||||
greaterThan(other: Semver): boolean {
|
||||
return Semver.compare(this, other) === 1;
|
||||
}
|
||||
}
|
||||
|
||||
function intOfString(str: string): number {
|
||||
const n = Number.parseInt(str, 10);
|
||||
if (Number.isNaN(n)) {
|
||||
throw new Error(`Error in parseInt(${JSON.stringify(str)})`);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import { Semver } from "../src/semver";
|
||||
|
||||
it("returns a formatted description", () => {
|
||||
expect(new Semver(1, 2, 3).versionString).toEqual("1.2.3");
|
||||
});
|
||||
|
||||
it("parses semver versions", () => {
|
||||
expect(Semver.parse("0.42.1").versionString).toEqual("0.42.1");
|
||||
});
|
||||
|
||||
it("parses versions that do not strictly adhere to semver", () => {
|
||||
expect(Semver.parse("1", true).versionString).toEqual("1.0.0");
|
||||
expect(Semver.parse("0.42", true).versionString).toEqual("0.42.0");
|
||||
});
|
||||
|
||||
it("throws when a version cannot be parsed", () => {
|
||||
expect(() => Semver.parse("1")).toThrow();
|
||||
expect(() => Semver.parse("1", false)).toThrow();
|
||||
});
|
||||
|
||||
it("returns whether or not it's equal to another Semver", () => {
|
||||
expect(Semver.parse("1.2.3").equals(new Semver(1, 2, 3))).toBe(true);
|
||||
expect(Semver.parse("1.2.3").equals(new Semver(3, 2, 1))).toBe(false);
|
||||
});
|
||||
|
||||
it("returns whether or not it's greater than another Semver", () => {
|
||||
expect(Semver.parse("1.2.3").greaterThan(new Semver(1, 2, 2))).toBe(true);
|
||||
expect(Semver.parse("1.2.3").equals(new Semver(1, 2, 4))).toBe(false);
|
||||
});
|
||||
@@ -7463,6 +7463,13 @@ semver@6.x, semver@^6.0.0, semver@^6.2.0, semver@^6.3.0:
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
||||
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
|
||||
|
||||
semver@^7.3.7:
|
||||
version "7.3.7"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
|
||||
integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
set-blocking@^2.0.0, set-blocking@~2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
|
||||
|
||||
Reference in New Issue
Block a user