@codemoreira/esad 1.4.6-9 → 2.0.0-rc.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.
@@ -1,118 +1,80 @@
1
- const fs = require('fs-extra');
2
- const path = require('path');
3
- const AdmZip = require('adm-zip');
4
- const chalk = require('chalk');
5
- const { getWorkspaceConfig, syncHostConfig } = require('../utils/config');
6
- const { resolveModuleMetadata, listAvailableModules } = require('../utils/resolution');
7
- const { prepareNative } = require('../utils/scaffold');
8
-
9
- module.exports = async (options) => {
10
- let cwd = process.cwd();
11
- let pkgPath = path.join(cwd, 'package.json');
12
-
13
- // Enforce Workspace Root
14
- const configObj = getWorkspaceConfig();
15
- if (!configObj) {
16
- console.error(chalk.red(`❌ Error: Call this command from the project root (esad.config.json not found).`));
17
- process.exit(1);
18
- }
19
-
20
- const workspaceRoot = path.dirname(configObj.path);
21
- const { projectName } = configObj.data;
22
-
23
- syncHostConfig(configObj);
24
-
25
- let moduleId = options.id;
26
-
27
- if (moduleId) {
28
- const meta = resolveModuleMetadata(moduleId, configObj);
29
- if (!meta) {
30
- console.error(chalk.red(`\n❌ Error: Module not found: ${moduleId}`));
31
- listAvailableModules(configObj);
32
- process.exit(1);
33
- }
34
- cwd = meta.path;
35
- moduleId = meta.id; // Correct fully qualified ID
36
- pkgPath = path.join(cwd, 'package.json');
37
- console.log(`📂 Module detected for Deploy: ${path.basename(cwd)}`);
38
- } else {
39
- // Target host by default if in root
40
- const hostDir = path.join(workspaceRoot, `${projectName}-host`);
41
- if (fs.existsSync(hostDir)) {
42
- cwd = hostDir;
43
- pkgPath = path.join(cwd, 'package.json');
44
- console.log(chalk.green(`📂 Host detected for Deploy: ${path.basename(cwd)}`));
45
- }
46
- }
47
-
48
- if (!fs.existsSync(pkgPath)) {
49
- console.error(chalk.red(`❌ Error: package.json not found in ${cwd}.`));
50
- process.exit(1);
51
- }
52
-
53
- const pkg = fs.readJsonSync(pkgPath);
54
- moduleId = moduleId || pkg.name;
55
- const version = options.version || pkg.version;
56
- const entry = options.entry || 'index.bundle';
57
-
58
- // Ensure Native Folders are present (useful for consistency)
59
- await prepareNative(cwd, 'all');
60
-
61
- console.log(`\n☁️ Starting ESAD Deploy for ${moduleId} (v${version})\n`);
62
-
63
- const config = configObj ? configObj.data : null;
64
- if (!config?.deployEndpoint) {
65
- console.error(`❌ Error: 'deployEndpoint' not configured in esad.config.json.`);
66
- process.exit(1);
67
- }
68
-
69
- const deployUrl = config.deployEndpoint.replace('{{moduleId}}', moduleId);
70
- console.log(`📡 Deployment Endpoint Resolved: ${deployUrl}`);
71
-
72
- const distPath = path.join(cwd, 'build');
73
- if (!fs.existsSync(distPath)) {
74
- console.error(`❌ Error: build/ directory not found in ${cwd}. Did you run the build command?`);
75
- process.exit(1);
76
- }
77
-
78
- const zip = new AdmZip();
79
- zip.addLocalFolder(distPath);
80
-
81
- const zipPath = path.join(cwd, `bundle-${moduleId}-${version}.zip`);
82
- zip.writeZip(zipPath);
83
- console.log(`🗜️ Zipped output to ${zipPath}`);
84
-
85
- console.log(`🚀 Uploading to CDN via multipart POST...`);
86
-
87
- try {
88
- const FormData = require('form-data'); // Standard in Node versions, or use native if available
89
- const form = new FormData();
90
- form.append('version', version);
91
- form.append('bundle', fs.createReadStream(zipPath));
92
-
93
- const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args));
94
-
95
- // Simple CDN expects POST /api/admin/assets/:id/versions
96
- const uploadUrl = deployUrl.includes('/versions') ? deployUrl : `${deployUrl}/versions`;
97
-
98
- const response = await fetch(uploadUrl, {
99
- method: 'POST',
100
- body: form,
101
- headers: form.getHeaders(),
102
- });
103
-
104
- if (response.ok) {
105
- const result = await response.json();
106
- console.log(chalk.green(`✅ Successfully uploaded ${moduleId} v${version} to CDN!`));
107
- console.log(`📄 Active Version is now: ${result.active_version}`);
108
- } else {
109
- const errorText = await response.text();
110
- console.error(chalk.red(`❌ Failed to upload: ${response.status} ${response.statusText}`));
111
- console.error(errorText);
112
- }
113
- } catch (err) {
114
- console.error(chalk.red(`❌ Error during upload: ${err.message}`));
115
- } finally {
116
- fs.unlinkSync(zipPath);
117
- }
118
- };
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+ const AdmZip = require('adm-zip');
4
+ const chalk = require('chalk');
5
+ const { getWorkspaceConfig } = require('../utils/config');
6
+ const { resolveModuleMetadata } = require('../utils/resolution');
7
+
8
+ module.exports = async (options) => {
9
+ const configObj = getWorkspaceConfig();
10
+ if (!configObj) {
11
+ console.error(chalk.red(`❌ Error: esad.config.js not found in this or parent directories.`));
12
+ process.exit(1);
13
+ }
14
+
15
+ const config = await configObj.load();
16
+ const workspaceRoot = configObj.root;
17
+ const projectName = config.default?.projectName || config.projectName;
18
+
19
+ let moduleId = options.id;
20
+ let cwd = process.cwd();
21
+
22
+ // Resolve Context
23
+ if (moduleId) {
24
+ const meta = resolveModuleMetadata(moduleId, configObj);
25
+ if (!meta) {
26
+ console.error(chalk.red(`❌ Error: Module not found: ${moduleId}`));
27
+ process.exit(1);
28
+ }
29
+ cwd = meta.path;
30
+ }
31
+
32
+ const pkgPath = path.join(cwd, 'package.json');
33
+ if (!fs.existsSync(pkgPath)) {
34
+ console.error(chalk.red(`❌ Error: package.json not found in ${cwd}.`));
35
+ process.exit(1);
36
+ }
37
+
38
+ const pkg = fs.readJsonSync(pkgPath);
39
+ const resolvedModuleId = moduleId || pkg.name;
40
+ const version = options.version || pkg.version;
41
+
42
+ console.log(`\n🚀 Starting ESAD Deploy for ${chalk.cyan(resolvedModuleId)} (v${version})\n`);
43
+
44
+ const distPath = path.join(cwd, 'build');
45
+ if (!fs.existsSync(distPath)) {
46
+ console.error(chalk.red(`❌ Error: build/ directory not found. Please run 'esad build' first.`));
47
+ process.exit(1);
48
+ }
49
+
50
+ // ZIP BUNDLE
51
+ const zip = new AdmZip();
52
+ zip.addLocalFolder(distPath);
53
+ const buffer = zip.toBuffer();
54
+
55
+ console.log(`🗜️ Generated bundle zip (${(buffer.length / 1024).toFixed(2)} KB)`);
56
+
57
+ // RUN DEPLOY HOOK
58
+ const deployHook = config.default?.deploy || config.deploy;
59
+
60
+ if (typeof deployHook !== 'function') {
61
+ console.error(chalk.red(`❌ Error: 'deploy' function not found in esad.config.js.`));
62
+ process.exit(1);
63
+ }
64
+
65
+ try {
66
+ console.log(`📡 Invoking custom 'deploy' hook...`);
67
+ const result = await deployHook(buffer, {
68
+ version,
69
+ moduleId: resolvedModuleId,
70
+ options
71
+ });
72
+
73
+ console.log(chalk.green(`\n✅ Deployment successful!`));
74
+ if (result) console.log(JSON.stringify(result, null, 2));
75
+ } catch (err) {
76
+ console.error(chalk.red(`\n❌ Deployment failed: ${err.message}`));
77
+ process.exit(1);
78
+ }
79
+ };
80
+
@@ -1,130 +1,70 @@
1
- const { spawn } = require('cross-spawn');
2
- const { getWorkspaceConfig, syncHostConfig } = require('../utils/config');
1
+ const { runProcess } = require('../utils/process');
2
+ const { getWorkspaceConfig } = require('../utils/config');
3
3
  const fs = require('fs-extra');
