Como dominar a suíte de comandos do Modeler CLI, decifrar os erros de compilação, aplicar convenções de nomenclatura universais e gerenciar arquivos de tradução de locale.
No ecossistema de desenvolvimento tradicional da Salesforce, desenvolvedores estão habituados com mensagens de erro claras e amigáveis emitidas pelo compilador do Apex ou pelo linter do Lightning Web Components (LWC). Mensagens como “Variable does not exist” ou “Missing return statement” indicam exatamente a linha e a coluna onde o problema reside.
No entanto, ao ingressar no desenvolvimento mobile offline para o Salesforce Consumer Goods Cloud (CGC) Modeler, o paradigma muda drasticamente.
O Modeler opera através de um compilador de múltiplos estágios acionado via linha de comando (sf mdl build). Esse compilador valida centenas de contratos XML contra schemas XSD rígidos, inspeciona assinaturas de cabeçalho JSDoc em scripts JavaScript (.bl.js), cruza dependências entre processos e telas e checa a integridade das tabelas do banco de dados relacional local (appl/data/app.db3).
Quando uma regra sintática ou convenção estrutural é violada, o compilador frequentemente emite códigos numéricos herméticos (como 03112630, 00000001 ou 00000016) ou, pior ainda, o build passa com sucesso, mas a aplicação quebra em tempo de execução com telas brancas ou avisos de componentes ausentes.
Neste guia técnico, vamos mapear a suíte completa de comandos do Modeler CLI, estabelecer as convenções de nomenclatura inegociáveis, construir uma matriz de troubleshooting para diagnóstico rápido de falhas e estruturar o ciclo de internacionalização (i18n) em arquivos de locale.
1. A Suíte de Comandos do Modeler CLI e o Fluxo de Trabalho
A compilação e teste de artefatos no Modeler é gerenciada pelo plugin do Salesforce CLI (@ind-rcg/modeler-sfdx-cli-plugin). Os comandos possuem duas variações de sintaxe: o formato longo (sf modeler workspace <comando>) e o atalho canônico curto (sf mdl <comando>).
Comandos Essenciais do Terminal:
| Comando Curto | Comando Completo | Propósito Operacional |
|---|---|---|
sf mdl build | sf modeler workspace build | Compila todos os contratos XML, arquivos .bl.js e gera os pacotes executáveis de runtime. |
sf mdl clean | sf modeler workspace cleanup | Remove todos os artefatos compilados na pasta de saída, prevenindo builds corrompidos. |
sf mdl validate | sf modeler workspace validate | Executa a validação estrita dos contratos contra os schemas XSD sem gerar artefatos físicos. |
sf mdl simulate | sf modeler workspace server start | Inicializa o servidor local de desenvolvimento e abre o simulador da aplicação móvel no navegador. |
sf mdl package | sf modeler workspace package | Empacota a aplicação compilada em um arquivo compactado pronto para implantação no ambiente Salesforce. |
sf mdl add | sf modeler workspace add | Executa o assistente de scaffolding interativo para criar novos módulos, BOs, LOs, UIs e processos. |
O Problema do “Stale Build” (Compilação Viciada)
Um dos problemas mais traiçoeiros enfrentados por desenvolvedores é alterar um arquivo de interface ou regra de negócio, rodar o sf mdl build e perceber que o simulador continua executando o código antigo. Isso acontece quando artefatos gerados anteriormente na pasta temporária não são sobrescritos.
A regra de ouro ao diagnosticar comportamentos inesperados é executar a limpeza completa antes da compilação:
# Limpeza forçada e recompilação limpa
sf mdl clean && sf mdl build
2. Naming Conventions: As Regras Inegociáveis de Nomenclatura
O compilador do Modeler baseia seu motor de descoberta e vinculação de dependências em convenções de nomenclatura estritas. Desviar desses padrões quebra o mapeamento entre camadas.
A Regra de Ouro: Chaves Primárias São Sempre pKey
No Modeler, qualquer campo que represente a chave primária de um registro **DEVE chamar-se rigorosamente pKey**. Nunca utilize nomes como id, Id, key ou pk.
Essa convenção aplica-se simultaneamente a três camadas:
- Na propriedade com
id="true"do BusinessObject:<SimpleProperty id="true" name="pKey" type="DomPKey" storable="false" dataSourceProperty="pKey" /> - Na propriedade com
id="true"do ListItem:<SimpleProperty id="true" name="pKey" type="DomPKey" dataSourceProperty="pKey" /> - No mapeamento do DataSource correspondente:
<Attribute name="pKey" table="Visit" column="Id" />
Padrões Canônicos de Nome de Arquivo e Prefixos:
| Tipo de Artefato | Convenção de Arquivo | Exemplo Real |
|---|---|---|
| DataSource de BO | DsBo{Nome}_sf.datasource.xml | DsBoVisit_sf.datasource.xml |
| DataSource de LO | DsLo{Nome}_sf.datasource.xml | DsLoOpenOrders_sf.datasource.xml |
| DataSource de Lookup | DsLu{Nome}_sf.datasource.xml | DsLuCustomer_sf.datasource.xml |
| BusinessObject | Bo{Nome}.businessobject.xml | BoVisit.businessobject.xml |
| ListObject | Lo{Nome}.listobject.xml | LoOpenOrders.listobject.xml |
| ListItem | Li{Nome}.listitem.xml | LiOpenOrders.listitem.xml |
| ProcessFlow | {Modulo}_{Tela}Process.processflow.xml | Call_DetailProcess.processflow.xml |
| UserInterface | {Modulo}_{Tela}UI.userinterface.xml | Call_DetailUI.userinterface.xml |
| Business Logic | {NomeDoObjeto}.{Metodo}.bl.js | BoVisit.AfterLoadAsync.bl.js |
Namespace com Dois Pontos Duplos (::) em Process e UI
Enquanto BusinessObjects e ListObjects utilizam identificadores simples (ex: name="BoVisit"), os contratos de **ProcessFlow e UserInterface exigem obrigatoriamente o formato de namespace corporativo Namespace::Nome**:
<!-- No ProcessFlow -->
<Process name="Call::VisitDetailProcess" defaultAction="ShowView" schemaVersion="0.0.0.5">
<!-- Na UserInterface -->
<UIDescription name="Call::VisitDetailUI" schemaVersion="0.0.0.5">
A Pegadinha da Estrutura de Pastas: ListObjects Ficam em BO/
Uma confusão comum é tentar criar uma pasta chamada LO/ dentro do módulo. No Modeler, **tanto BusinessObjects quanto ListObjects, ListItems e LookupObjects residem dentro da pasta src/{Modulo}/BO/**:
src/Call/
├── BO/
│ ├── BoVisit/
│ │ ├── BoVisit.businessobject.xml
│ │ └── Mv2/
│ └── LoOpenOrders/
│ ├── LoOpenOrders.listobject.xml
│ ├── LiOpenOrders.listitem.xml
│ └── Mv2/
├── DS/
│ ├── DsBoVisit_sf.datasource.xml
│ └── DsLoOpenOrders_sf.datasource.xml
└── PR/
└── Call_VisitDetail/
├── Call_VisitDetailProcess.processflow.xml
└── Call_VisitDetailUI.userinterface.xml
3. Matriz Completa de Troubleshooting: Erros de Build
Quando o comando sf mdl build falha, a mensagem do terminal indica a categoria da violação estrutural. Consulte a matriz abaixo para identificar e corrigir a falha instantaneamente:
1. Falhas em Business Logic e JSDoc (.bl.js)
| Código / Mensagem de Erro | Causa Raiz | Solução Técnica |
|---|---|---|
03112630 (Start tag missing) | O marcador de início de código customizado está ausente, incorreto ou tem indentação diferente de 4 espaços. | Verifique se as 3 linhas do marcador Add your customizing javaScript code below. estão idênticas ao template canônico com 4 espaços de recuo. |
03112631 (End tag missing) | O marcador final de código customizado foi alterado ou apagado. | Restaure o bloco de 3 linhas Add your customizing javaScript code above.. |
Cannot read properties of undefined (reading 'name') | Bloco JSDoc ausente ou tags obrigatórias faltando antes da função. | Adicione o cabeçalho JSDoc com @function, @this, @kind, @async e @namespace CUSTOM. |
method not found | O nome declarado na tag @function não coincide com o nome físico do arquivo ou com a tag <Method> no XML. | Sincronize rigorosamente o nome do método no XML, no arquivo .bl.js e na tag @function. |
2. Falhas Estruturais na UserInterface (.userinterface.xml)
| Mensagem de Erro | Causa Raiz | Solução Técnica |
|---|---|---|
Element 'DataGrid': This element is not expected | O controle DataGrid foi declarado isolado dentro de uma área comum. | Envolva o DataGrid obrigatoriamente dentro de um container <FastDataEntryGrid> acompanhado de uma <DataInputArea>. |
Element 'Label': This element is not expected | Tentativa de usar uma tag <Label> solta como elemento de tela. | Tags de texto não são controles de primeiro nível. Declare o texto via <Resource target="Label" type="Label" ... /> dentro do bloco <Bindings>. |
Element 'ImageButton': This element is not expected (Erro 00000001) | O botão com imagem foi inserido dentro de um GroupElement ou SingleElementArea. | Mova o ImageButton para ser filho direto de uma <Area areaPattern="GroupedElementsArea"> ou use ButtonGridArea. |
Missing required event or binding (Erro 00000016 / 00000018) | O CardContainer foi declarado sem os bindings de controle obrigatórios. | Adicione simultaneamente o binding target="IsReadyToLoad" e o evento <LoadContainerData event="..." />. |
3. Falhas no ProcessFlow (.processflow.xml)
| Mensagem de Erro | Causa Raiz | Solução Técnica |
|---|---|---|
Element 'TransitionTo': This element is not expected | Uma tag <TransitionTo> foi colocada dentro de uma ação presente em <EntryActions>. | Remova a tag. Ações de entrada executam de forma puramente linear e transicionam automaticamente para o defaultAction. |
Element 'Return': This element is not expected | A tag <Return> foi utilizada em uma ação que não gera retorno ou foi posicionada fora de ordem. | Verifique se o actionType suporta retorno e respeite a ordem interna dos nós da ação. |
Attribute 'object' is not allowed | Uso de sintaxe legada do Modeler em ações de carga. | Substitua o atributo object="BoVisit" por type="BoVisit". |
4. Falhas Silenciosas em Runtime (Quando o Build Passa, Mas o App Quebra)
Alguns dos bugs mais complexos não são capturados durante a compilação, manifestando-se apenas quando o simulador é inicializado:
1. SQLITE_ERROR: no such column: X
- Causa: O DataSource mapeou um campo do Salesforce que não existe no banco de dados local do dispositivo.
- Diagnóstico no Terminal: Inspecione as colunas físicas da tabela no SQLite:
sqlite3 appl/data/app.db3 "PRAGMA table_info(Visit);"
- Solução: Se o campo não aparecer na listagem, adicione-o ao Field Set de mobilidade na organização Salesforce (
Mobility_relevant) e realize uma nova sincronização móvel.
2. Database is not defined
- Causa: Em um DataSource scripteado (
external="true"), o desenvolvedor tentou executar consultas SQL diretamente dentro da tag<Load>(ex:Database.loadRecordsAsync(...)oudb.exec(...)). - Solução: O bloco
<Load>é um construtor de queries, não um executor. Ele deve obrigatoriamente RETORNAR a string SQL formatada através dereturn Utils.replaceMacrosParam(sql, params);.
3. “We couldn’t render this component” em Cockpit Cards
- Causa: A variável de contexto
DisplayedSubcomponentNameestá retornandonullou um valor que não coincide exatamente com o atributonamedo<CockpitList>. - Solução: Sincronize a string do
<CockpitList name="OpenOrders">com o literal passado na ação do ProcessFlow:<Input name="type" value="OpenOrders" type="Literal" />.
5. Arquitetura de Internacionalização (i18n): Rótulos de Locale
O Modeler suporta múltiplos idiomas de forma nativa através de arquivos XML organizados na pasta src/Locale/{lang}.locale.xml (como en.locale.xml e pt.locale.xml).
A estrutura do arquivo segue uma hierarquia estrita:
<?xml version="1.0" encoding="UTF-8"?>
<Locale language="pt" languageCode="pt">
<Translations>
<!-- SEÇÃO 1: READ-ONLY (Rótulos do Framework: Yes, No, Cancel, Save, OK) -->
<Framework>
<Label id="Yes" text="Sim" translationStatus="7" />
<Label id="No" text="Não" translationStatus="7" />
<Label id="Save" text="Salvar" translationStatus="7" />
</Framework>
<!-- SEÇÃO 2: READ-ONLY (Termos Globais Core: Customer, Product, Visit) -->
<Global>
<Label id="Customer" text="Cliente" translationStatus="7" />
</Global>
<!-- SEÇÃO 3: EDITÁVEL (Rótulos Customizados das Telas) -->
<UserInterfaceContracts>
<!-- Um container <UserInterface> para cada arquivo UI existente -->
<UserInterface id="Call_DetailUI">
<Label id="CustomerNotesLabelId" text="Observações Gerais do Cliente" translationStatus="7" />
<Label id="BtnConfirmRescheduleId" text="Confirmar Reagendamento" translationStatus="7" />
</UserInterface>
</UserInterfaceContracts>
</Translations>
</Locale>
Regras Críticas de Internacionalização:
- As Seções
FrameworkeGlobalSão Intocáveis: Nunca adicione ou edite rótulos nessas seções. Elas pertencem ao núcleo da Salesforce e são sobrescritas integralmente durante upgrades do pacote. - Rótulos Customizados Pertencem a
<UserInterfaceContracts>: Toda tradução de telas do seu projeto deve ser inserida dentro de um nó<UserInterface id="...">, cujo atributoiddeve coincidir exatamente com o nome da tela (ex:Call_DetailUI). - Identificadores de Rótulo Idênticos: O atributo
iddo<Label>deve ser exatamente o mesmo em todos os arquivos de idioma (en.locale.xml,pt.locale.xml,es.locale.xml). Apenas o atributotextvaria. - Ciclo de Vida do
translationStatus:
1(Novo): Rótulo criado, aguardando tradução formal.6(Revisão Necessária): O texto original em inglês foi modificado e as demais línguas precisam ser ajustadas.7(Traduzido e Verificado): Tradução aprovada para produção.

Conclusão
Compreender o comportamento do compilador do Modeler CLI, respeitar as convenções estritas de nomenclatura e aplicar padrões sistemáticos de troubleshooting são os divisores de águas entre projetos móveis caóticos e implementações de alta confiabilidade no Salesforce Consumer Goods Cloud. Ao antecipar falhas de XSD, validar esquemas de banco local preventivamente e manter a disciplina na gestão de rótulos internacionalizados, arquitetos e desenvolvedores garantem pipelines de entrega contínua estáveis e livres de regressões para a operação em campo.
Sua equipe já passou horas tentando decifrar um erro numérico no sf mdl build? Compartilhe suas experiências e dúvidas nos comentários abaixo ou marque seus colegas de engenharia na discussão no LinkedIn.
