@minecraft/creator-tools
Version:
Minecraft Creator Tools command line and libraries.
697 lines • 129 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExportMode = exports.MarkdownTop = void 0;
const Utilities_1 = __importDefault(require("../core/Utilities"));
const StorageUtilities_1 = __importDefault(require("../storage/StorageUtilities"));
const DataFormUtilities_1 = __importDefault(require("../dataform/DataFormUtilities"));
const IField_1 = require("../dataform/IField");
const EntityTypeDefinition_1 = __importDefault(require("../minecraft/EntityTypeDefinition"));
const ICondition_1 = require("../dataform/ICondition");
const FieldUtilities_1 = __importDefault(require("../dataform/FieldUtilities"));
const Log_1 = __importDefault(require("../core/Log"));
const ValidationRulesMarkdownGenerator_1 = __importDefault(require("./ValidationRulesMarkdownGenerator"));
// note: leave ms.date as a fixed date, to keep trivial noisy diffs from happening on every
// docgen run. Update manually when significant changes are made to the generator that
// warrant a new "version" of the generated docs.
exports.MarkdownTop = `---
author: mammerla
ms.author: mikeam
title: "{0}"
description: "{1}"
ai-usage: ai-assisted
ms.service: minecraft-bedrock-edition
ms.date: 02/11/2025
---
`;
var ExportMode;
(function (ExportMode) {
ExportMode[ExportMode["other"] = 0] = "other";
ExportMode[ExportMode["triggers"] = 1] = "triggers";
ExportMode[ExportMode["blockComponents"] = 2] = "blockComponents";
ExportMode[ExportMode["itemComponents"] = 3] = "itemComponents";
ExportMode[ExportMode["entityComponents"] = 4] = "entityComponents";
ExportMode[ExportMode["AIGoals"] = 5] = "AIGoals";
ExportMode[ExportMode["visuals"] = 6] = "visuals";
ExportMode[ExportMode["fogs"] = 7] = "fogs";
ExportMode[ExportMode["websockets"] = 8] = "websockets";
ExportMode[ExportMode["filters"] = 9] = "filters";
ExportMode[ExportMode["MCToolsVal"] = 10] = "MCToolsVal";
ExportMode[ExportMode["eventResponses"] = 11] = "eventResponses";
ExportMode[ExportMode["clientBiomes"] = 12] = "clientBiomes";
ExportMode[ExportMode["biomes"] = 13] = "biomes";
ExportMode[ExportMode["features"] = 14] = "features";
ExportMode[ExportMode["featureCore"] = 15] = "featureCore";
ExportMode[ExportMode["deferredRendering"] = 16] = "deferredRendering";
ExportMode[ExportMode["molang"] = 17] = "molang";
ExportMode[ExportMode["culling"] = 18] = "culling";
ExportMode[ExportMode["manifest"] = 19] = "manifest";
ExportMode[ExportMode["blockCullingComponents"] = 20] = "blockCullingComponents";
ExportMode[ExportMode["recipes"] = 21] = "recipes";
ExportMode[ExportMode["lootTables"] = 22] = "lootTables";
ExportMode[ExportMode["spawnRules"] = 23] = "spawnRules";
ExportMode[ExportMode["animations"] = 24] = "animations";
ExportMode[ExportMode["camera"] = 25] = "camera";
ExportMode[ExportMode["dialogue"] = 26] = "dialogue";
ExportMode[ExportMode["particles"] = 27] = "particles";
ExportMode[ExportMode["commands"] = 28] = "commands";
ExportMode[ExportMode["jigsaw"] = 29] = "jigsaw";
ExportMode[ExportMode["dimensions"] = 30] = "dimensions";
ExportMode[ExportMode["attachables"] = 31] = "attachables";
ExportMode[ExportMode["jsonUi"] = 32] = "jsonUi";
ExportMode[ExportMode["tradeTables"] = 33] = "tradeTables";
ExportMode[ExportMode["featureRules"] = 34] = "featureRules";
ExportMode[ExportMode["voxelShapes"] = 35] = "voxelShapes";
})(ExportMode || (exports.ExportMode = ExportMode = {}));
class FormMarkdownDocumentationGenerator {
_referenceFolder;
_skippedFiles = [];
/** Keyword param types that represent literal tokens in command syntax, not user-supplied values. */
static KEYWORD_PARAM_TYPES = new Set([
"SET",
"EASE",
"POS",
"ROT",
"FACING",
"DEFAULT",
"CLEAR",
"FADE",
"TIME",
"COLOR",
"ATTACH_TO_ENTITY",
"DETACH_FROM_ENTITY",
"TARGET_ENTITY",
"TARGET_CENTER_OFFSET",
"REMOVE_TARGET",
"VIEW_OFFSET",
"ENTITY_OFFSET",
"FOV_SET",
"FOV_CLEAR",
"PLAY_SPLINE",
// Execute subcommand keywords
"OPTION_AS",
"OPTION_AT",
"OPTION_IN",
"OPTION_POSITIONED",
"OPTION_ROTATED",
"OPTION_FACING",
"OPTION_ALIGN",
"OPTION_ANCHORED",
"OPTION_IF",
"OPTION_UNLESS",
"OPTION_RUN",
]);
/**
* Generate markdown documentation from form JSON files.
* @param formJsonInputFolder The folder containing form.json files
* @param outputFolder The folder to write generated markdown to
* @param referenceFolder Optional folder containing existing docs - files that exist here will be skipped
*/
async generateMarkdown(formJsonInputFolder, outputFolder, referenceFolder) {
this._referenceFolder = referenceFolder;
this._skippedFiles = [];
const formsByPath = {};
await this.loadFormJsonFromFolder(formsByPath, formJsonInputFolder, outputFolder);
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.features, "/FeaturesReference/Examples/Features/", "/features/minecraft_", "Feature", "Feature Type");
await this.exportMarkdownDocListPage(formsByPath, outputFolder, ExportMode.features, "/FeaturesReference/Examples/FeatureList.md", "/features/minecraft_", "Features", "Features");
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.featureCore, "/FeaturesReference/Examples/Features/", "/feature/", "Feature", "Feature Type");
await this.exportListYml(formsByPath, outputFolder, ExportMode.features, "/FeaturesReference/Examples/Features/TOC.yml", "/features/minecraft_", "- name: Features List\n href: ../FeatureList.md", "minecraft_");
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.AIGoals, "/EntityReference/Examples/EntityGoals/", "/entity/minecraft_behavior", "Entity", "AI Behavior Component");
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.visuals, "/VisualReference/", "/visual/", "Visuals", "Visual Element");
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.fogs, "/FogsReference/", "/fogs/", "Fogs", "Fog Element");
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.websockets, "/WebsocketsReference/", "/websockets/", "Websockets", "Websocket Packet");
await this.exportValidatorMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.MCToolsVal, "/MCToolsValReference/", "/mctoolsval/", "MCTools Validation Rules", "MCTools Validation Rules");
// Generate detailed validation rules documentation (user-friendly format with how-to-fix guidance)
const validationRulesGenerator = new ValidationRulesMarkdownGenerator_1.default();
// Access child folder directly - formJsonInputFolder should already be loaded
await formJsonInputFolder.load(false);
const mctoolsvalFolder = formJsonInputFolder.folders["mctoolsval"];
if (mctoolsvalFolder) {
await validationRulesGenerator.generateValidationDocs(mctoolsvalFolder, outputFolder);
}
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.triggers, "/EntityReference/Examples/EntityTriggers/", "/entity/minecraft_on", "Entity", "Entity Trigger");
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.filters, "/EntityReference/Examples/Filters/", "/entityfilters/", "Entity Filters", "Entity Filter Element");
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.eventResponses, "/EntityReference/Examples/EventActions/", "/entityevents/", "Entity Actions", "Entity Action Types");
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.blockCullingComponents, "/BlockCullingReference/Examples/BlockCullingRules/", "/client_block/", "Block Culling", "Block Culling");
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.blockComponents, "/BlockReference/Examples/BlockComponents/", "/block/minecraft_", "Block Components", "Block Component");
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.itemComponents, "/ItemReference/Examples/ItemComponents/", "/item_components/minecraft_", "Items", "Item Component");
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.entityComponents, "/EntityReference/Examples/EntityComponents/", "/entity/minecraft_", "Entity", "Entity Component");
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.deferredRendering, "/DeferredRendering/", "/client_deferred_rendering/", "Deferred Rendering", "Deferred Rendering");
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.clientBiomes, "/ClientBiomesReference/Examples/Components/", "/client_biome/", "Client Biome", "Client Biome");
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.manifest, "/ManifestReference/", "/3.0.0/", "Pack Manifest", "Pack Manifest");
await this.exportMarkdownDocListPage(formsByPath, outputFolder, ExportMode.clientBiomes, "/ClientBiomesReference/Examples/ComponentList.md", "/client_biome/minecraft_", "Client Biomes", "Components");
await this.exportListYml(formsByPath, outputFolder, ExportMode.clientBiomes, "/ClientBiomesReference/Examples/Components/TOC.yml", "/client_biome/minecraft_", "- name: Components List\r\n href: ../ComponentList.md", "minecraftClientBiomes_");
await this.exportMarkdownDocListPage(formsByPath, outputFolder, ExportMode.entityComponents, "/EntityReference/Examples/ComponentList.md", "/entity/minecraft_", "Entity Components", "EntityComponents");
await this.exportListYml(formsByPath, outputFolder, ExportMode.entityComponents, "/EntityReference/Examples/EntityComponents/TOC.yml", "/entity/minecraft_", "- name: Components List\n href: ../ComponentList.md", "minecraftComponent_");
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.biomes, "/BiomesReference/Examples/Components/", "/biome/", "Biome", "Biome");
await this.exportMarkdownDocListPage(formsByPath, outputFolder, ExportMode.biomes, "/BiomesReference/Examples/ComponentList.md", "/biome/minecraft_", "Biomes", "Components", ["capped.", "frozen_ocean.", "mesa.", "overworld.", "swamp", "the_end."] // these end up as children of surface_builder
);
await this.exportListYml(formsByPath, outputFolder, ExportMode.biomes, "/BiomesReference/Examples/Components/TOC.yml", "/biome/minecraft_", "- name: Components List\n href: ../ComponentList.md", "minecraftBiomes_", ["capped.", "frozen_ocean.", "mesa.", "overworld.", "swamp.", "the_end."]);
await this.exportMarkdownDocListPage(formsByPath, outputFolder, ExportMode.blockComponents, "/BlockReference/Examples/BlockComponents/BlockComponentsList.md", "/block/minecraft_", "Block Components", ".");
await this.exportListYml(formsByPath, outputFolder, ExportMode.blockComponents, "/BlockReference/Examples/BlockComponents/TOC.yml", "/block/minecraft_", "- name: Block Components List\n href: BlockComponentsList.md");
await this.exportMarkdownDocListPage(formsByPath, outputFolder, ExportMode.itemComponents, "/ItemReference/Examples/ItemComponentList.md", "/item_components/minecraft_", "Item Components", "./ItemComponents");
await this.exportListYml(formsByPath, outputFolder, ExportMode.itemComponents, "/ItemReference/Examples/ItemComponents/TOC.yml", "/item_components/minecraft_", "- name: Item Components List\n href: ../ItemComponentList.md");
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.culling, "/BlockCullingReference/", "/block_culling/", "Block Culling", "Block Culling");
await this.exportListYml(formsByPath, outputFolder, ExportMode.culling, "/BlockCullingReference/TOC.yml", "/block_culling/");
await this.exportMarkdownDocListPage(formsByPath, outputFolder, ExportMode.triggers, "/EntityReference/Examples/TriggerList.md", "/entity/minecraft_on", "Entity Triggers", "EntityTriggers");
await this.exportListYml(formsByPath, outputFolder, ExportMode.entityComponents, "/EntityReference/Examples/EntityTriggers/TOC.yml", "/entity/minecraft_on", "- name: Triggers List\n href: ../TriggerList.md", "minecraftTrigger_");
await this.exportMarkdownDocListPage(formsByPath, outputFolder, ExportMode.AIGoals, "/EntityReference/Examples/AIGoalList.md", "/entity/minecraft_behavior", "Entity Behavior (AI) Components", "EntityGoals");
await this.exportListYml(formsByPath, outputFolder, ExportMode.AIGoals, "/EntityReference/Examples/EntityGoals/TOC.yml", "/entity/minecraft_behavior", "- name: AI Component List\n href: ../AIGoalList.md");
await this.exportMarkdownDocListPage(formsByPath, outputFolder, ExportMode.filters, "/EntityReference/Examples/FilterList.md", "/entityfilters/", "Entity Filter Types", "Filters");
await this.exportListYml(formsByPath, outputFolder, ExportMode.filters, "/EntityReference/Examples/Filters/TOC.yml", "/entityfilters/", "- name: Entity Filter List\n href: ../FilterList.md");
await this.exportMarkdownDocListPage(formsByPath, outputFolder, ExportMode.eventResponses, "/EntityReference/Examples/EventActions.md", "/entityevents/", "Event Actions", "EventActions");
await this.exportListYml(formsByPath, outputFolder, ExportMode.eventResponses, "/EntityReference/Examples/EventActions/TOC.yml", "/entityevents/", "- name: Entity Event List\n href: ../EventActions.md");
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.molang, "/MolangReference/Examples/MolangConcepts/QueryFunctions/", "/molang/query_", "Molang", "Molang");
await this.exportMarkdownDocListPage(formsByPath, outputFolder, ExportMode.molang, "/MolangReference/Examples/MolangConcepts/QueryFunctions.md", "/molang/query_", "Molang Query Functions", "queryfunctions");
await this.exportListYml(formsByPath, outputFolder, ExportMode.eventResponses, "/MolangReference/Examples/MolangConcepts/QueryFunctions/TOC.yml", "/molang/query_", "- name: Molang Query Function List\n href: ../QueryFunctions.md");
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.molang, "/MolangReference/Examples/MolangConcepts/MathFunctions/", "/molang/math_", "Molang", "Molang");
await this.exportMarkdownDocListPage(formsByPath, outputFolder, ExportMode.molang, "/MolangReference/Examples/MolangConcepts/MathFunctions.md", "/molang/math_", "Molang Math Functions", "mathfunctions");
await this.exportListYml(formsByPath, outputFolder, ExportMode.eventResponses, "/MolangReference/Examples/MolangConcepts/MathFunctions/TOC.yml", "/molang/math_", "- name: Molang Math Function List\n href: ../MathFunctions.md");
// Recipe documentation
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.recipes, "/RecipeReference/Examples/RecipeDefinitions/", "/recipe/recipe_", "Recipes", "Recipe Definition");
await this.exportMarkdownDocListPage(formsByPath, outputFolder, ExportMode.recipes, "/RecipeReference/Examples/RecipeList.md", "/recipe/recipe_", "Recipes", "RecipeDefinitions");
await this.exportListYml(formsByPath, outputFolder, ExportMode.recipes, "/RecipeReference/Examples/RecipeDefinitions/TOC.yml", "/recipe/recipe_", "- name: Recipe List\n href: ../RecipeList.md");
// Loot Table documentation
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.lootTables, "/LootTableReference/Examples/LootTableComponents/", "/loot/loot_", "Loot Tables", "Loot Table Component");
await this.exportMarkdownDocListPage(formsByPath, outputFolder, ExportMode.lootTables, "/LootTableReference/Examples/LootTableList.md", "/loot/loot_", "Loot Tables", "LootTableComponents");
await this.exportListYml(formsByPath, outputFolder, ExportMode.lootTables, "/LootTableReference/Examples/LootTableComponents/TOC.yml", "/loot/loot_", "- name: Loot Table List\n href: ../LootTableList.md");
// Spawn Rules documentation
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.spawnRules, "/SpawnRulesReference/Examples/SpawnRulesComponents/", "/spawn/spawn_", "Spawn Rules", "Spawn Rule Component");
await this.exportMarkdownDocListPage(formsByPath, outputFolder, ExportMode.spawnRules, "/SpawnRulesReference/Examples/SpawnRulesList.md", "/spawn/spawn_", "Spawn Rules", "SpawnRulesComponents");
await this.exportListYml(formsByPath, outputFolder, ExportMode.spawnRules, "/SpawnRulesReference/Examples/SpawnRulesComponents/TOC.yml", "/spawn/spawn_", "- name: Spawn Rules List\n href: ../SpawnRulesList.md");
// Animation documentation
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.animations, "/AnimationsReference/Examples/AnimationDefinitions/", "/animation/animation_", "Animations", "Animation Definition");
await this.exportMarkdownDocListPage(formsByPath, outputFolder, ExportMode.animations, "/AnimationsReference/Examples/AnimationList.md", "/animation/animation_", "Animations", "AnimationDefinitions");
await this.exportListYml(formsByPath, outputFolder, ExportMode.animations, "/AnimationsReference/Examples/AnimationDefinitions/TOC.yml", "/animation/animation_", "- name: Animation List\n href: ../AnimationList.md");
// Camera Aim Assist documentation
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.camera, "/CameraReference/Examples/CameraDefinitions/", "/camera/", "Camera", "Camera Definition");
await this.exportMarkdownDocListPage(formsByPath, outputFolder, ExportMode.camera, "/CameraReference/Examples/CameraList.md", "/camera/", "Camera Definitions", "CameraDefinitions");
await this.exportListYml(formsByPath, outputFolder, ExportMode.camera, "/CameraReference/Examples/CameraDefinitions/TOC.yml", "/camera/", "- name: Camera List\n href: ../CameraList.md");
// NPC Dialogue documentation
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.dialogue, "/DialogueReference/Examples/DialogueDefinitions/", "/dialogue/dialogue_", "Dialogue", "Dialogue Definition");
await this.exportMarkdownDocListPage(formsByPath, outputFolder, ExportMode.dialogue, "/DialogueReference/Examples/DialogueList.md", "/dialogue/dialogue_", "NPC Dialogue", "DialogueDefinitions");
await this.exportListYml(formsByPath, outputFolder, ExportMode.dialogue, "/DialogueReference/Examples/DialogueDefinitions/TOC.yml", "/dialogue/dialogue_", "- name: Dialogue List\n href: ../DialogueList.md");
// Particle Effects documentation
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.particles, "/ParticlesReference/Examples/ParticleComponents/", "/client_particles/", "Particles", "Particle Component");
await this.exportMarkdownDocListPage(formsByPath, outputFolder, ExportMode.particles, "/ParticlesReference/Examples/ParticleList.md", "/client_particles/", "Particle Effects", "ParticleComponents");
await this.exportListYml(formsByPath, outputFolder, ExportMode.particles, "/ParticlesReference/Examples/ParticleComponents/TOC.yml", "/client_particles/", "- name: Particle List\n href: ../ParticleList.md");
// Commands documentation
await this.exportCommandMarkdownCatalogDocs(formsByPath, outputFolder, "/CommandsReference/Examples/Commands/", "/command/cmd_");
await this.exportCommandListPage(formsByPath, outputFolder, "/CommandsReference/Examples/CommandList.md", "/command/cmd_");
await this.exportListYml(formsByPath, outputFolder, ExportMode.commands, "/CommandsReference/Examples/Commands/TOC.yml", "/command/cmd_", "- name: Command List\n href: ../CommandList.md");
// Command Types documentation (argument types for commands)
await this.exportCommandTypeMarkdownCatalogDocs(formsByPath, outputFolder, "/CommandsReference/Examples/CommandTypes/", "/command/type_");
await this.exportMarkdownDocListPage(formsByPath, outputFolder, ExportMode.commands, "/CommandsReference/Examples/CommandTypeList.md", "/command/type_", "Command Argument Types", "CommandTypes");
await this.exportListYml(formsByPath, outputFolder, ExportMode.commands, "/CommandsReference/Examples/CommandTypes/TOC.yml", "/command/type_", "- name: Command Type List\n href: ../CommandTypeList.md");
// Jigsaw Structures documentation
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.jigsaw, "/JigsawReference/Examples/JigsawComponents/", "/jigsaw/", "Jigsaw Structures", "Jigsaw Component");
await this.exportMarkdownDocListPage(formsByPath, outputFolder, ExportMode.jigsaw, "/JigsawReference/Examples/JigsawList.md", "/jigsaw/", "Jigsaw Structures", "JigsawComponents");
await this.exportListYml(formsByPath, outputFolder, ExportMode.jigsaw, "/JigsawReference/Examples/JigsawComponents/TOC.yml", "/jigsaw/", "- name: Jigsaw List\n href: ../JigsawList.md");
// Dimensions documentation
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.dimensions, "/DimensionReference/Examples/DimensionComponents/", "/dimension/", "Dimensions", "Dimension Component");
await this.exportMarkdownDocListPage(formsByPath, outputFolder, ExportMode.dimensions, "/DimensionReference/Examples/DimensionList.md", "/dimension/", "Dimensions", "DimensionComponents");
await this.exportListYml(formsByPath, outputFolder, ExportMode.dimensions, "/DimensionReference/Examples/DimensionComponents/TOC.yml", "/dimension/", "- name: Dimension List\n href: ../DimensionList.md");
// Attachables documentation
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.attachables, "/AttachableReference/Examples/AttachableDefinitions/", "/attachable/", "Attachables", "Attachable Definition");
await this.exportMarkdownDocListPage(formsByPath, outputFolder, ExportMode.attachables, "/AttachableReference/Examples/AttachableList.md", "/attachable/", "Attachables", "AttachableDefinitions");
await this.exportListYml(formsByPath, outputFolder, ExportMode.attachables, "/AttachableReference/Examples/AttachableDefinitions/TOC.yml", "/attachable/", "- name: Attachable List\n href: ../AttachableList.md");
// JSON UI documentation
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.jsonUi, "/JsonUiReference/Examples/JsonUiComponents/", "/ui/ui_", "JSON UI", "JSON UI Component");
await this.exportMarkdownDocListPage(formsByPath, outputFolder, ExportMode.jsonUi, "/JsonUiReference/Examples/JsonUiList.md", "/ui/ui_", "JSON UI", "JsonUiComponents");
await this.exportListYml(formsByPath, outputFolder, ExportMode.jsonUi, "/JsonUiReference/Examples/JsonUiComponents/TOC.yml", "/ui/ui_", "- name: JSON UI List\n href: ../JsonUiList.md");
// Trade Tables documentation
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.tradeTables, "/TradeTableReference/Examples/TradeTableComponents/", "/trade/trade", "Trade Tables", "Trade Table Component");
await this.exportMarkdownDocListPage(formsByPath, outputFolder, ExportMode.tradeTables, "/TradeTableReference/Examples/TradeTableList.md", "/trade/trade", "Trade Tables", "TradeTableComponents");
await this.exportListYml(formsByPath, outputFolder, ExportMode.tradeTables, "/TradeTableReference/Examples/TradeTableComponents/TOC.yml", "/trade/trade", "- name: Trade Table List\n href: ../TradeTableList.md");
// Feature Rules documentation
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.featureRules, "/FeatureRulesReference/Examples/FeatureRulesComponents/", "/feature_rules/", "Feature Rules", "Feature Rule Component");
await this.exportMarkdownDocListPage(formsByPath, outputFolder, ExportMode.featureRules, "/FeatureRulesReference/Examples/FeatureRulesList.md", "/feature_rules/", "Feature Rules", "FeatureRulesComponents");
await this.exportListYml(formsByPath, outputFolder, ExportMode.featureRules, "/FeatureRulesReference/Examples/FeatureRulesComponents/TOC.yml", "/feature_rules/", "- name: Feature Rules List\n href: ../FeatureRulesList.md");
// Voxel Shapes documentation
await this.exportMarkdownCatalogDocs(formsByPath, outputFolder, ExportMode.voxelShapes, "/VoxelShapesReference/Examples/VoxelShapesComponents/", "/voxel_shapes/", "Voxel Shapes", "Voxel Shape Component");
await this.exportMarkdownDocListPage(formsByPath, outputFolder, ExportMode.voxelShapes, "/VoxelShapesReference/Examples/VoxelShapesList.md", "/voxel_shapes/", "Voxel Shapes", "VoxelShapesComponents");
await this.exportListYml(formsByPath, outputFolder, ExportMode.voxelShapes, "/VoxelShapesReference/Examples/VoxelShapesComponents/TOC.yml", "/voxel_shapes/", "- name: Voxel Shapes List\n href: ../VoxelShapesList.md");
// Log skipped files summary
if (this._skippedFiles.length > 0) {
Log_1.default.debug(`[FormMarkdownDocGen] Skipped ${this._skippedFiles.length} files that already exist in reference folder:`);
for (const skipped of this._skippedFiles) {
Log_1.default.verbose(`[FormMarkdownDocGen] - ${skipped}`);
}
}
}
/**
* Check if a file exists in the reference folder (existing docs).
* Also checks for common naming variations (e.g., minecraft_ prefix).
*/
async existsInReferenceFolder(relativePath) {
if (!this._referenceFolder) {
return false;
}
// Normalize path separators
const normalizedPath = relativePath.replace(/\\/g, "/");
// Check exact path
const exactFile = await this._referenceFolder.getFileFromRelativePath(normalizedPath);
if (exactFile && (await exactFile.exists())) {
return true;
}
// Check with minecraft prefix variations
const pathParts = normalizedPath.split("/");
const fileName = pathParts.pop() || "";
const dirPath = pathParts.join("/");
// Extract base name without extension for variations
const baseName = fileName.replace(/\.md$/i, "");
// Helper to capitalize first letter after underscore or prefix
const toTitleCase = (str) => str.charAt(0).toUpperCase() + str.slice(1);
// Try minecraftX_ prefix variations (common in existing docs)
// e.g., recipe_shaped.md -> minecraftRecipe_Shaped.md
const prefixVariations = [];
// For recipe_X -> minecraftRecipe_X (capitalized)
if (baseName.startsWith("recipe_")) {
const suffix = baseName.substring(7); // remove "recipe_"
prefixVariations.push("minecraftRecipe_" + toTitleCase(suffix) + ".md");
}
// For emitter_X -> minecraftEmitter_X (capitalized)
if (baseName.startsWith("emitter_")) {
const suffix = baseName.substring(8); // remove "emitter_"
prefixVariations.push("minecraftEmitter_" + suffix + ".md");
}
// For particle_X -> minecraftParticle_X
if (baseName.startsWith("particle_")) {
const suffix = baseName.substring(9); // remove "particle_"
prefixVariations.push("minecraftParticle_" + suffix + ".md");
}
// For loot_X -> various loot table file patterns
if (baseName.startsWith("loot_")) {
// Existing loot docs use different structure (enchantingtables.md, etc.)
// Skip individual loot component files if any loot doc exists
prefixVariations.push("enchantingtables.md");
prefixVariations.push("itemmodtables.md");
prefixVariations.push("miscellaneoustables.md");
}
// Generic minecraft prefix (minecraft + capitalized first letter)
prefixVariations.push("minecraft" + toTitleCase(baseName) + ".md");
for (const variation of prefixVariations) {
const variantPath = dirPath ? `${dirPath}/${variation}` : variation;
const variantFile = await this._referenceFolder.getFileFromRelativePath(variantPath);
if (variantFile && (await variantFile.exists())) {
return true;
}
}
return false;
}
getFileNameFromBaseName(baseName, exportMode) {
let fileName = baseName;
if (exportMode === ExportMode.triggers && fileName.startsWith("minecraft_on_")) {
fileName = "minecraftTrigger_" + baseName.substring(10);
}
else if (exportMode === ExportMode.triggers && fileName.startsWith("minecraft_on_")) {
fileName = "minecraftTrigger_" + baseName.substring(10);
}
else if (exportMode === ExportMode.AIGoals && fileName.startsWith("minecraft_behavior")) {
fileName = "minecraftB" + baseName.substring(11);
}
else if (exportMode === ExportMode.entityComponents && fileName.startsWith("minecraft_")) {
fileName = "minecraftComponent_" + baseName.substring(10);
}
else if (exportMode === ExportMode.blockComponents && fileName.startsWith("minecraft_")) {
fileName = "minecraftBlock_" + baseName.substring(10);
}
else if (exportMode === ExportMode.itemComponents && fileName.startsWith("minecraft_")) {
fileName = "minecraft_" + baseName.substring(10);
}
else if (exportMode === ExportMode.clientBiomes && fileName.startsWith("minecraft_")) {
fileName = "minecraftClientBiomes_" + baseName.substring(10);
}
else if (exportMode === ExportMode.biomes && fileName.startsWith("minecraft_")) {
fileName = "minecraftBiomes_" + baseName.substring(10);
}
else if (exportMode === ExportMode.commands && fileName.startsWith("cmd_")) {
fileName = baseName.substring(4);
}
fileName = fileName.replace("_horse_", "_horse.");
fileName = fileName.replace("_jump_", "_jump.");
if (fileName.indexOf("movement_tracking") < 0) {
fileName = fileName.replace("_movement_", "_movement.");
}
fileName = fileName.replace("_navigation_", "_navigation.");
fileName = fileName.replace("_player_", "_player.");
return fileName;
}
async exportMarkdownAggregatedPage(formsByPath, outputFolder, exportMode, filePath, formsPath, category, categoryExtended, exclusionList) {
const targetFolder = await outputFolder.ensureFolderFromRelativePath(filePath);
if (!targetFolder) {
return;
}
let hasEnsuredFolder = false;
formsByPath = this.getFormsFromFilter(formsByPath, formsPath, exportMode, exclusionList);
for (const formPath in formsByPath) {
const formO = formsByPath[formPath];
if (formO) {
let baseName = StorageUtilities_1.default.getBaseFromName(StorageUtilities_1.default.getLeafName(formPath));
if (baseName.endsWith(".form")) {
baseName = baseName.substring(0, baseName.length - 5);
}
let fileName = this.getFileNameFromBaseName(baseName, exportMode);
const relativeFilePath = filePath + fileName + ".md";
// Skip if file exists in reference folder
if (await this.existsInReferenceFolder(relativeFilePath)) {
this._skippedFiles.push(relativeFilePath);
continue;
}
if (!hasEnsuredFolder) {
await targetFolder.ensureExists();
hasEnsuredFolder = true;
}
const markdownFile = targetFolder.ensureFile(fileName + ".md");
await this.saveMarkdownDocFromForm(markdownFile, formO, baseName, exportMode, category, categoryExtended);
}
}
}
async exportValidatorMarkdownCatalogDocs(formsByPath, outputFolder, exportMode, subFolderPath, formsPath, categoryPlural, categorySingular, exclusionList) {
const targetFolder = await outputFolder.ensureFolderFromRelativePath(subFolderPath);
if (!targetFolder) {
return;
}
let hasEnsuredFolder = false;
formsByPath = this.getFormsFromFilter(formsByPath, formsPath, exportMode, exclusionList);
for (const formPath in formsByPath) {
const formO = formsByPath[formPath];
if (formO) {
let baseName = StorageUtilities_1.default.getBaseFromName(StorageUtilities_1.default.getLeafName(formPath));
if (baseName.endsWith(".form")) {
baseName = baseName.substring(0, baseName.length - 5);
}
let fileName = this.getFileNameFromBaseName(baseName, exportMode);
const relativeFilePath = subFolderPath + fileName + ".md";
// Skip if file exists in reference folder
if (await this.existsInReferenceFolder(relativeFilePath)) {
this._skippedFiles.push(relativeFilePath);
continue;
}
if (!hasEnsuredFolder) {
await targetFolder.ensureExists();
hasEnsuredFolder = true;
}
const markdownFile = targetFolder.ensureFile(fileName + ".md");
await this.saveValidatorMarkdownDocFromForm(markdownFile, formO, baseName, exportMode, categoryPlural, categorySingular);
}
}
}
async exportMarkdownCatalogDocs(formsByPath, outputFolder, exportMode, subFolderPath, formsPath, categoryPlural, categorySingular, exclusionList) {
const targetFolder = await outputFolder.ensureFolderFromRelativePath(subFolderPath);
if (!targetFolder) {
return;
}
let hasEnsuredFolder = false;
formsByPath = this.getFormsFromFilter(formsByPath, formsPath, exportMode, exclusionList);
for (const formPath in formsByPath) {
const formO = formsByPath[formPath];
if (formO) {
let baseName = StorageUtilities_1.default.getBaseFromName(StorageUtilities_1.default.getLeafName(formPath));
if (baseName.endsWith(".form")) {
baseName = baseName.substring(0, baseName.length - 5);
}
let fileName = this.getFileNameFromBaseName(baseName, exportMode);
const relativeFilePath = subFolderPath + fileName + ".md";
// Skip if file exists in reference folder
if (await this.existsInReferenceFolder(relativeFilePath)) {
this._skippedFiles.push(relativeFilePath);
continue;
}
if (!hasEnsuredFolder) {
await targetFolder.ensureExists();
hasEnsuredFolder = true;
}
const markdownFile = targetFolder.ensureFile(fileName + ".md");
await this.saveMarkdownDocFromForm(markdownFile, formO, baseName, exportMode, categoryPlural, categorySingular);
}
}
}
async exportMarkdownDocListPage(formsByPath, outputFolder, exportMode, subFolderPath, formsPath, category, subFolderName, exclusionList) {
// Skip if list page exists in reference folder
if (await this.existsInReferenceFolder(subFolderPath)) {
this._skippedFiles.push(subFolderPath);
return;
}
const targetFile = await outputFolder.ensureFileFromRelativePath(subFolderPath);
if (!targetFile) {
return;
}
formsByPath = this.getFormsFromFilter(formsByPath, formsPath, exportMode, exclusionList);
const content = [];
content.push(Utilities_1.default.stringFormat(exports.MarkdownTop, category + " Documentation - " + category, "A reference document describing all current " + category));
let internalDepCount = 0;
content.push("# " + category + " Documentation\n");
content.push("| " + category + " | Description |");
content.push("|:-----|:----------|");
for (const formPath in formsByPath) {
const formO = formsByPath[formPath];
if (formO && !formO.isDeprecated && !formO.isInternal) {
let baseName = StorageUtilities_1.default.getBaseFromName(StorageUtilities_1.default.getLeafName(formPath));
if (baseName.endsWith(".form")) {
baseName = baseName.substring(0, baseName.length - 5);
}
let canonName = EntityTypeDefinition_1.default.getComponentFromBaseFileName(baseName);
if (exportMode === ExportMode.commands && formO.title) {
// For commands, prefer the friendly title (e.g., "camera") over the id (e.g., "cmd_camera")
canonName = formO.title;
}
else if (formO.id) {
canonName = formO.id;
}
else if (formO.title) {
canonName = formO.title;
}
canonName = canonName?.replace(/\`/gi, "");
content.push("| [" +
canonName +
"](" +
subFolderName +
"/" +
this.getFileNameFromBaseName(baseName, exportMode) +
".md)| " +
(formO.description ? this.sanitizeForTable(this.getFirstSentence(formO.description)) : "") +
" |");
}
else if (formO) {
internalDepCount++;
}
}
if (internalDepCount > 0) {
content.push("");
content.push("## Internal/Deprecated Components");
content.push("These components are either deprecated or internal to Minecraft and not usable in custom content.");
content.push("");
content.push("| " + category + " | Description |");
content.push("|:-----|:----------|");
for (const formPath in formsByPath) {
const formO = formsByPath[formPath];
if (formO && (formO.isDeprecated || formO.isInternal)) {
let baseName = StorageUtilities_1.default.getBaseFromName(StorageUtilities_1.default.getLeafName(formPath));
if (baseName.endsWith(".form")) {
baseName = baseName.substring(0, baseName.length - 5);
}
let canonName = EntityTypeDefinition_1.default.getComponentFromBaseFileName(baseName);
content.push("| [" +
canonName +
"](" +
subFolderName +
"/" +
this.getFileNameFromBaseName(baseName, exportMode) +
".md)| " +
(formO.description ? this.sanitizeForTable(this.getFirstSentence(formO.description)) : "") +
" |");
}
}
}
targetFile.setContent(content.join("\n"));
await targetFile.saveContent();
}
async exportListYml(formsByPath, outputFolder, exportMode, subFolderPath, formsPath, header, prefix, exclusionList) {
// Skip if TOC.yml exists in reference folder
if (await this.existsInReferenceFolder(subFolderPath)) {
this._skippedFiles.push(subFolderPath);
return;
}
const targetFile = await outputFolder.ensureFileFromRelativePath(subFolderPath);
if (!targetFile) {
return;
}
const content = [];
if (header) {
content.push(header);
}
formsByPath = this.getFormsFromFilter(formsByPath, formsPath, exportMode, exclusionList);
for (const formPath in formsByPath) {
const formO = formsByPath[formPath];
if (formO && !formO.isDeprecated && !formO.isInternal) {
let baseName = StorageUtilities_1.default.getBaseFromName(StorageUtilities_1.default.getLeafName(formPath));
if (baseName.endsWith(".form")) {
baseName = baseName.substring(0, baseName.length - 5);
}
if (prefix && baseName.startsWith("minecraftComponent_")) {
baseName = prefix + baseName.substring(19);
}
else if (prefix && baseName.startsWith("minecraft_")) {
baseName = prefix + baseName.substring(10);
}
else if (prefix) {
baseName = prefix + baseName;
}
let name;
if (exportMode === ExportMode.commands && formO.title) {
// For commands, prefer the friendly title over the id
name = formO.title;
}
else {
name = formO.id ? formO.id : formO.title;
}
name = name?.replace(/\`/gi, "");
content.push("- name: " + name);
content.push(" href: " + this.getFileNameFromBaseName(baseName, exportMode) + ".md");
}
}
targetFile.setContent(content.join("\n"));
await targetFile.saveContent();
}
getFirstSentence(description) {
let endOfSentence = description.indexOf(". ");
if (endOfSentence >= 0) {
description = description.substring(0, endOfSentence + 1);
}
return description;
}
async saveMarkdownDocFromForm(markdownFile, form, baseName, exportMode, category, categoryExtended) {
const content = [];
let canonName = "minecraft:" + EntityTypeDefinition_1.default.getComponentFromBaseFileName(baseName);
if (exportMode === ExportMode.websockets && form.id) {
canonName = form.id;
}
content.push(Utilities_1.default.stringFormat(exports.MarkdownTop, category + " Documentation - " + canonName, "Describes the " + canonName + " " + categoryExtended.toLowerCase()));
content.push("# " + category + " Documentation - " + canonName + "\n");
if (form.isDeprecated) {
content.push("> [!IMPORTANT]");
content.push("> This type is now deprecated, and no longer in use in the latest versions of Minecraft.");
content.push("");
}
if (form.isInternal) {
content.push("> [!IMPORTANT]");
content.push("> This type is internal to vanilla Minecraft usage, and is not functional or supported within custom Minecraft content.");
content.push("");
}
// Render the form body first, then extract headings for a TOC.
// This guarantees TOC bookmarks match the actual rendered headings exactly.
const bodyContent = [];
await this.appendForm(form, bodyContent, 0);
// Extract top-level sub-section headings (### level) from rendered body for the TOC
const tocEntries = [];
for (const line of bodyContent) {
// Match ### headings (depth-1 sub-sections). Lines may start with \n.
const match = line.match(/^\n?### (.+)$/);
if (match) {
const headingText = match[1].trim();
// Skip "Properties" headings and "choices" tables — they're sub-headings, not TOC-worthy
if (!headingText.endsWith("Properties") && !headingText.endsWith("choices")) {
const bookmark = this.getMarkdownBookmark(headingText);
if (!tocEntries.some((e) => e.bookmark === bookmark)) {
tocEntries.push({ text: headingText, bookmark });
}
}
}
}
if (tocEntries.length > 4) {
content.push("## Contents\n");
for (const entry of tocEntries) {
content.push("- [" + entry.text + "](#" + entry.bookmark + ")");
}
content.push("");
}
content.push(...bodyContent);
if (form.samples) {
content.push("\n## Samples\n");
let samplesAdded = 0;
const linesAdded = [];
for (const samplePath in form.samples) {
let sampleArr = form.samples[samplePath];
let addedHeader = false;
if (sampleArr && samplesAdded < 12) {
const sampBaseName = StorageUtilities_1.default.getBaseFromName(StorageUtilities_1.default.getLeafName(samplePath));
let targetPath = samplePath;
if (!Array.isArray(sampleArr)) {
Log_1.default.debug("Malformed sample node at `" + samplePath + "` for file at `" + markdownFile.fullPath + "`");
}
else {
for (const sample of sampleArr) {
let line = "\n```json\n";
if (baseName.startsWith("minecraft_") &&
(typeof sample.content !== "string" || !sample.content.startsWith("minecraft:"))) {
line += '"' + canonName + '": ';
}
if (typeof sample.content === "object" || Array.isArray(sample.content)) {
line += JSON.stringify(sample.content, undefined, 2) + "\n```\n";
}
else {
if (typeof sample.content === "string") {
let cont = sample.content.trim();
if (cont.startsWith("{") && cont.endsWith("}")) {
line += cont + "\n```\n";
}
else {
line += '"' + cont + '"\n```\n';
}
}
else {
line += sample.content + "\n```\n";
}
}
if (!linesAdded.includes(line)) {
if (!addedHeader) {
addedHeader = true;
if (targetPath !== "samples" && targetPath !== "sample") {
if (targetPath.startsWith("/vanilla")) {
targetPath = "https://github.com/Mojang/bedrock-samples/tree/preview" + targetPath.substring(8);
}
else if (targetPath.startsWith("/samples")) {
targetPath = "https://github.com/microsoft/minecraft-samples/tree/main" + targetPath.substring(8);
}
const humanName = Utilities_1.default.humanifyMinecraftName(sampBaseName.substring(0, 1).toUpperCase() + sampBaseName.substring(1));
// Only render as a link if the path was converted to a full URL
if (targetPath.startsWith("http")) {
content.push("#### [" + humanName + "](" + targetPath + ")\n");
}
else {
content.push("#### " + humanName + "\n");
}
}
}
if (sampleArr.length > 1) {
content.push("At " + sample.path + ": ");
}
linesAdded.push(line);
content.push(line);
samplesAdded++;
}
}
}
}
}
}
markdownFile.setContent(content.join("\n"));
await markdownFile.saveContent();
}
async saveValidatorMarkdownDocFromForm(markdownFile, form, baseName, exportMode, category, categoryExtended) {
const content = [];
let canonName = "minecraft:" + EntityTypeDefinition_1.default.getComponentFromBaseFileName(baseName);
if (exportMode === ExportMode.websockets && form.id) {
canonName = form.id;
}
content.push(Utilities_1.default.stringFormat(exports.MarkdownTop, category + " Documentation - " + canonName, "Describes the " + canonName + " " + categoryExtended.toLowerCase()));
content.push("# " + category + " Documentation - " + canonName + "\n");
if (form.isDeprecated) {
content.push("> [!IMPORTANT]");
content.push("> This type is now deprecated, and no longer in use in the latest versions of Minecraft.");
content.push("");
}
await this.appendValidatorForm(form, conten