Skip to content

Commit 0b366f5

Browse files
akoclaude
andcommitted
fix: widget property validator respects MDL builtin property names
The validator landed in 1402742 was too strict — it rejected properties like \`Label:\`, \`Caption:\`, \`DataSource:\` on any widget whose \`.def.json\` didn't explicitly declare them. But these are MDL-recognized builtin property keywords that the widget engine routes via a dedicated path (\`isBuiltinPropName\` in \`widget_engine.go\`), not via propertyMappings — they're applied to the wrapper CustomWidget regardless of the widget's def. Broke \`make check-mdl\` on three real fixtures that worked before: - 17-custom-widget-examples.mdl: \`combobox cmbPriority (label: ...)\` - 17-custom-widget-examples.mdl: \`combobox cmbCategory (label: ...)\` - 33-alter-page-examples.mdl: \`combobox cbLifecycle (label: ...)\` (\`Label:\` is legitimate on every pluggable widget — it becomes the CustomWidget caption.) Fix: \`validatePluggableWidgetProperties\` now skips any key for which \`isBuiltinPropName\` returns true. Single source of truth — when the widget engine adds a new MDL-recognized keyword, the validator picks it up for free without a parallel list to maintain. The previously hand-curated \`universalWidgetProperties\` shrinks to just two AST-only keys (\`conditionalvisibility\`, \`conditionaleditability\`) that aren't recognized by isBuiltinPropName. Real typos still caught — verified \`optionsSourcType\` on combobox still produces \"did you mean \`optionsSourceType\`?\" against test5-app. All \`mxcli\` and \`mdl/executor\` tests still green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent b07c75b commit 0b366f5

1 file changed

Lines changed: 15 additions & 12 deletions

File tree

mdl/executor/validate_widgets.go

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,12 @@ import (
2020
"github.com/mendixlabs/mxcli/mdl/linter"
2121
)
2222

23-
// universalWidgetProperties are AST keys that the visitor sets on every
24-
// widget regardless of type. Any widget definition implicitly accepts them
25-
// even though they don't appear in propertyMappings.
26-
var universalWidgetProperties = map[string]bool{
27-
"widgettype": true, // set by the pluggablewidget / customwidget grammar
28-
"class": true, // Forms$Appearance.Class
29-
"style": true, // Forms$Appearance.Style
30-
"designproperties": true, // Forms$Appearance.DesignProperties
31-
"visible": true,
32-
"editable": true,
33-
"conditionalvisibility": true,
23+
// extraUniversalWidgetProperties are AST keys produced by the visitor that
24+
// aren't already covered by isBuiltinPropName (e.g. conditional-binding
25+
// metadata). The main allow-list is derived from isBuiltinPropName so the
26+
// validator stays in sync with whatever the widget engine actually accepts.
27+
var extraUniversalWidgetProperties = map[string]bool{
28+
"conditionalvisibility": true,
3429
"conditionaleditability": true,
3530
}
3631

@@ -122,6 +117,14 @@ func validatePluggableWidgetProperties(w *ast.WidgetV3, registry *WidgetRegistry
122117

123118
var out []linter.Violation
124119
for key := range w.Properties {
120+
// Builtin property names (Label, Class, Visible, DataSource, …) are
121+
// MDL-recognized keywords that the widget engine routes via a
122+
// dedicated path rather than via propertyMappings. Accept them
123+
// universally so the validator doesn't false-positive on legitimate
124+
// MDL idioms like `Label: 'X'` on widgets whose def.json omits it.
125+
if isBuiltinPropName(key) {
126+
continue
127+
}
125128
lower := strings.ToLower(key)
126129
if allowed[lower] {
127130
continue
@@ -186,7 +189,7 @@ func allowedWidgetProperties(def *WidgetDefinition) (map[string]bool, []string)
186189
}
187190
}
188191

189-
for k := range universalWidgetProperties {
192+
for k := range extraUniversalWidgetProperties {
190193
allowed[k] = true
191194
}
192195

0 commit comments

Comments
 (0)