Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,17 @@ jobs:
ruby-version: "4.0"
bundler-cache: true

- name: Check out pinned Mendix acceptance fixture
run: |
git clone --quiet https://github.com/ako/mxcli-sudoku.git /tmp/mxcli-sudoku
git -C /tmp/mxcli-sudoku checkout --quiet 918af72e43ca8e1d61fb0c32ad13d8da94b944e2

- name: Run strict test suite
env:
MXRB_COVERAGE: "1"
MXRB_LINE_COVERAGE_MIN: "98"
MXRB_BRANCH_COVERAGE_MIN: "95"
MXRB_ACCEPTANCE_MPRS: /tmp/mxcli-sudoku/Sudoku/Sudoku.mpr
run: bundle exec rspec

- name: Run gradual Ruby quality gate
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ remain optional external gates for compatibility and functional execution.
- write, inspect and compare deterministic MDA containers without invoking `mxbuild`;
- assemble executable portable Runtime ZIPs from materialized deployments and
installed version-matched Runtime files without invoking `mx` or `mxbuild`;
- preflight an existing MPR against its version-selected native web pipeline,
with precise unsupported widget, action, layout and nanoflow findings;
- clone, inspect and synchronize official Team Server Git repositories while
keeping PAT values in user-managed files.

Expand Down
20 changes: 20 additions & 0 deletions bin/mxrb
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,25 @@ when "validate"
exit 1
end

when "preflight"
path = ARGV.shift or abort "Usage: mxrb preflight <file.mpr> [--json]"
json = ARGV.delete("--json")
abort "Unknown arguments: #{ARGV.join(' ')}" unless ARGV.empty?
report = Mxrb.compatibility(path)
if json
puts JSON.pretty_generate(report.to_h)
else
puts "Project : #{report.path}"
puts "Mendix : #{report.mendix_version}"
puts "Units : #{report.stats[:units]}"
report.findings.each do |finding|
puts "[#{finding.severity.to_s.upcase}] #{finding.category} #{finding.type} " \
"at #{finding.location}: #{finding.message}"
end
puts "[mxrb] #{report.errors.size} error(s), #{report.warnings.size} warning(s)"
end
exit 1 unless report.compatible?

when "pack"
path = ARGV.shift or abort "Usage: mxrb pack <file.mpr> --output FILE.mda [--deployment DIR] [--force]"
option = lambda do |name|
Expand Down Expand Up @@ -1385,6 +1404,7 @@ else
widgets sync <definition.rb> <mpr> Synchronize pluggable widget schemas
export <file.mpr> <directory> Convert an MPR to editable Ruby
validate <file.mpr> Check MPR structure, hashes and v2 contents
preflight <file.mpr> [--json] Audit native compiler/runtime compatibility
compare <left.mpr> <right.mpr> Compare structural MPR snapshots
diff <left.mpr> <right.mpr> Print typed semantic changes
refs <file.mpr> <name> Find references to a Mendix artifact
Expand Down
36 changes: 28 additions & 8 deletions docs/de-DE/compiler.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,23 @@ sowie Save/Cancel-Aktionen. Create übergibt die GUID des neuen Objekts über
registriert, die aus den erlaubten Modulrollen der Seite abgeleitet werden.
Damit bleibt die Runtime-Autorisierung erhalten und wird nicht umgangen.

Layouts, Platzhalter, Scrollbereiche, Sidebars, Header, Navigationsmenüs,
Snippets, DataViews, ListViews, Formularelemente, Bilder, Combo Boxes, Gallery,
Data Grid 2 und schemagestützte Pluggable Widgets werden über die nativen
React-Client-Verträge kompiliert. Seiten-, Menü- und Widget-Aktionen decken
Navigation, Microflows, Nanoflows, Objektlebenszyklus, Links und Sign-out ab.
Nanoflow-Programme unterstützen außerdem Entscheidungen, Fehlerbehandlung,
JavaScript-Aktionen und serverseitige Microflow-/Commit-
Operationen, sofern der Modellvertrag sicher abgebildet werden kann.

Der React-Pfad kompiliert außerdem die offizielle Gallery mit XPath- und
Microflow-Listenquellen, Template-Inhalten, Auswahl und formatierten dynamischen
Attributen. Nanoflow-Quellen und -Aktionen verwenden die Mendix-Verträge
`NanoflowObjectListProperty`, `NanoflowObjectProperty` und `ActionProperty`.
MXRB erzeugt echte Clientprogramme `{ name, instructions }` für den auditierten
linearen Teilumfang: Return, Variablenerzeugung, Objekterzeugung und verschachtelte
Nanoflow-Aufrufe. Fehlende Flows, nicht sicher abbildbare Parameter, Verzweigungen,
JavaScript-Aktionen, Microflow-Aufrufe und andere unübersetzte Clientinstruktionen
MXRB erzeugt echte Clientprogramme `{ name, instructions }` für auditierten
Graph-Kontrollfluss, Returns, Variablen, Objekte, Nanoflow-/Microflow-Aufrufe,
JavaScript-, Seiten-, Nachrichten-, Validierungs- und Commit-Aktionen. Fehlende
Flows, unsichere Parameterabbildungen und nicht übersetzte Clientinstruktionen
schlagen geschlossen fehl und bleiben Befunde im Support-Manifest.

