@codemoreira/esad 2.0.1-0 → 2.0.1-2

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/bin/esad.js CHANGED
@@ -7,14 +7,21 @@ program
7
7
  .version(pkg.version)
8
8
  .description('esad - Easy Super App Development Toolkit (V2)');
9
9
 
10
- // --- COMMAND: esad create [name] --type [host|module|cdn] ---
10
+ // --- COMMAND: esad init [name] ---
11
+ program
12
+ .command('init [name]')
13
+ .description('Creates the base of an ESAD project (Workspace and Host App)')
14
+ .action(async (name) => {
15
+ await require('../src/cli/commands/init')(name);
16
+ });
17
+
18
+ // --- COMMAND: esad create [name] --type [module|cdn] ---
11
19
  program
12
20
  .command('create [name]')
13
- .option('-t, --type <type>', 'Type of project: host, module, cdn', 'module')
14
- .description('Unified scaffolding for host apps, modules, and cdn registries')
21
+ .option('-t, --type <type>', 'Type of resource: module, cdn', 'module')
22
+ .description('Expands an existing workspace by scaffolding modules or a local cdn')
15
23
  .action(async (name, options) => {
16
24
  await require('../src/cli/commands/create')(name, options);
17
- process.exit(0);
18
25
  });
19
26
 
20
27
  // --- COMMAND: esad dev [moduleId] ---
@@ -37,7 +44,6 @@ program
37
44
  .action(async (id, options) => {
38
45
  const opts = { ...options, id: id || options.id };
39
46
  await require('../src/cli/commands/build')(opts);
40
- process.exit(0);
41
47
  });
42
48
 
43
49
  // --- COMMAND: esad deploy [id] ---
@@ -49,7 +55,6 @@ program
49
55
  .action(async (id, options) => {
50
56
  const opts = { ...options, id: id || options.id };
51
57
  await require('../src/cli/commands/deploy')(opts);
52
- process.exit(0);
53
58
  });
54
59
 
55
60
  // --- COMMAND: esad host <sub> ---
@@ -58,7 +63,6 @@ program
58
63
  .description('Manage host application (android, ios, login)')
59
64
  .action(async (sub) => {
60
65
  await require('../src/cli/commands/host')(sub);
61
- process.exit(0);
62
66
  });
63
67
 
64
68
  // --- COMMAND: esad doctor ---
@@ -67,7 +71,6 @@ program
67
71
  .description('Check environment for common issues')
68
72
  .action(async () => {
69
73
  await require('../src/cli/commands/doctor')();
70
- process.exit(0);
71
74
  });
72
75
 
73
76
  // --- COMMAND: esad link [id] ---
@@ -76,7 +79,6 @@ program
76
79
  .description('Optimize development via local filesystem linking')
77
80
  .action(async (id) => {
78
81
  await require('../src/cli/commands/link')(id);
79
- process.exit(0);
80
82
  });
81
83
 
82
84
  program.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemoreira/esad",
3
- "version": "2.0.1-0",
3
+ "version": "2.0.1-2",
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",
@@ -50,6 +50,7 @@
50
50
  "cross-spawn": "^7.0.3",
51
51
  "form-data": "^4.0.0",
52
52
  "fs-extra": "^11.2.0",
53
+ "jiti": "^2.6.1",
53
54
  "node-fetch": "^2.7.0",
54
55
  "process": "^0.11.10"
55
56
  },
@@ -58,4 +59,4 @@
58
59
  "@types/react": "^19.2.14",
59
60
  "@types/react-native": "^0.72.8"
60
61
  }
