english-lang 0.2.4 → 0.2.5
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.
|
@@ -69,8 +69,15 @@ class ReactNativeBackend {
|
|
|
69
69
|
this.emitTypeDecl(type);
|
|
70
70
|
}
|
|
71
71
|
// Component function
|
|
72
|
-
|
|
72
|
+
const hasRouteParams = screen.receives && screen.receives.length > 0;
|
|
73
|
+
const propsArg = hasRouteParams ? '{ navigation, route }' : '{ navigation }';
|
|
74
|
+
this.line(`export function ${screen.name}(${propsArg}: any) {`);
|
|
73
75
|
this.depth++;
|
|
76
|
+
// Destructure navigation params
|
|
77
|
+
if (hasRouteParams) {
|
|
78
|
+
this.line(`const { ${screen.receives.join(', ')} } = (route.params ?? {}) as any;`);
|
|
79
|
+
this.line('');
|
|
80
|
+
}
|
|
74
81
|
// useState hooks
|
|
75
82
|
for (const field of screen.state) {
|
|
76
83
|
const tsType = this.tsType(field, stateVarTypes);
|
|
@@ -83,10 +90,22 @@ class ReactNativeBackend {
|
|
|
83
90
|
// useEffect for screen opens
|
|
84
91
|
const screenOpensHandler = screen.events.find(e => e.trigger.kind === 'ScreenOpens');
|
|
85
92
|
if (screenOpensHandler) {
|
|
93
|
+
const isAsync = this.bodyIsAsync(screenOpensHandler.body);
|
|
86
94
|
this.line('useEffect(() => {');
|
|
87
95
|
this.depth++;
|
|
88
|
-
|
|
89
|
-
this.
|
|
96
|
+
if (isAsync) {
|
|
97
|
+
this.line('(async () => {');
|
|
98
|
+
this.depth++;
|
|
99
|
+
for (const stmt of screenOpensHandler.body) {
|
|
100
|
+
this.emitStatement(stmt, stateVarTypes);
|
|
101
|
+
}
|
|
102
|
+
this.depth--;
|
|
103
|
+
this.line('})();');
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
for (const stmt of screenOpensHandler.body) {
|
|
107
|
+
this.emitStatement(stmt, stateVarTypes);
|
|
108
|
+
}
|
|
90
109
|
}
|
|
91
110
|
this.depth--;
|
|
92
111
|
this.line('}, []);');
|
|
@@ -97,7 +116,8 @@ class ReactNativeBackend {
|
|
|
97
116
|
if (event.trigger.kind !== 'ButtonPressed')
|
|
98
117
|
continue;
|
|
99
118
|
const fnName = buttonHandlers.get(event.trigger.label);
|
|
100
|
-
|
|
119
|
+
const asyncKw = this.bodyIsAsync(event.body) ? 'async ' : '';
|
|
120
|
+
this.line(`const ${fnName} = ${asyncKw}() => {`);
|
|
101
121
|
this.depth++;
|
|
102
122
|
for (const stmt of event.body) {
|
|
103
123
|
this.emitStatement(stmt, stateVarTypes);
|
|
@@ -221,6 +241,10 @@ class ReactNativeBackend {
|
|
|
221
241
|
}
|
|
222
242
|
this.depth--;
|
|
223
243
|
}
|
|
244
|
+
else {
|
|
245
|
+
this.line(`} catch (_e) {`);
|
|
246
|
+
this.line(` // network error — add "on failure: (err) ->" to handle it`);
|
|
247
|
+
}
|
|
224
248
|
this.line(`}`);
|
|
225
249
|
break;
|
|
226
250
|
}
|
|
@@ -647,6 +671,18 @@ class ReactNativeBackend {
|
|
|
647
671
|
};
|
|
648
672
|
return map[name] ?? name;
|
|
649
673
|
}
|
|
674
|
+
bodyIsAsync(stmts) {
|
|
675
|
+
return stmts.some(s => {
|
|
676
|
+
if (s.kind === 'FetchStatement')
|
|
677
|
+
return true;
|
|
678
|
+
if (s.kind === 'IfStatement') {
|
|
679
|
+
return this.bodyIsAsync(s.thenBody)
|
|
680
|
+
|| s.otherwiseIfBranches.some(b => this.bodyIsAsync(b.body))
|
|
681
|
+
|| (s.elseBody ? this.bodyIsAsync(s.elseBody) : false);
|
|
682
|
+
}
|
|
683
|
+
return false;
|
|
684
|
+
});
|
|
685
|
+
}
|
|
650
686
|
hasSlider(layout) {
|
|
651
687
|
const scan = (nodes) => {
|
|
652
688
|
for (const node of nodes) {
|
|
@@ -55,6 +55,7 @@ class Parser {
|
|
|
55
55
|
this.expect('NEWLINE');
|
|
56
56
|
this.expect('INDENT');
|
|
57
57
|
let title = null;
|
|
58
|
+
const receives = [];
|
|
58
59
|
const state = [];
|
|
59
60
|
const events = [];
|
|
60
61
|
const actions = [];
|
|
@@ -70,6 +71,21 @@ class Parser {
|
|
|
70
71
|
title = this.expect('STRING').value;
|
|
71
72
|
this.expectNewline();
|
|
72
73
|
}
|
|
74
|
+
else if (word === 'receives') {
|
|
75
|
+
// receives: param1, param2 ... (navigation route.params)
|
|
76
|
+
this.consumeWord();
|
|
77
|
+
this.expect('COLON');
|
|
78
|
+
this.expect('NEWLINE');
|
|
79
|
+
this.expect('INDENT');
|
|
80
|
+
while (!this.check('DEDENT') && !this.check('EOF')) {
|
|
81
|
+
this.skipNewlines();
|
|
82
|
+
if (this.check('DEDENT'))
|
|
83
|
+
break;
|
|
84
|
+
receives.push(this.expectWord());
|
|
85
|
+
this.expectNewline();
|
|
86
|
+
}
|
|
87
|
+
this.expect('DEDENT');
|
|
88
|
+
}
|
|
73
89
|
else if (word === 'state') {
|
|
74
90
|
this.consumeWord();
|
|
75
91
|
this.expect('COLON');
|
|
@@ -110,7 +126,7 @@ class Parser {
|
|
|
110
126
|
}
|
|
111
127
|
}
|
|
112
128
|
this.expect('DEDENT');
|
|
113
|
-
return { kind: 'ScreenDecl', name, title, state, events, actions, layout, line };
|
|
129
|
+
return { kind: 'ScreenDecl', name, title, receives, state, events, actions, layout, line };
|
|
114
130
|
}
|
|
115
131
|
// ── State field ───────────────────────────────────────────
|
|
116
132
|
//
|
|
@@ -1321,8 +1337,17 @@ class Parser {
|
|
|
1321
1337
|
if (word === 'when') {
|
|
1322
1338
|
events.push(this.parseEventHandler());
|
|
1323
1339
|
}
|
|
1340
|
+
else if (word === 'layout') {
|
|
1341
|
+
// Optional layout: section header (same as screens)
|
|
1342
|
+
this.consumeWord();
|
|
1343
|
+
this.expect('COLON');
|
|
1344
|
+
this.expect('NEWLINE');
|
|
1345
|
+
this.expect('INDENT');
|
|
1346
|
+
layout = this.parseLayoutNodes();
|
|
1347
|
+
this.expect('DEDENT');
|
|
1348
|
+
}
|
|
1324
1349
|
else {
|
|
1325
|
-
//
|
|
1350
|
+
// Layout nodes written directly (without layout: header)
|
|
1326
1351
|
layout = this.parseLayoutNodes();
|
|
1327
1352
|
}
|
|
1328
1353
|
}
|