Remove rnadom script

This commit is contained in:
Andras Schmelczer 2026-05-16 14:21:10 +01:00
parent 9256377c13
commit 20433bd9f0
5 changed files with 934 additions and 248 deletions

View file

@ -30,10 +30,11 @@ jobs:
npx playwright install --with-deps chromium
- name: Lint
run: npm run lint
run: npm run lint:check
- name: Typecheck
run: |
npm run unused:check
npm run typecheck
npm run typecheck:e2e

931
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -8,18 +8,17 @@
"dev": "vite --host 0.0.0.0",
"build": "vite build",
"preview": "vite preview",
"lint": "npm run lint:check",
"lint:check": "eslint --rule \"prettier/prettier: off\" \"src/**/*.ts\" && npm run unused:check",
"lint:fix": "eslint --fix \"src/**/*.ts\"",
"format": "prettier --write \"index.html\" \"src/**/*.{ts,scss,json,html}\" \"scripts/**/*.mjs\" \"e2e/**/*.ts\" \".forgejo/workflows/*.yml\" \"*.{json,js,ts,md}\" \".prettierrc\"",
"format:check": "prettier --check \"index.html\" \"src/**/*.{ts,scss,json,html}\" \"scripts/**/*.mjs\" \"e2e/**/*.ts\" \".forgejo/workflows/*.yml\" \"*.{json,js,ts,md}\" \".prettierrc\"",
"format": "prettier --write \"index.html\" \"src/**/*.{ts,scss,json,html}\" \"e2e/**/*.ts\" \".forgejo/workflows/*.yml\" \"*.{json,js,ts,md}\" \".prettierrc\"",
"format:check": "prettier --check \"index.html\" \"src/**/*.{ts,scss,json,html}\" \"e2e/**/*.ts\" \".forgejo/workflows/*.yml\" \"*.{json,js,ts,md}\" \".prettierrc\"",
"typecheck": "tsc --noEmit",
"typecheck:e2e": "tsc --noEmit --project tsconfig.playwright.json",
"test": "vitest run",
"test:e2e": "npm run build && playwright test",
"test:e2e:ui": "npm run build && playwright test --ui",
"test:watch": "vitest",
"unused:check": "node scripts/check-unused-exports.mjs",
"unused:check": "knip --exports --include-entry-exports",
"generate-icons": "pwa-assets-generator",
"update": "ncu"
},
@ -57,6 +56,7 @@
"eslint-plugin-unused-imports": "^4.4.1",
"gl-matrix": "^3.4.4",
"globals": "^17.6.0",
"knip": "^6.14.1",
"lightningcss": "^1.32.0",
"npm-check-updates": "^22.1.0",
"prettier": "^3.8.3",

View file

@ -1,43 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Not found</title>
<meta name="theme-color" content="#b7455e" />
<meta name="viewport" content="initial-scale=1.0" />
<style>
html,
body {
height: 100%;
}
body {
margin: 0;
display: flex;
justify-content: center;
align-items: center;
background-color: #b7455e;
}
div {
text-align: center;
}
h1,
a {
font-family: 'Roboto', 'Helvetica Neue', sans-serif;
font-weight: 100;
font-size: 3rem;
color: white;
padding: 2rem;
}
</style>
</head>
<body>
<div>
<h1>Page not found.</h1>
<a href="/">Go back</a>
</div>
</body>
</html>

View file

