170 lines
5.6 KiB
JavaScript
170 lines
5.6 KiB
JavaScript
#!/usr/bin/env node
|
|
import { readFileSync } from "node:fs";
|
|
import path from "node:path";
|
|
|
|
import { resolveInsideRoot } from "../src/core/workspace.js";
|
|
import { analyzeLive2DMinJsSource } from "../src/tools/deobfuscation.js";
|
|
import {
|
|
auditLive2DModuleMigrations,
|
|
type Live2DModuleMigrationDecisionCatalog,
|
|
type Live2DModuleMigrationDraftManifest,
|
|
} from "../src/tools/live2dModuleMigrations.js";
|
|
import {
|
|
auditLive2DIdentifierNameDecisionCatalog,
|
|
assertLive2DVariableDecisionCatalogIsOffline,
|
|
auditLive2DVariableNameDecisionCatalog,
|
|
type Live2DIdentifierNameDecisionCatalog,
|
|
type Live2DVariableNameDecisionCatalog,
|
|
type Live2DVariableNameSummary,
|
|
} from "../src/tools/live2dVariableNames.js";
|
|
|
|
interface CliOptions {
|
|
identifierDecisionPath: string;
|
|
implementationRoot: string;
|
|
manifestPath: string;
|
|
moduleDecisionPath: string;
|
|
productionRoot: string;
|
|
sourcePath: string;
|
|
variableDecisionPath: string;
|
|
}
|
|
|
|
const EXPECTED_SUMMARY: Live2DVariableNameSummary = {
|
|
directImplementationBindings: {
|
|
classifiedSynthetic: 67,
|
|
coveredFromSource: 1325,
|
|
locals: 810,
|
|
parameters: 582,
|
|
total: 1392,
|
|
},
|
|
functions: 567,
|
|
helperSyntheticBindings: 5,
|
|
ignoredLeadingImplementationThis: 116,
|
|
nonDirectSourceMappings: 413,
|
|
reviewedBindingSets: 76,
|
|
sourceBindings: { locals: 1169, parameters: 578, total: 1747 },
|
|
sourceDispositions: {
|
|
inlined: 233,
|
|
merged: 110,
|
|
"omitted-unreachable": 1,
|
|
"removed-source-defect": 5,
|
|
renamed: 1398,
|
|
},
|
|
syntheticDispositions: {
|
|
"defensive-extension": 3,
|
|
"semantic-split": 53,
|
|
"synthetic-helper": 16,
|
|
},
|
|
};
|
|
|
|
/** Parses only the workspace-bounded paths consumed by the current-state audit. */
|
|
function parseOptions(argv: string[]): CliOptions {
|
|
const args = new Map<string, string>();
|
|
for (let index = 0; index < argv.length; index += 2) {
|
|
const key = argv[index];
|
|
const value = argv[index + 1];
|
|
if (!key?.startsWith("--") || value === undefined) {
|
|
throw new Error(`Invalid Live2D variable audit argument near ${key ?? "<end>"}`);
|
|
}
|
|
args.set(key.slice(2), value);
|
|
}
|
|
return {
|
|
identifierDecisionPath: resolveInsideRoot(
|
|
args.get("identifier-decisions") ??
|
|
"docs/live2d-deobfuscation/identifier-name-decisions.json",
|
|
),
|
|
implementationRoot: resolveInsideRoot(
|
|
args.get("implementation-root") ??
|
|
"Vue/kt-blog-web/src/components/blog/live2d/vendor/cubism2Core",
|
|
),
|
|
manifestPath: resolveInsideRoot(
|
|
args.get("manifest") ?? "docs/live2d-deobfuscation/module-splits.json",
|
|
),
|
|
moduleDecisionPath: resolveInsideRoot(
|
|
args.get("module-decisions") ??
|
|
"docs/live2d-deobfuscation/module-migration-decisions.json",
|
|
),
|
|
productionRoot: resolveInsideRoot(
|
|
args.get("production-root") ?? "Vue/kt-blog-web/src/components/blog/live2d",
|
|
),
|
|
sourcePath: resolveInsideRoot(
|
|
args.get("source") ?? "Vue/kt-blog-web/public/live2d/wordpress-moc/live2d.min.js",
|
|
),
|
|
variableDecisionPath: resolveInsideRoot(
|
|
args.get("variable-decisions") ??
|
|
"docs/live2d-deobfuscation/variable-name-decisions.json",
|
|
),
|
|
};
|
|
}
|
|
|
|
/** Reads one typed JSON catalog and leaves semantic validation to its audit function. */
|
|
function readJson<T>(filePath: string): T {
|
|
return JSON.parse(readFileSync(filePath, "utf8")) as T;
|
|
}
|
|
|
|
/** Runs function-level migration proof before variable-level coverage and offline-only gates. */
|
|
function main(): void {
|
|
const options = parseOptions(process.argv.slice(2));
|
|
const source = readFileSync(options.sourcePath, "utf8");
|
|
const sourceAudit = analyzeLive2DMinJsSource(source, {
|
|
sourcePath: path
|
|
.relative(resolveInsideRoot("."), options.sourcePath)
|
|
.replaceAll("\\", "/"),
|
|
});
|
|
const moduleCatalog = readJson<Live2DModuleMigrationDecisionCatalog>(
|
|
options.moduleDecisionPath,
|
|
);
|
|
const migration = auditLive2DModuleMigrations({
|
|
decisions: moduleCatalog,
|
|
implementationRoot: options.implementationRoot,
|
|
moduleManifest: readJson<Live2DModuleMigrationDraftManifest>(options.manifestPath),
|
|
source,
|
|
sourceFunctions: sourceAudit.functions,
|
|
workspaceRoot: resolveInsideRoot("."),
|
|
});
|
|
if (
|
|
migration.summary.migrated !== 567 ||
|
|
migration.summary.omittedUnreachable !== 60 ||
|
|
migration.summary.pending !== 0 ||
|
|
migration.summary.total !== 627
|
|
) {
|
|
throw new Error(`Live2D module migration prerequisite is incomplete: ${JSON.stringify(migration.summary)}`);
|
|
}
|
|
const variable = auditLive2DVariableNameDecisionCatalog({
|
|
catalog: readJson<Live2DVariableNameDecisionCatalog>(options.variableDecisionPath),
|
|
implementationRoot: options.implementationRoot,
|
|
moduleCatalog,
|
|
});
|
|
if (JSON.stringify(variable.summary) !== JSON.stringify(EXPECTED_SUMMARY)) {
|
|
throw new Error(`Live2D variable census drifted: ${JSON.stringify(variable.summary)}`);
|
|
}
|
|
const identifiers = auditLive2DIdentifierNameDecisionCatalog(
|
|
readJson<Live2DIdentifierNameDecisionCatalog>(options.identifierDecisionPath),
|
|
options.productionRoot,
|
|
);
|
|
const expectedIdentifierSummary = {
|
|
"keep-semantic": 1,
|
|
remove: 10,
|
|
rename: 42,
|
|
total: 53,
|
|
unresolved: 0,
|
|
};
|
|
if (JSON.stringify(identifiers.summary) !== JSON.stringify(expectedIdentifierSummary)) {
|
|
throw new Error(
|
|
`Live2D identifier census drifted: ${JSON.stringify(identifiers.summary)}`,
|
|
);
|
|
}
|
|
assertLive2DVariableDecisionCatalogIsOffline(options.productionRoot);
|
|
process.stdout.write(
|
|
`${JSON.stringify({
|
|
identifiers,
|
|
migration: migration.summary,
|
|
offlineOnly: true,
|
|
ownerHelperTargets: variable.ownerHelperTargets,
|
|
sourceSha256: variable.sourceSha256,
|
|
variable: variable.summary,
|
|
})}\n`,
|
|
);
|
|
}
|
|
|
|
main();
|