In lokalen Runtime-Sitzungen im Developer-Modus versioniert mxrb außerdem
Expand Down Expand Up @@ -81,6 +90,18 @@ end
Bestehende Projekte können exportiert, in der DSL geändert und neu erzeugt
werden. Nicht unterstützte Widgets bleiben im Support-Manifest sichtbar.

Vor einem nativen Build kann das gesamte MPR mit dem für seine Mendix-Version
gewählten Renderer geprüft werden:

```bash
mxrb preflight App.mpr
mxrb preflight App.mpr --json
```

Der Nur-Lese-Befehl liefert bei nicht unterstützten Versionen oder
Client-Features einen Fehlerstatus. Mendix 11 wird über den React-Compiler
geprüft; 6/7/9 verwenden dieselbe Legacy-Dojo-Prüfung wie `WebBundleBuilder`.

Der Dojo-Pfad kompiliert Data Grid 1 mit Datenbank-, XPath- und
Microflow-Quellen sowie auditierten Such-, Sortier-, Paging-, Auswahl- und
Button-Verträgen. Alle anderen sichtbaren Widgets werden pro Seite in
Expand Down Expand Up @@ -130,10 +151,9 @@ Seitenbundles und fuhr sauber herunter. Danach bestand der Funktionstest
aus Version 11 einzusetzen;
- Data Grid 1 ist abgedeckt, andere Dojo-Widgets bleiben explizite
Manifestbefunde;
- Data Grid 2 ist für XPath-Datenquellen und Attributspalten abgedeckt. Gallery
deckt XPath/Microflow und den auditierten Nanoflow-Instruktionsumfang ab.
React-Formulare decken parameter- oder unterstützte Nanoflow-DataViews,
TextBox und Save/Cancel ab; andere Clientinstruktionen verwenden den Fallback;
- der Mendix-11-React-Pfad besitzt saubere Preflights und vollständige
Rspack-Builds für LearnNow, MyFirstModule, SLATaskApp, Sudoku und VetClinic.
Diese Fixture-Matrix belegt auditierte Verträge, keine universelle Kompatibilität;
- native Web-Bundles werden erzeugt; React Native verwendet weiterhin
bestehende Versions-Template- und Projektassets;
- `mx` und `mxbuild` sind niemals Fallbacks. Nicht unterstützte Eingaben führen
Expand Down
38 changes: 29 additions & 9 deletions docs/en-US/compiler.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,23 @@ commit/rollback operations are registered with the project user roles derived
from each page's allowed module roles. Runtime authorization is preserved
instead of bypassed.

Layouts, placeholders, scroll regions, sidebars, headers, navigation menus,
snippets, DataViews, ListViews, form controls, images, combo boxes, Gallery,
Data Grid 2 and schema-backed pluggable widgets are compiled through their
native React-client contracts. Page, menu and widget actions cover navigation,
microflows, nanoflows, object lifecycle, links and sign-out. Nanoflow programs
also cover decisions, error handlers, JavaScript actions and server-side
microflow/commit operations when their model contracts can be mapped safely.

The React path also compiles the official Gallery with XPath and microflow
list sources, templated item content, selection, and formatted dynamic
attributes. Client-side nanoflow sources and actions use the Mendix
`NanoflowObjectListProperty`, `NanoflowObjectProperty`, and `ActionProperty`
contracts. MXRB emits real `{ name, instructions }` client programs for the
audited linear instruction subset (return, variable creation, object creation,
and nested nanoflow calls). Missing flows, parameters that cannot be mapped,
branches, JavaScript actions, microflow calls, and other untranslated client
instructions fail closed and remain support-manifest findings.
contracts. MXRB emits real `{ name, instructions }` client programs for audited
graph control flow, returns, variables, objects, nested nanoflow/microflow calls,
JavaScript actions, page/message/validation actions and commits. Missing flows,
unsafe parameter mappings and untranslated client instructions fail closed and
remain support-manifest findings.

For local developer Runtime sessions, mxrb also versions dynamic page imports,
content-hashes the patched React Client chunk, and gives Rspack's self-import
Expand Down Expand Up @@ -78,6 +86,19 @@ end
Existing projects can be exported, changed in the DSL, and regenerated.
Unsupported widgets remain visible in the support manifest.

Before a native build, audit the complete MPR through the renderer selected for
its Mendix version:

