@codemoreira/esad 1.3.1 → 1.3.3
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 +32 -29
- package/package.json +1 -1
- package/src/cli/commands/deploy.js +29 -22
- package/src/cli/commands/dev.js +28 -21
- package/src/cli/utils/resolution.js +55 -0
package/README.md
CHANGED
|
@@ -1,51 +1,46 @@
|
|
|
1
1
|
# ESAD (Easy Super App Development) 🚀
|
|
2
2
|
|
|
3
|
-
Zero-Config CLI and DevTools for React Native Module Federation.
|
|
3
|
+
Zero-Config CLI and DevTools for React Native Module Federation + Expo.
|
|
4
4
|
|
|
5
|
-
ESAD is a unified toolkit designed to abstract all the complexity from Super App development using **Re.Pack (
|
|
5
|
+
ESAD is a unified toolkit designed to abstract all the complexity from Super App development using **Re.Pack (Rspack)** and **Expo**.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
## 🏗️ Commands
|
|
9
|
+
## 🏗️ CLI Commands
|
|
10
10
|
|
|
11
11
|
### 1. Initialize a Workspace
|
|
12
|
-
|
|
12
|
+
Clones the official Host Template and sets up a global workspace configuration.
|
|
13
13
|
```bash
|
|
14
14
|
npx @codemoreira/esad init my-project
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
### 2. Create a Federated Module
|
|
18
|
-
|
|
18
|
+
Clones the official Module Template and correctly configures it to join the Super App.
|
|
19
19
|
```bash
|
|
20
20
|
npx esad create-module module-rh
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
### 3.
|
|
24
|
-
|
|
23
|
+
### 3. Development Mode
|
|
24
|
+
Starts the local Rspack server and **automatically** prepares native folders (`android/ios`) with necessary Re.Pack patches (Gradle, Entry Points).
|
|
25
25
|
```bash
|
|
26
|
-
npx esad
|
|
26
|
+
npx esad host dev # To run the Host
|
|
27
|
+
npx esad dev --id module-rh --port 9000 # To run a Module
|
|
27
28
|
```
|
|
28
29
|
|
|
29
|
-
### 4.
|
|
30
|
-
|
|
30
|
+
### 4. Deployment
|
|
31
|
+
Builds, zips, and uploads the module bundle to the configured CDN registry.
|
|
31
32
|
```bash
|
|
32
|
-
npx esad
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
### 5. Deployment
|
|
36
|
-
Builds, zips, and uploads the module bundle to the configured CDN endpoint.
|
|
37
|
-
```bash
|
|
38
|
-
npx esad deploy --id module-rh --version 1.0.0 --entry index.bundle
|
|
33
|
+
npx esad deploy --id module-rh --version 1.0.0
|
|
39
34
|
```
|
|
40
35
|
|
|
41
36
|
---
|
|
42
37
|
|
|
43
38
|
## 🛠️ Library Usage
|
|
44
39
|
|
|
45
|
-
### 🎨 Bundler Plugin (
|
|
40
|
+
### 🎨 Bundler Plugin (`@codemoreira/esad/plugin`)
|
|
46
41
|
In your `rspack.config.mjs`, simplify everything:
|
|
47
42
|
```javascript
|
|
48
|
-
import { withESAD } from 'esad/plugin';
|
|
43
|
+
import { withESAD } from '@codemoreira/esad/plugin';
|
|
49
44
|
|
|
50
45
|
export default withESAD({
|
|
51
46
|
type: 'module', // or 'host'
|
|
@@ -53,22 +48,30 @@ export default withESAD({
|
|
|
53
48
|
});
|
|
54
49
|
```
|
|
55
50
|
|
|
56
|
-
### ⚡ Global State Hook (
|
|
57
|
-
Share state across different modules and the Host instantly and
|
|
51
|
+
### ⚡ Global State Hook (`@codemoreira/esad/client`)
|
|
52
|
+
Share state across different modules and the Host instantly and reactively:
|
|
58
53
|
```javascript
|
|
59
|
-
import { useESADState } from 'esad/client';
|
|
54
|
+
import { useESADState } from '@codemoreira/esad/client';
|
|
60
55
|
|
|
61
56
|
const [token, setToken] = useESADState('auth_token');
|
|
62
57
|
```
|
|
63
58
|
|
|
64
59
|
---
|
|
65
60
|
|
|
66
|
-
## 🏠
|
|
61
|
+
## 🏠 Template Features (Host & Module)
|
|
67
62
|
|
|
68
|
-
|
|
63
|
+
ESAD now uses a **Template-Based Scaffolding** system. Creating a project via CLI clones:
|
|
64
|
+
- [esad-template-host](https://github.com/CodeMoreira/esad-template-host)
|
|
65
|
+
- [esad-template-module](https://github.com/CodeMoreira/esad-template-module)
|
|
66
|
+
|
|
67
|
+
**Features included by default:**
|
|
68
|
+
- **🚀 Rspack + Re.Pack**: Blazing fast builds with Module Federation v2.
|
|
69
|
+
- **🎨 NativeWind v4**: Utility-first styling with Tailwind CSS logic.
|
|
70
|
+
- **🔐 Auth System**: Complete `AuthProvider` with `expo-secure-store`.
|
|
71
|
+
- **🛤️ Protected Routes**: Automatic redirection logic.
|
|
72
|
+
- **📦 Module Loader**: Robust dynamic remote loading via ESAD Registry.
|
|
73
|
+
|
|
74
|
+
---
|
|
69
75
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
- **🛤️ Protected Routes**: Automatic redirection between `login` and `(protected)` groups based on auth state.
|
|
73
|
-
- **📦 Module Loader**: A robust `lib/moduleLoader.ts` to fetch and initialize federated modules dynamically.
|
|
74
|
-
- **📱 Premium UI**: A clean, modern starting point for your Super App dashboard.
|
|
76
|
+
## 🎨 Architecture & Workflow
|
|
77
|
+
For a detailed view of the system's architecture and development cycles, see [ESAD_ARCHITECTURE.md](./ESAD_ARCHITECTURE.md).
|
package/package.json
CHANGED
|
@@ -1,39 +1,46 @@
|
|
|
1
1
|
const fs = require('fs-extra');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const AdmZip = require('adm-zip');
|
|
4
|
+
const chalk = require('chalk');
|
|
4
5
|
const { getWorkspaceConfig } = require('../utils/config');
|
|
6
|
+
const { resolveProjectDir, listAvailableModules } = require('../utils/resolution');
|
|
5
7
|
|
|
6
8
|
module.exports = async (options) => {
|
|
7
9
|
let cwd = process.cwd();
|
|
8
10
|
let pkgPath = path.join(cwd, 'package.json');
|
|
9
11
|
|
|
10
|
-
//
|
|
12
|
+
// Enforce Workspace Root
|
|
11
13
|
const configObj = getWorkspaceConfig();
|
|
12
|
-
if (configObj) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
14
|
+
if (!configObj) {
|
|
15
|
+
console.error(chalk.red(`❌ Erro: Comando deve ser executado na raiz do projeto (esad.config.json não encontrado).`));
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const workspaceRoot = path.dirname(configObj.path);
|
|
20
|
+
const { projectName } = configObj.data;
|
|
21
|
+
|
|
22
|
+
if (options.id) {
|
|
23
|
+
const targetDir = resolveProjectDir(options.id, configObj);
|
|
24
|
+
if (!targetDir) {
|
|
25
|
+
console.error(chalk.red(`\n❌ Erro: Não foi encontrado o módulo: ${options.id}`));
|
|
26
|
+
listAvailableModules(configObj);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
cwd = targetDir;
|
|
30
|
+
pkgPath = path.join(cwd, 'package.json');
|
|
31
|
+
console.log(chalk.green(`📂 Módulo detectado para Deploy: ${path.relative(workspaceRoot, cwd)}`));
|
|
32
|
+
} else {
|
|
33
|
+
// Target host by default if in root
|
|
34
|
+
const hostDir = path.join(workspaceRoot, `${projectName}-host`);
|
|
35
|
+
if (fs.existsSync(hostDir)) {
|
|
36
|
+
cwd = hostDir;
|
|
37
|
+
pkgPath = path.join(cwd, 'package.json');
|
|
38
|
+
console.log(chalk.green(`📂 Host detectado para Deploy: ${path.relative(workspaceRoot, cwd)}`));
|
|
32
39
|
}
|
|
33
40
|
}
|
|
34
41
|
|
|
35
42
|
if (!fs.existsSync(pkgPath)) {
|
|
36
|
-
console.error(`❌
|
|
43
|
+
console.error(chalk.red(`❌ Erro: Arquivo package.json não encontrado em ${cwd}.`));
|
|
37
44
|
process.exit(1);
|
|
38
45
|
}
|
|
39
46
|
|
package/src/cli/commands/dev.js
CHANGED
|
@@ -2,34 +2,41 @@ const { spawn } = require('cross-spawn');
|
|
|
2
2
|
const { getWorkspaceConfig } = require('../utils/config');
|
|
3
3
|
const fs = require('fs-extra');
|
|
4
4
|
const path = require('path');
|
|
5
|
+
const chalk = require('chalk');
|
|
5
6
|
const { prepareNative } = require('../utils/scaffold');
|
|
7
|
+
const { resolveProjectDir, listAvailableModules } = require('../utils/resolution');
|
|
6
8
|
|
|
7
9
|
module.exports = async (options) => {
|
|
8
10
|
let cwd = process.cwd();
|
|
9
11
|
let pkgPath = path.join(cwd, 'package.json');
|
|
10
12
|
|
|
11
|
-
//
|
|
13
|
+
// Enforce Workspace Root
|
|
12
14
|
const configObj = getWorkspaceConfig();
|
|
13
|
-
if (configObj) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
15
|
+
if (!configObj) {
|
|
16
|
+
console.error(chalk.red(`❌ Erro: Comando deve ser executado na raiz do projeto (esad.config.json não encontrado).`));
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const workspaceRoot = path.dirname(configObj.path);
|
|
21
|
+
const { projectName } = configObj.data;
|
|
22
|
+
|
|
23
|
+
if (options.id) {
|
|
24
|
+
const targetDir = resolveProjectDir(options.id, configObj);
|
|
25
|
+
if (!targetDir) {
|
|
26
|
+
console.error(chalk.red(`\n❌ Erro: Não foi encontrado o módulo: ${options.id}`));
|
|
27
|
+
listAvailableModules(configObj);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
cwd = targetDir;
|
|
31
|
+
pkgPath = path.join(cwd, 'package.json');
|
|
32
|
+
console.log(chalk.green(`📂 Módulo detectado: ${path.relative(workspaceRoot, cwd)}`));
|
|
33
|
+
} else {
|
|
34
|
+
// Target host by default if in root
|
|
35
|
+
const hostDir = path.join(workspaceRoot, `${projectName}-host`);
|
|
36
|
+
if (fs.existsSync(hostDir)) {
|
|
37
|
+
cwd = hostDir;
|
|
38
|
+
pkgPath = path.join(cwd, 'package.json');
|
|
39
|
+
console.log(chalk.green(`📂 Host detectado: ${path.relative(workspaceRoot, cwd)}`));
|
|
33
40
|
}
|
|
34
41
|
}
|
|
35
42
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Resolves a project directory from a given ID, with support for prefixed names and auto-detection.
|
|
7
|
+
* @param {string} id The provided project ID
|
|
8
|
+
* @param {Object} configObj The workspace configuration object
|
|
9
|
+
* @returns {string|null} The absolute path to the project directory, or null if not found.
|
|
10
|
+
*/
|
|
11
|
+
function resolveProjectDir(id, configObj) {
|
|
12
|
+
if (!configObj) return null;
|
|
13
|
+
|
|
14
|
+
const workspaceRoot = path.dirname(configObj.path);
|
|
15
|
+
const { projectName } = configObj.data;
|
|
16
|
+
|
|
17
|
+
// 1. Try exact match
|
|
18
|
+
let targetDir = path.join(workspaceRoot, id);
|
|
19
|
+
if (fs.existsSync(targetDir) && fs.statSync(targetDir).isDirectory()) {
|
|
20
|
+
return targetDir;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// 2. Try prefixed match (e.g., my-app-module-name)
|
|
24
|
+
targetDir = path.join(workspaceRoot, `${projectName}-${id}`);
|
|
25
|
+
if (fs.existsSync(targetDir) && fs.statSync(targetDir).isDirectory()) {
|
|
26
|
+
return targetDir;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Lists all available modules in the workspace, stripping the project prefix.
|
|
34
|
+
* @param {Object} configObj
|
|
35
|
+
*/
|
|
36
|
+
function listAvailableModules(configObj) {
|
|
37
|
+
const workspaceRoot = path.dirname(configObj.path);
|
|
38
|
+
const { projectName } = configObj.data;
|
|
39
|
+
|
|
40
|
+
const entries = fs.readdirSync(workspaceRoot, { withFileTypes: true });
|
|
41
|
+
const modules = entries
|
|
42
|
+
.filter(e => e.isDirectory() && e.name.startsWith(projectName) && !e.name.endsWith('-host') && !e.name.endsWith('-cdn'))
|
|
43
|
+
.map(e => {
|
|
44
|
+
const name = e.name.replace(`${projectName}-`, '');
|
|
45
|
+
return name;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
console.log(chalk.yellow('\nMódulos disponíveis:'));
|
|
49
|
+
modules.forEach(m => console.log(chalk.blue(`- ${m}`)));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = {
|
|
53
|
+
resolveProjectDir,
|
|
54
|
+
listAvailableModules
|
|
55
|
+
};
|