Handle references into inactive modules
This commit is contained in:
@@ -122,7 +122,8 @@ export async function runRuntimeAnalysis({ onProgress }={}) {
|
||||
listFiles: () => listFoundryFiles(onProgress),
|
||||
listSources: () => listFoundrySources(onProgress),
|
||||
onProgress,
|
||||
i18n: { format }
|
||||
i18n: { format },
|
||||
packageActivity: collectPackageActivity()
|
||||
});
|
||||
}
|
||||
|
||||
@@ -221,3 +222,13 @@ function findEmbeddedIndex(path, segment) {
|
||||
const candidate = path[index + 1];
|
||||
return Number.isInteger(candidate) ? candidate : null;
|
||||
}
|
||||
|
||||
function collectPackageActivity() {
|
||||
const modules = new Map();
|
||||
const moduleLabels = new Map();
|
||||
for (const module of game.modules.values()) {
|
||||
modules.set(module.id, !!module.active);
|
||||
moduleLabels.set(module.id, module.title ?? module.id);
|
||||
}
|
||||
return { modules, moduleLabels };
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ export class StorageAuditReportApp extends foundry.applications.api.ApplicationV
|
||||
hasAnalysis: !!this.#analysis,
|
||||
showAll: this.#showAll,
|
||||
progress: this.#progress,
|
||||
notices: this.#analysis?.notices ?? [],
|
||||
summary: this.#summarize(this.#analysis),
|
||||
groupedFindings
|
||||
};
|
||||
@@ -89,6 +90,7 @@ export class StorageAuditReportApp extends foundry.applications.api.ApplicationV
|
||||
</div>
|
||||
</section>
|
||||
${renderProgress(context.progress, context.loading)}
|
||||
${renderNotices(context.notices, context.loading)}
|
||||
${renderSummary(context.summary, context.loading)}
|
||||
${renderGroupedFindingList(context.groupedFindings, context.hasAnalysis, context.loading, context.showAll)}
|
||||
`;
|
||||
@@ -141,6 +143,7 @@ export class StorageAuditReportApp extends foundry.applications.api.ApplicationV
|
||||
if (!this.#analysis) return;
|
||||
const payload = {
|
||||
exportedAt: new Date().toISOString(),
|
||||
notices: this.#analysis.notices ?? [],
|
||||
summary: this.#summarize(this.#analysis),
|
||||
groupedFindings: groupFindings(this.#analysis.findings),
|
||||
findings: this.#analysis.findings.map(serializeFinding)
|
||||
@@ -232,6 +235,17 @@ function renderProgress(progress, loading) {
|
||||
`;
|
||||
}
|
||||
|
||||
function renderNotices(notices, loading) {
|
||||
if (loading || !notices?.length) return "";
|
||||
const items = notices.map(notice => `<li>${escapeHtml(notice.message ?? "")}</li>`).join("");
|
||||
return `
|
||||
<section class="storage-audit__summary storage-audit__summary--notices">
|
||||
<h3>${localize("KSA.Notice.Title")}</h3>
|
||||
<ul class="storage-audit__notice-list">${items}</ul>
|
||||
</section>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderSummary(summary, loading) {
|
||||
if (loading) return "";
|
||||
if (!summary) {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { buildFindings, createFileRecord } from "./finding-engine.js";
|
||||
import { extractReferencesFromValue } from "./reference-extractor.js";
|
||||
import { createFileLocator } from "./path-utils.js";
|
||||
|
||||
export async function analyzeStorage({ listFiles, listSources, onProgress, i18n }={}) {
|
||||
export async function analyzeStorage({ listFiles, listSources, onProgress, i18n, packageActivity }={}) {
|
||||
const files = [];
|
||||
let fileCount = 0;
|
||||
let sourceCount = 0;
|
||||
@@ -46,16 +46,16 @@ export async function analyzeStorage({ listFiles, listSources, onProgress, i18n
|
||||
}
|
||||
|
||||
onProgress?.({ phase: "findings", label: format(i18n, "KSA.Progress.ClassifyFindings"), files: fileCount, sources: sourceCount, references: referenceCount });
|
||||
const findings = buildFindings({ files, references, i18n });
|
||||
const result = buildFindings({ files, references, i18n, packageActivity });
|
||||
onProgress?.({
|
||||
phase: "done",
|
||||
label: format(i18n, "KSA.Progress.Completed"),
|
||||
files: fileCount,
|
||||
sources: sourceCount,
|
||||
references: referenceCount,
|
||||
findings: findings.length
|
||||
findings: result.findings.length
|
||||
});
|
||||
return { files, references, findings };
|
||||
return { files, references, findings: result.findings, notices: result.notices ?? [] };
|
||||
}
|
||||
|
||||
async function yieldToUI() {
|
||||
|
||||
@@ -32,7 +32,7 @@ function compareOwner(sourceScope, targetOwner) {
|
||||
return "allowed";
|
||||
}
|
||||
|
||||
export function buildFindings({ files, references, i18n }={}) {
|
||||
export function buildFindings({ files, references, i18n, packageActivity }={}) {
|
||||
const fileByLocator = new Map(files.map(file => [createCanonicalLocator(file.storage, file.path), file]));
|
||||
const fileLocators = files.map(file => ({
|
||||
storage: file.storage,
|
||||
@@ -43,6 +43,7 @@ export function buildFindings({ files, references, i18n }={}) {
|
||||
const refsByLocator = new Map();
|
||||
const wildcardReferences = [];
|
||||
const findings = [];
|
||||
const inactiveModuleReferenceIds = new Set();
|
||||
|
||||
for (const reference of resolvedReferences) {
|
||||
const normalized = reference.normalized;
|
||||
@@ -84,6 +85,10 @@ export function buildFindings({ files, references, i18n }={}) {
|
||||
|
||||
const ownerRelation = compareOwner(reference.sourceScope, file.ownerHint);
|
||||
if (ownerRelation === "non-package-to-package") {
|
||||
if (isInactiveModuleTarget(file, packageActivity)) {
|
||||
inactiveModuleReferenceIds.add(file.ownerHint.ownerId);
|
||||
continue;
|
||||
}
|
||||
if (isAnchoredInOwningPackage(file, matchingReferencesForFile(file, refsByLocator, wildcardReferences))) continue;
|
||||
findings.push({
|
||||
kind: "non-package-to-package-reference",
|
||||
@@ -137,7 +142,10 @@ export function buildFindings({ files, references, i18n }={}) {
|
||||
});
|
||||
}
|
||||
|
||||
return findings;
|
||||
return {
|
||||
findings,
|
||||
notices: createNotices({ inactiveModuleReferenceIds, packageActivity, i18n })
|
||||
};
|
||||
}
|
||||
|
||||
function resolveReferenceTarget(reference, fileByLocator, fileLocators) {
|
||||
@@ -225,6 +233,27 @@ function shouldReportOrphan(file, references) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function isInactiveModuleTarget(file, packageActivity) {
|
||||
if (file.ownerHint?.ownerType !== "module") return false;
|
||||
const active = packageActivity?.modules?.get?.(file.ownerHint.ownerId);
|
||||
return active === false;
|
||||
}
|
||||
|
||||
function createNotices({ inactiveModuleReferenceIds, packageActivity, i18n }) {
|
||||
const moduleIds = [...inactiveModuleReferenceIds].sort((a, b) => a.localeCompare(b));
|
||||
if (!moduleIds.length) return [];
|
||||
const moduleLabels = moduleIds.map(id => packageActivity?.moduleLabels?.get?.(id) ?? id);
|
||||
return [{
|
||||
kind: "inactive-module-references",
|
||||
severity: "info",
|
||||
moduleIds,
|
||||
moduleLabels,
|
||||
message: format(i18n, "KSA.Notice.InactiveModuleReferences", {
|
||||
modules: moduleLabels.join(", ")
|
||||
})
|
||||
}];
|
||||
}
|
||||
|
||||
function isDerivedSceneThumbnail(file) {
|
||||
const path = String(file.path ?? "");
|
||||
return /^worlds\/[^/]+\/assets\/scenes\/[^/]+-thumb\.(?:png|webp)$/u.test(path);
|
||||
|
||||
Reference in New Issue
Block a user