english-lang 0.2.5 → 0.2.6
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/README.md +25 -18
- package/dist/cli/engc.js +80 -33
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -91,31 +91,38 @@ export function HomeScreen({ navigation }: any) {
|
|
|
91
91
|
## Project structure
|
|
92
92
|
|
|
93
93
|
```
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
94
|
+
MyApp/ ← project root (.eng world)
|
|
95
|
+
├── package.json ← compile / watch scripts only
|
|
96
|
+
├── screens/ ← write your .eng files here
|
|
97
|
+
│ ├── HomeScreen.eng
|
|
98
|
+
│ └── DetailScreen.eng
|
|
99
|
+
└── rn/ ← entire React Native project
|
|
100
|
+
├── App.tsx ← auto-generated by engc (navigation wiring)
|
|
101
|
+
├── src/ ← auto-generated .tsx (engc output)
|
|
102
|
+
│ ├── HomeScreen.tsx
|
|
103
|
+
│ └── DetailScreen.tsx
|
|
104
|
+
├── ios/
|
|
105
|
+
├── android/
|
|
106
|
+
├── node_modules/
|
|
107
|
+
├── index.js
|
|
108
|
+
└── package.json
|
|
106
109
|
```
|
|
107
110
|
|
|
111
|
+
**Rules:**
|
|
112
|
+
- You only ever edit files in `screens/` — never touch `rn/src/` or `rn/App.tsx`
|
|
113
|
+
- `rn/App.tsx` is regenerated automatically whenever you add, rename, or remove a `.eng` file
|
|
114
|
+
- `rn/src/*.tsx` are regenerated automatically on every save
|
|
115
|
+
|
|
108
116
|
**Quick start:**
|
|
109
117
|
|
|
110
118
|
```bash
|
|
111
|
-
# One command sets everything up
|
|
112
|
-
engc init
|
|
113
|
-
cd my-app
|
|
119
|
+
# One command sets everything up: scaffolds RN, installs deps, starts Metro
|
|
120
|
+
engc init MyApp
|
|
114
121
|
|
|
115
|
-
#
|
|
116
|
-
npm run
|
|
122
|
+
# From inside MyApp/ — Metro + eng watcher in one command
|
|
123
|
+
npm run dev
|
|
117
124
|
|
|
118
|
-
# In a separate terminal
|
|
125
|
+
# In a separate terminal — run on device / simulator
|
|
119
126
|
cd rn && npx react-native run-ios
|
|
120
127
|
```
|
|
121
128
|
|
package/dist/cli/engc.js
CHANGED
|
@@ -50,32 +50,69 @@ const fs = __importStar(require("fs"));
|
|
|
50
50
|
const path = __importStar(require("path"));
|
|
51
51
|
const childProcess = __importStar(require("child_process"));
|
|
52
52
|
const index_1 = require("../index");
|
|
53
|
-
// ──
|
|
54
|
-
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
53
|
+
// ── App.tsx generator ────────────────────────────────────────
|
|
54
|
+
//
|
|
55
|
+
// Reads every .eng file in screens/, extracts the screen name from
|
|
56
|
+
// the first `screen Foo:` declaration, then writes rn/App.tsx with
|
|
57
|
+
// all screens wired into the navigator automatically.
|
|
58
|
+
// Called on init and after every file change during `engc dev`.
|
|
59
|
+
function screenNamesFromDir(screensDir) {
|
|
60
|
+
if (!fs.existsSync(screensDir))
|
|
61
|
+
return [];
|
|
62
|
+
const names = [];
|
|
63
|
+
for (const file of fs.readdirSync(screensDir)) {
|
|
64
|
+
if (!file.endsWith('.eng'))
|
|
65
|
+
continue;
|
|
66
|
+
try {
|
|
67
|
+
const src = fs.readFileSync(path.join(screensDir, file), 'utf8');
|
|
68
|
+
const m = src.match(/^\s*screen\s+(\w+)/m);
|
|
69
|
+
if (m)
|
|
70
|
+
names.push(m[1]);
|
|
71
|
+
}
|
|
72
|
+
catch { /* skip unreadable files */ }
|
|
73
|
+
}
|
|
74
|
+
return names;
|
|
75
|
+
}
|
|
76
|
+
function generateAppTsx(screensDir, rnDir) {
|
|
77
|
+
const names = screenNamesFromDir(screensDir);
|
|
78
|
+
if (names.length === 0)
|
|
79
|
+
return;
|
|
80
|
+
const initial = names.includes('HomeScreen') ? 'HomeScreen' : names[0];
|
|
81
|
+
const imports = names
|
|
82
|
+
.map(n => `import { ${n} } from './src/${n}';`)
|
|
83
|
+
.join('\n');
|
|
84
|
+
const stackScreens = names
|
|
85
|
+
.map(n => ` <Stack.Screen name="${n}" component={${n}} />`)
|
|
86
|
+
.join('\n');
|
|
87
|
+
const content = [
|
|
88
|
+
`// App.tsx — auto-generated by engc`,
|
|
89
|
+
`// Do not edit — add new screens by creating .eng files in screens/`,
|
|
90
|
+
``,
|
|
91
|
+
`import React from 'react';`,
|
|
92
|
+
`import { NavigationContainer } from '@react-navigation/native';`,
|
|
93
|
+
`import { createNativeStackNavigator } from '@react-navigation/native-stack';`,
|
|
94
|
+
`import { SafeAreaProvider } from 'react-native-safe-area-context';`,
|
|
95
|
+
``,
|
|
96
|
+
imports,
|
|
97
|
+
``,
|
|
98
|
+
`const Stack = createNativeStackNavigator();`,
|
|
99
|
+
``,
|
|
100
|
+
`export default function App() {`,
|
|
101
|
+
` return (`,
|
|
102
|
+
` <SafeAreaProvider>`,
|
|
103
|
+
` <NavigationContainer>`,
|
|
104
|
+
` <Stack.Navigator initialRouteName="${initial}">`,
|
|
105
|
+
stackScreens,
|
|
106
|
+
` </Stack.Navigator>`,
|
|
107
|
+
` </NavigationContainer>`,
|
|
108
|
+
` </SafeAreaProvider>`,
|
|
109
|
+
` );`,
|
|
110
|
+
`}`,
|
|
111
|
+
``,
|
|
112
|
+
].join('\n');
|
|
113
|
+
fs.writeFileSync(path.join(rnDir, 'App.tsx'), content, 'utf8');
|
|
114
|
+
console.log(`[engc] rn/App.tsx → ${names.length} screen${names.length === 1 ? '' : 's'} registered`);
|
|
77
115
|
}
|
|
78
|
-
`;
|
|
79
116
|
// ── Starter .eng file ─────────────────────────────────────────
|
|
80
117
|
const STARTER_ENG = `\
|
|
81
118
|
screen HomeScreen:
|
|
@@ -122,8 +159,9 @@ function devProject(projectDir = process.cwd()) {
|
|
|
122
159
|
metro.on('error', (err) => {
|
|
123
160
|
console.error('[engc] Metro error:', err.message);
|
|
124
161
|
});
|
|
125
|
-
//
|
|
162
|
+
// Initial compile + App.tsx generation
|
|
126
163
|
compileDir(screensDir, rnSrcDir);
|
|
164
|
+
generateAppTsx(screensDir, rnDir);
|
|
127
165
|
console.log(`[engc] Watching screens/ ...\n`);
|
|
128
166
|
fs.watch(screensDir, { recursive: false }, (event, filename) => {
|
|
129
167
|
if (!filename || !filename.endsWith('.eng'))
|
|
@@ -132,6 +170,7 @@ function devProject(projectDir = process.cwd()) {
|
|
|
132
170
|
if (!fs.existsSync(filePath))
|
|
133
171
|
return;
|
|
134
172
|
compileFile(filePath, rnSrcDir);
|
|
173
|
+
generateAppTsx(screensDir, rnDir); // re-register screens on every change
|
|
135
174
|
});
|
|
136
175
|
// Keep alive — exit both on Ctrl+C
|
|
137
176
|
process.on('SIGINT', () => {
|
|
@@ -183,9 +222,8 @@ function initProject(name) {
|
|
|
183
222
|
const rnDir = path.join(projectDir, 'rn');
|
|
184
223
|
console.log('\n[engc] Installing React Navigation ...');
|
|
185
224
|
childProcess.execSync('npm install @react-navigation/native @react-navigation/native-stack react-native-screens react-native-safe-area-context', { cwd: rnDir, stdio: 'inherit' });
|
|
186
|
-
//
|
|
187
|
-
fs.
|
|
188
|
-
console.log('[engc] Patched rn/App.tsx');
|
|
225
|
+
// Compile starter screens, then generate App.tsx from them
|
|
226
|
+
fs.mkdirSync(rnSrcDir, { recursive: true });
|
|
189
227
|
// Pod install for iOS
|
|
190
228
|
const iosDir = path.join(rnDir, 'ios');
|
|
191
229
|
if (fs.existsSync(iosDir)) {
|
|
@@ -200,10 +238,9 @@ function initProject(name) {
|
|
|
200
238
|
console.warn('[engc] Pod install failed — run it manually: cd rn/ios && pod install');
|
|
201
239
|
}
|
|
202
240
|
}
|
|
203
|
-
// Compile starter screens into rn/src/
|
|
204
|
-
fs.mkdirSync(rnSrcDir, { recursive: true });
|
|
205
241
|
console.log('\n[engc] Compiling starter screens ...');
|
|
206
242
|
compileDir(screensDir, rnSrcDir);
|
|
243
|
+
generateAppTsx(screensDir, rnDir);
|
|
207
244
|
console.log('\n[engc] All done! Starting Metro + eng watcher ...\n');
|
|
208
245
|
// Auto-start dev mode immediately
|
|
209
246
|
devProject(projectDir);
|
|
@@ -239,8 +276,10 @@ function compileDir(srcDir, outDir) {
|
|
|
239
276
|
compileFile(path.join(srcDir, file), outDir);
|
|
240
277
|
}
|
|
241
278
|
}
|
|
242
|
-
function watchDir(srcDir, outDir) {
|
|
279
|
+
function watchDir(srcDir, outDir, rnDir = null) {
|
|
243
280
|
compileDir(srcDir, outDir);
|
|
281
|
+
if (rnDir)
|
|
282
|
+
generateAppTsx(srcDir, rnDir);
|
|
244
283
|
console.log(`[engc] Watching ${srcDir} ...`);
|
|
245
284
|
fs.watch(srcDir, { recursive: false }, (event, filename) => {
|
|
246
285
|
if (!filename || !filename.endsWith('.eng'))
|
|
@@ -249,6 +288,8 @@ function watchDir(srcDir, outDir) {
|
|
|
249
288
|
if (!fs.existsSync(filePath))
|
|
250
289
|
return;
|
|
251
290
|
compileFile(filePath, outDir);
|
|
291
|
+
if (rnDir)
|
|
292
|
+
generateAppTsx(srcDir, rnDir);
|
|
252
293
|
});
|
|
253
294
|
}
|
|
254
295
|
// ── CLI entry point ───────────────────────────────────────────
|
|
@@ -304,11 +345,17 @@ Examples:
|
|
|
304
345
|
console.error(`[engc] Directory not found: ${srcDir}`);
|
|
305
346
|
process.exit(1);
|
|
306
347
|
}
|
|
348
|
+
// If outDir is rn/src/, its parent (rn/) is the RN project — regenerate App.tsx
|
|
349
|
+
const maybeRnDir = outDir ? path.dirname(outDir) : null;
|
|
350
|
+
const shouldGenApp = maybeRnDir != null
|
|
351
|
+
&& fs.existsSync(path.join(maybeRnDir, 'package.json'));
|
|
307
352
|
if (cmd === 'compile') {
|
|
308
353
|
compileDir(srcDir, outDir);
|
|
354
|
+
if (shouldGenApp)
|
|
355
|
+
generateAppTsx(srcDir, maybeRnDir);
|
|
309
356
|
}
|
|
310
357
|
else {
|
|
311
|
-
watchDir(srcDir, outDir);
|
|
358
|
+
watchDir(srcDir, outDir, shouldGenApp ? maybeRnDir : null);
|
|
312
359
|
}
|
|
313
360
|
return;
|
|
314
361
|
}
|