refactor: isolate playback and upload use cases
ci / module boundaries / boundaries (push) Successful in 6s
ci / api / api (push) Successful in 47s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m2s
ci / db / db (push) Successful in 1m4s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 1m30s
ci / ts / ts (packages + web) (push) Successful in 2m54s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m14s
ci / module boundaries / boundaries (push) Successful in 6s
ci / api / api (push) Successful in 47s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m2s
ci / db / db (push) Successful in 1m4s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 1m30s
ci / ts / ts (packages + web) (push) Successful in 2m54s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m14s
This commit is contained in:
1 parent
c84f90e2cc
commit
e158558da3
56 files changed
+3337
-2196
No files matched your search
@@ -0,0 +1,47 @@
|
||||
import { readdirSync, readFileSync } from 'node:fs';
|
||||
import { resolve, relative } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const root = fileURLToPath(new URL('../', import.meta.url));
|
||||
function sources(dir, extension) {
|
||||
return readdirSync(resolve(root, dir), { withFileTypes: true }).flatMap((entry) => {
|
||||
const path = `${dir}/${entry.name}`;
|
||||
return entry.isDirectory() ? sources(path, extension) : path.endsWith(extension) ? [path] : [];
|
||||
});
|
||||
}
|
||||
|
||||
// Check the boundaries introduced by the refactor. Existing query projections and
|
||||
// composition roots are deliberately outside these use-case-specific rules.
|
||||
const rules = [
|
||||
{
|
||||
files: sources('packages/core/src', '.ts'),
|
||||
forbidden: /(?:from\s*|import\s*)['"](?:[^'"]*apps\/|(?:@react-native[^'"]*|react(?:-native)?(?:[-\/]|['"]))|expo(?:[-\/]|['"])|\.\.[^'"]*(?:web|mobile|desktop)\/)/,
|
||||
message: 'core must not import platform/UI implementations',
|
||||
},
|
||||
{
|
||||
files: ['service.go', 'worker.go', 'placement.go', 'batch.go'].map((p) => `services/api/internal/uploads/${p}`),
|
||||
forbidden: /github\.com\/jackc\/pgx|\.store\.pool\b|\b(?:tx|pool)\.(?:Exec|Query|QueryRow)\(/,
|
||||
message: 'upload SQL and pgx belong in store/persistence adapters',
|
||||
},
|
||||
{
|
||||
files: sources('apps/web/src/features/uploads', '.tsx').filter((p) => !p.endsWith('/common.tsx')),
|
||||
forbidden: /\b(?:session|client)\.(?:GET|POST|PUT|PATCH|DELETE)\(/,
|
||||
message: 'upload views must use feature API/queries',
|
||||
},
|
||||
];
|
||||
|
||||
const errors = [];
|
||||
for (const rule of rules) {
|
||||
for (const path of rule.files) {
|
||||
const text = readFileSync(resolve(root, path), 'utf8');
|
||||
text.split(/\r?\n/).forEach((line, i) => {
|
||||
if (rule.forbidden.test(line)) errors.push(`${relative(root, resolve(root, path))}:${i + 1}: ${rule.message}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (errors.length) {
|
||||
console.error(errors.join('\n'));
|
||||
process.exitCode = 1;
|
||||
} else {
|
||||
console.log('Module boundary checks passed.');
|
||||
}
|
||||
Reference in new issue
Block a user