```bash
mxrb preflight App.mpr
mxrb preflight App.mpr --json
```

The command is read-only and exits non-zero for unsupported versions or client
features. Mendix 11 is audited through the React compiler, while 6/7/9 use the
same legacy Dojo audit as `WebBundleBuilder`; this prevents a report from
claiming compatibility through a renderer the build will not use.

The Dojo path compiles Data Grid 1 database, XPath, and microflow sources plus
audited search, sorting, paging, selection, and buttons. Every other visible
widget is recorded per page in `web/mxrb-legacy-pages.json` and is not claimed
Expand Down Expand Up @@ -125,10 +146,9 @@ cleanly. The `ACT Create Animal` functional test then passed.
instead of substituting the Java-21 launcher from 11;
- Data Grid 1 is covered while other Dojo widgets remain explicit manifest
findings;
- Data Grid 2 is covered for XPath datasource and attribute columns. Gallery
covers XPath/microflow and the audited nanoflow instruction subset. React
edit forms cover parameter- or supported-nanoflow-backed DataView/TextBox and
Save/Cancel; other widgets and client instructions use the manifest fallback;
- the Mendix 11 React path is covered by clean preflights and complete Rspack
builds of LearnNow, MyFirstModule, SLATaskApp, Sudoku and VetClinic. This
fixture matrix is evidence for the audited contracts, not a universal claim;
- native web bundles are generated; React Native still relies on existing
version-template and project assets;
- `mx` and `mxbuild` are never fallbacks. Unsupported input produces a clear
Expand Down
37 changes: 29 additions & 8 deletions docs/pt-BR/compiler.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,23 @@ editáveis e ações Save/Cancel. Create passa o GUID do novo objeto por
projeto derivados dos module roles permitidos pela página. A autorização da
Runtime é preservada, sem contorno de segurança.

Layouts, placeholders, regiões de scroll, sidebars, headers, menus de navegação,
snippets, DataViews, ListViews, controles de formulário, imagens, combo boxes,
Gallery, Data Grid 2 e widgets pluggable respaldados por schema são compilados
pelos contratos nativos do cliente React. Ações de páginas, menus e widgets
cobrem navegação, microflows, nanoflows, ciclo de vida de objetos, links e
sign-out. Programas nanoflow também cobrem decisões, error handlers,
ações JavaScript e operações server-side de microflow/commit quando o contrato
do modelo pode ser mapeado com segurança.

O caminho React também compila a Gallery oficial com listas XPath e microflow,
conteúdo de item por template, seleção e atributos dinâmicos formatados. Fontes
e ações nanoflow usam os contratos `NanoflowObjectListProperty`,
`NanoflowObjectProperty` e `ActionProperty` do cliente Mendix. O MXRB emite
programas cliente reais `{ name, instructions }` para o subconjunto linear
auditado: retorno, criação de variável, criação de objeto e chamadas de
nanoflow. Fluxos ausentes, parâmetros sem mapeamento seguro, decisões, ações
JavaScript, chamadas de microflow e demais instruções não traduzidas falham
programas cliente reais `{ name, instructions }` para controle de fluxo em
grafo, retornos, variáveis, objetos, chamadas de nanoflow/microflow, ações
JavaScript, páginas, mensagens, validações e commits. Fluxos ausentes,
mapeamentos de parâmetro inseguros e instruções cliente não traduzidas falham
fechado e permanecem achados no manifesto de suporte.

Nas sessões locais da Runtime em modo developer, o mxrb também versiona imports
Expand Down Expand Up @@ -81,6 +90,19 @@ end
Projetos existentes podem ser exportados, alterados na DSL e regenerados. Os
widgets fora do subconjunto continuam no manifesto, sem descarte silencioso.

Antes do build nativo, audite o MPR completo pelo renderer selecionado para sua
versão Mendix:

```bash
mxrb preflight App.mpr
mxrb preflight App.mpr --json
```

O comando é somente leitura e retorna código diferente de zero para versões ou
recursos cliente não suportados. Mendix 11 é auditado pelo compilador React;
6/7/9 usam a mesma auditoria Dojo legada do `WebBundleBuilder`. Assim o relatório
não declara compatibilidade por um renderer que o build real não utilizará.