61
- }
62
+ }
@@ -38,7 +38,7 @@ module.exports = async (options) => {
38
38
  syncContextDownwards(configObj);
39
39
 
40
40
  try {
41
- const bundleOutput = path.join(cwd, 'build', 'index.bundle'); // Simplified path as per V2
41
+ const bundleOutput = path.join(cwd, 'build', platform, 'index.bundle');
42
42
  fs.ensureDirSync(path.dirname(bundleOutput));
43
43
 
44
44
  // Run Re.Pack production build
@@ -6,68 +6,6 @@ const { cloneTemplate, renameProject } = require('../utils/scaffold');
6
6
  const { getWorkspaceConfig } = require('../utils/config');
7
7
  const templatesConfig = require('../templates/templates.json');
8
8
 
9
- const initHost = async (projectName) => {
10
- const workspaceDir = path.join(process.cwd(), projectName);
11
- console.log(`\nšŸš€ Initializing ESAD Workspace: ${projectName}...\n`);
12
-
13
- fs.ensureDirSync(workspaceDir);
14
-
15
- const configPath = path.join(workspaceDir, 'esad.config.js');
16
- if (!fs.existsSync(configPath)) {
17
- const configTemplate = `/**
18
- * ESAD: Super App Configuration
19
- */
20
- export default {
21
- projectName: '${projectName}',
22
-
23
- // 1. Development Overrides
24
- // Managed automatically by 'esad dev'
25
- devMode: {},
26
-
27
- // 2. Programmable Deployment
28
- // Receives the compiled bundle.
29
- async deploy(bundle, { version, moduleId, options }) {
30
- console.log('šŸš€ Starting custom upload for ' + moduleId + '...');
31
- // return { status: 'mock_success', moduleId, version };
32
- }
33
- };
34
- `;
35
- fs.writeFileSync(configPath, configTemplate);
36
- console.log(`āœ… Generated programmable configuration: esad.config.js`);
37
- }
38
-
39
- const gitignorePath = path.join(workspaceDir, '.gitignore');
40
- if (!fs.existsSync(gitignorePath)) {
41
- const hostName = `${projectName}-host`;
42
- const gitignoreContent = `# ESAD Workspace Git Configuration\n` +
43
- `/*\n\n` +
44
- `!/${hostName}/\n` +
45
- `!/esad.config.js\n` +
46
- `!/.gitignore\n` +
47
- `\nnode_modules/\n`;
48
- fs.writeFileSync(gitignorePath, gitignoreContent);
49
- console.log(`āœ… Generated .gitignore`);
50
- }
51
-
52
- const hostName = `${projectName}-host`;
53
- const hostDir = path.join(workspaceDir, hostName);
54
-
55
- try {
56
- await cloneTemplate(templatesConfig.host, hostDir);
57
- await renameProject(hostDir, hostName);
58
-
59
- // Inject local context mock immediately to avoid crashes on fresh boot
60
- fs.writeJsonSync(path.join(hostDir, '.esad.context.json'), { projectName, devMode: {} }, { spaces: 2 });
61
-
62
- console.log(`\nšŸ“¦ Installing dependencies into host...`);
63
- await runProcess('npm', ['install'], { cwd: hostDir });
64
- console.log(`\nšŸŽ‰ ESAD Workspace Initialized!`);
65
- console.log(`-> cd ${projectName}/${hostName}\n-> esad host dev (to start Host)`);
66
- } catch (err) {
67
- console.error(`āŒ Failed to init Host:`, err.message);
68
- }
69
- };
70
-
71
9
  const createModule = async (moduleName) => {
72
10
  const configObj = getWorkspaceConfig();
73
11
  if (!configObj) {
@@ -138,11 +76,9 @@ module.exports = async (name, options) => {
138
76
  const type = options.type;
139
77
 
140
78
  if (type === 'host') {
141
- if (!name) {
142
- console.error(chalk.red('āŒ Error: Project name is required for host creation.'));
143
- process.exit(1);
144
- }
145
- return await initHost(name);
79
+ console.error(chalk.red('āŒ Error: The "host" type is no longer supported in create.'));
80
+ console.error(chalk.yellow('šŸ‘‰ Use "esad init <project-name>" instead to create a new workspace.'));
81
+ process.exit(1);
146
82
  }
147
83
 
148
84
  if (type === 'module') {
@@ -157,5 +93,5 @@ module.exports = async (name, options) => {
157
93
  return await createCdn(name);
158
94
  }
159
95
 
160
- console.error(chalk.red(`āŒ Unknown type: ${type}. Valid types are: host, module, cdn.`));
96
+ console.error(chalk.red(`āŒ Unknown type: ${type}. Valid types are: module, cdn.`));
161
97
  };
@@ -0,0 +1,75 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+ const chalk = require('chalk');
4
+ const { runProcess } = require('../utils/process');
5
+ const { cloneTemplate, renameProject } = require('../utils/scaffold');
6
+ const templatesConfig = require('../templates/templates.json');
7
+
8
+ module.exports = async (projectName) => {
9
+ if (!projectName) {
10
+ console.error(chalk.red('āŒ Error: Project name is required to initialize a new ESAD workspace.'));
11
+ console.error(chalk.yellow('šŸ‘‰ Usage: esad init <project-name>'));
12
+ process.exit(1);
13
+ }
14
+
15
+ const workspaceDir = path.join(process.cwd(), projectName);
16
+ console.log(`\nšŸš€ Initializing ESAD Workspace: ${projectName}...\n`);
17
+
18
+ fs.ensureDirSync(workspaceDir);
19
+
20
+ const configPath = path.join(workspaceDir, 'esad.config.js');
21
+ if (!fs.existsSync(configPath)) {
22
+ const configTemplate = `/**
23
+ * ESAD: Super App Configuration
24
+ */
25
+ export default {
26
+ projectName: '${projectName}',
27
+
28
+ // 1. Development Overrides
29
+ // Managed automatically by 'esad dev'
30
+ devMode: {},
31
+
32
+ // 2. Programmable Deployment
33
+ // Receives the compiled bundle.
34
+ async deploy(bundle, { version, moduleId, options }) {
35
+ console.log('šŸš€ Starting custom upload for ' + moduleId + '...');
36
+ // return { status: 'mock_success', moduleId, version };
37
+ }
38
+ };
39
+ `;
40
+ fs.writeFileSync(configPath, configTemplate);
41
+ console.log(`āœ… Generated programmable configuration: esad.config.js`);
42
+ }
43
+
44
+ const gitignorePath = path.join(workspaceDir, '.gitignore');
45
+ if (!fs.existsSync(gitignorePath)) {
46
+ const hostName = `${projectName}-host`;
47
+ const gitignoreContent = `# ESAD Workspace Git Configuration\n` +
48
+ `# Note: We use a whitelist approach because modules should be kept in separate repositories.\n` +
49
+ `/*\n\n` +
50
+ `!/${hostName}/\n` +
51
+ `!/esad.config.js\n` +
52
+ `!/.gitignore\n` +
53
+ `\nnode_modules/\n`;
54
+ fs.writeFileSync(gitignorePath, gitignoreContent);
55
+ console.log(`āœ… Generated .gitignore`);
56
+ }
57
+
58
+ const hostName = `${projectName}-host`;
59
+ const hostDir = path.join(workspaceDir, hostName);
60
+
61
+ try {
62
+ await cloneTemplate(templatesConfig.host, hostDir);
63
+ await renameProject(hostDir, hostName);
64
+
65
+ // Inject local context mock immediately to avoid crashes on fresh boot
66
+ fs.writeJsonSync(path.join(hostDir, '.esad.context.json'), { projectName, devMode: {} }, { spaces: 2 });
67
+
68
+ console.log(`\nšŸ“¦ Installing dependencies into host...`);
69
+ await runProcess('npm', ['install'], { cwd: hostDir });
70
+ console.log(`\nšŸŽ‰ ESAD Workspace Initialized!`);
71
+ console.log(`-> cd ${projectName}/${hostName}\n-> esad host dev (to start Host)`);
72
+ } catch (err) {
73
+ console.error(`āŒ Failed to init Host:`, err.message);
74
+ }
75
+ };
@@ -20,7 +20,7 @@ const getWorkspaceConfig = () => {
20
20
 
21
21
  try {
22
22
  const jiti = createJiti(__filename);
23
- const configModule = jiti.import(configPath);
23
+ const configModule = jiti(configPath);
24
24
  // jiti.import returns a promise for async imports if needed, but for esad.config.js
25
25
  // we expect a sync structure or we resolve it.
26
26
  // However, jiti v2 import is async.
@@ -4,63 +4,69 @@ const path = require('path');
4
4
  const fs = require('fs-extra');
5
5
 
6
6
  const runProcess = (cmd, args, cwd = process.cwd()) => {
7
- return new Promise((resolve, reject) => {
8
- let finished = false;
9
- let started = false;
7
+ let childRef;
8
+ const promise = new Promise((resolve, reject) => {
9
+ let finished = false;
10
+ let started = false;
10
11
 
11
- const finalize = (fn, arg) => {
12
- if (finished) return;
13
- finished = true;
14
- fn(arg);
15
- };
12
+ const finalize = (fn, arg) => {
13
+ if (finished) return;
14
+ finished = true;
15
+ fn(arg);
16
+ };
16
17
 
17
- const isWin = process.platform === 'win32';
18
- const localBinPath = path.join(cwd, 'node_modules', '.bin', isWin ? `${cmd}.cmd` : cmd);
19
-
20
- let command = cmd;
21
- let finalArgs = args;
22
- let useNativeSpawn = false;
18
+ const isWin = process.platform === 'win32';
19
+ const localBin = isWin ? `${cmd}.cmd` : cmd;
20
+ const localBinPath = path.join(cwd, 'node_modules', '.bin', localBin);
21
+
22
+ let command = cmd;
23
+ let finalArgs = args;
24
+ let useNativeSpawn = false;
23
25
 
24
- if (fs.existsSync(localBinPath)) {
25
- command = isWin ? `node_modules\\.bin\\${cmd}.cmd` : `./node_modules/.bin/${cmd}`;
26
- useNativeSpawn = isWin; // Use native spawn on Windows for local binaries to avoid cross-spawn issues
27
- } else {
28
- command = isWin ? 'npx.cmd' : 'npx';
29
- finalArgs = [cmd, ...args];
30
- }
31
-
32
- console.log(`[ESAD] Resolved Command: ${command} (CWD: ${cwd})`);
26
+ if (fs.existsSync(localBinPath)) {
27
+ // 1. Priority: Local node_modules/.bin (fastest & consistent)
28
+ command = isWin ? `node_modules\\.bin\\${localBin}` : `./node_modules/.bin/${localBin}`;
29
+ useNativeSpawn = isWin;
30
+ } else {
31
+ // 2. Fallback: System Path (git, npm, npx, etc)
32
+ // cross-spawn handles .cmd/.exe resolution automatically
33
+ command = cmd;
34
+ finalArgs = args;
35
+ useNativeSpawn = false;
36
+ }
37
+
38
+ console.log(`[ESAD] Resolved Command: ${command} (CWD: ${cwd})`);
33
39
 
34
- // Mark as started after a short delay
35
- setTimeout(() => { started = true; }, 2000);
40
+ setTimeout(() => { started = true; }, 2000);
36
41
 
37
- const spawnFn = useNativeSpawn ? nativeSpawn : spawn;
38
- const child = spawnFn(command, finalArgs, {
39
- stdio: 'inherit',
40
- cwd,
41
- shell: true
42
- });
42
+ const spawnFn = useNativeSpawn ? nativeSpawn : spawn;
43
+ childRef = spawnFn(command, finalArgs, {
44
+ stdio: 'inherit',
45
+ cwd,
46
+ shell: true
47
+ });
43
48
 
44
- child.on('error', err => {
45
- // If the process already "started" (ran for > 2s), we ignore early-spawn errors
46
- // as they are likely shell artifacts from the process exiting.
47
- if (started && err.code === 'ENOENT') {
48
- console.warn(`[ESAD] Warning: Late ENOENT ignored for ${cmd}.`);
49
- return;
50
- }
49
+ childRef.on('error', err => {
50
+ if (started && err.code === 'ENOENT') {
51
+ console.warn(`[ESAD] Warning: Late ENOENT ignored for ${cmd}.`);
52
+ return;
53
+ }
54
+ finalize(reject, err);
55
+ });
51
56
 
52
- console.error(`[ESAD] Process Error: ${err.code} - ${err.message}`);
53
- finalize(reject, new Error(`Failed to start ${cmd}: ${err.message}`));
57
+ childRef.on('close', code => {
58
+ if (code !== 0) {
59
+ finalize(reject, new Error(`Process ${cmd} exited with code ${code}`));
60
+ } else {
61
+ finalize(resolve);
62
+ }
63
+ });
54
64
  });
55
65
 
56
- child.on('close', code => {
57
- if (code !== 0) {
58
- finalize(reject, new Error(`Process ${cmd} exited with code ${code}`));
59
- } else {
60
- finalize(resolve);
61
- }
62
- });
63
- });
64
- };
66
+ promise.child = childRef;
67
+ promise.kill = (signal) => childRef && childRef.kill(signal);
68
+
69
+ return promise;
70
+ };
65
71
 
66
72
  module.exports = { runProcess };
@@ -51,13 +51,14 @@ const clearAllDevMode = (configPath) => {
51
51
  fs.writeFileSync(configPath, content);
52
52
  };
53
53
 
54
+ const { createJiti } = require('jiti');
55
+
54
56
  const syncContextDownwards = (configObj) => {
55
57
  if (!fs.existsSync(configObj.path)) return;
56
58
  const configDir = path.dirname(configObj.path);
57
59
 
58
- // Clear cache to read fresh state
59
- delete require.cache[require.resolve(configObj.path)];
60
- let configContent = require(configObj.path);
60
+ const jiti = createJiti(__filename);
61
+ let configContent = jiti(configObj.path);
61
62
  if (configContent.default) configContent = configContent.default;
62
63
 
63
64
  const exportData = {