Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | 9x 9x 9x 9x 9x 9x 9x 9x 9x 12196x 9x 9x 12196x 12196x 9x 12179x 8x 9x 16709x 50798x 4538x 46260x 42493x 42493x 42493x 9x 12171x 340x 340x 1016x 5056x 340x 2x 2x 9x 12171x 12171x 34981x 12171x 40720x 40720x 12171x 9x 12171x 12171x 9x 2x | /** * Shared Handler Utilities * * Common functionality used by both secure VM and trusted execution handlers. */ import type { FileReaderFn, ScriptEventHandler, ScriptEventHandlerFieldSchema, ScriptEventHelperDef, ScriptEventPresetValue, UserPresetsGroup, } from "lib/scriptEventsHandlers/handlerTypes"; import * as eventHelpers from "lib/events/helpers"; import * as eventSystemHelpers from "lib/helpers/eventSystem"; import * as compileEntityEvents from "lib/compiler/compileEntityEvents"; import * as scriptValueTypes from "shared/lib/scriptValue/types"; import * as scriptValueHelpers from "shared/lib/scriptValue/helpers"; import * as l10n from "shared/lib/lang/l10n"; import trimLines from "shared/lib/helpers/trimlines"; import { createScriptEventHandlerAPI } from "./handlerApi"; /** * Creates the standard set of mock dependencies available to handler code. */ export const createMockRequire = ( readFile: FileReaderFn, ): Record<string, unknown> => { return { "plugin-api": createScriptEventHandlerAPI(readFile), // Legacy compatibility for older plugins "./helpers": eventHelpers, "../helpers/l10n": l10n, "../helpers/eventSystem": eventSystemHelpers, "../compiler/compileEntityEvents": compileEntityEvents, "../helpers/trimlines": trimLines, "shared/lib/helpers/trimlines": trimLines, "shared/lib/scriptValue/helpers": scriptValueHelpers, "shared/lib/scriptValue/types": scriptValueTypes, }; }; /** * Analyzes JavaScript code to determine module system (ES modules vs CommonJS). */ const ESM_RE = /(^|\n|\r)\s*import\s+(?!\()|(^|\n|\r)\s*export\s+(?:\*|default|{|\w)/m; export const detectModuleSystem = (code: string): "module" | "global" => { const isESM = ESM_RE.test(code); return isESM ? "module" : "global"; }; /** * Validates that the loaded handler exports the required properties. */ export const validateHandlerMetadata = ( metadata: unknown, filename: string, ): void => { if ( !metadata || typeof metadata !== "object" || !("id" in metadata) || typeof metadata.id !== "string" ) { throw new Error( `Script event handler at ${filename} does not export an 'id' property.`, ); } }; /** * Creates a flat lookup table for efficient field access during editor runtime. */ export const buildFieldsLookup = ( handler: ScriptEventHandler, fields: ScriptEventHandlerFieldSchema[], ): void => { for (const field of fields) { if (field.type === "group" && field.fields) { buildFieldsLookup(handler, field.fields); } else if (field.key) { // Mark fields that have post-update callbacks for renderer optimization const fieldRecord = field as Record<string, unknown>; (field as ScriptEventHandlerFieldSchema).hasPostUpdateFn = !!fieldRecord.postUpdateFn; handler.fieldsLookup[field.key] = field; } } }; /** * Validates that user preset groups account for all handler fields. */ export const validateUserPresets = (handler: ScriptEventHandler): void => { if (!handler.userPresetsGroups) return; const allFields = Object.keys(handler.fieldsLookup); const presetFields = handler.userPresetsGroups .map((group: { fields: string[] }) => group.fields) .flat() .concat(handler.userPresetsIgnore ?? []); const missingFields = allFields.filter((key) => !presetFields.includes(key)); if (missingFields.length > 0) { console.error( `${handler.id} defined userPresetsGroups but did not include some fields in either userPresetsGroups or userPresetsIgnore`, ); console.error("Missing fields: " + missingFields.join(", ")); } }; /** * Creates the base handler object structure with all required properties. */ export const createHandlerBase = ( metadata: Record<string, unknown>, hasAutoLabelFunction: boolean, cleanup: () => void, ): Omit< ScriptEventHandler & { cleanup: () => void }, "autoLabel" | "compile" > => { const handler = { ...metadata, id: metadata.id as string, fields: Array.isArray(metadata.fields) ? (metadata.fields as ScriptEventHandlerFieldSchema[]) : [], cleanup, hasAutoLabel: hasAutoLabelFunction, fieldsLookup: {} as Record<string, ScriptEventHandlerFieldSchema>, name: metadata.name as string | undefined, description: metadata.description as string | undefined, groups: metadata.groups as string[] | string | undefined, subGroups: metadata.subGroups as Record<string, string> | undefined, deprecated: metadata.deprecated as boolean | undefined, isConditional: false, editableSymbol: metadata.editableSymbol as boolean | undefined, allowChildrenBeforeInitFade: metadata.allowChildrenBeforeInitFade as | boolean | undefined, waitUntilAfterInitFade: metadata.waitUntilAfterInitFade as | boolean | undefined, helper: metadata.helper as ScriptEventHelperDef | undefined, presets: metadata.presets as ScriptEventPresetValue[] | undefined, userPresetsGroups: metadata.userPresetsGroups as | UserPresetsGroup[] | undefined, userPresetsIgnore: metadata.userPresetsIgnore as string[] | undefined, }; // Set conditional flag based on presence of events fields handler.isConditional = handler.fields && !!handler.fields.find((f) => f.type === "events"); // Mark fields that have post-update callbacks for renderer optimization for (const field of handler.fields) { const fieldRecord = field as Record<string, unknown>; (field as ScriptEventHandlerFieldSchema).hasPostUpdateFn = !!fieldRecord.postUpdateFn; } return handler; }; /** * Assembles a complete handler by building lookup tables and validating configuration. */ export const finalizeHandler = (handler: ScriptEventHandler): void => { buildFieldsLookup(handler, handler.fields); validateUserPresets(handler); }; export const noReadFileFn: FileReaderFn = (_path: string): string => { throw new Error("This handler is not configured to read files."); }; |