Skip to content

Commit

Permalink
Add JSDoc types to internal scripts (#5305)
Browse files Browse the repository at this point in the history
* Add type check for script files

* Use script types

* Add missing inquirer types
  • Loading branch information
lukastaegert committed Dec 23, 2023
1 parent d56ac63 commit 4c6635b
Show file tree
Hide file tree
Showing 15 changed files with 319 additions and 50 deletions.
20 changes: 20 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -81,7 +81,7 @@
"test:package": "node scripts/test-package.js",
"test:options": "node scripts/test-options.js",
"test:only": "mocha test/test.js",
"test:typescript": "shx rm -rf test/typescript/dist && shx cp -r dist test/typescript/ && tsc --noEmit -p test/typescript && tsc --noEmit",
"test:typescript": "shx rm -rf test/typescript/dist && shx cp -r dist test/typescript/ && tsc --noEmit -p test/typescript && tsc --noEmit && tsc --noEmit -p scripts",
"test:browser": "mocha test/browser/index.js",
"watch": "rollup --config rollup.config.ts --configPlugin typescript --watch"
},
Expand Down Expand Up @@ -125,6 +125,7 @@
"@rollup/plugin-typescript": "11.1.5",
"@rollup/pluginutils": "^5.1.0",
"@types/estree": "1.0.5",
"@types/inquirer": "^9.0.7",
"@types/mocha": "^10.0.6",
"@types/node": "18.0.0",
"@types/yargs-parser": "^21.0.3",
Expand Down
23 changes: 23 additions & 0 deletions scripts/declarations.d.ts
@@ -0,0 +1,23 @@
declare module 'github-api' {
export interface Repo {
listPullRequests({ state: string }): Promise<{
data: { number: number; title: string; head: { sha: string } }[];
}>;

getPullRequest(pr: number): Promise<{ data: { body: string; user: { login: string } } }>;

createRelease(release: { body: string; name: string; tag_name: string }): Promise<void>;
}

export interface Issues {
createIssueComment(issueNumber: number, text: string): Promise<void>;
}

export default class GitHub {
constructor({ token: string });

getRepo(organization: string, repository: string): Repo;

getIssues(organization: string, repository: string): Issues;
}
}
4 changes: 4 additions & 0 deletions scripts/find-config.js
@@ -1,6 +1,10 @@
import { readdir } from 'node:fs/promises';
import { resolve } from 'node:path';

