@codemoreira/esad 2.0.0-rc.2 โ†’ 2.0.1-1

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.0-rc.2",
3
+ "version": "2.0.1-1",
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
  },
@@ -4,7 +4,7 @@ const fs = require('fs-extra');
4
4
  const chalk = require('chalk');
5
5
  const { getWorkspaceConfig } = require('../utils/config');
6
6
  const { resolveProjectDir } = require('../utils/resolution');
7
- const { clearAllDevMode } = require('../utils/transformer');
7
+ const { clearAllDevMode, syncContextDownwards } = require('../utils/transformer');
8
8
 
9
9
  module.exports = async (options) => {
10
10
  const configObj = getWorkspaceConfig();
@@ -35,9 +35,10 @@ module.exports = async (options) => {
35
35
  // 1. CLEANUP CONFIG (Avoid shipping local dev URLs)
36
36
  console.log(chalk.gray(`๐Ÿงน Cleaning up devMode mappings in esad.config.js...`));
37
37
  clearAllDevMode(configObj.path);
38
+ syncContextDownwards(configObj);
38
39
 
39
40
  try {
40
- const bundleOutput = path.join(cwd, 'build', 'index.bundle'); // Simplified path as per V2
41
+ const bundleOutput = path.join(cwd, 'build', platform, 'index.bundle');
41
42
  fs.ensureDirSync(path.dirname(bundleOutput));
42
43
 
43
44
  // Run Re.Pack production build
@@ -6,64 +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
- console.log(`\n๐Ÿ“ฆ Installing dependencies into host...`);
59
- await runProcess('npm', ['install'], { cwd: hostDir });
60
- console.log(`\n๐ŸŽ‰ ESAD Workspace Initialized!`);
61
- console.log(`-> cd ${projectName}/${hostName}\n-> esad host dev (to start Host)`);
62
- } catch (err) {
63
- console.error(`โŒ Failed to init Host:`, err.message);
64
- }
65
- };
66
-
67
9
  const createModule = async (moduleName) => {
68
10
  const configObj = getWorkspaceConfig();
69
11
  if (!configObj) {
@@ -85,6 +27,10 @@ const createModule = async (moduleName) => {
85
27
  try {
86
28
  await cloneTemplate(templatesConfig.module, targetDir);
87
29
  await renameProject(targetDir, finalModuleName);
30
+
31
+ // Inject local context mock immediately
32
+ fs.writeJsonSync(path.join(targetDir, '.esad.context.json'), { projectName, devMode: {} }, { spaces: 2 });
33
+
88
34
  console.log(`\n๐Ÿ“ฆ Installing dependencies...`);
89
35
  await runProcess('npm', ['install'], { cwd: targetDir });
90
36
  console.log(`\n๐ŸŽ‰ Module ${finalModuleName} is ready!`);
@@ -130,11 +76,9 @@ module.exports = async (name, options) => {
130
76
  const type = options.type;
131
77
 
132
78
  if (type === 'host') {
133
- if (!name) {
134
- console.error(chalk.red('โŒ Error: Project name is required for host creation.'));
135
- process.exit(1);
136
- }
137
- 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);
138
82
  }
139
83
 
140
84
  if (type === 'module') {
@@ -149,5 +93,5 @@ module.exports = async (name, options) => {
149
93
  return await createCdn(name);
150
94
  }
151
95
 
152
- 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.`));
153
97
  };
@@ -45,13 +45,14 @@ module.exports = async (options) => {
45
45
  return;
46
46
  }
47
47
 
48
- const { updateDevMode, removeDevMode } = require('../utils/transformer');
48
+ const { updateDevMode, removeDevMode, syncContextDownwards } = require('../utils/transformer');
49
49
 
50
50
  console.log(`\nโšก Starting ESAD Dev Server for ${chalk.cyan(moduleId)} on port ${port}...\n`);
51
51
 
52
52
  // Automate devMode update in esad.config.js
53
53
  const localBundleUrl = `http://localhost:${port}/index.bundle`;
54
54
  updateDevMode(configObj.path, moduleId, localBundleUrl);
55
+ syncContextDownwards(configObj);
55
56
  console.log(chalk.gray(`๐Ÿ“ก Mode: Module Dev. Host configured to load ${moduleId} from ${localBundleUrl}`));
56
57
 
57
58
  const proc = runProcess('npx', ['react-native', 'webpack-start', '--port', port], { cwd });
@@ -59,6 +60,7 @@ module.exports = async (options) => {
59
60
  const shutdown = async () => {
60
61
  console.log(`\n๐Ÿ›‘ Stopping ESAD Dev Server and reverting config...`);
61
62
  removeDevMode(configObj.path, moduleId);
63
+ syncContextDownwards(configObj);
62
64
  if (proc.kill) proc.kill();
63
65
  process.exit(0);
64
66
  };
@@ -32,10 +32,10 @@ module.exports = async () => {
32
32
  const envPath = path.join(process.cwd(), '.env');
33
33
  if (fs.existsSync(envPath)) {
34
34
  const envContent = fs.readFileSync(envPath, 'utf8');
35
- if (envContent.includes('registryUrl')) {
36
- console.log(`${chalk.green('โœ”')} registryUrl found in .env`);
35
+ if (envContent.includes('EXPO_PUBLIC_REGISTRY_URL')) {
36
+ console.log(`${chalk.green('โœ”')} EXPO_PUBLIC_REGISTRY_URL found in .env`);
37
37
  } else {
38
- console.warn(`${chalk.yellow('โš ')} Warning: registryUrl not found in .env (Host might fail to resolve modules).`);
38
+ console.warn(`${chalk.yellow('โš ')} Warning: EXPO_PUBLIC_REGISTRY_URL not found in .env (Host might fail to resolve modules).`);
39
39
  warnings++;
40
40
  }
41
41
  } else {
@@ -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,67 @@ 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
+ const promise = new Promise((resolve, reject) => {
8
+ let finished = false;
9
+ let started = false;
10
10
 
11
- const finalize = (fn, arg) => {
12
- if (finished) return;
13
- finished = true;
14
- fn(arg);
15
- };
11
+ const finalize = (fn, arg) => {
12
+ if (finished) return;
13
+ finished = true;
14
+ fn(arg);
15
+ };
16
16
 
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;
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;
23
23
 
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})`);
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})`);
33
33
 
34
- // Mark as started after a short delay
35
- setTimeout(() => { started = true; }, 2000);
34
+ // Mark as started after a short delay
35
+ setTimeout(() => { started = true; }, 2000);
36
36
 
37
- const spawnFn = useNativeSpawn ? nativeSpawn : spawn;
38
- const child = spawnFn(command, finalArgs, {
39
- stdio: 'inherit',
40
- cwd,
41
- shell: true
42
- });
37
+ const spawnFn = useNativeSpawn ? nativeSpawn : spawn;
38
+ const child = spawnFn(command, finalArgs, {
39
+ stdio: 'inherit',
40
+ cwd,
41
+ shell: true
42
+ });
43
43
 
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
- }
44
+ // Attach child to promise for control
45
+ promise.child = child;
46
+ promise.kill = (signal) => child.kill(signal);
51
47
 
52
- console.error(`[ESAD] Process Error: ${err.code} - ${err.message}`);
53
- finalize(reject, new Error(`Failed to start ${cmd}: ${err.message}`));
54
- });
48
+ child.on('error', err => {
49
+ if (started && err.code === 'ENOENT') {
50
+ console.warn(`[ESAD] Warning: Late ENOENT ignored for ${cmd}.`);
51
+ return;
52
+ }
55
53
 
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
- }
54
+ console.error(`[ESAD] Process Error: ${err.code} - ${err.message}`);
55
+ finalize(reject, new Error(`Failed to start ${cmd}: ${err.message}`));
56
+ });
57
+
58
+ child.on('close', code => {
59
+ if (code !== 0) {
60
+ finalize(reject, new Error(`Process ${cmd} exited with code ${code}`));
61
+ } else {
62
+ finalize(resolve);
63
+ }
64
+ });
62
65
  });
63
- });
64
- };
66
+
67
+ return promise;
68
+ };
65
69
 
