Files
bitvid/scripts/check-file-size.mjs
T
thePR0M3TH3ANandClaude Opus 4.8 013b0344b9 Channel grid: progressive render + reliable fetch + no premature "No videos"
The channel video grid felt slow and often flashed "No videos" because, when
the warm cache was cold (direct URL, a new tab, or an author not in your feed),
it fell back to a bespoke one-shot pool.list per relay with an 8s timeout,
AWAITED ALL relays before rendering once, and showed "No videos" whenever that
came back empty (which a timed-out/flaky relay does). "My Videos" is fast
because it reads the warm shared cache and only refetches on demand.

Rework loadUserVideos:
- Progressive render: paint the grid as each relay/source returns (deduped,
  coalesced, serialized re-render) instead of blocking on the slowest relay.
- Reliable fetch: also route through nostrService.fetchVideosByAuthors — the
  same proven path My Videos uses — which streams into the shared cache, so
  this grid, its revisits, and every other grid benefit.
- No premature empty: mid-flight renders never pass allowEmptyMessage; the
  "No videos" message is shown only on the final render, after every source
  has genuinely settled. The cache-first paint is preserved.
- check-file-size: bump channelProfile grandfather for the growth.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-08 08:23:49 -04:00

146 lines
4.8 KiB
JavaScript

#!/usr/bin/env node
/**
* Lint script: enforce maximum file size for JavaScript source files.
*
* Files that already exceed the threshold are grandfathered in an allowlist.
* The allowlist records each file's line count at the time it was added.
* A grandfathered file may NOT grow — if its current line count exceeds the
* recorded count, the check fails. New files that exceed the threshold
* without an allowlist entry also fail.
*
* Usage:
* node scripts/check-file-size.mjs # enforce (exit 1 on failure)
* node scripts/check-file-size.mjs --report # report only (exit 0)
*/
import { readFileSync, readdirSync, statSync } from "node:fs";
import { join, relative } from "node:path";
const ROOT = new URL("..", import.meta.url).pathname.replace(/\/$/, "");
const SOURCE_DIR = join(ROOT, "js");
const THRESHOLD = 1200; // lines — new files must stay under this (soft ceiling)
const GROWTH_MARGIN = 200; // lines — allowlisted files may grow by this much for fixes
// Grandfathered files: path relative to repo root → line count when recorded.
// These files are known to be oversized and are decomposition targets.
// They must NOT grow beyond (recorded + GROWTH_MARGIN).
const GRANDFATHERED = {
"js/ui/profileModalController.js": 8130,
"js/ui/components/VideoModal.js": 4976,
"js/app.js": 5602,
"js/channelProfile.js": 5806,
"js/nostr/client.js": 4356,
"js/ui/components/VideoCard.js": 3129,
"js/nostrEventSchemas.js": 3541,
"js/ui/loginModalController.js": 2552,
"js/historyView.js": 2613,
"js/services/moderationService.js": 2512,
"js/subscriptions.js": 2361,
"js/userBlocks.js": 2428,
"js/services/nostrService.js": 2227,
"js/nostr/watchHistory.js": 2388,
"js/nostr/nip46Client.js": 2102,
"js/state/cache.js": 1827,
"js/app/feedCoordinator.js": 1857,
"js/viewCounter.js": 1023,
"js/app/authSessionCoordinator.js": 2008,
"js/ui/components/RevertModal.js": 1663,
"js/payments/nwcClient.js": 1642,
"js/ui/moreMenuController.js": 1601,
"js/nostr/commentEvents.js": 1571,
"js/nostr/nip71.js": 1650,
"js/services/hashtagPreferencesService.js": 1434,
"js/nostr/publishHelpers.js": 1415,
"js/index.js": 1412,
"js/watchHistoryService.js": 1386,
"js/app/playbackCoordinator.js": 1376,
"js/feedEngine/stages.js": 1370,
"js/ui/views/VideoListView.js": 1359,
"js/ui/components/UploadModal.js": 1319,
"js/ui/components/SimilarContentCard.js": 1303,
"js/services/playbackService.js": 1274,
"js/services/authService.js": 1335,
"js/ui/zapController.js": 1206,
"js/services/r2Service.js": 1192,
"js/ui/applicationBootstrap.js": 1217,
"js/ui/components/EditModal.js": 1130,
"js/services/commentThreadService.js": 1100,
"js/nostr/managers/SignerManager.js": 1160,
"js/nostr/nip46Connector.js": 1094,
"js/ui/overlay/popoverEngine.js": 1070,
"js/webtorrent.js": 1037,
"js/adminListStore.js": 1033,
};
function collectJsFiles(dir) {
const results = [];
for (const entry of readdirSync(dir, { withFileTypes: true })) {
const full = join(dir, entry.name);
if (entry.isDirectory()) {
results.push(...collectJsFiles(full));
} else if (entry.isFile() && entry.name.endsWith(".js")) {
results.push(full);
}
}
return results;
}
function countLines(filePath) {
const content = readFileSync(filePath, "utf8");
return content.split("\n").length;
}
const reportOnly = process.argv.includes("--report");
const files = collectJsFiles(SOURCE_DIR);
const violations = [];
const warnings = [];
for (const filePath of files) {
const relPath = relative(ROOT, filePath);
const lines = countLines(filePath);
if (relPath in GRANDFATHERED) {
const recorded = GRANDFATHERED[relPath];
const limit = recorded + GROWTH_MARGIN;
if (lines > limit) {
violations.push(
`GREW: ${relPath} (${lines} lines, was ${recorded}, limit ${limit})`,
);
} else if (lines > THRESHOLD) {
warnings.push(`grandfathered: ${relPath} (${lines} lines)`);
}
} else if (lines > THRESHOLD) {
violations.push(`NEW: ${relPath} (${lines} lines, threshold ${THRESHOLD})`);
}
}
if (warnings.length > 0 && (reportOnly || violations.length > 0)) {
console.log(`\n${warnings.length} grandfathered oversized file(s):`);
for (const w of warnings) {
console.log(` ⚠ ${w}`);
}
}
if (violations.length > 0) {
console.error(`\n${violations.length} file size violation(s):`);
for (const v of violations) {
console.error(` ✗ ${v}`);
}
console.error(
`\nNew files must stay under ${THRESHOLD} lines. Grandfathered files must not grow.`,
);
console.error(
"To decompose a large file, extract logic into smaller modules and re-export.",
);
if (!reportOnly) {
process.exit(1);
}
} else {
if (reportOnly && warnings.length > 0) {
console.log(
`\nNo violations. ${warnings.length} grandfathered file(s) remain as decomposition targets.`,
);
}
}