mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-01-16 08:08:18 +00:00
Fixes missing dependencies in useMemo and useCallback hooks to prevent stale closures and potential bugs. Adds null/undefined guards before navigation in music components to prevent crashes when attempting to navigate with missing IDs. Corrects query key from "company" to "genre" in genre page to ensure proper cache invalidation. Updates Jellyseerr references to Seerr throughout documentation and error messages for consistency. Improves type safety by adding error rejection handling in SeerrApi and memoizing components to optimize re-renders.
257 lines
7.2 KiB
JavaScript
257 lines
7.2 KiB
JavaScript
const { execFileSync } = require("node:child_process");
|
|
const process = require("node:process");
|
|
|
|
// Enhanced ANSI color codes and styles
|
|
const colors = {
|
|
red: "\x1b[31m",
|
|
green: "\x1b[32m",
|
|
yellow: "\x1b[33m",
|
|
blue: "\x1b[34m",
|
|
magenta: "\x1b[35m",
|
|
cyan: "\x1b[36m",
|
|
white: "\x1b[37m",
|
|
gray: "\x1b[90m",
|
|
reset: "\x1b[0m",
|
|
bold: "\x1b[1m",
|
|
dim: "\x1b[2m",
|
|
underline: "\x1b[4m",
|
|
bg: {
|
|
red: "\x1b[41m",
|
|
green: "\x1b[42m",
|
|
yellow: "\x1b[43m",
|
|
blue: "\x1b[44m",
|
|
},
|
|
};
|
|
|
|
const border = "━".repeat(80);
|
|
|
|
// Center the title within the border
|
|
const title = "🔥 STREAMYFIN TYPESCRIPT CHECK";
|
|
const titlePadding = Math.floor((80 - title.length) / 2);
|
|
const centeredTitle = " ".repeat(titlePadding) + title;
|
|
|
|
const useColor = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
|
|
function log(message, color = "") {
|
|
if (useColor && color) {
|
|
console.log(`${color}${message}${colors.reset}`);
|
|
} else {
|
|
console.log(String(message));
|
|
}
|
|
}
|
|
|
|
function formatError(errorLine) {
|
|
if (!useColor) return errorLine;
|
|
|
|
// Color file paths in cyan
|
|
let formatted = errorLine.replace(
|
|
/^([^(]+\([^)]+\):)/,
|
|
`${colors.cyan}$1${colors.reset}`,
|
|
);
|
|
|
|
// Color error codes in red bold
|
|
formatted = formatted.replace(
|
|
/(error TS\d+:)/g,
|
|
`${colors.red}${colors.bold}$1${colors.reset}`,
|
|
);
|
|
|
|
// Color type names in yellow
|
|
formatted = formatted.replace(
|
|
/(Type '[^']*')/g,
|
|
`${colors.yellow}$1${colors.reset}`,
|
|
);
|
|
|
|
// Color property names in magenta
|
|
formatted = formatted.replace(
|
|
/(Property '[^']*')/g,
|
|
`${colors.magenta}$1${colors.reset}`,
|
|
);
|
|
|
|
return formatted;
|
|
}
|
|
|
|
function parseErrorsAndCreateSummary(errorOutput) {
|
|
const lines = errorOutput.split("\n").filter((line) => line.trim());
|
|
const errorsByFile = new Map();
|
|
const formattedErrors = [];
|
|
|
|
let currentError = [];
|
|
|
|
for (const line of lines) {
|
|
const trimmedLine = line.trim();
|
|
if (!trimmedLine) continue;
|
|
|
|
// Check if this is the start of a new error (has file path and error code)
|
|
const errorMatch = line.match(/^([^(]+\([^)]+\):)\s*(error TS\d+:)/);
|
|
|
|
if (errorMatch) {
|
|
// If we have a previous error, add it to the list
|
|
if (currentError.length > 0) {
|
|
formattedErrors.push(currentError.join("\n"));
|
|
currentError = [];
|
|
}
|
|
|
|
// Extract file info for summary
|
|
const filePath = errorMatch[1].split("(")[0];
|
|
if (!errorsByFile.has(filePath)) {
|
|
errorsByFile.set(filePath, 0);
|
|
}
|
|
errorsByFile.set(filePath, errorsByFile.get(filePath) + 1);
|
|
|
|
// Start new error
|
|
currentError.push(formatError(line));
|
|
} else if (currentError.length > 0) {
|
|
// This is a continuation of the current error
|
|
currentError.push(` ${colors.gray}${line}${colors.reset}`);
|
|
} else if (line.match(/Found \d+ errors? in \d+ files?/)) {
|
|
// Skip the summary line; no action needed for this line
|
|
} else {
|
|
// Standalone line
|
|
formattedErrors.push(formatError(line));
|
|
}
|
|
}
|
|
|
|
// Add the last error if exists
|
|
if (currentError.length > 0) {
|
|
formattedErrors.push(currentError.join("\n"));
|
|
}
|
|
|
|
return { formattedErrors, errorsByFile };
|
|
}
|
|
|
|
function createErrorSummaryTable(errorsByFile) {
|
|
if (errorsByFile.size === 0) return "";
|
|
|
|
const sortedFiles = Array.from(errorsByFile.entries()).sort(
|
|
(a, b) => b[1] - a[1],
|
|
); // Sort by error count descending
|
|
|
|
let table = `\n${colors.gray}${colors.bold}Errors Files${colors.reset}\n`;
|
|
|
|
for (const [file, count] of sortedFiles) {
|
|
const paddedCount = String(count).padStart(6);
|
|
table += `${colors.red}${paddedCount}${colors.reset} ${colors.cyan}${file}${colors.reset}\n`;
|
|
}
|
|
|
|
return table;
|
|
}
|
|
|
|
function runTypeCheck() {
|
|
const extraArgs = process.argv.slice(2);
|
|
|
|
// Prefer local TypeScript binary when available
|
|
const runnerArgs = ["-p", "tsconfig.json", "--noEmit", ...extraArgs];
|
|
let execArgs = null;
|
|
try {
|
|
const tscBin = require.resolve("typescript/bin/tsc");
|
|
execArgs = { cmd: process.execPath, args: [tscBin, ...runnerArgs] };
|
|
} catch {
|
|
// fallback to PATH tsc
|
|
execArgs = {
|
|
cmd: "tsc",
|
|
args: ["-p", "tsconfig.json", "--noEmit", ...extraArgs],
|
|
};
|
|
}
|
|
|
|
try {
|
|
log(
|
|
`🔍 ${colors.bold}Running TypeScript type check...${colors.reset} ${colors.gray}${extraArgs.join(" ")}${colors.reset}`.trim(),
|
|
colors.blue,
|
|
);
|
|
|
|
const MAX_BUFFER_SIZE = 64 * 1024 * 1024; // 64MB
|
|
|
|
execFileSync(execArgs.cmd, execArgs.args, {
|
|
encoding: "utf8",
|
|
stdio: ["ignore", "pipe", "pipe"],
|
|
maxBuffer: MAX_BUFFER_SIZE,
|
|
env: { ...process.env, FORCE_COLOR: "0" },
|
|
});
|
|
|
|
log(
|
|
`✅ ${colors.bold}TypeScript check passed${colors.reset} - no errors found!`,
|
|
colors.green,
|
|
);
|
|
return { ok: true };
|
|
} catch (error) {
|
|
const errorOutput = (error && (error.stderr || error.stdout)) || "";
|
|
|
|
// Filter out seerr utils errors - this is a third-party git submodule
|
|
// that generates a large volume of known type errors
|
|
const filteredLines = errorOutput.split("\n").filter((line) => {
|
|
const trimmedLine = line.trim();
|
|
return trimmedLine && !trimmedLine.includes("utils/jellyseerr");
|
|
});
|
|
|
|
if (filteredLines.length > 0) {
|
|
// Count TypeScript error occurrences (TS####)
|
|
const remainingMatches = (
|
|
filteredLines.join("\n").match(/\berror\s+TS\d+:/gi) || []
|
|
).length;
|
|
|
|
// Parse errors and create formatted output with summary
|
|
const { formattedErrors, errorsByFile } = parseErrorsAndCreateSummary(
|
|
filteredLines.join("\n"),
|
|
);
|
|
|
|
// Enhanced error header
|
|
log(
|
|
`\n${colors.bg.red} ERROR ${colors.reset} ${colors.red}${colors.bold}TypeScript errors found:${colors.reset}`,
|
|
);
|
|
console.log();
|
|
|
|
// Display errors with spacing between each error
|
|
for (let i = 0; i < formattedErrors.length; i++) {
|
|
console.log(formattedErrors[i]);
|
|
|
|
// Add spacing between errors (but not after the last one)
|
|
if (i < formattedErrors.length - 1) {
|
|
console.log(); // Empty line between errors
|
|
}
|
|
}
|
|
|
|
// Create and display summary table
|
|
const summaryTable = createErrorSummaryTable(errorsByFile);
|
|
if (summaryTable) {
|
|
console.log(summaryTable);
|
|
}
|
|
|
|
// Clean summary - just the error count
|
|
const errorIcon = "🚨";
|
|
log(
|
|
`${errorIcon} ${colors.red}${colors.bold}${remainingMatches} TypeScript error${remainingMatches !== 1 ? "s" : ""}${colors.reset}`,
|
|
"",
|
|
);
|
|
|
|
return { ok: false };
|
|
}
|
|
|
|
log(
|
|
`✅ ${colors.bold}TypeScript check passed${colors.reset} ${colors.gray}(seerr utils errors ignored)${colors.reset}`,
|
|
colors.green,
|
|
);
|
|
return { ok: true };
|
|
}
|
|
}
|
|
|
|
// Enhanced header
|
|
console.log(`${colors.blue}${colors.bold}${border}${colors.reset}`);
|
|
console.log(`${colors.blue}${colors.bold}${centeredTitle}${colors.reset}`);
|
|
console.log(`${colors.blue}${colors.bold}${border}${colors.reset}`);
|
|
console.log();
|
|
|
|
// Main execution
|
|
const result = runTypeCheck();
|
|
|
|
console.log();
|
|
if (!result.ok) {
|
|
log(
|
|
`${colors.red}${colors.bold}🔥 Typecheck failed - please fix the errors above${colors.reset}`,
|
|
);
|
|
process.exitCode = 1;
|
|
} else {
|
|
log(
|
|
`${colors.green}${colors.bold}🎉 All checks passed! Ready to ship 🚀${colors.reset}`,
|
|
);
|
|
}
|