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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/esad.js CHANGED
@@ -1,89 +1,82 @@
1
1
  #!/usr/bin/env node
2
-
3
- const { program } = require('commander');
4
- const pkg = require('../package.json');
5
-
6
- // Import Commands
7
- const initCommand = require('../src/cli/commands/init');
8
- const createModuleCommand = require('../src/cli/commands/createModule');
9
- const createCdnCommand = require('../src/cli/commands/createCdn');
10
- const deployCommand = require('../src/cli/commands/deploy');
11
- const devCommand = require('../src/cli/commands/dev');
12
- const hostCommand = require('../src/cli/commands/host');
13
- const buildCommand = require('../src/cli/commands/build');
14
-
15
- program
16
- .version(pkg.version)
17
- .description('esad - Easy Super App Development Toolkit');
18
-
19
- // --- COMMMAND: esad init ---
20
- program
21
- .command('init <project-name>')
22
- .description('Scaffold a new ESAD workspace containing the Host App')
23
- .action(async (name) => {
24
- await initCommand(name);
25
- process.exit(0);
26
- });
27
-
28
- // --- COMMAND: esad create-cdn ---
29
- program
30
- .command('create-cdn [cdn-name]')
31
- .description('Scaffold the CDN / Registry backend')
32
- .action(async (name) => {
33
- await createCdnCommand(name);
34
- process.exit(0);
35
- });
36
-
37
- // --- COMMAND: esad host ---
38
- program
39
- .command('host <subcommand>')
40
- .description('Manage the Host App (dev, android, ios)')
41
- .action(async (sub) => {
42
- await hostCommand(sub);
43
- process.exit(0);
44
- });
45
-
46
- // --- COMMAND: esad create-module ---
47
- program
48
- .command('create-module <module-name>')
49
- .description('Scaffold a React Native mini-app automatically configured for Module Federation via ESAD')
50
- .action(async (name) => {
51
- await createModuleCommand(name);
52
- process.exit(0);
53
- });
54
-
55
- // --- COMMAND: esad build ---
56
- program
57
- .command('build')
58
- .option('-i, --id <moduleId>', 'The Module ID to build')
59
- .option('-p, --platform <platform>', 'Platform to build for (android, ios)', 'android')
60
- .description('Builds a production bundle for the host or a specific module')
61
- .action(async (options) => {
62
- await buildCommand(options);
63
- process.exit(0);
64
- });
65
-
66
- // --- COMMAND: esad deploy ---
67
- program
68
- .command('deploy')
69
- .option('-v, --version <semver>', 'Version number (e.g., 1.0.0)')
70
- .option('-i, --id <moduleId>', 'The Module ID to deploy')
71
- .option('-e, --entry <entryFileName>', 'The name of the main entry bundle (e.g., index.bundle)', 'index.bundle')
72
- .description('Zips the local dist directory and uploads it to the configured deployment endpoint')
73
- .action(async (options) => {
74
- await deployCommand(options);
75
- process.exit(0);
76
- });
77
-
78
- // --- COMMAND: esad dev ---
79
- program
80
- .command('dev')
81
- .option('-i, --id <moduleId>', 'The Module ID to run in dev mode')
82
- .option('-p, --port <port>', 'The port to run the dev server on', '8081')
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.1",
3
+ "version": "2.0.1-0",
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",
@@ -58,4 +58,4 @@
58
58
  "@types/react": "^19.2.14",
59
59
  "@types/react-native": "^0.72.8"
60
60
  }
61
- }
61
+ }
@@ -4,7 +4,7 @@ const fs = require('fs-extra');
4
4
  const chalk = require('chalk');
5
5
  const { getWorkspaceConfig } = require('../utils/config');
6
6
  const { resolveProjectDir } = require('../utils/resolution');
7
- const { clearAllDevMode } = require('../utils/transformer');
7
+ const { clearAllDevMode, syncContextDownwards } = require('../utils/transformer');
8
8
 
