@codemoreira/esad 2.0.1-2 → 2.0.1-4
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 +1 -1
- package/package.json +1 -1
- package/src/cli/commands/dev.js +172 -72
- package/src/cli/commands/host.js +42 -152
- package/src/cli/commands/init.js +15 -5
- package/src/cli/utils/config.js +5 -6
- package/src/cli/utils/process.js +2 -1
- package/src/cli/utils/transformer.js +9 -10
package/bin/esad.js
CHANGED
|
@@ -29,7 +29,7 @@ program
|
|
|
29
29
|
.command('dev [id]') // [id] as alias to -i for better UX
|
|
30
30
|
.option('-i, --id <moduleId>', 'The Module ID to run in dev mode')
|
|
31
31
|
.option('-p, --port <port>', 'The port to run the dev server on', '8081')
|
|
32
|
-
.description('Starts the
|
|
32
|
+
.description('Starts the development environment for a module or the host application')
|
|
33
33
|
.action(async (id, options) => {
|
|
34
34
|
const opts = { ...options, id: id || options.id };
|
|
35
35
|
await require('../src/cli/commands/dev')(opts);
|
package/package.json
CHANGED
package/src/cli/commands/dev.js
CHANGED
|
@@ -1,72 +1,172 @@
|
|
|
1
|
-
const { runProcess } = require('../utils/process');
|
|
2
|
-
const { getWorkspaceConfig } = require('../utils/config');
|
|
3
|
-
const fs = require('fs-extra');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const chalk = require('chalk');
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
|
|
1
|
+
const { runProcess } = require('../utils/process');
|
|
2
|
+
const { getWorkspaceConfig } = require('../utils/config');
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const chalk = require('chalk');
|
|
6
|
+
const http = require('http');
|
|
7
|
+
const readline = require('readline');
|
|
8
|
+
const { spawn } = require('cross-spawn');
|
|
9
|
+
const { prepareNative } = require('../utils/scaffold');
|
|
10
|
+
const { resolveProjectDir } = require('../utils/resolution');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Check if a port is in use
|
|
14
|
+
*/
|
|
15
|
+
const isPortInUse = (port) => new Promise((resolve) => {
|
|
16
|
+
const req = http.get(`http://localhost:${port}`, (res) => {
|
|
17
|
+
resolve(true);
|
|
18
|
+
});
|
|
19
|
+
req.on('error', () => {
|
|
20
|
+
resolve(false);
|
|
21
|
+
});
|
|
22
|
+
req.end();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const runHostDevFlow = async (cwd) => {
|
|
26
|
+
const rl = readline.createInterface({
|
|
27
|
+
input: process.stdin,
|
|
28
|
+
output: process.stdout
|
|
29
|
+
});
|
|
30
|
+
const askQuestion = (query) => new Promise((resolve) => rl.question(query, resolve));
|
|
31
|
+
|
|
32
|
+
console.log(`\n${chalk.bold('ESAD Host Dev Manager')}`);
|
|
33
|
+
console.log(chalk.dim(`---------------------`));
|
|
34
|
+
console.log(`[a] Run on Android`);
|
|
35
|
+
console.log(`[i] Run on iOS`);
|
|
36
|
+
console.log(`[b] Bundler Only`);
|
|
37
|
+
console.log(`[c] Cancel`);
|
|
38
|
+
|
|
39
|
+
const choice = (await askQuestion(`\nSelect platform: `)).toLowerCase();
|
|
40
|
+
|
|
41
|
+
if (choice === 'c') {
|
|
42
|
+
console.log(`\n❌ Cancelled.`);
|
|
43
|
+
rl.close();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const portBusy = await isPortInUse(8081);
|
|
48
|
+
let shouldStartBundler = true;
|
|
49
|
+
|
|
50
|
+
if (portBusy) {
|
|
51
|
+
console.log(`\n⚠️ Warning: Port 8081 is already in use.`);
|
|
52
|
+
console.log(`💡 Skipping Bundler startup. Proceeding with Native Build.\n`);
|
|
53
|
+
shouldStartBundler = false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (shouldStartBundler && choice !== 'c') {
|
|
57
|
+
console.log(`\n🛠️ Starting Rspack Bundler...`);
|
|
58
|
+
if (process.platform === 'win32') {
|
|
59
|
+
spawn('cmd', ['/c', 'start', '/D', cwd, 'npx.cmd', 'react-native', 'webpack-start'], {
|
|
60
|
+
detached: true,
|
|
61
|
+
stdio: 'ignore',
|
|
62
|
+
shell: true
|
|
63
|
+
}).unref();
|
|
64
|
+
} else {
|
|
65
|
+
spawn('npx', ['react-native', 'webpack-start'], {
|
|
66
|
+
cwd,
|
|
67
|
+
detached: true,
|
|
68
|
+
stdio: 'inherit',
|
|
69
|
+
shell: true
|
|
70
|
+
}).unref();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
console.log(`⏳ Waiting for Rspack Bundler on port 8081...`);
|
|
74
|
+
const waitForBundler = async () => {
|
|
75
|
+
for (let i = 0; i < 30; i++) {
|
|
76
|
+
if (await isPortInUse(8081)) return true;
|
|
77
|
+
await new Promise(r => setTimeout(r, 2000));
|
|
78
|
+
}
|
|
79
|
+
return false;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
if (!(await waitForBundler())) {
|
|
83
|
+
console.error(`\n❌ Timeout: Bundler did not respond.`);
|
|
84
|
+
rl.close();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
console.log(`✅ Bundler ready!`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (choice === 'a') {
|
|
91
|
+
console.log(`🤖 Launching Android...`);
|
|
92
|
+
await runProcess('react-native', ['run-android', '--no-packager'], cwd);
|
|
93
|
+
} else if (choice === 'i') {
|
|
94
|
+
console.log(`🍎 Launching iOS...`);
|
|
95
|
+
await runProcess('react-native', ['run-ios', '--no-packager'], cwd);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
rl.close();
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
module.exports = async (options) => {
|
|
102
|
+
const configObj = getWorkspaceConfig();
|
|
103
|
+
if (!configObj) {
|
|
104
|
+
console.error(chalk.red(`❌ Error: esad.config.js not found. Run this from your project root.`));
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const config = configObj.data;
|
|
109
|
+
const workspaceRoot = configObj.root;
|
|
110
|
+
const projectName = config.projectName;
|
|
111
|
+
|
|
112
|
+
let cwd = process.cwd();
|
|
113
|
+
let selectedModuleId = options.id;
|
|
114
|
+
|
|
115
|
+
// 1. Module Dev Flow
|
|
116
|
+
if (selectedModuleId) {
|
|
117
|
+
const targetDir = resolveProjectDir(selectedModuleId, configObj);
|
|
118
|
+
if (!targetDir) {
|
|
119
|
+
console.error(chalk.red(`❌ Error: Module not found: ${selectedModuleId}`));
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
cwd = targetDir;
|
|
123
|
+
|
|
124
|
+
const pkg = fs.readJsonSync(path.join(cwd, 'package.json'));
|
|
125
|
+
const moduleId = selectedModuleId || pkg.name;
|
|
126
|
+
const port = options.port || '8081';
|
|
127
|
+
|
|
128
|
+
await prepareNative(cwd, 'all');
|
|
129
|
+
|
|
130
|
+
const { updateDevMode, removeDevMode, syncContextDownwards } = require('../utils/transformer');
|
|
131
|
+
|
|
132
|
+
console.log(`\n⚡ Starting ESAD Dev Server for ${chalk.cyan(moduleId)} on port ${port}...\n`);
|
|
133
|
+
const localBundleUrl = `http://localhost:${port}/index.bundle`;
|
|
134
|
+
updateDevMode(configObj.path, moduleId, localBundleUrl);
|
|
135
|
+
syncContextDownwards(configObj);
|
|
136
|
+
|
|
137
|
+
const proc = runProcess('npx', ['react-native', 'webpack-start', '--port', port], { cwd });
|
|
138
|
+
|
|
139
|
+
const shutdown = async () => {
|
|
140
|
+
console.log(`\n🛑 Stopping ESAD Dev Server and reverting config...`);
|
|
141
|
+
removeDevMode(configObj.path, moduleId);
|
|
142
|
+
syncContextDownwards(configObj);
|
|
143
|
+
if (proc.kill) proc.kill();
|
|
144
|
+
process.exit(0);
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
process.on('SIGINT', shutdown);
|
|
148
|
+
process.on('SIGTERM', shutdown);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// 2. Host Dev Flow (Auto-detection)
|
|
153
|
+
const pkgPath = path.join(cwd, 'package.json');
|
|
154
|
+
let isHost = fs.existsSync(pkgPath) && fs.readJsonSync(pkgPath).name.endsWith('-host');
|
|
155
|
+
|
|
156
|
+
if (!isHost) {
|
|
157
|
+
const hostDir = path.join(workspaceRoot, `${projectName}-host`);
|
|
158
|
+
if (fs.existsSync(hostDir)) {
|
|
159
|
+
cwd = hostDir;
|
|
160
|
+
isHost = true;
|
|
161
|
+
console.log(`📂 Auto-detected Host App: ${chalk.dim(path.relative(process.cwd(), hostDir))}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (isHost) {
|
|
166
|
+
await prepareNative(cwd, 'all');
|
|
167
|
+
await runHostDevFlow(cwd);
|
|
168
|
+
} else {
|
|
169
|
+
console.error(chalk.red(`❌ Error: Could not detect Host or Module context.`));
|
|
170
|
+
console.log(`👉 Try: esad dev <module-id>`);
|
|
171
|
+
}
|
|
172
|
+
};
|
package/src/cli/commands/host.js
CHANGED
|
@@ -1,152 +1,42 @@
|
|
|
1
|
-
const { runProcess } = require('../utils/process');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const fs = require('fs-extra');
|
|
4
|
-
const {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
pkgPath = path.join(cwd, 'package.json');
|
|
44
|
-
console.log(`📂 Auto-detected Host App folder: ${path.relative(process.cwd(), hostDir)}`);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (!fs.existsSync(pkgPath)) {
|
|
49
|
-
console.error(`❌ Error: Call this command from inside the Host App or the Workspace Root.`);
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const pkg = fs.readJsonSync(pkgPath);
|
|
54
|
-
|
|
55
|
-
// 1. Initial Checks & Automated Native Preparation
|
|
56
|
-
if (subcommand === 'dev' || subcommand === 'start') {
|
|
57
|
-
await prepareNative(cwd, 'all');
|
|
58
|
-
|
|
59
|
-
// 3. Platform Selection
|
|
60
|
-
console.log(`\nESAD Host Dev Manager`);
|
|
61
|
-
console.log(`---------------------`);
|
|
62
|
-
console.log(`[a] Run on Android`);
|
|
63
|
-
console.log(`[i] Run on iOS`);
|
|
64
|
-
console.log(`[b] Bundler Only`);
|
|
65
|
-
console.log(`[c] Cancel`);
|
|
66
|
-
|
|
67
|
-
const choice = (await askQuestion(`\nSelect platform: `)).toLowerCase();
|
|
68
|
-
|
|
69
|
-
if (choice === 'c') {
|
|
70
|
-
console.log(`\n❌ Cancelled.`);
|
|
71
|
-
rl.close();
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// 4. Check for Port 8081
|
|
76
|
-
const portBusy = await isPortInUse(8081);
|
|
77
|
-
let shouldStartBundler = true;
|
|
78
|
-
|
|
79
|
-
if (portBusy) {
|
|
80
|
-
console.log(`\n⚠️ Warning: Port 8081 is already in use by another process.`);
|
|
81
|
-
console.log(`💡 Skipping Bundler startup - assuming it's already running. Proceeding with Native Build only.\n`);
|
|
82
|
-
shouldStartBundler = false;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// 4. Start Bundler in a New Window (if needed)
|
|
86
|
-
if (shouldStartBundler && choice !== 'c') {
|
|
87
|
-
console.log(`\n🛠️ Starting Rspack Bundler in a new window...`);
|
|
88
|
-
if (process.platform === 'win32') {
|
|
89
|
-
const npxCmd = 'npx.cmd';
|
|
90
|
-
spawn('cmd', ['/c', 'start', '/D', cwd, npxCmd, 'react-native', 'webpack-start'], {
|
|
91
|
-
detached: true,
|
|
92
|
-
stdio: 'ignore',
|
|
93
|
-
shell: true
|
|
94
|
-
}).unref();
|
|
95
|
-
} else {
|
|
96
|
-
spawn('npx', ['react-native', 'webpack-start'], {
|
|
97
|
-
cwd,
|
|
98
|
-
detached: true,
|
|
99
|
-
stdio: 'inherit',
|
|
100
|
-
shell: true
|
|
101
|
-
}).unref();
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// 5. Wait for Bundler (Port 8081)
|
|
105
|
-
console.log(`⏳ Waiting for Rspack Bundler to initialize on port 8081...`);
|
|
106
|
-
const waitForBundler = async () => {
|
|
107
|
-
for (let i = 0; i < 30; i++) {
|
|
108
|
-
if (await isPortInUse(8081)) return true;
|
|
109
|
-
await new Promise(r => setTimeout(r, 2000));
|
|
110
|
-
}
|
|
111
|
-
return false;
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
const isReady = await waitForBundler();
|
|
115
|
-
if (!isReady) {
|
|
116
|
-
console.error(`\n❌ Timeout: Bundler did not respond after 60 seconds.`);
|
|
117
|
-
rl.close();
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
console.log(`✅ Bundler stable and ready to use!`);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// 6. Launch Native App
|
|
124
|
-
if (choice === 'a') {
|
|
125
|
-
console.log(`🤖 Compiling and launching on Android...`);
|
|
126
|
-
await runProcess('react-native', ['run-android', '--no-packager'], cwd);
|
|
127
|
-
} else if (choice === 'i') {
|
|
128
|
-
console.log(`🍎 Compiling and launching on iOS...`);
|
|
129
|
-
await runProcess('react-native', ['run-ios', '--no-packager'], cwd);
|
|
130
|
-
} else if (choice === 'b') {
|
|
131
|
-
if (portBusy) {
|
|
132
|
-
console.log(`✨ Bundler is already active. You can launch manual native runs.`);
|
|
133
|
-
} else {
|
|
134
|
-
console.log(`✨ Bundler is running. You can open the app manually.`);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
rl.close();
|
|
139
|
-
} else {
|
|
140
|
-
// Other subcommands (android, ios directly)
|
|
141
|
-
try {
|
|
142
|
-
if (subcommand === 'android') {
|
|
143
|
-
await runProcess('react-native', ['run-android', '--no-packager'], cwd);
|
|
144
|
-
} else if (subcommand === 'ios') {
|
|
145
|
-
await runProcess('react-native', ['run-ios', '--no-packager'], cwd);
|
|
146
|
-
}
|
|
147
|
-
} catch (err) {
|
|
148
|
-
console.error(`❌ Error running host command: ${err.message}`);
|
|
149
|
-
}
|
|
150
|
-
rl.close();
|
|
151
|
-
}
|
|
152
|
-
};
|
|
1
|
+
const { runProcess } = require('../utils/process');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
const { getWorkspaceConfig } = require('../utils/config');
|
|
5
|
+
|
|
6
|
+
module.exports = async (subcommand) => {
|
|
7
|
+
let cwd = process.cwd();
|
|
8
|
+
let pkgPath = path.join(cwd, 'package.json');
|
|
9
|
+
|
|
10
|
+
const configObj = getWorkspaceConfig();
|
|
11
|
+
if (configObj) {
|
|
12
|
+
const workspaceRoot = configObj.root;
|
|
13
|
+
const { projectName } = configObj.data;
|
|
14
|
+
const hostDir = path.join(workspaceRoot, `${projectName}-host`);
|
|
15
|
+
|
|
16
|
+
if (fs.existsSync(hostDir)) {
|
|
17
|
+
cwd = hostDir;
|
|
18
|
+
pkgPath = path.join(cwd, 'package.json');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!fs.existsSync(pkgPath)) {
|
|
23
|
+
console.error(`❌ Error: Call this command from inside the Host App or the Workspace Root.`);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Handle direct platform commands
|
|
28
|
+
try {
|
|
29
|
+
if (subcommand === 'android') {
|
|
30
|
+
console.log(`🤖 Compiling and launching on Android...`);
|
|
31
|
+
await runProcess('react-native', ['run-android', '--no-packager'], cwd);
|
|
32
|
+
} else if (subcommand === 'ios') {
|
|
33
|
+
console.log(`🍎 Compiling and launching on iOS...`);
|
|
34
|
+
await runProcess('react-native', ['run-ios', '--no-packager'], cwd);
|
|
35
|
+
} else {
|
|
36
|
+
console.log(`\n💡 Tip: Use 'esad dev' for the interactive dev manager.`);
|
|
37
|
+
console.log(` Or use 'esad host android' / 'esad host ios' for direct runs.`);
|
|
38
|
+
}
|
|
39
|
+
} catch (err) {
|
|
40
|
+
console.error(`❌ Error running host command: ${err.message}`);
|
|
41
|
+
}
|
|
42
|
+
};
|
package/src/cli/commands/init.js
CHANGED
|
@@ -63,13 +63,23 @@ export default {
|
|
|
63
63
|
await renameProject(hostDir, hostName);
|
|
64
64
|
|
|
65
65
|
// Inject local context mock immediately to avoid crashes on fresh boot
|
|
66
|
-
|
|
66
|
+
const context = { projectName, devMode: {} };
|
|
67
|
+
fs.writeJsonSync(path.join(hostDir, '.esad.context.json'), context, { spaces: 2 });
|
|
67
68
|
|
|
68
|
-
|
|
69
|
+
// Stabilize filesystem before heavy operations
|
|
70
|
+
await new Promise(res => setTimeout(res, 500));
|
|
71
|
+
|
|
72
|
+
console.log(`\n📦 Installing dependencies into host (this may take a minute)...`);
|
|
69
73
|
await runProcess('npm', ['install'], { cwd: hostDir });
|
|
70
|
-
|
|
71
|
-
console.log(
|
|
74
|
+
|
|
75
|
+
console.log(chalk.green(`\n🎉 ESAD Workspace Initialized successfully!`));
|
|
76
|
+
console.log(chalk.cyan(`\n👉 Next steps:`));
|
|
77
|
+
console.log(` 1. cd ${projectName}/${hostName}`);
|
|
78
|
+
console.log(` 2. esad host dev (to start Host)`);
|
|
79
|
+
console.log(` 3. esad dev (in a module folder to federate)\n`);
|
|
72
80
|
} catch (err) {
|
|
73
|
-
console.error(
|
|
81
|
+
console.error(chalk.red(`\n❌ Failed to initialize workspace:`));
|
|
82
|
+
console.error(chalk.yellow(` ${err.message}`));
|
|
83
|
+
console.log(chalk.dim(`\n Check npm logs if it was an installation error.`));
|
|
74
84
|
}
|
|
75
85
|
};
|
package/src/cli/utils/config.js
CHANGED
|
@@ -20,15 +20,14 @@ const getWorkspaceConfig = () => {
|
|
|
20
20
|
|
|
21
21
|
try {
|
|
22
22
|
const jiti = createJiti(__filename);
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
// However, jiti v2 import is async.
|
|
27
|
-
|
|
23
|
+
let configData = jiti(configPath);
|
|
24
|
+
if (configData.default) configData = configData.default;
|
|
25
|
+
|
|
28
26
|
return {
|
|
29
27
|
path: configPath,
|
|
30
28
|
root: path.dirname(configPath),
|
|
31
|
-
|
|
29
|
+
data: configData,
|
|
30
|
+
load: () => configData
|
|
32
31
|
};
|
|
33
32
|
} catch (err) {
|
|
34
33
|
console.error(`❌ Failed to load esad.config.js: ${err.message}`);
|
package/src/cli/utils/process.js
CHANGED
|
@@ -3,7 +3,8 @@ const nativeSpawn = require('child_process').spawn;
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const fs = require('fs-extra');
|
|
5
5
|
|
|
6
|
-
const runProcess = (cmd, args,
|
|
6
|
+
const runProcess = (cmd, args, options = process.cwd()) => {
|
|
7
|
+
const cwd = typeof options === 'string' ? options : (options.cwd || process.cwd());
|
|
7
8
|
let childRef;
|
|
8
9
|
const promise = new Promise((resolve, reject) => {
|
|
9
10
|
let finished = false;
|
|
@@ -10,20 +10,19 @@ const updateDevMode = (configPath, moduleId, url) => {
|
|
|
10
10
|
|
|
11
11
|
// 1. Ensure devMode object exists
|
|
12
12
|
if (!content.includes('devMode:')) {
|
|
13
|
-
// Inject devMode
|
|
14
|
-
content = content.replace(/
|
|
13
|
+
// Inject devMode after the opening brace of export default
|
|
14
|
+
content = content.replace(/(export default\s*\{)/, `$1\n devMode: {},\n`);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
// 2. Add or update the module entry
|
|
18
18
|
const entryRegex = new RegExp(`(['"]${moduleId}['"]|${moduleId}):\\s*['"]([^'"]*)['"]`, 'g');
|
|
19
19
|
|
|
20
20
|
if (entryRegex.test(content)) {
|
|
21
|
-
// Update existing
|
|
22
|
-
content = content.replace(entryRegex,
|
|
21
|
+
// Update existing entry
|
|
22
|
+
content = content.replace(entryRegex, `'${moduleId}': '${url}'`);
|
|
23
23
|
} else {
|
|
24
|
-
// Insert new entry
|
|
25
|
-
|
|
26
|
-
content = content.replace(devModeRegex, `$1\n '${moduleId}': '${url}',`);
|
|
24
|
+
// Insert new entry right after devMode: {
|
|
25
|
+
content = content.replace(/(devMode:\s*\{)/, `$1\n '${moduleId}': '${url}',`);
|
|
27
26
|
}
|
|
28
27
|
|
|
29
28
|
fs.writeFileSync(configPath, content);
|
|
@@ -44,9 +43,9 @@ const clearAllDevMode = (configPath) => {
|
|
|
44
43
|
if (!fs.existsSync(configPath)) return;
|
|
45
44
|
let content = fs.readFileSync(configPath, 'utf8');
|
|
46
45
|
|
|
47
|
-
//
|
|
48
|
-
const devModeBlockRegex =
|
|
49
|
-
content = content.replace(devModeBlockRegex, '');
|
|
46
|
+
// Simply reset devMode to empty object
|
|
47
|
+
const devModeBlockRegex = /devMode:\s*{[\s\S]*?}/;
|
|
48
|
+
content = content.replace(devModeBlockRegex, 'devMode: {}');
|
|
50
49
|
|
|
51
50
|
fs.writeFileSync(configPath, content);
|
|
52
51
|
};
|