fix(custom-widgets): finish v2 hardening

This commit is contained in:
Thomas Camlong committed 2026-07-20 23:39:57 +02:00
1 parent 7c66c912e7
commit 1c02678a7e
97 files changed
+2833 -12393

No files matched your search

@@ -130,7 +130,11 @@ export function CustomWidgetCodeEditor(props: CustomWidgetCodeEditorProps) {
/>
{props.reference && (
<Collapse expanded={referenceOpened}>
<CustomWidgetSchemaReference reference={props.reference} messages={props.messages} />
<CustomWidgetSchemaReference
id={`${props.id}-schema-reference`}
reference={props.reference}
messages={props.messages}
/>
</Collapse>
)}
<div className={classes.viewport}>
@@ -201,9 +205,11 @@ export function ReadOnlyCustomWidgetCode(props: Omit<CustomWidgetCodeEditorProps
}
export function CustomWidgetSchemaReference({
id,
reference,
messages,
}: {
id: string;
reference: CustomWidgetSchemaReferenceData;
messages: CustomWidgetEditorMessages;
}) {
@@ -219,7 +225,7 @@ export function CustomWidgetSchemaReference({
{Object.entries(values).map(([key, value]) => (
<Tabs.Panel key={key} value={key} pt="xs">
<ReadOnlyCustomWidgetCode
id={`schema-reference-${key}`}
id={`${id}-${key}`}
label={labels[key as keyof typeof labels]}
language="json"
value={JSON.stringify(value, null, 2)}
@@ -9,6 +9,7 @@ import {
getCustomJsxComponentProps,
} from "../core/component-catalog";
import { customJsxTablerIconNames } from "../core/tabler-icons";
import { ROOT_BINDINGS } from "../jsx/analyzer-language";
const enabledCatalogComponents = customJsxAuthoringCatalog.components.filter(
(component) => component.safety !== "denied",
@@ -40,7 +41,7 @@ const iconCompletions: Completion[] = customJsxTablerIconNames.map((name) => ({
detail: "Safe Tabler icon",
}));
const safeBlockCompletions: Completion[] = [
export const customJsxSafeBlockCompletions: Completion[] = [
snippetCompletion("map((item) => {\n\tconst value = ${};\n\treturn ${};\n})", {
label: "map block",
detail: "Safe immutable collection callback",
@@ -53,6 +54,12 @@ const safeBlockCompletions: Completion[] = [
}),
];
const rootBindingCompletions: Completion[] = [...ROOT_BINDINGS].toSorted().map((label) => ({
label,
type: ["data", "status", "options", "inputs"].includes(label) ? "variable" : "function",
detail: "Safe Custom JSX binding",
}));
export function getCustomJsxLocalConstCompletions(source: string, cursor: number): Completion[] {
const wrapped = `<>${source}</>`;
const target = cursor + 2;
@@ -63,6 +70,18 @@ export function getCustomJsxLocalConstCompletions(source: string, cursor: number
activeNode = parent;
}
const completions: Completion[] = [];
let arrow = activeNode.parent;
while (arrow && arrow.name !== "ArrowFunction") arrow = arrow.parent;
arrow
?.getChild("ParamList")
?.getChildren("VariableDefinition")
.forEach((definition) => {
completions.push({
label: wrapped.slice(definition.from, definition.to),
type: "variable",
detail: "Safe callback parameter",
});
});
for (let statement = activeNode.firstChild; statement; statement = statement.nextSibling) {
if (statement.name !== "VariableDeclaration" || statement.from >= target) continue;
if (statement.firstChild?.name !== "const") continue;
@@ -97,7 +116,13 @@ export const createCustomJsxCompletionSource =
: componentCompletions;
return {
from: word?.from ?? context.pos,
options: [...localCompletions, ...safeBlockCompletions, ...contextualCompletions, ...additional],
options: [
...localCompletions,
...customJsxSafeBlockCompletions,
...rootBindingCompletions,
...contextualCompletions,
...additional,
],
};
};