@codemoreira/esad 1.4.6-3 → 1.4.6-5
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 +29 -2
- package/package.json +1 -1
- package/src/cli/commands/build.js +2 -1
- package/src/cli/commands/dev.js +2 -1
- package/src/cli/commands/host.js +14 -10
- package/src/cli/utils/scaffold.js +31 -15
- package/src/plugin/config-plugin.js +45 -0
- package/src/plugin/index.js +8 -0
package/bin/esad.js
CHANGED
|
@@ -36,13 +36,40 @@ program
|
|
|
36
36
|
|
|
37
37
|
// --- COMMAND: esad host ---
|
|
38
38
|
program
|
|
39
|
-
.command('host
|
|
40
|
-
.description('Manage the Host App (
|
|
39
|
+
.command('host [subcommand]')
|
|
40
|
+
.description('Manage the Host App (subcommands: dev, android, ios)')
|
|
41
41
|
.action(async (sub) => {
|
|
42
42
|
await hostCommand(sub);
|
|
43
43
|
process.exit(0);
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
+
// --- COMMAND: esad android ---
|
|
47
|
+
program
|
|
48
|
+
.command('android')
|
|
49
|
+
.description('Run the Host App on Android')
|
|
50
|
+
.action(async () => {
|
|
51
|
+
await hostCommand('android');
|
|
52
|
+
process.exit(0);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// --- COMMAND: esad ios ---
|
|
56
|
+
program
|
|
57
|
+
.command('ios')
|
|
58
|
+
.description('Run the Host App on iOS')
|
|
59
|
+
.action(async () => {
|
|
60
|
+
await hostCommand('ios');
|
|
61
|
+
process.exit(0);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// --- COMMAND: esad start ---
|
|
65
|
+
program
|
|
66
|
+
.command('start')
|
|
67
|
+
.description('Alias for esad host dev')
|
|
68
|
+
.action(async () => {
|
|
69
|
+
await hostCommand('dev');
|
|
70
|
+
process.exit(0);
|
|
71
|
+
});
|
|
72
|
+
|
|
46
73
|
// --- COMMAND: esad create-module ---
|
|
47
74
|
program
|
|
48
75
|
.command('create-module <module-name>')
|
package/package.json
CHANGED
|
@@ -52,7 +52,8 @@ module.exports = async (options) => {
|
|
|
52
52
|
'--platform', platform,
|
|
53
53
|
'--dev', 'false',
|
|
54
54
|
'--bundle-output', bundleOutput,
|
|
55
|
-
'--assets-dest', path.dirname(bundleOutput)
|
|
55
|
+
'--assets-dest', path.dirname(bundleOutput),
|
|
56
|
+
'--reset-cache'
|
|
56
57
|
], cwd);
|
|
57
58
|
|
|
58
59
|
console.log(chalk.green(`\n✅ Build complete! Assets generated in build/ directory.`));
|
package/src/cli/commands/dev.js
CHANGED
|
@@ -84,7 +84,8 @@ module.exports = async (options) => {
|
|
|
84
84
|
'--platform', platform,
|
|
85
85
|
'--dev', 'false',
|
|
86
86
|
'--bundle-output', bundleOutput,
|
|
87
|
-
'--assets-dest', path.dirname(bundleOutput)
|
|
87
|
+
'--assets-dest', path.dirname(bundleOutput),
|
|
88
|
+
'--reset-cache'
|
|
88
89
|
], cwd);
|
|
89
90
|
} catch (err) {
|
|
90
91
|
console.error(chalk.red(`❌ Build failed.`));
|
package/src/cli/commands/host.js
CHANGED
|
@@ -7,12 +7,7 @@ const readline = require('readline');
|
|
|
7
7
|
const { getWorkspaceConfig } = require('../utils/config');
|
|
8
8
|
const { prepareNative } = require('../utils/scaffold');
|
|
9
9
|
|
|
10
|
-
const
|
|
11
|
-
input: process.stdin,
|
|
12
|
-
output: process.stdout
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
const askQuestion = (query) => new Promise((resolve) => rl.question(query, resolve));
|
|
10
|
+
const askQuestion = (query, rl) => new Promise((resolve) => rl.question(query, resolve));
|
|
16
11
|
|
|
17
12
|
/**
|
|
18
13
|
* Check if a port is in use
|
|
@@ -28,6 +23,12 @@ const isPortInUse = (port) => new Promise((resolve) => {
|
|
|
28
23
|
});
|
|
29
24
|
|
|
30
25
|
module.exports = async (subcommand) => {
|
|
26
|
+
const rl = readline.createInterface({
|
|
27
|
+
input: process.stdin,
|
|
28
|
+
output: process.stdout
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (!subcommand) subcommand = 'dev';
|
|
31
32
|
let cwd = process.cwd();
|
|
32
33
|
let pkgPath = path.join(cwd, 'package.json');
|
|
33
34
|
|
|
@@ -64,7 +65,7 @@ module.exports = async (subcommand) => {
|
|
|
64
65
|
console.log(`[b] Bundler Only`);
|
|
65
66
|
console.log(`[c] Cancel`);
|
|
66
67
|
|
|
67
|
-
const choice = (await askQuestion(`\nSelect platform:
|
|
68
|
+
const choice = (await askQuestion(`\nSelect platform: `, rl)).toLowerCase();
|
|
68
69
|
|
|
69
70
|
if (choice === 'c') {
|
|
70
71
|
console.log(`\n❌ Cancelled.`);
|
|
@@ -86,13 +87,13 @@ module.exports = async (subcommand) => {
|
|
|
86
87
|
if (shouldStartBundler && choice !== 'c') {
|
|
87
88
|
console.log(`\n🛠️ Starting Rspack Bundler in a new window...`);
|
|
88
89
|
if (process.platform === 'win32') {
|
|
89
|
-
spawn('cmd', ['/c', 'start', '/D', cwd, 'npx', 'react-native', '
|
|
90
|
+
spawn('cmd', ['/c', 'start', '/D', cwd, 'npx', 'react-native', 'start', '--reset-cache'], {
|
|
90
91
|
detached: true,
|
|
91
92
|
stdio: 'ignore',
|
|
92
93
|
shell: true
|
|
93
94
|
}).unref();
|
|
94
95
|
} else {
|
|
95
|
-
spawn('npx', ['react-native', '
|
|
96
|
+
spawn('npx', ['react-native', 'start', '--reset-cache'], {
|
|
96
97
|
cwd,
|
|
97
98
|
detached: true,
|
|
98
99
|
stdio: 'inherit',
|
|
@@ -139,13 +140,16 @@ module.exports = async (subcommand) => {
|
|
|
139
140
|
// Other subcommands (android, ios directly)
|
|
140
141
|
try {
|
|
141
142
|
if (subcommand === 'android') {
|
|
143
|
+
console.log(`🤖 Compiling and launching on Android...`);
|
|
142
144
|
await runProcess('npx', ['expo', 'run:android', '--no-bundler'], cwd);
|
|
143
145
|
} else if (subcommand === 'ios') {
|
|
146
|
+
console.log(`🍎 Compiling and launching on iOS...`);
|
|
144
147
|
await runProcess('npx', ['expo', 'run:ios', '--no-bundler'], cwd);
|
|
145
148
|
}
|
|
146
149
|
} catch (err) {
|
|
147
150
|
console.error(`❌ Error running host command: ${err.message}`);
|
|
151
|
+
} finally {
|
|
152
|
+
rl.close();
|
|
148
153
|
}
|
|
149
|
-
rl.close();
|
|
150
154
|
}
|
|
151
155
|
};
|
|
@@ -35,15 +35,21 @@ async function renameProject(targetDir, newName) {
|
|
|
35
35
|
if (appJson.expo) {
|
|
36
36
|
appJson.expo.name = newName;
|
|
37
37
|
appJson.expo.slug = newName;
|
|
38
|
+
appJson.expo.scheme = newName.replace(/[^a-zA-Z0-9]/g, '').toLowerCase();
|
|
39
|
+
|
|
38
40
|
if (appJson.expo.android) {
|
|
39
41
|
appJson.expo.android.package = `com.anonymous.${newName.replace(/[^a-zA-Z0-9]/g, '')}`;
|
|
40
42
|
}
|
|
43
|
+
|
|
44
|
+
if (appJson.expo.ios) {
|
|
45
|
+
appJson.expo.ios.bundleIdentifier = `com.anonymous.${newName.replace(/[^a-zA-Z0-9]/g, '')}`;
|
|
46
|
+
}
|
|
41
47
|
} else {
|
|
42
48
|
appJson.name = newName;
|
|
43
49
|
appJson.slug = newName;
|
|
44
50
|
}
|
|
45
51
|
await fs.writeJson(appJsonPath, appJson, { spaces: 2 });
|
|
46
|
-
console.log(`✅ Updated app.json name/slug/package.`);
|
|
52
|
+
console.log(`✅ Updated app.json name/slug/package/scheme.`);
|
|
47
53
|
}
|
|
48
54
|
|
|
49
55
|
// 3. Update Rspack Config if exists
|
|
@@ -60,27 +66,37 @@ async function renameProject(targetDir, newName) {
|
|
|
60
66
|
}
|
|
61
67
|
|
|
62
68
|
/**
|
|
63
|
-
* Prepares the native folders and
|
|
69
|
+
* Prepares the native folders and ensures the ESAD Config Plugin is registered
|
|
64
70
|
*/
|
|
65
71
|
async function prepareNative(cwd, platform = 'android') {
|
|
72
|
+
const appJsonPath = path.join(cwd, 'app.json');
|
|
73
|
+
|
|
74
|
+
// 1. Ensure Config Plugin is in app.json
|
|
75
|
+
if (fs.existsSync(appJsonPath)) {
|
|
76
|
+
const appJson = await fs.readJson(appJsonPath);
|
|
77
|
+
if (appJson.expo) {
|
|
78
|
+
const plugins = appJson.expo.plugins || [];
|
|
79
|
+
const pluginName = '@codemoreira/esad/plugin/config-plugin';
|
|
80
|
+
|
|
81
|
+
if (!plugins.includes(pluginName) && !plugins.some(p => Array.isArray(p) && p[0] === pluginName)) {
|
|
82
|
+
appJson.expo.plugins = [...plugins, pluginName];
|
|
83
|
+
await fs.writeJson(appJsonPath, appJson, { spaces: 2 });
|
|
84
|
+
console.log(`✅ Added ESAD Config Plugin to app.json.`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 2. Run Prebuild (this will trigger the plugin)
|
|
66
90
|
if (!fs.existsSync(path.join(cwd, 'android')) && (platform === 'android' || platform === 'all')) {
|
|
67
91
|
console.log(`📦 Native folder not found. Running expo prebuild...`);
|
|
68
92
|
await runProcess('npx', ['expo', 'prebuild', '--platform', 'android'], cwd);
|
|
93
|
+
} else if (platform === 'all' || platform === 'ios' || platform === 'android') {
|
|
94
|
+
// If folder exists, we still might want to run prebuild to sync changes if forced,
|
|
95
|
+
// but ESAD's dev command usually assumes it's managed.
|
|
96
|
+
// For now, let's just ensure react-native.config.js exists.
|
|
69
97
|
}
|
|
70
98
|
|
|
71
|
-
//
|
|
72
|
-
const buildGradlePath = path.join(cwd, 'android/app/build.gradle');
|
|
73
|
-
if (fs.existsSync(buildGradlePath)) {
|
|
74
|
-
let content = await fs.readFile(buildGradlePath, 'utf8');
|
|
75
|
-
if (!content.includes('project.ext.react')) {
|
|
76
|
-
const patch = `\nproject.ext.react = [\n bundleCommand: "repack-bundle",\n bundleConfig: "rspack.config.mjs"\n]\n\n`;
|
|
77
|
-
content = content.replace(/react \{/, `${patch}react {`);
|
|
78
|
-
await fs.writeFile(buildGradlePath, content);
|
|
79
|
-
console.log(`✅ Patched android/app/build.gradle for Re.Pack.`);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Create react-native.config.js if missing
|
|
99
|
+
// 3. Create react-native.config.js if missing
|
|
84
100
|
const rnConfigPath = path.join(cwd, 'react-native.config.js');
|
|
85
101
|
if (!fs.existsSync(rnConfigPath)) {
|
|
86
102
|
const content = `module.exports = {\n commands: require('@callstack/repack/commands/rspack'),\n};\n`;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const { withAppBuildGradle, withXcodeProject } = require('expo/config-plugins');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ESAD Re.Pack Config Plugin
|
|
5
|
+
*
|
|
6
|
+
* Automates the native patching required for Re.Pack/Rspack to work with Expo.
|
|
7
|
+
* Replaces manual patching in ESAD CLI.
|
|
8
|
+
*/
|
|
9
|
+
module.exports = (config) => {
|
|
10
|
+
// --- iOS Patching ---
|
|
11
|
+
config = withXcodeProject(config, (configuration) => {
|
|
12
|
+
const xcodeProject = configuration.modResults;
|
|
13
|
+
const bundleReactNativeCodeAndImagesBuildPhase = xcodeProject.buildPhaseObject(
|
|
14
|
+
'PBXShellScriptBuildPhase',
|
|
15
|
+
'Bundle React Native code and images'
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
if (bundleReactNativeCodeAndImagesBuildPhase) {
|
|
19
|
+
const script = JSON.parse(bundleReactNativeCodeAndImagesBuildPhase.shellScript);
|
|
20
|
+
const patched = script
|
|
21
|
+
.replace(
|
|
22
|
+
/if \[\[ -z "\$CLI_PATH" \]\]; then[\s\S]*?fi\n?/g,
|
|
23
|
+
`export CLI_PATH="$("$NODE_BINARY" --print "require('path').dirname(require.resolve('@react-native-community/cli/package.json')) + '/build/bin.js'")"`
|
|
24
|
+
)
|
|
25
|
+
.replace(/if \[\[ -z "\$BUNDLE_COMMAND" \]\]; then[\s\S]*?fi\n?/g, '');
|
|
26
|
+
|
|
27
|
+
bundleReactNativeCodeAndImagesBuildPhase.shellScript = JSON.stringify(patched);
|
|
28
|
+
}
|
|
29
|
+
return configuration;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// --- Android Patching ---
|
|
33
|
+
config = withAppBuildGradle(config, (configuration) => {
|
|
34
|
+
const buildGradle = configuration.modResults.contents;
|
|
35
|
+
// Ensure Re.Pack bundle command is used
|
|
36
|
+
const patched = buildGradle
|
|
37
|
+
.replace(/cliFile.*/, '')
|
|
38
|
+
.replace(/bundleCommand.*/, 'bundleCommand = "bundle"');
|
|
39
|
+
|
|
40
|
+
configuration.modResults.contents = patched;
|
|
41
|
+
return configuration;
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return config;
|
|
45
|
+
};
|
package/src/plugin/index.js
CHANGED
|
@@ -3,6 +3,7 @@ const fs = require('node:fs');
|
|
|
3
3
|
const Repack = require('@callstack/repack');
|
|
4
4
|
const { ExpoModulesPlugin } = require('@callstack/repack-plugin-expo-modules');
|
|
5
5
|
const { ProvidePlugin, DefinePlugin } = require('@rspack/core');
|
|
6
|
+
const { ReanimatedPlugin } = require('@callstack/repack-plugin-reanimated');
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* ESAD Re.Pack Plugin Wrapper
|
|
@@ -86,9 +87,16 @@ function withESAD(env, options) {
|
|
|
86
87
|
new DefinePlugin({
|
|
87
88
|
'process.env.NODE_ENV': JSON.stringify(isDev ? 'development' : 'production'),
|
|
88
89
|
'__DEV__': JSON.stringify(isDev),
|
|
90
|
+
'process.env.EXPO_BASE_URL': JSON.stringify(''),
|
|
91
|
+
'process.env.EXPO_OS': JSON.stringify(platform),
|
|
92
|
+
'process.env.EXPO_PROJECT_ROOT': JSON.stringify(dirname),
|
|
93
|
+
'process.env.EXPO_ROUTER_ABS_APP_ROOT': JSON.stringify(path.resolve(dirname, 'app')),
|
|
94
|
+
'process.env.EXPO_ROUTER_APP_ROOT': JSON.stringify('./app'),
|
|
95
|
+
'process.env.EXPO_ROUTER_IMPORT_MODE': JSON.stringify('sync'),
|
|
89
96
|
}),
|
|
90
97
|
new ExpoModulesPlugin(),
|
|
91
98
|
new Repack.RepackPlugin(),
|
|
99
|
+
new ReanimatedPlugin(),
|
|
92
100
|
new Repack.plugins.ModuleFederationPluginV2({
|
|
93
101
|
name: id,
|
|
94
102
|
filename: `${id}.container.js.bundle`,
|