feat: improve viewer diagnostics panel

This commit is contained in:
nejimakibird 2026-06-19 16:19:42 +09:00
parent 7142d79901
commit 4819e8d5a8
4 changed files with 107 additions and 0 deletions

38
main.js
View file

@ -12890,6 +12890,7 @@ function validateStandaloneDomains(index, warnings) {
});
}
}
validateStandaloneDomainParents(entriesByDomainId, warnings);
for (const entries of entriesByDomainId.values()) {
if (entries.length < 2) {
continue;
@ -12918,6 +12919,24 @@ function validateStandaloneDomains(index, warnings) {
compareStandaloneDomainFields(sortedEntries, warnings);
}
}
function validateStandaloneDomainParents(entriesByDomainId, warnings) {
for (const entries of entriesByDomainId.values()) {
for (const entry of entries) {
const parent = entry.domain.parent?.trim();
if (!parent || parent === entry.domain.id || entriesByDomainId.has(parent)) {
continue;
}
warnings.push({
code: "unresolved-reference",
message: formatDomainParentUnknownMessage(parent),
severity: "warning",
path: entry.path,
field: "Domains.parent",
context: { rowIndex: entry.domain.rowIndex + 1 }
});
}
}
}
function compareStandaloneDomainFields(entries, warnings) {
for (const field of STANDALONE_DOMAIN_CANONICAL_FIELDS) {
const values = new Set(entries.map((entry) => entry.domain[field]?.trim() ?? ""));
@ -13326,6 +13345,7 @@ function ensureVaultValidation(index) {
return;
}
clearVaultValidationWarnings(index);
clearDomainParentNotDefinedWarnings(index);
for (const warning of validateVaultIndex(index)) {
pushWarning(index.warningsByFilePath, warning.path ?? "vault", {
...warning,
@ -13676,6 +13696,24 @@ function clearVaultValidationWarnings(index) {
);
}
}
function clearDomainParentNotDefinedWarnings(index) {
for (const [path2, warnings] of Object.entries(index.warningsByFilePath)) {
const model = index.modelsByFilePath[path2];
if (model?.fileType !== "domains") {
continue;
}
index.warningsByFilePath[path2] = warnings.filter(
(warning) => !getDomainParentNotDefinedValue(warning)
);
}
}
function getDomainParentNotDefinedValue(warning) {
if (warning.code !== "unresolved-reference" || warning.field !== "Domains.parent") {
return null;
}
const match = warning.message.match(/^Domain parent "([^"]+)" is not defined\.$/);
return match?.[1] ?? null;
}
function getDuplicateModelKey(model) {
if (model.fileType === "markdown") {
return null;

View file

@ -14,6 +14,7 @@ import {
formatDfdObjectUnknownLocalDomainMessage,
formatDfdLocalDomainFieldMismatchMessage,
formatDfdLocalDomainMissingSharedMessage,
formatDomainParentUnknownMessage,
formatStandaloneDomainDuplicateMessage,
formatStandaloneDomainFieldConflictMessage
} from "./domain-diagnostics";
@ -127,6 +128,8 @@ function validateStandaloneDomains(
}
}
validateStandaloneDomainParents(entriesByDomainId, warnings);
for (const entries of entriesByDomainId.values()) {
if (entries.length < 2) {
continue;
@ -159,6 +162,28 @@ function validateStandaloneDomains(
}
}
function validateStandaloneDomainParents(
entriesByDomainId: Map<string, Array<{ domain: DomainEntry; path: string }>>,
warnings: ValidationWarning[]
): void {
for (const entries of entriesByDomainId.values()) {
for (const entry of entries) {
const parent = entry.domain.parent?.trim();
if (!parent || parent === entry.domain.id || entriesByDomainId.has(parent)) {
continue;
}
warnings.push({
code: "unresolved-reference",
message: formatDomainParentUnknownMessage(parent),
severity: "warning",
path: entry.path,
field: "Domains.parent",
context: { rowIndex: entry.domain.rowIndex + 1 }
});
}
}
}
function compareStandaloneDomainFields(
entries: Array<{ domain: DomainEntry; path: string }>,
warnings: ValidationWarning[]

View file

@ -182,6 +182,7 @@ export function ensureVaultValidation(index: ModelingVaultIndex): void {
}
clearVaultValidationWarnings(index);
clearDomainParentNotDefinedWarnings(index);
for (const warning of validateVaultIndex(index)) {
pushWarning(index.warningsByFilePath, warning.path ?? "vault", {
...warning,
@ -599,6 +600,27 @@ function clearVaultValidationWarnings(index: ModelingVaultIndex): void {
}
}
function clearDomainParentNotDefinedWarnings(index: ModelingVaultIndex): void {
for (const [path, warnings] of Object.entries(index.warningsByFilePath)) {
const model = index.modelsByFilePath[path];
if (model?.fileType !== "domains") {
continue;
}
index.warningsByFilePath[path] = warnings.filter(
(warning) => !getDomainParentNotDefinedValue(warning)
);
}
}
function getDomainParentNotDefinedValue(warning: ValidationWarning): string | null {
if (warning.code !== "unresolved-reference" || warning.field !== "Domains.parent") {
return null;
}
const match = warning.message.match(/^Domain parent "([^"]+)" is not defined\.$/);
return match?.[1] ?? null;
}
function getDuplicateModelKey(
model: ParsedFileModel
): { key: string; id: string } | null {

View file

@ -534,6 +534,24 @@ test("localizes standalone Domain diagnostics", () => {
);
});
test("allows standalone Domains parent from another Domains file", () => {
const index = buildVaultIndex([
domainsFile("COUNTRY_EXAMPLE.md", "COUNTRY_EXAMPLE", "| software_develop | Software develop | business | | Shared parent |"),
domainsFile("COUNTRY_EXAMPLE_1.md", "COUNTRY_EXAMPLE_1", [
"| software_develop_eigyo_1 | Sales 1 | team | software_develop | External parent |",
"| software_develop_kaihatsu_1 | Development 1 | team | software_develop | External parent |"
].join("\n"))
]);
const messages = (index.warningsByFilePath["COUNTRY_EXAMPLE_1.md"] ?? []).map(
(warning) => warning.message
);
assert.equal(
messages.includes("Domain parent \"software_develop\" is not defined."),
false
);
});
test("validates duplicate standalone Domains across files", () => {
const index = buildVaultIndex([
{
@ -1498,6 +1516,10 @@ id: DOMAIN-DIAGRAM-LOGISTICS
["company", "logistics", "warehouse", "team_1"]
);
assert.equal(resolved.warnings.length, 0);
const sourceMessages = (index.warningsByFilePath["model/domains/DOMAINS-WAREHOUSE.md"] ?? []).map(
(warning) => warning.message
);
assert.equal(sourceMessages.includes("Domain parent \"logistics\" is not defined."), false);
assert.match(buildDomainMindmapMermaid(resolved.domains), /会社全体organization/);
assert.match(buildDomainHierarchyMermaid(resolved.domains), /subgraph domain_company/);
assert.match(