perf: reduce 3D runtime artifacts (#7)
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
import { createHash } from 'node:crypto';
|
||||
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
||||
import { dirname, relative, resolve, sep } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const projectRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
||||
const distRoot = resolve(projectRoot, 'apps/viewer-3d/dist');
|
||||
const maxDistBytes = 9_000_000;
|
||||
|
||||
function listFiles(directory) {
|
||||
return readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
|
||||
const path = resolve(directory, entry.name);
|
||||
return entry.isDirectory() ? listFiles(path) : [path];
|
||||
});
|
||||
}
|
||||
|
||||
function toRelative(path) {
|
||||
return relative(distRoot, path).split(sep).join('/');
|
||||
}
|
||||
|
||||
if (!existsSync(distRoot)) {
|
||||
throw new Error('3D production dist가 없습니다. 먼저 npm run build를 실행하세요.');
|
||||
}
|
||||
|
||||
const files = listFiles(distRoot);
|
||||
const relativeFiles = files.map(toRelative);
|
||||
const totalBytes = files.reduce((sum, path) => sum + statSync(path).size, 0);
|
||||
const failures = [];
|
||||
|
||||
for (const path of relativeFiles) {
|
||||
if (path.endsWith('.map')) failures.push(`production source map 포함: ${path}`);
|
||||
if (path === 'web-ifc/web-ifc-mt.wasm') {
|
||||
failures.push(`미사용 multi-thread IFC WASM 포함: ${path}`);
|
||||
}
|
||||
if (path.startsWith('draco/') || path.startsWith('basis/')) {
|
||||
failures.push(`Three.js bundled decoder의 public 중복 포함: ${path}`);
|
||||
}
|
||||
}
|
||||
|
||||
for (const pattern of [
|
||||
/^assets\/draco_decoder-.+\.wasm$/,
|
||||
/^assets\/basis_transcoder-.+\.wasm$/,
|
||||
/^web-ifc\/web-ifc\.wasm$/,
|
||||
]) {
|
||||
if (!relativeFiles.some((path) => pattern.test(path))) {
|
||||
failures.push(`필수 runtime artifact 누락: ${pattern}`);
|
||||
}
|
||||
}
|
||||
|
||||
const hashes = new Map();
|
||||
for (const path of files) {
|
||||
if (statSync(path).size < 50_000) continue;
|
||||
const hash = createHash('sha256').update(readFileSync(path)).digest('hex');
|
||||
const paths = hashes.get(hash) ?? [];
|
||||
paths.push(toRelative(path));
|
||||
hashes.set(hash, paths);
|
||||
}
|
||||
for (const paths of hashes.values()) {
|
||||
if (paths.length > 1) failures.push(`50 kB 이상 중복 artifact: ${paths.join(', ')}`);
|
||||
}
|
||||
|
||||
if (totalBytes > maxDistBytes) {
|
||||
failures.push(
|
||||
`3D production dist budget 초과: ${totalBytes} bytes > ${maxDistBytes} bytes`,
|
||||
);
|
||||
}
|
||||
|
||||
if (failures.length > 0) {
|
||||
console.error(failures.map((failure) => `- ${failure}`).join('\n'));
|
||||
process.exitCode = 1;
|
||||
} else {
|
||||
console.log(
|
||||
`[artifact-check] ${relativeFiles.length} files, ${totalBytes} bytes, duplicate 0`,
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user