@loj-lang/rdsl-compiler 0.6.1 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/codegen.js +229 -20
- package/dist/codegen.js.map +1 -1
- package/dist/normalize.js +116 -10
- package/dist/normalize.js.map +1 -1
- package/package.json +2 -2
package/dist/codegen.js
CHANGED
|
@@ -565,7 +565,7 @@ function createCodegenSignature(value) {
|
|
|
565
565
|
function generateModelTypes(model) {
|
|
566
566
|
const generatedFields = generatedModelFields(model);
|
|
567
567
|
const lines = [];
|
|
568
|
-
lines.push(`// Generated by
|
|
568
|
+
lines.push(`// Generated by Loj RDSL compiler v0.6.2`);
|
|
569
569
|
lines.push(`// @source-node ${model.id}`);
|
|
570
570
|
lines.push(``);
|
|
571
571
|
// Generate TypeScript interface
|
|
@@ -640,7 +640,7 @@ function generateReadModelTypes(readModel) {
|
|
|
640
640
|
const inputTypeName = readModelInputTypeName(readModel);
|
|
641
641
|
const metaConstName = readModelMetaConstName(readModel);
|
|
642
642
|
const lines = [];
|
|
643
|
-
lines.push(`// Generated by
|
|
643
|
+
lines.push(`// Generated by Loj RDSL compiler v0.6.2`);
|
|
644
644
|
lines.push(`// @source-node ${readModel.id}`);
|
|
645
645
|
lines.push(``);
|
|
646
646
|
lines.push(`export interface ${inputTypeName} {`);
|
|
@@ -795,7 +795,7 @@ function generateListView(ir, resource, view, model) {
|
|
|
795
795
|
rowActions.push(`{ label: ${messageLikeToRuntimeValueSource(listWorkflowLabel)}, href: (row) => ${workflowViewHrefExpression(resource.name, 'row', { returnToExpr: 'getCurrentAppHref()' })} }`);
|
|
796
796
|
}
|
|
797
797
|
const hasRowActions = rowActions.length > 0 || Boolean(deleteAction);
|
|
798
|
-
lines.push(`// Generated by
|
|
798
|
+
lines.push(`// Generated by Loj RDSL compiler v0.6.2`);
|
|
799
799
|
lines.push(`// @source-node ${view.id}`);
|
|
800
800
|
lines.push(``);
|
|
801
801
|
lines.push(`import React, { useCallback } from 'react';`);
|
|
@@ -1272,6 +1272,14 @@ function generateEditView(ir, resource, view, model) {
|
|
|
1272
1272
|
const newItemLabelMessage = view.messages?.newItemLabel ?? 'New';
|
|
1273
1273
|
const workflowPreconditionMessage = resource.workflowMessages?.preconditionBlocked ?? 'Action blocked by workflow preconditions';
|
|
1274
1274
|
const workflowInvalidTransitionMessage = resource.workflowMessages?.invalidTransition ?? 'Invalid workflow transition request';
|
|
1275
|
+
const fieldLabelMessages = [
|
|
1276
|
+
...collectFormFieldDecoratorMessages(view.fields, 'label'),
|
|
1277
|
+
...view.includes.flatMap((include) => collectFormFieldDecoratorMessages(include.fields, 'label')),
|
|
1278
|
+
];
|
|
1279
|
+
const fieldPlaceholderMessages = [
|
|
1280
|
+
...collectFormFieldDecoratorMessages(view.fields, 'placeholder'),
|
|
1281
|
+
...view.includes.flatMap((include) => collectFormFieldDecoratorMessages(include.fields, 'placeholder')),
|
|
1282
|
+
];
|
|
1275
1283
|
const usesMessageResolver = someMessageLikesNeedRuntimeResolver([
|
|
1276
1284
|
saveLabelMessage,
|
|
1277
1285
|
saveAndContinueLabelMessage,
|
|
@@ -1286,6 +1294,8 @@ function generateEditView(ir, resource, view, model) {
|
|
|
1286
1294
|
versionConflictFieldNamesLiteral !== '[]' ? reloadLatestMessage : undefined,
|
|
1287
1295
|
hasWorkflow ? workflowPreconditionMessage : undefined,
|
|
1288
1296
|
hasWorkflow ? workflowInvalidTransitionMessage : undefined,
|
|
1297
|
+
...fieldLabelMessages,
|
|
1298
|
+
...fieldPlaceholderMessages,
|
|
1289
1299
|
]);
|
|
1290
1300
|
const lookupOptionsConstName = componentResourceOptionsConstName(componentName);
|
|
1291
1301
|
const formTitleElementConstName = componentTitleElementConstName(componentName);
|
|
@@ -1344,7 +1354,7 @@ function generateEditView(ir, resource, view, model) {
|
|
|
1344
1354
|
for (const compilation of includeLinkedDerivationCompilations.values()) {
|
|
1345
1355
|
compilation?.helperImports.forEach((name) => derivationRuntimeImports.add(name));
|
|
1346
1356
|
}
|
|
1347
|
-
lines.push(`// Generated by
|
|
1357
|
+
lines.push(`// Generated by Loj RDSL compiler v0.6.2`);
|
|
1348
1358
|
lines.push(`// @source-node ${view.id}`);
|
|
1349
1359
|
lines.push(``);
|
|
1350
1360
|
lines.push(`import React, { useCallback } from 'react';`);
|
|
@@ -1536,9 +1546,10 @@ function generateEditView(ir, resource, view, model) {
|
|
|
1536
1546
|
for (const binding of includeBindings) {
|
|
1537
1547
|
const includeKey = camelCase(binding.fieldName);
|
|
1538
1548
|
const existingItemsName = `${includeKey}ExistingItems`;
|
|
1549
|
+
const includeDefaultItem = includeFieldDefaultObjectLiteral(binding.targetModel, binding.fields);
|
|
1539
1550
|
lines.push(` nextFormData.${binding.fieldName} = formEdits.${binding.fieldName} ?? (${existingItemsName}.length > 0`);
|
|
1540
1551
|
lines.push(` ? ${existingItemsName}`);
|
|
1541
|
-
lines.push(` : Array.from({ length: ${binding.minItems} }, () => ({})));`);
|
|
1552
|
+
lines.push(` : Array.from({ length: ${binding.minItems} }, () => (${includeDefaultItem})));`);
|
|
1542
1553
|
}
|
|
1543
1554
|
lines.push(` return nextFormData;`);
|
|
1544
1555
|
lines.push(` }, [record, formEdits${includeBindings.map((binding) => `, ${camelCase(binding.fieldName)}ExistingItems`).join('')}]);`);
|
|
@@ -1705,6 +1716,7 @@ function generateEditView(ir, resource, view, model) {
|
|
|
1705
1716
|
const handleAdd = `handleAdd${capitalize(includeKey)}`;
|
|
1706
1717
|
const handleRemove = `handleRemove${capitalize(includeKey)}`;
|
|
1707
1718
|
const handleChange = `handle${capitalize(includeKey)}FieldChange`;
|
|
1719
|
+
const includeDefaultItem = includeFieldDefaultObjectLiteral(binding.targetModel, binding.fields);
|
|
1708
1720
|
const removeGuard = binding.mode === 'append-only'
|
|
1709
1721
|
? `currentItems.length <= ${binding.minItems} || Boolean(currentItems[index]?.id)`
|
|
1710
1722
|
: `currentItems.length <= ${binding.minItems}`;
|
|
@@ -1712,7 +1724,7 @@ function generateEditView(ir, resource, view, model) {
|
|
|
1712
1724
|
lines.push(` const ${handleAdd} = useCallback(() => {`);
|
|
1713
1725
|
lines.push(` setFormEdits((prev) => ({`);
|
|
1714
1726
|
lines.push(` ...prev,`);
|
|
1715
|
-
lines.push(` ${binding.fieldName}: [...(prev.${binding.fieldName} ?? formData.${binding.fieldName}), {}],`);
|
|
1727
|
+
lines.push(` ${binding.fieldName}: [...(prev.${binding.fieldName} ?? formData.${binding.fieldName}), ${includeDefaultItem}],`);
|
|
1716
1728
|
lines.push(` }));`);
|
|
1717
1729
|
lines.push(` }, [formData.${binding.fieldName}]);`);
|
|
1718
1730
|
lines.push(` const ${handleRemove} = useCallback((index: number) => {`);
|
|
@@ -1924,6 +1936,8 @@ function generateEditView(ir, resource, view, model) {
|
|
|
1924
1936
|
const disabled = field.decorators.find(d => d.name === 'disabled');
|
|
1925
1937
|
const fieldType = getFormFieldType(field, model);
|
|
1926
1938
|
const modelField = model.fields.find(f => f.name === field.field);
|
|
1939
|
+
const labelMessage = formFieldLabelMessage(field);
|
|
1940
|
+
const placeholderMessage = formFieldPlaceholderMessage(field);
|
|
1927
1941
|
let optionsAttr = '';
|
|
1928
1942
|
if (modelField?.fieldType.type === 'enum') {
|
|
1929
1943
|
optionsAttr = `options={${enumValuesExpression(modelMetaConstName(model), field.field)}} optionKeyPrefix={${enumKeyPrefixExpression(modelMetaConstName(model), field.field)}} optionLabels={${enumLabelsExpression(modelMetaConstName(model), field.field)}}`;
|
|
@@ -1937,13 +1951,15 @@ function generateEditView(ir, resource, view, model) {
|
|
|
1937
1951
|
lines.push(` {/* @source-node ${field.id} */}`);
|
|
1938
1952
|
lines.push(` {${fieldVisible} ? (`);
|
|
1939
1953
|
lines.push(` <FormField`);
|
|
1940
|
-
lines.push(` label=
|
|
1954
|
+
lines.push(` label={${messageLikeToRuntimeTextSource(labelMessage)}}`);
|
|
1941
1955
|
lines.push(` name="${field.field}"`);
|
|
1942
1956
|
lines.push(` type="${fieldType}"`);
|
|
1943
1957
|
lines.push(` value={formData.${field.field} ?? ''}`);
|
|
1944
1958
|
lines.push(` onChange={(v: unknown) => handleFieldChange('${field.field}', v)}`);
|
|
1945
1959
|
lines.push(` schema={${modelName}Schema.${field.field}}`);
|
|
1946
1960
|
lines.push(` error={fieldErrors[${JSON.stringify(field.field)}] ?? null}`);
|
|
1961
|
+
if (placeholderMessage)
|
|
1962
|
+
lines.push(` placeholder={${messageLikeToRuntimeTextSource(placeholderMessage)}}`);
|
|
1947
1963
|
if (optionsAttr)
|
|
1948
1964
|
lines.push(` ${optionsAttr}`);
|
|
1949
1965
|
lines.push(` disabled={${disabled ? 'true' : formFieldDisabledExpression(field, modelField, fieldEnabled, `linkedDerivedFieldNames.includes('${field.field}')`)}}`);
|
|
@@ -1992,6 +2008,8 @@ function generateEditView(ir, resource, view, model) {
|
|
|
1992
2008
|
}
|
|
1993
2009
|
const fieldType = getFormFieldType(field, binding.targetModel);
|
|
1994
2010
|
const modelField = binding.targetModel.fields.find((candidate) => candidate.name === field.field);
|
|
2011
|
+
const labelMessage = formFieldLabelMessage(field);
|
|
2012
|
+
const placeholderMessage = formFieldPlaceholderMessage(field);
|
|
1995
2013
|
let optionsAttr = '';
|
|
1996
2014
|
if (modelField?.fieldType.type === 'enum') {
|
|
1997
2015
|
optionsAttr = `options={${enumValuesExpression(modelMetaConstName(binding.targetModel), field.field)}} optionKeyPrefix={${enumKeyPrefixExpression(modelMetaConstName(binding.targetModel), field.field)}} optionLabels={${enumLabelsExpression(modelMetaConstName(binding.targetModel), field.field)}}`;
|
|
@@ -2005,12 +2023,14 @@ function generateEditView(ir, resource, view, model) {
|
|
|
2005
2023
|
lines.push(` {/* @source-node ${field.id} */}`);
|
|
2006
2024
|
lines.push(` {${fieldVisible} ? (`);
|
|
2007
2025
|
lines.push(` <FormField`);
|
|
2008
|
-
lines.push(` label=
|
|
2026
|
+
lines.push(` label={${messageLikeToRuntimeTextSource(labelMessage)}}`);
|
|
2009
2027
|
lines.push(` name="${field.field}"`);
|
|
2010
2028
|
lines.push(` type="${fieldType}"`);
|
|
2011
2029
|
lines.push(` value={item.${field.field} ?? ''}`);
|
|
2012
2030
|
lines.push(` onChange={(v: unknown) => ${handleChange}(index, '${field.field}', v)}`);
|
|
2013
2031
|
lines.push(` schema={${binding.targetModel.name}Schema.${field.field}}`);
|
|
2032
|
+
if (placeholderMessage)
|
|
2033
|
+
lines.push(` placeholder={${messageLikeToRuntimeTextSource(placeholderMessage)}}`);
|
|
2014
2034
|
if (optionsAttr)
|
|
2015
2035
|
lines.push(` ${optionsAttr}`);
|
|
2016
2036
|
lines.push(` disabled={${formFieldDisabledExpression(field, modelField, fieldEnabled, `${includeKey}LinkedDerivedFieldNames.includes('${field.field}')`, existingItemDisabled)}}`);
|
|
@@ -2156,6 +2176,14 @@ function generateCreateView(ir, resource, view, model) {
|
|
|
2156
2176
|
const newItemLabelMessage = view.messages?.newItemLabel ?? 'New';
|
|
2157
2177
|
const workflowPreconditionMessage = resource.workflowMessages?.preconditionBlocked ?? 'Action blocked by workflow preconditions';
|
|
2158
2178
|
const workflowInvalidTransitionMessage = resource.workflowMessages?.invalidTransition ?? 'Invalid workflow transition request';
|
|
2179
|
+
const fieldLabelMessages = [
|
|
2180
|
+
...collectFormFieldDecoratorMessages(view.fields, 'label'),
|
|
2181
|
+
...view.includes.flatMap((include) => collectFormFieldDecoratorMessages(include.fields, 'label')),
|
|
2182
|
+
];
|
|
2183
|
+
const fieldPlaceholderMessages = [
|
|
2184
|
+
...collectFormFieldDecoratorMessages(view.fields, 'placeholder'),
|
|
2185
|
+
...view.includes.flatMap((include) => collectFormFieldDecoratorMessages(include.fields, 'placeholder')),
|
|
2186
|
+
];
|
|
2159
2187
|
const usesMessageResolver = someMessageLikesNeedRuntimeResolver([
|
|
2160
2188
|
createLabelMessage,
|
|
2161
2189
|
createAndContinueLabelMessage,
|
|
@@ -2168,6 +2196,8 @@ function generateCreateView(ir, resource, view, model) {
|
|
|
2168
2196
|
versionConflictFieldNamesLiteral !== '[]' ? versionConflictMessage : undefined,
|
|
2169
2197
|
hasWorkflow ? workflowPreconditionMessage : undefined,
|
|
2170
2198
|
hasWorkflow ? workflowInvalidTransitionMessage : undefined,
|
|
2199
|
+
...fieldLabelMessages,
|
|
2200
|
+
...fieldPlaceholderMessages,
|
|
2171
2201
|
]);
|
|
2172
2202
|
const seedParamAppliersConstName = createSeedFields.length > 0 ? staticComponentConstName(componentName, 'SeedParamAppliers') : null;
|
|
2173
2203
|
const lookupOptionsConstName = componentResourceOptionsConstName(componentName);
|
|
@@ -2227,7 +2257,7 @@ function generateCreateView(ir, resource, view, model) {
|
|
|
2227
2257
|
for (const compilation of includeLinkedDerivationCompilations.values()) {
|
|
2228
2258
|
compilation?.helperImports.forEach((name) => derivationRuntimeImports.add(name));
|
|
2229
2259
|
}
|
|
2230
|
-
lines.push(`// Generated by
|
|
2260
|
+
lines.push(`// Generated by Loj RDSL compiler v0.6.2`);
|
|
2231
2261
|
lines.push(`// @source-node ${view.id}`);
|
|
2232
2262
|
lines.push(``);
|
|
2233
2263
|
lines.push(`import React, { useCallback } from 'react';`);
|
|
@@ -2391,8 +2421,16 @@ function generateCreateView(ir, resource, view, model) {
|
|
|
2391
2421
|
if (hasWorkflow) {
|
|
2392
2422
|
lines.push(` ${resource.workflow.program.field}: ${JSON.stringify(workflowInitialState(resource))} as ${modelName}['${resource.workflow.program.field}'],`);
|
|
2393
2423
|
}
|
|
2424
|
+
for (const field of view.fields) {
|
|
2425
|
+
const modelField = model.fields.find((candidate) => candidate.name === field.field);
|
|
2426
|
+
const defaultValueLiteral = modelField ? fieldDefaultValueLiteral(modelField) : undefined;
|
|
2427
|
+
if (defaultValueLiteral !== undefined) {
|
|
2428
|
+
lines.push(` ${field.field}: ${defaultValueLiteral},`);
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2394
2431
|
for (const binding of includeBindings) {
|
|
2395
|
-
|
|
2432
|
+
const includeDefaultItem = includeFieldDefaultObjectLiteral(binding.targetModel, binding.fields);
|
|
2433
|
+
lines.push(` ${binding.fieldName}: Array.from({ length: ${binding.minItems} }, () => (${includeDefaultItem})),`);
|
|
2396
2434
|
}
|
|
2397
2435
|
lines.push(` };`);
|
|
2398
2436
|
lines.push(` if (!searchParams) return initial;`);
|
|
@@ -2569,6 +2607,7 @@ function generateCreateView(ir, resource, view, model) {
|
|
|
2569
2607
|
const handleAdd = `handleAdd${capitalize(includeKey)}`;
|
|
2570
2608
|
const handleRemove = `handleRemove${capitalize(includeKey)}`;
|
|
2571
2609
|
const handleChange = `handle${capitalize(includeKey)}FieldChange`;
|
|
2610
|
+
const includeDefaultItem = includeFieldDefaultObjectLiteral(binding.targetModel, binding.fields);
|
|
2572
2611
|
const removeGuard = binding.mode === 'append-only'
|
|
2573
2612
|
? `prev.${binding.fieldName}.length <= ${binding.minItems} || Boolean(prev.${binding.fieldName}[index]?.id)`
|
|
2574
2613
|
: `prev.${binding.fieldName}.length <= ${binding.minItems}`;
|
|
@@ -2576,7 +2615,7 @@ function generateCreateView(ir, resource, view, model) {
|
|
|
2576
2615
|
lines.push(` const ${handleAdd} = useCallback(() => {`);
|
|
2577
2616
|
lines.push(` setFormData((prev) => ({`);
|
|
2578
2617
|
lines.push(` ...prev,`);
|
|
2579
|
-
lines.push(` ${binding.fieldName}: [...prev.${binding.fieldName}, {}],`);
|
|
2618
|
+
lines.push(` ${binding.fieldName}: [...prev.${binding.fieldName}, ${includeDefaultItem}],`);
|
|
2580
2619
|
lines.push(` }));`);
|
|
2581
2620
|
lines.push(` }, []);`);
|
|
2582
2621
|
lines.push(` const ${handleRemove} = useCallback((index: number) => {`);
|
|
@@ -2754,6 +2793,8 @@ function generateCreateView(ir, resource, view, model) {
|
|
|
2754
2793
|
else {
|
|
2755
2794
|
const fieldType = getFormFieldType(field, model);
|
|
2756
2795
|
const modelField = model.fields.find(f => f.name === field.field);
|
|
2796
|
+
const labelMessage = formFieldLabelMessage(field);
|
|
2797
|
+
const placeholderMessage = formFieldPlaceholderMessage(field);
|
|
2757
2798
|
let optionsAttr = '';
|
|
2758
2799
|
if (modelField?.fieldType.type === 'enum') {
|
|
2759
2800
|
optionsAttr = `options={${enumValuesExpression(modelMetaConstName(model), field.field)}} optionKeyPrefix={${enumKeyPrefixExpression(modelMetaConstName(model), field.field)}} optionLabels={${enumLabelsExpression(modelMetaConstName(model), field.field)}}`;
|
|
@@ -2767,13 +2808,15 @@ function generateCreateView(ir, resource, view, model) {
|
|
|
2767
2808
|
lines.push(` {/* @source-node ${field.id} */}`);
|
|
2768
2809
|
lines.push(` {${fieldVisible} ? (`);
|
|
2769
2810
|
lines.push(` <FormField`);
|
|
2770
|
-
lines.push(` label=
|
|
2811
|
+
lines.push(` label={${messageLikeToRuntimeTextSource(labelMessage)}}`);
|
|
2771
2812
|
lines.push(` name="${field.field}"`);
|
|
2772
2813
|
lines.push(` type="${fieldType}"`);
|
|
2773
2814
|
lines.push(` value={formData.${field.field} ?? ''}`);
|
|
2774
2815
|
lines.push(` onChange={(v: unknown) => handleFieldChange('${field.field}', v)}`);
|
|
2775
2816
|
lines.push(` schema={${modelName}Schema.${field.field}}`);
|
|
2776
2817
|
lines.push(` error={fieldErrors[${JSON.stringify(field.field)}] ?? null}`);
|
|
2818
|
+
if (placeholderMessage)
|
|
2819
|
+
lines.push(` placeholder={${messageLikeToRuntimeTextSource(placeholderMessage)}}`);
|
|
2777
2820
|
if (optionsAttr)
|
|
2778
2821
|
lines.push(` ${optionsAttr}`);
|
|
2779
2822
|
lines.push(` disabled={${formFieldDisabledExpression(field, modelField, fieldEnabled, `linkedDerivedFieldNames.includes('${field.field}')`)}}`);
|
|
@@ -2820,6 +2863,8 @@ function generateCreateView(ir, resource, view, model) {
|
|
|
2820
2863
|
}
|
|
2821
2864
|
const fieldType = getFormFieldType(field, binding.targetModel);
|
|
2822
2865
|
const modelField = binding.targetModel.fields.find((candidate) => candidate.name === field.field);
|
|
2866
|
+
const labelMessage = formFieldLabelMessage(field);
|
|
2867
|
+
const placeholderMessage = formFieldPlaceholderMessage(field);
|
|
2823
2868
|
let optionsAttr = '';
|
|
2824
2869
|
if (modelField?.fieldType.type === 'enum') {
|
|
2825
2870
|
optionsAttr = `options={${enumValuesExpression(modelMetaConstName(binding.targetModel), field.field)}} optionKeyPrefix={${enumKeyPrefixExpression(modelMetaConstName(binding.targetModel), field.field)}} optionLabels={${enumLabelsExpression(modelMetaConstName(binding.targetModel), field.field)}}`;
|
|
@@ -2833,12 +2878,14 @@ function generateCreateView(ir, resource, view, model) {
|
|
|
2833
2878
|
lines.push(` {/* @source-node ${field.id} */}`);
|
|
2834
2879
|
lines.push(` {${fieldVisible} ? (`);
|
|
2835
2880
|
lines.push(` <FormField`);
|
|
2836
|
-
lines.push(` label=
|
|
2881
|
+
lines.push(` label={${messageLikeToRuntimeTextSource(labelMessage)}}`);
|
|
2837
2882
|
lines.push(` name="${binding.fieldName}.${field.field}"`);
|
|
2838
2883
|
lines.push(` type="${fieldType}"`);
|
|
2839
2884
|
lines.push(` value={item.${field.field} ?? ''}`);
|
|
2840
2885
|
lines.push(` onChange={(v: unknown) => ${handleChange}(index, '${field.field}', v)}`);
|
|
2841
2886
|
lines.push(` schema={${binding.targetModel.name}Schema.${field.field}}`);
|
|
2887
|
+
if (placeholderMessage)
|
|
2888
|
+
lines.push(` placeholder={${messageLikeToRuntimeTextSource(placeholderMessage)}}`);
|
|
2842
2889
|
if (optionsAttr) {
|
|
2843
2890
|
lines.push(` ${optionsAttr}`);
|
|
2844
2891
|
}
|
|
@@ -2885,7 +2932,7 @@ function generateWorkflowMetaModule(resource) {
|
|
|
2885
2932
|
const workflowStateMetaLiteralSource = workflowStateMetaRuntimeLiteral(resource);
|
|
2886
2933
|
const workflowStateMetaByNameLiteralSource = workflowStateMetaByNameRuntimeLiteral(resource);
|
|
2887
2934
|
const workflowTransitionTargetsLiteralSource = workflowTransitionTargetsRuntimeLiteral(resource);
|
|
2888
|
-
lines.push(`// Generated by
|
|
2935
|
+
lines.push(`// Generated by Loj RDSL compiler v0.6.2`);
|
|
2889
2936
|
lines.push(`// @source-node ${resource.id}`);
|
|
2890
2937
|
lines.push(``);
|
|
2891
2938
|
if (workflowRuntimeImports.size > 0) {
|
|
@@ -2980,7 +3027,7 @@ function generateReadView(ir, resource, view, model) {
|
|
|
2980
3027
|
hasWorkflow ? workflowAdvanceToStepMessage : undefined,
|
|
2981
3028
|
...(hasWorkflow ? [workflowPreconditionMessage, workflowInvalidTransitionMessage] : []),
|
|
2982
3029
|
]);
|
|
2983
|
-
lines.push(`// Generated by
|
|
3030
|
+
lines.push(`// Generated by Loj RDSL compiler v0.6.2`);
|
|
2984
3031
|
lines.push(`// @source-node ${view.id}`);
|
|
2985
3032
|
lines.push(``);
|
|
2986
3033
|
lines.push(`import React from 'react';`);
|
|
@@ -3324,7 +3371,7 @@ function generateWorkflowView(ir, resource, model) {
|
|
|
3324
3371
|
: resource.views.list
|
|
3325
3372
|
? appLocalHrefExpression(JSON.stringify(`/${resource.name}`))
|
|
3326
3373
|
: 'null';
|
|
3327
|
-
lines.push(`// Generated by
|
|
3374
|
+
lines.push(`// Generated by Loj RDSL compiler v0.6.2`);
|
|
3328
3375
|
lines.push(`// @source-node ${resource.id}`);
|
|
3329
3376
|
lines.push(``);
|
|
3330
3377
|
lines.push(`import React from 'react';`);
|
|
@@ -3572,7 +3619,7 @@ function generateRelatedCollectionView(ir, resource, model, panel) {
|
|
|
3572
3619
|
const relatedNotFoundMessage = 'Record not found';
|
|
3573
3620
|
const relatedLoadingMessage = 'Loading...';
|
|
3574
3621
|
const relatedEmptyMessage = 'No related records';
|
|
3575
|
-
lines.push(`// Generated by
|
|
3622
|
+
lines.push(`// Generated by Loj RDSL compiler v0.6.2`);
|
|
3576
3623
|
lines.push(`// @source-node ${panel.panelId}`);
|
|
3577
3624
|
lines.push(``);
|
|
3578
3625
|
lines.push(`import React from 'react';`);
|
|
@@ -4146,7 +4193,7 @@ function generatePage(ir, page) {
|
|
|
4146
4193
|
itemsName: binding.itemsName,
|
|
4147
4194
|
});
|
|
4148
4195
|
}
|
|
4149
|
-
lines.push(`// Generated by
|
|
4196
|
+
lines.push(`// Generated by Loj RDSL compiler v0.6.2`);
|
|
4150
4197
|
lines.push(`// @source-node ${page.id}`);
|
|
4151
4198
|
lines.push(``);
|
|
4152
4199
|
lines.push(`import React from 'react';`);
|
|
@@ -5735,7 +5782,7 @@ function appendRelatedPanelsSectionComponent(lines, options) {
|
|
|
5735
5782
|
// ─── Router ──────────────────────────────────────────────────────
|
|
5736
5783
|
function generateRouter(ir) {
|
|
5737
5784
|
const lines = [];
|
|
5738
|
-
lines.push(`// Generated by
|
|
5785
|
+
lines.push(`// Generated by Loj RDSL compiler v0.6.2`);
|
|
5739
5786
|
lines.push(`// @source-node app.main.router`);
|
|
5740
5787
|
lines.push(``);
|
|
5741
5788
|
lines.push(`import React from 'react';`);
|
|
@@ -5960,7 +6007,7 @@ function generateAppEntry(ir) {
|
|
|
5960
6007
|
|| group.items.some((item) => messageLikeNeedsRuntimeResolver(item.label))));
|
|
5961
6008
|
const primaryNavigationConstName = staticComponentConstName('App', 'PrimaryNavigation');
|
|
5962
6009
|
const secondaryNavigationConstName = staticComponentConstName('App', 'SecondaryNavigation');
|
|
5963
|
-
lines.push(`// Generated by
|
|
6010
|
+
lines.push(`// Generated by Loj RDSL compiler v0.6.2`);
|
|
5964
6011
|
lines.push(`// @source-node app.main`);
|
|
5965
6012
|
lines.push(`// Prefer editing source .web.loj/.style.loj files or documented escape hatches instead of this generated file.`);
|
|
5966
6013
|
lines.push(`// This is the application entry point.`);
|
|
@@ -8376,6 +8423,168 @@ function enumLabelsExpression(modelMetaName, fieldName) {
|
|
|
8376
8423
|
function enumValuesExpression(modelMetaName, fieldName) {
|
|
8377
8424
|
return `((${modelMetaName}.enumValues as Record<string, readonly string[] | undefined>)[${JSON.stringify(fieldName)}] ?? ([] as readonly string[]))`;
|
|
8378
8425
|
}
|
|
8426
|
+
function collectFormFieldDecoratorMessages(fields, decoratorName) {
|
|
8427
|
+
return fields
|
|
8428
|
+
.map((field) => formFieldDecoratorMessageLike(field, decoratorName))
|
|
8429
|
+
.filter((message) => Boolean(message));
|
|
8430
|
+
}
|
|
8431
|
+
function formFieldLabelMessage(field) {
|
|
8432
|
+
return formFieldDecoratorMessageLike(field, 'label') ?? columnLabel(field.field);
|
|
8433
|
+
}
|
|
8434
|
+
function formFieldPlaceholderMessage(field) {
|
|
8435
|
+
return formFieldDecoratorMessageLike(field, 'placeholder');
|
|
8436
|
+
}
|
|
8437
|
+
function includeFieldDefaultObjectLiteral(model, fields) {
|
|
8438
|
+
const entries = fields.flatMap((field) => {
|
|
8439
|
+
const modelField = model.fields.find((candidate) => candidate.name === field.field);
|
|
8440
|
+
if (!modelField) {
|
|
8441
|
+
return [];
|
|
8442
|
+
}
|
|
8443
|
+
const defaultValueLiteral = fieldDefaultValueLiteral(modelField);
|
|
8444
|
+
if (defaultValueLiteral === undefined) {
|
|
8445
|
+
return [];
|
|
8446
|
+
}
|
|
8447
|
+
return [`${JSON.stringify(field.field)}: ${defaultValueLiteral}`];
|
|
8448
|
+
});
|
|
8449
|
+
if (entries.length === 0) {
|
|
8450
|
+
return '{}';
|
|
8451
|
+
}
|
|
8452
|
+
return `{ ${entries.join(', ')} }`;
|
|
8453
|
+
}
|
|
8454
|
+
function fieldDefaultValueLiteral(field) {
|
|
8455
|
+
const decorator = field.decorators.find((candidate) => candidate.name === 'default');
|
|
8456
|
+
if (!decorator?.args) {
|
|
8457
|
+
return undefined;
|
|
8458
|
+
}
|
|
8459
|
+
const raw = 'value' in decorator.args
|
|
8460
|
+
? decorator.args.value
|
|
8461
|
+
: undefined;
|
|
8462
|
+
const value = normalizeDecoratorScalarValue(raw);
|
|
8463
|
+
if (value === undefined) {
|
|
8464
|
+
return undefined;
|
|
8465
|
+
}
|
|
8466
|
+
return JSON.stringify(value);
|
|
8467
|
+
}
|
|
8468
|
+
function formFieldDecoratorMessageLike(field, decoratorName) {
|
|
8469
|
+
const decorator = field.decorators.find((candidate) => candidate.name === decoratorName);
|
|
8470
|
+
if (!decorator?.args) {
|
|
8471
|
+
return undefined;
|
|
8472
|
+
}
|
|
8473
|
+
return normalizeDecoratorMessageLikeValue(decorator.args);
|
|
8474
|
+
}
|
|
8475
|
+
function normalizeDecoratorMessageLikeValue(args) {
|
|
8476
|
+
const directValue = normalizeDecoratorScalarValue(args.value);
|
|
8477
|
+
if (typeof directValue === 'string' && directValue.length > 0) {
|
|
8478
|
+
const descriptorFromJson = parseMessageLikeDescriptorJson(directValue);
|
|
8479
|
+
if (descriptorFromJson) {
|
|
8480
|
+
return descriptorFromJson;
|
|
8481
|
+
}
|
|
8482
|
+
return directValue;
|
|
8483
|
+
}
|
|
8484
|
+
const key = normalizeDecoratorStringValue(args.key);
|
|
8485
|
+
const defaultMessage = normalizeDecoratorStringValue(args.defaultMessage);
|
|
8486
|
+
let values;
|
|
8487
|
+
if (isPlainObject(args.values)) {
|
|
8488
|
+
values = Object.fromEntries(Object.entries(args.values)
|
|
8489
|
+
.map(([name, value]) => [name, normalizeDecoratorScalarValue(value)])
|
|
8490
|
+
.filter((entry) => entry[1] !== undefined));
|
|
8491
|
+
}
|
|
8492
|
+
if (!key && !defaultMessage && (!values || Object.keys(values).length === 0)) {
|
|
8493
|
+
return undefined;
|
|
8494
|
+
}
|
|
8495
|
+
const descriptor = {};
|
|
8496
|
+
if (key) {
|
|
8497
|
+
descriptor.key = key;
|
|
8498
|
+
}
|
|
8499
|
+
if (defaultMessage) {
|
|
8500
|
+
descriptor.defaultMessage = defaultMessage;
|
|
8501
|
+
}
|
|
8502
|
+
if (values && Object.keys(values).length > 0) {
|
|
8503
|
+
descriptor.values = values;
|
|
8504
|
+
}
|
|
8505
|
+
return descriptor;
|
|
8506
|
+
}
|
|
8507
|
+
function normalizeDecoratorStringValue(value) {
|
|
8508
|
+
const normalized = normalizeDecoratorScalarValue(value);
|
|
8509
|
+
if (typeof normalized !== 'string') {
|
|
8510
|
+
return undefined;
|
|
8511
|
+
}
|
|
8512
|
+
const trimmed = normalized.trim();
|
|
8513
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
8514
|
+
}
|
|
8515
|
+
function normalizeDecoratorScalarValue(value) {
|
|
8516
|
+
if (value === null || typeof value === 'number' || typeof value === 'boolean') {
|
|
8517
|
+
return value;
|
|
8518
|
+
}
|
|
8519
|
+
if (typeof value !== 'string') {
|
|
8520
|
+
return undefined;
|
|
8521
|
+
}
|
|
8522
|
+
const trimmed = value.trim();
|
|
8523
|
+
if (!trimmed) {
|
|
8524
|
+
return undefined;
|
|
8525
|
+
}
|
|
8526
|
+
const quoted = trimmed.match(/^(['"])([\s\S]*)\1$/);
|
|
8527
|
+
if (quoted) {
|
|
8528
|
+
return quoted[2];
|
|
8529
|
+
}
|
|
8530
|
+
if (trimmed === 'true') {
|
|
8531
|
+
return true;
|
|
8532
|
+
}
|
|
8533
|
+
if (trimmed === 'false') {
|
|
8534
|
+
return false;
|
|
8535
|
+
}
|
|
8536
|
+
if (trimmed === 'null') {
|
|
8537
|
+
return null;
|
|
8538
|
+
}
|
|
8539
|
+
if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
|
|
8540
|
+
const parsed = Number(trimmed);
|
|
8541
|
+
if (Number.isFinite(parsed)) {
|
|
8542
|
+
return parsed;
|
|
8543
|
+
}
|
|
8544
|
+
}
|
|
8545
|
+
return trimmed;
|
|
8546
|
+
}
|
|
8547
|
+
function isPlainObject(value) {
|
|
8548
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
8549
|
+
}
|
|
8550
|
+
function parseMessageLikeDescriptorJson(value) {
|
|
8551
|
+
const trimmed = value.trim();
|
|
8552
|
+
if (!trimmed.startsWith('{') || !trimmed.endsWith('}')) {
|
|
8553
|
+
return undefined;
|
|
8554
|
+
}
|
|
8555
|
+
let parsed;
|
|
8556
|
+
try {
|
|
8557
|
+
parsed = JSON.parse(trimmed);
|
|
8558
|
+
}
|
|
8559
|
+
catch {
|
|
8560
|
+
return undefined;
|
|
8561
|
+
}
|
|
8562
|
+
if (!isPlainObject(parsed)) {
|
|
8563
|
+
return undefined;
|
|
8564
|
+
}
|
|
8565
|
+
const key = normalizeDecoratorStringValue(parsed.key);
|
|
8566
|
+
const defaultMessage = normalizeDecoratorStringValue(parsed.defaultMessage);
|
|
8567
|
+
let values;
|
|
8568
|
+
if (isPlainObject(parsed.values)) {
|
|
8569
|
+
values = Object.fromEntries(Object.entries(parsed.values)
|
|
8570
|
+
.map(([name, raw]) => [name, normalizeDecoratorScalarValue(raw)])
|
|
8571
|
+
.filter((entry) => entry[1] !== undefined));
|
|
8572
|
+
}
|
|
8573
|
+
if (!key && !defaultMessage && (!values || Object.keys(values).length === 0)) {
|
|
8574
|
+
return undefined;
|
|
8575
|
+
}
|
|
8576
|
+
const descriptor = {};
|
|
8577
|
+
if (key) {
|
|
8578
|
+
descriptor.key = key;
|
|
8579
|
+
}
|
|
8580
|
+
if (defaultMessage) {
|
|
8581
|
+
descriptor.defaultMessage = defaultMessage;
|
|
8582
|
+
}
|
|
8583
|
+
if (values && Object.keys(values).length > 0) {
|
|
8584
|
+
descriptor.values = values;
|
|
8585
|
+
}
|
|
8586
|
+
return descriptor;
|
|
8587
|
+
}
|
|
8379
8588
|
function fieldFormatExpression(modelMetaName, fieldName) {
|
|
8380
8589
|
return `((${modelMetaName}.fieldFormats as Record<string, unknown>)[${JSON.stringify(fieldName)}] as any)`;
|
|
8381
8590
|
}
|