mirror of
https://github.com/chenasraf/DefinitelyTyped-tools.git
synced 2026-05-17 17:48:07 +00:00
Move stuff around and make it build
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
*.d.ts
|
||||
dist/**
|
||||
node_modules
|
||||
node_modules
|
||||
packages/publisher/output
|
||||
@@ -1,2 +1,3 @@
|
||||
node_modules
|
||||
dist
|
||||
packages/publisher/output
|
||||
|
||||
15
.vscode/launch.json
vendored
Normal file
15
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "pwa-chrome",
|
||||
"request": "launch",
|
||||
"name": "Launch Chrome against localhost",
|
||||
"url": "http://localhost:8080",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
||||
5262
package-lock.json
generated
5262
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
7
packages/definitions-parser/package-lock.json
generated
7
packages/definitions-parser/package-lock.json
generated
@@ -14,10 +14,9 @@
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "13.7.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.7.tgz",
|
||||
"integrity": "sha512-Uo4chgKbnPNlxQwoFmYIwctkQVkMMmsAoGGU4JKwLuvBefF0pCq4FybNSnfkfRCpC7ZW7kttcC/TrRtAJsvGtg==",
|
||||
"dev": true
|
||||
"version": "12.12.35",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.35.tgz",
|
||||
"integrity": "sha512-ASYsaKecA7TUsDrqIGPNk3JeEox0z/0XR/WsJJ8BIX/9+SkMSImQXKWfU/yBrSyc7ZSE/NPqLu36Nur0miCFfQ=="
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "8.1.0",
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"dependencies": {
|
||||
"@definitelytyped/header-parser": "^0.0.22",
|
||||
"@definitelytyped/utils": "^0.0.22",
|
||||
"@types/node": "^12.12.29",
|
||||
"fs-extra": "^8.1.0",
|
||||
"typescript": "^3.8.3"
|
||||
},
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import {
|
||||
AllPackages,
|
||||
formatDependencyVersion,
|
||||
getMangledNameForScopedPackage,
|
||||
PackageBase,
|
||||
PackageId,
|
||||
TypingsData
|
||||
} from "@definitelytyped/definitions-parser";
|
||||
import { mapDefined, mapIterable, sort } from "@definitelytyped/utils";
|
||||
import {
|
||||
TypingsData,
|
||||
AllPackages,
|
||||
PackageId,
|
||||
PackageBase,
|
||||
getMangledNameForScopedPackage,
|
||||
formatDependencyVersion
|
||||
} from "./packages";
|
||||
|
||||
export interface Affected {
|
||||
readonly changedPackages: readonly TypingsData[];
|
||||
191
packages/definitions-parser/src/git.ts
Normal file
191
packages/definitions-parser/src/git.ts
Normal file
@@ -0,0 +1,191 @@
|
||||
import { sourceBranch } from "./lib/settings";
|
||||
import {
|
||||
PackageId,
|
||||
DependencyVersion,
|
||||
formatDependencyVersion,
|
||||
getDependencyFromFile,
|
||||
AllPackages,
|
||||
NotNeededPackage
|
||||
} from "./packages";
|
||||
import {
|
||||
Logger,
|
||||
execAndThrowErrors,
|
||||
flatMapIterable,
|
||||
mapIterable,
|
||||
FS,
|
||||
consoleLogger,
|
||||
assertDefined,
|
||||
Semver,
|
||||
UncachedNpmInfoClient,
|
||||
NpmInfo
|
||||
} from "@definitelytyped/utils";
|
||||
import { assert } from "console";
|
||||
import { getAffectedPackages } from "./get-affected-packages";
|
||||
|
||||
export interface GitDiff {
|
||||
status: "A" | "D" | "M";
|
||||
file: string;
|
||||
}
|
||||
|
||||
/*
|
||||
We have to be careful about how we get the diff because travis uses a shallow clone.
|
||||
|
||||
Travis runs:
|
||||
git clone --depth=50 https://github.com/DefinitelyTyped/DefinitelyTyped.git DefinitelyTyped
|
||||
cd DefinitelyTyped
|
||||
git fetch origin +refs/pull/123/merge
|
||||
git checkout -qf FETCH_HEAD
|
||||
|
||||
If editing this code, be sure to test on both full and shallow clones.
|
||||
*/
|
||||
export async function gitDiff(log: Logger, definitelyTypedPath: string): Promise<GitDiff[]> {
|
||||
try {
|
||||
await run(`git rev-parse --verify ${sourceBranch}`);
|
||||
// If this succeeds, we got the full clone.
|
||||
} catch (_) {
|
||||
// This is a shallow clone.
|
||||
await run(`git fetch origin ${sourceBranch}`);
|
||||
await run(`git branch ${sourceBranch} FETCH_HEAD`);
|
||||
}
|
||||
|
||||
let diff = (await run(`git diff ${sourceBranch} --name-status`)).trim();
|
||||
if (diff === "") {
|
||||
// We are probably already on master, so compare to the last commit.
|
||||
diff = (await run(`git diff ${sourceBranch}~1 --name-status`)).trim();
|
||||
}
|
||||
return diff.split("\n").map(line => {
|
||||
const [status, file] = line.split(/\s+/, 2);
|
||||
return { status: status.trim(), file: file.trim() } as GitDiff;
|
||||
});
|
||||
|
||||
async function run(cmd: string): Promise<string> {
|
||||
log(`Running: ${cmd}`);
|
||||
const stdout = await execAndThrowErrors(cmd, definitelyTypedPath);
|
||||
log(stdout);
|
||||
return stdout;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns all immediate subdirectories of the root directory that have changed. */
|
||||
export function gitChanges(diffs: GitDiff[]): PackageId[] {
|
||||
const changedPackages = new Map<string, Map<string, DependencyVersion>>();
|
||||
|
||||
for (const diff of diffs) {
|
||||
const dep = getDependencyFromFile(diff.file);
|
||||
if (dep) {
|
||||
const versions = changedPackages.get(dep.name);
|
||||
if (!versions) {
|
||||
changedPackages.set(dep.name, new Map([[formatDependencyVersion(dep.version), dep.version]]));
|
||||
} else {
|
||||
versions.set(formatDependencyVersion(dep.version), dep.version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(
|
||||
flatMapIterable(changedPackages, ([name, versions]) => mapIterable(versions, ([_, version]) => ({ name, version })))
|
||||
);
|
||||
}
|
||||
|
||||
export async function getAffectedPackagesFromDiff(
|
||||
dt: FS,
|
||||
definitelyTypedPath: string,
|
||||
selection: "all" | "affected" | RegExp
|
||||
) {
|
||||
const allPackages = await AllPackages.read(dt);
|
||||
const diffs = await gitDiff(consoleLogger.info, definitelyTypedPath);
|
||||
if (diffs.find(d => d.file === "notNeededPackages.json")) {
|
||||
const uncached = new UncachedNpmInfoClient();
|
||||
for (const deleted of getNotNeededPackages(allPackages, diffs)) {
|
||||
const source = await uncached.fetchNpmInfo(deleted.libraryName); // eg @babel/parser
|
||||
const typings = await uncached.fetchNpmInfo(deleted.fullNpmName); // eg @types/babel__parser
|
||||
checkNotNeededPackage(deleted, source, typings);
|
||||
}
|
||||
}
|
||||
|
||||
const affected =
|
||||
selection === "all"
|
||||
? { changedPackages: allPackages.allTypings(), dependentPackages: [], allPackages }
|
||||
: selection === "affected"
|
||||
? getAffectedPackages(allPackages, gitChanges(diffs))
|
||||
: {
|
||||
changedPackages: allPackages.allTypings().filter(t => selection.test(t.name)),
|
||||
dependentPackages: [],
|
||||
allPackages
|
||||
};
|
||||
|
||||
console.log(
|
||||
`Testing ${affected.changedPackages.length} changed packages: ${affected.changedPackages
|
||||
.map(t => t.desc)
|
||||
.toString()}`
|
||||
);
|
||||
console.log(
|
||||
`Testing ${affected.dependentPackages.length} dependent packages: ${affected.dependentPackages
|
||||
.map(t => t.desc)
|
||||
.toString()}`
|
||||
);
|
||||
return affected;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. libraryName must exist on npm (SKIPPED and preferably/optionally have been the libraryName in just-deleted header)
|
||||
* (SKIPPED 2.) sourceRepoURL must exist and be the npm homepage
|
||||
* 3. asOfVersion must be newer than `@types/name@latest` on npm
|
||||
* 4. `name@asOfVersion` must exist on npm
|
||||
*
|
||||
* I skipped (2) because the cached npm info doesn't include it. I might add it later.
|
||||
*/
|
||||
export function checkNotNeededPackage(
|
||||
unneeded: NotNeededPackage,
|
||||
source: NpmInfo | undefined,
|
||||
typings: NpmInfo | undefined
|
||||
) {
|
||||
source = assertDefined(
|
||||
source,
|
||||
`The entry for ${unneeded.fullNpmName} in notNeededPackages.json has
|
||||
"libraryName": "${unneeded.libraryName}", but there is no npm package with this name.
|
||||
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.`
|
||||
)
|
||||
);
|
||||
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}.`
|
||||
);
|
||||
assert(
|
||||
source.versions.has(unneeded.version.versionString),
|
||||
`The specified version ${unneeded.version.versionString} of ${unneeded.libraryName} is not on npm.`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. find all the deleted files and group by toplevel
|
||||
* 2. Make sure that there are no packages left with deleted entries
|
||||
* 3. make sure that each toplevel deleted has a matching entry in notNeededPackages
|
||||
*/
|
||||
export function getNotNeededPackages(allPackages: AllPackages, diffs: GitDiff[]): Iterable<NotNeededPackage> {
|
||||
const deletedPackages = new Set(
|
||||
diffs
|
||||
.filter(d => d.status === "D")
|
||||
.map(
|
||||
d =>
|
||||
assertDefined(
|
||||
getDependencyFromFile(d.file),
|
||||
`Unexpected file deleted: ${d.file}
|
||||
When removing packages, you should only delete files that are a part of removed packages.`
|
||||
).name
|
||||
)
|
||||
);
|
||||
return mapIterable(deletedPackages, p => {
|
||||
if (allPackages.hasTypingFor({ name: p, version: "*" })) {
|
||||
throw new Error(`Please delete all files in ${p} when adding it to notNeededPackages.json.`);
|
||||
}
|
||||
return assertDefined(allPackages.getNotNeededPackage(p), `Deleted package ${p} is not in notNeededPackages.json.`);
|
||||
});
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
export * from "./clean";
|
||||
export * from "./data-file";
|
||||
export * from "./get-affected-packages";
|
||||
export * from "./get-definitely-typed";
|
||||
export * from "./git";
|
||||
export * from "./parse-definitions";
|
||||
export * from "./mocks";
|
||||
export * from "./packages";
|
||||
|
||||
@@ -141,8 +141,8 @@ function getTypesVersionsAndPackageJson(ls: readonly string[]): LsMinusTypesVers
|
||||
* parseVersionFromDirectoryName("v0.61") // { major: 0, minor: 61 }
|
||||
* ```
|
||||
*/
|
||||
export function parseVersionFromDirectoryName(directoryName: string): TypingVersion | undefined {
|
||||
const match = /^v(\d+)(\.(\d+))?$/.exec(directoryName);
|
||||
export function parseVersionFromDirectoryName(directoryName: string | undefined): TypingVersion | undefined {
|
||||
const match = /^v(\d+)(\.(\d+))?$/.exec(directoryName!);
|
||||
if (match === null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,9 @@ import { joinPaths, readFileSync } from "@definitelytyped/utils";
|
||||
const root = joinPaths(__dirname, "..", "..");
|
||||
export const dataDirPath = joinPaths(root, "data");
|
||||
|
||||
export const sourceBranch = "master";
|
||||
export const typesDirectoryName = "types";
|
||||
|
||||
/** URL to download the repository from. */
|
||||
export const definitelyTypedZipUrl = "https://codeload.github.com/DefinitelyTyped/DefinitelyTyped/tar.gz/master";
|
||||
|
||||
|
||||
@@ -10,7 +10,8 @@ import {
|
||||
AllTypeScriptVersion
|
||||
} from "@definitelytyped/utils";
|
||||
import { readDataFile } from "./data-file";
|
||||
import { scopeName } from "./lib/settings";
|
||||
import { scopeName, typesDirectoryName } from "./lib/settings";
|
||||
import { parseVersionFromDirectoryName } from "./parse-definitions";
|
||||
|
||||
export class AllPackages {
|
||||
static async read(dt: FS): Promise<AllPackages> {
|
||||
@@ -611,3 +612,31 @@ export function readNotNeededPackages(dt: FS): readonly NotNeededPackage[] {
|
||||
raw => new NotNeededPackage(raw)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* For "types/a/b/c", returns { name: "a", version: "*" }.
|
||||
* For "types/a/v3/c", returns { name: "a", version: 3 }.
|
||||
* For "x", returns undefined.
|
||||
*/
|
||||
export function getDependencyFromFile(file: string): PackageId | undefined {
|
||||
const parts = file.split("/");
|
||||
if (parts.length <= 2) {
|
||||
// It's not in a typings directory at all.
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const [typesDirName, name, subDirName] = parts; // Ignore any other parts
|
||||
|
||||
if (typesDirName !== typesDirectoryName) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (subDirName) {
|
||||
const version = parseVersionFromDirectoryName(subDirName);
|
||||
if (version !== undefined) {
|
||||
return { name, version };
|
||||
}
|
||||
}
|
||||
|
||||
return { name, version: "*" };
|
||||
}
|
||||
|
||||
5
packages/perf/package-lock.json
generated
5
packages/perf/package-lock.json
generated
@@ -69,6 +69,11 @@
|
||||
"universal-user-agent": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "12.12.35",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.35.tgz",
|
||||
"integrity": "sha512-ASYsaKecA7TUsDrqIGPNk3JeEox0z/0XR/WsJJ8BIX/9+SkMSImQXKWfU/yBrSyc7ZSE/NPqLu36Nur0miCFfQ=="
|
||||
},
|
||||
"@types/parsimmon": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/parsimmon/-/parsimmon-1.10.0.tgz",
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"@definitelytyped/header-parser": "^0.0.22",
|
||||
"@definitelytyped/utils": "^0.0.22",
|
||||
"@octokit/rest": "^16.33.0",
|
||||
"@types/node": "^12.12.29",
|
||||
"dtslint": "^2.0.3",
|
||||
"markdown-table": "^1.1.3",
|
||||
"typescript": "^3.8.3"
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export * from './metrics';
|
||||
export * from './overallChange';
|
||||
export * from './summarizePackageBenchmark';
|
||||
export * from "./metrics";
|
||||
export * from "./overallChange";
|
||||
export * from "./summarizePackageBenchmark";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { mean } from '../measure/utils';
|
||||
import { PackageBenchmarkSummary, Document, config, getPercentDiff, supportsMemoryUsage, not } from '../common';
|
||||
import { assertNever } from 'types-publisher/bin/util/util';
|
||||
import { mean } from "../measure/utils";
|
||||
import { PackageBenchmarkSummary, Document, config, getPercentDiff, supportsMemoryUsage, not } from "../common";
|
||||
import { assertNever } from "@definitelytyped/utils";
|
||||
|
||||
export interface FormatOptions {
|
||||
precision?: number;
|
||||
@@ -10,9 +10,9 @@ export interface FormatOptions {
|
||||
}
|
||||
|
||||
export const enum SignificanceLevel {
|
||||
Warning = 'warning',
|
||||
Alert = 'alert',
|
||||
Awesome = 'awesome',
|
||||
Warning = "warning",
|
||||
Alert = "alert",
|
||||
Awesome = "awesome"
|
||||
}
|
||||
|
||||
export type GetSignificance = (
|
||||
@@ -34,19 +34,19 @@ export interface Metric {
|
||||
}
|
||||
|
||||
export type MetricName =
|
||||
| 'typeCount'
|
||||
| 'memoryUsage'
|
||||
| 'assignabilityCacheSize'
|
||||
| 'samplesTaken'
|
||||
| 'identifierCount'
|
||||
| 'completionsMean'
|
||||
| 'completionsStdDev'
|
||||
| 'completionsAvgCV'
|
||||
| 'quickInfoMean'
|
||||
| 'quickInfoStdDev'
|
||||
| 'quickInfoAvgCV'
|
||||
| 'completionsWorstMean'
|
||||
| 'quickInfoWorstMean';
|
||||
| "typeCount"
|
||||
| "memoryUsage"
|
||||
| "assignabilityCacheSize"
|
||||
| "samplesTaken"
|
||||
| "identifierCount"
|
||||
| "completionsMean"
|
||||
| "completionsStdDev"
|
||||
| "completionsAvgCV"
|
||||
| "quickInfoMean"
|
||||
| "quickInfoStdDev"
|
||||
| "quickInfoAvgCV"
|
||||
| "completionsWorstMean"
|
||||
| "quickInfoWorstMean";
|
||||
|
||||
function getDefaultSignificance(percentDiff: number): SignificanceLevel | undefined {
|
||||
if (percentDiff > config.comparison.percentDiffAlertThreshold) {
|
||||
@@ -58,10 +58,12 @@ function getDefaultSignificance(percentDiff: number): SignificanceLevel | undefi
|
||||
if (percentDiff < config.comparison.percentDiffAwesomeThreshold) {
|
||||
return SignificanceLevel.Awesome;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
function getOrderOfMagnitudeSignificance(percentDiff: number): SignificanceLevel | undefined {
|
||||
if (percentDiff > 10) { // decimal: 10 = 1000% = 10x increase
|
||||
if (percentDiff > 10) {
|
||||
// decimal: 10 = 1000% = 10x increase
|
||||
return SignificanceLevel.Alert;
|
||||
}
|
||||
if (percentDiff > 5) {
|
||||
@@ -70,18 +72,31 @@ function getOrderOfMagnitudeSignificance(percentDiff: number): SignificanceLevel
|
||||
if (percentDiff < -5) {
|
||||
return SignificanceLevel.Awesome;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const getInsignificant = () => undefined;
|
||||
|
||||
function proportionalTo(proportionalTo: MetricName) {
|
||||
return (getSignificance: GetSignificance): GetSignificance => (percentDiff, beforeValue, afterValue, beforeDoc, afterDoc) => {
|
||||
return (getSignificance: GetSignificance): GetSignificance => (
|
||||
percentDiff,
|
||||
beforeValue,
|
||||
afterValue,
|
||||
beforeDoc,
|
||||
afterDoc
|
||||
) => {
|
||||
const proportionalToBeforeValue = metrics[proportionalTo].getValue(beforeDoc);
|
||||
const proportionalToAfterValue = metrics[proportionalTo].getValue(afterDoc);
|
||||
if (typeof proportionalToBeforeValue === 'number' && typeof proportionalToAfterValue === 'number') {
|
||||
if (typeof proportionalToBeforeValue === "number" && typeof proportionalToAfterValue === "number") {
|
||||
const proportionalToPercentDiff = getPercentDiff(proportionalToAfterValue, proportionalToBeforeValue);
|
||||
const defaultSignificance = getSignificance(percentDiff, beforeValue, afterValue, beforeDoc, afterDoc);
|
||||
const weightedSignificance = getSignificance(percentDiff - proportionalToPercentDiff, beforeValue, afterValue, beforeDoc, afterDoc);
|
||||
const weightedSignificance = getSignificance(
|
||||
percentDiff - proportionalToPercentDiff,
|
||||
beforeValue,
|
||||
afterValue,
|
||||
beforeDoc,
|
||||
afterDoc
|
||||
);
|
||||
// Can’t give out a gold star unless it’s absolutely better, otherwise it looks really confusing
|
||||
// when type count increased by 400% and that gets treated as “awesome” when identifier count
|
||||
// increased by 500%. It may _be_ awesome, but it looks confusing.
|
||||
@@ -90,16 +105,23 @@ function proportionalTo(proportionalTo: MetricName) {
|
||||
}
|
||||
return weightedSignificance;
|
||||
}
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
enum FineIf {
|
||||
LessThan = -1,
|
||||
GreaterThanOrEqualTo = 1,
|
||||
GreaterThanOrEqualTo = 1
|
||||
}
|
||||
|
||||
function withThreshold(fineIf: FineIf, threshold: number) {
|
||||
return (getSignificance: GetSignificance): GetSignificance => (percentDiff, beforeValue, afterValue, beforeDoc, afterDoc) => {
|
||||
return (getSignificance: GetSignificance): GetSignificance => (
|
||||
percentDiff,
|
||||
beforeValue,
|
||||
afterValue,
|
||||
beforeDoc,
|
||||
afterDoc
|
||||
) => {
|
||||
const significance = getSignificance(percentDiff, beforeValue, afterValue, beforeDoc, afterDoc);
|
||||
if (afterValue * fineIf >= threshold * fineIf) {
|
||||
switch (significance) {
|
||||
@@ -118,7 +140,13 @@ function withThreshold(fineIf: FineIf, threshold: number) {
|
||||
}
|
||||
|
||||
function ignoreIfEitherBenchmark(predicate: (document: Document<PackageBenchmarkSummary>) => boolean) {
|
||||
return (getSignificance: GetSignificance): GetSignificance => (percentDiff, beforeValue, afterValue, beforeDoc, afterDoc) => {
|
||||
return (getSignificance: GetSignificance): GetSignificance => (
|
||||
percentDiff,
|
||||
beforeValue,
|
||||
afterValue,
|
||||
beforeDoc,
|
||||
afterDoc
|
||||
) => {
|
||||
if (predicate(beforeDoc) || predicate(afterDoc)) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -129,7 +157,7 @@ function ignoreIfEitherBenchmark(predicate: (document: Document<PackageBenchmark
|
||||
function compose(x: CreateGetSignificance, ...xs: CreateGetSignificance[]): CreateGetSignificance {
|
||||
return getSignificance => {
|
||||
let current = x(getSignificance);
|
||||
while (x = xs.pop()!) {
|
||||
while ((x = xs.pop()!)) {
|
||||
current = x(current);
|
||||
}
|
||||
return current;
|
||||
@@ -138,99 +166,99 @@ function compose(x: CreateGetSignificance, ...xs: CreateGetSignificance[]): Crea
|
||||
|
||||
export const metrics: { [K in MetricName]: Metric } = {
|
||||
typeCount: {
|
||||
columnName: 'Type count',
|
||||
sentenceName: 'type count',
|
||||
columnName: "Type count",
|
||||
sentenceName: "type count",
|
||||
formatOptions: { precision: 0 },
|
||||
getValue: x => x.body.typeCount,
|
||||
getSignificance: compose(
|
||||
proportionalTo('identifierCount'),
|
||||
withThreshold(FineIf.LessThan, 5000),
|
||||
)(getOrderOfMagnitudeSignificance),
|
||||
proportionalTo("identifierCount"),
|
||||
withThreshold(FineIf.LessThan, 5000)
|
||||
)(getOrderOfMagnitudeSignificance)
|
||||
},
|
||||
memoryUsage: {
|
||||
columnName: 'Memory usage (MiB)',
|
||||
sentenceName: 'memory usage',
|
||||
columnName: "Memory usage (MiB)",
|
||||
sentenceName: "memory usage",
|
||||
getValue: x => x.body.memoryUsage / 2 ** 20,
|
||||
getSignificance: compose(
|
||||
proportionalTo('identifierCount'),
|
||||
proportionalTo("identifierCount"),
|
||||
withThreshold(FineIf.LessThan, 65),
|
||||
ignoreIfEitherBenchmark(not(supportsMemoryUsage)),
|
||||
)(getOrderOfMagnitudeSignificance),
|
||||
ignoreIfEitherBenchmark(not(supportsMemoryUsage))
|
||||
)(getOrderOfMagnitudeSignificance)
|
||||
},
|
||||
assignabilityCacheSize: {
|
||||
columnName: 'Assignability cache size',
|
||||
sentenceName: 'assignability cache size',
|
||||
columnName: "Assignability cache size",
|
||||
sentenceName: "assignability cache size",
|
||||
formatOptions: { precision: 0 },
|
||||
getValue: x => x.body.relationCacheSizes && x.body.relationCacheSizes.assignable,
|
||||
getSignificance: compose(
|
||||
proportionalTo('identifierCount'),
|
||||
withThreshold(FineIf.LessThan, 1000),
|
||||
)(getOrderOfMagnitudeSignificance),
|
||||
proportionalTo("identifierCount"),
|
||||
withThreshold(FineIf.LessThan, 1000)
|
||||
)(getOrderOfMagnitudeSignificance)
|
||||
},
|
||||
samplesTaken: {
|
||||
columnName: 'Samples taken',
|
||||
sentenceName: 'number of samples taken',
|
||||
columnName: "Samples taken",
|
||||
sentenceName: "number of samples taken",
|
||||
formatOptions: { precision: 0 },
|
||||
getValue: x => Math.max(x.body.completions.trials, x.body.quickInfo.trials),
|
||||
getSignificance: getInsignificant,
|
||||
getSignificance: getInsignificant
|
||||
},
|
||||
identifierCount: {
|
||||
columnName: 'Identifiers in tests',
|
||||
sentenceName: 'number of identifiers present in test files',
|
||||
columnName: "Identifiers in tests",
|
||||
sentenceName: "number of identifiers present in test files",
|
||||
formatOptions: { precision: 0 },
|
||||
getValue: x => x.body.testIdentifierCount,
|
||||
getSignificance: getInsignificant,
|
||||
getSignificance: getInsignificant
|
||||
},
|
||||
completionsMean: {
|
||||
columnName: 'Mean duration (ms)',
|
||||
sentenceName: 'mean duration for getting completions at a position',
|
||||
columnName: "Mean duration (ms)",
|
||||
sentenceName: "mean duration for getting completions at a position",
|
||||
getValue: x => x.body.completions.mean,
|
||||
getSignificance: withThreshold(FineIf.LessThan, 150)(getDefaultSignificance),
|
||||
getSignificance: withThreshold(FineIf.LessThan, 150)(getDefaultSignificance)
|
||||
},
|
||||
completionsStdDev: {
|
||||
columnName: 'Std. deviation (ms)',
|
||||
sentenceName: 'standard deviation of the durations for getting completions at a position',
|
||||
columnName: "Std. deviation (ms)",
|
||||
sentenceName: "standard deviation of the durations for getting completions at a position",
|
||||
getValue: x => x.body.completions.standardDeviation,
|
||||
getSignificance: getInsignificant,
|
||||
getSignificance: getInsignificant
|
||||
},
|
||||
completionsAvgCV: {
|
||||
columnName: 'Mean [CV](https://en.wikipedia.org/wiki/Coefficient_of_variation)',
|
||||
sentenceName: 'mean coefficient of variation of samples measured for completions time',
|
||||
columnName: "Mean [CV](https://en.wikipedia.org/wiki/Coefficient_of_variation)",
|
||||
sentenceName: "mean coefficient of variation of samples measured for completions time",
|
||||
getValue: x => x.body.completions.meanCoefficientOfVariation,
|
||||
getSignificance: getInsignificant,
|
||||
formatOptions: { percentage: true, noDiff: true },
|
||||
formatOptions: { percentage: true, noDiff: true }
|
||||
},
|
||||
completionsWorstMean: {
|
||||
columnName: 'Worst duration (ms)',
|
||||
sentenceName: 'worst-case duration for getting completions at a position',
|
||||
columnName: "Worst duration (ms)",
|
||||
sentenceName: "worst-case duration for getting completions at a position",
|
||||
getValue: x => mean(x.body.completions.worst.completionsDurations),
|
||||
getSignificance: withThreshold(FineIf.LessThan, 200)(getDefaultSignificance),
|
||||
getSignificance: withThreshold(FineIf.LessThan, 200)(getDefaultSignificance)
|
||||
},
|
||||
quickInfoMean: {
|
||||
columnName: 'Mean duration (ms)',
|
||||
sentenceName: 'mean duration for getting quick info at a position',
|
||||
columnName: "Mean duration (ms)",
|
||||
sentenceName: "mean duration for getting quick info at a position",
|
||||
getValue: x => x.body.quickInfo.mean,
|
||||
getSignificance: withThreshold(FineIf.LessThan, 150)(getDefaultSignificance),
|
||||
getSignificance: withThreshold(FineIf.LessThan, 150)(getDefaultSignificance)
|
||||
},
|
||||
quickInfoStdDev: {
|
||||
columnName: 'Std. deviation (ms)',
|
||||
sentenceName: 'standard deviation of the durations for getting quick info at a position',
|
||||
columnName: "Std. deviation (ms)",
|
||||
sentenceName: "standard deviation of the durations for getting quick info at a position",
|
||||
getValue: x => x.body.quickInfo.standardDeviation,
|
||||
getSignificance: getInsignificant,
|
||||
getSignificance: getInsignificant
|
||||
},
|
||||
quickInfoAvgCV: {
|
||||
columnName: 'Mean [CV](https://en.wikipedia.org/wiki/Coefficient_of_variation)',
|
||||
sentenceName: 'mean coefficient of variation of samples measured for quick info time',
|
||||
columnName: "Mean [CV](https://en.wikipedia.org/wiki/Coefficient_of_variation)",
|
||||
sentenceName: "mean coefficient of variation of samples measured for quick info time",
|
||||
getValue: x => x.body.quickInfo.meanCoefficientOfVariation,
|
||||
getSignificance: getInsignificant,
|
||||
formatOptions: { percentage: true },
|
||||
formatOptions: { percentage: true }
|
||||
},
|
||||
quickInfoWorstMean: {
|
||||
columnName: 'Worst duration (ms)',
|
||||
sentenceName: 'worst-case duration for getting quick info at a position',
|
||||
columnName: "Worst duration (ms)",
|
||||
sentenceName: "worst-case duration for getting quick info at a position",
|
||||
getValue: x => mean(x.body.quickInfo.worst.quickInfoDurations),
|
||||
getSignificance: withThreshold(FineIf.LessThan, 200)(getDefaultSignificance),
|
||||
},
|
||||
getSignificance: withThreshold(FineIf.LessThan, 200)(getDefaultSignificance)
|
||||
}
|
||||
};
|
||||
|
||||
export interface ComparedMetric {
|
||||
@@ -239,22 +267,28 @@ export interface ComparedMetric {
|
||||
significance: SignificanceLevel;
|
||||
}
|
||||
|
||||
export function getInterestingMetrics(before: Document<PackageBenchmarkSummary>, after: Document<PackageBenchmarkSummary>): ComparedMetric[] {
|
||||
return Object.values(metrics).reduce((acc: { metric: Metric, percentDiff: number, significance: SignificanceLevel }[], metric) => {
|
||||
const beforeValue = metric.getValue(before);
|
||||
const afterValue = metric.getValue(after);
|
||||
const percentDiff = isNonNaNNumber(beforeValue) && isNonNaNNumber(afterValue) && getPercentDiff(afterValue, beforeValue);
|
||||
const significance = typeof percentDiff === 'number' && metric.getSignificance(percentDiff, beforeValue!, afterValue!, before, after);
|
||||
if (percentDiff && significance) {
|
||||
return [
|
||||
...acc,
|
||||
{ metric, percentDiff, significance },
|
||||
];
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
export function getInterestingMetrics(
|
||||
before: Document<PackageBenchmarkSummary>,
|
||||
after: Document<PackageBenchmarkSummary>
|
||||
): ComparedMetric[] {
|
||||
return Object.values(metrics).reduce(
|
||||
(acc: { metric: Metric; percentDiff: number; significance: SignificanceLevel }[], metric) => {
|
||||
const beforeValue = metric.getValue(before);
|
||||
const afterValue = metric.getValue(after);
|
||||
const percentDiff =
|
||||
isNonNaNNumber(beforeValue) && isNonNaNNumber(afterValue) && getPercentDiff(afterValue, beforeValue);
|
||||
const significance =
|
||||
typeof percentDiff === "number" &&
|
||||
metric.getSignificance(percentDiff, beforeValue!, afterValue!, before, after);
|
||||
if (percentDiff && significance) {
|
||||
return [...acc, { metric, percentDiff, significance }];
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
[]
|
||||
);
|
||||
}
|
||||
|
||||
function isNonNaNNumber(n: any): n is number {
|
||||
return typeof n === 'number' && !isNaN(n);
|
||||
return typeof n === "number" && !isNaN(n);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { PackageBenchmarkSummary, Document } from '../common';
|
||||
import { getInterestingMetrics, SignificanceLevel } from './metrics';
|
||||
import { assertNever } from 'types-publisher/bin/util/util';
|
||||
import { PackageBenchmarkSummary, Document } from "../common";
|
||||
import { getInterestingMetrics, SignificanceLevel } from "./metrics";
|
||||
import { assertNever } from "@definitelytyped/utils";
|
||||
|
||||
type BeforeAndAfter = [Document<PackageBenchmarkSummary> | undefined, Document<PackageBenchmarkSummary>];
|
||||
|
||||
@@ -8,10 +8,13 @@ export enum OverallChange {
|
||||
Same = 0,
|
||||
Worse = 1 << 0,
|
||||
Better = 1 << 1,
|
||||
Mixed = Worse | Better,
|
||||
Mixed = Worse | Better
|
||||
}
|
||||
|
||||
export function getOverallChangeForSingleComparison(before: Document<PackageBenchmarkSummary>, after: Document<PackageBenchmarkSummary>) {
|
||||
export function getOverallChangeForSingleComparison(
|
||||
before: Document<PackageBenchmarkSummary>,
|
||||
after: Document<PackageBenchmarkSummary>
|
||||
) {
|
||||
let change = OverallChange.Same;
|
||||
for (const { significance } of getInterestingMetrics(before, after)) {
|
||||
switch (significance) {
|
||||
@@ -30,7 +33,7 @@ export function getOverallChangeForSingleComparison(before: Document<PackageBenc
|
||||
}
|
||||
|
||||
export function getOverallChangeForComparisons(comparisons: BeforeAndAfter[]): OverallChange | undefined {
|
||||
let change: OverallChange | undefined = undefined;
|
||||
let change: OverallChange | undefined;
|
||||
for (const comparison of comparisons) {
|
||||
if (comparison[0]) {
|
||||
if (change === undefined) {
|
||||
@@ -40,4 +43,4 @@ export function getOverallChangeForComparisons(comparisons: BeforeAndAfter[]): O
|
||||
}
|
||||
}
|
||||
return change;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { PackageBenchmark, PackageBenchmarkSummary, StatSummary, LanguageServiceBenchmark } from '../common';
|
||||
import { max, mean, median, stdDev, coefficientOfVariation } from '../measure/utils';
|
||||
import { PackageBenchmark, PackageBenchmarkSummary, StatSummary, LanguageServiceBenchmark } from "../common";
|
||||
import { max, mean, median, stdDev, coefficientOfVariation } from "../measure/utils";
|
||||
|
||||
export function summarize(benchmark: PackageBenchmark): PackageBenchmarkSummary {
|
||||
return {
|
||||
@@ -16,21 +16,26 @@ export function summarize(benchmark: PackageBenchmark): PackageBenchmarkSummary
|
||||
testIdentifierCount: benchmark.testIdentifierCount,
|
||||
requestedLanguageServiceTestIterations: benchmark.requestedLanguageServiceTestIterations,
|
||||
languageServiceCrashed: benchmark.languageServiceCrashed,
|
||||
...summarizeStats(benchmark.languageServiceBenchmarks),
|
||||
}
|
||||
...summarizeStats(benchmark.languageServiceBenchmarks)
|
||||
};
|
||||
}
|
||||
|
||||
export function summarizeStats(benchmarks: LanguageServiceBenchmark[]): Pick<PackageBenchmarkSummary, 'quickInfo' | 'completions'> {
|
||||
export function summarizeStats(
|
||||
benchmarks: LanguageServiceBenchmark[]
|
||||
): Pick<PackageBenchmarkSummary, "quickInfo" | "completions"> {
|
||||
return [
|
||||
['completions', (benchmark: LanguageServiceBenchmark) => benchmark.completionsDurations] as const,
|
||||
['quickInfo', (benchmark: LanguageServiceBenchmark) => benchmark.quickInfoDurations] as const,
|
||||
["completions", (benchmark: LanguageServiceBenchmark) => benchmark.completionsDurations] as const,
|
||||
["quickInfo", (benchmark: LanguageServiceBenchmark) => benchmark.quickInfoDurations] as const
|
||||
].reduce((acc, [key, getDurations]) => {
|
||||
const [means, cvs] = benchmarks.reduce((acc, b) => {
|
||||
const durations = getDurations(b);
|
||||
acc[0].push(mean(durations));
|
||||
acc[1].push(coefficientOfVariation(durations));
|
||||
return acc;
|
||||
}, [[], []] as [number[], number[]]);
|
||||
const [means, cvs] = benchmarks.reduce(
|
||||
(acc, b) => {
|
||||
const durations = getDurations(b);
|
||||
acc[0].push(mean(durations));
|
||||
acc[1].push(coefficientOfVariation(durations));
|
||||
return acc;
|
||||
},
|
||||
[[], []] as [number[], number[]]
|
||||
);
|
||||
|
||||
const worst = max(benchmarks, b => mean(getDurations(b)));
|
||||
const stats: StatSummary<LanguageServiceBenchmark> = {
|
||||
@@ -39,12 +44,12 @@ export function summarizeStats(benchmarks: LanguageServiceBenchmark[]): Pick<Pac
|
||||
standardDeviation: stdDev(means),
|
||||
meanCoefficientOfVariation: mean(cvs),
|
||||
trials: means.length,
|
||||
worst,
|
||||
worst
|
||||
};
|
||||
|
||||
return {
|
||||
...acc,
|
||||
[key]: stats,
|
||||
[key]: stats
|
||||
};
|
||||
}, {} as Record<'completions' | 'quickInfo', StatSummary<LanguageServiceBenchmark>>);
|
||||
}, {} as Record<"completions" | "quickInfo", StatSummary<LanguageServiceBenchmark>>);
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"composite": true
|
||||
},
|
||||
"references": [
|
||||
{ "path": "../common" },
|
||||
{ "path": "../measure" }
|
||||
]
|
||||
}
|
||||
@@ -1,12 +1,22 @@
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import { getDatabase, DatabaseAccessLevel, config, getParsedPackages, assertString, assertBoolean, withDefault, assertNumber, getSystemInfo } from '../common';
|
||||
import { getTypeScript } from '../measure/getTypeScript';
|
||||
import { insertDocument } from '../write';
|
||||
import { printSummary, measurePerf } from '../measure';
|
||||
import { Args } from '../common';
|
||||
import { PackageId } from 'types-publisher/bin/lib/packages';
|
||||
import { summarize } from '../analysis';
|
||||
import * as os from "os";
|
||||
import * as path from "path";
|
||||
import {
|
||||
getDatabase,
|
||||
DatabaseAccessLevel,
|
||||
config,
|
||||
getParsedPackages,
|
||||
assertString,
|
||||
assertBoolean,
|
||||
withDefault,
|
||||
assertNumber,
|
||||
getSystemInfo,
|
||||
Args
|
||||
} from "../common";
|
||||
import { getTypeScript } from "../measure/getTypeScript";
|
||||
import { insertDocument } from "../write";
|
||||
import { printSummary, measurePerf } from "../measure";
|
||||
import { summarize } from "../analysis";
|
||||
import { PackageId, formatDependencyVersion, parseVersionFromDirectoryName } from "@definitelytyped/definitions-parser";
|
||||
const currentSystem = getSystemInfo();
|
||||
|
||||
export interface BenchmarkPackageOptions {
|
||||
@@ -29,35 +39,40 @@ export interface BenchmarkPackageOptions {
|
||||
|
||||
function convertArgs({ file, ...args }: Args): BenchmarkPackageOptions {
|
||||
if (file) {
|
||||
const fileContents = require(path.resolve(assertString(file, 'file')));
|
||||
const fileContents = require(path.resolve(assertString(file, "file")));
|
||||
if (fileContents.system.hash !== currentSystem.hash) {
|
||||
console.warn('Systems mismatch; requested:');
|
||||
console.warn("Systems mismatch; requested:");
|
||||
console.warn(JSON.stringify(fileContents.system, undefined, 2) + os.EOL);
|
||||
console.warn('Current:');
|
||||
console.warn("Current:");
|
||||
console.warn(JSON.stringify(currentSystem, undefined, 2) + os.EOL);
|
||||
}
|
||||
return {
|
||||
groups: fileContents.groups,
|
||||
...convertArgs({ ...fileContents.options, ...args }),
|
||||
failOnErrors: false,
|
||||
failOnErrors: false
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
package: args.package ? assertString(args.package) : undefined,
|
||||
agentIndex: typeof args.agentIndex !== 'undefined' ? assertNumber(args.agentIndex, 'agentIndex') : undefined,
|
||||
upload: assertBoolean(withDefault(args.upload, true), 'upload'),
|
||||
tsVersion: withDefault(args.tsVersion, 'next').toString(),
|
||||
progress: assertBoolean(withDefault(args.progress, false), 'progress'),
|
||||
iterations: assertNumber(withDefault(args.iterations, 5), 'iterations'),
|
||||
nProcesses: assertNumber(withDefault(args.nProcesses, os.cpus().length), 'nProcesses'),
|
||||
maxRunSeconds: args.maxRunSeconds ? assertNumber(args.maxRunSeconds, 'maxRunSeconds') : undefined,
|
||||
printSummary: assertBoolean(withDefault(args.printSummary, true), 'printSummary'),
|
||||
definitelyTypedPath: path.resolve(assertString(withDefault(args.definitelyTypedPath, process.cwd()), 'definitelyTypedPath')),
|
||||
agentIndex: typeof args.agentIndex !== "undefined" ? assertNumber(args.agentIndex, "agentIndex") : undefined,
|
||||
upload: assertBoolean(withDefault(args.upload, true), "upload"),
|
||||
tsVersion: withDefault(args.tsVersion, "next").toString(),
|
||||
progress: assertBoolean(withDefault(args.progress, false), "progress"),
|
||||
iterations: assertNumber(withDefault(args.iterations, 5), "iterations"),
|
||||
nProcesses: assertNumber(withDefault(args.nProcesses, os.cpus().length), "nProcesses"),
|
||||
maxRunSeconds: args.maxRunSeconds ? assertNumber(args.maxRunSeconds, "maxRunSeconds") : undefined,
|
||||
printSummary: assertBoolean(withDefault(args.printSummary, true), "printSummary"),
|
||||
definitelyTypedPath: path.resolve(
|
||||
assertString(withDefault(args.definitelyTypedPath, process.cwd()), "definitelyTypedPath")
|
||||
),
|
||||
failOnErrors: true,
|
||||
installTypeScript: true,
|
||||
localTypeScriptPath: assertString(withDefault(args.localTypeScriptPath, path.resolve('built/local')), 'localTypeScriptPath'),
|
||||
reverse: !!args.reverse,
|
||||
localTypeScriptPath: assertString(
|
||||
withDefault(args.localTypeScriptPath, path.resolve("built/local")),
|
||||
"localTypeScriptPath"
|
||||
),
|
||||
reverse: !!args.reverse
|
||||
};
|
||||
}
|
||||
|
||||
@@ -65,25 +80,32 @@ export async function benchmark(args: Args) {
|
||||
const options = convertArgs(args);
|
||||
const time = new Date();
|
||||
if (options.groups) {
|
||||
let group = options.groups[assertNumber(options.agentIndex, 'agentIndex')];
|
||||
let group = options.groups[assertNumber(options.agentIndex, "agentIndex")];
|
||||
if (options.reverse) {
|
||||
group = group.reverse();
|
||||
}
|
||||
|
||||
for (let i = 0; i < group.length; i++) {
|
||||
const packageId = group[i];
|
||||
const logString = `Benchmarking ${packageId.name}/${packageId.majorVersion} (${i + 1} of ${group.length})`;
|
||||
const logString =
|
||||
`Benchmarking ${packageId.name}/${formatDependencyVersion(packageId.version)} ` +
|
||||
`(${i + 1} of ${group.length})`;
|
||||
console.log(logString);
|
||||
console.log('='.repeat(logString.length) + os.EOL);
|
||||
await benchmarkPackage(packageId.name, packageId.majorVersion.toString(), time, options);
|
||||
console.log("=".repeat(logString.length) + os.EOL);
|
||||
await benchmarkPackage(packageId.name, formatDependencyVersion(packageId.version), time, options);
|
||||
}
|
||||
} else {
|
||||
const [packageName, packageVersion] = assertString(options.package, 'package').split('/');
|
||||
const [packageName, packageVersion] = assertString(options.package, "package").split("/");
|
||||
await benchmarkPackage(packageName, packageVersion, time, options);
|
||||
}
|
||||
}
|
||||
|
||||
export async function benchmarkPackage(packageName: string, packageVersion: string | undefined, batchRunStart: Date, options: BenchmarkPackageOptions) {
|
||||
export async function benchmarkPackage(
|
||||
packageName: string,
|
||||
packageVersion: string | undefined,
|
||||
batchRunStart: Date,
|
||||
options: BenchmarkPackageOptions
|
||||
) {
|
||||
const {
|
||||
upload,
|
||||
progress,
|
||||
@@ -95,14 +117,14 @@ export async function benchmarkPackage(packageName: string, packageVersion: stri
|
||||
definitelyTypedPath,
|
||||
failOnErrors,
|
||||
installTypeScript,
|
||||
localTypeScriptPath,
|
||||
localTypeScriptPath
|
||||
} = options;
|
||||
const version = packageVersion ? parseInt(packageVersion.replace(/^v/, ''), 10) || '*' as const : '*';
|
||||
const version = parseVersionFromDirectoryName(packageVersion) || "*";
|
||||
const { allPackages } = await getParsedPackages(definitelyTypedPath);
|
||||
if (!allPackages.tryGetTypingsData({ name: packageName, majorVersion: version })) {
|
||||
if (!allPackages.tryGetTypingsData({ name: packageName, version })) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
const { ts, tsPath } = await getTypeScript(tsVersion.toString(), localTypeScriptPath, installTypeScript);
|
||||
const benchmark = await measurePerf({
|
||||
packageName,
|
||||
@@ -117,7 +139,7 @@ export async function benchmarkPackage(packageName: string, packageVersion: stri
|
||||
tsPath,
|
||||
ts,
|
||||
batchRunStart,
|
||||
failOnErrors,
|
||||
failOnErrors
|
||||
});
|
||||
|
||||
const summary = summarize(benchmark);
|
||||
@@ -131,8 +153,9 @@ export async function benchmarkPackage(packageName: string, packageVersion: stri
|
||||
const item = await insertDocument(
|
||||
summary,
|
||||
config.database.packageBenchmarksDocumentSchemaVersion,
|
||||
packageBenchmarks);
|
||||
|
||||
packageBenchmarks
|
||||
);
|
||||
|
||||
return { benchmark, summary, id: item.id };
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,36 @@
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import { getDatabase, DatabaseAccessLevel, config, getChangedPackages, packageIdsAreEqual, PackageBenchmarkSummary, Args, getParsedPackages, assertString, withDefault, Document, createDocument, assertNumber, shuffle, systemsAreCloseEnough, getSystemInfo } from '../common';
|
||||
import { getLatestBenchmark } from '../query';
|
||||
import { AllPackages } from 'types-publisher/bin/lib/packages';
|
||||
import { benchmarkPackage } from './benchmark';
|
||||
import { execAndThrowErrors } from 'types-publisher/bin/util/util';
|
||||
import { getAffectedPackages } from 'types-publisher/bin/tester/get-affected-packages';
|
||||
import { printSummary } from '../measure';
|
||||
import { getTypeScript } from '../measure/getTypeScript';
|
||||
import { postInitialComparisonResults } from '../github/postInitialComparisonResults';
|
||||
import { postDependentsComparisonResult } from '../github/postDependentsComparisonResults';
|
||||
import * as os from "os";
|
||||
import * as path from "path";
|
||||
import {
|
||||
getDatabase,
|
||||
DatabaseAccessLevel,
|
||||
config,
|
||||
getChangedPackages,
|
||||
packageIdsAreEqual,
|
||||
PackageBenchmarkSummary,
|
||||
Args,
|
||||
getParsedPackages,
|
||||
assertString,
|
||||
withDefault,
|
||||
Document,
|
||||
createDocument,
|
||||
assertNumber,
|
||||
shuffle,
|
||||
systemsAreCloseEnough,
|
||||
getSystemInfo
|
||||
} from "../common";
|
||||
import { getLatestBenchmark } from "../query";
|
||||
import { benchmarkPackage } from "./benchmark";
|
||||
import { printSummary } from "../measure";
|
||||
import { getTypeScript } from "../measure/getTypeScript";
|
||||
import { postInitialComparisonResults } from "../github/postInitialComparisonResults";
|
||||
import { postDependentsComparisonResult } from "../github/postDependentsComparisonResults";
|
||||
import {
|
||||
AllPackages,
|
||||
getAffectedPackages,
|
||||
PackageId,
|
||||
parseVersionFromDirectoryName
|
||||
} from "@definitelytyped/definitions-parser";
|
||||
import { execAndThrowErrors } from "@definitelytyped/utils";
|
||||
const currentSystem = getSystemInfo();
|
||||
|
||||
export interface CompareOptions {
|
||||
@@ -23,16 +44,21 @@ export interface CompareOptions {
|
||||
}
|
||||
|
||||
export async function compare(args: Args) {
|
||||
const definitelyTypedPath = path.resolve(assertString(withDefault(args.definitelyTypedPath, process.cwd()), 'definitelyTypedPath'));
|
||||
const typeScriptVersionMajorMinor = assertString(args.typeScriptVersion ? args.typeScriptVersion.toString() : undefined, 'typeScriptVersion');
|
||||
const definitelyTypedPath = path.resolve(
|
||||
assertString(withDefault(args.definitelyTypedPath, process.cwd()), "definitelyTypedPath")
|
||||
);
|
||||
const typeScriptVersionMajorMinor = assertString(
|
||||
args.typeScriptVersion ? args.typeScriptVersion.toString() : undefined,
|
||||
"typeScriptVersion"
|
||||
);
|
||||
const { allPackages } = await getParsedPackages(definitelyTypedPath);
|
||||
const changedPackages = await getChangedPackages({ diffTo: 'origin/master', definitelyTypedPath });
|
||||
const changedPackages = await getChangedPackages({ diffTo: "origin/master", definitelyTypedPath });
|
||||
const maxRunSeconds = args.maxRunSeconds ? assertNumber(args.maxRunSeconds) : undefined;
|
||||
const shouldComment = !!args.comment;
|
||||
const upload = !!args.upload;
|
||||
const runDependents = args.runDependents ? typeof args.runDependents === 'number' ? args.runDependents : 2 : 0;
|
||||
const runDependents = args.runDependents ? (typeof args.runDependents === "number" ? args.runDependents : 2) : 0;
|
||||
if (!changedPackages) {
|
||||
console.log('No changed packages; nothing to do');
|
||||
console.log("No changed packages; nothing to do");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -46,45 +72,52 @@ export async function compare(args: Args) {
|
||||
}
|
||||
|
||||
console.log(`Comparing ${affectedPackage.id.name}/v${affectedPackage.major} because it changed...\n\n`);
|
||||
comparisons.push(await compareBenchmarks({
|
||||
allPackages,
|
||||
definitelyTypedPath,
|
||||
typeScriptVersionMajorMinor,
|
||||
packageName: affectedPackage.id.name,
|
||||
packageVersion: affectedPackage.major,
|
||||
maxRunSeconds,
|
||||
upload,
|
||||
}));
|
||||
comparisons.push(
|
||||
await compareBenchmarks({
|
||||
allPackages,
|
||||
definitelyTypedPath,
|
||||
typeScriptVersionMajorMinor,
|
||||
packageName: affectedPackage.id.name,
|
||||
packageVersion: affectedPackage.major,
|
||||
maxRunSeconds,
|
||||
upload
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const outOfTime = (Date.now() - startTime) / 1000 > (maxRunSeconds ?? Infinity);
|
||||
const dependentsToTest = runDependents && !outOfTime ? shuffle(affectedPackages.dependentPackages).slice(0, runDependents) : [];
|
||||
const dependentsToTest =
|
||||
runDependents && !outOfTime ? shuffle(affectedPackages.dependentPackages).slice(0, runDependents) : [];
|
||||
if (comparisons.length) {
|
||||
const message = await postInitialComparisonResults({
|
||||
comparisons,
|
||||
dependentCount: dependentsToTest.length,
|
||||
dryRun: !shouldComment,
|
||||
dryRun: !shouldComment
|
||||
});
|
||||
console.log('\n' + message + '\n');
|
||||
console.log("\n" + message + "\n");
|
||||
}
|
||||
|
||||
const dependentComparisons: [Document<PackageBenchmarkSummary> | undefined, Document<PackageBenchmarkSummary>][] = [];
|
||||
for (const affectedPackage of dependentsToTest) {
|
||||
console.log(`Comparing ${affectedPackage.id.name}/v${affectedPackage.major} because it depends on something that changed...\n\n`);
|
||||
dependentComparisons.push(await compareBenchmarks({
|
||||
allPackages,
|
||||
definitelyTypedPath,
|
||||
typeScriptVersionMajorMinor,
|
||||
packageName: affectedPackage.id.name,
|
||||
packageVersion: affectedPackage.major,
|
||||
maxRunSeconds,
|
||||
upload,
|
||||
}));
|
||||
console.log(
|
||||
`Comparing ${affectedPackage.id.name}/v${affectedPackage.major} because it depends on something that changed...\n\n`
|
||||
);
|
||||
dependentComparisons.push(
|
||||
await compareBenchmarks({
|
||||
allPackages,
|
||||
definitelyTypedPath,
|
||||
typeScriptVersionMajorMinor,
|
||||
packageName: affectedPackage.id.name,
|
||||
packageVersion: affectedPackage.major,
|
||||
maxRunSeconds,
|
||||
upload
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (dependentComparisons.length) {
|
||||
const message = await postDependentsComparisonResult({ comparisons: dependentComparisons, dryRun: !shouldComment });
|
||||
console.log('\n' + message + '\n');
|
||||
console.log("\n" + message + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,24 +128,29 @@ export async function compareBenchmarks({
|
||||
packageName,
|
||||
packageVersion,
|
||||
maxRunSeconds,
|
||||
upload = true,
|
||||
upload = true
|
||||
}: CompareOptions): Promise<[Document<PackageBenchmarkSummary> | undefined, Document<PackageBenchmarkSummary>]> {
|
||||
const { packageBenchmarks: container } = await getDatabase(DatabaseAccessLevel.Read);
|
||||
const latestBenchmarkDocument = await getLatestBenchmark({
|
||||
container,
|
||||
typeScriptVersionMajorMinor,
|
||||
packageName,
|
||||
packageVersion,
|
||||
packageVersion
|
||||
});
|
||||
|
||||
let latestBenchmark: PackageBenchmarkSummary | undefined = latestBenchmarkDocument && latestBenchmarkDocument.body;
|
||||
const packageId = { name: packageName, majorVersion: packageVersion };
|
||||
const packageId: PackageId = {
|
||||
name: packageName,
|
||||
version: parseVersionFromDirectoryName(packageVersion.toString()) || "*"
|
||||
};
|
||||
|
||||
const changedPackagesBetweenLastRunAndMaster = latestBenchmark && await getChangedPackages({
|
||||
diffFrom: 'origin/master',
|
||||
diffTo: latestBenchmark.sourceVersion,
|
||||
definitelyTypedPath,
|
||||
});
|
||||
const changedPackagesBetweenLastRunAndMaster =
|
||||
latestBenchmark &&
|
||||
(await getChangedPackages({
|
||||
diffFrom: "origin/master",
|
||||
diffTo: latestBenchmark.sourceVersion,
|
||||
definitelyTypedPath
|
||||
}));
|
||||
|
||||
if (latestBenchmarkDocument && !systemsAreCloseEnough(currentSystem, latestBenchmarkDocument.system)) {
|
||||
latestBenchmark = undefined;
|
||||
@@ -126,8 +164,10 @@ export async function compareBenchmarks({
|
||||
needsRerun = affected.some(affectedPackage => packageIdsAreEqual(packageId, affectedPackage.id));
|
||||
}
|
||||
if (needsRerun) {
|
||||
console.log(`No comparable benchmark for ${packageName}/v${packageVersion}. Checking out master and running one...`);
|
||||
await execAndThrowErrors('git checkout origin/master && git clean -xdf types', definitelyTypedPath);
|
||||
console.log(
|
||||
`No comparable benchmark for ${packageName}/v${packageVersion}. Checking out master and running one...`
|
||||
);
|
||||
await execAndThrowErrors("git checkout origin/master && git clean -xdf types", definitelyTypedPath);
|
||||
const latest = await benchmarkPackage(packageName, packageVersion.toString(), new Date(), {
|
||||
definitelyTypedPath,
|
||||
printSummary: false,
|
||||
@@ -138,7 +178,7 @@ export async function compareBenchmarks({
|
||||
nProcesses: os.cpus().length,
|
||||
failOnErrors: true,
|
||||
installTypeScript: false,
|
||||
maxRunSeconds,
|
||||
maxRunSeconds
|
||||
});
|
||||
await execAndThrowErrors(`git checkout . && git checkout - && git clean -xdf types`, definitelyTypedPath);
|
||||
latestBenchmark = latest && latest.summary;
|
||||
@@ -155,20 +195,21 @@ export async function compareBenchmarks({
|
||||
nProcesses: os.cpus().length,
|
||||
failOnErrors: true,
|
||||
installTypeScript: false,
|
||||
maxRunSeconds,
|
||||
maxRunSeconds
|
||||
}))!.summary;
|
||||
|
||||
if (latestBenchmark) {
|
||||
console.log('\nmaster');
|
||||
console.log('======');
|
||||
console.log("\nmaster");
|
||||
console.log("======");
|
||||
console.log(printSummary([latestBenchmark]));
|
||||
}
|
||||
|
||||
console.log('\nHEAD');
|
||||
console.log('====');
|
||||
console.log("\nHEAD");
|
||||
console.log("====");
|
||||
console.log(printSummary([currentBenchmark]));
|
||||
return [
|
||||
latestBenchmarkDocument || latestBenchmark && createDocument(latestBenchmark, config.database.packageBenchmarksDocumentSchemaVersion),
|
||||
createDocument(currentBenchmark, config.database.packageBenchmarksDocumentSchemaVersion),
|
||||
latestBenchmarkDocument ||
|
||||
(latestBenchmark && createDocument(latestBenchmark, config.database.packageBenchmarksDocumentSchemaVersion)),
|
||||
createDocument(currentBenchmark, config.database.packageBenchmarksDocumentSchemaVersion)
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,15 +1,44 @@
|
||||
import * as os from 'os';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { promisify } from 'util';
|
||||
import { PackageId, AllPackages } from "types-publisher/bin/lib/packages";
|
||||
import { getDatabase, DatabaseAccessLevel, Document, PackageBenchmarkSummary, getChangedPackages, packageIdsAreEqual, getParsedPackages, config, toPackageKey, parsePackageKey, createDocument, Args, assertString, withDefault, assertNumber, assertDefined, assertBoolean, getSystemInfo, systemsAreCloseEnough, JSONDocument, deserializeSummary, QueryResult, TypeScriptComparisonRun, getSourceVersion } from "../common";
|
||||
import * as os from "os";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import { promisify } from "util";
|
||||
import {
|
||||
getDatabase,
|
||||
DatabaseAccessLevel,
|
||||
Document,
|
||||
PackageBenchmarkSummary,
|
||||
getChangedPackages,
|
||||
packageIdsAreEqual,
|
||||
getParsedPackages,
|
||||
config,
|
||||
toPackageKey,
|
||||
parsePackageKey,
|
||||
createDocument,
|
||||
Args,
|
||||
assertString,
|
||||
withDefault,
|
||||
assertNumber,
|
||||
assertBoolean,
|
||||
getSystemInfo,
|
||||
systemsAreCloseEnough,
|
||||
JSONDocument,
|
||||
deserializeSummary,
|
||||
QueryResult,
|
||||
TypeScriptComparisonRun,
|
||||
getSourceVersion
|
||||
} from "../common";
|
||||
import { Container, Response } from "@azure/cosmos";
|
||||
import { FS } from "types-publisher/bin/get-definitely-typed";
|
||||
import { benchmarkPackage } from "./benchmark";
|
||||
import { getTypeScript } from '../measure/getTypeScript';
|
||||
import { postTypeScriptComparisonResults } from '../github/postTypeScriptComparisonResult';
|
||||
import { insertDocument } from '../write';
|
||||
import { getTypeScript } from "../measure/getTypeScript";
|
||||
import { postTypeScriptComparisonResults } from "../github/postTypeScriptComparisonResult";
|
||||
import { insertDocument } from "../write";
|
||||
import {
|
||||
PackageId,
|
||||
AllPackages,
|
||||
formatDependencyVersion,
|
||||
parseVersionFromDirectoryName
|
||||
} from "@definitelytyped/definitions-parser";
|
||||
import { assertDefined, FS } from "@definitelytyped/utils";
|
||||
const writeFile = promisify(fs.writeFile);
|
||||
const currentSystem = getSystemInfo();
|
||||
|
||||
@@ -28,23 +57,30 @@ export interface CompareTypeScriptOptions {
|
||||
|
||||
function convertArgs({ file, ...args }: Args): CompareTypeScriptOptions {
|
||||
if (file) {
|
||||
const jsonContent = require(path.resolve(assertString(file, 'file')));
|
||||
const jsonContent = require(path.resolve(assertString(file, "file")));
|
||||
return {
|
||||
...convertArgs({ ...args, ...jsonContent.options }),
|
||||
groups: jsonContent.groups,
|
||||
groups: jsonContent.groups
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
compareAgainstMajorMinor: assertDefined(args.compareAgainstMajorMinor, 'compareAgainstMajorMinor').toString(),
|
||||
definitelyTypedPath: assertString(args.definitelyTypedPath, 'definitelyTypedPath'),
|
||||
typeScriptPath: assertString(withDefault(args.typeScriptPath, path.resolve('built/local')), 'typeScriptPath'),
|
||||
packages: args.packages === true ? undefined : args.packages ? assertString(args.packages, 'packages').split(',').map(p => parsePackageKey(p.trim())) : undefined,
|
||||
maxRunSeconds: args.maxRunSeconds ? assertNumber(args.maxRunSeconds, 'maxRunSeconds') : undefined,
|
||||
upload: assertBoolean(withDefault(args.upload, true), 'upload'),
|
||||
outFile: args.outFile ? assertString(args.outFile, 'outFile') : undefined,
|
||||
agentCount: args.agentCount ? assertNumber(args.agentCount, 'agentCount') : undefined,
|
||||
agentIndex: typeof args.agentIndex !== 'undefined' ? assertNumber(args.agentIndex, 'agentIndex') : undefined,
|
||||
compareAgainstMajorMinor: assertDefined(args.compareAgainstMajorMinor, "compareAgainstMajorMinor").toString(),
|
||||
definitelyTypedPath: assertString(args.definitelyTypedPath, "definitelyTypedPath"),
|
||||
typeScriptPath: assertString(withDefault(args.typeScriptPath, path.resolve("built/local")), "typeScriptPath"),
|
||||
packages:
|
||||
args.packages === true
|
||||
? undefined
|
||||
: args.packages
|
||||
? assertString(args.packages, "packages")
|
||||
.split(",")
|
||||
.map(p => parsePackageKey(p.trim()))
|
||||
: undefined,
|
||||
maxRunSeconds: args.maxRunSeconds ? assertNumber(args.maxRunSeconds, "maxRunSeconds") : undefined,
|
||||
upload: assertBoolean(withDefault(args.upload, true), "upload"),
|
||||
outFile: args.outFile ? assertString(args.outFile, "outFile") : undefined,
|
||||
agentCount: args.agentCount ? assertNumber(args.agentCount, "agentCount") : undefined,
|
||||
agentIndex: typeof args.agentIndex !== "undefined" ? assertNumber(args.agentIndex, "agentIndex") : undefined
|
||||
};
|
||||
}
|
||||
|
||||
@@ -64,51 +100,70 @@ export async function compareTypeScript({
|
||||
...opts
|
||||
}: CompareTypeScriptOptions) {
|
||||
const getAllPackages = createGetAllPackages(definitelyTypedPath);
|
||||
const { packageBenchmarks, typeScriptComparisons } = await getDatabase(upload && !outFile ? DatabaseAccessLevel.Write : DatabaseAccessLevel.Read);
|
||||
const agentIndex = groups && assertDefined(opts.agentIndex, 'agentIndex');
|
||||
const { packageBenchmarks, typeScriptComparisons } = await getDatabase(
|
||||
upload && !outFile ? DatabaseAccessLevel.Write : DatabaseAccessLevel.Read
|
||||
);
|
||||
const agentIndex = groups && assertDefined(opts.agentIndex, "agentIndex");
|
||||
const priorResults = groups
|
||||
? new Map<string, QueryResult<Document<PackageBenchmarkSummary>>>(Object.keys(groups[agentIndex!]).map(key => [key, deserializeSummary(groups[agentIndex!][key])] as const))
|
||||
: await getPackagesToTestAndPriorResults(packageBenchmarks, compareAgainstMajorMinor, definitelyTypedPath, getAllPackages, packages);
|
||||
|
||||
if (outFile) {
|
||||
const agentCount = assertDefined(opts.agentCount, 'agentCount');
|
||||
const fileContent = JSON.stringify({
|
||||
options: {
|
||||
? new Map<string, QueryResult<Document<PackageBenchmarkSummary>>>(
|
||||
Object.keys(groups[agentIndex!]).map(key => [key, deserializeSummary(groups[agentIndex!][key])] as const)
|
||||
)
|
||||
: await getPackagesToTestAndPriorResults(
|
||||
packageBenchmarks,
|
||||
compareAgainstMajorMinor,
|
||||
definitelyTypedPath,
|
||||
maxRunSeconds,
|
||||
typeScriptPath,
|
||||
upload,
|
||||
},
|
||||
groups: Array.from(priorResults.keys()).reduce((groups, key, index) => {
|
||||
const agentIndex = index % agentCount;
|
||||
if (groups[agentIndex]) {
|
||||
groups[agentIndex][key] = priorResults.get(key)!;
|
||||
} else {
|
||||
groups[agentIndex] = { [key]: priorResults.get(key)! };
|
||||
}
|
||||
return groups;
|
||||
}, [] as { [key: string]: Document<PackageBenchmarkSummary> }[]),
|
||||
}, undefined, 2);
|
||||
getAllPackages,
|
||||
packages
|
||||
);
|
||||
|
||||
return writeFile(outFile, fileContent, 'utf8');
|
||||
if (outFile) {
|
||||
const agentCount = assertDefined(opts.agentCount, "agentCount");
|
||||
const fileContent = JSON.stringify(
|
||||
{
|
||||
options: {
|
||||
compareAgainstMajorMinor,
|
||||
definitelyTypedPath,
|
||||
maxRunSeconds,
|
||||
typeScriptPath,
|
||||
upload
|
||||
},
|
||||
groups: Array.from(priorResults.keys()).reduce((groups, key, index) => {
|
||||
const agentIndex = index % agentCount;
|
||||
if (groups[agentIndex]) {
|
||||
groups[agentIndex][key] = priorResults.get(key)!;
|
||||
} else {
|
||||
groups[agentIndex] = { [key]: priorResults.get(key)! };
|
||||
}
|
||||
return groups;
|
||||
}, [] as { [key: string]: Document<PackageBenchmarkSummary> }[])
|
||||
},
|
||||
undefined,
|
||||
2
|
||||
);
|
||||
|
||||
return writeFile(outFile, fileContent, "utf8");
|
||||
}
|
||||
|
||||
await getTypeScript(compareAgainstMajorMinor);
|
||||
const packagesToTest = packages ? packages.map(p => `${p.name}/v${p.majorVersion}`) : Array.from(priorResults.keys());
|
||||
const packagesToTest = packages
|
||||
? packages.map(p => `${p.name}/v${formatDependencyVersion(p.version)}`)
|
||||
: Array.from(priorResults.keys());
|
||||
const now = new Date();
|
||||
const comparisons: [Document<PackageBenchmarkSummary>, Document<PackageBenchmarkSummary>][] = [];
|
||||
for (const packageKey of packagesToTest) {
|
||||
const { name, majorVersion } = parsePackageKey(packageKey);
|
||||
let priorResult: Document<PackageBenchmarkSummary> | QueryResult<Document<PackageBenchmarkSummary>> | undefined = priorResults.get(packageKey);
|
||||
let priorResultId = priorResult && 'id' in priorResult && priorResult.id;
|
||||
const { name, version } = parsePackageKey(packageKey);
|
||||
let priorResult:
|
||||
| Document<PackageBenchmarkSummary>
|
||||
| QueryResult<Document<PackageBenchmarkSummary>>
|
||||
| undefined = priorResults.get(packageKey);
|
||||
const priorResultId = priorResult && "id" in priorResult && priorResult.id;
|
||||
if (priorResult) {
|
||||
if (!systemsAreCloseEnough(currentSystem, priorResult.system)) {
|
||||
console.log(`Skipping ${packageKey} because the system is too different`);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
const benchmark = await benchmarkPackage(name, majorVersion.toString(), now, {
|
||||
const benchmark = await benchmarkPackage(name, formatDependencyVersion(version), now, {
|
||||
definitelyTypedPath,
|
||||
iterations: config.benchmarks.languageServiceIterations,
|
||||
tsVersion: compareAgainstMajorMinor,
|
||||
@@ -118,7 +173,7 @@ export async function compareTypeScript({
|
||||
upload,
|
||||
installTypeScript: false,
|
||||
failOnErrors: false,
|
||||
maxRunSeconds,
|
||||
maxRunSeconds
|
||||
});
|
||||
|
||||
if (!benchmark) {
|
||||
@@ -132,31 +187,35 @@ export async function compareTypeScript({
|
||||
};
|
||||
}
|
||||
|
||||
const currentResult = createDocument((await benchmarkPackage(name, majorVersion.toString(), now, {
|
||||
definitelyTypedPath,
|
||||
iterations: config.benchmarks.languageServiceIterations,
|
||||
tsVersion: 'local',
|
||||
localTypeScriptPath: typeScriptPath,
|
||||
nProcesses: os.cpus().length,
|
||||
printSummary: true,
|
||||
progress: false,
|
||||
upload: false,
|
||||
installTypeScript: false,
|
||||
failOnErrors: false,
|
||||
maxRunSeconds,
|
||||
}))!.summary, config.database.packageBenchmarksDocumentSchemaVersion);
|
||||
|
||||
const currentResult = createDocument(
|
||||
(await benchmarkPackage(name, formatDependencyVersion(version), now, {
|
||||
definitelyTypedPath,
|
||||
iterations: config.benchmarks.languageServiceIterations,
|
||||
tsVersion: "local",
|
||||
localTypeScriptPath: typeScriptPath,
|
||||
nProcesses: os.cpus().length,
|
||||
printSummary: true,
|
||||
progress: false,
|
||||
upload: false,
|
||||
installTypeScript: false,
|
||||
failOnErrors: false,
|
||||
maxRunSeconds
|
||||
}))!.summary,
|
||||
config.database.packageBenchmarksDocumentSchemaVersion
|
||||
);
|
||||
|
||||
comparisons.push([priorResult!, currentResult]);
|
||||
if (upload && priorResultId) {
|
||||
const comparison: TypeScriptComparisonRun = {
|
||||
sourceVersion: getSourceVersion(typeScriptPath),
|
||||
compareAgainstPackageBenchmarkId: priorResultId,
|
||||
headBenchmark: currentResult.body,
|
||||
headBenchmark: currentResult.body
|
||||
};
|
||||
await insertDocument(
|
||||
comparison,
|
||||
config.database.typeScriptComparisonsDocumentSchemaVersion,
|
||||
typeScriptComparisons);
|
||||
typeScriptComparisons
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,19 +223,29 @@ export async function compareTypeScript({
|
||||
console.log(comment);
|
||||
}
|
||||
|
||||
async function getPackagesToTestAndPriorResults(container: Container, typeScriptVersion: string, definitelyTypedPath: string, getAllPackages: ReturnType<typeof createGetAllPackages>, packageList?: PackageId[]) {
|
||||
const iterator: AsyncIterable<Response<QueryResult<Document<PackageBenchmarkSummary>>>> = await container.items.query({
|
||||
query: `SELECT * FROM ${config.database.packageBenchmarksContainerId} b` +
|
||||
` WHERE b.body.typeScriptVersionMajorMinor = @typeScriptVersion` +
|
||||
(packageList ?
|
||||
` AND b.body.packageName IN (${packageList!.map(({ name }) => `"${name}"`).join(', ')})` // Couldn’t figure out how to do this with parameters
|
||||
: ''),
|
||||
parameters: [
|
||||
{ name: '@typeScriptVersion', value: typeScriptVersion },
|
||||
],
|
||||
}, {
|
||||
enableCrossPartitionQuery: true,
|
||||
}).getAsyncIterator();
|
||||
async function getPackagesToTestAndPriorResults(
|
||||
container: Container,
|
||||
typeScriptVersion: string,
|
||||
definitelyTypedPath: string,
|
||||
getAllPackages: ReturnType<typeof createGetAllPackages>,
|
||||
packageList?: PackageId[]
|
||||
) {
|
||||
const iterator: AsyncIterable<Response<QueryResult<Document<PackageBenchmarkSummary>>>> = await container.items
|
||||
.query(
|
||||
{
|
||||
query:
|
||||
`SELECT * FROM ${config.database.packageBenchmarksContainerId} b` +
|
||||
` WHERE b.body.typeScriptVersionMajorMinor = @typeScriptVersion` +
|
||||
(packageList
|
||||
? ` AND b.body.packageName IN (${packageList!.map(({ name }) => `"${name}"`).join(", ")})` // Couldn’t figure out how to do this with parameters
|
||||
: ""),
|
||||
parameters: [{ name: "@typeScriptVersion", value: typeScriptVersion }]
|
||||
},
|
||||
{
|
||||
enableCrossPartitionQuery: true
|
||||
}
|
||||
)
|
||||
.getAsyncIterator();
|
||||
|
||||
const packageKeys = packageList && packageList.map(toPackageKey);
|
||||
const packages = new Map<string, QueryResult<Document<PackageBenchmarkSummary>>>();
|
||||
@@ -192,7 +261,10 @@ async function getPackagesToTestAndPriorResults(container: Container, typeScript
|
||||
continue;
|
||||
}
|
||||
|
||||
const packageId = { name: result.body.packageName, majorVersion: parseInt(result.body.packageVersion, 10) || '*' as const };
|
||||
const packageId: PackageId = {
|
||||
name: result.body.packageName,
|
||||
version: parseVersionFromDirectoryName(result.body.packageVersion) || ("*" as const)
|
||||
};
|
||||
const changedPackages = await getChangedPackages({ diffTo: result.body.sourceVersion, definitelyTypedPath });
|
||||
if (changedPackages && changedPackages.some(packageIdsAreEqual(packageId))) {
|
||||
console.log(`Skipping ${packageKey} because it changed`);
|
||||
@@ -213,7 +285,7 @@ async function getPackagesToTestAndPriorResults(container: Container, typeScript
|
||||
}
|
||||
|
||||
function createGetAllPackages(definitelyTypedPath: string) {
|
||||
let result: { allPackages: AllPackages, definitelyTypedFS: FS } | undefined;
|
||||
let result: { allPackages: AllPackages; definitelyTypedFS: FS } | undefined;
|
||||
return async () => {
|
||||
return result || (result = await getParsedPackages(definitelyTypedPath));
|
||||
};
|
||||
|
||||
@@ -1,12 +1,23 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { promisify } from 'util';
|
||||
import { getDatabase, getParsedPackages, DatabaseAccessLevel, compact, Args, assertNumber, assertString, getSystemInfo, getChangedPackages, packageIdsAreEqual, systemsAreCloseEnough } from '../common';
|
||||
import { nAtATime } from 'types-publisher/bin/util/util';
|
||||
import { getAffectedPackages } from 'types-publisher/bin/tester/get-affected-packages';
|
||||
import { PackageId } from 'types-publisher/bin/lib/packages';
|
||||
import { BenchmarkPackageOptions } from './benchmark';
|
||||
import { getLatestBenchmark } from '../query';
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import { promisify } from "util";
|
||||
import {
|
||||
getDatabase,
|
||||
getParsedPackages,
|
||||
DatabaseAccessLevel,
|
||||
compact,
|
||||
Args,
|
||||
assertNumber,
|
||||
assertString,
|
||||
getSystemInfo,
|
||||
getChangedPackages,
|
||||
packageIdsAreEqual,
|
||||
systemsAreCloseEnough
|
||||
} from "../common";
|
||||
import { BenchmarkPackageOptions } from "./benchmark";
|
||||
import { getLatestBenchmark } from "../query";
|
||||
import { nAtATime } from "@definitelytyped/utils";
|
||||
import { getAffectedPackages, PackageId, formatDependencyVersion } from "@definitelytyped/definitions-parser";
|
||||
const writeFile = promisify(fs.writeFile);
|
||||
const currentSystem = getSystemInfo();
|
||||
|
||||
@@ -18,27 +29,22 @@ export interface GetPackagesToBenchmarkOptions {
|
||||
}
|
||||
|
||||
function convertArgs(args: Args): GetPackagesToBenchmarkOptions {
|
||||
const tsVersion = (args.typeScriptVersion || '').toString();
|
||||
if (tsVersion.split('.').length !== 2) {
|
||||
const tsVersion = (args.typeScriptVersion || "").toString();
|
||||
if (tsVersion.split(".").length !== 2) {
|
||||
throw new Error(`Argument 'typeScriptVersion' must be in format 'major.minor' (e.g. '3.1')`);
|
||||
}
|
||||
const dtPath = assertString(args.definitelyTypedPath || process.cwd(), 'definitelyTypedPath');
|
||||
const dtPath = assertString(args.definitelyTypedPath || process.cwd(), "definitelyTypedPath");
|
||||
const definitelyTypedPath = path.isAbsolute(dtPath) ? dtPath : path.resolve(process.cwd(), dtPath);
|
||||
return {
|
||||
definitelyTypedPath,
|
||||
agentCount: assertNumber(args.agentCount, 'agentCount'),
|
||||
agentCount: assertNumber(args.agentCount, "agentCount"),
|
||||
typeScriptVersionMajorMinor: tsVersion,
|
||||
outFile: assertString(args.outFile, 'outFile'),
|
||||
outFile: assertString(args.outFile, "outFile")
|
||||
};
|
||||
}
|
||||
|
||||
export async function getPackagesToBenchmark(args: Args) {
|
||||
const {
|
||||
definitelyTypedPath,
|
||||
agentCount,
|
||||
typeScriptVersionMajorMinor,
|
||||
outFile,
|
||||
} = convertArgs(args);
|
||||
const { definitelyTypedPath, agentCount, typeScriptVersionMajorMinor, outFile } = convertArgs(args);
|
||||
const { allPackages } = await getParsedPackages(definitelyTypedPath);
|
||||
const { packageBenchmarks: container } = await getDatabase(DatabaseAccessLevel.Read);
|
||||
const changedPackages = await nAtATime(10, allPackages.allTypings(), async typingsData => {
|
||||
@@ -46,17 +52,19 @@ export async function getPackagesToBenchmark(args: Args) {
|
||||
container,
|
||||
typeScriptVersionMajorMinor,
|
||||
packageName: typingsData.id.name,
|
||||
packageVersion: typingsData.id.majorVersion,
|
||||
packageVersion: formatDependencyVersion(typingsData.id.version)
|
||||
});
|
||||
|
||||
// No previous run exists; run one
|
||||
if (!result) {
|
||||
return typingsData.id;
|
||||
}
|
||||
|
||||
|
||||
// System specs are different; run it
|
||||
if (!systemsAreCloseEnough(result.system, currentSystem)) {
|
||||
console.log(`Queueing ${typingsData.id.name}/${typingsData.id.majorVersion} due to system change`);
|
||||
console.log(
|
||||
`Queueing ${typingsData.id.name}/v${formatDependencyVersion(typingsData.id.version)} due to system change`
|
||||
);
|
||||
return typingsData.id;
|
||||
}
|
||||
|
||||
@@ -69,6 +77,8 @@ export async function getPackagesToBenchmark(args: Args) {
|
||||
// Package has changed; run it
|
||||
return typingsData.id;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
});
|
||||
|
||||
const affectedPackages = getAffectedPackages(allPackages, compact(changedPackages));
|
||||
@@ -86,15 +96,23 @@ export async function getPackagesToBenchmark(args: Args) {
|
||||
const benchmarkOptions: Partial<BenchmarkPackageOptions> = {
|
||||
definitelyTypedPath,
|
||||
tsVersion: typeScriptVersionMajorMinor,
|
||||
upload: true,
|
||||
}
|
||||
upload: true
|
||||
};
|
||||
|
||||
await writeFile(outFile, JSON.stringify({
|
||||
changedPackageCount: affectedPackages.changedPackages.length,
|
||||
dependentPackageCount: affectedPackages.dependentPackages.length,
|
||||
totalPackageCount: packagesToBenchmark.length,
|
||||
system: currentSystem,
|
||||
options: benchmarkOptions,
|
||||
groups,
|
||||
}, undefined, 2), 'utf8');
|
||||
await writeFile(
|
||||
outFile,
|
||||
JSON.stringify(
|
||||
{
|
||||
changedPackageCount: affectedPackages.changedPackages.length,
|
||||
dependentPackageCount: affectedPackages.dependentPackages.length,
|
||||
totalPackageCount: packagesToBenchmark.length,
|
||||
system: currentSystem,
|
||||
options: benchmarkOptions,
|
||||
groups
|
||||
},
|
||||
undefined,
|
||||
2
|
||||
),
|
||||
"utf8"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
import { deserializeArgs } from '../common';
|
||||
import { benchmark } from './benchmark';
|
||||
import { getPackagesToBenchmark } from './getPackagesToBenchmark';
|
||||
import { compare } from './compare';
|
||||
import { compareTypeScriptCLI } from './compareTypeScript';
|
||||
|
||||
import { deserializeArgs } from "../common";
|
||||
import { benchmark } from "./benchmark";
|
||||
import { getPackagesToBenchmark } from "./getPackagesToBenchmark";
|
||||
import { compare } from "./compare";
|
||||
import { compareTypeScriptCLI } from "./compareTypeScript";
|
||||
|
||||
const entry = process.argv[2];
|
||||
const args = deserializeArgs(process.argv.slice(3));
|
||||
(async () => {
|
||||
try {
|
||||
switch (entry) {
|
||||
case 'benchmark':
|
||||
case "benchmark":
|
||||
return benchmark(args);
|
||||
case 'getPackagesToBenchmark':
|
||||
case "getPackagesToBenchmark":
|
||||
return getPackagesToBenchmark(args);
|
||||
case 'compare':
|
||||
case "compare":
|
||||
return compare(args);
|
||||
case 'compareTypeScript':
|
||||
case "compareTypeScript":
|
||||
return compareTypeScriptCLI(args);
|
||||
default:
|
||||
console.error(`Unrecognized entry '${entry}'`);
|
||||
@@ -27,7 +26,7 @@ const args = deserializeArgs(process.argv.slice(3));
|
||||
}
|
||||
})();
|
||||
|
||||
process.on('unhandledRejection', err => {
|
||||
process.on("unhandledRejection", err => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"composite": true
|
||||
},
|
||||
"references": [
|
||||
{ "path": "../analysis" },
|
||||
{ "path": "../common" },
|
||||
{ "path": "../github" },
|
||||
{ "path": "../measure" },
|
||||
{ "path": "../query" },
|
||||
{ "path": "../write" }
|
||||
]
|
||||
}
|
||||
@@ -1,43 +1,46 @@
|
||||
import { assertDefined } from 'types-publisher/bin/util/util';
|
||||
import { assertDefined } from "@definitelytyped/utils";
|
||||
|
||||
export const config = {
|
||||
benchmarks: {
|
||||
languageServiceIterations: 5,
|
||||
languageServiceIterations: 5
|
||||
},
|
||||
database: {
|
||||
benchmarksDatabaseId: 'benchmarks',
|
||||
packageBenchmarksContainerId: 'packageBenchmarks',
|
||||
benchmarksDatabaseId: "benchmarks",
|
||||
packageBenchmarksContainerId: "packageBenchmarks",
|
||||
packageBenchmarksDocumentSchemaVersion: 4,
|
||||
typeScriptComparisonsContainerId: 'typeScriptComparisons',
|
||||
typeScriptComparisonsContainerId: "typeScriptComparisons",
|
||||
typeScriptComparisonsDocumentSchemaVersion: 1,
|
||||
endpoint: 'https://dt-perf.documents.azure.com:443/',
|
||||
endpoint: "https://dt-perf.documents.azure.com:443/",
|
||||
get writeKey() {
|
||||
return assertDefined(
|
||||
process.env.DATABASE_WRITE_KEY,
|
||||
`Required environment variable 'DATABASE_WRITE_KEY' was not set`);
|
||||
`Required environment variable 'DATABASE_WRITE_KEY' was not set`
|
||||
);
|
||||
},
|
||||
get readKey() {
|
||||
return assertDefined(
|
||||
process.env.DATABASE_READ_KEY,
|
||||
`Required environment variable 'DATABASE_READ_KEY' was not set`);
|
||||
`Required environment variable 'DATABASE_READ_KEY' was not set`
|
||||
);
|
||||
}
|
||||
},
|
||||
github: {
|
||||
userAgent: 'definitely-not-slow',
|
||||
typeScriptBotUsername: 'typescript-bot',
|
||||
userAgent: "definitely-not-slow",
|
||||
typeScriptBotUsername: "typescript-bot",
|
||||
get typeScriptBotAuthToken() {
|
||||
return assertDefined(
|
||||
process.env.TYPESCRIPT_BOT_GITHUB_TOKEN,
|
||||
`Required environment variable 'TYPESCRIPT_BOT_GITHUB_TOKEN' was not set`);
|
||||
`Required environment variable 'TYPESCRIPT_BOT_GITHUB_TOKEN' was not set`
|
||||
);
|
||||
},
|
||||
commonParams: {
|
||||
owner: 'DefinitelyTyped',
|
||||
repo: 'DefinitelyTyped',
|
||||
},
|
||||
owner: "DefinitelyTyped",
|
||||
repo: "DefinitelyTyped"
|
||||
}
|
||||
},
|
||||
comparison: {
|
||||
percentDiffWarningThreshold: 0.2,
|
||||
percentDiffAlertThreshold: 1,
|
||||
percentDiffAwesomeThreshold: -0.25,
|
||||
},
|
||||
percentDiffAwesomeThreshold: -0.25
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { CosmosClient, Database, Container } from '@azure/cosmos';
|
||||
import { config } from './config';
|
||||
import { assertNever } from 'types-publisher/bin/util/util';
|
||||
import { CosmosClient, Database, Container } from "@azure/cosmos";
|
||||
import { config } from "./config";
|
||||
import { assertNever } from "@definitelytyped/utils";
|
||||
|
||||
export const enum DatabaseAccessLevel {
|
||||
Read = 'read',
|
||||
Write = 'write',
|
||||
Read = "read",
|
||||
Write = "write"
|
||||
}
|
||||
|
||||
function getKey(accessLevel: DatabaseAccessLevel) {
|
||||
@@ -16,32 +16,34 @@ function getKey(accessLevel: DatabaseAccessLevel) {
|
||||
default:
|
||||
assertNever(accessLevel);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export async function getDatabase(accessLevel: DatabaseAccessLevel): Promise<{
|
||||
export async function getDatabase(
|
||||
accessLevel: DatabaseAccessLevel
|
||||
): Promise<{
|
||||
database: Database;
|
||||
packageBenchmarks: Container;
|
||||
typeScriptComparisons: Container;
|
||||
}> {
|
||||
const client = new CosmosClient({
|
||||
endpoint: config.database.endpoint,
|
||||
key: getKey(accessLevel),
|
||||
key: getKey(accessLevel)
|
||||
});
|
||||
|
||||
const { database } = await client.databases.createIfNotExists({
|
||||
id: config.database.benchmarksDatabaseId,
|
||||
id: config.database.benchmarksDatabaseId
|
||||
});
|
||||
|
||||
const { container: packageBenchmarks } = await database.containers.createIfNotExists({
|
||||
id: config.database.packageBenchmarksContainerId,
|
||||
partitionKey: {
|
||||
kind: 'Hash',
|
||||
paths: ['/body/packageName']
|
||||
kind: "Hash",
|
||||
paths: ["/body/packageName"]
|
||||
}
|
||||
});
|
||||
|
||||
const { container: typeScriptComparisons } = await database.containers.createIfNotExists({
|
||||
id: config.database.typeScriptComparisonsContainerId,
|
||||
id: config.database.typeScriptComparisonsContainerId
|
||||
});
|
||||
|
||||
return { database, packageBenchmarks, typeScriptComparisons };
|
||||
|
||||
@@ -1,27 +1,38 @@
|
||||
import * as os from 'os';
|
||||
import { AllPackages, typesDataFilename } from 'types-publisher/bin/lib/packages';
|
||||
import { FS, getLocallyInstalledDefinitelyTyped } from 'types-publisher/bin/get-definitely-typed';
|
||||
import { consoleLogger } from 'types-publisher/bin/util/logging';
|
||||
import { dataFilePath } from 'types-publisher/bin/lib/common';
|
||||
import parseDefinitions from 'types-publisher/bin/parse-definitions';
|
||||
import { getSourceVersion, pathExists } from './utils';
|
||||
import * as os from "os";
|
||||
import {
|
||||
AllPackages,
|
||||
typesDataFilename,
|
||||
getLocallyInstalledDefinitelyTyped,
|
||||
dataFilePath,
|
||||
parseDefinitions
|
||||
} from "@definitelytyped/definitions-parser";
|
||||
import { FS, consoleLogger } from "@definitelytyped/utils";
|
||||
import { getSourceVersion, pathExists } from "./utils";
|
||||
|
||||
let parsedSourceVersion: string | undefined;
|
||||
export async function getParsedPackages(definitelyTypedPath: string): Promise<{
|
||||
export async function getParsedPackages(
|
||||
definitelyTypedPath: string
|
||||
): Promise<{
|
||||
definitelyTypedFS: FS;
|
||||
allPackages: AllPackages;
|
||||
}> {
|
||||
const currentSourceVersion = await getSourceVersion(definitelyTypedPath);
|
||||
const definitelyTypedFS = getLocallyInstalledDefinitelyTyped(definitelyTypedPath);
|
||||
const isDebugging = process.execArgv.some(arg => arg.startsWith('--inspect'));
|
||||
const isDebugging = process.execArgv.some(arg => arg.startsWith("--inspect"));
|
||||
const needsReparse = !parsedSourceVersion || parsedSourceVersion !== currentSourceVersion;
|
||||
if (process.env.NODE_ENV === 'production' || needsReparse || !(await pathExists(dataFilePath(typesDataFilename)))) {
|
||||
await parseDefinitions(definitelyTypedFS, isDebugging ? undefined : {
|
||||
definitelyTypedPath,
|
||||
nProcesses: os.cpus().length,
|
||||
}, consoleLogger);
|
||||
if (process.env.NODE_ENV === "production" || needsReparse || !(await pathExists(dataFilePath(typesDataFilename)))) {
|
||||
await parseDefinitions(
|
||||
definitelyTypedFS,
|
||||
isDebugging
|
||||
? undefined
|
||||
: {
|
||||
definitelyTypedPath,
|
||||
nProcesses: os.cpus().length
|
||||
},
|
||||
consoleLogger
|
||||
);
|
||||
parsedSourceVersion = currentSourceVersion;
|
||||
}
|
||||
const allPackages = await AllPackages.read(definitelyTypedFS);
|
||||
return { definitelyTypedFS, allPackages };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export * from './config';
|
||||
export * from './db';
|
||||
export * from './getParsedPackages';
|
||||
export * from './types';
|
||||
export * from './utils';
|
||||
export * from './support';
|
||||
export * from "./config";
|
||||
export * from "./db";
|
||||
export * from "./getParsedPackages";
|
||||
export * from "./types";
|
||||
export * from "./utils";
|
||||
export * from "./support";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Document, PackageBenchmarkSummary } from './types';
|
||||
import { Document, PackageBenchmarkSummary } from "./types";
|
||||
|
||||
export function supportsMemoryUsage(doc: Document<PackageBenchmarkSummary>) {
|
||||
return doc.version >= 4;
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CpuInfo } from 'os';
|
||||
import { CpuInfo } from "os";
|
||||
|
||||
export type RelationCacheSizes = {
|
||||
assignable: number;
|
||||
@@ -77,7 +77,7 @@ export interface LanguageServiceSingleMeasurement {
|
||||
export type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
|
||||
|
||||
export interface SystemInfo {
|
||||
cpus: Omit<CpuInfo, 'times'>[];
|
||||
cpus: Omit<CpuInfo, "times">[];
|
||||
arch: string;
|
||||
platform: string;
|
||||
release: string;
|
||||
@@ -94,14 +94,19 @@ export interface Document<T> {
|
||||
}
|
||||
|
||||
type Serialized<T extends {}> = {
|
||||
[K in keyof T]:
|
||||
T[K] extends string ? string :
|
||||
T[K] extends number ? number :
|
||||
T[K] extends boolean ? boolean :
|
||||
T[K] extends Date ? string :
|
||||
T[K] extends ReadonlyArray<infer U> ? Serialized<U>[] :
|
||||
T[K] extends {} ? Serialized<T[K]> :
|
||||
never;
|
||||
[K in keyof T]: T[K] extends string
|
||||
? string
|
||||
: T[K] extends number
|
||||
? number
|
||||
: T[K] extends boolean
|
||||
? boolean
|
||||
: T[K] extends Date
|
||||
? string
|
||||
: T[K] extends ReadonlyArray<infer U>
|
||||
? Serialized<U>[]
|
||||
: T[K] extends {}
|
||||
? Serialized<T[K]>
|
||||
: never;
|
||||
};
|
||||
|
||||
export interface JSONDocument<T extends {}> {
|
||||
@@ -111,4 +116,4 @@ export interface JSONDocument<T extends {}> {
|
||||
body: Serialized<T>;
|
||||
}
|
||||
|
||||
export type QueryResult<T extends {}> = T & { id: string };
|
||||
export type QueryResult<T extends {}> = T & { id: string };
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import * as os from 'os';
|
||||
import * as fs from 'fs';
|
||||
import { randomBytes } from 'crypto';
|
||||
import { createHash } from 'crypto';
|
||||
import { promisify } from 'util';
|
||||
import { SystemInfo, Document, JSONDocument, PackageBenchmarkSummary, QueryResult } from './types';
|
||||
import { PackageId } from 'types-publisher/bin/lib/packages';
|
||||
import { execAndThrowErrors } from 'types-publisher/bin/util/util';
|
||||
import { gitChanges } from 'types-publisher/bin/tester/test-runner';
|
||||
import { execSync } from 'child_process';
|
||||
import * as os from "os";
|
||||
import * as fs from "fs";
|
||||
import { randomBytes, createHash } from "crypto";
|
||||
import { promisify } from "util";
|
||||
import { execSync } from "child_process";
|
||||
import { SystemInfo, Document, JSONDocument, PackageBenchmarkSummary, QueryResult } from "./types";
|
||||
import { execAndThrowErrors } from "@definitelytyped/utils";
|
||||
import {
|
||||
PackageId,
|
||||
gitChanges,
|
||||
formatDependencyVersion,
|
||||
parseVersionFromDirectoryName,
|
||||
TypingVersion
|
||||
} from "@definitelytyped/definitions-parser";
|
||||
|
||||
export const pathExists = promisify(fs.exists);
|
||||
|
||||
@@ -17,24 +21,26 @@ export function ensureExists(...pathNames: string[]): string {
|
||||
return pathName;
|
||||
}
|
||||
}
|
||||
const pathNamesPrint = pathNames.length > 1 ? '\n' + pathNames.map(s => ` - ${s}`).join('\n') : `'${pathNames[0]}`;
|
||||
const pathNamesPrint = pathNames.length > 1 ? "\n" + pathNames.map(s => ` - ${s}`).join("\n") : `'${pathNames[0]}`;
|
||||
throw new Error(`File or directory does not exist: ${pathNamesPrint}`);
|
||||
}
|
||||
|
||||
export type Args = { [key: string]: string | boolean | number };
|
||||
export interface Args {
|
||||
[key: string]: string | boolean | number;
|
||||
}
|
||||
|
||||
export function deserializeArgs(args: string[]): Args {
|
||||
const obj: Args = {};
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const arg = args[i];
|
||||
if (arg.startsWith('--')) {
|
||||
if (arg.startsWith("--")) {
|
||||
const nextArg = args[i + 1];
|
||||
if (!nextArg || nextArg.startsWith('--')) {
|
||||
if (!nextArg || nextArg.startsWith("--")) {
|
||||
obj[arg.slice(2)] = true;
|
||||
} else {
|
||||
const numberArg = parseFloat(nextArg);
|
||||
const boolArg = nextArg === 'true' ? true : nextArg === 'false' ? false : undefined;
|
||||
obj[arg.slice(2)] = typeof boolArg === 'boolean' ? boolArg : (isNaN(numberArg) ? nextArg : numberArg);
|
||||
const boolArg = nextArg === "true" ? true : nextArg === "false" ? false : undefined;
|
||||
obj[arg.slice(2)] = typeof boolArg === "boolean" ? boolArg : isNaN(numberArg) ? nextArg : numberArg;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
@@ -43,43 +49,38 @@ export function deserializeArgs(args: string[]): Args {
|
||||
}
|
||||
|
||||
export function serializeArgs(args: Args): string {
|
||||
return Object.keys(args).map(arg => `--${arg}` + (args[arg] === true ? '' : args[arg].toString())).join(' ');
|
||||
return Object.keys(args)
|
||||
.map(arg => `--${arg}` + (args[arg] === true ? "" : args[arg].toString()))
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
export function compact<T>(arr: (T | null | undefined)[]): T[] {
|
||||
return arr.filter((elem): elem is T => elem != undefined);
|
||||
return arr.filter((elem): elem is T => elem !== null && elem !== undefined);
|
||||
}
|
||||
|
||||
export function assertString(input: any, name?: string): string {
|
||||
if (typeof input !== 'string') {
|
||||
throw new Error(`Expected a string for input${name ? ` '${name}'` : ''} but received ${typeof input}`);
|
||||
if (typeof input !== "string") {
|
||||
throw new Error(`Expected a string for input${name ? ` '${name}'` : ""} but received ${typeof input}`);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
export function assertNumber(input: any, name?: string): number {
|
||||
if (typeof input !== 'number') {
|
||||
throw new Error(`Expected a number for input${name ? ` '${name}'` : ''} but received ${typeof input}`);
|
||||
if (typeof input !== "number") {
|
||||
throw new Error(`Expected a number for input${name ? ` '${name}'` : ""} but received ${typeof input}`);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
export function assertBoolean(input: any, name?: string): boolean {
|
||||
if (typeof input !== 'boolean') {
|
||||
throw new Error(`Expected a boolean for input${name ? ` '${name}'` : ''} but received ${typeof input}`);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
export function assertDefined<T>(input: T | null | undefined, name?: string): T {
|
||||
if (input == undefined) {
|
||||
throw new Error(`Expected ${name ? ` '${name}'` : ''} to be defined`);
|
||||
if (typeof input !== "boolean") {
|
||||
throw new Error(`Expected a boolean for input${name ? ` '${name}'` : ""} but received ${typeof input}`);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
export function withDefault<T>(input: T, defaultValue: T): T {
|
||||
return typeof input === 'undefined' ? defaultValue : input;
|
||||
return typeof input === "undefined" ? defaultValue : input;
|
||||
}
|
||||
|
||||
export function getSystemInfo(): SystemInfo {
|
||||
@@ -89,12 +90,14 @@ export function getSystemInfo(): SystemInfo {
|
||||
platform: os.platform(),
|
||||
release: os.release(),
|
||||
totalmem: os.totalmem(),
|
||||
nodeVersion: process.version,
|
||||
nodeVersion: process.version
|
||||
};
|
||||
|
||||
return {
|
||||
...info,
|
||||
hash: createHash('md5').update(JSON.stringify(info)).digest('hex'),
|
||||
hash: createHash("md5")
|
||||
.update(JSON.stringify(info))
|
||||
.digest("hex")
|
||||
};
|
||||
}
|
||||
|
||||
@@ -105,7 +108,7 @@ export interface GetChangedPackagesOptions {
|
||||
}
|
||||
|
||||
export async function getChangedPackages({
|
||||
diffFrom = 'HEAD',
|
||||
diffFrom = "HEAD",
|
||||
diffTo,
|
||||
definitelyTypedPath
|
||||
}: GetChangedPackagesOptions) {
|
||||
@@ -114,9 +117,9 @@ export async function getChangedPackages({
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const changes = diff.split('\n').map(line => {
|
||||
const changes = diff.split("\n").map(line => {
|
||||
const [status, file] = line.split(/\s+/, 2);
|
||||
return { status: status.trim() as 'A' | 'D' | 'M', file: file.trim() };
|
||||
return { status: status.trim() as "A" | "D" | "M", file: file.trim() };
|
||||
});
|
||||
|
||||
return gitChanges(changes);
|
||||
@@ -125,9 +128,9 @@ export async function getChangedPackages({
|
||||
export function packageIdsAreEqual(a: PackageId): (b: PackageId) => boolean;
|
||||
export function packageIdsAreEqual(a: PackageId, b: PackageId): boolean;
|
||||
export function packageIdsAreEqual(a: PackageId, b?: PackageId): boolean | ((b: PackageId) => boolean) {
|
||||
return typeof b === 'undefined' ? equalsA : equalsA(b);
|
||||
return typeof b === "undefined" ? equalsA : equalsA(b);
|
||||
function equalsA(b: PackageId) {
|
||||
return a.name === b.name && a.majorVersion === b.majorVersion;
|
||||
return a.name === b.name && a.version === b.version;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,15 +146,16 @@ export function systemsAreCloseEnough(a: SystemInfo, b: SystemInfo, cpuSpeedTole
|
||||
if (a.hash === b.hash) {
|
||||
return true;
|
||||
}
|
||||
return a.arch === b.arch
|
||||
&& a.platform === b.platform
|
||||
&& a.nodeVersion === b.nodeVersion
|
||||
&& a.cpus.length === b.cpus.length
|
||||
&& a.cpus.every((cpu, index) => {
|
||||
return (
|
||||
a.arch === b.arch &&
|
||||
a.platform === b.platform &&
|
||||
a.nodeVersion === b.nodeVersion &&
|
||||
a.cpus.length === b.cpus.length &&
|
||||
a.cpus.every((cpu, index) => {
|
||||
const otherCPU = b.cpus[index];
|
||||
return cpu.model === otherCPU.model
|
||||
&& isWithin(cpu.speed, otherCPU.speed, cpuSpeedTolerance);
|
||||
});
|
||||
return cpu.model === otherCPU.model && isWithin(cpu.speed, otherCPU.speed, cpuSpeedTolerance);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export function createDocument<T>(body: T, version: number): Document<T> {
|
||||
@@ -159,50 +163,59 @@ export function createDocument<T>(body: T, version: number): Document<T> {
|
||||
version,
|
||||
createdAt: new Date(),
|
||||
system: getSystemInfo(),
|
||||
body,
|
||||
body
|
||||
};
|
||||
}
|
||||
|
||||
export function parsePackageKey(key: string): PackageId {
|
||||
const [name, version] = key.split('/');
|
||||
const [name, versionString] = key.split("/");
|
||||
const version = parseVersionFromDirectoryName(versionString);
|
||||
return {
|
||||
name,
|
||||
majorVersion: parseInt(version.replace(/^v/, '')) || '*' as const,
|
||||
version: version || ("*" as const)
|
||||
};
|
||||
}
|
||||
|
||||
export function toPackageKey(name: string, majorVersion: string): string;
|
||||
export function toPackageKey(name: string, version: string | TypingVersion): string;
|
||||
export function toPackageKey(packageId: PackageId): string;
|
||||
export function toPackageKey(packageIdOrName: string | PackageId, majorVersion?: string) {
|
||||
const { name, majorVersion: version } = typeof packageIdOrName === 'string' ? { name: packageIdOrName, majorVersion } : packageIdOrName;
|
||||
return `${name}/v${version}`;
|
||||
export function toPackageKey(packageIdOrName: string | PackageId, version?: string | TypingVersion) {
|
||||
const packageId =
|
||||
typeof packageIdOrName === "string"
|
||||
? {
|
||||
name: packageIdOrName,
|
||||
version: (typeof version === "string" ? parseVersionFromDirectoryName(version) : version) || ("*" as const)
|
||||
}
|
||||
: packageIdOrName;
|
||||
return `${packageId.name}/${formatDependencyVersion(packageId.version)}`;
|
||||
}
|
||||
|
||||
export function deserializeSummary(doc: QueryResult<JSONDocument<PackageBenchmarkSummary>>): QueryResult<Document<PackageBenchmarkSummary>>;
|
||||
export function deserializeSummary(
|
||||
doc: QueryResult<JSONDocument<PackageBenchmarkSummary>>
|
||||
): QueryResult<Document<PackageBenchmarkSummary>>;
|
||||
export function deserializeSummary(doc: JSONDocument<PackageBenchmarkSummary>): Document<PackageBenchmarkSummary> {
|
||||
return {
|
||||
...doc,
|
||||
createdAt: new Date(doc.createdAt),
|
||||
body: {
|
||||
...doc.body,
|
||||
batchRunStart: new Date(doc.body.batchRunStart),
|
||||
},
|
||||
batchRunStart: new Date(doc.body.batchRunStart)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function getSourceVersion(cwd: string) {
|
||||
return execSync('git rev-parse HEAD', { cwd, encoding: 'utf8' }).trim()
|
||||
return execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim();
|
||||
}
|
||||
|
||||
export function shuffle<T>(array: readonly T[]): T[] {
|
||||
const output = array.slice();
|
||||
let counter = output.length;
|
||||
while (counter > 0) {
|
||||
const index = Math.floor(randomBytes(1).readUInt8(0) / 2 ** 8 * counter);
|
||||
counter--;
|
||||
const elem = output[counter];
|
||||
output[counter] = output[index];
|
||||
output[index] = elem;
|
||||
const index = Math.floor((randomBytes(1).readUInt8(0) / 2 ** 8) * counter);
|
||||
counter--;
|
||||
const elem = output[counter];
|
||||
output[counter] = output[index];
|
||||
output[index] = elem;
|
||||
}
|
||||
|
||||
return output;
|
||||
@@ -219,4 +232,5 @@ export function findLast<T>(arr: T[], predicate: (element: T) => boolean): T | u
|
||||
return element;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { IssuesListCommentsResponseItem } from '@octokit/rest';
|
||||
import { config } from '../common';
|
||||
import { OverallChange } from '../analysis';
|
||||
import { IssuesListCommentsResponseItem } from "@octokit/rest";
|
||||
import { config } from "../common";
|
||||
import { OverallChange } from "../analysis";
|
||||
|
||||
const commentTagStart = '<!-- @dt-perf ';
|
||||
const commentTagEnd = ' -->';
|
||||
const commentTagStart = "<!-- @dt-perf ";
|
||||
const commentTagEnd = " -->";
|
||||
|
||||
function assertCommentSafe(str: string) {
|
||||
if (str.includes('-->')) {
|
||||
throw new Error('Data is unsafe to serialize in HTML comment');
|
||||
if (str.includes("-->")) {
|
||||
throw new Error("Data is unsafe to serialize in HTML comment");
|
||||
}
|
||||
return str;
|
||||
}
|
||||
@@ -20,19 +20,18 @@ export interface CommentData {
|
||||
}
|
||||
|
||||
export function createPerfCommentBody(data: CommentData, body: string): string {
|
||||
return [
|
||||
commentTagStart + assertCommentSafe(JSON.stringify({ version: 2, data })) + commentTagEnd,
|
||||
'',
|
||||
body,
|
||||
].join('\n');
|
||||
return [commentTagStart + assertCommentSafe(JSON.stringify({ version: 2, data })) + commentTagEnd, "", body].join(
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
|
||||
export function isPerfComment({ body, user }: Pick<IssuesListCommentsResponseItem, 'body' | 'user'>): boolean {
|
||||
return user.login === config.github.typeScriptBotUsername
|
||||
&& body.trimLeft().startsWith(commentTagStart);
|
||||
export function isPerfComment({ body, user }: Pick<IssuesListCommentsResponseItem, "body" | "user">): boolean {
|
||||
return user.login === config.github.typeScriptBotUsername && body.trimLeft().startsWith(commentTagStart);
|
||||
}
|
||||
|
||||
export function getCommentData(comment: Pick<IssuesListCommentsResponseItem, 'body' | 'user'>): CommentData | undefined {
|
||||
export function getCommentData(
|
||||
comment: Pick<IssuesListCommentsResponseItem, "body" | "user">
|
||||
): CommentData | undefined {
|
||||
if (isPerfComment(comment)) {
|
||||
try {
|
||||
const trimmed = comment.body.trimLeft();
|
||||
@@ -44,4 +43,5 @@ export function getCommentData(comment: Pick<IssuesListCommentsResponseItem, 'bo
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,114 +1,181 @@
|
||||
import table from 'markdown-table';
|
||||
import { PackageBenchmarkSummary, Document, config, getPercentDiff, compact, supportsMemoryUsage } from '../common';
|
||||
import { metrics, Metric, FormatOptions, SignificanceLevel } from '../analysis';
|
||||
import { assertNever } from 'types-publisher/bin/util/util';
|
||||
import table from "markdown-table";
|
||||
import { PackageBenchmarkSummary, Document, config, getPercentDiff, compact, supportsMemoryUsage } from "../common";
|
||||
import { metrics, Metric, FormatOptions, SignificanceLevel } from "../analysis";
|
||||
import { assertNever } from "@definitelytyped/utils";
|
||||
|
||||
export function createComparisonTable(before: Document<PackageBenchmarkSummary>, after: Document<PackageBenchmarkSummary>, beforeTitle: string, afterTitle: string) {
|
||||
return table(compact([
|
||||
['', beforeTitle, afterTitle, 'diff'],
|
||||
['**Batch compilation**'],
|
||||
supportsMemoryUsage(before) && supportsMemoryUsage(after)
|
||||
? createComparisonRowFromMetric(metrics.memoryUsage, before, after)
|
||||
: undefined,
|
||||
createComparisonRowFromMetric(metrics.typeCount, before, after),
|
||||
createComparisonRowFromMetric(metrics.assignabilityCacheSize, before, after),
|
||||
[],
|
||||
['**Language service**'],
|
||||
createComparisonRowFromMetric(metrics.samplesTaken, before, after),
|
||||
createComparisonRowFromMetric(metrics.identifierCount, before, after),
|
||||
['**`getCompletionsAtPosition`**'],
|
||||
createComparisonRowFromMetric(metrics.completionsMean, before, after, { indent: 1 }),
|
||||
createComparisonRowFromMetric(metrics.completionsAvgCV, before, after, { indent: 1 }),
|
||||
createComparisonRowFromMetric(metrics.completionsWorstMean, before, after, { indent: 1 }),
|
||||
createComparisonRow('Worst identifier', before, after, x => sourceLink(
|
||||
x.body.completions.worst.identifierText,
|
||||
x.body.sourceVersion,
|
||||
x.body.completions.worst.fileName,
|
||||
x.body.completions.worst.line), undefined, { indent: 1 }),
|
||||
['**`getQuickInfoAtPosition`**'],
|
||||
createComparisonRowFromMetric(metrics.quickInfoMean, before, after, { indent: 1 }),
|
||||
createComparisonRowFromMetric(metrics.quickInfoAvgCV, before, after, { indent: 1 }),
|
||||
createComparisonRowFromMetric(metrics.quickInfoWorstMean, before, after, { indent: 1 }),
|
||||
createComparisonRow('Worst identifier', before, after, x => sourceLink(
|
||||
x.body.quickInfo.worst.identifierText,
|
||||
x.body.sourceVersion,
|
||||
x.body.quickInfo.worst.fileName,
|
||||
x.body.quickInfo.worst.line), undefined, { indent: 1 }),
|
||||
|
||||
// Only show system info if they’re not identical
|
||||
...(before.system.hash === after.system.hash ? [] : [
|
||||
export function createComparisonTable(
|
||||
before: Document<PackageBenchmarkSummary>,
|
||||
after: Document<PackageBenchmarkSummary>,
|
||||
beforeTitle: string,
|
||||
afterTitle: string
|
||||
) {
|
||||
return table(
|
||||
compact([
|
||||
["", beforeTitle, afterTitle, "diff"],
|
||||
["**Batch compilation**"],
|
||||
supportsMemoryUsage(before) && supportsMemoryUsage(after)
|
||||
? createComparisonRowFromMetric(metrics.memoryUsage, before, after)
|
||||
: undefined,
|
||||
createComparisonRowFromMetric(metrics.typeCount, before, after),
|
||||
createComparisonRowFromMetric(metrics.assignabilityCacheSize, before, after),
|
||||
[],
|
||||
['**System information**'],
|
||||
createComparisonRow('Node version', before, after, x => x.system.nodeVersion),
|
||||
createComparisonRow('CPU count', before, after, x => x.system.cpus.length, undefined, { precision: 0 }),
|
||||
createComparisonRow('CPU speed', before, after, x => `${x.system.cpus[0].speed / 1000} GHz`),
|
||||
createComparisonRow('CPU model', before, after, x => x.system.cpus[0].model),
|
||||
createComparisonRow('CPU Architecture', before, after, x => x.system.arch),
|
||||
createComparisonRow('Memory', before, after, x => `${format(x.system.totalmem / 2 ** 30)} GiB`),
|
||||
createComparisonRow('Platform', before, after, x => x.system.platform),
|
||||
createComparisonRow('Release', before, after, x => x.system.release),
|
||||
]),
|
||||
]));
|
||||
["**Language service**"],
|
||||
createComparisonRowFromMetric(metrics.samplesTaken, before, after),
|
||||
createComparisonRowFromMetric(metrics.identifierCount, before, after),
|
||||
["**`getCompletionsAtPosition`**"],
|
||||
createComparisonRowFromMetric(metrics.completionsMean, before, after, { indent: 1 }),
|
||||
createComparisonRowFromMetric(metrics.completionsAvgCV, before, after, { indent: 1 }),
|
||||
createComparisonRowFromMetric(metrics.completionsWorstMean, before, after, { indent: 1 }),
|
||||
createComparisonRow(
|
||||
"Worst identifier",
|
||||
before,
|
||||
after,
|
||||
x =>
|
||||
sourceLink(
|
||||
x.body.completions.worst.identifierText,
|
||||
x.body.sourceVersion,
|
||||
x.body.completions.worst.fileName,
|
||||
x.body.completions.worst.line
|
||||
),
|
||||
undefined,
|
||||
{ indent: 1 }
|
||||
),
|
||||
["**`getQuickInfoAtPosition`**"],
|
||||
createComparisonRowFromMetric(metrics.quickInfoMean, before, after, { indent: 1 }),
|
||||
createComparisonRowFromMetric(metrics.quickInfoAvgCV, before, after, { indent: 1 }),
|
||||
createComparisonRowFromMetric(metrics.quickInfoWorstMean, before, after, { indent: 1 }),
|
||||
createComparisonRow(
|
||||
"Worst identifier",
|
||||
before,
|
||||
after,
|
||||
x =>
|
||||
sourceLink(
|
||||
x.body.quickInfo.worst.identifierText,
|
||||
x.body.sourceVersion,
|
||||
x.body.quickInfo.worst.fileName,
|
||||
x.body.quickInfo.worst.line
|
||||
),
|
||||
undefined,
|
||||
{ indent: 1 }
|
||||
),
|
||||
|
||||
// Only show system info if they’re not identical
|
||||
...(before.system.hash === after.system.hash
|
||||
? []
|
||||
: [
|
||||
[],
|
||||
["**System information**"],
|
||||
createComparisonRow("Node version", before, after, x => x.system.nodeVersion),
|
||||
createComparisonRow("CPU count", before, after, x => x.system.cpus.length, undefined, { precision: 0 }),
|
||||
createComparisonRow("CPU speed", before, after, x => `${x.system.cpus[0].speed / 1000} GHz`),
|
||||
createComparisonRow("CPU model", before, after, x => x.system.cpus[0].model),
|
||||
createComparisonRow("CPU Architecture", before, after, x => x.system.arch),
|
||||
createComparisonRow("Memory", before, after, x => `${format(x.system.totalmem / 2 ** 30)} GiB`),
|
||||
createComparisonRow("Platform", before, after, x => x.system.platform),
|
||||
createComparisonRow("Release", before, after, x => x.system.release)
|
||||
])
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
export function createSingleRunTable(benchmark: Document<PackageBenchmarkSummary>) {
|
||||
return table([
|
||||
['**Batch compilation**'],
|
||||
["**Batch compilation**"],
|
||||
// createSingleRunRowFromMetric(metrics.memoryUsage, benchmark),
|
||||
createSingleRunRowFromMetric(metrics.typeCount, benchmark),
|
||||
createSingleRunRowFromMetric(metrics.assignabilityCacheSize, benchmark),
|
||||
[],
|
||||
['**Language service measurements**'],
|
||||
["**Language service measurements**"],
|
||||
createSingleRunRowFromMetric(metrics.samplesTaken, benchmark),
|
||||
createSingleRunRowFromMetric(metrics.identifierCount, benchmark),
|
||||
['**`getCompletionsAtPosition`**'],
|
||||
["**`getCompletionsAtPosition`**"],
|
||||
createSingleRunRowFromMetric(metrics.completionsMean, benchmark, { indent: 1 }),
|
||||
createSingleRunRowFromMetric(metrics.completionsAvgCV, benchmark, { indent: 1 }),
|
||||
createSingleRunRowFromMetric(metrics.completionsWorstMean, benchmark, { indent: 1 }),
|
||||
createSingleRunRow('Worst identifier', benchmark, x => sourceLink(
|
||||
x.body.completions.worst.identifierText,
|
||||
x.body.sourceVersion,
|
||||
x.body.completions.worst.fileName,
|
||||
x.body.completions.worst.line), { indent: 1 }),
|
||||
['**`getQuickInfoAtPosition`**'],
|
||||
createSingleRunRow(
|
||||
"Worst identifier",
|
||||
benchmark,
|
||||
x =>
|
||||
sourceLink(
|
||||
x.body.completions.worst.identifierText,
|
||||
x.body.sourceVersion,
|
||||
x.body.completions.worst.fileName,
|
||||
x.body.completions.worst.line
|
||||
),
|
||||
{ indent: 1 }
|
||||
),
|
||||
["**`getQuickInfoAtPosition`**"],
|
||||
createSingleRunRowFromMetric(metrics.quickInfoMean, benchmark, { indent: 1 }),
|
||||
createSingleRunRowFromMetric(metrics.quickInfoAvgCV, benchmark, { indent: 1 }),
|
||||
createSingleRunRowFromMetric(metrics.quickInfoWorstMean, benchmark, { indent: 1 }),
|
||||
createSingleRunRow('Worst identifier', benchmark, x => sourceLink(
|
||||
x.body.quickInfo.worst.identifierText,
|
||||
x.body.sourceVersion,
|
||||
x.body.quickInfo.worst.fileName,
|
||||
x.body.quickInfo.worst.line), { indent: 1 }),
|
||||
createSingleRunRow(
|
||||
"Worst identifier",
|
||||
benchmark,
|
||||
x =>
|
||||
sourceLink(
|
||||
x.body.quickInfo.worst.identifierText,
|
||||
x.body.sourceVersion,
|
||||
x.body.quickInfo.worst.fileName,
|
||||
x.body.quickInfo.worst.line
|
||||
),
|
||||
{ indent: 1 }
|
||||
),
|
||||
[],
|
||||
['**System information**'],
|
||||
createSingleRunRow('Node version', benchmark, x => x.system.nodeVersion),
|
||||
createSingleRunRow('CPU count', benchmark, x => x.system.cpus.length, { precision: 0 }),
|
||||
createSingleRunRow('CPU speed', benchmark, x => `${x.system.cpus[0].speed / 1000} GHz`),
|
||||
createSingleRunRow('CPU model', benchmark, x => x.system.cpus[0].model),
|
||||
createSingleRunRow('CPU Architecture', benchmark, x => x.system.arch),
|
||||
createSingleRunRow('Memory', benchmark, x => `${format(x.system.totalmem / 2 ** 30)} GiB`),
|
||||
createSingleRunRow('Platform', benchmark, x => x.system.platform),
|
||||
createSingleRunRow('Release', benchmark, x => x.system.release),
|
||||
["**System information**"],
|
||||
createSingleRunRow("Node version", benchmark, x => x.system.nodeVersion),
|
||||
createSingleRunRow("CPU count", benchmark, x => x.system.cpus.length, { precision: 0 }),
|
||||
createSingleRunRow("CPU speed", benchmark, x => `${x.system.cpus[0].speed / 1000} GHz`),
|
||||
createSingleRunRow("CPU model", benchmark, x => x.system.cpus[0].model),
|
||||
createSingleRunRow("CPU Architecture", benchmark, x => x.system.arch),
|
||||
createSingleRunRow("Memory", benchmark, x => `${format(x.system.totalmem / 2 ** 30)} GiB`),
|
||||
createSingleRunRow("Platform", benchmark, x => x.system.platform),
|
||||
createSingleRunRow("Release", benchmark, x => x.system.release)
|
||||
]);
|
||||
}
|
||||
|
||||
function sourceLink(text: string, sourceVersion: string, fileName: string, line: number) {
|
||||
return `[${text}](/${config.github.commonParams.owner}/${config.github.commonParams.repo}/blob/${sourceVersion.replace('\n', '')}/${fileName}#L${line})`;
|
||||
return `[${text}](/${config.github.commonParams.owner}/${
|
||||
config.github.commonParams.repo
|
||||
}/blob/${sourceVersion.replace("\n", "")}/${fileName}#L${line})`;
|
||||
}
|
||||
|
||||
function createComparisonRowFromMetric(metric: Metric, before: Document<PackageBenchmarkSummary>, after: Document<PackageBenchmarkSummary>, formatOptions: FormatOptions = {}) {
|
||||
function createComparisonRowFromMetric(
|
||||
metric: Metric,
|
||||
before: Document<PackageBenchmarkSummary>,
|
||||
after: Document<PackageBenchmarkSummary>,
|
||||
formatOptions: FormatOptions = {}
|
||||
) {
|
||||
const beforeValue = metric.getValue(before);
|
||||
const afterValue = metric.getValue(after);
|
||||
const format = { ...metric.formatOptions, ...formatOptions };
|
||||
const percentDiff = !format.noDiff && typeof beforeValue === 'number' && typeof afterValue === 'number' && !isNaN(afterValue) && !isNaN(afterValue)
|
||||
? getPercentDiff(afterValue, beforeValue)
|
||||
: undefined;
|
||||
const diffString = typeof percentDiff === 'number' ? formatDiff(percentDiff, metric.getSignificance(percentDiff, beforeValue!, afterValue!, before, after), format.precision) : undefined;
|
||||
const percentDiff =
|
||||
!format.noDiff &&
|
||||
typeof beforeValue === "number" &&
|
||||
typeof afterValue === "number" &&
|
||||
!isNaN(afterValue) &&
|
||||
!isNaN(afterValue)
|
||||
? getPercentDiff(afterValue, beforeValue)
|
||||
: undefined;
|
||||
const diffString =
|
||||
typeof percentDiff === "number"
|
||||
? formatDiff(
|
||||
percentDiff,
|
||||
metric.getSignificance(percentDiff, beforeValue!, afterValue!, before, after),
|
||||
format.precision
|
||||
)
|
||||
: undefined;
|
||||
return createComparisonRow(metric.columnName, before, after, metric.getValue, diffString, format);
|
||||
}
|
||||
|
||||
function createSingleRunRowFromMetric(metric: Metric, benchmark: Document<PackageBenchmarkSummary>, formatOptions?: FormatOptions) {
|
||||
return createSingleRunRow(metric.columnName, benchmark, metric.getValue, { ...metric.formatOptions, ...formatOptions });
|
||||
function createSingleRunRowFromMetric(
|
||||
metric: Metric,
|
||||
benchmark: Document<PackageBenchmarkSummary>,
|
||||
formatOptions?: FormatOptions
|
||||
) {
|
||||
return createSingleRunRow(metric.columnName, benchmark, metric.getValue, {
|
||||
...metric.formatOptions,
|
||||
...formatOptions
|
||||
});
|
||||
}
|
||||
|
||||
function createComparisonRow(
|
||||
@@ -117,16 +184,16 @@ function createComparisonRow(
|
||||
b: Document<PackageBenchmarkSummary>,
|
||||
getValue: (x: Document<PackageBenchmarkSummary>) => number | string | undefined,
|
||||
diff?: string,
|
||||
formatOptions: FormatOptions = {},
|
||||
formatOptions: FormatOptions = {}
|
||||
): string[] {
|
||||
const aValue = getValue(a);
|
||||
const bValue = getValue(b);
|
||||
|
||||
|
||||
return [
|
||||
indent(title, formatOptions.indent || 0),
|
||||
format(aValue, formatOptions),
|
||||
format(bValue, formatOptions),
|
||||
diff || '',
|
||||
diff || ""
|
||||
];
|
||||
}
|
||||
|
||||
@@ -138,43 +205,50 @@ function createSingleRunRow(
|
||||
): string[] {
|
||||
const value = getValue(benchmark);
|
||||
|
||||
return [
|
||||
indent(title, formatOptions.indent || 0),
|
||||
format(value, formatOptions),
|
||||
];
|
||||
return [indent(title, formatOptions.indent || 0), format(value, formatOptions)];
|
||||
}
|
||||
|
||||
function indent(text: string, level: number): string {
|
||||
return ' '.repeat(4 * level) + text;
|
||||
return " ".repeat(4 * level) + text;
|
||||
}
|
||||
|
||||
export function formatDiff(percentDiff: number, significance: SignificanceLevel | undefined, precision?: number): string {
|
||||
const percentString = format(percentDiff, { percentage: true, precision }, '%', true);
|
||||
export function formatDiff(
|
||||
percentDiff: number,
|
||||
significance: SignificanceLevel | undefined,
|
||||
precision?: number
|
||||
): string {
|
||||
const percentString = format(percentDiff, { percentage: true, precision }, "%", true);
|
||||
if (!significance || !percentString) {
|
||||
return percentString;
|
||||
}
|
||||
|
||||
switch (significance) {
|
||||
case SignificanceLevel.Warning: return `**${percentString}** 🔸`;
|
||||
case SignificanceLevel.Alert: return `**${percentString}** 🚨`;
|
||||
case SignificanceLevel.Awesome: return `**${percentString}** 🌟`;
|
||||
default: return assertNever(significance);
|
||||
case SignificanceLevel.Warning:
|
||||
return `**${percentString}** 🔸`;
|
||||
case SignificanceLevel.Alert:
|
||||
return `**${percentString}** 🚨`;
|
||||
case SignificanceLevel.Awesome:
|
||||
return `**${percentString}** 🌟`;
|
||||
default:
|
||||
return assertNever(significance);
|
||||
}
|
||||
}
|
||||
|
||||
function format(
|
||||
x: string | number | undefined,
|
||||
{ precision = 1, percentage }: FormatOptions = {},
|
||||
unit = percentage ? '%' : '',
|
||||
unit = percentage ? "%" : "",
|
||||
showPlusSign?: boolean
|
||||
): string {
|
||||
switch (typeof x) {
|
||||
case 'string': return x + unit;
|
||||
case 'number':
|
||||
if (isNaN(x) || !isFinite(x)) return '';
|
||||
let numString = (percentage ? x * 100 : x).toFixed(precision).replace(/^-0(\.0*)?$/, '0$1');
|
||||
case "string":
|
||||
return x + unit;
|
||||
case "number":
|
||||
if (isNaN(x) || !isFinite(x)) return "";
|
||||
let numString = (percentage ? x * 100 : x).toFixed(precision).replace(/^-0(\.0*)?$/, "0$1");
|
||||
if (showPlusSign && x > 0 && !/^0(\.0*)?$/.test(numString)) numString = `+${numString}`;
|
||||
return numString + unit;
|
||||
default: return '';
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,55 +1,71 @@
|
||||
import { createComparisonTable, createSingleRunTable } from './createTable';
|
||||
import { PackageBenchmarkSummary, systemsAreCloseEnough, Document, compact, toPackageKey } from '../common';
|
||||
import { getInterestingMetrics, SignificanceLevel, ComparedMetric } from '../analysis';
|
||||
import { createComparisonTable, createSingleRunTable } from "./createTable";
|
||||
import { PackageBenchmarkSummary, systemsAreCloseEnough, Document, compact, toPackageKey } from "../common";
|
||||
import { getInterestingMetrics, SignificanceLevel, ComparedMetric } from "../analysis";
|
||||
|
||||
export function createTablesWithAnalysesMessage(pairs: [Document<PackageBenchmarkSummary> | undefined, Document<PackageBenchmarkSummary>][], prNumber: number, alwaysWriteHeading = false, alwaysCollapseDetails = false) {
|
||||
return pairs.map(([before, after]) => {
|
||||
const interestingMetrics = before && getInterestingMetrics(before, after);
|
||||
const shouldCollapseDetails = alwaysCollapseDetails || !interestingMetrics || !interestingMetrics.length;
|
||||
const messageBody = [
|
||||
before
|
||||
? createComparisonTable(before, after, getBeforeTitle(before, after), getAfterTitle(before, after, prNumber))
|
||||
: createSingleRunTable(after),
|
||||
``,
|
||||
before && getSystemMismatchMessage(before, after),
|
||||
].join('\n');
|
||||
export function createTablesWithAnalysesMessage(
|
||||
pairs: [Document<PackageBenchmarkSummary> | undefined, Document<PackageBenchmarkSummary>][],
|
||||
prNumber: number,
|
||||
alwaysWriteHeading = false,
|
||||
alwaysCollapseDetails = false
|
||||
) {
|
||||
return pairs
|
||||
.map(([before, after]) => {
|
||||
const interestingMetrics = before && getInterestingMetrics(before, after);
|
||||
const shouldCollapseDetails = alwaysCollapseDetails || !interestingMetrics || !interestingMetrics.length;
|
||||
const messageBody = [
|
||||
before
|
||||
? createComparisonTable(before, after, getBeforeTitle(before, after), getAfterTitle(before, after, prNumber))
|
||||
: createSingleRunTable(after),
|
||||
``,
|
||||
before && getSystemMismatchMessage(before, after)
|
||||
].join("\n");
|
||||
|
||||
return compact([
|
||||
pairs.length > 1 || alwaysWriteHeading ? `### ${after.body.packageName}/v${after.body.packageVersion}` : undefined,
|
||||
getIntroMessage(before, after),
|
||||
``,
|
||||
getLanguageServiceCrashMessage(after),
|
||||
``,
|
||||
shouldCollapseDetails ? details(messageBody, getDetailsSummaryTitle(pairs.length, after)) : messageBody,
|
||||
``,
|
||||
interestingMetrics && getInterestingMetricsMessage(interestingMetrics),
|
||||
]).join('\n');
|
||||
}).join('\n\n');
|
||||
return compact([
|
||||
pairs.length > 1 || alwaysWriteHeading
|
||||
? `### ${after.body.packageName}/v${after.body.packageVersion}`
|
||||
: undefined,
|
||||
getIntroMessage(before, after),
|
||||
``,
|
||||
getLanguageServiceCrashMessage(after),
|
||||
``,
|
||||
shouldCollapseDetails ? details(messageBody, getDetailsSummaryTitle(pairs.length, after)) : messageBody,
|
||||
``,
|
||||
interestingMetrics && getInterestingMetricsMessage(interestingMetrics)
|
||||
]).join("\n");
|
||||
})
|
||||
.join("\n\n");
|
||||
}
|
||||
|
||||
function getDetailsSummaryTitle(comparisonsCount: number, benchmark: Document<PackageBenchmarkSummary>) {
|
||||
let titleStart = '<strong>Comparison details';
|
||||
let titleStart = "<strong>Comparison details";
|
||||
if (comparisonsCount > 1) {
|
||||
titleStart += ` for ${toPackageKey(benchmark.body.packageName, benchmark.body.packageVersion)}`;
|
||||
}
|
||||
return titleStart + '</strong> 📊';
|
||||
return titleStart + "</strong> 📊";
|
||||
}
|
||||
|
||||
function getBeforeTitle(before: Document<PackageBenchmarkSummary>, after: Document<PackageBenchmarkSummary>) {
|
||||
if (before.body.packageVersion === after.body.packageVersion) {
|
||||
return 'master';
|
||||
return "master";
|
||||
}
|
||||
return `${before.body.packageVersion}@master`;
|
||||
}
|
||||
|
||||
function getAfterTitle(before: Document<PackageBenchmarkSummary>, after: Document<PackageBenchmarkSummary>, prNumber: number) {
|
||||
function getAfterTitle(
|
||||
before: Document<PackageBenchmarkSummary>,
|
||||
after: Document<PackageBenchmarkSummary>,
|
||||
prNumber: number
|
||||
) {
|
||||
if (before.body.packageVersion === after.body.packageVersion) {
|
||||
return `#${prNumber}`;
|
||||
}
|
||||
return `${after.body.packageVersion} in #${prNumber}`;
|
||||
}
|
||||
|
||||
function getIntroMessage(before: Document<PackageBenchmarkSummary> | undefined, after: Document<PackageBenchmarkSummary>) {
|
||||
function getIntroMessage(
|
||||
before: Document<PackageBenchmarkSummary> | undefined,
|
||||
after: Document<PackageBenchmarkSummary>
|
||||
) {
|
||||
if (before && before.body.packageVersion === after.body.packageVersion) {
|
||||
return;
|
||||
}
|
||||
@@ -61,11 +77,14 @@ function getIntroMessage(before: Document<PackageBenchmarkSummary> | undefined,
|
||||
|
||||
function getLanguageServiceCrashMessage(benchmark: Document<PackageBenchmarkSummary>) {
|
||||
if (benchmark.body.languageServiceCrashed) {
|
||||
return `Before we get into it, I need to mention that **the language service crashed** while taking these measurements. ` +
|
||||
return (
|
||||
`Before we get into it, I need to mention that **the language service crashed** while taking these measurements. ` +
|
||||
`This isn’t your fault—on the contrary, you helped us find a probably TypeScript bug! But, be aware that these results ` +
|
||||
`may or may not be quite what they should be, depending on how many locations in your tests caused a crash. Paging ` +
|
||||
`@andrewbranch to investigate.`;
|
||||
`@andrewbranch to investigate.`
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
function getSystemMismatchMessage(a: Document<PackageBenchmarkSummary>, b: Document<PackageBenchmarkSummary>) {
|
||||
@@ -83,28 +102,28 @@ function getInterestingMetricsMessage(interestingMetrics: readonly ComparedMetri
|
||||
return `Wow, it looks like all the big movers moved in the right direction! Way to go! 🌟 I won’t post performance data again unless it gets worse.`;
|
||||
}
|
||||
if (interestingMetrics.length > 3 && awesomeMetrics.length / interestingMetrics.length < 0.5) {
|
||||
return 'It looks like there are several metrics that changed quite a bit. You might want to take a look and make sure your changes won’t cause slow-downs for users consuming these types.';
|
||||
return "It looks like there are several metrics that changed quite a bit. You might want to take a look and make sure your changes won’t cause slow-downs for users consuming these types.";
|
||||
}
|
||||
const metricsToCheck = interestingMetrics.filter(({ significance }) => significance === SignificanceLevel.Warning || significance === SignificanceLevel.Alert);
|
||||
return `Looks like there were a couple significant differences—take a look at `
|
||||
+ formatListForSentence(metricsToCheck.map(m => `**${m.metric.sentenceName}**`))
|
||||
+ ` to make sure everything looks ok.`;
|
||||
const metricsToCheck = interestingMetrics.filter(
|
||||
({ significance }) => significance === SignificanceLevel.Warning || significance === SignificanceLevel.Alert
|
||||
);
|
||||
return (
|
||||
`Looks like there were a couple significant differences—take a look at ` +
|
||||
formatListForSentence(metricsToCheck.map(m => `**${m.metric.sentenceName}**`)) +
|
||||
` to make sure everything looks ok.`
|
||||
);
|
||||
}
|
||||
|
||||
function details(details: string, summary?: string) {
|
||||
return compact([
|
||||
'<details>',
|
||||
summary && `<summary>${summary}</summary>`,
|
||||
'',
|
||||
details,
|
||||
`</details>`
|
||||
]).join('\n');
|
||||
return compact(["<details>", summary && `<summary>${summary}</summary>`, "", details, `</details>`]).join("\n");
|
||||
}
|
||||
|
||||
function formatListForSentence(items: string[]) {
|
||||
return items.map((item, index) => {
|
||||
const isFirst = index === 0;
|
||||
const isLast = index === items.length - 1;
|
||||
return !isFirst && isLast ? `and ${item}` : item;
|
||||
}).join(items.length > 2 ? ', ' : ' ');
|
||||
return items
|
||||
.map((item, index) => {
|
||||
const isFirst = index === 0;
|
||||
const isLast = index === items.length - 1;
|
||||
return !isFirst && isLast ? `and ${item}` : item;
|
||||
})
|
||||
.join(items.length > 2 ? ", " : " ");
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import Octokit from '@octokit/rest';
|
||||
import { config } from '../common';
|
||||
import Octokit from "@octokit/rest";
|
||||
import { config } from "../common";
|
||||
|
||||
let octokit: Octokit | undefined;
|
||||
export function getOctokit() {
|
||||
return octokit || (octokit = new Octokit({
|
||||
auth: config.github.typeScriptBotAuthToken,
|
||||
userAgent: config.github.userAgent,
|
||||
}));
|
||||
}
|
||||
return (
|
||||
octokit ||
|
||||
(octokit = new Octokit({
|
||||
auth: config.github.typeScriptBotAuthToken,
|
||||
userAgent: config.github.userAgent
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { PackageBenchmarkSummary, Document, config, compact } from '../common';
|
||||
import { assertDefined } from 'types-publisher/bin/util/util';
|
||||
import { getOctokit } from './getOctokit';
|
||||
import { createTablesWithAnalysesMessage } from './createTablesWithAnalysesMessage';
|
||||
import { createPerfCommentBody } from './comment';
|
||||
import { getOverallChangeForComparisons } from '../analysis';
|
||||
import { PackageBenchmarkSummary, Document, config, compact } from "../common";
|
||||
import { getOctokit } from "./getOctokit";
|
||||
import { createTablesWithAnalysesMessage } from "./createTablesWithAnalysesMessage";
|
||||
import { createPerfCommentBody } from "./comment";
|
||||
import { getOverallChangeForComparisons } from "../analysis";
|
||||
import { assertDefined } from "@definitelytyped/utils";
|
||||
|
||||
type BeforeAndAfter = [Document<PackageBenchmarkSummary> | undefined, Document<PackageBenchmarkSummary>];
|
||||
|
||||
@@ -12,33 +12,35 @@ export interface PostDependentsComparisonResultOptions {
|
||||
dryRun: boolean;
|
||||
}
|
||||
|
||||
export async function postDependentsComparisonResult({
|
||||
comparisons,
|
||||
dryRun,
|
||||
}: PostDependentsComparisonResultOptions) {
|
||||
export async function postDependentsComparisonResult({ comparisons, dryRun }: PostDependentsComparisonResultOptions) {
|
||||
const message = compact([
|
||||
`Ok, I’m back! As promised, here are the results from dependent packages`,
|
||||
``,
|
||||
createTablesWithAnalysesMessage(
|
||||
comparisons,
|
||||
parseInt(process.env.SYSTEM_PULLREQUEST_PULLREQUESTNUMBER || '')),
|
||||
true,
|
||||
]).join('\n');
|
||||
createTablesWithAnalysesMessage(comparisons, parseInt(process.env.SYSTEM_PULLREQUEST_PULLREQUESTNUMBER || "")),
|
||||
true
|
||||
]).join("\n");
|
||||
|
||||
if (!dryRun) {
|
||||
try {
|
||||
const prNumber = parseInt(assertDefined(
|
||||
process.env.SYSTEM_PULLREQUEST_PULLREQUESTNUMBER,
|
||||
`Required environment variable 'SYSTEM_PULLREQUEST_PULLREQUESTNUMBER' was not set.`), 10);
|
||||
const prNumber = parseInt(
|
||||
assertDefined(
|
||||
process.env.SYSTEM_PULLREQUEST_PULLREQUESTNUMBER,
|
||||
`Required environment variable 'SYSTEM_PULLREQUEST_PULLREQUESTNUMBER' was not set.`
|
||||
),
|
||||
10
|
||||
);
|
||||
|
||||
const octokit = getOctokit();
|
||||
await octokit.issues.createComment({
|
||||
...config.github.commonParams,
|
||||
issue_number: prNumber,
|
||||
body: createPerfCommentBody({
|
||||
overallChange: getOverallChangeForComparisons(comparisons),
|
||||
benchmarks: comparisons.map(([, b]) => ({ createdAt: b.createdAt })),
|
||||
}, message),
|
||||
body: createPerfCommentBody(
|
||||
{
|
||||
overallChange: getOverallChangeForComparisons(comparisons),
|
||||
benchmarks: comparisons.map(([, b]) => ({ createdAt: b.createdAt }))
|
||||
},
|
||||
message
|
||||
)
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(message);
|
||||
@@ -47,4 +49,3 @@ export async function postDependentsComparisonResult({
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { PackageBenchmarkSummary, Document, config, compact, findLast } from '../common';
|
||||
import { getOctokit } from './getOctokit';
|
||||
import { assertDefined } from 'types-publisher/bin/util/util';
|
||||
import { createTablesWithAnalysesMessage } from './createTablesWithAnalysesMessage';
|
||||
import { isPerfComment, createPerfCommentBody, getCommentData, CommentData } from './comment';
|
||||
import { OverallChange, getOverallChangeForComparisons } from '../analysis';
|
||||
import { setLabels } from './setLabels';
|
||||
import { PackageBenchmarkSummary, Document, config, compact, findLast } from "../common";
|
||||
import { getOctokit } from "./getOctokit";
|
||||
import { createTablesWithAnalysesMessage } from "./createTablesWithAnalysesMessage";
|
||||
import { isPerfComment, createPerfCommentBody, getCommentData, CommentData } from "./comment";
|
||||
import { OverallChange, getOverallChangeForComparisons } from "../analysis";
|
||||
import { setLabels } from "./setLabels";
|
||||
import { assertDefined } from "@definitelytyped/utils";
|
||||
|
||||
type BeforeAndAfter = [Document<PackageBenchmarkSummary> | undefined, Document<PackageBenchmarkSummary>];
|
||||
|
||||
@@ -17,32 +17,34 @@ export interface PostInitialComparisonResultsOptions {
|
||||
export async function postInitialComparisonResults({
|
||||
comparisons,
|
||||
dependentCount,
|
||||
dryRun,
|
||||
dryRun
|
||||
}: PostInitialComparisonResultsOptions) {
|
||||
|
||||
if (dryRun) {
|
||||
return getFullFirstPostMessage(
|
||||
createTablesWithAnalysesMessage(
|
||||
comparisons,
|
||||
parseInt(process.env.SYSTEM_PULLREQUEST_PULLREQUESTNUMBER || '')),
|
||||
dependentCount);
|
||||
createTablesWithAnalysesMessage(comparisons, parseInt(process.env.SYSTEM_PULLREQUEST_PULLREQUESTNUMBER || "")),
|
||||
dependentCount
|
||||
);
|
||||
} else {
|
||||
try {
|
||||
const prNumber = parseInt(assertDefined(
|
||||
process.env.SYSTEM_PULLREQUEST_PULLREQUESTNUMBER,
|
||||
`Required environment variable 'SYSTEM_PULLREQUEST_PULLREQUESTNUMBER' was not set.`), 10);
|
||||
const prNumber = parseInt(
|
||||
assertDefined(
|
||||
process.env.SYSTEM_PULLREQUEST_PULLREQUESTNUMBER,
|
||||
`Required environment variable 'SYSTEM_PULLREQUEST_PULLREQUESTNUMBER' was not set.`
|
||||
),
|
||||
10
|
||||
);
|
||||
|
||||
const octokit = getOctokit();
|
||||
const comments = await octokit.issues.listComments({
|
||||
...config.github.commonParams,
|
||||
issue_number: prNumber,
|
||||
issue_number: prNumber
|
||||
});
|
||||
|
||||
const currentOverallChange = getOverallChangeForComparisons(comparisons);
|
||||
const mostRecentComment = findLast(comments.data, isPerfComment);
|
||||
const commentData: CommentData = {
|
||||
overallChange: currentOverallChange,
|
||||
benchmarks: comparisons.map(([, b]) => ({ createdAt: b.createdAt })),
|
||||
benchmarks: comparisons.map(([, b]) => ({ createdAt: b.createdAt }))
|
||||
};
|
||||
if (mostRecentComment) {
|
||||
const lastOverallChange = getCommentData(mostRecentComment)?.overallChange;
|
||||
@@ -55,20 +57,26 @@ export async function postInitialComparisonResults({
|
||||
const message = getConciseUpdateMessage(
|
||||
lastOverallChange,
|
||||
currentOverallChange,
|
||||
createTablesWithAnalysesMessage(comparisons, prNumber, /*alwaysWriteHeader*/ false, /*alwaysCollapseDetails*/ true),
|
||||
comparisons[0][1].body.sourceVersion);
|
||||
createTablesWithAnalysesMessage(
|
||||
comparisons,
|
||||
prNumber,
|
||||
/*alwaysWriteHeader*/ false,
|
||||
/*alwaysCollapseDetails*/ true
|
||||
),
|
||||
comparisons[0][1].body.sourceVersion
|
||||
);
|
||||
|
||||
await octokit.issues.createComment({
|
||||
...config.github.commonParams,
|
||||
issue_number: prNumber,
|
||||
body: createPerfCommentBody(commentData, message),
|
||||
body: createPerfCommentBody(commentData, message)
|
||||
});
|
||||
} else {
|
||||
const message = getFullFirstPostMessage(createTablesWithAnalysesMessage(comparisons, prNumber), dependentCount);
|
||||
await octokit.issues.createComment({
|
||||
...config.github.commonParams,
|
||||
issue_number: prNumber,
|
||||
body: createPerfCommentBody(commentData, message),
|
||||
body: createPerfCommentBody(commentData, message)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -77,6 +85,7 @@ export async function postInitialComparisonResults({
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
function getFullFirstPostMessage(mainMessage: string, dependentCount: number): string {
|
||||
@@ -88,25 +97,28 @@ function getFullFirstPostMessage(mainMessage: string, dependentCount: number): s
|
||||
`Let’s review the numbers, shall we?`,
|
||||
``,
|
||||
mainMessage
|
||||
]).join('\n');
|
||||
]).join("\n");
|
||||
}
|
||||
|
||||
function getConciseUpdateMessage(
|
||||
prevOverallChange: OverallChange | undefined,
|
||||
overallChange: OverallChange | undefined,
|
||||
mainMessage: string,
|
||||
sha: string,
|
||||
sha: string
|
||||
): string {
|
||||
const gotBetter = (prevOverallChange ?? 0) & OverallChange.Worse && !((overallChange ?? 0) & OverallChange.Worse);
|
||||
return [
|
||||
`Updated numbers for you here from ${sha.slice(0, 7)}. ${gotBetter ? 'Nice job, these numbers look better.' : ''}`,
|
||||
`Updated numbers for you here from ${sha.slice(0, 7)}. ${gotBetter ? "Nice job, these numbers look better." : ""}`,
|
||||
``,
|
||||
mainMessage,
|
||||
].join('\n');
|
||||
mainMessage
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
function getDependentsMessage(dependentCount: number): string | undefined {
|
||||
if (dependentCount) {
|
||||
return `I’m still measuring **${dependentCount} other package${dependentCount === 1 ? '' : 's'}** that depend${dependentCount === 1 ? 's' : ''} on these typings, and will post another comment with those results when I’m done. But in the meantime, you can go ahead and see the results of what you directly changed in this PR.`;
|
||||
return `I’m still measuring **${dependentCount} other package${dependentCount === 1 ? "" : "s"}** that depend${
|
||||
dependentCount === 1 ? "s" : ""
|
||||
} on these typings, and will post another comment with those results when I’m done. But in the meantime, you can go ahead and see the results of what you directly changed in this PR.`;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { PackageBenchmarkSummary, Document, toPackageKey } from '../common';
|
||||
import { createComparisonTable } from './createTable';
|
||||
import { PackageBenchmarkSummary, Document, toPackageKey } from "../common";
|
||||
import { createComparisonTable } from "./createTable";
|
||||
|
||||
type BeforeAndAfter = [Document<PackageBenchmarkSummary>, Document<PackageBenchmarkSummary>];
|
||||
|
||||
@@ -8,13 +8,15 @@ export interface PostTypeScriptComparisonResultsOptions {
|
||||
dryRun: boolean;
|
||||
}
|
||||
|
||||
export async function postTypeScriptComparisonResults({
|
||||
comparisons,
|
||||
}: PostTypeScriptComparisonResultsOptions) {
|
||||
const message = comparisons.map(([baseline, head]) => [
|
||||
`### ${toPackageKey(baseline.body.packageName, baseline.body.packageVersion)}`,
|
||||
createComparisonTable(baseline, head, baseline.body.typeScriptVersion, 'HEAD'),
|
||||
].join('\n')).join('\n\n');
|
||||
export async function postTypeScriptComparisonResults({ comparisons }: PostTypeScriptComparisonResultsOptions) {
|
||||
const message = comparisons
|
||||
.map(([baseline, head]) =>
|
||||
[
|
||||
`### ${toPackageKey(baseline.body.packageName, baseline.body.packageVersion)}`,
|
||||
createComparisonTable(baseline, head, baseline.body.typeScriptVersion, "HEAD")
|
||||
].join("\n")
|
||||
)
|
||||
.join("\n\n");
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { getOctokit } from './getOctokit';
|
||||
import { OverallChange } from '../analysis';
|
||||
import { config } from '../common';
|
||||
import Octokit = require('@octokit/rest');
|
||||
import { getOctokit } from "./getOctokit";
|
||||
import { OverallChange } from "../analysis";
|
||||
import { config } from "../common";
|
||||
import Octokit = require("@octokit/rest");
|
||||
|
||||
const perfLabels = ['Perf: Same', 'Perf: Better', 'Perf: Mixed', 'Perf: Worse'];
|
||||
const perfLabels = ["Perf: Same", "Perf: Better", "Perf: Mixed", "Perf: Worse"];
|
||||
|
||||
function isPerfLabel(label: Octokit.IssuesListLabelsOnIssueResponseItem) {
|
||||
return perfLabels.includes(label.name);
|
||||
@@ -11,18 +11,23 @@ function isPerfLabel(label: Octokit.IssuesListLabelsOnIssueResponseItem) {
|
||||
|
||||
function toLabel(change: OverallChange | undefined): string | undefined {
|
||||
switch (change) {
|
||||
case OverallChange.Same: return perfLabels[0];
|
||||
case OverallChange.Better: return perfLabels[1];
|
||||
case OverallChange.Mixed: return perfLabels[2];
|
||||
case OverallChange.Worse: return perfLabels[3];
|
||||
case OverallChange.Same:
|
||||
return perfLabels[0];
|
||||
case OverallChange.Better:
|
||||
return perfLabels[1];
|
||||
case OverallChange.Mixed:
|
||||
return perfLabels[2];
|
||||
case OverallChange.Worse:
|
||||
return perfLabels[3];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
export async function setLabels(prNumber: number, overallChange: OverallChange | undefined) {
|
||||
const octokit = getOctokit();
|
||||
const labels = await octokit.issues.listLabelsOnIssue({
|
||||
...config.github.commonParams,
|
||||
issue_number: prNumber,
|
||||
issue_number: prNumber
|
||||
});
|
||||
|
||||
const perfLabels = labels.data.filter(isPerfLabel);
|
||||
@@ -34,7 +39,7 @@ export async function setLabels(prNumber: number, overallChange: OverallChange |
|
||||
await octokit.issues.removeLabel({
|
||||
...config.github.commonParams,
|
||||
issue_number: prNumber,
|
||||
name: label.name,
|
||||
name: label.name
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"composite": true
|
||||
},
|
||||
"references": [
|
||||
{ "path": "../analysis" },
|
||||
{ "path": "../common" }
|
||||
]
|
||||
}
|
||||
100
packages/perf/src/github/types/markdown-table.d.ts
vendored
100
packages/perf/src/github/types/markdown-table.d.ts
vendored
@@ -1,55 +1,57 @@
|
||||
declare module 'markdown-table' {
|
||||
export type ColumnAlignment = 'l' | 'r' | 'c' | '.' | '';
|
||||
export interface MarkdownTableOptions {
|
||||
/**
|
||||
* One style for all columns, or styles for their respective columns (`string` or `string[]`).
|
||||
* Each style is either `'l'` (left), `'r'` (right), `'c'` (centre), or `'.'` (dot). Other values are treated
|
||||
* as `''`, which doesn’t place the colon but does left align. Only the lowercased first character is
|
||||
* used, so Right is fine.
|
||||
*/
|
||||
align?: ColumnAlignment | ColumnAlignment[];
|
||||
/**
|
||||
* Value to insert between cells (`string`, default: `' | '`). Careful, setting this to a non-pipe breaks GitHub
|
||||
* Flavoured Markdown.
|
||||
*/
|
||||
delimiter?: string;
|
||||
/**
|
||||
* Value to insert at the beginning of every row (`string`, default: `'| '`).
|
||||
*/
|
||||
start?: string;
|
||||
/**
|
||||
* Value to insert at the end of every row (`string`, default: `' |'`).
|
||||
*/
|
||||
end?: string;
|
||||
/**
|
||||
* Whether to display a rule between the header and the body of the table (`boolean`, default: `true`). Careful,
|
||||
* will break GitHub Flavoured Markdown when `false`.
|
||||
*/
|
||||
rule?: boolean;
|
||||
/**
|
||||
* Method to detect the length of a cell (`Function`, default: `s => s.length`).
|
||||
*
|
||||
* ANSI-sequences mess up tables on terminals. To fix this, you have to pass in a `stringLength` option to detect
|
||||
* the “visible” length of a cell.
|
||||
*
|
||||
* @example
|
||||
* var strip = require('strip-ansi')
|
||||
*
|
||||
* function stringLength(cell) {
|
||||
* return strip(cell).length
|
||||
* }
|
||||
*/
|
||||
stringLength?: (cellContents: string) => number;
|
||||
/**
|
||||
* Whether to pad the markdown for table cells to make them the same width (`boolean`, default: `true`). Setting
|
||||
* this to false will cause the table rows to remain staggered.
|
||||
*/
|
||||
pad?: boolean;
|
||||
declare module "markdown-table" {
|
||||
namespace table {
|
||||
export type ColumnAlignment = "l" | "r" | "c" | "." | "";
|
||||
export interface MarkdownTableOptions {
|
||||
/**
|
||||
* One style for all columns, or styles for their respective columns (`string` or `string[]`).
|
||||
* Each style is either `'l'` (left), `'r'` (right), `'c'` (centre), or `'.'` (dot). Other values are treated
|
||||
* as `''`, which doesn’t place the colon but does left align. Only the lowercased first character is
|
||||
* used, so Right is fine.
|
||||
*/
|
||||
align?: ColumnAlignment | ColumnAlignment[];
|
||||
/**
|
||||
* Value to insert between cells (`string`, default: `' | '`). Careful, setting this to a non-pipe breaks GitHub
|
||||
* Flavoured Markdown.
|
||||
*/
|
||||
delimiter?: string;
|
||||
/**
|
||||
* Value to insert at the beginning of every row (`string`, default: `'| '`).
|
||||
*/
|
||||
start?: string;
|
||||
/**
|
||||
* Value to insert at the end of every row (`string`, default: `' |'`).
|
||||
*/
|
||||
end?: string;
|
||||
/**
|
||||
* Whether to display a rule between the header and the body of the table (`boolean`, default: `true`). Careful,
|
||||
* will break GitHub Flavoured Markdown when `false`.
|
||||
*/
|
||||
rule?: boolean;
|
||||
/**
|
||||
* Method to detect the length of a cell (`Function`, default: `s => s.length`).
|
||||
*
|
||||
* ANSI-sequences mess up tables on terminals. To fix this, you have to pass in a `stringLength` option to detect
|
||||
* the “visible” length of a cell.
|
||||
*
|
||||
* @example
|
||||
* var strip = require('strip-ansi')
|
||||
*
|
||||
* function stringLength(cell) {
|
||||
* return strip(cell).length
|
||||
* }
|
||||
*/
|
||||
stringLength?: (cellContents: string) => number;
|
||||
/**
|
||||
* Whether to pad the markdown for table cells to make them the same width (`boolean`, default: `true`). Setting
|
||||
* this to false will cause the table rows to remain staggered.
|
||||
*/
|
||||
pad?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a given matrix of strings (an array of arrays of strings) into a table.
|
||||
*/
|
||||
function table(rows: string[][], options?: MarkdownTableOptions): string;
|
||||
function table(rows: string[][], options?: table.MarkdownTableOptions): string;
|
||||
export = table;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
import * as path from 'path';
|
||||
import { CompilerOptions, LanguageServiceHost } from 'typescript';
|
||||
import { ensureExists } from '../common';
|
||||
const basePath = path.resolve(__dirname, '../../..');
|
||||
import { CompilerOptions, LanguageServiceHost } from "typescript";
|
||||
import { ensureExists } from "../common";
|
||||
|
||||
export function createLanguageServiceHost(
|
||||
ts: typeof import('typescript'),
|
||||
ts: typeof import("typescript"),
|
||||
compilerOptions: CompilerOptions,
|
||||
testPaths: string[],
|
||||
testPaths: string[]
|
||||
): LanguageServiceHost {
|
||||
let version = 0;
|
||||
return {
|
||||
directoryExists: ts.sys.directoryExists,
|
||||
getCompilationSettings: () => compilerOptions,
|
||||
getCurrentDirectory: ts.sys.getCurrentDirectory,
|
||||
getDefaultLibFileName: () => require.resolve('typescript/lib/lib.d.ts'),
|
||||
getDefaultLibFileName: () => require.resolve("typescript/lib/lib.d.ts"),
|
||||
getNewLine: () => ts.sys.newLine,
|
||||
getScriptFileNames: () => testPaths,
|
||||
fileExists: ts.sys.fileExists,
|
||||
getDirectories: ts.sys.getDirectories,
|
||||
getScriptSnapshot: fileName => ts.ScriptSnapshot.fromString(ts.sys.readFile(ensureExists(fileName))!),
|
||||
getScriptVersion: () => (version++).toString(),
|
||||
getScriptVersion: () => (version++).toString()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import * as os from 'os';
|
||||
import { FormatDiagnosticsHost } from 'typescript';
|
||||
import * as os from "os";
|
||||
import { FormatDiagnosticsHost } from "typescript";
|
||||
|
||||
export const formatDiagnosticsHost: FormatDiagnosticsHost = {
|
||||
getCanonicalFileName: fileName => fileName,
|
||||
getNewLine: () => os.EOL,
|
||||
getCurrentDirectory: () => process.cwd(),
|
||||
getCurrentDirectory: () => process.cwd()
|
||||
};
|
||||
|
||||
@@ -1,24 +1,31 @@
|
||||
import * as path from 'path';
|
||||
import { ParsedCommandLine } from 'typescript';
|
||||
import { ensureExists } from '../common';
|
||||
import { formatDiagnosticsHost } from './formatDiagnosticsHost';
|
||||
import * as path from "path";
|
||||
import { ParsedCommandLine } from "typescript";
|
||||
import { ensureExists } from "../common";
|
||||
import { formatDiagnosticsHost } from "./formatDiagnosticsHost";
|
||||
|
||||
export function getParsedCommandLineForPackage(ts: typeof import('typescript'), packagePath: string): ParsedCommandLine {
|
||||
const tsConfigPath = ensureExists(path.resolve(packagePath, 'tsconfig.json'));
|
||||
const parsedCommandLine = ts.getParsedCommandLineOfConfigFile(tsConfigPath, {}, {
|
||||
fileExists: ts.sys.fileExists,
|
||||
getCurrentDirectory: ts.sys.getCurrentDirectory,
|
||||
readDirectory: ts.sys.readDirectory,
|
||||
readFile: ts.sys.readFile,
|
||||
useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames,
|
||||
onUnRecoverableConfigFileDiagnostic: diagnostic => {
|
||||
console.error(ts.formatDiagnostic(diagnostic, formatDiagnosticsHost));
|
||||
},
|
||||
});
|
||||
export function getParsedCommandLineForPackage(
|
||||
ts: typeof import("typescript"),
|
||||
packagePath: string
|
||||
): ParsedCommandLine {
|
||||
const tsConfigPath = ensureExists(path.resolve(packagePath, "tsconfig.json"));
|
||||
const parsedCommandLine = ts.getParsedCommandLineOfConfigFile(
|
||||
tsConfigPath,
|
||||
{},
|
||||
{
|
||||
fileExists: ts.sys.fileExists,
|
||||
getCurrentDirectory: ts.sys.getCurrentDirectory,
|
||||
readDirectory: ts.sys.readDirectory,
|
||||
readFile: ts.sys.readFile,
|
||||
useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames,
|
||||
onUnRecoverableConfigFileDiagnostic: diagnostic => {
|
||||
console.error(ts.formatDiagnostic(diagnostic, formatDiagnosticsHost));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (!parsedCommandLine) {
|
||||
throw new Error(`Could not get ParsedCommandLine from config file: ${tsConfigPath}`);
|
||||
}
|
||||
|
||||
return parsedCommandLine;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { promisify } from 'util';
|
||||
import { installAll, typeScriptPath, cleanInstalls, installNext } from 'dtslint/bin/installer';
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import { promisify } from "util";
|
||||
import { installAll, typeScriptPath, cleanInstalls, installNext } from "dtslint/bin/installer";
|
||||
const exists = promisify(fs.exists);
|
||||
|
||||
export async function getTypeScript(
|
||||
version: string,
|
||||
localTypeScriptPath?: string,
|
||||
install = true,
|
||||
): Promise<{ ts: typeof import('typescript'), tsPath: string }> {
|
||||
install = true
|
||||
): Promise<{ ts: typeof import("typescript"); tsPath: string }> {
|
||||
const tsPath = path.resolve(typeScriptPath(version, localTypeScriptPath));
|
||||
if (install) {
|
||||
if (version === 'next') {
|
||||
if (version === "next") {
|
||||
await cleanInstalls();
|
||||
await installNext();
|
||||
} else if (!await exists(tsPath)) {
|
||||
} else if (!(await exists(tsPath))) {
|
||||
await installAll();
|
||||
}
|
||||
}
|
||||
if (!await exists(tsPath)) {
|
||||
if (!(await exists(tsPath))) {
|
||||
throw new Error(`Version ${version} is not available at ${tsPath}`);
|
||||
}
|
||||
return {
|
||||
ts: await import(tsPath),
|
||||
tsPath,
|
||||
tsPath
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './measurePerf';
|
||||
export * from './printSummary';
|
||||
export * from "./measurePerf";
|
||||
export * from "./printSummary";
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import { allDependencies, getAffectedPackages } from 'types-publisher/bin/tester/get-affected-packages';
|
||||
import { AllPackages, PackageId } from 'types-publisher/bin/lib/packages';
|
||||
import { nAtATime, execAndThrowErrors } from 'types-publisher/bin/util/util';
|
||||
import { pathExists } from '../common';
|
||||
import * as os from "os";
|
||||
import * as path from "path";
|
||||
import { AllPackages, PackageId, getAffectedPackages, allDependencies } from "@definitelytyped/definitions-parser";
|
||||
import { nAtATime, execAndThrowErrors } from "@definitelytyped/utils";
|
||||
import { pathExists } from "../common";
|
||||
|
||||
export async function installDependencies(allPackages: AllPackages, packageId: PackageId, typesPath: string): Promise<void> {
|
||||
export async function installDependencies(
|
||||
allPackages: AllPackages,
|
||||
packageId: PackageId,
|
||||
typesPath: string
|
||||
): Promise<void> {
|
||||
const { changedPackages, dependentPackages } = getAffectedPackages(allPackages, [packageId]);
|
||||
const dependencies = allDependencies(allPackages, [...changedPackages, ...dependentPackages]);
|
||||
await nAtATime(Math.min(os.cpus().length, 6), dependencies, async typingsData => {
|
||||
const packagePath = path.join(typesPath, typingsData.name);
|
||||
if (!await pathExists(path.join(packagePath, 'package.json'))) {
|
||||
return;
|
||||
}
|
||||
const packagePath = path.join(typesPath, typingsData.name);
|
||||
if (!(await pathExists(path.join(packagePath, "package.json")))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cmd = 'npm install --ignore-scripts --no-shrinkwrap --no-package-lock --no-bin-links';
|
||||
return execAndThrowErrors(cmd, packagePath);
|
||||
const cmd = "npm install --ignore-scripts --no-shrinkwrap --no-package-lock --no-bin-links";
|
||||
return execAndThrowErrors(cmd, packagePath);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CompilerOptions } from 'typescript';
|
||||
import { formatDiagnosticsHost } from './formatDiagnosticsHost';
|
||||
import { PackageBenchmark } from '../common';
|
||||
import { CompilerOptions } from "typescript";
|
||||
import { formatDiagnosticsHost } from "./formatDiagnosticsHost";
|
||||
import { PackageBenchmark } from "../common";
|
||||
|
||||
export const measureBatchCompilationWorkerFilename = __filename;
|
||||
|
||||
@@ -10,35 +10,38 @@ export interface MeasureBatchCompilationChildProcessArgs {
|
||||
options: CompilerOptions;
|
||||
}
|
||||
|
||||
export type MeasureBatchCompilationChildProcessResult = Pick<PackageBenchmark, 'typeCount' | 'relationCacheSizes' | 'memoryUsage'>;
|
||||
export type MeasureBatchCompilationChildProcessResult = Pick<
|
||||
PackageBenchmark,
|
||||
"typeCount" | "relationCacheSizes" | "memoryUsage"
|
||||
>;
|
||||
|
||||
if (!module.parent) {
|
||||
if (!process.send) {
|
||||
throw new Error('Process was not started as a forked process');
|
||||
throw new Error("Process was not started as a forked process");
|
||||
}
|
||||
|
||||
process.on('message', async ([message]: MeasureBatchCompilationChildProcessArgs[]) => {
|
||||
const ts: typeof import('typescript') = await import(message.tsPath);
|
||||
process.on("message", async ([message]: MeasureBatchCompilationChildProcessArgs[]) => {
|
||||
const ts: typeof import("typescript") = await import(message.tsPath);
|
||||
const program = ts.createProgram({ rootNames: message.fileNames, options: message.options });
|
||||
const diagnostics = program.getSemanticDiagnostics().filter(diagnostic => {
|
||||
return diagnostic.code === 2307; // Cannot find module
|
||||
});
|
||||
if (diagnostics.length) {
|
||||
console.log(ts.formatDiagnostics(diagnostics, formatDiagnosticsHost));
|
||||
throw new Error('Compilation had errors');
|
||||
throw new Error("Compilation had errors");
|
||||
}
|
||||
|
||||
|
||||
const result: MeasureBatchCompilationChildProcessResult = {
|
||||
typeCount: (program as any).getTypeCount(),
|
||||
memoryUsage: ts.sys.getMemoryUsage!(),
|
||||
relationCacheSizes: (program as any).getRelationCacheSizes && (program as any).getRelationCacheSizes(),
|
||||
relationCacheSizes: (program as any).getRelationCacheSizes && (program as any).getRelationCacheSizes()
|
||||
};
|
||||
|
||||
process.send!(result);
|
||||
});
|
||||
|
||||
process.on('unhandledRejection', (err) => {
|
||||
process.on("unhandledRejection", err => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import * as assert from 'assert';
|
||||
import { PerformanceObserver, performance } from 'perf_hooks';
|
||||
import { Omit, LanguageServiceSingleMeasurement } from '../common';
|
||||
import { getParsedCommandLineForPackage } from './getParsedCommandLineForPackage';
|
||||
import { createLanguageServiceHost } from './createLanguageServiceHost';
|
||||
import { LanguageService, ParsedCommandLine, LanguageServiceHost } from 'typescript';
|
||||
import * as assert from "assert";
|
||||
import { PerformanceObserver, performance } from "perf_hooks";
|
||||
import { Omit, LanguageServiceSingleMeasurement } from "../common";
|
||||
import { getParsedCommandLineForPackage } from "./getParsedCommandLineForPackage";
|
||||
import { createLanguageServiceHost } from "./createLanguageServiceHost";
|
||||
import { LanguageService, ParsedCommandLine, LanguageServiceHost } from "typescript";
|
||||
export const measureLanguageServiceWorkerFilename = __filename;
|
||||
|
||||
export interface MeasureLanguageServiceArgs extends Omit<LanguageServiceSingleMeasurement, 'quickInfoDuration' | 'completionsDuration'> {
|
||||
export interface MeasureLanguageServiceArgs
|
||||
extends Omit<LanguageServiceSingleMeasurement, "quickInfoDuration" | "completionsDuration"> {
|
||||
packageDirectory: string;
|
||||
}
|
||||
|
||||
@@ -21,18 +22,18 @@ function isMeasureLanguageServiceArgs(_: any): _ is MeasureLanguageServiceChildP
|
||||
|
||||
if (!module.parent) {
|
||||
if (!process.send) {
|
||||
throw new Error('Process was not started as a forked process');
|
||||
throw new Error("Process was not started as a forked process");
|
||||
}
|
||||
|
||||
let ts: typeof import('typescript') | undefined;
|
||||
let ts: typeof import("typescript") | undefined;
|
||||
let commandLine: ParsedCommandLine | undefined;
|
||||
let languageServiceHost: LanguageServiceHost | undefined;
|
||||
let languageService: LanguageService | undefined;
|
||||
|
||||
process.on('message', async (message: unknown) => {
|
||||
process.on("message", async (message: unknown) => {
|
||||
if (isMeasureLanguageServiceArgs(message)) {
|
||||
if (!ts || !commandLine || !languageServiceHost || !languageService) {
|
||||
ts = await import(message.tsPath) as typeof import('typescript');
|
||||
ts = (await import(message.tsPath)) as typeof import("typescript");
|
||||
commandLine = getParsedCommandLineForPackage(ts, message.packageDirectory);
|
||||
languageServiceHost = createLanguageServiceHost(ts, commandLine.options, commandLine.fileNames);
|
||||
languageService = ts.createLanguageService(languageServiceHost);
|
||||
@@ -44,33 +45,35 @@ if (!module.parent) {
|
||||
const positionMeasurement = await measureLanguageService(languageService, message);
|
||||
process.send!(positionMeasurement);
|
||||
} else {
|
||||
throw new Error('Invalid command-line arguments');
|
||||
throw new Error("Invalid command-line arguments");
|
||||
}
|
||||
});
|
||||
|
||||
process.on('unhandledRejection', (err) => {
|
||||
process.on("unhandledRejection", err => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
async function measureLanguageService(languageService: LanguageService, args: MeasureLanguageServiceArgs): Promise<LanguageServiceSingleMeasurement> {
|
||||
async function measureLanguageService(
|
||||
languageService: LanguageService,
|
||||
args: MeasureLanguageServiceArgs
|
||||
): Promise<LanguageServiceSingleMeasurement> {
|
||||
return {
|
||||
fileName: args.fileName,
|
||||
start: args.start,
|
||||
...measureAtPosition(args.fileName, args.start),
|
||||
...measureAtPosition(args.fileName, args.start)
|
||||
};
|
||||
|
||||
function measureAtPosition(
|
||||
fileName: string,
|
||||
position: number,
|
||||
): Pick<LanguageServiceSingleMeasurement, 'quickInfoDuration' | 'completionsDuration'> {
|
||||
|
||||
position: number
|
||||
): Pick<LanguageServiceSingleMeasurement, "quickInfoDuration" | "completionsDuration"> {
|
||||
let quickInfoDuration = NaN;
|
||||
let completionsDuration = NaN;
|
||||
const observer = new PerformanceObserver((list) => {
|
||||
const completionsMeasurement = list.getEntriesByName('completionsMeasurement')[0];
|
||||
const quickInfoMeasurement = list.getEntriesByName('quickInfoMeasurement')[0];
|
||||
const observer = new PerformanceObserver(list => {
|
||||
const completionsMeasurement = list.getEntriesByName("completionsMeasurement")[0];
|
||||
const quickInfoMeasurement = list.getEntriesByName("quickInfoMeasurement")[0];
|
||||
if (completionsMeasurement) {
|
||||
completionsDuration = completionsMeasurement.duration;
|
||||
}
|
||||
@@ -79,32 +82,32 @@ async function measureLanguageService(languageService: LanguageService, args: Me
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe({ entryTypes: ['measure'] });
|
||||
observer.observe({ entryTypes: ["measure"] });
|
||||
getCompletionsAtPosition(languageService, fileName, position);
|
||||
getQuickInfoAtPosition(languageService, fileName, position);
|
||||
assert.ok(!isNaN(quickInfoDuration), 'No measurement was recorded for quick info');
|
||||
assert.ok(!isNaN(completionsDuration), 'No measurement was recorded for completions');
|
||||
assert.ok(!isNaN(quickInfoDuration), "No measurement was recorded for quick info");
|
||||
assert.ok(!isNaN(completionsDuration), "No measurement was recorded for completions");
|
||||
observer.disconnect();
|
||||
|
||||
return {
|
||||
quickInfoDuration,
|
||||
completionsDuration,
|
||||
completionsDuration
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getCompletionsAtPosition(languageService: LanguageService, fileName: string, pos: number): boolean {
|
||||
performance.mark('beforeCompletions');
|
||||
performance.mark("beforeCompletions");
|
||||
const completions = languageService.getCompletionsAtPosition(fileName, pos, undefined);
|
||||
performance.mark('afterCompletions');
|
||||
performance.measure('completionsMeasurement', 'beforeCompletions', 'afterCompletions');
|
||||
performance.mark("afterCompletions");
|
||||
performance.measure("completionsMeasurement", "beforeCompletions", "afterCompletions");
|
||||
return !!completions && completions.entries.length > 0;
|
||||
}
|
||||
|
||||
function getQuickInfoAtPosition(languageService: LanguageService, fileName: string, pos: number): boolean {
|
||||
performance.mark('beforeQuickInfo');
|
||||
performance.mark("beforeQuickInfo");
|
||||
const quickInfo = languageService.getQuickInfoAtPosition(fileName, pos);
|
||||
performance.mark('afterQuickInfo');
|
||||
performance.measure('quickInfoMeasurement', 'beforeQuickInfo', 'afterQuickInfo');
|
||||
performance.mark("afterQuickInfo");
|
||||
performance.measure("quickInfoMeasurement", "beforeQuickInfo", "afterQuickInfo");
|
||||
return !!quickInfo;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import { execSync } from 'child_process';
|
||||
import { PerformanceObserver, performance } from 'perf_hooks';
|
||||
import { AllPackages } from 'types-publisher/bin/lib/packages';
|
||||
import { Semver } from 'types-publisher/bin/lib/versions';
|
||||
import { Node, SourceFile, Extension, CompilerOptions } from 'typescript';
|
||||
import { LanguageServiceBenchmark, PackageBenchmark, LanguageServiceSingleMeasurement, toPackageKey } from '../common';
|
||||
import { installDependencies } from './installDependencies';
|
||||
import { getParsedCommandLineForPackage } from './getParsedCommandLineForPackage';
|
||||
import { runWithListeningChildProcesses, runWithChildProcesses } from 'types-publisher/bin/util/util';
|
||||
import { measureLanguageServiceWorkerFilename, MeasureLanguageServiceChildProcessArgs } from './measureLanguageServiceWorker';
|
||||
import { MeasureBatchCompilationChildProcessArgs, measureBatchCompilationWorkerFilename, MeasureBatchCompilationChildProcessResult } from './measureBatchCompilationWorker';
|
||||
import * as os from "os";
|
||||
import * as path from "path";
|
||||
import { execSync } from "child_process";
|
||||
import { PerformanceObserver, performance } from "perf_hooks";
|
||||
import { Node, SourceFile, Extension, CompilerOptions } from "typescript";
|
||||
import { LanguageServiceBenchmark, PackageBenchmark, LanguageServiceSingleMeasurement, toPackageKey } from "../common";
|
||||
import { installDependencies } from "./installDependencies";
|
||||
import { getParsedCommandLineForPackage } from "./getParsedCommandLineForPackage";
|
||||
import {
|
||||
measureLanguageServiceWorkerFilename,
|
||||
MeasureLanguageServiceChildProcessArgs
|
||||
} from "./measureLanguageServiceWorker";
|
||||
import {
|
||||
MeasureBatchCompilationChildProcessArgs,
|
||||
measureBatchCompilationWorkerFilename,
|
||||
MeasureBatchCompilationChildProcessResult
|
||||
} from "./measureBatchCompilationWorker";
|
||||
import { AllPackages, parseVersionFromDirectoryName } from "@definitelytyped/definitions-parser";
|
||||
import { runWithListeningChildProcesses, runWithChildProcesses, Semver } from "@definitelytyped/utils";
|
||||
|
||||
export interface MeasurePerfOptions {
|
||||
packageName: string;
|
||||
@@ -23,7 +29,7 @@ export interface MeasurePerfOptions {
|
||||
iterations: number;
|
||||
allPackages: AllPackages;
|
||||
tsPath: string;
|
||||
ts: typeof import('typescript');
|
||||
ts: typeof import("typescript");
|
||||
batchRunStart: Date;
|
||||
failOnErrors?: boolean;
|
||||
}
|
||||
@@ -41,22 +47,26 @@ export async function measurePerf({
|
||||
tsPath,
|
||||
ts,
|
||||
batchRunStart,
|
||||
failOnErrors,
|
||||
failOnErrors
|
||||
}: MeasurePerfOptions) {
|
||||
let duration = NaN;
|
||||
const sourceVersion = execSync('git rev-parse HEAD', { cwd: definitelyTypedRootPath, encoding: 'utf8' }).trim();
|
||||
const sourceVersion = execSync("git rev-parse HEAD", { cwd: definitelyTypedRootPath, encoding: "utf8" }).trim();
|
||||
const observer = new PerformanceObserver(list => {
|
||||
const totalMeasurement = list.getEntriesByName('benchmark')[0];
|
||||
const totalMeasurement = list.getEntriesByName("benchmark")[0];
|
||||
duration = totalMeasurement.duration;
|
||||
});
|
||||
|
||||
observer.observe({ entryTypes: ['measure'] });
|
||||
performance.mark('benchmarkStart');
|
||||
const typesPath = path.join(definitelyTypedRootPath, 'types');
|
||||
const typings = allPackages.getTypingsData({ name: packageName, majorVersion: parseInt(packageVersion, 10) || '*' });
|
||||
observer.observe({ entryTypes: ["measure"] });
|
||||
performance.mark("benchmarkStart");
|
||||
const typesPath = path.join(definitelyTypedRootPath, "types");
|
||||
const version = parseVersionFromDirectoryName(packageVersion) || "*";
|
||||
const typings = allPackages.getTypingsData({
|
||||
name: packageName,
|
||||
version
|
||||
});
|
||||
const packagePath = path.join(typesPath, typings.subDirectoryPath);
|
||||
const typesVersion = getLatestTypesVersionForTypeScriptVersion(typings.typesVersions, typeScriptVersion);
|
||||
const latestTSTypesDir = path.resolve(packagePath, typesVersion ? `ts${typesVersion}` : '.');
|
||||
const latestTSTypesDir = path.resolve(packagePath, typesVersion ? `ts${typesVersion}` : ".");
|
||||
await installDependencies(allPackages, typings.id, typesPath);
|
||||
|
||||
const commandLine = getParsedCommandLineForPackage(ts, latestTSTypesDir);
|
||||
@@ -67,7 +77,11 @@ export async function measurePerf({
|
||||
let languageServiceCrashed = false;
|
||||
const testMatrix = createLanguageServiceTestMatrix(testPaths, latestTSTypesDir, commandLine.options, iterations);
|
||||
if (progress) {
|
||||
updateProgress(`${toPackageKey(packageName, packageVersion)}: benchmarking over ${nProcesses} processes`, 0, testMatrix.inputs.length);
|
||||
updateProgress(
|
||||
`${toPackageKey(packageName, version)}: benchmarking over ${nProcesses} processes`,
|
||||
0,
|
||||
testMatrix.inputs.length
|
||||
);
|
||||
}
|
||||
|
||||
await runWithListeningChildProcesses({
|
||||
@@ -80,33 +94,34 @@ export async function measurePerf({
|
||||
softTimeoutMs: maxRunSeconds * 1000,
|
||||
handleCrash: input => {
|
||||
languageServiceCrashed = true;
|
||||
console.error('Failed measurement on request:', JSON.stringify(input, undefined, 2));
|
||||
console.error("Failed measurement on request:", JSON.stringify(input, undefined, 2));
|
||||
},
|
||||
handleOutput: (measurement: LanguageServiceSingleMeasurement) => {
|
||||
testMatrix.addMeasurement(measurement);
|
||||
done++;
|
||||
if (progress) {
|
||||
updateProgress(
|
||||
`${toPackageKey(packageName, packageVersion)}: benchmarking over ${nProcesses} processes`,
|
||||
`${toPackageKey(packageName, version)}: benchmarking over ${nProcesses} processes`,
|
||||
done,
|
||||
testMatrix.inputs.length);
|
||||
testMatrix.inputs.length
|
||||
);
|
||||
} else if (Date.now() - lastUpdate > 1000 * 60 * 5) {
|
||||
// Log every 5 minutes or so to make sure Pipelines doesn’t shut us down
|
||||
console.log((100 * done / testMatrix.inputs.length).toFixed(1) + '% done...');
|
||||
console.log(((100 * done) / testMatrix.inputs.length).toFixed(1) + "% done...");
|
||||
lastUpdate = Date.now();
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
if (progress && done !== testMatrix.inputs.length) {
|
||||
updateProgress(`${toPackageKey(packageName, packageVersion)}: timed out`, done, testMatrix.inputs.length);
|
||||
updateProgress(`${toPackageKey(packageName, version)}: timed out`, done, testMatrix.inputs.length);
|
||||
process.stdout.write(os.EOL);
|
||||
}
|
||||
|
||||
const batchCompilationInput: MeasureBatchCompilationChildProcessArgs = {
|
||||
tsPath,
|
||||
fileNames: commandLine.fileNames,
|
||||
options: commandLine.options,
|
||||
options: commandLine.options
|
||||
};
|
||||
|
||||
let batchCompilationResult: MeasureBatchCompilationChildProcessResult | undefined;
|
||||
@@ -117,15 +132,15 @@ export async function measurePerf({
|
||||
nProcesses: 1,
|
||||
handleOutput: (result: MeasureBatchCompilationChildProcessResult) => {
|
||||
batchCompilationResult = result;
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
if (!batchCompilationResult) {
|
||||
throw new Error('Failed to get batch compilation metrics');
|
||||
throw new Error("Failed to get batch compilation metrics");
|
||||
}
|
||||
|
||||
performance.mark('benchmarkEnd');
|
||||
performance.measure('benchmark', 'benchmarkStart', 'benchmarkEnd');
|
||||
|
||||
performance.mark("benchmarkEnd");
|
||||
performance.measure("benchmark", "benchmarkStart", "benchmarkEnd");
|
||||
|
||||
const measurement: PackageBenchmark = {
|
||||
...batchCompilationResult,
|
||||
@@ -139,7 +154,7 @@ export async function measurePerf({
|
||||
requestedLanguageServiceTestIterations: iterations,
|
||||
languageServiceCrashed,
|
||||
testIdentifierCount: testMatrix.uniquePositionCount,
|
||||
batchRunStart,
|
||||
batchRunStart
|
||||
};
|
||||
|
||||
return measurement;
|
||||
@@ -149,8 +164,7 @@ export async function measurePerf({
|
||||
ts.forEachChild(sourceFile, function visit(node) {
|
||||
if (ts.isIdentifier(node)) {
|
||||
identifiers.push(node);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
ts.forEachChild(node, visit);
|
||||
}
|
||||
});
|
||||
@@ -160,7 +174,7 @@ export async function measurePerf({
|
||||
function getTestFileNames(fileNames: readonly string[]) {
|
||||
return fileNames.filter(name => {
|
||||
const ext = path.extname(name);
|
||||
return (ext === Extension.Ts || ext === Extension.Tsx) && !name.endsWith(Extension.Dts);
|
||||
return (ext === Extension.Ts || ext === Extension.Tsx) && !name.endsWith(Extension.Dts);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -179,7 +193,8 @@ export async function measurePerf({
|
||||
const sourceFile = ts.createSourceFile(
|
||||
testPath,
|
||||
ts.sys.readFile(testPath)!,
|
||||
compilerOptions.target || ts.ScriptTarget.Latest);
|
||||
compilerOptions.target || ts.ScriptTarget.Latest
|
||||
);
|
||||
// Reverse: more complex examples are usually near the end of test files,
|
||||
// so prioritize those.
|
||||
const identifiers = getIdentifiers(sourceFile).reverse();
|
||||
@@ -200,7 +215,7 @@ export async function measurePerf({
|
||||
line: lineAndCharacter.line + 1,
|
||||
offset: lineAndCharacter.character + 1,
|
||||
completionsDurations: [],
|
||||
quickInfoDurations: [],
|
||||
quickInfoDurations: []
|
||||
};
|
||||
positionMap.set(start, benchmark);
|
||||
}
|
||||
@@ -208,7 +223,7 @@ export async function measurePerf({
|
||||
fileName: testPath,
|
||||
start,
|
||||
packageDirectory,
|
||||
tsPath,
|
||||
tsPath
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -223,30 +238,38 @@ export async function measurePerf({
|
||||
},
|
||||
getAllBenchmarks: () => {
|
||||
return Array.prototype.concat
|
||||
.apply([], Array.from(fileMap.values()).map(map => Array.from(map.values())))
|
||||
.filter((benchmark: LanguageServiceBenchmark) =>
|
||||
benchmark.completionsDurations.length > 0 ||
|
||||
benchmark.quickInfoDurations.length > 0);
|
||||
},
|
||||
.apply(
|
||||
[],
|
||||
Array.from(fileMap.values()).map(map => Array.from(map.values()))
|
||||
)
|
||||
.filter(
|
||||
(benchmark: LanguageServiceBenchmark) =>
|
||||
benchmark.completionsDurations.length > 0 || benchmark.quickInfoDurations.length > 0
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getLatestTypesVersionForTypeScriptVersion(typesVersions: readonly string[], typeScriptVersion: string): string | undefined {
|
||||
const tsVersion = Semver.parse(typeScriptVersion.replace(/-dev.*$/, ''));
|
||||
function getLatestTypesVersionForTypeScriptVersion(
|
||||
typesVersions: readonly string[],
|
||||
typeScriptVersion: string
|
||||
): string | undefined {
|
||||
const tsVersion = Semver.parse(typeScriptVersion.replace(/-dev.*$/, ""));
|
||||
for (let i = typesVersions.length - 1; i > 0; i--) {
|
||||
const typesVersion = Semver.parse(typesVersions[i]);
|
||||
if (tsVersion.greaterThan(typesVersion)) {
|
||||
return typesVersions[i];
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
function updateProgress(text: string, done: number, total: number) {
|
||||
const padDigits = total.toString().length - done.toString().length;
|
||||
if (done === total) {
|
||||
if (process.stdout.clearLine && process.stdout.cursorTo) {
|
||||
process.stdout.clearLine();
|
||||
process.stdout.clearLine(0);
|
||||
process.stdout.cursorTo(0);
|
||||
process.stdout.write(`${text} (✔)`);
|
||||
process.stdout.write(os.EOL);
|
||||
@@ -254,8 +277,8 @@ function updateProgress(text: string, done: number, total: number) {
|
||||
} else if (!done) {
|
||||
process.stdout.write(`${text}`);
|
||||
} else if (process.stdout.clearLine && process.stdout.cursorTo) {
|
||||
process.stdout.clearLine();
|
||||
process.stdout.clearLine(0);
|
||||
process.stdout.cursorTo(0);
|
||||
process.stdout.write(`${text} ${' '.repeat(padDigits)}(${done}/${total} trials)`);
|
||||
process.stdout.write(`${text} ${" ".repeat(padDigits)}(${done}/${total} trials)`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import * as os from 'os';
|
||||
import { PackageBenchmarkSummary, getSystemInfo } from '../common';
|
||||
import { mean, stdDev } from './utils';
|
||||
import * as os from "os";
|
||||
import { PackageBenchmarkSummary } from "../common";
|
||||
import { mean, stdDev } from "./utils";
|
||||
|
||||
function toPrecision(n: number, precision: number): string {
|
||||
return isNaN(n) ? 'N/A' : n.toPrecision(precision);
|
||||
return isNaN(n) ? "N/A" : n.toPrecision(precision);
|
||||
}
|
||||
|
||||
export function printSummary(summaries: PackageBenchmarkSummary[]) {
|
||||
@@ -13,37 +13,57 @@ export function printSummary(summaries: PackageBenchmarkSummary[]) {
|
||||
const { worst: worstQuickInfo } = quickInfo;
|
||||
const versionString = `Version ${benchmark.packageVersion}`;
|
||||
console.log(os.EOL + versionString);
|
||||
console.log('-'.repeat(versionString.length));
|
||||
console.log(' Total duration (ms): ', benchmark.benchmarkDuration);
|
||||
console.log(' Type count: ', benchmark.typeCount);
|
||||
console.log(' Memory usage: ', benchmark.memoryUsage);
|
||||
console.log("-".repeat(versionString.length));
|
||||
console.log(" Total duration (ms): ", benchmark.benchmarkDuration);
|
||||
console.log(" Type count: ", benchmark.typeCount);
|
||||
console.log(" Memory usage: ", benchmark.memoryUsage);
|
||||
if (benchmark.relationCacheSizes) {
|
||||
console.log(' Cache sizes');
|
||||
console.log(' Assignability: ', benchmark.relationCacheSizes.assignable);
|
||||
console.log(' Identity: ', benchmark.relationCacheSizes.identity);
|
||||
console.log(' Subtype: ', benchmark.relationCacheSizes.subtype);
|
||||
console.log(" Cache sizes");
|
||||
console.log(" Assignability: ", benchmark.relationCacheSizes.assignable);
|
||||
console.log(" Identity: ", benchmark.relationCacheSizes.identity);
|
||||
console.log(" Subtype: ", benchmark.relationCacheSizes.subtype);
|
||||
}
|
||||
console.log(' Completions');
|
||||
console.log(' Trials ', `${completions.trials} (sampled from ${benchmark.testIdentifierCount * benchmark.requestedLanguageServiceTestIterations})`);
|
||||
console.log(' Mean (ms): ', toPrecision(completions.mean, 6));
|
||||
console.log(' Median (ms): ', toPrecision(completions.median, 6));
|
||||
console.log(' Mean CV: ', toPrecision(completions.meanCoefficientOfVariation, 6));
|
||||
console.log(' Worst');
|
||||
console.log(' Duration (ms): ', toPrecision(mean(worstCompletion.completionsDurations), 6));
|
||||
console.log(' Trials: ', `${worstCompletion.completionsDurations.length} (wanted ${benchmark.requestedLanguageServiceTestIterations})`);
|
||||
console.log(' Std. deviation: ', toPrecision(stdDev(worstCompletion.completionsDurations), 6));
|
||||
console.log(' Identifier: ', worstCompletion.identifierText);
|
||||
console.log(' Location: ', `${worstCompletion.fileName}(${worstCompletion.line},${worstCompletion.offset})`);
|
||||
console.log(' Quick Info');
|
||||
console.log(' Trials ', `${quickInfo.trials} (sampled from ${benchmark.testIdentifierCount * benchmark.requestedLanguageServiceTestIterations})`);
|
||||
console.log(' Mean (ms): ', toPrecision(quickInfo.mean, 6));
|
||||
console.log(' Median (ms): ', toPrecision(quickInfo.median, 6));
|
||||
console.log(' Mean CV: ', toPrecision(quickInfo.meanCoefficientOfVariation, 6));
|
||||
console.log(' Worst');
|
||||
console.log(' Duration (ms): ', toPrecision(mean(worstQuickInfo.quickInfoDurations), 6));
|
||||
console.log(' Trials: ', `${worstCompletion.completionsDurations.length} (wanted ${benchmark.requestedLanguageServiceTestIterations})`);
|
||||
console.log(' Std. deviation: ', toPrecision(stdDev(worstQuickInfo.quickInfoDurations), 6));
|
||||
console.log(' Identifier: ', worstQuickInfo.identifierText);
|
||||
console.log(' Location: ', `${worstQuickInfo.fileName}(${worstQuickInfo.line},${worstQuickInfo.offset})`);
|
||||
console.log(" Completions");
|
||||
console.log(
|
||||
" Trials ",
|
||||
`${completions.trials} (sampled from ${benchmark.testIdentifierCount *
|
||||
benchmark.requestedLanguageServiceTestIterations})`
|
||||
);
|
||||
console.log(" Mean (ms): ", toPrecision(completions.mean, 6));
|
||||
console.log(" Median (ms): ", toPrecision(completions.median, 6));
|
||||
console.log(" Mean CV: ", toPrecision(completions.meanCoefficientOfVariation, 6));
|
||||
console.log(" Worst");
|
||||
console.log(" Duration (ms): ", toPrecision(mean(worstCompletion.completionsDurations), 6));
|
||||
console.log(
|
||||
" Trials: ",
|
||||
`${worstCompletion.completionsDurations.length} (wanted ${benchmark.requestedLanguageServiceTestIterations})`
|
||||
);
|
||||
console.log(" Std. deviation: ", toPrecision(stdDev(worstCompletion.completionsDurations), 6));
|
||||
console.log(" Identifier: ", worstCompletion.identifierText);
|
||||
console.log(
|
||||
" Location: ",
|
||||
`${worstCompletion.fileName}(${worstCompletion.line},${worstCompletion.offset})`
|
||||
);
|
||||
console.log(" Quick Info");
|
||||
console.log(
|
||||
" Trials ",
|
||||
`${quickInfo.trials} (sampled from ${benchmark.testIdentifierCount *
|
||||
benchmark.requestedLanguageServiceTestIterations})`
|
||||
);
|
||||
console.log(" Mean (ms): ", toPrecision(quickInfo.mean, 6));
|
||||
console.log(" Median (ms): ", toPrecision(quickInfo.median, 6));
|
||||
console.log(" Mean CV: ", toPrecision(quickInfo.meanCoefficientOfVariation, 6));
|
||||
console.log(" Worst");
|
||||
console.log(" Duration (ms): ", toPrecision(mean(worstQuickInfo.quickInfoDurations), 6));
|
||||
console.log(
|
||||
" Trials: ",
|
||||
`${worstCompletion.completionsDurations.length} (wanted ${benchmark.requestedLanguageServiceTestIterations})`
|
||||
);
|
||||
console.log(" Std. deviation: ", toPrecision(stdDev(worstQuickInfo.quickInfoDurations), 6));
|
||||
console.log(" Identifier: ", worstQuickInfo.identifierText);
|
||||
console.log(
|
||||
" Location: ",
|
||||
`${worstQuickInfo.fileName}(${worstQuickInfo.line},${worstQuickInfo.offset})`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"composite": true
|
||||
},
|
||||
"references": [
|
||||
{ "path": "../common" }
|
||||
]
|
||||
}
|
||||
2
packages/perf/src/measure/types/dtslint.d.ts
vendored
2
packages/perf/src/measure/types/dtslint.d.ts
vendored
@@ -1,4 +1,4 @@
|
||||
declare module 'dtslint/bin/installer' {
|
||||
declare module "dtslint/bin/installer" {
|
||||
export function installAll(): Promise<void>;
|
||||
export function installNext(): Promise<void>;
|
||||
export function cleanInstalls(): Promise<void>;
|
||||
|
||||
10
packages/perf/src/measure/types/node.d.ts
vendored
10
packages/perf/src/measure/types/node.d.ts
vendored
@@ -1,10 +0,0 @@
|
||||
import 'node';
|
||||
|
||||
declare global {
|
||||
namespace NodeJS {
|
||||
interface WriteStream {
|
||||
clearLine?(): void;
|
||||
cursorTo?(pos: number): void;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ export function mean(xs: number[]) {
|
||||
}
|
||||
|
||||
export function max<T>(xs: T[], mapper: (x: T) => number): T {
|
||||
return xs.reduce((max, x) => mapper(x) > mapper(max) ? x : max, xs[0]);
|
||||
return xs.reduce((max, x) => (mapper(x) > mapper(max) ? x : max), xs[0]);
|
||||
}
|
||||
|
||||
export function median(xs: number[]) {
|
||||
@@ -30,4 +30,4 @@ export function coefficientOfVariation(xs: number[]): number {
|
||||
const standardDeviation = stdDev(xs);
|
||||
const avg = mean(xs);
|
||||
return standardDeviation / avg;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Container } from '@azure/cosmos';
|
||||
import { config, PackageBenchmarkSummary, Document, QueryResult } from '../common';
|
||||
import { Container } from "@azure/cosmos";
|
||||
import { config, PackageBenchmarkSummary, Document, QueryResult } from "../common";
|
||||
|
||||
export interface GetLatestBenchmarkOptions {
|
||||
container: Container;
|
||||
@@ -12,21 +12,26 @@ export async function getLatestBenchmark({
|
||||
container,
|
||||
packageName,
|
||||
packageVersion,
|
||||
typeScriptVersionMajorMinor,
|
||||
typeScriptVersionMajorMinor
|
||||
}: GetLatestBenchmarkOptions): Promise<QueryResult<Document<PackageBenchmarkSummary>> | undefined> {
|
||||
const response = await container.items.query({
|
||||
query:
|
||||
`SELECT TOP 1 * FROM ${config.database.packageBenchmarksContainerId} b` +
|
||||
` WHERE b.body.packageName = @packageName` +
|
||||
` AND b.body.packageVersion = @packageVersion` +
|
||||
` AND b.body.typeScriptVersionMajorMinor = @tsVersion` +
|
||||
` ORDER BY b.createdAt DESC`,
|
||||
parameters: [
|
||||
{ name: '@packageName', value: packageName },
|
||||
{ name: '@packageVersion', value: packageVersion.toString() },
|
||||
{ name: '@tsVersion', value: typeScriptVersionMajorMinor },
|
||||
],
|
||||
}, { enableCrossPartitionQuery: true }).current();
|
||||
const response = await container.items
|
||||
.query(
|
||||
{
|
||||
query:
|
||||
`SELECT TOP 1 * FROM ${config.database.packageBenchmarksContainerId} b` +
|
||||
` WHERE b.body.packageName = @packageName` +
|
||||
` AND b.body.packageVersion = @packageVersion` +
|
||||
` AND b.body.typeScriptVersionMajorMinor = @tsVersion` +
|
||||
` ORDER BY b.createdAt DESC`,
|
||||
parameters: [
|
||||
{ name: "@packageName", value: packageName },
|
||||
{ name: "@packageVersion", value: packageVersion.toString() },
|
||||
{ name: "@tsVersion", value: typeScriptVersionMajorMinor }
|
||||
]
|
||||
},
|
||||
{ enableCrossPartitionQuery: true }
|
||||
)
|
||||
.current();
|
||||
|
||||
return response.result;
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"composite": true
|
||||
},
|
||||
"references": [
|
||||
{ "path": "../common" }
|
||||
]
|
||||
}
|
||||
@@ -1,9 +1,21 @@
|
||||
import { Container, Item } from '@azure/cosmos';
|
||||
import { PackageBenchmarkSummary, createDocument, TypeScriptComparisonRun } from '../common';
|
||||
import { Container, Item } from "@azure/cosmos";
|
||||
import { PackageBenchmarkSummary, createDocument, TypeScriptComparisonRun } from "../common";
|
||||
|
||||
export async function insertDocument(comparison: TypeScriptComparisonRun, version: number, container: Container): Promise<Item>;
|
||||
export async function insertDocument(benchmark: PackageBenchmarkSummary, version: number, container: Container): Promise<Item>;
|
||||
export async function insertDocument(benchmark: PackageBenchmarkSummary | TypeScriptComparisonRun, version: number, container: Container): Promise<Item> {
|
||||
export async function insertDocument(
|
||||
comparison: TypeScriptComparisonRun,
|
||||
version: number,
|
||||
container: Container
|
||||
): Promise<Item>;
|
||||
export async function insertDocument(
|
||||
benchmark: PackageBenchmarkSummary,
|
||||
version: number,
|
||||
container: Container
|
||||
): Promise<Item>;
|
||||
export async function insertDocument(
|
||||
benchmark: PackageBenchmarkSummary | TypeScriptComparisonRun,
|
||||
version: number,
|
||||
container: Container
|
||||
): Promise<Item> {
|
||||
const response = await container.items.create(createDocument(benchmark, version));
|
||||
return response.item;
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"composite": true
|
||||
},
|
||||
"references": [
|
||||
{ "path": "../common" }
|
||||
]
|
||||
}
|
||||
@@ -1,32 +1,48 @@
|
||||
import { metrics, SignificanceLevel } from '../../src/analysis';
|
||||
import { Document, PackageBenchmarkSummary, config } from '../../src/common';
|
||||
import { metrics, SignificanceLevel } from "../../src/analysis";
|
||||
import { Document, PackageBenchmarkSummary, config } from "../../src/common";
|
||||
|
||||
describe('analysis', () => {
|
||||
describe('metrics', () => {
|
||||
test('proportionalTo significance', () => {
|
||||
const significance1 = metrics.typeCount.getSignificance(6, 1000, 6000,
|
||||
describe("analysis", () => {
|
||||
describe("metrics", () => {
|
||||
test("proportionalTo significance", () => {
|
||||
const significance1 = metrics.typeCount.getSignificance(
|
||||
6,
|
||||
1000,
|
||||
6000,
|
||||
{ body: { testIdentifierCount: 1000, typeCount: 1000 } } as Document<PackageBenchmarkSummary>,
|
||||
{ body: { testIdentifierCount: 6000, typeCount: 6000 } } as Document<PackageBenchmarkSummary>);
|
||||
{ body: { testIdentifierCount: 6000, typeCount: 6000 } } as Document<PackageBenchmarkSummary>
|
||||
);
|
||||
|
||||
expect(significance1).toBe(undefined);
|
||||
|
||||
const significance2 = metrics.typeCount.getSignificance(6, 1000, 6000,
|
||||
const significance2 = metrics.typeCount.getSignificance(
|
||||
6,
|
||||
1000,
|
||||
6000,
|
||||
{ body: { testIdentifierCount: 1000, typeCount: 1000 } } as Document<PackageBenchmarkSummary>,
|
||||
{ body: { testIdentifierCount: 1000, typeCount: 6000 } } as Document<PackageBenchmarkSummary>);
|
||||
|
||||
{ body: { testIdentifierCount: 1000, typeCount: 6000 } } as Document<PackageBenchmarkSummary>
|
||||
);
|
||||
|
||||
expect(significance2).toBe(SignificanceLevel.Warning);
|
||||
|
||||
const significance3 = metrics.typeCount.getSignificance(config.comparison.percentDiffWarningThreshold + .01, 1000, 200,
|
||||
const significance3 = metrics.typeCount.getSignificance(
|
||||
config.comparison.percentDiffWarningThreshold + 0.01,
|
||||
1000,
|
||||
200,
|
||||
{ body: { testIdentifierCount: 1000, typeCount: 1000 } } as Document<PackageBenchmarkSummary>,
|
||||
{ body: { testIdentifierCount: 5000, typeCount: 2000 } } as Document<PackageBenchmarkSummary>);
|
||||
{ body: { testIdentifierCount: 5000, typeCount: 2000 } } as Document<PackageBenchmarkSummary>
|
||||
);
|
||||
|
||||
expect(significance3).toBe(undefined);
|
||||
});
|
||||
|
||||
test('withThreshold significance', () => {
|
||||
const significance1 = metrics.typeCount.getSignificance(6, 100, 600,
|
||||
test("withThreshold significance", () => {
|
||||
const significance1 = metrics.typeCount.getSignificance(
|
||||
6,
|
||||
100,
|
||||
600,
|
||||
{ body: { testIdentifierCount: 100, typeCount: 100 } } as Document<PackageBenchmarkSummary>,
|
||||
{ body: { testIdentifierCount: 600, typeCount: 600 } } as Document<PackageBenchmarkSummary>);
|
||||
{ body: { testIdentifierCount: 600, typeCount: 600 } } as Document<PackageBenchmarkSummary>
|
||||
);
|
||||
|
||||
expect(significance1).toBe(undefined);
|
||||
});
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { summarize } from '../../src/analysis';
|
||||
const benchmark = require('./fixtures/abbrev.packageBenchmark.json');
|
||||
import { summarize } from "../../src/analysis";
|
||||
const benchmark = require("./fixtures/abbrev.packageBenchmark.json");
|
||||
|
||||
describe('analysis', () => {
|
||||
describe('summarizePackageBenchmarks', () => {
|
||||
test('snapshot', () => {
|
||||
describe("analysis", () => {
|
||||
describe("summarizePackageBenchmarks", () => {
|
||||
test("snapshot", () => {
|
||||
expect(summarize(benchmark)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
import { createPerfCommentBody, getCommentData } from '../../src/github/comment';
|
||||
import { OverallChange } from '../../src/analysis';
|
||||
import { config } from '../../src/common';
|
||||
import { IssuesListResponseItemUser } from '@octokit/rest';
|
||||
import { createPerfCommentBody, getCommentData } from "../../src/github/comment";
|
||||
import { OverallChange } from "../../src/analysis";
|
||||
import { config } from "../../src/common";
|
||||
import { IssuesListResponseItemUser } from "@octokit/rest";
|
||||
|
||||
describe('github', () => {
|
||||
describe('comment', () => {
|
||||
it('serializes -> deserializes correctly', () => {
|
||||
describe("github", () => {
|
||||
describe("comment", () => {
|
||||
it("serializes -> deserializes correctly", () => {
|
||||
const data = { benchmarks: [], overallChange: OverallChange.Better };
|
||||
const commentBody = createPerfCommentBody(data, "\n\nHello");
|
||||
expect(getCommentData({
|
||||
body: commentBody,
|
||||
user: { login: config.github.typeScriptBotUsername } as IssuesListResponseItemUser },
|
||||
)).toEqual(data);
|
||||
expect(
|
||||
getCommentData({
|
||||
body: commentBody,
|
||||
user: { login: config.github.typeScriptBotUsername } as IssuesListResponseItemUser
|
||||
})
|
||||
).toEqual(data);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { formatDiff } from '../../src/github/createTable';
|
||||
import { SignificanceLevel } from '../../src/analysis';
|
||||
import { formatDiff } from "../../src/github/createTable";
|
||||
import { SignificanceLevel } from "../../src/analysis";
|
||||
|
||||
describe('github', () => {
|
||||
describe('createTable', () => {
|
||||
describe('formatDiff', () => {
|
||||
it('returns an empty string when diff is not representable', () => {
|
||||
expect(formatDiff(Number.POSITIVE_INFINITY, SignificanceLevel.Alert)).toBe('');
|
||||
expect(formatDiff(Number.NaN, SignificanceLevel.Alert)).toBe('');
|
||||
describe("github", () => {
|
||||
describe("createTable", () => {
|
||||
describe("formatDiff", () => {
|
||||
it("returns an empty string when diff is not representable", () => {
|
||||
expect(formatDiff(Number.POSITIVE_INFINITY, SignificanceLevel.Alert)).toBe("");
|
||||
expect(formatDiff(Number.NaN, SignificanceLevel.Alert)).toBe("");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [
|
||||
{ "path": "../definitions-parser" },
|
||||
{ "path": "../utils" },
|
||||
]
|
||||
}
|
||||
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [
|
||||
{ "path": "../definitions-parser" },
|
||||
{ "path": "../utils" }
|
||||
]
|
||||
}
|
||||
|
||||
1107
packages/publisher/package-lock.json
generated
1107
packages/publisher/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -10,8 +10,6 @@
|
||||
"@definitelytyped/header-parser": "^0.0.22",
|
||||
"@definitelytyped/utils": "^0.0.22",
|
||||
"@octokit/rest": "^16.1.0",
|
||||
"@types/node": "^12.12.29",
|
||||
"@types/tar-stream": "^1.6.0",
|
||||
"adal-node": "^0.1.22",
|
||||
"applicationinsights": "^1.0.7",
|
||||
"azure-keyvault": "^3.0.4",
|
||||
@@ -21,11 +19,8 @@
|
||||
"fstream": "^1.0.12",
|
||||
"longjohn": "^0.2.11",
|
||||
"npm": "^6.13.4",
|
||||
"npm-registry-client": "^8.1.0",
|
||||
"oboe": "^2.1.3",
|
||||
"source-map-support": "^0.4.0",
|
||||
"tar": "^2.2.2",
|
||||
"tar-stream": "^1.6.2",
|
||||
"travis-fold": "^0.1.2",
|
||||
"typescript": "next",
|
||||
"yargs": "^8.0.2"
|
||||
@@ -33,9 +28,9 @@
|
||||
"devDependencies": {
|
||||
"@types/fs-extra": "4.0.0",
|
||||
"@types/mz": "^0.0.31",
|
||||
"@types/node": "^12.12.29",
|
||||
"@types/oboe": "^2.0.28",
|
||||
"@types/source-map-support": "^0.4.0",
|
||||
"@types/tar": "^1.0.27",
|
||||
"@types/travis-fold": "^0.1.0",
|
||||
"@types/yargs": "^8.0.1"
|
||||
},
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import assert = require("assert");
|
||||
|
||||
import { defaultLocalOptions } from "./lib/common";
|
||||
import { CachedNpmInfoClient, NpmInfoVersion, UncachedNpmInfoClient, withNpmCache } from "./lib/npm-client";
|
||||
import { ChangedPackages, ChangedPackagesJson, ChangedTypingJson, versionsFilename } from "./lib/versions";
|
||||
import {
|
||||
getDefinitelyTyped,
|
||||
@@ -19,7 +18,11 @@ import {
|
||||
loggerWithErrors,
|
||||
FS,
|
||||
LoggerWithErrors,
|
||||
Semver
|
||||
Semver,
|
||||
UncachedNpmInfoClient,
|
||||
withNpmCache,
|
||||
CachedNpmInfoClient,
|
||||
NpmInfoVersion
|
||||
} from "@definitelytyped/utils";
|
||||
|
||||
if (!module.parent) {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { defaultLocalOptions } from "./lib/common";
|
||||
import { NpmInfoRawVersions, NpmInfoVersion, UncachedNpmInfoClient } from "./lib/npm-client";
|
||||
import {
|
||||
getDefinitelyTyped,
|
||||
AllPackages,
|
||||
@@ -19,7 +18,10 @@ import {
|
||||
logger,
|
||||
writeLog,
|
||||
Logger,
|
||||
Semver
|
||||
Semver,
|
||||
UncachedNpmInfoClient,
|
||||
NpmInfoRawVersions,
|
||||
NpmInfoVersion
|
||||
} from "@definitelytyped/utils";
|
||||
|
||||
if (!module.parent) {
|
||||
|
||||
@@ -2,9 +2,14 @@ import assert = require("assert");
|
||||
import oboe = require("oboe");
|
||||
|
||||
import { packageHasTypes } from "./check-parse-results";
|
||||
import { UncachedNpmInfoClient } from "./lib/npm-client";
|
||||
import { npmRegistry } from "./lib/settings";
|
||||
import { filterNAtATimeOrdered, logUncaughtErrors, ProgressBar, strProgress } from "@definitelytyped/utils";
|
||||
import {
|
||||
filterNAtATimeOrdered,
|
||||
logUncaughtErrors,
|
||||
ProgressBar,
|
||||
strProgress,
|
||||
UncachedNpmInfoClient,
|
||||
npmRegistry
|
||||
} from "@definitelytyped/utils";
|
||||
import { defaultLocalOptions } from "./lib/common";
|
||||
import { ParseDefinitionsOptions, writeDataFile } from "@definitelytyped/definitions-parser";
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import * as yargs from "yargs";
|
||||
|
||||
import { getDefinitelyTyped, AllPackages, writeDataFile, TypingsData } from "@definitelytyped/definitions-parser";
|
||||
import { loggerWithErrors, logUncaughtErrors } from "@definitelytyped/utils";
|
||||
import { UncachedNpmInfoClient } from "./lib/npm-client";
|
||||
import { loggerWithErrors, logUncaughtErrors, UncachedNpmInfoClient } from "@definitelytyped/utils";
|
||||
import { defaultLocalOptions } from "./lib/common";
|
||||
|
||||
if (!module.parent) {
|
||||
|
||||
@@ -5,12 +5,18 @@ import calculateVersions from "./calculate-versions";
|
||||
import { clean } from "./clean";
|
||||
import createSearchIndex from "./create-search-index";
|
||||
import generatePackages from "./generate-packages";
|
||||
import { UncachedNpmInfoClient } from "./lib/npm-client";
|
||||
import publishPackages from "./publish-packages";
|
||||
import publishRegistry from "./publish-registry";
|
||||
import uploadBlobsAndUpdateIssue from "./upload-blobs";
|
||||
import { getDefinitelyTyped, parseDefinitions, ParseDefinitionsOptions } from "@definitelytyped/definitions-parser";
|
||||
import { Fetcher, logUncaughtErrors, loggerWithErrors, LoggerWithErrors, assertDefined } from "@definitelytyped/utils";
|
||||
import {
|
||||
Fetcher,
|
||||
logUncaughtErrors,
|
||||
loggerWithErrors,
|
||||
LoggerWithErrors,
|
||||
assertDefined,
|
||||
UncachedNpmInfoClient
|
||||
} from "@definitelytyped/utils";
|
||||
import { currentTimeStamp, numberOfOsProcesses } from "./util/util";
|
||||
import validate from "./validate";
|
||||
import { defaultLocalOptions } from "./lib/common";
|
||||
|
||||
@@ -3,10 +3,8 @@ import { emptyDir, mkdir, mkdirp, readFileSync } from "fs-extra";
|
||||
import * as path from "path";
|
||||
import * as yargs from "yargs";
|
||||
|
||||
import { defaultLocalOptions, Registry } from "./lib/common";
|
||||
import { CachedNpmInfoClient, UncachedNpmInfoClient, withNpmCache, skipBadPublishes } from "./lib/npm-client";
|
||||
import { defaultLocalOptions } from "./lib/common";
|
||||
import { outputDirPath, sourceBranch } from "./lib/settings";
|
||||
import { writeTgz } from "./util/tgz";
|
||||
import {
|
||||
assertNever,
|
||||
joinPaths,
|
||||
@@ -17,7 +15,12 @@ import {
|
||||
logger,
|
||||
writeLog,
|
||||
writeFile,
|
||||
Logger
|
||||
Logger,
|
||||
writeTgz,
|
||||
withNpmCache,
|
||||
UncachedNpmInfoClient,
|
||||
Registry,
|
||||
CachedNpmInfoClient
|
||||
} from "@definitelytyped/utils";
|
||||
import {
|
||||
getDefinitelyTyped,
|
||||
@@ -33,6 +36,7 @@ import {
|
||||
} from "@definitelytyped/definitions-parser";
|
||||
import { readChangedPackages, ChangedPackages } from "./lib/versions";
|
||||
import { outputDirectory } from "./util/util";
|
||||
import { skipBadPublishes } from "./lib/npm";
|
||||
|
||||
const mitLicense = readFileSync(joinPaths(__dirname, "..", "LICENSE"), "utf-8");
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
export { withNpmCache, CachedNpmInfoClient, NpmPublishClient, UncachedNpmInfoClient } from "./lib/npm-client";
|
||||
export { clean } from "./clean";
|
||||
export { getLatestTypingVersion } from "./calculate-versions";
|
||||
export { parseNProcesses } from "./tester/test-runner";
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { BlobService, common, createBlobService, ErrorOrResponse, ErrorOrResult } from "azure-storage";
|
||||
import * as fs from "fs";
|
||||
import * as https from "https";
|
||||
import { gzip, unGzip } from "../util/tgz";
|
||||
import { getSecret, Secret } from "./secrets";
|
||||
import { azureContainer, azureStorageAccount } from "./settings";
|
||||
import { streamOfString, streamDone, stringOfStream, parseJson } from "@definitelytyped/utils";
|
||||
import { streamOfString, streamDone, stringOfStream, parseJson, gzip, unGzip } from "@definitelytyped/utils";
|
||||
|
||||
export default class BlobWriter {
|
||||
static async create(): Promise<BlobWriter> {
|
||||
|
||||
@@ -6,14 +6,6 @@ if (process.env.LONGJOHN) {
|
||||
longjohn.async_trace_limit = -1; // unlimited
|
||||
}
|
||||
|
||||
/** Which registry to publish to */
|
||||
export enum Registry {
|
||||
/** types-registry and @types/* on NPM */
|
||||
NPM,
|
||||
/** @definitelytyped/types-registry and @types/* on Github */
|
||||
Github
|
||||
}
|
||||
|
||||
export interface TesterOptions extends ParseDefinitionsOptions {
|
||||
// Tester can only run on files stored on-disk.
|
||||
readonly definitelyTypedPath: string;
|
||||
|
||||
44
packages/publisher/src/lib/npm.ts
Normal file
44
packages/publisher/src/lib/npm.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { NotNeededPackage } from "@definitelytyped/definitions-parser";
|
||||
import { Logger, assertDefined, Semver, best, CachedNpmInfoClient } from "@definitelytyped/utils";
|
||||
|
||||
/**
|
||||
* When we fail to publish a deprecated package, it leaves behind an entry in the time property.
|
||||
* So the keys of 'time' give the actual 'latest'.
|
||||
* If that's not equal to the expected latest, try again by bumping the patch version of the last attempt by 1.
|
||||
*/
|
||||
export function skipBadPublishes(pkg: NotNeededPackage, client: CachedNpmInfoClient, log: Logger) {
|
||||
// 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({
|
||||
asOfVersion: plusOne.versionString,
|
||||
libraryName: pkg.libraryName,
|
||||
sourceRepoURL: pkg.sourceRepoURL,
|
||||
typingsPackageName: pkg.name
|
||||
});
|
||||
}
|
||||
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()
|
||||
);
|
||||
if (!actual) {
|
||||
throw new Error("failed to find actual latest");
|
||||
}
|
||||
return actual[0];
|
||||
}
|
||||
@@ -1,7 +1,12 @@
|
||||
import assert = require("assert");
|
||||
import { Logger, joinPaths, readFileAndWarn, TypeScriptVersion } from "@definitelytyped/utils";
|
||||
import { Registry } from "./common";
|
||||
import { NpmPublishClient } from "./npm-client";
|
||||
import {
|
||||
Logger,
|
||||
joinPaths,
|
||||
readFileAndWarn,
|
||||
TypeScriptVersion,
|
||||
NpmPublishClient,
|
||||
Registry
|
||||
} from "@definitelytyped/utils";
|
||||
import { NotNeededPackage, AnyPackage } from "@definitelytyped/definitions-parser";
|
||||
import { ChangedTyping } from "./versions";
|
||||
import { outputDirectory } from "../util/util";
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
import { join as joinPaths } from "path";
|
||||
|
||||
/** URL of the NPM registry to upload to. */
|
||||
export const npmRegistryHostName = "registry.npmjs.org";
|
||||
export const githubRegistryHostName = "npm.pkg.github.com";
|
||||
export const npmRegistry = `https://${npmRegistryHostName}/`;
|
||||
export const githubRegistry = `https://${githubRegistryHostName}/`;
|
||||
export const npmApi = "api.npmjs.org";
|
||||
|
||||
const root = joinPaths(__dirname, "..", "..");
|
||||
export const outputDirPath = joinPaths(root, "output");
|
||||
export const validateOutputPath = joinPaths(root, "validateOutput");
|
||||
@@ -25,5 +18,3 @@ export const azureContainer = "typespublisher";
|
||||
export const azureKeyvault = "https://types-publisher-keys.vault.azure.net";
|
||||
/** Issue in types-publisher that we will use to report webhook errors. */
|
||||
export const errorsIssue = "Microsoft/types-publisher/issues/40";
|
||||
|
||||
export const typesDirectoryName = "types";
|
||||
|
||||
@@ -1,12 +1,23 @@
|
||||
import appInsights = require("applicationinsights");
|
||||
import * as yargs from "yargs";
|
||||
|
||||
import { defaultLocalOptions, Registry } from "./lib/common";
|
||||
import { NpmPublishClient, UncachedNpmInfoClient, withNpmCache, skipBadPublishes } from "./lib/npm-client";
|
||||
import { defaultLocalOptions } from "./lib/common";
|
||||
import { deprecateNotNeededPackage, publishNotNeededPackage, publishTypingsPackage } from "./lib/package-publisher";
|
||||
import { getDefinitelyTyped, AllPackages } from "@definitelytyped/definitions-parser";
|
||||
import { loggerWithErrors, logUncaughtErrors, logger, Fetcher, writeLog } from "@definitelytyped/utils";
|
||||
import {
|
||||
loggerWithErrors,
|
||||
logUncaughtErrors,
|
||||
logger,
|
||||
Fetcher,
|
||||
writeLog,
|
||||
NpmPublishClient,
|
||||
Registry,
|
||||
withNpmCache,
|
||||
UncachedNpmInfoClient
|
||||
} from "@definitelytyped/utils";
|
||||
import { readChangedPackages, ChangedPackages } from "./lib/versions";
|
||||
import { skipBadPublishes } from "./lib/npm";
|
||||
import { getSecret, Secret } from "./lib/secrets";
|
||||
|
||||
if (!module.parent) {
|
||||
const dry = !!yargs.argv.dry;
|
||||
@@ -20,7 +31,11 @@ if (!module.parent) {
|
||||
const log = logger()[0];
|
||||
try {
|
||||
await deprecateNotNeededPackage(
|
||||
await NpmPublishClient.create(undefined, Registry.Github),
|
||||
await NpmPublishClient.create(
|
||||
await getSecret(Secret.GITHUB_PUBLISH_ACCESS_TOKEN),
|
||||
undefined,
|
||||
Registry.Github
|
||||
),
|
||||
AllPackages.readSingleNotNeeded(deprecateName, dt),
|
||||
false /*dry*/,
|
||||
log
|
||||
@@ -30,7 +45,7 @@ if (!module.parent) {
|
||||
log("publishing to github failed: " + e.toString());
|
||||
}
|
||||
await deprecateNotNeededPackage(
|
||||
await NpmPublishClient.create(undefined, Registry.NPM),
|
||||
await NpmPublishClient.create(await getSecret(Secret.NPM_TOKEN), undefined, Registry.NPM),
|
||||
AllPackages.readSingleNotNeeded(deprecateName, dt),
|
||||
/*dry*/ false,
|
||||
log
|
||||
@@ -59,8 +74,12 @@ export default async function publishPackages(
|
||||
log("=== Publishing packages ===");
|
||||
}
|
||||
|
||||
const client = await NpmPublishClient.create(undefined, Registry.NPM);
|
||||
const ghClient = await NpmPublishClient.create(undefined, Registry.Github);
|
||||
const client = await NpmPublishClient.create(await getSecret(Secret.NPM_TOKEN), undefined, Registry.NPM);
|
||||
const ghClient = await NpmPublishClient.create(
|
||||
await getSecret(Secret.GITHUB_PUBLISH_ACCESS_TOKEN),
|
||||
undefined,
|
||||
Registry.Github
|
||||
);
|
||||
|
||||
for (const cp of changedPackages.changedTypings) {
|
||||
log(`Publishing ${cp.pkg.desc}...`);
|
||||
|
||||
@@ -2,8 +2,7 @@ import assert = require("assert");
|
||||
import { emptyDir } from "fs-extra";
|
||||
import * as yargs from "yargs";
|
||||
|
||||
import { defaultLocalOptions, Registry as RegistryName } from "./lib/common";
|
||||
import { CachedNpmInfoClient, NpmPublishClient, UncachedNpmInfoClient, withNpmCache } from "./lib/npm-client";
|
||||
import { defaultLocalOptions } from "./lib/common";
|
||||
import { outputDirPath, validateOutputPath } from "./lib/settings";
|
||||
import {
|
||||
getDefinitelyTyped,
|
||||
@@ -30,8 +29,14 @@ import {
|
||||
sleep,
|
||||
npmInstallFlags,
|
||||
readJson,
|
||||
Semver
|
||||
Semver,
|
||||
UncachedNpmInfoClient,
|
||||
withNpmCache,
|
||||
NpmPublishClient,
|
||||
CachedNpmInfoClient,
|
||||
Registry as RegistryName
|
||||
} from "@definitelytyped/utils";
|
||||
import { getSecret, Secret } from "./lib/secrets";
|
||||
|
||||
const typesRegistry = "types-registry";
|
||||
const registryOutputPath = joinPaths(outputDirPath, typesRegistry);
|
||||
@@ -85,7 +90,12 @@ export default async function publishRegistry(
|
||||
const packageJson = generatePackageJson(packageName, registryName, newVersion, newContentHash);
|
||||
await generate(registry, packageJson);
|
||||
|
||||
const publishClient = () => NpmPublishClient.create({ defaultTag: "next" }, registryName);
|
||||
const token =
|
||||
registryName === RegistryName.Github
|
||||
? await getSecret(Secret.GITHUB_PUBLISH_ACCESS_TOKEN)
|
||||
: await getSecret(Secret.NPM_TOKEN);
|
||||
|
||||
const publishClient = () => NpmPublishClient.create(token, { defaultTag: "next" }, registryName);
|
||||
if (!highestSemverVersion.equals(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.
|
||||
|
||||
@@ -1,41 +1,31 @@
|
||||
import assert = require("assert");
|
||||
import { existsSync, readFileSync } from "fs";
|
||||
import { pathExists, remove } from "fs-extra";
|
||||
import os = require("os");
|
||||
import * as fold from "travis-fold";
|
||||
import * as yargs from "yargs";
|
||||
import { TesterOptions, defaultLocalOptions } from "../lib/common";
|
||||
import { NpmInfo, UncachedNpmInfoClient } from "../lib/npm-client";
|
||||
import { sourceBranch, typesDirectoryName } from "../lib/settings";
|
||||
import {
|
||||
getDefinitelyTyped,
|
||||
AllPackages,
|
||||
DependencyVersion,
|
||||
formatDependencyVersion,
|
||||
NotNeededPackage,
|
||||
PackageId,
|
||||
TypingsData,
|
||||
parseVersionFromDirectoryName
|
||||
getAffectedPackagesFromDiff,
|
||||
gitChanges,
|
||||
gitDiff
|
||||
} from "@definitelytyped/definitions-parser";
|
||||
import {
|
||||
assertDefined,
|
||||
CrashRecoveryState,
|
||||
execAndThrowErrors,
|
||||
joinPaths,
|
||||
logUncaughtErrors,
|
||||
mapIterable,
|
||||
runWithListeningChildProcesses,
|
||||
loggerWithErrors,
|
||||
consoleLogger,
|
||||
FS,
|
||||
Semver,
|
||||
npmInstallFlags,
|
||||
Logger,
|
||||
flatMapIterable,
|
||||
installAllTypeScriptVersions
|
||||
} from "@definitelytyped/utils";
|
||||
|
||||
import { allDependencies, getAffectedPackages } from "./get-affected-packages";
|
||||
import { allDependencies, getAffectedPackages } from "@definitelytyped/definitions-parser/src/get-affected-packages";
|
||||
import { numberOfOsProcesses } from "../util/util";
|
||||
|
||||
const perfDir = joinPaths(os.homedir(), ".dts", "perf");
|
||||
@@ -55,11 +45,6 @@ if (!module.parent) {
|
||||
}
|
||||
}
|
||||
|
||||
export interface GitDiff {
|
||||
status: "A" | "D" | "M";
|
||||
file: string;
|
||||
}
|
||||
|
||||
async function testAffectedOnly(options: TesterOptions): Promise<void> {
|
||||
const changes = getAffectedPackages(
|
||||
await AllPackages.read(await getDefinitelyTyped(options, loggerWithErrors()[0])),
|
||||
@@ -109,109 +94,6 @@ export default async function runTests(
|
||||
await doRunTests([...changedPackages, ...dependentPackages], new Set(changedPackages), typesPath, nProcesses);
|
||||
}
|
||||
|
||||
export async function getAffectedPackagesFromDiff(
|
||||
dt: FS,
|
||||
definitelyTypedPath: string,
|
||||
selection: "all" | "affected" | RegExp
|
||||
) {
|
||||
const allPackages = await AllPackages.read(dt);
|
||||
const diffs = await gitDiff(consoleLogger.info, definitelyTypedPath);
|
||||
if (diffs.find(d => d.file === "notNeededPackages.json")) {
|
||||
const uncached = new UncachedNpmInfoClient();
|
||||
for (const deleted of getNotNeededPackages(allPackages, diffs)) {
|
||||
const source = await uncached.fetchNpmInfo(deleted.libraryName); // eg @babel/parser
|
||||
const typings = await uncached.fetchNpmInfo(deleted.fullNpmName); // eg @types/babel__parser
|
||||
checkNotNeededPackage(deleted, source, typings);
|
||||
}
|
||||
}
|
||||
|
||||
const affected =
|
||||
selection === "all"
|
||||
? { changedPackages: allPackages.allTypings(), dependentPackages: [], allPackages }
|
||||
: selection === "affected"
|
||||
? getAffectedPackages(allPackages, gitChanges(diffs))
|
||||
: {
|
||||
changedPackages: allPackages.allTypings().filter(t => selection.test(t.name)),
|
||||
dependentPackages: [],
|
||||
allPackages
|
||||
};
|
||||
|
||||
console.log(
|
||||
`Testing ${affected.changedPackages.length} changed packages: ${affected.changedPackages
|
||||
.map(t => t.desc)
|
||||
.toString()}`
|
||||
);
|
||||
console.log(
|
||||
`Testing ${affected.dependentPackages.length} dependent packages: ${affected.dependentPackages
|
||||
.map(t => t.desc)
|
||||
.toString()}`
|
||||
);
|
||||
return affected;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. find all the deleted files and group by toplevel
|
||||
* 2. Make sure that there are no packages left with deleted entries
|
||||
* 3. make sure that each toplevel deleted has a matching entry in notNeededPackages
|
||||
*/
|
||||
export function getNotNeededPackages(allPackages: AllPackages, diffs: GitDiff[]): Iterable<NotNeededPackage> {
|
||||
const deletedPackages = new Set(
|
||||
diffs
|
||||
.filter(d => d.status === "D")
|
||||
.map(
|
||||
d =>
|
||||
assertDefined(
|
||||
getDependencyFromFile(d.file),
|
||||
`Unexpected file deleted: ${d.file}
|
||||
When removing packages, you should only delete files that are a part of removed packages.`
|
||||
).name
|
||||
)
|
||||
);
|
||||
return mapIterable(deletedPackages, p => {
|
||||
if (allPackages.hasTypingFor({ name: p, version: "*" })) {
|
||||
throw new Error(`Please delete all files in ${p} when adding it to notNeededPackages.json.`);
|
||||
}
|
||||
return assertDefined(allPackages.getNotNeededPackage(p), `Deleted package ${p} is not in notNeededPackages.json.`);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. libraryName must exist on npm (SKIPPED and preferably/optionally have been the libraryName in just-deleted header)
|
||||
* (SKIPPED 2.) sourceRepoURL must exist and be the npm homepage
|
||||
* 3. asOfVersion must be newer than `@types/name@latest` on npm
|
||||
* 4. `name@asOfVersion` must exist on npm
|
||||
*
|
||||
* I skipped (2) because the cached npm info doesn't include it. I might add it later.
|
||||
*/
|
||||
export function checkNotNeededPackage(
|
||||
unneeded: NotNeededPackage,
|
||||
source: NpmInfo | undefined,
|
||||
typings: NpmInfo | undefined
|
||||
) {
|
||||
source = assertDefined(
|
||||
source,
|
||||
`The entry for ${unneeded.fullNpmName} in notNeededPackages.json has
|
||||
"libraryName": "${unneeded.libraryName}", but there is no npm package with this name.
|
||||
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.`
|
||||
)
|
||||
);
|
||||
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}.`
|
||||
);
|
||||
assert(
|
||||
source.versions.has(unneeded.version.versionString),
|
||||
`The specified version ${unneeded.version.versionString} of ${unneeded.libraryName} is not on npm.`
|
||||
);
|
||||
}
|
||||
|
||||
async function doInstalls(allPackages: AllPackages, packages: Iterable<TypingsData>, typesPath: string): Promise<void> {
|
||||
console.log("Installing NPM dependencies...");
|
||||
|
||||
@@ -346,91 +228,3 @@ async function doRunTests(
|
||||
|
||||
throw new Error(`The following packages had errors: ${allFailures.map(e => e[0]).join(", ")}`);
|
||||
}
|
||||
|
||||
/** Returns all immediate subdirectories of the root directory that have changed. */
|
||||
export function gitChanges(diffs: GitDiff[]): PackageId[] {
|
||||
const changedPackages = new Map<string, Map<string, DependencyVersion>>();
|
||||
|
||||
for (const diff of diffs) {
|
||||
const dep = getDependencyFromFile(diff.file);
|
||||
if (dep) {
|
||||
const versions = changedPackages.get(dep.name);
|
||||
if (!versions) {
|
||||
changedPackages.set(dep.name, new Map([[formatDependencyVersion(dep.version), dep.version]]));
|
||||
} else {
|
||||
versions.set(formatDependencyVersion(dep.version), dep.version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(
|
||||
flatMapIterable(changedPackages, ([name, versions]) => mapIterable(versions, ([_, version]) => ({ name, version })))
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
We have to be careful about how we get the diff because travis uses a shallow clone.
|
||||
|
||||
Travis runs:
|
||||
git clone --depth=50 https://github.com/DefinitelyTyped/DefinitelyTyped.git DefinitelyTyped
|
||||
cd DefinitelyTyped
|
||||
git fetch origin +refs/pull/123/merge
|
||||
git checkout -qf FETCH_HEAD
|
||||
|
||||
If editing this code, be sure to test on both full and shallow clones.
|
||||
*/
|
||||
export async function gitDiff(log: Logger, definitelyTypedPath: string): Promise<GitDiff[]> {
|
||||
try {
|
||||
await run(`git rev-parse --verify ${sourceBranch}`);
|
||||
// If this succeeds, we got the full clone.
|
||||
} catch (_) {
|
||||
// This is a shallow clone.
|
||||
await run(`git fetch origin ${sourceBranch}`);
|
||||
await run(`git branch ${sourceBranch} FETCH_HEAD`);
|
||||
}
|
||||
|
||||
let diff = (await run(`git diff ${sourceBranch} --name-status`)).trim();
|
||||
if (diff === "") {
|
||||
// We are probably already on master, so compare to the last commit.
|
||||
diff = (await run(`git diff ${sourceBranch}~1 --name-status`)).trim();
|
||||
}
|
||||
return diff.split("\n").map(line => {
|
||||
const [status, file] = line.split(/\s+/, 2);
|
||||
return { status: status.trim(), file: file.trim() } as GitDiff;
|
||||
});
|
||||
|
||||
async function run(cmd: string): Promise<string> {
|
||||
log(`Running: ${cmd}`);
|
||||
const stdout = await execAndThrowErrors(cmd, definitelyTypedPath);
|
||||
log(stdout);
|
||||
return stdout;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For "types/a/b/c", returns { name: "a", version: "*" }.
|
||||
* For "types/a/v3/c", returns { name: "a", version: 3 }.
|
||||
* For "x", returns undefined.
|
||||
*/
|
||||
function getDependencyFromFile(file: string): PackageId | undefined {
|
||||
const parts = file.split("/");
|
||||
if (parts.length <= 2) {
|
||||
// It's not in a typings directory at all.
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const [typesDirName, name, subDirName] = parts; // Ignore any other parts
|
||||
|
||||
if (typesDirName !== typesDirectoryName) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (subDirName) {
|
||||
const version = parseVersionFromDirectoryName(subDirName);
|
||||
if (version !== undefined) {
|
||||
return { name, version };
|
||||
}
|
||||
}
|
||||
|
||||
return { name, version: "*" };
|
||||
}
|
||||
|
||||
@@ -2,12 +2,10 @@ import yargs = require("yargs");
|
||||
|
||||
import checkParseResults from "../check-parse-results";
|
||||
import { clean } from "../clean";
|
||||
import { getDefinitelyTyped, parseDefinitions } from "@definitelytyped/definitions-parser";
|
||||
import { logUncaughtErrors, loggerWithErrors } from "@definitelytyped/utils";
|
||||
import { getDefinitelyTyped, parseDefinitions, getAffectedPackagesFromDiff } from "@definitelytyped/definitions-parser";
|
||||
import { logUncaughtErrors, loggerWithErrors, UncachedNpmInfoClient } from "@definitelytyped/utils";
|
||||
import { TesterOptions } from "../lib/common";
|
||||
import { UncachedNpmInfoClient } from "../lib/npm-client";
|
||||
|
||||
import runTests, { getAffectedPackagesFromDiff, parseNProcesses, testerOptions } from "./test-runner";
|
||||
import runTests, { parseNProcesses, testerOptions } from "./test-runner";
|
||||
|
||||
if (!module.parent) {
|
||||
const options = testerOptions(!!yargs.argv.runFromDefinitelyTyped);
|
||||
|
||||
9
packages/publisher/src/types/fstream.d.ts
vendored
9
packages/publisher/src/types/fstream.d.ts
vendored
@@ -1,9 +0,0 @@
|
||||
export function Reader(options: ReaderOptions): NodeJS.ReadableStream;
|
||||
interface ReaderOptions {
|
||||
path: string;
|
||||
type: "Directory";
|
||||
filter(entry: FStreamEntry): boolean;
|
||||
}
|
||||
interface FStreamEntry {
|
||||
props: { type: string; mode: number };
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
// Definitions transcribed from https://github.com/npm/npm-registry-client
|
||||
declare class RegClient {
|
||||
constructor(config?: RegClient.Config);
|
||||
request(
|
||||
uri: string,
|
||||
params: RegClient.RequestParams,
|
||||
cb: (error: Error, data: unknown, json: unknown, response: unknown) => void
|
||||
): void;
|
||||
publish(uri: string, params: RegClient.PublishParams, cb: (error: Error) => void): void;
|
||||
deprecate(
|
||||
uri: string,
|
||||
params: RegClient.DeprecateParams,
|
||||
cb: (error: Error, data: unknown, raw: string, response: unknown) => void
|
||||
): void;
|
||||
distTags: {
|
||||
add(uri: string, params: RegClient.AddTagParams, cb: (error: Error) => void): void;
|
||||
};
|
||||
}
|
||||
|
||||
declare namespace RegClient {
|
||||
interface Config {
|
||||
defaultTag?: string;
|
||||
}
|
||||
interface RequestParams {
|
||||
method?: string;
|
||||
body?: {};
|
||||
}
|
||||
interface PublishParams {
|
||||
metadata: {};
|
||||
access: "public" | "restricted";
|
||||
body: NodeJS.ReadableStream;
|
||||
auth: Credentials;
|
||||
}
|
||||
interface AddTagParams {
|
||||
package: string;
|
||||
version: string;
|
||||
distTag: string;
|
||||
auth: Credentials;
|
||||
}
|
||||
interface DeprecateParams {
|
||||
version: string;
|
||||
message: string;
|
||||
auth: Credentials;
|
||||
}
|
||||
interface Credentials {
|
||||
token: string;
|
||||
}
|
||||
}
|
||||
|
||||
export = RegClient;
|
||||
@@ -1,56 +0,0 @@
|
||||
import { createWriteStream } from "fs";
|
||||
import { FStreamEntry, Reader } from "fstream";
|
||||
import { Pack } from "tar";
|
||||
import * as zlib from "zlib";
|
||||
import { streamDone } from "@definitelytyped/utils";
|
||||
|
||||
export function gzip(input: NodeJS.ReadableStream): NodeJS.ReadableStream {
|
||||
return input.pipe(zlib.createGzip());
|
||||
}
|
||||
|
||||
export function unGzip(input: NodeJS.ReadableStream): NodeJS.ReadableStream {
|
||||
const output = zlib.createGunzip();
|
||||
input.pipe(output);
|
||||
return output;
|
||||
}
|
||||
|
||||
export function writeTgz(inputDirectory: string, outFileName: string): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
resolve(streamDone(createTgz(inputDirectory, reject).pipe(createWriteStream(outFileName))));
|
||||
});
|
||||
}
|
||||
|
||||
// To output this for testing: Export it and:
|
||||
// `require("./bin/lib/npm-client").createTgz("./output/foo", err => { throw err }).pipe(fs.createWriteStream("foo.tgz"))`
|
||||
export function createTgz(dir: string, onError: (error: Error) => void): NodeJS.ReadableStream {
|
||||
return gzip(createTar(dir, onError));
|
||||
}
|
||||
|
||||
function createTar(dir: string, onError: (error: Error) => void): NodeJS.ReadableStream {
|
||||
const packer = Pack({ noProprietary: true }).on("error", onError);
|
||||
|
||||
return Reader({ path: dir, type: "Directory", filter: addDirectoryExecutablePermission })
|
||||
.on("error", onError)
|
||||
.pipe(packer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Work around a bug where directories bundled on Windows do not have executable permission when extracted on Linux.
|
||||
* https://github.com/npm/node-tar/issues/7#issuecomment-17572926
|
||||
*/
|
||||
function addDirectoryExecutablePermission(entry: FStreamEntry): boolean {
|
||||
if (entry.props.type === "Directory") {
|
||||
entry.props.mode = addExecutePermissionsFromReadPermissions(entry.props.mode);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function addExecutePermissionsFromReadPermissions(mode: number): number {
|
||||
// Constant that gives execute permissions to owner, group, and others. "+x"
|
||||
const allExecutePermissions = 0o111;
|
||||
// Moves the bits for read permissions into the place for execute permissions.
|
||||
// In other words, a component will have execute permissions if it has read permissions.
|
||||
const readPermissionsAsExecutePermissions = (mode >>> 2) & allExecutePermissions; // tslint:disable-line no-bitwise
|
||||
// Add these additional execute permissions to the mode.
|
||||
return mode | readPermissionsAsExecutePermissions; // tslint:disable-line no-bitwise
|
||||
}
|
||||
835
packages/utils/package-lock.json
generated
835
packages/utils/package-lock.json
generated
@@ -22,11 +22,30 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/minipass": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/minipass/-/minipass-2.2.0.tgz",
|
||||
"integrity": "sha512-wuzZksN4w4kyfoOv/dlpov4NOunwutLA/q7uc00xU02ZyUY+aoM5PWIXEKBMnm0NHd4a+N71BMjq+x7+2Af1fg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "12.12.29",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.29.tgz",
|
||||
"integrity": "sha512-yo8Qz0ygADGFptISDj3pOC9wXfln/5pQaN/ysDIzOaAWXt73cNHmtEC8zSO2Y+kse/txmwIAJzkYZ5fooaS5DQ=="
|
||||
},
|
||||
"@types/tar": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/tar/-/tar-4.0.3.tgz",
|
||||
"integrity": "sha512-Z7AVMMlkI8NTWF0qGhC4QIX0zkV/+y0J8x7b/RsHrN0310+YNjoJd8UrApCiGBCWtKjxS9QhNqLi2UJNToh5hA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/minipass": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/tar-stream": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/tar-stream/-/tar-stream-2.1.0.tgz",
|
||||
@@ -36,6 +55,112 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"ajv": {
|
||||
"version": "6.12.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz",
|
||||
"integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==",
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
|
||||
"optional": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
|
||||
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
|
||||
"optional": true
|
||||
},
|
||||
"are-we-there-yet": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
|
||||
"integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"delegates": "^1.0.0",
|
||||
"readable-stream": "^2.0.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"readable-stream": {
|
||||
"version": "2.3.7",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
||||
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"optional": true
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
|
||||
"integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
|
||||
"requires": {
|
||||
"safer-buffer": "~2.1.0"
|
||||
}
|
||||
},
|
||||
"assert-plus": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
|
||||
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
|
||||
},
|
||||
"asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
|
||||
},
|
||||
"aws-sign2": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
|
||||
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
|
||||
},
|
||||
"aws4": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz",
|
||||
"integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug=="
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
|
||||
},
|
||||
"bcrypt-pbkdf": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
||||
"integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
|
||||
"requires": {
|
||||
"tweetnacl": "^0.14.3"
|
||||
}
|
||||
},
|
||||
"bl": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-3.0.0.tgz",
|
||||
@@ -44,6 +169,38 @@
|
||||
"readable-stream": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"block-stream": {
|
||||
"version": "0.0.9",
|
||||
"resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
|
||||
"integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
|
||||
"requires": {
|
||||
"inherits": "~2.0.0"
|
||||
}
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"buffer-from": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
|
||||
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
|
||||
},
|
||||
"builtins": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz",
|
||||
"integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og="
|
||||
},
|
||||
"caseless": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
|
||||
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
|
||||
},
|
||||
"charm": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/charm/-/charm-1.0.2.tgz",
|
||||
@@ -52,6 +209,104 @@
|
||||
"inherits": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
|
||||
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
|
||||
"optional": true
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"requires": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
||||
},
|
||||
"concat-stream": {
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
|
||||
"integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
|
||||
"requires": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"inherits": "^2.0.3",
|
||||
"readable-stream": "^2.2.2",
|
||||
"typedarray": "^0.0.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"readable-stream": {
|
||||
"version": "2.3.7",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
||||
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"requires": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
||||
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
|
||||
"optional": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
|
||||
},
|
||||
"dashdash": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
||||
"integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
|
||||
"requires": {
|
||||
"assert-plus": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
|
||||
},
|
||||
"delegates": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
||||
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
|
||||
"optional": true
|
||||
},
|
||||
"ecc-jsbn": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
|
||||
"integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
|
||||
"requires": {
|
||||
"jsbn": "~0.1.0",
|
||||
"safer-buffer": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"end-of-stream": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||
@@ -60,6 +315,41 @@
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"extend": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
|
||||
},
|
||||
"extsprintf": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
|
||||
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
|
||||
},
|
||||
"fast-deep-equal": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz",
|
||||
"integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA=="
|
||||
},
|
||||
"fast-json-stable-stringify": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
||||
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
|
||||
},
|
||||
"forever-agent": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
|
||||
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
|
||||
},
|
||||
"form-data": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
|
||||
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
|
||||
"requires": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.6",
|
||||
"mime-types": "^2.1.12"
|
||||
}
|
||||
},
|
||||
"fs-constants": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||
@@ -75,16 +365,167 @@
|
||||
"universalify": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
||||
},
|
||||
"fstream": {
|
||||
"version": "1.0.12",
|
||||
"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
|
||||
"integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.2",
|
||||
"inherits": "~2.0.0",
|
||||
"mkdirp": ">=0.5 0",
|
||||
"rimraf": "2"
|
||||
},
|
||||
"dependencies": {
|
||||
"mkdirp": {
|
||||
"version": "0.5.5",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||
"requires": {
|
||||
"minimist": "^1.2.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"gauge": {
|
||||
"version": "2.7.4",
|
||||
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
|
||||
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"aproba": "^1.0.3",
|
||||
"console-control-strings": "^1.0.0",
|
||||
"has-unicode": "^2.0.0",
|
||||
"object-assign": "^4.1.0",
|
||||
"signal-exit": "^3.0.0",
|
||||
"string-width": "^1.0.1",
|
||||
"strip-ansi": "^3.0.1",
|
||||
"wide-align": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"getpass": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
|
||||
"integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
|
||||
"requires": {
|
||||
"assert-plus": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.6",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
||||
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.0.4",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
|
||||
"integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ=="
|
||||
},
|
||||
"har-schema": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
|
||||
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
|
||||
},
|
||||
"har-validator": {
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
|
||||
"integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
|
||||
"requires": {
|
||||
"ajv": "^6.5.5",
|
||||
"har-schema": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"has-unicode": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
|
||||
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
|
||||
"optional": true
|
||||
},
|
||||
"hosted-git-info": {
|
||||
"version": "2.8.8",
|
||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
|
||||
"integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg=="
|
||||
},
|
||||
"http-signature": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
|
||||
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
|
||||
"requires": {
|
||||
"assert-plus": "^1.0.0",
|
||||
"jsprim": "^1.2.2",
|
||||
"sshpk": "^1.7.0"
|
||||
}
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||
"requires": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
||||
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"is-typedarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
|
||||
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
|
||||
},
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
||||
},
|
||||
"isstream": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
|
||||
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
|
||||
},
|
||||
"jsbn": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
|
||||
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
|
||||
},
|
||||
"json-schema": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
|
||||
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
|
||||
},
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
|
||||
},
|
||||
"json-stringify-safe": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
||||
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
|
||||
},
|
||||
"jsonfile": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
|
||||
@@ -93,6 +534,113 @@
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"jsprim": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
|
||||
"integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
|
||||
"requires": {
|
||||
"assert-plus": "1.0.0",
|
||||
"extsprintf": "1.3.0",
|
||||
"json-schema": "0.2.3",
|
||||
"verror": "1.10.0"
|
||||
}
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.43.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
|
||||
"integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ=="
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.26",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
|
||||
"integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
|
||||
"requires": {
|
||||
"mime-db": "1.43.0"
|
||||
}
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
},
|
||||
"normalize-package-data": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
|
||||
"integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
|
||||
"requires": {
|
||||
"hosted-git-info": "^2.1.4",
|
||||
"resolve": "^1.10.0",
|
||||
"semver": "2 || 3 || 4 || 5",
|
||||
"validate-npm-package-license": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"npm-package-arg": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz",
|
||||
"integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==",
|
||||
"requires": {
|
||||
"hosted-git-info": "^2.7.1",
|
||||
"osenv": "^0.1.5",
|
||||
"semver": "^5.6.0",
|
||||
"validate-npm-package-name": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"npm-registry-client": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/npm-registry-client/-/npm-registry-client-8.6.0.tgz",
|
||||
"integrity": "sha512-Qs6P6nnopig+Y8gbzpeN/dkt+n7IyVd8f45NTMotGk6Qo7GfBmzwYx6jRLoOOgKiMnaQfYxsuyQlD8Mc3guBhg==",
|
||||
"requires": {
|
||||
"concat-stream": "^1.5.2",
|
||||
"graceful-fs": "^4.1.6",
|
||||
"normalize-package-data": "~1.0.1 || ^2.0.0",
|
||||
"npm-package-arg": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0",
|
||||
"npmlog": "2 || ^3.1.0 || ^4.0.0",
|
||||
"once": "^1.3.3",
|
||||
"request": "^2.74.0",
|
||||
"retry": "^0.10.0",
|
||||
"safe-buffer": "^5.1.1",
|
||||
"semver": "2 >=2.2.1 || 3.x || 4 || 5",
|
||||
"slide": "^1.1.3",
|
||||
"ssri": "^5.2.4"
|
||||
}
|
||||
},
|
||||
"npmlog": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
|
||||
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"are-we-there-yet": "~1.1.2",
|
||||
"console-control-strings": "~1.1.0",
|
||||
"gauge": "~2.7.3",
|
||||
"set-blocking": "~2.0.0"
|
||||
}
|
||||
},
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
|
||||
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
|
||||
"optional": true
|
||||
},
|
||||
"oauth-sign": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
|
||||
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
|
||||
"optional": true
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
@@ -101,6 +649,60 @@
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"os-homedir": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
|
||||
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
|
||||
},
|
||||
"os-tmpdir": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
|
||||
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
|
||||
},
|
||||
"osenv": {
|
||||
"version": "0.1.5",
|
||||
"resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
|
||||
"integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
|
||||
"requires": {
|
||||
"os-homedir": "^1.0.0",
|
||||
"os-tmpdir": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
|
||||
},
|
||||
"path-parse": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
||||
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
|
||||
},
|
||||
"performance-now": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
|
||||
},
|
||||
"process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
|
||||
},
|
||||
"psl": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
|
||||
"integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
|
||||
},
|
||||
"punycode": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.5.2",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
|
||||
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
@@ -111,11 +713,149 @@
|
||||
"util-deprecate": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"request": {
|
||||
"version": "2.88.2",
|
||||
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
|
||||
"integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
|
||||
"requires": {
|
||||
"aws-sign2": "~0.7.0",
|
||||
"aws4": "^1.8.0",
|
||||
"caseless": "~0.12.0",
|
||||
"combined-stream": "~1.0.6",
|
||||
"extend": "~3.0.2",
|
||||
"forever-agent": "~0.6.1",
|
||||
"form-data": "~2.3.2",
|
||||
"har-validator": "~5.1.3",
|
||||
"http-signature": "~1.2.0",
|
||||
"is-typedarray": "~1.0.0",
|
||||
"isstream": "~0.1.2",
|
||||
"json-stringify-safe": "~5.0.1",
|
||||
"mime-types": "~2.1.19",
|
||||
"oauth-sign": "~0.9.0",
|
||||
"performance-now": "^2.1.0",
|
||||
"qs": "~6.5.2",
|
||||
"safe-buffer": "^5.1.2",
|
||||
"tough-cookie": "~2.5.0",
|
||||
"tunnel-agent": "^0.6.0",
|
||||
"uuid": "^3.3.2"
|
||||
}
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.15.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz",
|
||||
"integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==",
|
||||
"requires": {
|
||||
"path-parse": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"retry": {
|
||||
"version": "0.10.1",
|
||||
"resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz",
|
||||
"integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q="
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
|
||||
"integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
|
||||
},
|
||||
"set-blocking": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
|
||||
"optional": true
|
||||
},
|
||||
"signal-exit": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
|
||||
"integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==",
|
||||
"optional": true
|
||||
},
|
||||
"slide": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz",
|
||||
"integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc="
|
||||
},
|
||||
"spdx-correct": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
|
||||
"integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==",
|
||||
"requires": {
|
||||
"spdx-expression-parse": "^3.0.0",
|
||||
"spdx-license-ids": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"spdx-exceptions": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz",
|
||||
"integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA=="
|
||||
},
|
||||
"spdx-expression-parse": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
|
||||
"integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
|
||||
"requires": {
|
||||
"spdx-exceptions": "^2.1.0",
|
||||
"spdx-license-ids": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"spdx-license-ids": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
|
||||
"integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q=="
|
||||
},
|
||||
"sshpk": {
|
||||
"version": "1.16.1",
|
||||
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
|
||||
"integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
|
||||
"requires": {
|
||||
"asn1": "~0.2.3",
|
||||
"assert-plus": "^1.0.0",
|
||||
"bcrypt-pbkdf": "^1.0.0",
|
||||
"dashdash": "^1.12.0",
|
||||
"ecc-jsbn": "~0.1.1",
|
||||
"getpass": "^0.1.1",
|
||||
"jsbn": "~0.1.0",
|
||||
"safer-buffer": "^2.0.2",
|
||||
"tweetnacl": "~0.14.0"
|
||||
}
|
||||
},
|
||||
"ssri": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz",
|
||||
"integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==",
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.1"
|
||||
}
|
||||
},
|
||||
"string-width": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
"strip-ansi": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
@@ -124,6 +864,25 @@
|
||||
"safe-buffer": "~5.2.0"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"tar": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz",
|
||||
"integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==",
|
||||
"requires": {
|
||||
"block-stream": "*",
|
||||
"fstream": "^1.0.12",
|
||||
"inherits": "2"
|
||||
}
|
||||
},
|
||||
"tar-stream": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.0.tgz",
|
||||
@@ -136,16 +895,92 @@
|
||||
"readable-stream": "^3.1.1"
|
||||
}
|
||||
},
|
||||
"tough-cookie": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
|
||||
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
|
||||
"requires": {
|
||||
"psl": "^1.1.28",
|
||||
"punycode": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"tunnel-agent": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
|
||||
"requires": {
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"tweetnacl": {
|
||||
"version": "0.14.5",
|
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
||||
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
|
||||
},
|
||||
"typedarray": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
|
||||
},
|
||||
"universalify": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
|
||||
},
|
||||
"uri-js": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
|
||||
"integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
|
||||
"requires": {
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||
},
|
||||
"uuid": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
|
||||
},
|
||||
"validate-npm-package-license": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
|
||||
"integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
|
||||
"requires": {
|
||||
"spdx-correct": "^3.0.0",
|
||||
"spdx-expression-parse": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"validate-npm-package-name": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz",
|
||||
"integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=",
|
||||
"requires": {
|
||||
"builtins": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"verror": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
|
||||
"integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
|
||||
"requires": {
|
||||
"assert-plus": "^1.0.0",
|
||||
"core-util-is": "1.0.2",
|
||||
"extsprintf": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"wide-align": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
|
||||
"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"string-width": "^1.0.2 || 2"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
|
||||
@@ -22,11 +22,15 @@
|
||||
"@types/node": "^12.12.29",
|
||||
"charm": "^1.0.2",
|
||||
"fs-extra": "^8.1.0",
|
||||
"fstream": "^1.0.12",
|
||||
"npm-registry-client": "^8.6.0",
|
||||
"tar": "^2.2.2",
|
||||
"tar-stream": "^2.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/charm": "^1.0.1",
|
||||
"@types/fs-extra": "^8.1.0",
|
||||
"@types/tar": "^4.0.3",
|
||||
"@types/tar-stream": "^2.1.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
||||
@@ -5,6 +5,7 @@ export * from "./fs";
|
||||
export * from "./io";
|
||||
export * from "./logging";
|
||||
export * from "./miscellany";
|
||||
export * from "./npm";
|
||||
export * from "./process";
|
||||
export * from "./progress";
|
||||
export * from "./semver";
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
import "./types/fstream";
|
||||
import {
|
||||
readFile as readFileWithEncoding,
|
||||
readFileSync as readFileWithEncodingSync,
|
||||
stat,
|
||||
writeFile as writeFileWithEncoding,
|
||||
writeJson as writeJsonRaw
|
||||
writeJson as writeJsonRaw,
|
||||
createWriteStream
|
||||
} from "fs-extra";
|
||||
import { FStreamEntry, Reader } from "fstream";
|
||||
import { Pack } from "tar";
|
||||
import tarStream from "tar-stream";
|
||||
import https, { Agent, request } from "https";
|
||||
import zlib from "zlib";
|
||||
import { request as httpRequest } from "http";
|
||||
import { Readable as ReadableStream } from "stream";
|
||||
import { StringDecoder } from "string_decoder";
|
||||
import { parseJson, withoutStart } from "./miscellany";
|
||||
import { parseJson, withoutStart, sleep } from "./miscellany";
|
||||
import { FS, Dir, InMemoryFS } from "./fs";
|
||||
import { assertDefined } from "./assertions";
|
||||
|
||||
@@ -161,10 +165,6 @@ function doRequest(options: FetchOptions, makeRequest: typeof request, agent?: A
|
||||
});
|
||||
}
|
||||
|
||||
export async function sleep(seconds: number): Promise<void> {
|
||||
return new Promise<void>(resolve => setTimeout(resolve, seconds * 1000));
|
||||
}
|
||||
|
||||
export async function isDirectory(path: string): Promise<boolean> {
|
||||
return (await stat(path)).isDirectory();
|
||||
}
|
||||
@@ -216,3 +216,54 @@ export function downloadAndExtractFile(url: string): Promise<FS> {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function gzip(input: NodeJS.ReadableStream): NodeJS.ReadableStream {
|
||||
return input.pipe(zlib.createGzip());
|
||||
}
|
||||
|
||||
export function unGzip(input: NodeJS.ReadableStream): NodeJS.ReadableStream {
|
||||
const output = zlib.createGunzip();
|
||||
input.pipe(output);
|
||||
return output;
|
||||
}
|
||||
|
||||
export function writeTgz(inputDirectory: string, outFileName: string): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
resolve(streamDone(createTgz(inputDirectory, reject).pipe(createWriteStream(outFileName))));
|
||||
});
|
||||
}
|
||||
|
||||
// To output this for testing:
|
||||
// `require("./dist/io").createTgz("./src", err => { throw err }).pipe(fs.createWriteStream("foo.tgz"))`
|
||||
export function createTgz(dir: string, onError: (error: Error) => void): NodeJS.ReadableStream {
|
||||
return gzip(createTar(dir, onError));
|
||||
}
|
||||
|
||||
function createTar(dir: string, onError: (error: Error) => void): NodeJS.ReadableStream {
|
||||
const packer = Pack({ noProprietary: true, path: dir }).on("error", onError);
|
||||
|
||||
return Reader({ path: dir, type: "Directory", filter: addDirectoryExecutablePermission })
|
||||
.on("error", onError)
|
||||
.pipe(packer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Work around a bug where directories bundled on Windows do not have executable permission when extracted on Linux.
|
||||
* https://github.com/npm/node-tar/issues/7#issuecomment-17572926
|
||||
*/
|
||||
function addDirectoryExecutablePermission(entry: FStreamEntry): boolean {
|
||||
if (entry.props.type === "Directory") {
|
||||
entry.props.mode = addExecutePermissionsFromReadPermissions(entry.props.mode);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function addExecutePermissionsFromReadPermissions(mode: number): number {
|
||||
// Constant that gives execute permissions to owner, group, and others. "+x"
|
||||
const allExecutePermissions = 0o111;
|
||||
// Moves the bits for read permissions into the place for execute permissions.
|
||||
// In other words, a component will have execute permissions if it has read permissions.
|
||||
const readPermissionsAsExecutePermissions = (mode >>> 2) & allExecutePermissions; // tslint:disable-line no-bitwise
|
||||
// Add these additional execute permissions to the mode.
|
||||
return mode | readPermissionsAsExecutePermissions; // tslint:disable-line no-bitwise
|
||||
}
|
||||
|
||||
@@ -29,3 +29,7 @@ export function unmangleScopedPackage(packageName: string): string | undefined {
|
||||
const separator = "__";
|
||||
return packageName.includes(separator) ? `@${packageName.replace(separator, "/")}` : undefined;
|
||||
}
|
||||
|
||||
export async function sleep(seconds: number): Promise<void> {
|
||||
return new Promise<void>(resolve => setTimeout(resolve, seconds * 1000));
|
||||
}
|
||||
|
||||
@@ -1,32 +1,30 @@
|
||||
import "./types/npm-registry-client";
|
||||
import assert = require("assert");
|
||||
import { ensureFile, pathExists } from "fs-extra";
|
||||
import RegClient = require("npm-registry-client");
|
||||
import { ensureFile, pathExists, readJson, writeJson, readFile } from "fs-extra";
|
||||
import RegClient from "npm-registry-client";
|
||||
import { resolve as resolveUrl } from "url";
|
||||
import { createTgz } from "../util/tgz";
|
||||
import { joinPaths } from "./fs";
|
||||
import { loggerWithErrors, Logger } from "./logging";
|
||||
import { mapToRecord, recordToMap } from "./collections";
|
||||
import { Fetcher, createTgz } from "./io";
|
||||
import { sleep, identity } from "./miscellany";
|
||||
import { assertNever } from "./assertions";
|
||||
|
||||
import { Registry } from "./common";
|
||||
import { getSecret, Secret } from "./secrets";
|
||||
import { githubRegistry, npmApi, npmRegistry, npmRegistryHostName } from "./settings";
|
||||
import {
|
||||
joinPaths,
|
||||
loggerWithErrors,
|
||||
readJson,
|
||||
recordToMap,
|
||||
writeJson,
|
||||
mapToRecord,
|
||||
Fetcher,
|
||||
sleep,
|
||||
assertNever,
|
||||
Logger,
|
||||
readFile,
|
||||
identity,
|
||||
best,
|
||||
assertDefined,
|
||||
Semver
|
||||
} from "@definitelytyped/utils";
|
||||
import { NotNeededPackage } from "@definitelytyped/definitions-parser";
|
||||
export const npmRegistryHostName = "registry.npmjs.org";
|
||||
export const githubRegistryHostName = "npm.pkg.github.com";
|
||||
export const npmRegistry = `https://${npmRegistryHostName}/`;
|
||||
export const githubRegistry = `https://${githubRegistryHostName}/`;
|
||||
export const npmApi = "api.npmjs.org";
|
||||
|
||||
const cacheFile = joinPaths(__dirname, "..", "..", "cache", "npmInfo.json");
|
||||
const cacheFile = joinPaths(__dirname, "..", "cache", "npmInfo.json");
|
||||
|
||||
/** Which registry to publish to */
|
||||
export enum Registry {
|
||||
/** types-registry and @types/* on NPM */
|
||||
NPM,
|
||||
/** @definitelytyped/types-registry and @types/* on Github */
|
||||
Github
|
||||
}
|
||||
|
||||
export type NpmInfoCache = ReadonlyMap<string, NpmInfo>;
|
||||
|
||||
@@ -163,16 +161,16 @@ function splitToFixedSizeGroups(names: readonly string[], chunkSize: number): re
|
||||
}
|
||||
|
||||
export class NpmPublishClient {
|
||||
static async create(config?: RegClient.Config, registry: Registry = Registry.NPM): Promise<NpmPublishClient> {
|
||||
static async create(
|
||||
token: string,
|
||||
config?: RegClient.Config,
|
||||
registry: Registry = Registry.NPM
|
||||
): Promise<NpmPublishClient> {
|
||||
switch (registry) {
|
||||
case Registry.NPM:
|
||||
return new NpmPublishClient(new RegClient(config), { token: await getSecret(Secret.NPM_TOKEN) }, npmRegistry);
|
||||
return new NpmPublishClient(new RegClient(config), { token }, npmRegistry);
|
||||
case Registry.Github:
|
||||
return new NpmPublishClient(
|
||||
new RegClient(config),
|
||||
{ token: await getSecret(Secret.GITHUB_PUBLISH_ACCESS_TOKEN) },
|
||||
githubRegistry
|
||||
);
|
||||
return new NpmPublishClient(new RegClient(config), { token }, githubRegistry);
|
||||
default:
|
||||
assertNever(registry);
|
||||
}
|
||||
@@ -257,45 +255,3 @@ function promisifyVoid(callsBack: (cb: (error: Error | undefined) => void) => vo
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* When we fail to publish a deprecated package, it leaves behind an entry in the time property.
|
||||
* So the keys of 'time' give the actual 'latest'.
|
||||
* If that's not equal to the expected latest, try again by bumping the patch version of the last attempt by 1.
|
||||
*/
|
||||
export function skipBadPublishes(pkg: NotNeededPackage, client: CachedNpmInfoClient, log: Logger) {
|
||||
// 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({
|
||||
asOfVersion: plusOne.versionString,
|
||||
libraryName: pkg.libraryName,
|
||||
sourceRepoURL: pkg.sourceRepoURL,
|
||||
typingsPackageName: pkg.name
|
||||
});
|
||||
}
|
||||
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()
|
||||
);
|
||||
if (!actual) {
|
||||
throw new Error("failed to find actual latest");
|
||||
}
|
||||
return actual[0];
|
||||
}
|
||||
11
packages/utils/src/types/fstream.ts
Normal file
11
packages/utils/src/types/fstream.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
declare module "fstream" {
|
||||
export function Reader(options: ReaderOptions): NodeJS.ReadableStream;
|
||||
interface ReaderOptions {
|
||||
path: string;
|
||||
type: "Directory";
|
||||
filter(entry: FStreamEntry): boolean;
|
||||
}
|
||||
interface FStreamEntry {
|
||||
props: { type: string; mode: number };
|
||||
}
|
||||
}
|
||||
52
packages/utils/src/types/npm-registry-client.ts
Normal file
52
packages/utils/src/types/npm-registry-client.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
declare module "npm-registry-client" {
|
||||
// Definitions transcribed from https://github.com/npm/npm-registry-client
|
||||
class RegClient {
|
||||
constructor(config?: RegClient.Config);
|
||||
request(
|
||||
uri: string,
|
||||
params: RegClient.RequestParams,
|
||||
cb: (error: Error, data: unknown, json: unknown, response: unknown) => void
|
||||
): void;
|
||||
publish(uri: string, params: RegClient.PublishParams, cb: (error: Error) => void): void;
|
||||
deprecate(
|
||||
uri: string,
|
||||
params: RegClient.DeprecateParams,
|
||||
cb: (error: Error, data: unknown, raw: string, response: unknown) => void
|
||||
): void;
|
||||
distTags: {
|
||||
add(uri: string, params: RegClient.AddTagParams, cb: (error: Error) => void): void;
|
||||
};
|
||||
}
|
||||
|
||||
namespace RegClient {
|
||||
interface Config {
|
||||
defaultTag?: string;
|
||||
}
|
||||
interface RequestParams {
|
||||
method?: string;
|
||||
body?: {};
|
||||
}
|
||||
interface PublishParams {
|
||||
metadata: {};
|
||||
access: "public" | "restricted";
|
||||
body: NodeJS.ReadableStream;
|
||||
auth: Credentials;
|
||||
}
|
||||
interface AddTagParams {
|
||||
package: string;
|
||||
version: string;
|
||||
distTag: string;
|
||||
auth: Credentials;
|
||||
}
|
||||
interface DeprecateParams {
|
||||
version: string;
|
||||
message: string;
|
||||
auth: Credentials;
|
||||
}
|
||||
interface Credentials {
|
||||
token: string;
|
||||
}
|
||||
}
|
||||
|
||||
export = RegClient;
|
||||
}
|
||||
Reference in New Issue
Block a user