@codemoreira/esad 2.0.0-rc.1 โ 2.0.0-rc.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 +81 -88
- package/package.json +1 -1
- package/src/cli/commands/create.js +153 -0
- package/src/cli/commands/doctor.js +72 -0
- package/src/cli/commands/link.js +35 -0
- package/src/cli/utils/transformer.js +54 -44
- package/src/cli/commands/createCdn.js +0 -44
- package/src/cli/commands/createModule.js +0 -41
- package/src/cli/commands/init.js +0 -78
package/bin/esad.js
CHANGED
|
@@ -1,89 +1,82 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const { program } = require('commander');
|
|
4
|
-
const pkg = require('../package.json');
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
.
|
|
49
|
-
.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
// --- COMMAND: esad
|
|
56
|
-
program
|
|
57
|
-
.command('
|
|
58
|
-
.
|
|
59
|
-
.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
.description('Starts the dev server and updates the external registry to bypass CDN')
|
|
84
|
-
.action(async (options) => {
|
|
85
|
-
await devCommand(options);
|
|
86
|
-
// Note: dev command has its own shutdown logic with SIGINT/SIGTERM
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
program.parse(process.argv);
|
|
2
|
+
|
|
3
|
+
const { program } = require('commander');
|
|
4
|
+
const pkg = require('../package.json');
|
|
5
|
+
|
|
6
|
+
program
|
|
7
|
+
.version(pkg.version)
|
|
8
|
+
.description('esad - Easy Super App Development Toolkit (V2)');
|
|
9
|
+
|
|
10
|
+
// --- COMMAND: esad create [name] --type [host|module|cdn] ---
|
|
11
|
+
program
|
|
12
|
+
.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')
|
|
15
|
+
.action(async (name, options) => {
|
|
16
|
+
await require('../src/cli/commands/create')(name, options);
|
|
17
|
+
process.exit(0);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// --- COMMAND: esad dev [moduleId] ---
|
|
21
|
+
program
|
|
22
|
+
.command('dev [id]') // [id] as alias to -i for better UX
|
|
23
|
+
.option('-i, --id <moduleId>', 'The Module ID to run in dev mode')
|
|
24
|
+
.option('-p, --port <port>', 'The port to run the dev server on', '8081')
|
|
25
|
+
.description('Starts the dev server and updates the local mapping')
|
|
26
|
+
.action(async (id, options) => {
|
|
27
|
+
const opts = { ...options, id: id || options.id };
|
|
28
|
+
await require('../src/cli/commands/dev')(opts);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// --- COMMAND: esad build [id] ---
|
|
32
|
+
program
|
|
33
|
+
.command('build [id]')
|
|
34
|
+
.option('-i, --id <moduleId>', 'The Module ID to build')
|
|
35
|
+
.option('-p, --platform <platform>', 'Platform: android, ios', 'android')
|
|
36
|
+
.description('Builds a production bundle')
|
|
37
|
+
.action(async (id, options) => {
|
|
38
|
+
const opts = { ...options, id: id || options.id };
|
|
39
|
+
await require('../src/cli/commands/build')(opts);
|
|
40
|
+
process.exit(0);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// --- COMMAND: esad deploy [id] ---
|
|
44
|
+
program
|
|
45
|
+
.command('deploy [id]')
|
|
46
|
+
.option('-i, --id <moduleId>', 'The Module ID to deploy')
|
|
47
|
+
.option('-v, --version <version>', 'Specific version to deploy')
|
|
48
|
+
.description('Executes the programmable deploy hook')
|
|
49
|
+
.action(async (id, options) => {
|
|
50
|
+
const opts = { ...options, id: id || options.id };
|
|
51
|
+
await require('../src/cli/commands/deploy')(opts);
|
|
52
|
+
process.exit(0);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// --- COMMAND: esad host <sub> ---
|
|
56
|
+
program
|
|
57
|
+
.command('host <subcommand>')
|
|
58
|
+
.description('Manage host application (android, ios, login)')
|
|
59
|
+
.action(async (sub) => {
|
|
60
|
+
await require('../src/cli/commands/host')(sub);
|
|
61
|
+
process.exit(0);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// --- COMMAND: esad doctor ---
|
|
65
|
+
program
|
|
66
|
+
.command('doctor')
|
|
67
|
+
.description('Check environment for common issues')
|
|
68
|
+
.action(async () => {
|
|
69
|
+
await require('../src/cli/commands/doctor')();
|
|
70
|
+
process.exit(0);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// --- COMMAND: esad link [id] ---
|
|
74
|
+
program
|
|
75
|
+
.command('link [id]')
|
|
76
|
+
.description('Optimize development via local filesystem linking')
|
|
77
|
+
.action(async (id) => {
|
|
78
|
+
await require('../src/cli/commands/link')(id);
|
|
79
|
+
process.exit(0);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
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.
|
|
3
|
+
"version": "2.0.0-rc.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",
|
|
@@ -0,0 +1,153 @@
|
|
|
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 { getWorkspaceConfig } = require('../utils/config');
|
|
7
|
+
const templatesConfig = require('../templates/templates.json');
|
|
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
|
+
const createModule = async (moduleName) => {
|
|
68
|
+
const configObj = getWorkspaceConfig();
|
|
69
|
+
if (!configObj) {
|
|
70
|
+
console.error(`โ Error: Call this command from inside an ESAD workspace (esad.config.js not found).`);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const config = await configObj.load();
|
|
75
|
+
const projectName = config.default?.projectName || config.projectName;
|
|
76
|
+
|
|
77
|
+
const isPrefixed = moduleName.startsWith(`${projectName}-`);
|
|
78
|
+
const finalModuleName = isPrefixed ? moduleName : `${projectName}-${moduleName}`;
|
|
79
|
+
|
|
80
|
+
const workspaceDir = configObj.root;
|
|
81
|
+
const targetDir = path.join(workspaceDir, finalModuleName);
|
|
82
|
+
|
|
83
|
+
console.log(`\n๐ฆ Creating federated mini-app: ${finalModuleName}...\n`);
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
await cloneTemplate(templatesConfig.module, targetDir);
|
|
87
|
+
await renameProject(targetDir, finalModuleName);
|
|
88
|
+
console.log(`\n๐ฆ Installing dependencies...`);
|
|
89
|
+
await runProcess('npm', ['install'], { cwd: targetDir });
|
|
90
|
+
console.log(`\n๐ Module ${finalModuleName} is ready!`);
|
|
91
|
+
} catch (err) {
|
|
92
|
+
console.error(`โ Failed to scaffold module`, err.message);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const createCdn = async (cdnName) => {
|
|
97
|
+
const configObj = getWorkspaceConfig();
|
|
98
|
+
if (!configObj) {
|
|
99
|
+
console.error(`โ Error: Call this command from inside an ESAD workspace (esad.config.js not found).`);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const config = await configObj.load();
|
|
104
|
+
const projectName = config.default?.projectName || config.projectName;
|
|
105
|
+
|
|
106
|
+
const finalCdnName = cdnName || `${projectName}-cdn`;
|
|
107
|
+
const cdnPath = path.join(process.cwd(), finalCdnName);
|
|
108
|
+
|
|
109
|
+
if (fs.existsSync(cdnPath)) {
|
|
110
|
+
console.error(`โ Error: Directory ./${finalCdnName} already exists.`);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
console.log(`\n๐ฆ Initializing Flux Registry & CDN: ${finalCdnName}...\n`);
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
console.log(`๐ก Cloning template from GitHub...`);
|
|
118
|
+
await runProcess('git', ['clone', 'https://github.com/CodeMoreira/simple-cdn.git', finalCdnName]);
|
|
119
|
+
console.log(`๐งน Cleaning up template metadata...`);
|
|
120
|
+
await fs.remove(path.join(cdnPath, '.git'));
|
|
121
|
+
console.log(`\n๐ฅ Installing dependencies (this may take a minute)...`);
|
|
122
|
+
await runProcess('npm', ['install'], { cwd: cdnPath });
|
|
123
|
+
console.log(`\nโ
CDN Registry created successfully in ./${finalCdnName}\n`);
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.error(`\nโ Failed to create CDN: ${error.message}`);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
module.exports = async (name, options) => {
|
|
130
|
+
const type = options.type;
|
|
131
|
+
|
|
132
|
+
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);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (type === 'module') {
|
|
141
|
+
if (!name) {
|
|
142
|
+
console.error(chalk.red('โ Error: Module name is required. Usage: esad create [name] --type module'));
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
return await createModule(name);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (type === 'cdn') {
|
|
149
|
+
return await createCdn(name);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
console.error(chalk.red(`โ Unknown type: ${type}. Valid types are: host, module, cdn.`));
|
|
153
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
const { getWorkspaceConfig } = require('../utils/config');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
|
|
6
|
+
module.exports = async () => {
|
|
7
|
+
console.log(`\n๐ฅ ${chalk.bold('ESAD Doctor: Environment Diagnostics')}\n`);
|
|
8
|
+
|
|
9
|
+
const configObj = getWorkspaceConfig();
|
|
10
|
+
let errors = 0;
|
|
11
|
+
let warnings = 0;
|
|
12
|
+
|
|
13
|
+
// 1. Config Validation
|
|
14
|
+
if (!configObj) {
|
|
15
|
+
console.error(`${chalk.red('โ')} esad.config.js not found in any parent directories.`);
|
|
16
|
+
errors++;
|
|
17
|
+
} else {
|
|
18
|
+
console.log(`${chalk.green('โ')} esad.config.js detected at: ${chalk.gray(configObj.path)}`);
|
|
19
|
+
|
|
20
|
+
// 2. Deployment Hook Validation
|
|
21
|
+
const config = await configObj.load();
|
|
22
|
+
const deployHook = config.default?.deploy || config.deploy;
|
|
23
|
+
if (typeof deployHook === 'function') {
|
|
24
|
+
console.log(`${chalk.green('โ')} Generic 'deploy' hook is properly defined.`);
|
|
25
|
+
} else {
|
|
26
|
+
console.warn(`${chalk.yellow('โ ')} Warning: 'deploy' hook is missing or is not a function.`);
|
|
27
|
+
warnings++;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 3. Registry & Auth Check
|
|
32
|
+
const envPath = path.join(process.cwd(), '.env');
|
|
33
|
+
if (fs.existsSync(envPath)) {
|
|
34
|
+
const envContent = fs.readFileSync(envPath, 'utf8');
|
|
35
|
+
if (envContent.includes('registryUrl')) {
|
|
36
|
+
console.log(`${chalk.green('โ')} registryUrl found in .env`);
|
|
37
|
+
} else {
|
|
38
|
+
console.warn(`${chalk.yellow('โ ')} Warning: registryUrl not found in .env (Host might fail to resolve modules).`);
|
|
39
|
+
warnings++;
|
|
40
|
+
}
|
|
41
|
+
} else {
|
|
42
|
+
console.warn(`${chalk.yellow('โ ')} Warning: .env file not found in current directory.`);
|
|
43
|
+
warnings++;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 4. Dependency Sync Check (Simple version)
|
|
47
|
+
const pkgPath = path.join(process.cwd(), 'package.json');
|
|
48
|
+
if (fs.existsSync(pkgPath)) {
|
|
49
|
+
const pkg = fs.readJsonSync(pkgPath);
|
|
50
|
+
const deps = pkg.dependencies || {};
|
|
51
|
+
|
|
52
|
+
const criticalDeps = ['react-native', '@callstack/repack', 'react'];
|
|
53
|
+
criticalDeps.forEach(dep => {
|
|
54
|
+
if (deps[dep]) {
|
|
55
|
+
console.log(`${chalk.green('โ')} Found critical dependency: ${dep} (${deps[dep]})`);
|
|
56
|
+
} else {
|
|
57
|
+
console.warn(`${chalk.yellow('โ ')} Missing critical dependency in this context: ${dep}`);
|
|
58
|
+
warnings++;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
console.log(`\n------------------------------------------------`);
|
|
64
|
+
if (errors === 0) {
|
|
65
|
+
console.log(`${chalk.bold.green('STABLE')}: ${errors} errors, ${warnings} warnings.`);
|
|
66
|
+
if (warnings > 0) console.log(`๐ Consider addressing the warnings to ensure a perfect developer experience.`);
|
|
67
|
+
} else {
|
|
68
|
+
console.log(`${chalk.bold.red('UNSTABLE')}: ${errors} errors, ${warnings} warnings.`);
|
|
69
|
+
console.log(`๐ Run 'esad init' or fix the manual errors above.`);
|
|
70
|
+
}
|
|
71
|
+
console.log(`------------------------------------------------\n`);
|
|
72
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const { getWorkspaceConfig } = require('../utils/config');
|
|
2
|
+
const { resolveProjectDir } = require('../utils/resolution');
|
|
3
|
+
const { updateDevMode } = require('../utils/transformer');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const chalk = require('chalk');
|
|
6
|
+
|
|
7
|
+
module.exports = async (moduleId) => {
|
|
8
|
+
const configObj = getWorkspaceConfig();
|
|
9
|
+
if (!configObj) {
|
|
10
|
+
console.error(chalk.red(`โ Error: esad.config.js not found.`));
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (!moduleId) {
|
|
15
|
+
console.error(chalk.red(`โ Error: Module ID is required for linking. Usage: esad link [module-name]`));
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const targetDir = resolveProjectDir(moduleId, configObj);
|
|
20
|
+
if (!targetDir) {
|
|
21
|
+
console.error(chalk.red(`โ Error: Module not found: ${moduleId}`));
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Generate a local filesystem link
|
|
26
|
+
// Re.Pack can handle file:// on some platforms, or we use this as a hint for the resolver.
|
|
27
|
+
const localPath = `file://${path.join(targetDir, 'build', 'index.bundle')}`;
|
|
28
|
+
|
|
29
|
+
updateDevMode(configObj.path, moduleId, localPath);
|
|
30
|
+
|
|
31
|
+
console.log(`\n๐ ${chalk.bold('Module Linked Locally')}`);
|
|
32
|
+
console.log(`${chalk.cyan(moduleId)} -> ${chalk.gray(localPath)}`);
|
|
33
|
+
console.log(`\n๐ Run 'esad build ${moduleId}' to generate the bundle if you haven't yet.`);
|
|
34
|
+
console.log(`๐ก The Host app's ScriptManager will now prioritize this local file.\n`);
|
|
35
|
+
};
|
|
@@ -1,44 +1,54 @@
|
|
|
1
|
-
const fs = require('fs-extra');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Safely updates the devMode object in esad.config.js
|
|
6
|
-
* This uses a simple yet robust regex approach to preserve comments/formatting.
|
|
7
|
-
*/
|
|
8
|
-
const updateDevMode = (configPath, moduleId, url) => {
|
|
9
|
-
let content = fs.readFileSync(configPath, 'utf8');
|
|
10
|
-
|
|
11
|
-
// 1. Ensure devMode object exists
|
|
12
|
-
if (!content.includes('devMode:')) {
|
|
13
|
-
// Inject devMode before the last closing brace (naive but effective for clean configs)
|
|
14
|
-
content = content.replace(/}([^}]*)$/, ` devMode: {},\n}$1`);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// 2. Add or update the module entry
|
|
18
|
-
const entryRegex = new RegExp(`(['"]${moduleId}['"]|${moduleId}):\\s*['"]([^'"]*)['"]`, 'g');
|
|
19
|
-
|
|
20
|
-
if (entryRegex.test(content)) {
|
|
21
|
-
// Update existing
|
|
22
|
-
content = content.replace(entryRegex, `$1: '${url}'`);
|
|
23
|
-
} else {
|
|
24
|
-
// Insert new entry into devMode object
|
|
25
|
-
const devModeRegex = /(devMode:\s*{)/;
|
|
26
|
-
content = content.replace(devModeRegex, `$1\n '${moduleId}': '${url}',`);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
fs.writeFileSync(configPath, content);
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
if (!fs.existsSync(configPath)) return;
|
|
34
|
-
let content = fs.readFileSync(configPath, 'utf8');
|
|
35
|
-
|
|
36
|
-
// Remove
|
|
37
|
-
const
|
|
38
|
-
content = content.replace(
|
|
39
|
-
|
|
40
|
-
fs.writeFileSync(configPath, content);
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Safely updates the devMode object in esad.config.js
|
|
6
|
+
* This uses a simple yet robust regex approach to preserve comments/formatting.
|
|
7
|
+
*/
|
|
8
|
+
const updateDevMode = (configPath, moduleId, url) => {
|
|
9
|
+
let content = fs.readFileSync(configPath, 'utf8');
|
|
10
|
+
|
|
11
|
+
// 1. Ensure devMode object exists
|
|
12
|
+
if (!content.includes('devMode:')) {
|
|
13
|
+
// Inject devMode before the last closing brace (naive but effective for clean configs)
|
|
14
|
+
content = content.replace(/}([^}]*)$/, ` devMode: {},\n}$1`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// 2. Add or update the module entry
|
|
18
|
+
const entryRegex = new RegExp(`(['"]${moduleId}['"]|${moduleId}):\\s*['"]([^'"]*)['"]`, 'g');
|
|
19
|
+
|
|
20
|
+
if (entryRegex.test(content)) {
|
|
21
|
+
// Update existing
|
|
22
|
+
content = content.replace(entryRegex, `$1: '${url}'`);
|
|
23
|
+
} else {
|
|
24
|
+
// Insert new entry into devMode object
|
|
25
|
+
const devModeRegex = /(devMode:\s*{)/;
|
|
26
|
+
content = content.replace(devModeRegex, `$1\n '${moduleId}': '${url}',`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
fs.writeFileSync(configPath, content);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const removeDevMode = (configPath, moduleId) => {
|
|
33
|
+
if (!fs.existsSync(configPath)) return;
|
|
34
|
+
let content = fs.readFileSync(configPath, 'utf8');
|
|
35
|
+
|
|
36
|
+
// Remove specific module entry
|
|
37
|
+
const entryRegex = new RegExp(`\\s*['"]?${moduleId}['"]?:\\s*['"]([^'"]*)['"],?`, 'g');
|
|
38
|
+
content = content.replace(entryRegex, '');
|
|
39
|
+
|
|
40
|
+
fs.writeFileSync(configPath, content);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const clearAllDevMode = (configPath) => {
|
|
44
|
+
if (!fs.existsSync(configPath)) return;
|
|
45
|
+
let content = fs.readFileSync(configPath, 'utf8');
|
|
46
|
+
|
|
47
|
+
// Remove the entire devMode block
|
|
48
|
+
const devModeBlockRegex = /\s*devMode:\s*{[\s\S]*?},?/g;
|
|
49
|
+
content = content.replace(devModeBlockRegex, '');
|
|
50
|
+
|
|
51
|
+
fs.writeFileSync(configPath, content);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
module.exports = { updateDevMode, removeDevMode, clearAllDevMode };
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
const { runProcess } = require('../utils/process');
|
|
2
|
-
const { getWorkspaceConfig } = require('../utils/config');
|
|
3
|
-
const fs = require('fs-extra');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
|
|
6
|
-
module.exports = async (cdnName) => {
|
|
7
|
-
const configObj = getWorkspaceConfig();
|
|
8
|
-
if (!configObj) {
|
|
9
|
-
console.error(`โ Error: Call this command from inside an ESAD workspace (esad.config.json not found).`);
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const finalCdnName = cdnName || `${configObj.data.projectName}-cdn`;
|
|
14
|
-
const cdnPath = path.join(process.cwd(), finalCdnName);
|
|
15
|
-
|
|
16
|
-
if (fs.existsSync(cdnPath)) {
|
|
17
|
-
console.error(`โ Error: Directory ./${finalCdnName} already exists.`);
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
console.log(`\n๐ฆ Initializing Flux Registry & CDN: ${finalCdnName}...\n`);
|
|
22
|
-
|
|
23
|
-
try {
|
|
24
|
-
// 1. Clone the template
|
|
25
|
-
console.log(`๐ก Cloning template from GitHub...`);
|
|
26
|
-
await runProcess('git', ['clone', 'https://github.com/CodeMoreira/simple-cdn.git', finalCdnName]);
|
|
27
|
-
|
|
28
|
-
// 2. Remove .git from template
|
|
29
|
-
console.log(`๐งน Cleaning up template metadata...`);
|
|
30
|
-
await fs.remove(path.join(cdnPath, '.git'));
|
|
31
|
-
|
|
32
|
-
// 3. Install dependencies
|
|
33
|
-
console.log(`\n๐ฅ Installing dependencies (this may take a minute)...`);
|
|
34
|
-
await runProcess('npm', ['install'], cdnPath);
|
|
35
|
-
|
|
36
|
-
console.log(`\nโ
CDN Registry created successfully in ./${finalCdnName}\n`);
|
|
37
|
-
console.log(`To start your CDN:`);
|
|
38
|
-
console.log(` cd ${finalCdnName}`);
|
|
39
|
-
console.log(` npm start\n`);
|
|
40
|
-
|
|
41
|
-
} catch (error) {
|
|
42
|
-
console.error(`\nโ Failed to create CDN: ${error.message}`);
|
|
43
|
-
}
|
|
44
|
-
};
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
const fs = require('fs-extra');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const { runProcess } = require('../utils/process');
|
|
4
|
-
const { getWorkspaceConfig } = require('../utils/config');
|
|
5
|
-
const { cloneTemplate, renameProject } = require('../utils/scaffold');
|
|
6
|
-
const templatesConfig = require('../templates/templates.json');
|
|
7
|
-
|
|
8
|
-
module.exports = async (moduleName) => {
|
|
9
|
-
const configObj = getWorkspaceConfig();
|
|
10
|
-
if (!configObj) {
|
|
11
|
-
console.error(`โ Error: Call this command from inside an ESAD workspace (esad.config.js not found).`);
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const config = await configObj.load();
|
|
16
|
-
const projectName = config.default?.projectName || config.projectName;
|
|
17
|
-
|
|
18
|
-
const isPrefixed = moduleName.startsWith(`${projectName}-`);
|
|
19
|
-
|
|
20
|
-
const finalModuleName = isPrefixed ? moduleName : `${projectName}-${moduleName}`;
|
|
21
|
-
|
|
22
|
-
const workspaceDir = path.dirname(configObj.path);
|
|
23
|
-
const targetDir = path.join(workspaceDir, finalModuleName);
|
|
24
|
-
|
|
25
|
-
console.log(`\n๐ฆ Creating federated mini-app: ${finalModuleName}...\n`);
|
|
26
|
-
|
|
27
|
-
try {
|
|
28
|
-
// 1. Clone Template instead of react-native init
|
|
29
|
-
await cloneTemplate(templatesConfig.module, targetDir);
|
|
30
|
-
|
|
31
|
-
// 2. Rename Project
|
|
32
|
-
await renameProject(targetDir, finalModuleName);
|
|
33
|
-
|
|
34
|
-
console.log(`\n๐ฆ Installing dependencies...`);
|
|
35
|
-
await runProcess('npm', ['install'], targetDir);
|
|
36
|
-
|
|
37
|
-
console.log(`\n๐ Module ${finalModuleName} is ready!`);
|
|
38
|
-
} catch (err) {
|
|
39
|
-
console.error(`โ Failed to scaffold module`, err.message);
|
|
40
|
-
}
|
|
41
|
-
};
|
package/src/cli/commands/init.js
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
const fs = require('fs-extra');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const { runProcess } = require('../utils/process');
|
|
4
|
-
const { cloneTemplate, renameProject } = require('../utils/scaffold');
|
|
5
|
-
const templatesConfig = require('../templates/templates.json');
|
|
6
|
-
|
|
7
|
-
module.exports = async (projectName) => {
|
|
8
|
-
const workspaceDir = path.join(process.cwd(), projectName);
|
|
9
|
-
console.log(`\n๐ Initializing ESAD Workspace: ${projectName}...\n`);
|
|
10
|
-
|
|
11
|
-
fs.ensureDirSync(workspaceDir);
|
|
12
|
-
|
|
13
|
-
const configPath = path.join(workspaceDir, 'esad.config.js');
|
|
14
|
-
if (!fs.existsSync(configPath)) {
|
|
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`);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const gitignorePath = path.join(workspaceDir, '.gitignore');
|
|
47
|
-
if (!fs.existsSync(gitignorePath)) {
|
|
48
|
-
const hostName = `${projectName}-host`;
|
|
49
|
-
const gitignoreContent = `# ESAD Workspace Git Configuration\n` +
|
|
50
|
-
`/*\n\n` +
|
|
51
|
-
`!/${hostName}/\n` +
|
|
52
|
-
`!/esad.config.js\n` +
|
|
53
|
-
`!/.gitignore\n` +
|
|
54
|
-
`\nnode_modules/\n`;
|
|
55
|
-
fs.writeFileSync(gitignorePath, gitignoreContent);
|
|
56
|
-
console.log(`โ
Generated .gitignore`);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const hostName = `${projectName}-host`;
|
|
61
|
-
const hostDir = path.join(workspaceDir, hostName);
|
|
62
|
-
|
|
63
|
-
try {
|
|
64
|
-
// 1. Clone Template instead of create-expo-app
|
|
65
|
-
await cloneTemplate(templatesConfig.host, hostDir);
|
|
66
|
-
|
|
67
|
-
// 2. Rename Project
|
|
68
|
-
await renameProject(hostDir, hostName);
|
|
69
|
-
|
|
70
|
-
console.log(`\n๐ฆ Installing dependencies into host...`);
|
|
71
|
-
await runProcess('npm', ['install'], hostDir);
|
|
72
|
-
|
|
73
|
-
console.log(`\n๐ ESAD Workspace Initialized!`);
|
|
74
|
-
console.log(`-> cd ${projectName}/${hostName}\n-> esad host dev (to start Host)`);
|
|
75
|
-
} catch (err) {
|
|
76
|
-
console.error(`โ Failed to init Host:`, err.message);
|
|
77
|
-
}
|
|
78
|
-
};
|