@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 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 (Webpack)** and **React Native**.
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
- Creates a main project folder with a Host (Expo-ready) and a global configuration.
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
- Scaffolds a new mini-app correctly named and configured to join the Super App.
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. Create a Service Registry / CDN
24
- Sets up the backend registry used for dynamic routing and file hosting.
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 create-cdn
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. Development Mode (Real-time HMR)
30
- Starts the local packager and **automatically** notifies the Registry to bypass the CDN, so your Host App sees your local changes instantly.
30
+ ### 4. Deployment
31
+ Builds, zips, and uploads the module bundle to the configured CDN registry.
31
32
  ```bash
32
- npx esad dev --id module-rh --port 8081
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 (`esad/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 (`esad/client`)
57
- Share state across different modules and the Host instantly and reatively:
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
- ## 🏠 Host App Features (by Default)
61
+ ## 🏠 Template Features (Host & Module)
67
62
 
68
- When you run `esad init`, the generated Host App comes pre-configured with:
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
- - **🎨 NativeWind v4**: Utility-first styling ready to use with Tailwind CSS logic.
71
- - **🔐 Auth System**: Complete `AuthProvider` with persistent token storage using `expo-secure-store`.
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemoreira/esad",
3
- "version": "1.3.1",
3
+ "version": "1.3.3",
4
4
  "description": "Easy Super App Development - Zero-Config CLI and DevTools for React Native Module Federation",
5
5
  "main": "src/plugin/index.js",
6
6
  "types": "./src/plugin/index.d.ts",
@@ -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
- // Try to find workspace config for root-level execution
12
+ // Enforce Workspace Root
11
13
  const configObj = getWorkspaceConfig();
12
- if (configObj) {
13
- const workspaceRoot = path.dirname(configObj.path);
14
- const { projectName } = configObj.data;
15
-
16
- // If ID is provided, try to find that module/host folder
17
- if (options.id) {
18
- const targetDir = path.join(workspaceRoot, options.id);
19
- if (fs.existsSync(targetDir)) {
20
- cwd = targetDir;
21
- pkgPath = path.join(cwd, 'package.json');
22
- console.log(`📂 Auto-detected Project folder: ${path.relative(process.cwd(), targetDir)}`);
23
- }
24
- } else if (!fs.existsSync(pkgPath)) {
25
- // If no ID and no package.json in current dir, assume we want to deploy the host from root
26
- const hostDir = path.join(workspaceRoot, `${projectName}-host`);
27
- if (fs.existsSync(hostDir)) {
28
- cwd = hostDir;
29
- pkgPath = path.join(cwd, 'package.json');
30
- console.log(`📂 Auto-detected Host App folder: ${path.relative(process.cwd(), hostDir)}`);
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(`❌ Error: Call this command from inside a Project directory or the Workspace Root.`);
43
+ console.error(chalk.red(`❌ Erro: Arquivo package.json não encontrado em ${cwd}.`));
37
44
  process.exit(1);
38
45
  }
39
46
 
@@ -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
- // Try to find workspace config for root-level execution
13
+ // Enforce Workspace Root
12
14
  const configObj = getWorkspaceConfig();
13
- if (configObj) {
14
- const workspaceRoot = path.dirname(configObj.path);
15
- const { projectName } = configObj.data;
16
-
17
- if (options.id) {
18
- // Target a specific module
19
- const moduleDir = path.join(workspaceRoot, options.id);
20
- if (fs.existsSync(moduleDir)) {
21
- cwd = moduleDir;
22
- pkgPath = path.join(cwd, 'package.json');
23
- console.log(`📂 Auto-detected Module folder: ${path.relative(process.cwd(), moduleDir)}`);
24
- }
25
- } else {
26
- // Target host by default if in root
27
- const hostDir = path.join(workspaceRoot, `${projectName}-host`);
28
- if (fs.existsSync(hostDir)) {
29
- cwd = hostDir;
30
- pkgPath = path.join(cwd, 'package.json');
31
- console.log(`📂 Auto-detected Host App folder: ${path.relative(process.cwd(), hostDir)}`);
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
+ };