@codemoreira/esad 2.0.1-4 ā 2.0.1-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 +88 -82
- package/bin/esad.js +5 -12
- package/package.json +1 -1
- package/src/cli/commands/dev.js +23 -20
- package/src/cli/commands/init.js +1 -1
- package/src/cli/utils/scaffold.js +8 -1
- package/src/plugin/index.js +7 -6
- package/src/cli/commands/host.js +0 -42
package/README.md
CHANGED
|
@@ -1,82 +1,88 @@
|
|
|
1
|
-
# ESAD (Easy Super App Development) š
|
|
2
|
-
|
|
3
|
-
Zero-Config CLI and DevTools for React Native Module Federation + Expo.
|
|
4
|
-
|
|
5
|
-
ESAD is a unified toolkit designed to abstract all the complexity from Super App development using **Re.Pack (Rspack)** and **Expo**.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## šļø CLI
|
|
10
|
-
|
|
11
|
-
### 1. Initialize a Workspace
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
###
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
1
|
+
# ESAD (Easy Super App Development) š
|
|
2
|
+
|
|
3
|
+
Zero-Config CLI and DevTools for React Native Module Federation + Expo.
|
|
4
|
+
|
|
5
|
+
ESAD is a unified toolkit designed to abstract all the complexity from Super App development using **Re.Pack (Rspack)** and **Expo**. It provides a professional, linear workflow from scaffolding to deployment.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## šļø CLI Workflow (V2)
|
|
10
|
+
|
|
11
|
+
### 1. Initialize a Workspace
|
|
12
|
+
Creates the project root, a programmable `esad.config.js`, and the Host Application.
|
|
13
|
+
```bash
|
|
14
|
+
npx @codemoreira/esad init my-project
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### 2. Expand your Workspace
|
|
18
|
+
Create new Federated Modules or a Local Registry/CDN.
|
|
19
|
+
```bash
|
|
20
|
+
esad create my-module # Creates a new module (Feature)
|
|
21
|
+
esad create --type cdn # Scaffolds a local registry for testing
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### 3. Development Manager (Unified)
|
|
25
|
+
The `dev` command is the single entry point for development. It automatically detects if it should run the Host or a Module.
|
|
26
|
+
|
|
27
|
+
**Run the Host App (Interactive):**
|
|
28
|
+
```bash
|
|
29
|
+
esad dev
|
|
30
|
+
```
|
|
31
|
+
*Allows selecting Android, iOS, or Bundler Only. Automatically patches native files.*
|
|
32
|
+
|
|
33
|
+
**Run a specific Module:**
|
|
34
|
+
```bash
|
|
35
|
+
esad dev my-module --port 9000
|
|
36
|
+
```
|
|
37
|
+
*Starts the module server and updates the Host's local mapping automatically.*
|
|
38
|
+
|
|
39
|
+
### 4. Build & Deploy
|
|
40
|
+
Prepare and push your features to the registry.
|
|
41
|
+
|
|
42
|
+
**Build for Production:**
|
|
43
|
+
```bash
|
|
44
|
+
esad build my-module --platform android
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Execute Programmable Deploy:**
|
|
48
|
+
```bash
|
|
49
|
+
esad deploy my-module --version 1.0.0
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## š ļø Library Usage
|
|
55
|
+
|
|
56
|
+
### šØ Bundler Plugin (`@codemoreira/esad/plugin`)
|
|
57
|
+
Wrap your configuration to enable ESAD's smart resolution and redirection logic:
|
|
58
|
+
```javascript
|
|
59
|
+
import { withESAD } from '@codemoreira/esad/plugin';
|
|
60
|
+
|
|
61
|
+
export default withESAD({
|
|
62
|
+
type: 'module', // or 'host'
|
|
63
|
+
id: 'my-mini-app'
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### ā” Global State SDK (`@codemoreira/esad/client`)
|
|
68
|
+
Share state across the Host and all Remote Modules reactively:
|
|
69
|
+
```javascript
|
|
70
|
+
import { useESADState } from '@codemoreira/esad/client';
|
|
71
|
+
|
|
72
|
+
const [user, setUser] = useESADState('user');
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## š Template Features (Host & Module)
|
|
78
|
+
ESAD templates provide a high-end starting point:
|
|
79
|
+
- **š Rspack + Re.Pack**: Blazing fast builds.
|
|
80
|
+
- **š± Professional Architecture**: Modular structure (`src/api`, `src/components`, `src/navigation`).
|
|
81
|
+
- **š¤ļø Dynamic Navigation**: Pre-configured Module Viewer with **Suspense** and **ErrorBoundary** support.
|
|
82
|
+
- **š State-Driven Auth**: Built-in login and session management via the ESAD SDK.
|
|
83
|
+
- **š§ Automated Native Patching**: Zero-config injection into Android/iOS projects.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## šØ Architecture & Workflow
|
|
88
|
+
For technical diagrams and the full lifecycle, see [ESAD_ARCHITECTURE.md](./ESAD_ARCHITECTURE.md).
|
package/bin/esad.js
CHANGED
|
@@ -24,12 +24,13 @@ program
|
|
|
24
24
|
await require('../src/cli/commands/create')(name, options);
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
-
// --- COMMAND: esad dev [
|
|
27
|
+
// --- COMMAND: esad dev [id] ---
|
|
28
28
|
program
|
|
29
|
-
.command('dev [id]')
|
|
29
|
+
.command('dev [id]')
|
|
30
30
|
.option('-i, --id <moduleId>', 'The Module ID to run in dev mode')
|
|
31
31
|
.option('-p, --port <port>', 'The port to run the dev server on', '8081')
|
|
32
|
-
.
|
|
32
|
+
.option('--platform <platform>', 'Platform for host: android, ios, bundler')
|
|
33
|
+
.description('Starts the development environment. Run without [id] for Host App or with [id] for a specific Module.')
|
|
33
34
|
.action(async (id, options) => {
|
|
34
35
|
const opts = { ...options, id: id || options.id };
|
|
35
36
|
await require('../src/cli/commands/dev')(opts);
|
|
@@ -40,7 +41,7 @@ program
|
|
|
40
41
|
.command('build [id]')
|
|
41
42
|
.option('-i, --id <moduleId>', 'The Module ID to build')
|
|
42
43
|
.option('-p, --platform <platform>', 'Platform: android, ios', 'android')
|
|
43
|
-
.description('Builds a production bundle')
|
|
44
|
+
.description('Builds a production bundle for a module or the host application')
|
|
44
45
|
.action(async (id, options) => {
|
|
45
46
|
const opts = { ...options, id: id || options.id };
|
|
46
47
|
await require('../src/cli/commands/build')(opts);
|
|
@@ -57,14 +58,6 @@ program
|
|
|
57
58
|
await require('../src/cli/commands/deploy')(opts);
|
|
58
59
|
});
|
|
59
60
|
|
|
60
|
-
// --- COMMAND: esad host <sub> ---
|
|
61
|
-
program
|
|
62
|
-
.command('host <subcommand>')
|
|
63
|
-
.description('Manage host application (android, ios, login)')
|
|
64
|
-
.action(async (sub) => {
|
|
65
|
-
await require('../src/cli/commands/host')(sub);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
61
|
// --- COMMAND: esad doctor ---
|
|
69
62
|
program
|
|
70
63
|
.command('doctor')
|
package/package.json
CHANGED
package/src/cli/commands/dev.js
CHANGED
|
@@ -22,25 +22,29 @@ const isPortInUse = (port) => new Promise((resolve) => {
|
|
|
22
22
|
req.end();
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
-
const runHostDevFlow = async (cwd) => {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
25
|
+
const runHostDevFlow = async (cwd, options = {}) => {
|
|
26
|
+
let choice = options.platform ? options.platform.charAt(0).toLowerCase() : null;
|
|
27
|
+
|
|
28
|
+
if (!choice) {
|
|
29
|
+
const rl = readline.createInterface({
|
|
30
|
+
input: process.stdin,
|
|
31
|
+
output: process.stdout
|
|
32
|
+
});
|
|
33
|
+
const askQuestion = (query) => new Promise((resolve) => rl.question(query, resolve));
|
|
34
|
+
|
|
35
|
+
console.log(`\n${chalk.bold('ESAD Host Dev Manager')}`);
|
|
36
|
+
console.log(chalk.dim(`---------------------`));
|
|
37
|
+
console.log(`[a] Run on Android`);
|
|
38
|
+
console.log(`[i] Run on iOS`);
|
|
39
|
+
console.log(`[b] Bundler Only`);
|
|
40
|
+
console.log(`[c] Cancel`);
|
|
41
|
+
|
|
42
|
+
choice = (await askQuestion(`\nSelect platform: `)).toLowerCase();
|
|
43
|
+
rl.close();
|
|
44
|
+
}
|
|
40
45
|
|
|
41
46
|
if (choice === 'c') {
|
|
42
47
|
console.log(`\nā Cancelled.`);
|
|
43
|
-
rl.close();
|
|
44
48
|
return;
|
|
45
49
|
}
|
|
46
50
|
|
|
@@ -81,7 +85,6 @@ const runHostDevFlow = async (cwd) => {
|
|
|
81
85
|
|
|
82
86
|
if (!(await waitForBundler())) {
|
|
83
87
|
console.error(`\nā Timeout: Bundler did not respond.`);
|
|
84
|
-
rl.close();
|
|
85
88
|
return;
|
|
86
89
|
}
|
|
87
90
|
console.log(`ā
Bundler ready!`);
|
|
@@ -93,9 +96,9 @@ const runHostDevFlow = async (cwd) => {
|
|
|
93
96
|
} else if (choice === 'i') {
|
|
94
97
|
console.log(`š Launching iOS...`);
|
|
95
98
|
await runProcess('react-native', ['run-ios', '--no-packager'], cwd);
|
|
99
|
+
} else if (choice === 'b') {
|
|
100
|
+
console.log(`⨠Bundler is active.`);
|
|
96
101
|
}
|
|
97
|
-
|
|
98
|
-
rl.close();
|
|
99
102
|
};
|
|
100
103
|
|
|
101
104
|
module.exports = async (options) => {
|
|
@@ -164,7 +167,7 @@ module.exports = async (options) => {
|
|
|
164
167
|
|
|
165
168
|
if (isHost) {
|
|
166
169
|
await prepareNative(cwd, 'all');
|
|
167
|
-
await runHostDevFlow(cwd);
|
|
170
|
+
await runHostDevFlow(cwd, options);
|
|
168
171
|
} else {
|
|
169
172
|
console.error(chalk.red(`ā Error: Could not detect Host or Module context.`));
|
|
170
173
|
console.log(`š Try: esad dev <module-id>`);
|
package/src/cli/commands/init.js
CHANGED
|
@@ -75,7 +75,7 @@ export default {
|
|
|
75
75
|
console.log(chalk.green(`\nš ESAD Workspace Initialized successfully!`));
|
|
76
76
|
console.log(chalk.cyan(`\nš Next steps:`));
|
|
77
77
|
console.log(` 1. cd ${projectName}/${hostName}`);
|
|
78
|
-
console.log(` 2. esad
|
|
78
|
+
console.log(` 2. esad dev (to start Host)`);
|
|
79
79
|
console.log(` 3. esad dev (in a module folder to federate)\n`);
|
|
80
80
|
} catch (err) {
|
|
81
81
|
console.error(chalk.red(`\nā Failed to initialize workspace:`));
|
|
@@ -75,8 +75,15 @@ async function prepareNative(cwd, platform = 'android') {
|
|
|
75
75
|
if (!content.includes('project.ext.react')) {
|
|
76
76
|
const patch = `\nproject.ext.react = [\n bundleCommand: "repack-bundle",\n bundleConfig: "rspack.config.mjs"\n]\n\n`;
|
|
77
77
|
content = content.replace(/react \{/, `${patch}react {`);
|
|
78
|
+
|
|
79
|
+
// Force androidx.core version to avoid SDK 36 requirement conflict
|
|
80
|
+
if (!content.includes('androidx.core:core:')) {
|
|
81
|
+
const forcePatch = `\nconfigurations.all {\n resolutionStrategy {\n force 'androidx.core:core:1.15.0'\n force 'androidx.core:core-ktx:1.15.0'\n }\n}\n\n`;
|
|
82
|
+
content = forcePatch + content;
|
|
83
|
+
}
|
|
84
|
+
|
|
78
85
|
await fs.writeFile(buildGradlePath, content);
|
|
79
|
-
console.log(`ā
Patched android/app/build.gradle for Re.Pack.`);
|
|
86
|
+
console.log(`ā
Patched android/app/build.gradle for Re.Pack and AndroidX versions.`);
|
|
80
87
|
}
|
|
81
88
|
}
|
|
82
89
|
|
package/src/plugin/index.js
CHANGED
|
@@ -59,7 +59,7 @@ function withESAD(env, options) {
|
|
|
59
59
|
{
|
|
60
60
|
test: /\.[cm]?[jt]sx?$/,
|
|
61
61
|
include: [
|
|
62
|
-
/node_modules[\\/](react-native|@react-native|expo|expo-modules-core|@expo|react-navigation|@react-navigation|@unimodules|unimodules|native-base)/,
|
|
62
|
+
/node_modules[\\/](react-native|@react-native|expo|expo-modules-core|@expo|react-navigation|@react-navigation|@unimodules|unimodules|native-base|react-native-screens|react-native-reanimated)/,
|
|
63
63
|
],
|
|
64
64
|
type: 'javascript/auto',
|
|
65
65
|
resolve: { fullySpecified: false },
|
|
@@ -67,10 +67,10 @@ function withESAD(env, options) {
|
|
|
67
67
|
loader: 'babel-loader',
|
|
68
68
|
options: {
|
|
69
69
|
presets: [
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
],
|
|
70
|
+
'babel-preset-expo',
|
|
71
|
+
],
|
|
72
|
+
plugins: [
|
|
73
|
+
['@babel/plugin-transform-react-jsx', { runtime: 'automatic' }],
|
|
74
74
|
],
|
|
75
75
|
sourceType: 'unambiguous',
|
|
76
76
|
caller: { name: 'repack' },
|
|
@@ -80,11 +80,12 @@ function withESAD(env, options) {
|
|
|
80
80
|
{
|
|
81
81
|
test: /\.[cm]?[jt]sx?$/,
|
|
82
82
|
include: [dirname],
|
|
83
|
+
exclude: [/node_modules/],
|
|
83
84
|
type: 'javascript/auto',
|
|
84
85
|
use: {
|
|
85
86
|
loader: 'babel-loader',
|
|
86
87
|
options: {
|
|
87
|
-
presets: ['babel-preset
|
|
88
|
+
presets: ['@react-native/babel-preset'],
|
|
88
89
|
sourceType: 'unambiguous',
|
|
89
90
|
caller: { name: 'repack' },
|
|
90
91
|
},
|
package/src/cli/commands/host.js
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
const { runProcess } = require('../utils/process');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const fs = require('fs-extra');
|
|
4
|
-
const { getWorkspaceConfig } = require('../utils/config');
|
|
5
|
-
|
|
6
|
-
module.exports = async (subcommand) => {
|
|
7
|
-
let cwd = process.cwd();
|
|
8
|
-
let pkgPath = path.join(cwd, 'package.json');
|
|
9
|
-
|
|
10
|
-
const configObj = getWorkspaceConfig();
|
|
11
|
-
if (configObj) {
|
|
12
|
-
const workspaceRoot = configObj.root;
|
|
13
|
-
const { projectName } = configObj.data;
|
|
14
|
-
const hostDir = path.join(workspaceRoot, `${projectName}-host`);
|
|
15
|
-
|
|
16
|
-
if (fs.existsSync(hostDir)) {
|
|
17
|
-
cwd = hostDir;
|
|
18
|
-
pkgPath = path.join(cwd, 'package.json');
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (!fs.existsSync(pkgPath)) {
|
|
23
|
-
console.error(`ā Error: Call this command from inside the Host App or the Workspace Root.`);
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Handle direct platform commands
|
|
28
|
-
try {
|
|
29
|
-
if (subcommand === 'android') {
|
|
30
|
-
console.log(`š¤ Compiling and launching on Android...`);
|
|
31
|
-
await runProcess('react-native', ['run-android', '--no-packager'], cwd);
|
|
32
|
-
} else if (subcommand === 'ios') {
|
|
33
|
-
console.log(`š Compiling and launching on iOS...`);
|
|
34
|
-
await runProcess('react-native', ['run-ios', '--no-packager'], cwd);
|
|
35
|
-
} else {
|
|
36
|
-
console.log(`\nš” Tip: Use 'esad dev' for the interactive dev manager.`);
|
|
37
|
-
console.log(` Or use 'esad host android' / 'esad host ios' for direct runs.`);
|
|
38
|
-
}
|
|
39
|
-
} catch (err) {
|
|
40
|
-
console.error(`ā Error running host command: ${err.message}`);
|
|
41
|
-
}
|
|
42
|
-
};
|