No Dojo, o Data Grid 1 cobre fontes database, XPath e microflow, pesquisa,
ordenação, paginação, seleção e botões auditados. Outros widgets visuais são
registrados por página em `web/mxrb-legacy-pages.json`; não são declarados como
Expand Down Expand Up @@ -129,10 +151,9 @@ encerrou limpo. O teste funcional `ACT Create Animal` passou em seguida.
substituir pelo launcher Java 21 da versão 11;
- Data Grid 1 está coberto, mas os demais widgets Dojo continuam explicitamente
pendentes no manifesto legado;
- Data Grid 2 está coberto para datasource XPath e colunas de atributo. Gallery
cobre XPath/microflow e o subconjunto auditado de instruções nanoflow. Os
formulários React cobrem DataView/TextBox por parâmetro ou nanoflow suportado
e Save/Cancel; outros widgets e instruções cliente usam o fallback do manifesto;
- o caminho React do Mendix 11 tem preflight limpo e builds Rspack completos de
LearnNow, MyFirstModule, SLATaskApp, Sudoku e VetClinic. Essa matriz comprova
os contratos auditados, não compatibilidade universal;
- bundles web nativos são gerados; o pipeline React Native ainda depende dos
assets de template/projeto existentes;
- `mx` e `mxbuild` não são fallbacks. Um recurso sem compilador produz erro ou
Expand Down
6 changes: 6 additions & 0 deletions lib/mxrb.rb
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,12 @@
require_relative "mxrb/compiler/data_grid_bundle_compiler"
require_relative "mxrb/compiler/gallery_bundle_compiler"
require_relative "mxrb/compiler/image_bundle_compiler"
require_relative "mxrb/compiler/generic_widget_bundle_compiler"
require_relative "mxrb/compiler/combo_box_bundle_compiler"
require_relative "mxrb/compiler/legacy_data_grid_compiler"
require_relative "mxrb/compiler/legacy_page_builder"
require_relative "mxrb/compiler/page_bundle_compiler"
require_relative "mxrb/compiler/compatibility_analyzer"
require_relative "mxrb/compiler/page_bundle_builder"
require_relative "mxrb/compiler/web_bundle_builder"
require_relative "mxrb/compiler/portable_packager"
Expand Down Expand Up @@ -177,4 +179,8 @@ def self.functional_definition(path)
def self.runtime_plan(path, **options)
Runtime::Toolchain.new(path, **options).plan
end

def self.compatibility(path)
Compiler::CompatibilityAnalyzer.new(path).analyze
end
end
34 changes: 29 additions & 5 deletions lib/mxrb/compiler/combo_box_bundle_compiler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
module Mxrb
module Compiler
# Compiles association- and database-backed instances of the official React Combo box widget.
class ComboBoxBundleCompiler # rubocop:disable Metrics/ClassLength
# rubocop:disable Metrics
class ComboBoxBundleCompiler
include ModelValues

WIDGET_ID = 'com.mendix.widget.web.combobox.Combobox'
Expand Down Expand Up @@ -59,7 +60,7 @@ def database_source_supported?

def association_source_supported?
primitive('source') == 'context' && primitive('optionsSourceType') == 'association' &&
association_step && association_caption_attribute
association_step && (association_caption_attribute || association_caption_expression)
end

def source_properties
Expand Down Expand Up @@ -88,11 +89,30 @@ def database_association_properties
end

def association_properties
{
optionsSourceAssociationCaptionAttribute: raw(list_attribute_property(association_caption_attribute)),
properties = {
attributeAssociation: raw(association_property(association_step)),
optionsSourceAssociationDataSource: raw(list_property)
}
if association_caption_attribute
properties[:optionsSourceAssociationCaptionAttribute] = raw(
list_attribute_property(association_caption_attribute)
)
else
properties[:optionsSourceAssociationCaptionExpression] = raw(list_expression_property)
end
properties
end

def list_expression_property
source = value('optionsSourceAssociationCaptionExpression')&.fetch('Expression', '').to_s
path = source[%r{\A\$currentObject/(.+)\z}, 1]
"ListExpressionProperty(#{javascript(
expression: {
expr: { type: 'variable', variable: 'currentObject', path: },
args: { currentObject: { widget: widget_key, source: 'object' } }
},
dataSourceId: data_source_id
)})"
end

def association_property(step)
Expand Down Expand Up @@ -175,6 +195,7 @@ def target_attribute = target_value&.fetch('AttributeRef', nil)
def database_caption_attribute = value('optionsSourceDatabaseCaptionAttribute')&.fetch('AttributeRef', nil)
def database_value_attribute = value('optionsSourceDatabaseValueAttribute')&.fetch('AttributeRef', nil)
def association_caption_attribute = value('optionsSourceAssociationCaptionAttribute')&.fetch('AttributeRef', nil)
def association_caption_expression = value('optionsSourceAssociationCaptionExpression')&.fetch('Expression', nil)

def association_step
entity_steps(value('attributeAssociation')).first
Expand Down Expand Up @@ -256,6 +277,8 @@ def widget_type
end

def document_index
return @source.document_index if @source.is_a?(SourceModel)

{}.tap { |index| @source.documents.each { index_document(_1, index) } }
end

Expand All @@ -268,6 +291,7 @@ def index_document(value, index)
when Array then value.each { index_document(_1, index) }
end
end
end # rubocop:enable Metrics/ClassLength
end
# rubocop:enable Metrics
end
end
Loading