@minecraft/creator-tools
Version:
Minecraft Creator Tools command line and libraries.
130 lines (129 loc) • 6.72 kB
JavaScript
;
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FormSchemaItemInfoGeneratorTest = void 0;
const ProjectInfoItem_1 = __importDefault(require("./ProjectInfoItem"));
const CreatorToolsHost_1 = __importDefault(require("../app/CreatorToolsHost"));
const axios_1 = __importDefault(require("axios"));
const Utilities_1 = __importDefault(require("../core/Utilities"));
const Database_1 = __importDefault(require("../minecraft/Database"));
const IInfoItemData_1 = require("./IInfoItemData");
const DataFormValidator_1 = __importDefault(require("../dataform/DataFormValidator"));
const StorageUtilities_1 = __importDefault(require("../storage/StorageUtilities"));
const ProjectItemUtilities_1 = __importDefault(require("../app/ProjectItemUtilities"));
const IProjectItemData_1 = require("../app/IProjectItemData");
var FormSchemaItemInfoGeneratorTest;
(function (FormSchemaItemInfoGeneratorTest) {
FormSchemaItemInfoGeneratorTest[FormSchemaItemInfoGeneratorTest["couldNotParseJson"] = 401] = "couldNotParseJson";
FormSchemaItemInfoGeneratorTest[FormSchemaItemInfoGeneratorTest["couldNotFindForm"] = 402] = "couldNotFindForm";
})(FormSchemaItemInfoGeneratorTest || (exports.FormSchemaItemInfoGeneratorTest = FormSchemaItemInfoGeneratorTest = {}));
/**
* Validates JSON files against Minecraft documentation-based form schemas.
*
* @see {@link ../../public/data/forms/mctoolsval/jsonf.form.json} for topic definitions
*/
class FormSchemaItemInfoGenerator {
id = "JSONF";
title = "JSON Structure";
canAlwaysProcess = true;
_schemaContentByPath = {};
/**
* Cache of loaded form definitions keyed by formPath.
* Avoids redundant Database.ensureFormLoadedByPath() calls for items sharing the same form.
*
* Lifecycle: This generator is instantiated once per validation run by GeneratorRegistrations.
* Caches are valid for the duration of a single ProjectInfoSet.generate() call and are
* naturally discarded when the generator instance is garbage-collected after the run.
*/
_formCache = new Map();
/**
* Cache of validation contexts keyed by formPath.
* Items with the same form share a subForm cache, avoiding repeated async subForm lookups.
*
* Lifecycle: Same as _formCache — scoped to a single validation run.
*/
_contextCache = new Map();
uuidRegex = new RegExp("[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}");
constructor() {
this.testUuid = this.testUuid.bind(this);
this.testUri = this.testUri.bind(this);
}
summarize(info, infoSet) { }
async loadSchema(uri) {
const res = await axios_1.default.get(Utilities_1.default.ensureEndsWithSlash(CreatorToolsHost_1.default.contentWebRoot) + uri);
return res.data;
}
async generate(projectItem, contentIndex) {
const items = [];
// Fast path: skip items that can never have a form.
// getFormPathForType returns undefined for most item types (textures, audio, etc.).
// Only geometry items need special logic via getFormPath().
if (projectItem.itemType !== IProjectItemData_1.ProjectItemType.modelGeometryJson &&
ProjectItemUtilities_1.default.getFormPathForType(projectItem.itemType) === undefined) {
return items;
}
if (projectItem.primaryFile &&
projectItem.primaryFile.content &&
typeof projectItem.primaryFile.content === "string") {
const formPath = projectItem.getFormPath();
if (formPath) {
// Use cached form definition to avoid repeated Database lookups
let form = this._formCache.get(formPath);
if (form === undefined) {
const loaded = await Database_1.default.ensureFormLoadedByPath(formPath);
form = loaded || null;
this._formCache.set(formPath, form);
}
if (form) {
const data = StorageUtilities_1.default.getJsonObject(projectItem.primaryFile);
if (!data) {
items.push(new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.error, this.id, FormSchemaItemInfoGeneratorTest.couldNotParseJson, "Could not parse JSON. " +
(projectItem.primaryFile.errorStateMessage ? projectItem.primaryFile.errorStateMessage : ""), projectItem, formPath));
}
else {
// Share validation context (and its subForm cache) across items
// with the same formPath for massive speedup on repeated forms.
let context = this._contextCache.get(formPath);
if (!context) {
context = {
depth: 0,
subFormCache: new Map(),
};
this._contextCache.set(formPath, context);
}
// Reset depth for each new item (subFormCache is intentionally preserved)
const itemContext = {
depth: 0,
subFormCache: context.subFormCache,
};
const results = await DataFormValidator_1.default.validate(data, form, undefined, undefined, itemContext);
if (results) {
for (const result of results) {
items.push(new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.warning, this.id, result.type, result.message, projectItem));
}
}
}
}
else {
items.push(new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.internalProcessingError, this.id, FormSchemaItemInfoGeneratorTest.couldNotFindForm, "Could not find expected form", undefined, formPath));
}
}
}
return items;
}
testUuid(uuidString) {
return this.uuidRegex.test(uuidString);
}
testUnknownFormat(formatString) {
return true;
}
testUri(uriString) {
// could get much more sophisticated here...
return uriString.indexOf("://") >= 0;
}
}
exports.default = FormSchemaItemInfoGenerator;