@ -1,197 +0,0 @@
import { existsSync, readdirSync, readFileSync } from 'node:fs';
import path from 'node:path';
import ts from 'typescript';
const projectRoot = process.cwd();
const sourceRoot = path.join(projectRoot, 'src');
const toPosix = (value) => value.split(path.sep).join('/');
const listTypeScriptFiles = (directory) =>
readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
const entryPath = path.join(directory, entry.name);
if (entry.isDirectory()) {
return listTypeScriptFiles(entryPath);
}
return entry.isFile() && entry.name.endsWith('.ts') && !entry.name.endsWith('.d.ts')
? [entryPath]
: [];
});
const files = listTypeScriptFiles(sourceRoot);
const fileSet = new Set(files.map((file) => path.resolve(file)));
const resolveModule = (fromFile, specifier) => {
if (!specifier.startsWith('.')) {
return null;
}
const base = path.resolve(path.dirname(fromFile), specifier);
const candidates = [
`${base}.ts`,
path.join(base, 'index.ts'),
base.endsWith('.ts') ? base : null,
].filter(Boolean);
return (
candidates.find((candidate) => existsSync(candidate) && fileSet.has(candidate)) ??
null
);
};
const exportKey = (file, name) => `${path.resolve(file)}:${name}`;
const isExported = (node) =>
ts.canHaveModifiers(node) &&
(ts.getModifiers(node) ?? []).some(
(modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword
);
const isDefaultExported = (node) =>
ts.canHaveModifiers(node) &&
(ts.getModifiers(node) ?? []).some(
(modifier) => modifier.kind === ts.SyntaxKind.DefaultKeyword
);
const exportedDeclarations = new Map();
const usedExports = new Set();
const wildcardUsedFiles = new Set();
const markUsed = (fromFile, name) => {
usedExports.add(exportKey(fromFile, name));
};
const collectImportUsage = (file, sourceFile) => {
sourceFile.forEachChild((node) => {
if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
const resolved = resolveModule(file, node.moduleSpecifier.text);
if (!resolved || !node.importClause) {
return;
}
if (node.importClause.name) {
markUsed(resolved, 'default');
}
const namedBindings = node.importClause.namedBindings;
if (namedBindings && ts.isNamedImports(namedBindings)) {
namedBindings.elements.forEach((element) => {
markUsed(resolved, (element.propertyName ?? element.name).text);
});
} else if (namedBindings && ts.isNamespaceImport(namedBindings)) {
wildcardUsedFiles.add(path.resolve(resolved));
}
return;
}
if (
ts.isExportDeclaration(node) &&
node.moduleSpecifier &&
ts.isStringLiteral(node.moduleSpecifier)
) {
const resolved = resolveModule(file, node.moduleSpecifier.text);
if (!resolved) {
return;
}
if (!node.exportClause) {
wildcardUsedFiles.add(path.resolve(resolved));
return;
}
if (ts.isNamedExports(node.exportClause)) {
node.exportClause.elements.forEach((element) => {
markUsed(resolved, (element.propertyName ?? element.name).text);
});
}
}
});
};
const collectExportedDeclarations = (file, sourceFile) => {
if (file.endsWith('.test.ts')) {
return;
}
sourceFile.forEachChild((node) => {
if (ts.isVariableStatement(node) && isExported(node)) {
node.declarationList.declarations.forEach((declaration) => {
if (ts.isIdentifier(declaration.name)) {
exportedDeclarations.set(exportKey(file, declaration.name.text), {
file,
name: declaration.name.text,
});
}
});
return;
}
if (
(ts.isFunctionDeclaration(node) ||
ts.isClassDeclaration(node) ||
ts.isInterfaceDeclaration(node) ||
ts.isTypeAliasDeclaration(node) ||
ts.isEnumDeclaration(node)) &&
isExported(node)
) {
if (isDefaultExported(node)) {
exportedDeclarations.set(exportKey(file, 'default'), { file, name: 'default' });
return;
}
if (node.name) {
exportedDeclarations.set(exportKey(file, node.name.text), {
file,
name: node.name.text,
});
}
return;
}
if (
ts.isExportDeclaration(node) &&
!node.moduleSpecifier &&
node.exportClause &&
ts.isNamedExports(node.exportClause)
) {
node.exportClause.elements.forEach((element) => {
exportedDeclarations.set(exportKey(file, element.name.text), {
file,
name: element.name.text,
});
});
}
});
};
const parsedFiles = files.map((file) => ({
file,
sourceFile: ts.createSourceFile(
file,
readFileSync(file, 'utf8'),
ts.ScriptTarget.Latest,
true,
ts.ScriptKind.TS
),
}));
parsedFiles.forEach(({ file, sourceFile }) => collectImportUsage(file, sourceFile));
parsedFiles.forEach(({ file, sourceFile }) =>
collectExportedDeclarations(file, sourceFile)
);
const unusedExports = Array.from(exportedDeclarations.entries())
.filter(
([key, declaration]) =>
!usedExports.has(key) && !wildcardUsedFiles.has(declaration.file)
)
.map(([, declaration]) => declaration)
.sort((left, right) =>
`${left.file}:${left.name}`.localeCompare(`${right.file}:${right.name}`)
);
if (unusedExports.length > 0) {
console.error('Unused exported declarations found:');
unusedExports.forEach(({ file, name }) => {
console.error(`- ${toPosix(path.relative(projectRoot, file))}: ${name}`);
});
process.exitCode = 1;
}