diff --git a/package-lock.json b/package-lock.json index e40fe6a9..ce3c0ed7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "deepslate-1.18.2": "npm:deepslate@0.9.0", "deepslate-1.20.4": "npm:deepslate@0.20.1", "diff": "^7.0.0", + "dompurify": "^3.2.6", "highlight.js": "^11.5.1", "howler": "^2.2.3", "js-yaml": "^3.14.1", @@ -1443,9 +1444,9 @@ "dev": true }, "node_modules/@types/trusted-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", - "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==" + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==" }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "5.28.0", @@ -2384,6 +2385,14 @@ "url": "https://github.com/fb55/domhandler?sponsor=1" } }, + "node_modules/dompurify": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.6.tgz", + "integrity": "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/domutils": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", @@ -6037,9 +6046,9 @@ "dev": true }, "@types/trusted-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", - "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==" + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==" }, "@typescript-eslint/eslint-plugin": { "version": "5.28.0", @@ -6660,6 +6669,14 @@ "domelementtype": "^2.3.0" } }, + "dompurify": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.6.tgz", + "integrity": "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==", + "requires": { + "@types/trusted-types": "^2.0.7" + } + }, "domutils": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", diff --git a/package.json b/package.json index 102a2b9a..34a4e12a 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "deepslate-1.18.2": "npm:deepslate@0.9.0", "deepslate-1.20.4": "npm:deepslate@0.20.1", "diff": "^7.0.0", + "dompurify": "^3.2.6", "highlight.js": "^11.5.1", "howler": "^2.2.3", "js-yaml": "^3.14.1", diff --git a/public/mcdoc/create.mcdoc b/public/mcdoc/create.mcdoc index 7af6198e..4a8b4f18 100644 --- a/public/mcdoc/create.mcdoc +++ b/public/mcdoc/create.mcdoc @@ -1,9 +1,12 @@ +use ::java::world::component::DataComponentPatch + dispatch minecraft:resource[create:recipes] to struct Recipes { - type: #[id] Type, + type: Type, ...create:recipes[[type]], } enum(string) Type { + Compacting = "create:compacting", Crushing = "create:crushing", Cutting = "create:cutting", Deploying = "create:deploying", @@ -25,6 +28,48 @@ struct NBT { Potion?: string, } +type Item = struct { + #[until="1.21.1"] + item: string, + #[since="1.21.1"] + id: string, + chance?: float @ 0.., + count?: int @ 1.., + #[since="1.21.1"] + components?: DataComponentPatch, +} + +type ItemWithCount = struct { + #[until="1.21.1"] + item: string, + #[since="1.21.1"] + id: string, + count?: int @ 0.., + #[since="1.21.1"] + components?: DataComponentPatch, +} + +type SimpleItem = struct { + #[until="1.21.1"] + item: string, + #[since="1.21.1"] + id: string, + #[since="1.21.1"] + components?: DataComponentPatch, +} + +type Fluid = struct { + #[until="1.21.1"] + fluid: string, + #[until="1.21.1"] + nbt?: NBT, + #[since="1.21.1"] + id: string, + amount: int @ 1.., + #[since="1.21.1"] + components?: DataComponentPatch, +} + type ItemOrTag = ( struct { item: string, @@ -33,168 +78,175 @@ type ItemOrTag = ( } ) -type FluidOrTag = ( +type ItemOrTagWithCount = ( struct { - fluid: string, - amount: int @ 1.., - nbt?: NBT, + item: string, + count?: int @ 1.., } | struct { - fluidTag: string, - amount: int @ 1.., - nbt?: NBT, + tag: string, + count?: int @ 1.., } ) +type FluidOrTag = ( + struct { + fluid: string, + #[since="1.21.1"] + type: "fluid_stack", + amount: int @ 1.., + #[until="1.21.1"] + nbt?: NBT, + #[since="1.21.1"] + components?: DataComponentPatch, + } | struct { + #[until="1.21.1"] + fluidTag: string, + #[since="1.21.1"] + fluid_tag: string, + #[since="1.21.1"] + type: "fluid_tag", + amount: int @ 1.., + #[until="1.21.1"] + nbt?: NBT, + #[since="1.21.1"] + components?: DataComponentPatch, + } +) + +type MixingResult = struct { + id: string, + /// Used for items; optional field. + count?: int @ 1.., + /// Used for fluids; mandatory field. + amount?: int @ 1.., + components?: DataComponentPatch, +} + +dispatch create:recipes[create:compacting] to struct { + ingredients: [(ItemOrTagWithCount | FluidOrTag)] @ 1.., + results: ( + #[until="1.21.1"] + [(ItemWithCount | Fluid)] @ 1.. | + #[since="1.21.1"] + [MixingResult] @ 1 | + ), +} + dispatch create:recipes[create:crushing] to struct { + #[until="1.21.1"] processingTime: int @ 1.., + #[since="1.21.1"] + processing_time: int @ 1.., ingredients: [ItemOrTag] @ 1, - results: [struct { - chance?: float @ 0.., - count?: int @ 1.., - item: string, - }] @ 1.., + results: [Item] @ 1.., } dispatch create:recipes[create:cutting] to struct { + #[until="1.21.1"] processingTime: int @ 1.., - ingredients: [struct { - item?: string, // Make the user select only one - tag?: string, - count?: int @ 1.., - }] @ 1, - results: [struct { - item: string, - count?: int @ 1.., - }] @ 1, + #[since="1.21.1"] + processing_time: int @ 1.., + ingredients: [ItemOrTag] @ 1, + results: [Item] @ 1, } dispatch create:recipes[create:deploying] to struct { - /// The first object is the base item and the second object is the ingredient + /// The first item is the base item; the second is the ingredient to be deployed. ingredients: [ItemOrTag] @ 2, + /// Defaults to false. + #[until="1.21.1"] keepHeldItem?: boolean, - results: [struct { - item: string, - }] @ 1, + /// Defaults to false. + #[since="1.21.1"] + keep_held_item?: boolean, + results: [SimpleItem] @ 1, } dispatch create:recipes[create:emptying] to struct { ingredients: [ItemOrTag] @ 1, - results: [struct { - item: string, - count?: int @ 1.., - }, struct { - fluid: string, - amount: int @ 1.., - }], + results: [SimpleItem, Fluid], } dispatch create:recipes[create:filling] to struct { ingredients: [ItemOrTag, FluidOrTag], - results: [struct { item: string }] @ 1, + results: [SimpleItem] @ 1, } dispatch create:recipes[create:haunting] to struct { ingredients: [ItemOrTag] @ 1, - results: [struct { - chance?: float @ 0.., - count?: int @ 1.., - item: string, - }] @ 1.., + results: [Item] @ 1.., } dispatch create:recipes[create:item_application] to struct { - /// The first object is the base item and the second object is the ingredient + /// The first item is the base item; the second is the ingredient to be applied. ingredients: [ItemOrTag] @ 2, - results: [struct { - item: string, - }] @ 1, + results: [SimpleItem] @ 1, } dispatch create:recipes[create:mechanical_crafting] to struct { - acceptMirrored?: boolean, - /// Warning: JEI will not display recipes greater in size than 9x9 - pattern: [string], + #[until="1.21.1"] + acceptMirrored: boolean, + #[since="1.21.1"] + accept_mirrored: boolean, + /// Identifier for the category this goes in the recipe book. + #[since="1.21.1"] + category: string, + /// **Warning:** Recipes larger than 9x9 will not be displayed in JEI. + pattern: [#[crafting_ingredient(definition=true)] string], key: struct { - [string]: ItemOrTag, - }, - result: struct { - count?: int @ 1.., - item: string, + [#[crafting_ingredient] string]: ItemOrTag, }, + result: ItemWithCount, + /// Determines if a notification is shown when unlocking this recipe. Defaults to true. + #[since="1.21.1"] + show_notification?: boolean, } dispatch create:recipes[create:milling] to struct { + #[until="1.21.1"] processingTime: int @ 1.., + #[since="1.21.1"] + processing_time: int @ 1.., ingredients: [ItemOrTag] @ 1, - results: [struct { - chance?: float @ 0.., - count?: int @ 1.., - item: string, - }] @ 1.., + results: [Item] @ 1.., } dispatch create:recipes[create:mixing] to struct { + #[until="1.21.1"] heatRequirement?: ("heated" | "superheated"), - ingredients: [(struct { - count: int @ 1.., - item: string, - } | struct { - count: int @ 1.., - tag: string, - } | struct { - fluid: string, - amount: int @ 1.., - nbt?: NBT, - } | struct { - fluidTag: string, - amount: int @ 1.., - nbt?: NBT, - })] @ 1.., - results: [(struct { - count: int @ 1.., - item: string, - } | struct { - fluid: string, - amount: int @ 1.., - nbt?: NBT, - })] @ 1, + #[since="1.21.1"] + heat_requirement?: ("heated" | "superheated"), + ingredients: [(ItemOrTagWithCount | FluidOrTag)] @ 1.., + results: ( + #[until="1.21.1"] + [(ItemWithCount | Fluid)] @ 1.. | + #[since="1.21.1"] + [MixingResult] @ 1 | + ), } dispatch create:recipes[create:pressing] to struct { - ingredients: [ItemOrTag] @ 1, - results: [struct { - item: string, - count?: int @ 1.., - }] @ 1, + ingredients: [ItemOrTag] @ 1.., + results: [ItemWithCount] @ 1, } dispatch create:recipes[create:sandpaper_polishing] to struct { ingredients: [ItemOrTag] @ 1, - results: [struct { - item: string, - count?: int @ 1.., - }] @ 1, + results: [ItemWithCount] @ 1, } dispatch create:recipes[create:sequenced_assembly] to struct { ingredient: ItemOrTag, loops: int @ 1.., - results: [struct { - chance?: float @ 0.., - count?: int @ 1.., - item: string, - }], + results: [Item] @ 1.., sequence: [Recipes], - transitionalItem: struct { - item: string, - }, + #[until="1.21.1"] + transitionalItem: SimpleItem, + #[since="1.21.1"] + transitional_item: SimpleItem, } dispatch create:recipes[create:splashing] to struct { ingredients: [ItemOrTag] @ 1, - results: [struct { - chance?: float @ 0.., - count?: int @ 1.., - item: string, - }] @ 1.., + results: [Item] @ 1.., } diff --git a/src/app/components/generator/McdocRenderer.tsx b/src/app/components/generator/McdocRenderer.tsx index c69d3d1e..5252231c 100644 --- a/src/app/components/generator/McdocRenderer.tsx +++ b/src/app/components/generator/McdocRenderer.tsx @@ -9,6 +9,7 @@ import { handleAttributes } from '@spyglassmc/mcdoc/lib/runtime/attribute/index. import type { SimplifiedEnum, SimplifiedMcdocType, SimplifiedMcdocTypeNoUnion, SimplifiedStructType, SimplifiedStructTypePairField } from '@spyglassmc/mcdoc/lib/runtime/checker/index.js' import { getValues } from '@spyglassmc/mcdoc/lib/runtime/completer/index.js' import { Identifier, ItemStack } from 'deepslate' +import DOMPurify from 'dompurify' import { marked } from 'marked' import { useCallback, useEffect, useMemo, useState } from 'preact/hooks' import config from '../../Config.js' @@ -1185,9 +1186,16 @@ interface KeyProps { function Key({ label, doc, raw }: KeyProps) { const [shown, setShown] = useFocus() + const cleanDoc = useMemo(() => { + if (!doc) { + return doc + } + return DOMPurify.sanitize(marked(doc), { FORBID_ATTR: ['style'] }) + }, [doc]) + return }