66
70
  module.exports = { runProcess };
@@ -51,4 +51,33 @@ const clearAllDevMode = (configPath) => {
51
51
  fs.writeFileSync(configPath, content);
52
52
  };
53
53
 
54
- module.exports = { updateDevMode, removeDevMode, clearAllDevMode };
54
+ const { createJiti } = require('jiti');
55
+
56
+ const syncContextDownwards = (configObj) => {
57
+ if (!fs.existsSync(configObj.path)) return;
58
+ const configDir = path.dirname(configObj.path);
59
+
60
+ const jiti = createJiti(__filename);
61
+ let configContent = jiti(configObj.path);
62
+ if (configContent.default) configContent = configContent.default;
63
+
64
+ const exportData = {
65
+ projectName: configContent.projectName || 'esad-workspace',
66
+ devMode: configContent.devMode || {}
67
+ };
68
+
69
+ try {
70
+ const children = fs.readdirSync(configDir, { withFileTypes: true })
71
+ .filter(dirent => dirent.isDirectory())
72
+ .map(dirent => path.join(configDir, dirent.name))
73
+ .filter(dir => fs.existsSync(path.join(dir, 'package.json')));
74
+
75
+ for (const childDir of children) {
76
+ fs.writeJsonSync(path.join(childDir, '.esad.context.json'), exportData, { spaces: 2 });
77
+ }
78
+ } catch (err) {
79
+ console.error('Failed to sync context downwards:', err.message);
80
+ }
81
+ };
82
+
83
+ module.exports = { updateDevMode, removeDevMode, clearAllDevMode, syncContextDownwards };