Android React native 编译错误问题

前言

今天维护以前老公司的 Android 项目,一跑起来发现编译都过不去,由于 Gradle 报错堆栈查错基本上很麻烦…在此记录解决过程。

A problem occurred starting process ‘command ‘node’’

1
2
3
* What went wrong:
Execution failed for task ':app:recordFilesBeforeBundleCommandProductionDebug'.
> A problem occurred starting process 'command 'node''

首先是无法找到node,我是很费解…,我现在写node和前端都一直在用不知道为何没找到,以前也能跑…,废话不说先解决
使用./gradlew assembleProduction --debug执行打包,然后找到recordFilesBeforeBundleCommandProductionDebug这个task,发现是react-native-code-push要用到node,然后进入codepush.gradle查看,查找node,发现关键代码

1
2
3
4
// react-native-code-push -> codepush.gradle
def config = project.hasProperty("react") ? project.react : [];
...
def nodeExecutableAndArgs = config.nodeExecutableAndArgs ?: ["node"]

就是这里决定了node调用,如果config.nodeExecutableAndArgs没有就直接用node,那么这样找不到只能我自己弄绝对路径,使用which node查看具体路径/usr/local/bin/node

在 app 的build.gradle 加上

1
2
3
4
5
6
7
8
9
10
11
// app -> build.gradle

// 因为这种路径可能每个人不一样,我就写在这里还不用提交给 git 污染
def reactNativeFile = rootProject.file("react-native.properties")
def reactNativeProperties = new Properties()
// 这样就可以直接使用文件内写好的变量
reactNativeProperties.load(new FileInputStream(reactNativeFile))

project.ext.react = [
nodeExecutableAndArgs: [reactNativeProperties.nodeExecutableAndArg],
];
1
2
# react-native.properties
nodeExecutableAndArg=/usr/local/bin/node

这样就可以解决了第一个问题。

react-native-code-push node_modules 路径错误

1
2
3
4
5
internal/modules/cjs/loader.js:583
throw err;
^

Error: Cannot find module '/Users/YeungKC/AndroidStudioProjects/node_modules/react-native-code-push/scripts/recordFilesBeforeBundleCommand.js'

这里其实是node_modules路径错误,我的 React Native 项目其实存在于/Users/YeungKC/ReactNativeProjects里面,而这里连 Android 项目文件夹都没有…这种路径出错怪怪的。
路径能看出是react-native-code-push出问题,再进入codepush.gradle源码搜索nodeExecutableAndArgs这个地方在什么地方用,路径看看能不能设置

1
2
3
4
5
6
7
8
9
// react-native-code-push -> codepush.gradle
generateBundledResourcesHash = tasks.create(
name: "generateBundledResourcesHash${targetName}",
type: Exec) {
commandLine (*nodeExecutableAndArgs, "${nodeModulesPath}/react-native-code-push/scripts/generateBundledResourcesHash.js", resourcesDir, jsBundleFile, jsBundleDir)

enabled config."bundleIn${targetName}" ||
config."bundleIn${variant.buildType.name.capitalize()}" ?:
targetName.toLowerCase().contains("release")

发现有好几处这样类似的代码,幸好他有nodeModulesPath这个变量,找找这个变量怎么定义的

1
2
3
4
5
6
7
8
9
// react-native-code-push -> codepush.gradle
def nodeModulesPath;
if (config.root) {
nodeModulesPath = Paths.get(config.root, "/node_modules");
} else if (project.hasProperty('nodeModulesPath')) {
nodeModulesPath = project.nodeModulesPath
} else {
nodeModulesPath = "../../node_modules";
}

好办了,nodeModulesPath定义一个就搞定,注意这种相对路径的话需要使用file()转为绝对路径,虽然源码这里代码有处理,但是还是尽量自己控制。

1
2
// app -> build.gradle
project.ext.nodeModulesPath = file(reactNativeProperties.nodeModulesPath)
1
2
# react-native.properties
nodeModulesPath=../../../ReactNativeProjects/react native project name/node_modules/

跑起来,然后又报错了

1
2
3
4
5
internal/modules/cjs/loader.js:583
throw err;
^

Error: Cannot find module '/Users/YeungKC/AndroidStudioProjects/node_modules/react-native/local-cli/cli.js'

这次是react-native本体,依然看源码怎么处理,如果有就加进去

1
2
3
4
5
6
7
8
9
10
11
12
// react-native -> react.gradle
def config = project.hasProperty("react") ? project.react : [];

def cliPath = config.cliPath ?: "node_modules/react-native/local-cli/cli.js"

if (Os.isFamily(Os.FAMILY_WINDOWS)) {
commandLine("cmd", "/c", *nodeExecutableAndArgs, cliPath, "bundle", "--platform", "android", "--dev", "${devEnabled}",
"--reset-cache", "--entry-file", entryFile, "--bundle-output", jsBundleFile, "--assets-dest", resourcesDir, *extraPackagerArgs)
} else {
commandLine(*nodeExecutableAndArgs, cliPath, "bundle", "--platform", "android", "--dev", "${devEnabled}",
"--reset-cache", "--entry-file", entryFile, "--bundle-output", jsBundleFile, "--assets-dest", resourcesDir, *extraPackagerArgs)
}

也是路径问题,不过这次有点不同

1
2
3
4
5
6
// app -> build.gradle
project.ext.react = [
nodeExecutableAndArgs: [reactNativeProperties.nodeExecutableAndArg],
// 新增这一行就解决了
cliPath : file("$reactNativeProperties.nodeModulesPath/react-native/local-cli/cli.js")
];

总结

这种容易出现在中途集成 React Native 的项目身上,如果新建基本没遇过这种问题,奇怪的是这个项目项目在以前还是可以的正常编译运行……