4
4
  const path = require('path');
5
5
  const chalk = require('chalk');
6
- const { resolveModuleMetadata, listAvailableModules, resolveProjectDir } = require('../utils/resolution');
7
- const { runProcess } = require('../utils/process');
8
6
  const { prepareNative } = require('../utils/scaffold');
9
- const AdmZip = require('adm-zip');
7
+ const { resolveProjectDir } = require('../utils/resolution');
10
8
 
11
9
  module.exports = async (options) => {
12
- let cwd = process.cwd();
13
- let pkgPath = path.join(cwd, 'package.json');
14
-
15
- // Enforce Workspace Root
16
10
  const configObj = getWorkspaceConfig();
17
11
  if (!configObj) {
18
- console.error(chalk.red(`❌ Error: Call this command from the project root (esad.config.json not found).`));
12
+ console.error(chalk.red(`❌ Error: esad.config.js not found. Run this from your project root.`));
19
13
  process.exit(1);
20
14
  }
21
15
 
22
- const workspaceRoot = path.dirname(configObj.path);
23
- const { projectName } = configObj.data;
24
-
25
- // Synchronize Host Config
26
- syncHostConfig(configObj);
16
+ const config = await configObj.load();
17
+ const workspaceRoot = configObj.root;
18
+ const projectName = config.default?.projectName || config.projectName;
27
19
 
28
- if (options.id) {
29
- const metadata = resolveModuleMetadata(options.id, configObj);
30
- if (!metadata) {
31
- console.error(chalk.red(`\n❌ Error: Module not found: ${options.id}`));
32
- listAvailableModules(configObj);
20
+ let cwd = process.cwd();
21
+ let selectedModuleId = options.id;
22
+
23
+ if (selectedModuleId) {
24
+ const targetDir = resolveProjectDir(selectedModuleId, configObj);
25
+ if (!targetDir) {
26
+ console.error(chalk.red(`❌ Error: Module not found: ${selectedModuleId}`));
33
27
  process.exit(1);
34
28
  }
35
- cwd = metadata.path;
36
- moduleId = metadata.id;
37
- pkgPath = path.join(cwd, 'package.json');
38
- console.log(chalk.green(`📂 Module detected: ${path.relative(workspaceRoot, cwd)} (ID: ${moduleId})`));
39
- } else {
40
- // Target host by default if in root
41
- const hostDir = path.join(workspaceRoot, `${projectName}-host`);
42
- if (fs.existsSync(hostDir)) {
43
- cwd = hostDir;
44
- moduleId = pkg.name; // Fallback to package name if in host
45
- pkgPath = path.join(cwd, 'package.json');
46
- console.log(chalk.green(`📂 Host detected: ${path.relative(workspaceRoot, cwd)}`));
47
- }
48
- }
49
-
50
- if (!fs.existsSync(pkgPath)) {
51
- console.error(chalk.red(`❌ Error: package.json not found in ${cwd}.`));
52
- process.exit(1);
29
+ cwd = targetDir;
53
30
  }
54
31
 
32
+ const pkgPath = path.join(cwd, 'package.json');
55
33
  const pkg = fs.readJsonSync(pkgPath);
56
- if (!moduleId) moduleId = pkg.name;
57
- const platform = options.platform || 'android';
34
+ const moduleId = selectedModuleId || pkg.name;
35
+ const port = options.port || '8081';
58
36
 
59
- console.log(chalk.cyan(`\n☁️ Starting ESAD Dev-Push for ${moduleId} (${platform})\n`));
37
+ // Determine if it's a Host or Module
38
+ const isHost = pkg.name.endsWith('-host') || pkg.dependencies?.['@callstack/repack'];
60
39
 
61
- const config = configObj ? configObj.data : null;
62
- const devUrlBase = config?.devModeEndpoint || config?.deployEndpoint?.replace('/versions', '');
63
-
64
- if (!devUrlBase) {
65
- console.error(chalk.red(`❌ Error: 'devModeEndpoint' or 'deployEndpoint' not configured in esad.config.json.`));
66
- process.exit(1);
67
- }
40
+ await prepareNative(cwd, 'all');
68
41
 
69
- const devUploadUrl = devUrlBase.replace('{{moduleId}}', moduleId) + '/dev';
70
- console.log(`📡 Dev-Cloud Endpoint: ${devUploadUrl}`);
71
-
72
- // 0. Prepare Native
73
- await prepareNative(cwd, platform);
42
+ if (isHost && !selectedModuleId) {
43
+ console.log(`\n🚀 Starting ${chalk.green('Host App')} Dev Server (Re.Pack/Rspack)...\n`);
44
+ await runProcess('npx', ['react-native', 'webpack-start'], { cwd });
45
+ return;
46
+ }
74
47
 
75
- // 1. Build
76
- console.log(`\n🏗️ Step 1/3: Building bundle...`);
77
- const bundleOutput = path.join(cwd, 'build', platform, 'index.bundle');
78
- await fs.ensureDir(path.dirname(bundleOutput));
48
+ const { updateDevMode, removeDevMode } = require('../utils/transformer');
79
49
 
80
- try {
81
- await runProcess('npx', [
82
- 'react-native',
83
- 'webpack-bundle',
84
- '--platform', platform,
85
- '--dev', 'false',
86
- '--bundle-output', bundleOutput,
87
- '--assets-dest', path.dirname(bundleOutput),
88
- '--reset-cache'
89
- ], cwd);
90
- } catch (err) {
91
- console.error(chalk.red(`❌ Build failed.`));
92
- process.exit(1);
93
- }
50
+ console.log(`\n⚡ Starting ESAD Dev Server for ${chalk.cyan(moduleId)} on port ${port}...\n`);
51
+
52
+ // Automate devMode update in esad.config.js
53
+ const localBundleUrl = `http://localhost:${port}/index.bundle`;
54
+ updateDevMode(configObj.path, moduleId, localBundleUrl);
55
+ console.log(chalk.gray(`📡 Mode: Module Dev. Host configured to load ${moduleId} from ${localBundleUrl}`));
94
56
 
95
- // 2. Zip
96
- console.log(`\n🗜️ Step 2/3: Zipping assets...`);
97
- const zip = new AdmZip();
98
- const buildDir = path.join(cwd, 'build');
99
- zip.addLocalFolder(buildDir);
100
- const zipPath = path.join(cwd, `dev-bundle-${moduleId}.zip`);
101
- zip.writeZip(zipPath);
57
+ const proc = runProcess('npx', ['react-native', 'webpack-start', '--port', port], { cwd });
102
58
 
103
- // 3. Upload
104
- console.log(`\n🚀 Step 3/3: Pushing to Dev-Cloud...`);
105
- try {
106
- const FormData = require('form-data');
107
- const form = new FormData();
108
- form.append('bundle', fs.createReadStream(zipPath));
59
+ const shutdown = async () => {
60
+ console.log(`\n🛑 Stopping ESAD Dev Server and reverting config...`);
61
+ removeDevMode(configObj.path, moduleId);
62
+ if (proc.kill) proc.kill();
63
+ process.exit(0);
64
+ };
109
65
 
110
- const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args));
111
- const response = await fetch(devUploadUrl, {
112
- method: 'POST',
113
- body: form,
114
- headers: form.getHeaders(),
115
- });
116
66
 
117
- if (response.ok) {
118
- console.log(chalk.green(`\n✅ Successfully synced ${moduleId} to Dev-Cloud!`));
119
- console.log(`📱 Host app configured with 'devModeFor: ["${options.id || moduleId}"]' will now load this version.\n`);
120
- } else {
121
- const errorText = await response.text();
122
- console.error(chalk.red(`❌ Cloud Sync failed: ${response.status} ${response.statusText}`));
123
- console.error(errorText);
124
- }
125
- } catch (err) {
126
- console.error(chalk.red(`❌ Error during sync: ${err.message}`));
127
- } finally {
128
- if (fs.existsSync(zipPath)) fs.unlinkSync(zipPath);
129
- }
67
+ process.on('SIGINT', shutdown);
68
+ process.on('SIGTERM', shutdown);
130
69
  };