9
9
  module.exports = async (options) => {
10
10
  const configObj = getWorkspaceConfig();
@@ -35,6 +35,7 @@ module.exports = async (options) => {
35
35
  // 1. CLEANUP CONFIG (Avoid shipping local dev URLs)
36
36
  console.log(chalk.gray(`๐Ÿงน Cleaning up devMode mappings in esad.config.js...`));
37
37
  clearAllDevMode(configObj.path);
38
+ syncContextDownwards(configObj);
38
39
 
39
40
  try {
40
41
  const bundleOutput = path.join(cwd, 'build', 'index.bundle'); // Simplified path as per V2
@@ -0,0 +1,161 @@
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
+
59
+ // Inject local context mock immediately to avoid crashes on fresh boot
60
+ fs.writeJsonSync(path.join(hostDir, '.esad.context.json'), { projectName, devMode: {} }, { spaces: 2 });
61
+
62
+ console.log(`\n๐Ÿ“ฆ Installing dependencies into host...`);
63
+ await runProcess('npm', ['install'], { cwd: hostDir });
64
+ console.log(`\n๐ŸŽ‰ ESAD Workspace Initialized!`);
65
+ console.log(`-> cd ${projectName}/${hostName}\n-> esad host dev (to start Host)`);
66
+ } catch (err) {
67
+ console.error(`โŒ Failed to init Host:`, err.message);
68
+ }
69
+ };
70
+
71
+ const createModule = async (moduleName) => {
72
+ const configObj = getWorkspaceConfig();
73
+ if (!configObj) {
74
+ console.error(`โŒ Error: Call this command from inside an ESAD workspace (esad.config.js not found).`);
75
+ return;
76
+ }
77
+
78
+ const config = await configObj.load();
79
+ const projectName = config.default?.projectName || config.projectName;
80
+
81
+ const isPrefixed = moduleName.startsWith(`${projectName}-`);
82
+ const finalModuleName = isPrefixed ? moduleName : `${projectName}-${moduleName}`;
83
+
84
+ const workspaceDir = configObj.root;
85
+ const targetDir = path.join(workspaceDir, finalModuleName);
86
+
87
+ console.log(`\n๐Ÿ“ฆ Creating federated mini-app: ${finalModuleName}...\n`);
88
+
89
+ try {
90
+ await cloneTemplate(templatesConfig.module, targetDir);
91
+ await renameProject(targetDir, finalModuleName);
92
+
93
+ // Inject local context mock immediately
94
+ fs.writeJsonSync(path.join(targetDir, '.esad.context.json'), { projectName, devMode: {} }, { spaces: 2 });
95
+
96
+ console.log(`\n๐Ÿ“ฆ Installing dependencies...`);
97
+ await runProcess('npm', ['install'], { cwd: targetDir });
98
+ console.log(`\n๐ŸŽ‰ Module ${finalModuleName} is ready!`);
99
+ } catch (err) {
100
+ console.error(`โŒ Failed to scaffold module`, err.message);
101
+ }
102
+ };
103
+
104
+ const createCdn = async (cdnName) => {
105
+ const configObj = getWorkspaceConfig();
106
+ if (!configObj) {
107
+ console.error(`โŒ Error: Call this command from inside an ESAD workspace (esad.config.js not found).`);
108
+ return;
109
+ }
110
+
111
+ const config = await configObj.load();
112
+ const projectName = config.default?.projectName || config.projectName;
113
+
114
+ const finalCdnName = cdnName || `${projectName}-cdn`;
115
+ const cdnPath = path.join(process.cwd(), finalCdnName);
116
+
117
+ if (fs.existsSync(cdnPath)) {
118
+ console.error(`โŒ Error: Directory ./${finalCdnName} already exists.`);
119
+ return;
120
+ }
121
+
122
+ console.log(`\n๐Ÿ“ฆ Initializing Flux Registry & CDN: ${finalCdnName}...\n`);
123
+
124
+ try {
125
+ console.log(`๐Ÿ“ก Cloning template from GitHub...`);
126
+ await runProcess('git', ['clone', 'https://github.com/CodeMoreira/simple-cdn.git', finalCdnName]);
127
+ console.log(`๐Ÿงน Cleaning up template metadata...`);
128
+ await fs.remove(path.join(cdnPath, '.git'));
129
+ console.log(`\n๐Ÿ“ฅ Installing dependencies (this may take a minute)...`);
130
+ await runProcess('npm', ['install'], { cwd: cdnPath });
131
+ console.log(`\nโœ… CDN Registry created successfully in ./${finalCdnName}\n`);
132
+ } catch (error) {
133
+ console.error(`\nโŒ Failed to create CDN: ${error.message}`);
134
+ }
135
+ };
136
+
137
+ module.exports = async (name, options) => {
138
+ const type = options.type;
139
+
140
+ if (type === 'host') {
141
+ if (!name) {
142
+ console.error(chalk.red('โŒ Error: Project name is required for host creation.'));
143
+ process.exit(1);
144
+ }
145
+ return await initHost(name);
146
+ }
147
+
148
+ if (type === 'module') {
149
+ if (!name) {
150
+ console.error(chalk.red('โŒ Error: Module name is required. Usage: esad create [name] --type module'));
151
+ process.exit(1);
152
+ }
153
+ return await createModule(name);
154
+ }
155
+
156
+ if (type === 'cdn') {
157
+ return await createCdn(name);
158
+ }
159
+
160
+ console.error(chalk.red(`โŒ Unknown type: ${type}. Valid types are: host, module, cdn.`));
161
+ };
@@ -45,13 +45,14 @@ module.exports = async (options) => {
45
45
  return;
46
46
  }
47
47
 
48
- const { updateDevMode, removeDevMode } = require('../utils/transformer');
48
+ const { updateDevMode, removeDevMode, syncContextDownwards } = require('../utils/transformer');
49
49
 
50
50
  console.log(`\nโšก Starting ESAD Dev Server for ${chalk.cyan(moduleId)} on port ${port}...\n`);
51
51
 
52
52
  // Automate devMode update in esad.config.js
53
53
  const localBundleUrl = `http://localhost:${port}/index.bundle`;
54
54
  updateDevMode(configObj.path, moduleId, localBundleUrl);
55
+ syncContextDownwards(configObj);
55
56
  console.log(chalk.gray(`๐Ÿ“ก Mode: Module Dev. Host configured to load ${moduleId} from ${localBundleUrl}`));
56
57
 
57
58
  const proc = runProcess('npx', ['react-native', 'webpack-start', '--port', port], { cwd });
@@ -59,6 +60,7 @@ module.exports = async (options) => {
59
60
  const shutdown = async () => {
60
61
  console.log(`\n๐Ÿ›‘ Stopping ESAD Dev Server and reverting config...`);
61
62
  removeDevMode(configObj.path, moduleId);
63
+ syncContextDownwards(configObj);
62
64
  if (proc.kill) proc.kill();
63
65
  process.exit(0);
64
66
  };
@@ -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('EXPO_PUBLIC_REGISTRY_URL')) {
36
+ console.log(`${chalk.green('โœ”')} EXPO_PUBLIC_REGISTRY_URL found in .env`);
37
+ } else {
38
+ console.warn(`${chalk.yellow('โš ')} Warning: EXPO_PUBLIC_REGISTRY_URL 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,82 @@
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 clearAllDevMode = (configPath) => {
33
- if (!fs.existsSync(configPath)) return;
34
- let content = fs.readFileSync(configPath, 'utf8');
35
-
36
- // Remove the entire devMode block
37
- const devModeBlockRegex = /\s*devMode:\s*{[\s\S]*?},?/g;
38
- content = content.replace(devModeBlockRegex, '');
39
-
40
- fs.writeFileSync(configPath, content);
41
- };
42
-
43
- module.exports = { updateDevMode, removeDevMode, clearAllDevMode };
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
+ const syncContextDownwards = (configObj) => {
55
+ if (!fs.existsSync(configObj.path)) return;
56
+ const configDir = path.dirname(configObj.path);
57
+
58
+ // Clear cache to read fresh state
59
+ delete require.cache[require.resolve(configObj.path)];
60
+ let configContent = require(configObj.path);
61
+ if (configContent.default) configContent = configContent.default;
62
+
63
+ const exportData = {
64
+ projectName: configContent.projectName || 'esad-workspace',
65
+ devMode: configContent.devMode || {}
66
+ };
67
+
68
+ try {
69
+ const children = fs.readdirSync(configDir, { withFileTypes: true })
70
+ .filter(dirent => dirent.isDirectory())
71
+ .map(dirent => path.join(configDir, dirent.name))
72
+ .filter(dir => fs.existsSync(path.join(dir, 'package.json')));
73
+
74
+ for (const childDir of children) {
75
+ fs.writeJsonSync(path.join(childDir, '.esad.context.json'), exportData, { spaces: 2 });
76
+ }
77
+ } catch (err) {
78
+ console.error('Failed to sync context downwards:', err.message);
79
+ }
80
+ };
81
+
82
+ module.exports = { updateDevMode, removeDevMode, clearAllDevMode, syncContextDownwards };
@@ -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
- };
@@ -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
- };