@codemoreira/esad 1.4.6-8 → 1.4.6
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 +82 -96
- package/bin/esad.js +88 -114
- package/package.json +13 -13
- package/src/cli/commands/build.js +2 -9
- package/src/cli/commands/createCdn.js +44 -45
- package/src/cli/commands/deploy.js +112 -118
- package/src/cli/commands/dev.js +42 -83
- package/src/cli/commands/host.js +15 -18
- package/src/cli/utils/config.js +11 -37
- package/src/cli/utils/process.js +66 -21
- package/src/cli/utils/scaffold.js +96 -112
- package/src/client/index.js +69 -82
- package/src/plugin/index.js +52 -57
- package/src/plugin/config-plugin.js +0 -45
|
@@ -1,118 +1,112 @@
|
|
|
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
|
|
6
|
-
const { resolveModuleMetadata, listAvailableModules } = require('../utils/resolution');
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
let
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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, listAvailableModules } = require('../utils/resolution');
|
|
7
|
+
|
|
8
|
+
module.exports = async (options) => {
|
|
9
|
+
let cwd = process.cwd();
|
|
10
|
+
let pkgPath = path.join(cwd, 'package.json');
|
|
11
|
+
|
|
12
|
+
// Enforce Workspace Root
|
|
13
|
+
const configObj = getWorkspaceConfig();
|
|
14
|
+
if (!configObj) {
|
|
15
|
+
console.error(chalk.red(`❌ Error: Call this command from the project root (esad.config.json not found).`));
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const workspaceRoot = path.dirname(configObj.path);
|
|
20
|
+
const { projectName } = configObj.data;
|
|
21
|
+
|
|
22
|
+
let moduleId = options.id;
|
|
23
|
+
|
|
24
|
+
if (moduleId) {
|
|
25
|
+
const meta = resolveModuleMetadata(moduleId, configObj);
|
|
26
|
+
if (!meta) {
|
|
27
|
+
console.error(chalk.red(`\n❌ Error: Module not found: ${moduleId}`));
|
|
28
|
+
listAvailableModules(configObj);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
cwd = meta.path;
|
|
32
|
+
moduleId = meta.id; // Correct fully qualified ID
|
|
33
|
+
pkgPath = path.join(cwd, 'package.json');
|
|
34
|
+
console.log(`📂 Module detected for Deploy: ${path.basename(cwd)}`);
|
|
35
|
+
} else {
|
|
36
|
+
// Target host by default if in root
|
|
37
|
+
const hostDir = path.join(workspaceRoot, `${projectName}-host`);
|
|
38
|
+
if (fs.existsSync(hostDir)) {
|
|
39
|
+
cwd = hostDir;
|
|
40
|
+
pkgPath = path.join(cwd, 'package.json');
|
|
41
|
+
console.log(chalk.green(`📂 Host detected for Deploy: ${path.basename(cwd)}`));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!fs.existsSync(pkgPath)) {
|
|
46
|
+
console.error(chalk.red(`❌ Error: package.json not found in ${cwd}.`));
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const pkg = fs.readJsonSync(pkgPath);
|
|
51
|
+
moduleId = moduleId || pkg.name;
|
|
52
|
+
const version = options.version || pkg.version;
|
|
53
|
+
const entry = options.entry || 'index.bundle';
|
|
54
|
+
|
|
55
|
+
console.log(`\n☁️ Starting ESAD Deploy for ${moduleId} (v${version})\n`);
|
|
56
|
+
|
|
57
|
+
const config = configObj ? configObj.data : null;
|
|
58
|
+
if (!config?.deployEndpoint) {
|
|
59
|
+
console.error(`❌ Error: 'deployEndpoint' not configured in esad.config.json.`);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const deployUrl = config.deployEndpoint.replace('{{moduleId}}', moduleId);
|
|
64
|
+
console.log(`📡 Deployment Endpoint Resolved: ${deployUrl}`);
|
|
65
|
+
|
|
66
|
+
const distPath = path.join(cwd, 'build');
|
|
67
|
+
if (!fs.existsSync(distPath)) {
|
|
68
|
+
console.error(`❌ Error: build/ directory not found in ${cwd}. Did you run the build command?`);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const zip = new AdmZip();
|
|
73
|
+
zip.addLocalFolder(distPath);
|
|
74
|
+
|
|
75
|
+
const zipPath = path.join(cwd, `bundle-${moduleId}-${version}.zip`);
|
|
76
|
+
zip.writeZip(zipPath);
|
|
77
|
+
console.log(`🗜️ Zipped output to ${zipPath}`);
|
|
78
|
+
|
|
79
|
+
console.log(`🚀 Uploading to CDN via multipart POST...`);
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
const FormData = require('form-data'); // Standard in Node versions, or use native if available
|
|
83
|
+
const form = new FormData();
|
|
84
|
+
form.append('version', version);
|
|
85
|
+
form.append('bundle', fs.createReadStream(zipPath));
|
|
86
|
+
|
|
87
|
+
const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args));
|
|
88
|
+
|
|
89
|
+
// Simple CDN expects POST /api/admin/assets/:id/versions
|
|
90
|
+
const uploadUrl = deployUrl.includes('/versions') ? deployUrl : `${deployUrl}/versions`;
|
|
91
|
+
|
|
92
|
+
const response = await fetch(uploadUrl, {
|
|
93
|
+
method: 'POST',
|
|
94
|
+
body: form,
|
|
95
|
+
headers: form.getHeaders(),
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
if (response.ok) {
|
|
99
|
+
const result = await response.json();
|
|
100
|
+
console.log(chalk.green(`✅ Successfully uploaded ${moduleId} v${version} to CDN!`));
|
|
101
|
+
console.log(`📄 Active Version is now: ${result.active_version}`);
|
|
102
|
+
} else {
|
|
103
|
+
const errorText = await response.text();
|
|
104
|
+
console.error(chalk.red(`❌ Failed to upload: ${response.status} ${response.statusText}`));
|
|
105
|
+
console.error(errorText);
|
|
106
|
+
}
|
|
107
|
+
} catch (err) {
|
|
108
|
+
console.error(chalk.red(`❌ Error during upload: ${err.message}`));
|
|
109
|
+
} finally {
|
|
110
|
+
fs.unlinkSync(zipPath);
|
|
111
|
+
}
|
|
112
|
+
};
|
package/src/cli/commands/dev.js
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
const { spawn } = require('cross-spawn');
|
|
2
|
-
const { getWorkspaceConfig
|
|
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
|
|
7
|
+
const { resolveProjectDir, listAvailableModules } = require('../utils/resolution');
|
|
10
8
|
|
|
11
9
|
module.exports = async (options) => {
|
|
12
10
|
let cwd = process.cwd();
|
|
@@ -22,109 +20,70 @@ module.exports = async (options) => {
|
|
|
22
20
|
const workspaceRoot = path.dirname(configObj.path);
|
|
23
21
|
const { projectName } = configObj.data;
|
|
24
22
|
|
|
25
|
-
// Synchronize Host Config
|
|
26
|
-
syncHostConfig(configObj);
|
|
27
|
-
|
|
28
23
|
if (options.id) {
|
|
29
|
-
const
|
|
30
|
-
if (!
|
|
24
|
+
const targetDir = resolveProjectDir(options.id, configObj);
|
|
25
|
+
if (!targetDir) {
|
|
31
26
|
console.error(chalk.red(`\n❌ Error: Module not found: ${options.id}`));
|
|
32
27
|
listAvailableModules(configObj);
|
|
33
28
|
process.exit(1);
|
|
34
29
|
}
|
|
35
|
-
cwd =
|
|
36
|
-
moduleId = metadata.id;
|
|
30
|
+
cwd = targetDir;
|
|
37
31
|
pkgPath = path.join(cwd, 'package.json');
|
|
38
|
-
console.log(chalk.green(`📂 Module detected: ${path.relative(workspaceRoot, cwd)}
|
|
32
|
+
console.log(chalk.green(`📂 Module detected: ${path.relative(workspaceRoot, cwd)}`));
|
|
39
33
|
} else {
|
|
40
34
|
// Target host by default if in root
|
|
41
35
|
const hostDir = path.join(workspaceRoot, `${projectName}-host`);
|
|
42
36
|
if (fs.existsSync(hostDir)) {
|
|
43
37
|
cwd = hostDir;
|
|
44
|
-
moduleId = pkg.name; // Fallback to package name if in host
|
|
45
38
|
pkgPath = path.join(cwd, 'package.json');
|
|
46
39
|
console.log(chalk.green(`📂 Host detected: ${path.relative(workspaceRoot, cwd)}`));
|
|
47
40
|
}
|
|
48
41
|
}
|
|
49
42
|
|
|
50
|
-
if (!fs.existsSync(pkgPath)) {
|
|
51
|
-
console.error(chalk.red(`❌ Error: package.json not found in ${cwd}.`));
|
|
52
|
-
process.exit(1);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
43
|
const pkg = fs.readJsonSync(pkgPath);
|
|
56
|
-
|
|
57
|
-
const
|
|
44
|
+
const moduleId = options.id || pkg.name;
|
|
45
|
+
const port = options.port || '8081';
|
|
58
46
|
|
|
59
|
-
|
|
47
|
+
// Determine if it's a Host or Module
|
|
48
|
+
const isHost = pkg.name.endsWith('-host') || pkg.dependencies?.['@callstack/repack'];
|
|
60
49
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if (!devUrlBase) {
|
|
65
|
-
console.error(chalk.red(`❌ Error: 'devModeEndpoint' or 'deployEndpoint' not configured in esad.config.json.`));
|
|
66
|
-
process.exit(1);
|
|
67
|
-
}
|
|
50
|
+
// 1. Initial Checks & Automated Native Preparation
|
|
51
|
+
await prepareNative(cwd, 'all');
|
|
68
52
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
53
|
+
if (isHost && !options.id) {
|
|
54
|
+
console.log(`\n🚀 Starting Host App Dev Server (Re.Pack/Rspack)...\n`);
|
|
55
|
+
await spawn('npx', ['react-native', 'webpack-start'], { cwd, stdio: 'inherit', shell: true });
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
74
58
|
|
|
75
|
-
|
|
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));
|
|
59
|
+
console.log(`\n⚡ Starting ESAD Dev Server for ${moduleId} on port ${port}...\n`);
|
|
79
60
|
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
}
|
|
61
|
+
const config = configObj ? configObj.data : null;
|
|
62
|
+
let devApiUrl = config?.devModeEndpoint ? config.devModeEndpoint.replace('{{moduleId}}', moduleId) : null;
|
|
94
63
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
64
|
+
const setDevMode = async (isActive) => {
|
|
65
|
+
if (!devApiUrl) return;
|
|
66
|
+
try {
|
|
67
|
+
const body = {
|
|
68
|
+
is_dev_mode: isActive,
|
|
69
|
+
...(isActive && { dev_url: `http://localhost:${port}/index.bundle` })
|
|
70
|
+
};
|
|
71
|
+
const res = await fetch(devApiUrl, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
|
|
72
|
+
if (res.ok) console.log(`📡 Registry Sync: Dev Override is ${isActive ? 'ON' : 'OFF'} for module ${moduleId}`);
|
|
73
|
+
} catch(e) { /* ignore */ }
|
|
74
|
+
};
|
|
102
75
|
|
|
103
|
-
|
|
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));
|
|
76
|
+
await setDevMode(true);
|
|
109
77
|
|
|
110
|
-
|
|
111
|
-
const response = await fetch(devUploadUrl, {
|
|
112
|
-
method: 'POST',
|
|
113
|
-
body: form,
|
|
114
|
-
headers: form.getHeaders(),
|
|
115
|
-
});
|
|
78
|
+
const proc = spawn('npx', ['react-native', 'webpack-start', '--port', port], { cwd, stdio: 'inherit', shell: true });
|
|
116
79
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
console.error(chalk.red(`❌ Error during sync: ${err.message}`));
|
|
127
|
-
} finally {
|
|
128
|
-
if (fs.existsSync(zipPath)) fs.unlinkSync(zipPath);
|
|
129
|
-
}
|
|
80
|
+
const shutdown = async () => {
|
|
81
|
+
console.log(`\n🛑 Stopping ESAD Dev Server and reverting registry status...`);
|
|
82
|
+
await setDevMode(false);
|
|
83
|
+
proc.kill();
|
|
84
|
+
process.exit(0);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
process.on('SIGINT', shutdown);
|
|
88
|
+
process.on('SIGTERM', shutdown);
|
|
130
89
|
};
|
package/src/cli/commands/host.js
CHANGED
|
@@ -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
|
|
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:
|
|
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
|
-
|
|
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'
|
|
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('
|
|
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('
|
|
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
|
-
|
|
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
|
-
|
|
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
|
};
|
package/src/cli/utils/config.js
CHANGED
|
@@ -1,37 +1,11 @@
|
|
|
1
|
-
const fs = require('fs-extra');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
|
|
4
|
-
const getWorkspaceConfig = () => {
|
|
5
|
-
let
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
+
|
|
4
|
+
const getWorkspaceConfig = () => {
|
|
5
|
+
let configPath = path.join(process.cwd(), 'esad.config.json');
|
|
6
|
+
if (!fs.existsSync(configPath)) configPath = path.join(process.cwd(), '../esad.config.json');
|
|
7
|
+
if (!fs.existsSync(configPath)) return null;
|
|
8
|
+
return { path: configPath, data: fs.readJsonSync(configPath) };
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
module.exports = { getWorkspaceConfig };
|
package/src/cli/utils/process.js
CHANGED
|
@@ -1,21 +1,66 @@
|
|
|
1
|
-
const { spawn } = require('cross-spawn');
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
const { spawn } = require('cross-spawn');
|
|
2
|
+
const nativeSpawn = require('child_process').spawn;
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
|
|
6
|
+
const runProcess = (cmd, args, cwd = process.cwd()) => {
|
|
7
|
+
return new Promise((resolve, reject) => {
|
|
8
|
+
let finished = false;
|
|
9
|
+
let started = false;
|
|
10
|
+
|
|
11
|
+
const finalize = (fn, arg) => {
|
|
12
|
+
if (finished) return;
|
|
13
|
+
finished = true;
|
|
14
|
+
fn(arg);
|
|
15
|
+
};
|
|
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;
|
|
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})`);
|
|
33
|
+
|
|
34
|
+
// Mark as started after a short delay
|
|
35
|
+
setTimeout(() => { started = true; }, 2000);
|
|
36
|
+
|
|
37
|
+
const spawnFn = useNativeSpawn ? nativeSpawn : spawn;
|
|
38
|
+
const child = spawnFn(command, finalArgs, {
|
|
39
|
+
stdio: 'inherit',
|
|
40
|
+
cwd,
|
|
41
|
+
shell: true
|
|
42
|
+
});
|
|
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
|
+
}
|
|
51
|
+
|
|
52
|
+
console.error(`[ESAD] Process Error: ${err.code} - ${err.message}`);
|
|
53
|
+
finalize(reject, new Error(`Failed to start ${cmd}: ${err.message}`));
|
|
54
|
+
});
|
|
55
|
+
|
|
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
|
+
};
|
|
65
|
+
|
|
66
|
+
module.exports = { runProcess };
|