70
+
@@ -7,7 +7,12 @@ const readline = require('readline');
7
7
  const { getWorkspaceConfig } = require('../utils/config');
8
8
  const { prepareNative } = require('../utils/scaffold');
9
9
 
10
- const askQuestion = (query, rl) => new Promise((resolve) => rl.question(query, resolve));
10
+ const rl = readline.createInterface({
11
+ input: process.stdin,
12
+ output: process.stdout
13
+ });
14
+
15
+ const askQuestion = (query) => new Promise((resolve) => rl.question(query, resolve));
11
16
 
12
17
  /**
13
18
  * Check if a port is in use
@@ -23,12 +28,6 @@ const isPortInUse = (port) => new Promise((resolve) => {
23
28
  });
24
29
 
25
30
  module.exports = async (subcommand) => {
26
- const rl = readline.createInterface({
27
- input: process.stdin,
28
- output: process.stdout
29
- });
30
-
31
- if (!subcommand) subcommand = 'dev';
32
31
  let cwd = process.cwd();
33
32
  let pkgPath = path.join(cwd, 'package.json');
34
33
 
@@ -65,7 +64,7 @@ module.exports = async (subcommand) => {
65
64
  console.log(`[b] Bundler Only`);
66
65
  console.log(`[c] Cancel`);
67
66
 
68
- const choice = (await askQuestion(`\nSelect platform: `, rl)).toLowerCase();
67
+ const choice = (await askQuestion(`\nSelect platform: `)).toLowerCase();
69
68
 
70
69
  if (choice === 'c') {
71
70
  console.log(`\n❌ Cancelled.`);
@@ -87,13 +86,14 @@ module.exports = async (subcommand) => {
87
86
  if (shouldStartBundler && choice !== 'c') {
88
87
  console.log(`\n🛠️ Starting Rspack Bundler in a new window...`);
89
88
  if (process.platform === 'win32') {
90
- spawn('cmd', ['/c', 'start', '/D', cwd, 'npx', 'react-native', 'start', '--reset-cache'], {
89
+ const npxCmd = 'npx.cmd';
90
+ spawn('cmd', ['/c', 'start', '/D', cwd, npxCmd, 'react-native', 'webpack-start'], {
91
91
  detached: true,
92
92
  stdio: 'ignore',
93
93
  shell: true
94
94
  }).unref();
95
95
  } else {
96
- spawn('npx', ['react-native', 'start', '--reset-cache'], {
96
+ spawn('npx', ['react-native', 'webpack-start'], {
97
97
  cwd,
98
98
  detached: true,
99
99
  stdio: 'inherit',
@@ -123,10 +123,10 @@ module.exports = async (subcommand) => {
123
123
  // 6. Launch Native App
124
124
  if (choice === 'a') {
125
125
  console.log(`🤖 Compiling and launching on Android...`);
126
- await runProcess('npx', ['expo', 'run:android', '--no-bundler'], cwd);
126
+ await runProcess('react-native', ['run-android', '--no-packager'], cwd);
127
127
  } else if (choice === 'i') {
128
128
  console.log(`🍎 Compiling and launching on iOS...`);
129
- await runProcess('npx', ['expo', 'run:ios', '--no-bundler'], cwd);
129
+ await runProcess('react-native', ['run-ios', '--no-packager'], cwd);
130
130
  } else if (choice === 'b') {
131
131
  if (portBusy) {
132
132
  console.log(`✨ Bundler is already active. You can launch manual native runs.`);
@@ -140,16 +140,13 @@ module.exports = async (subcommand) => {
140
140
  // Other subcommands (android, ios directly)
141
141
  try {
142
142
  if (subcommand === 'android') {
143
- console.log(`🤖 Compiling and launching on Android...`);
144
- await runProcess('npx', ['expo', 'run:android', '--no-bundler'], cwd);
143
+ await runProcess('react-native', ['run-android', '--no-packager'], cwd);
145
144
  } else if (subcommand === 'ios') {
146
- console.log(`🍎 Compiling and launching on iOS...`);
147
- await runProcess('npx', ['expo', 'run:ios', '--no-bundler'], cwd);
145
+ await runProcess('react-native', ['run-ios', '--no-packager'], cwd);
148
146
  }
149
147
  } catch (err) {
150
148
  console.error(`❌ Error running host command: ${err.message}`);
151
- } finally {
152
- rl.close();
153
149
  }
150
+ rl.close();
154
151
  }
155
152
  };
@@ -10,34 +10,53 @@ module.exports = async (projectName) => {
10
10
 
11
11
  fs.ensureDirSync(workspaceDir);
12
12
 
13
- const configPath = path.join(workspaceDir, 'esad.config.json');
13
+ const configPath = path.join(workspaceDir, 'esad.config.js');
14
14
  if (!fs.existsSync(configPath)) {
15
- const configTemplate = {
16
- projectName: projectName,
17
- registryUrl: "http://localhost:3000/modules",
18
- deployEndpoint: "http://localhost:3000/api/admin/modules/{{moduleId}}/versions",
19
- devModeEndpoint: "http://localhost:3000/api/admin/modules/{{moduleId}}"
20
- };
21
- fs.writeJsonSync(configPath, configTemplate, { spaces: 2 });
22
- console.log(`✅ Generated dynamic configuration file: esad.config.json`);
15
+ const configTemplate = `/**
16
+ * ESAD: Super App Configuration
17
+ */
18
+ export default {
19
+ projectName: '${projectName}',
20
+
21
+ // 1. Development Overrides
22
+ // Managed automatically by 'esad dev'
23
+ devMode: {},
24
+
25
+ // 2. Programmable Deployment
26
+ // Receives the compiled bundle.
27
+ async deploy(bundle, { version, moduleId, options }) {
28
+ console.log('🚀 Starting custom upload for ' + moduleId + '...');
29
+
30
+ // Example: Simple CDN V2 upload
31
+ // const response = await fetch('http://localhost:3000/api/admin/modules/' + moduleId + '/versions', {
32
+ // method: 'POST',
33
+ // body: bundle,
34
+ // headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
35
+ // });
36
+ // return await response.json();
37
+
38
+ return { status: 'mock_success', moduleId, version };
39
+ }
40
+ };
41
+ `;
42
+ fs.writeFileSync(configPath, configTemplate);
43
+ console.log(`✅ Generated programmable configuration: esad.config.js`);
23
44
  }
24
45
 
25
46
  const gitignorePath = path.join(workspaceDir, '.gitignore');
26
47
  if (!fs.existsSync(gitignorePath)) {
27
48
  const hostName = `${projectName}-host`;
28
49
  const gitignoreContent = `# ESAD Workspace Git Configuration\n` +
