From 2f55a56b89eee95d7c0f4f3c33248947c51d5c25 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Fri, 9 Jan 2026 22:14:16 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9D=20Add=20docstrings=20to=20`feature?= =?UTF-8?q?/github-action-package`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Docstrings generation was requested by @srod. * https://github.com/srod/node-minify/pull/2760#issuecomment-3730682095 The following files were modified: * `packages/action/src/checks.ts` * `packages/action/src/index.ts` * `packages/action/src/inputs.ts` * `packages/action/src/minify.ts` * `packages/action/src/outputs.ts` * `packages/action/src/reporters/annotations.ts` * `packages/action/src/reporters/comment.ts` * `packages/action/src/reporters/summary.ts` * `packages/benchmark/src/runner.ts` * `packages/utils/src/getFilesizeGzippedInBytes.ts` * `scripts/check-published.ts` --- packages/action/src/checks.ts | 11 +++++++- packages/action/src/index.ts | 9 ++++++- packages/action/src/inputs.ts | 27 ++++++++++++++++++- packages/action/src/minify.ts | 23 +++++++++++++++- packages/action/src/outputs.ts | 14 +++++++++- packages/action/src/reporters/annotations.ts | 18 ++++++++++++- packages/action/src/reporters/comment.ts | 14 +++++++++- packages/action/src/reporters/summary.ts | 17 +++++++++++- packages/benchmark/src/runner.ts | 9 ++++++- .../utils/src/getFilesizeGzippedInBytes.ts | 19 +++++++------ scripts/check-published.ts | 23 +++++++++++++++- 11 files changed, 164 insertions(+), 20 deletions(-) diff --git a/packages/action/src/checks.ts b/packages/action/src/checks.ts index a9136b4d4..bc6872e37 100644 --- a/packages/action/src/checks.ts +++ b/packages/action/src/checks.ts @@ -6,6 +6,15 @@ import type { ActionInputs } from "./types.ts"; +/** + * Check whether a percent reduction violates configured thresholds. + * + * @param reduction - Percent change in size after minification (positive means size decreased; negative means size increased) + * @param inputs - Configuration with: + * - `failOnIncrease`: if true, treat any increase (negative `reduction`) as a violation + * - `minReduction`: minimum allowed reduction percentage; values below this are violations when > 0 + * @returns A human-readable error message describing the threshold violation, or `null` if no violation + */ export function checkThresholds( reduction: number, inputs: Pick @@ -19,4 +28,4 @@ export function checkThresholds( } return null; -} +} \ No newline at end of file diff --git a/packages/action/src/index.ts b/packages/action/src/index.ts index 0ef799678..d8ef90a81 100644 --- a/packages/action/src/index.ts +++ b/packages/action/src/index.ts @@ -14,6 +14,13 @@ import { addAnnotations } from "./reporters/annotations.ts"; import { postPRComment } from "./reporters/comment.ts"; import { generateSummary } from "./reporters/summary.ts"; +/** + * Orchestrates the minification workflow for the GitHub Action. + * + * Parses and validates inputs, runs the minification, and persists outputs. + * Optionally generates a summary, posts a pull-request comment when running in a PR, and adds annotations based on inputs. + * If configured thresholds are violated or an error is thrown, signals action failure with an explanatory message. + */ async function run(): Promise { try { const inputs = parseInputs(); @@ -56,4 +63,4 @@ async function run(): Promise { } } -run(); +run(); \ No newline at end of file diff --git a/packages/action/src/inputs.ts b/packages/action/src/inputs.ts index ace53de07..1a2d93c1e 100644 --- a/packages/action/src/inputs.ts +++ b/packages/action/src/inputs.ts @@ -20,6 +20,22 @@ const DEPRECATED_COMPRESSORS: Record = { sqwish: "sqwish is no longer maintained. Use 'lightningcss' or 'clean-css' instead.", }; +/** + * Parse and validate GitHub Action inputs into an ActionInputs object. + * + * Reads inputs such as `input`, `output`, `compressor`, `type`, `options`, + * reporting flags, benchmarking settings, and other flags, applying defaults + * and validations (including JSON parsing for `options` and required `type` + * for certain compressors). + * + * @returns An object containing the parsed action inputs, including `input`, + * `output`, `compressor`, `type`, `options`, report flags, benchmark settings, + * `minReduction`, `includeGzip`, `workingDirectory`, and `githubToken`. + * + * @throws Error if a compressor that requires a `type` is selected but `type` + * is not provided. + * @throws Error if the `options` input is present but is not valid JSON. + */ export function parseInputs(): ActionInputs { const compressor = getInput("compressor") || "terser"; const type = getInput("type") as "js" | "css" | undefined; @@ -64,6 +80,15 @@ export function parseInputs(): ActionInputs { }; } +/** + * Validates a compressor identifier and emits warnings for deprecated or non-built-in compressors. + * + * Emits a warning when the compressor is listed as deprecated and emits a separate warning + * when the compressor is not recognized as a built-in compressor (indicating it will be + * treated as a custom npm package or local file). + * + * @param compressor - The compressor name or identifier to validate (e.g., "terser", "esbuild", or a custom package) + */ export function validateCompressor(compressor: string): void { const deprecationMessage = DEPRECATED_COMPRESSORS[compressor]; if (deprecationMessage) { @@ -80,4 +105,4 @@ export function validateCompressor(compressor: string): void { export const validateJavaCompressor = validateCompressor; -export { DEPRECATED_COMPRESSORS, JAVA_COMPRESSORS, TYPE_REQUIRED_COMPRESSORS }; +export { DEPRECATED_COMPRESSORS, JAVA_COMPRESSORS, TYPE_REQUIRED_COMPRESSORS }; \ No newline at end of file diff --git a/packages/action/src/minify.ts b/packages/action/src/minify.ts index f3fa493e8..fc634181b 100644 --- a/packages/action/src/minify.ts +++ b/packages/action/src/minify.ts @@ -10,11 +10,32 @@ import { minify } from "@node-minify/core"; import { getFilesizeGzippedRaw, resolveCompressor } from "@node-minify/utils"; import type { ActionInputs, FileResult, MinifyResult } from "./types.ts"; +/** + * Get the size of a file in bytes. + * + * @param filePath - Path to the file + * @returns The file size in bytes + */ async function getFileSize(filePath: string): Promise { const stats = await stat(filePath); return stats.size; } +/** + * Minifies a single input file according to the provided action inputs and returns summary metrics. + * + * Uses `inputs` to resolve input/output paths, run the selected compressor with optional type and options, + * and optionally computes gzipped size. The result includes per-file metrics and aggregated totals. + * + * @param inputs - Configuration for the minification run (input/output paths relative to `workingDirectory`, `compressor` selection, optional `type` and `options`, and `includeGzip` flag) + * @returns A `MinifyResult` containing: + * - `files`: an array with one `FileResult` (`file`, `originalSize`, `minifiedSize`, `reduction`, optional `gzipSize`, `timeMs`) + * - `compressor`: the human-readable compressor label + * - `totalOriginalSize`: original file size in bytes + * - `totalMinifiedSize`: minified file size in bytes + * - `totalReduction`: percentage reduction (0–100) + * - `totalTimeMs`: elapsed minification time in milliseconds + */ export async function runMinification( inputs: ActionInputs ): Promise { @@ -67,4 +88,4 @@ export async function runMinification( totalReduction: reduction, totalTimeMs: timeMs, }; -} +} \ No newline at end of file diff --git a/packages/action/src/outputs.ts b/packages/action/src/outputs.ts index 8880dba9a..51aa9a28d 100644 --- a/packages/action/src/outputs.ts +++ b/packages/action/src/outputs.ts @@ -7,6 +7,13 @@ import { setOutput } from "@actions/core"; import type { BenchmarkResult, MinifyResult } from "./types.ts"; +/** + * Publishes minification metrics from a MinifyResult to GitHub Actions outputs. + * + * Sets the following outputs: `original-size`, `minified-size`, `reduction-percent` (formatted to two decimal places), `time-ms`, and `report-json`. If per-file gzip sizes are present, also sets `gzip-size` to the total gzip size across files. + * + * @param result - Minification summary containing total and per-file metrics used to populate action outputs + */ export function setMinifyOutputs(result: MinifyResult): void { setOutput("original-size", result.totalOriginalSize); setOutput("minified-size", result.totalMinifiedSize); @@ -23,6 +30,11 @@ export function setMinifyOutputs(result: MinifyResult): void { } } +/** + * Exposes benchmark metrics as GitHub Actions outputs. + * + * @param result - The benchmark result object whose properties are published as Action outputs. When present, `recommended` is written to `benchmark-winner`, `bestCompression` to `best-compression`, and `bestSpeed` to `best-speed`. The entire `result` is written as JSON to `benchmark-json`. + */ export function setBenchmarkOutputs(result: BenchmarkResult): void { if (result.recommended) { setOutput("benchmark-winner", result.recommended); @@ -34,4 +46,4 @@ export function setBenchmarkOutputs(result: BenchmarkResult): void { setOutput("best-speed", result.bestSpeed); } setOutput("benchmark-json", JSON.stringify(result)); -} +} \ No newline at end of file diff --git a/packages/action/src/reporters/annotations.ts b/packages/action/src/reporters/annotations.ts index 953409643..b8de9822c 100644 --- a/packages/action/src/reporters/annotations.ts +++ b/packages/action/src/reporters/annotations.ts @@ -10,6 +10,16 @@ import type { MinifyResult } from "../types.ts"; const LOW_REDUCTION_THRESHOLD = 20; const VERY_LOW_REDUCTION_THRESHOLD = 5; +/** + * Emit GitHub Actions annotations for each minified file based on its size reduction. + * + * For each file in `result.files` this reports an annotation scoped to that file: + * - An error if the minified file is larger than the original. + * - A warning if the reduction is very small, suggesting review for dead code or prior minification. + * - A notice if the reduction is low, suggesting the file may already be optimized. + * + * @param result - Minification result containing an array of file reports with reduction percentages and file paths + */ export function addAnnotations(result: MinifyResult): void { for (const file of result.files) { if (file.reduction < 0) { @@ -34,6 +44,12 @@ export function addAnnotations(result: MinifyResult): void { } } +/** + * Record an error annotation for a specific file indicating that minification failed. + * + * @param file - The file path to associate with the annotation + * @param errorMsg - A human-readable message describing the minification error + */ export function addErrorAnnotation(file: string, errorMsg: string): void { error(`Minification failed: ${errorMsg}`, { file }); -} +} \ No newline at end of file diff --git a/packages/action/src/reporters/comment.ts b/packages/action/src/reporters/comment.ts index 2aeae7465..5a4b8315d 100644 --- a/packages/action/src/reporters/comment.ts +++ b/packages/action/src/reporters/comment.ts @@ -11,6 +11,12 @@ import type { MinifyResult } from "../types.ts"; const COMMENT_TAG = ""; +/** + * Posts or updates a pull request comment with a minification report. + * + * @param result - Minification results containing per-file metrics, totals, compressor name, and execution time + * @param githubToken - GitHub API token used to authenticate requests; when `undefined`, the function skips posting + */ export async function postPRComment( result: MinifyResult, githubToken: string | undefined @@ -58,6 +64,12 @@ export async function postPRComment( } } +/** + * Builds the Markdown body for the node-minify PR comment, including a per-file table, totals, and a configuration section. + * + * @param result - Minification results used to populate the report (expects `files`, `totalOriginalSize`, `totalMinifiedSize`, `totalReduction`, `compressor`, and `totalTimeMs`) + * @returns The Markdown string for the PR comment, beginning with `COMMENT_TAG` and containing the file table, total summary, and configuration details + */ function generateCommentBody(result: MinifyResult): string { const filesTable = result.files .map( @@ -86,4 +98,4 @@ ${filesTable} --- *Generated by [node-minify](https://github.com/srod/node-minify) action* `; -} +} \ No newline at end of file diff --git a/packages/action/src/reporters/summary.ts b/packages/action/src/reporters/summary.ts index a92b829dd..5628d2c8c 100644 --- a/packages/action/src/reporters/summary.ts +++ b/packages/action/src/reporters/summary.ts @@ -8,6 +8,14 @@ import { summary } from "@actions/core"; import { prettyBytes } from "@node-minify/utils"; import type { BenchmarkResult, MinifyResult } from "../types.ts"; +/** + * Generate a GitHub Actions summary reporting per-file minification metrics and totals. + * + * Builds a Markdown table with columns File, Original, Minified, Reduction, Gzip, and Time, + * includes the compressor name, and appends a total line showing aggregated original/minified sizes and overall reduction. + * + * @param result - Minification results containing per-file metrics and aggregate totals used to populate the summary + */ export async function generateSummary(result: MinifyResult): Promise { const rows = result.files.map((f) => [ { data: `\`${f.file}\`` }, @@ -40,6 +48,13 @@ export async function generateSummary(result: MinifyResult): Promise { .write(); } +/** + * Generate a Markdown benchmark summary comparing compressors and write it to the GitHub Actions summary. + * + * Builds a table of compressors showing status, size, reduction, gzip size, and time; marks recommended, best-speed, and best-compression entries with badges; and includes the source file and recommended compressor in the summary. + * + * @param result - BenchmarkResult containing the file path, originalSize, a list of compressors with their metrics or errors, and optional recommended/best markers used to annotate the table + */ export async function generateBenchmarkSummary( result: BenchmarkResult ): Promise { @@ -90,4 +105,4 @@ export async function generateBenchmarkSummary( .addBreak() .addRaw(`**Recommended:** ${result.recommended || "N/A"}`) .write(); -} +} \ No newline at end of file diff --git a/packages/benchmark/src/runner.ts b/packages/benchmark/src/runner.ts index 17650a696..658cd6140 100644 --- a/packages/benchmark/src/runner.ts +++ b/packages/benchmark/src/runner.ts @@ -53,6 +53,13 @@ export async function runBenchmark( }; } +/** + * Benchmarks a single input file using the configured compressors and returns per-compressor metrics. + * + * @param file - Path to the input file to benchmark + * @param options - Benchmark configuration (compressors to run, iterations, callbacks, and metric options) + * @returns A FileResult containing the file path, original size in bytes and human-readable form, and an array of CompressorMetrics for each attempted compressor + */ async function benchmarkFile( file: string, options: BenchmarkOptions @@ -228,4 +235,4 @@ function cleanupTempFiles(files: string[]): void { unlinkSync(file); } catch {} } -} +} \ No newline at end of file diff --git a/packages/utils/src/getFilesizeGzippedInBytes.ts b/packages/utils/src/getFilesizeGzippedInBytes.ts index f513bdcfa..34f92daf0 100644 --- a/packages/utils/src/getFilesizeGzippedInBytes.ts +++ b/packages/utils/src/getFilesizeGzippedInBytes.ts @@ -10,10 +10,11 @@ import { isValidFile } from "./isValidFile.ts"; import { prettyBytes } from "./prettyBytes.ts"; /** - * Internal helper to calculate gzipped size of a file using streaming. - * @param file - Path to the file - * @returns Gzipped size in bytes - * @throws {FileOperationError} If file doesn't exist or is not a valid file + * Compute the gzipped size of a file in bytes. + * + * @param file - Path to the file to measure + * @returns The gzipped size in bytes + * @throws FileOperationError if the file does not exist or the path is not a valid file * @internal */ async function getGzipSize(file: string): Promise { @@ -45,12 +46,10 @@ async function getGzipSize(file: string): Promise { } /** - * Get the gzipped file size as a human-readable string. + * Get the gzipped size of a file as a human-readable string. + * * @param file - Path to the file - * @returns Formatted gzipped file size string (e.g., "1.5 kB") - * @example - * const size = await getFilesizeGzippedInBytes('bundle.js') - * console.log(size) // '12.3 kB' + * @returns The gzipped size formatted for display (for example, "1.5 kB") */ export async function getFilesizeGzippedInBytes(file: string): Promise { try { @@ -83,4 +82,4 @@ export async function getFilesizeGzippedRaw(file: string): Promise { error as Error ); } -} +} \ No newline at end of file diff --git a/scripts/check-published.ts b/scripts/check-published.ts index 58af1b8d2..57057a0d4 100644 --- a/scripts/check-published.ts +++ b/scripts/check-published.ts @@ -4,6 +4,11 @@ import { join } from "node:path"; const PACKAGES_DIR = "packages"; +/** + * Lists package subdirectories under PACKAGES_DIR that contain a package.json file. + * + * @returns An array of directory names for packages that have a package.json in their folder + */ function getPackageDirs() { return readdirSync(PACKAGES_DIR, { withFileTypes: true }) .filter((entry) => entry.isDirectory()) @@ -13,6 +18,13 @@ function getPackageDirs() { .map((entry) => entry.name); } +/** + * Checks whether an npm package exists on the registry and whether a specific version is published. + * + * @param packageName - The npm package name to check (e.g., "lodash") + * @param version - The package version to check for publication (e.g., "1.2.3") + * @returns An object with `exists`: `true` if the package is found on the npm registry, `false` otherwise; and `publishedVersion`: `true` if the specified `version` is published, `false` otherwise. + */ async function checkPublished(packageName: string, version: string) { try { const latest = execSync(`npm view ${packageName} version --json`, { @@ -38,6 +50,15 @@ async function checkPublished(packageName: string, version: string) { } } +/** + * Checks all non-private packages under the packages directory and reports their publication status on npm. + * + * Scans each package's package.json, queries npm to determine whether the package exists and whether the current + * package version is published, and prints categorized results to the console: + * - packages not found on npm (new packages) + * - packages with unpublished versions (updates needed) + * - or a confirmation that all packages are up to date + */ async function main() { const dirs = getPackageDirs(); const results = []; @@ -81,4 +102,4 @@ async function main() { main().catch((err) => { console.error("Error checking packages:", err); process.exit(1); -}); +}); \ No newline at end of file