/**
* @param {string} targetDirectory
* @return {Promise<string>}
*/
export async function findConfigFileName(targetDirectory) {
const filesInWorkingDirectory = new Set(await readdir(targetDirectory));
for (const extension of ['mjs', 'cjs', 'ts', 'js']) {
Expand Down
39 changes: 26 additions & 13 deletions scripts/helpers.js
Expand Up @@ -5,24 +5,32 @@ import { blue, bold, cyan, green, magenta, red, yellow } from './colors.js';
const colors = [cyan, yellow, blue, red, green, magenta];
let nextColorIndex = 0;

/**
* @param {string} command
* @param {string[]} parameters
* @param {Parameters<typeof spawn>[2]=} options
* @return {Promise<void>}
*/
export function runWithEcho(command, parameters, options) {
const color = colors[nextColorIndex];
nextColorIndex = (nextColorIndex + 1) % colors.length;
return new Promise((resolve, reject) => {
const cmdString = formatCommand(command, parameters);
console.error(bold(`\n${color`Run>`} ${cmdString}`));
return /** @type {Promise<void>} */ (
new Promise((resolve, reject) => {
const cmdString = formatCommand(command, parameters);
console.error(bold(`\n${color('Run>')} ${cmdString}`));

const childProcess = spawn(command, parameters, { stdio: 'inherit', ...options });
const childProcess = spawn(command, parameters, { stdio: 'inherit', ...options });

childProcess.on('close', code => {
if (code) {
reject(new Error(`"${cmdString}" exited with code ${code}.`));
} else {
console.error(bold(`${color`Finished>`} ${cmdString}\n`));
resolve();
}
});
});
childProcess.on('close', code => {
if (code) {
reject(new Error(`"${cmdString}" exited with code ${code}.`));
} else {
console.error(bold(`${color('Finished>')} ${cmdString}\n`));
resolve();
}
});
})
);
}

/**
Expand All @@ -48,6 +56,11 @@ export function runAndGetStdout(command, parameters) {
});
}

/**
* @param {string} command
* @param {string[]} parameters
* @return {string}
*/
function formatCommand(command, parameters) {
return [command, ...parameters].join(' ');
}
Expand Down
8 changes: 6 additions & 2 deletions scripts/perf-init.js
Expand Up @@ -24,9 +24,13 @@ rmSync(TARGET_DIR, {
recursive: true
});

const [, repo, , branch] = VALID_REPO.exec(repoWithBranch);
const repoWithBranchMatch = VALID_REPO.exec(repoWithBranch);
if (!repoWithBranchMatch) {
throw new Error('Could not match repository and branch.');
}
const [, repo, , branch] = repoWithBranchMatch;

const gitArguments = ['clone', '--depth', 1, '--progress'];
const gitArguments = ['clone', '--depth', '1', '--progress'];
if (branch) {
console.error(`Cloning branch "${branch}" of "${repo}"...`);
gitArguments.push('--branch', branch);
Expand Down
109 changes: 96 additions & 13 deletions scripts/perf.js
Expand Up @@ -11,6 +11,14 @@ import { loadConfigFile } from '../dist/loadConfigFile.js';
import { rollup } from '../dist/rollup.js';
import { findConfigFileName } from './find-config.js';

/**
* @typedef {Record<string,{memory:number,time:number}>} PersistedTimings
*/

/**
* @typedef {Record<string, [number, number, number][]>} AccumulatedTimings
*/

const initialDirectory = cwd();
const targetDirectory = fileURLToPath(new URL('../perf', import.meta.url).href);
const perfFile = fileURLToPath(new URL('../perf/rollup.perf.json', import.meta.url).href);
Expand Down Expand Up @@ -51,6 +59,12 @@ console.info(

await calculatePrintAndPersistTimings(configs.options[0], await getExistingTimings());

/**
* @param {number[]} times
* @param {number} runs
* @param {number} discarded
* @return {number}
*/
function getSingleAverage(times, runs, discarded) {
const actualDiscarded = Math.min(discarded, runs - 1);
return (
Expand All @@ -63,7 +77,16 @@ function getSingleAverage(times, runs, discarded) {
);
}

/**
* @param {AccumulatedTimings} accumulatedMeasurements
* @param {number} runs
* @param {number} discarded
* @return {PersistedTimings}
*/
function getAverage(accumulatedMeasurements, runs, discarded) {
/**
* @type {PersistedTimings}
*/
const average = {};
for (const label of Object.keys(accumulatedMeasurements)) {
average[label] = {
Expand All @@ -82,50 +105,80 @@ function getAverage(accumulatedMeasurements, runs, discarded) {
return average;
}

/**
* @param {import('rollup').MergedRollupOptions} config
* @param {PersistedTimings} existingTimings
* @return {Promise<void>}
*/
async function calculatePrintAndPersistTimings(config, existingTimings) {
const timings = await buildAndGetTimings(config);
for (const label of Object.keys(timings)) {
timings[label] = [timings[label]];
const serializedTimings = await buildAndGetTimings(config);
/**
* @type {Record<string, [number, number, number][]>}
*/
const accumulatedTimings = {};
for (const label of Object.keys(serializedTimings)) {
accumulatedTimings[label] = [serializedTimings[label]];
}
for (let currentRun = 1; currentRun < numberOfRunsToAverage; currentRun++) {
const numberOfLinesToClear = printMeasurements(
getAverage(timings, currentRun, numberOfDiscardedResults),
getAverage(accumulatedTimings, currentRun, numberOfDiscardedResults),
existingTimings,
/^#/
);
console.info(`Completed run ${currentRun}.`);
const currentTimings = await buildAndGetTimings(config);
clearLines(numberOfLinesToClear);
for (const label of Object.keys(timings)) {
for (const label of Object.keys(accumulatedTimings)) {
if (currentTimings.hasOwnProperty(label)) {
timings[label].push(currentTimings[label]);
accumulatedTimings[label].push(currentTimings[label]);
} else {
delete timings[label];
delete accumulatedTimings[label];
}
}
}
const averageTimings = getAverage(timings, numberOfRunsToAverage, numberOfDiscardedResults);
const averageTimings = getAverage(
accumulatedTimings,
numberOfRunsToAverage,
numberOfDiscardedResults
);
printMeasurements(averageTimings, existingTimings);
if (Object.keys(existingTimings).length === 0) persistTimings(averageTimings);
if (Object.keys(existingTimings).length === 0) {
persistTimings(averageTimings);
}
}

/**
* @param {import('rollup').MergedRollupOptions} config
* @return {Promise<import('rollup').SerializedTimings>}
*/
async function buildAndGetTimings(config) {
config.perf = true;
if (Array.isArray(config.output)) {
config.output = config.output[0];
}
const output = Array.isArray(config.output) ? config.output[0] : config.output;
// @ts-expect-error garbage collection may not be enabled
gc();
chdir(targetDirectory);
const bundle = await rollup(config);
chdir(initialDirectory);
await bundle.generate(config.output);
await bundle.generate(output);
if (!bundle.getTimings) {
throw new Error('Timings not found in the bundle.');
}
return bundle.getTimings();
}

/**
* @param {PersistedTimings} average
* @param {PersistedTimings} existingAverage
* @param {RegExp} filter
* @return {number}
*/
function printMeasurements(average, existingAverage, filter = /.*/) {
const printedLabels = Object.keys(average).filter(label => filter.test(label));
console.info('');
for (const label of printedLabels) {
/**
* @type {function(string): string}
*/
let color = identity;
if (label[0] === '#') {
color = bold;
Expand All @@ -148,10 +201,16 @@ function printMeasurements(average, existingAverage, filter = /.*/) {
return printedLabels.length + 2;
}

/**
* @param {number} numberOfLines
*/
function clearLines(numberOfLines) {
console.info('\u001B[A' + '\u001B[2K\u001B[A'.repeat(numberOfLines));
}

/**
* @return {PersistedTimings}
*/
function getExistingTimings() {
try {
const timings = JSON.parse(readFileSync(perfFile, 'utf8'));
Expand All @@ -164,6 +223,9 @@ function getExistingTimings() {
}
}

/**
* @param {PersistedTimings} timings
*/
function persistTimings(timings) {
try {
writeFileSync(perfFile, JSON.stringify(timings, null, 2), 'utf8');
Expand All @@ -174,7 +236,15 @@ function persistTimings(timings) {
}
}

/**
* @param {number} currentTime
* @param {number} persistedTime
* @return {string}
*/
function getFormattedTime(currentTime, persistedTime = currentTime) {
/**
* @type {function(string): string}
*/
let color = identity,
formattedTime = `${currentTime.toFixed(0)}ms`;
const absoluteDeviation = Math.abs(currentTime - persistedTime);
Expand All @@ -191,7 +261,15 @@ function getFormattedTime(currentTime, persistedTime = currentTime) {
return color(formattedTime);
}

/**
* @param {number} currentMemory
* @param {number} persistedMemory
* @return {string}
*/
function getFormattedMemory(currentMemory, persistedMemory = currentMemory) {
/**
* @type {function(string): string}
*/
let color = identity,
formattedMemory = prettyBytes(currentMemory);
const absoluteDeviation = Math.abs(currentMemory - persistedMemory);
Expand All @@ -204,6 +282,11 @@ function getFormattedMemory(currentMemory, persistedMemory = currentMemory) {
return color(formattedMemory);
}

/**
* @template T
* @param {T} x
* @returns {T}
*/
function identity(x) {
return x;
}

0 comments on commit 4c6635b

Please sign in to comment.