29
- `# Ignore everything by default\n` +
30
50
  `/*\n\n` +
31
- `# Exceptions: Track only the Host and Configs\n` +
32
51
  `!/${hostName}/\n` +
33
- `!/esad.config.json\n` +
52
+ `!/esad.config.js\n` +
34
53
  `!/.gitignore\n` +
35
- `\n# Ignore node_modules\n` +
36
- `node_modules/\n`;
54
+ `\nnode_modules/\n`;
37
55
  fs.writeFileSync(gitignorePath, gitignoreContent);
38
56
  console.log(`✅ Generated .gitignore`);
39
57
  }
40
58
 
59
+
41
60
  const hostName = `${projectName}-host`;
42
61
  const hostDir = path.join(workspaceDir, hostName);
43
62
 
@@ -1,37 +1,40 @@
1
- const fs = require('fs-extra');
2
- const path = require('path');
3
-
4
- const getWorkspaceConfig = () => {
5
- let cwd = process.cwd();
6
- let configPath = path.join(cwd, 'esad.config.json');
7
-
8
- // Search up to 2 levels (root or project folder)
9
- if (!fs.existsSync(configPath)) configPath = path.join(cwd, '..', 'esad.config.json');
10
- if (!fs.existsSync(configPath)) configPath = path.join(cwd, '..', '..', 'esad.config.json');
11
-
12
- if (!fs.existsSync(configPath)) return null;
13
- return { path: configPath, data: fs.readJsonSync(configPath) };
14
- };
15
-
16
- const syncHostConfig = (configObj) => {
17
- if (!configObj) return;
18
- const { projectName } = configObj.data;
19
- const workspaceRoot = path.dirname(configObj.path);
20
- const hostDir = path.join(workspaceRoot, `${projectName}-host`);
21
-
22
- if (fs.existsSync(hostDir)) {
23
- const hostConfigPath = path.join(hostDir, 'esad.config.json');
24
-
25
- // Only pass client-safe fields
26
- const clientConfig = {
27
- projectName: configObj.data.projectName,
28
- registryUrl: configObj.data.registryUrl,
29
- devModeFor: configObj.data.devModeFor || []
30
- };
31
-
32
- fs.writeJsonSync(hostConfigPath, clientConfig, { spaces: 2 });
33
- console.log(`\n🔄 Sync: Config propagated to ${path.relative(workspaceRoot, hostConfigPath)}`);
34
- }
35
- };
36
-
37
- module.exports = { getWorkspaceConfig, syncHostConfig };
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+ const { createJiti } = require('jiti');
4
+
5
+ const getWorkspaceConfig = () => {
6
+ let current = process.cwd();
7
+ let configPath = null;
8
+
9
+ // Search upwards for esad.config.js
10
+ while (current !== path.parse(current).root) {
11
+ const check = path.join(current, 'esad.config.js');
12
+ if (fs.existsSync(check)) {
13
+ configPath = check;
14
+ break;
15
+ }
16
+ current = path.dirname(current);
17
+ }
18
+
19
+ if (!configPath) return null;
20
+
21
+ try {
22
+ const jiti = createJiti(__filename);
23
+ const configModule = jiti.import(configPath);
24
+ // jiti.import returns a promise for async imports if needed, but for esad.config.js
25
+ // we expect a sync structure or we resolve it.
26
+ // However, jiti v2 import is async.
27
+
28
+ return {
29
+ path: configPath,
30
+ root: path.dirname(configPath),
31
+ load: () => configModule
32
+ };
33
+ } catch (err) {
34
+ console.error(`❌ Failed to load esad.config.js: ${err.message}`);
35
+ return null;
36
+ }
37
+ };
38
+
39
+ module.exports = { getWorkspaceConfig };
40
+