Remove rnadom script
This commit is contained in:
parent
9256377c13
commit
20433bd9f0
5 changed files with 934 additions and 248 deletions
|
|
@ -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
931
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue