UNPKG

@minecraft/creator-tools

Version:

Minecraft Creator Tools command line and libraries.

697 lines 129 kB
"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