Unable to "prepare" some projects [9.0.0]
Bug Report
Problem
Running cordova prepare inside a stub project does not create the project correctly.
What is expected to happen?
For a subsequent call to cordova build android to produce an APK.
What does actually happen?
cordova build android will fail with:
Unable to load PlatformApi from platform. Error: Cannot find module 'q'
Unhandled error. (The platform "android" does not appear to be a valid cordova platform. It is missing API.js. android not supported.)
Information
Some of our projects are not restoring correctly when I run "cordova prepare" on them, despite prepare not reporting failure. After some investigation it appears that the root 'node_modules' folder is effectively empty (only contains the cordova plugins, not any other dependencies), causing subsequent cordova commands to fail. I haven't been able to figure out what factor causes the failure as some complex projects seem unaffected but a similar minimal example project seems to trigger the issue. This isn't an issue on versions of cordova prior to 9.0.0.
Command or Code
- Unzip example project stub_project.zip
- Navigate to project in terminal
- Run the following commands
cordova prepare --verbose
No scripts found for hook "before_prepare".
Checking config.xml and package.json for saved platforms that haven't been added to the project
Config.xml and package.json platforms are different. Updating package.json with most current list of platforms.
Package.json and config.xml platforms are different. Updating config.xml with most current list of platforms.
Discovered platform "android" in config.xml or package.json. Adding it to the project
No scripts found for hook "before_platform_add".
No version supplied. Retrieving version from config.xml...
Grabbing pinned version.
Using cordova-fetch for cordova-android@^8.0.0
fetch: Installing cordova-android@^8.0.0 to [...]\cordova_9_testcase
Running command: npm install cordova-android@^8.0.0 --production --no-save
Command finished with error code 0: npm install,cordova-android@^8.0.0,--production,--no-save
Removing "cordova-" prefix from cordova-android
Adding android project...
PlatformApi successfully found for platform android
Creating Cordova project for the Android platform:
Path: platforms\android
Package: com.test.project
Name: Test_project
Activity: MainActivity
Android target: android-28
Copying android template project to platforms\android
Subproject Path: CordovaLib
Subproject Path: app
Android project created with [email protected]
Saving [email protected] into platforms.json
No scripts found for hook "after_platform_add".
PlatformApi successfully found for platform android
Checking for saved plugins that haven't been added to the project
Plugin 'cordova-plugin-whitelist' found in config.xml... Migrating it to package.json
Discovered saved plugin "cordova-plugin-whitelist". Adding it to the project
No scripts found for hook "before_plugin_add".
No version specified for cordova-plugin-whitelist, retrieving version from config.xml
No version for cordova-plugin-whitelist saved in config.xml or package.json
Attempting to use npm info for cordova-plugin-whitelist to choose a compatible release
Running command: npm view cordova-plugin-whitelist --json
Command finished with error code 0: npm view,cordova-plugin-whitelist,--json
Running command: [...]\cordova_9_testcase\platforms\android\cordova\version
Command finished with error code 0: [...]\cordova_9_testcase\platforms\android\cordova\version
Ignoring invalid version in cordova-plugin-whitelist cordovaDependencies: 2.0.0 (must be a single version "[...]\cordova_9_testcase\plugins\cordova-plugin-whitelist"
Calling plugman.install on plugin "[...]\cordova_9_testcase\plugins\cordova-plugin-whitelist" for platform "android
Installing "cordova-plugin-whitelist" for android
Running command: [...]\cordova_9_testcase\platforms\android\cordova\version
Command finished with error code 0: [...]\cordova_9_testcase\platforms\android\cordova\version
Running command: [...]\cordova_9_testcase\platforms\android\cordova\version
Command finished with error code 0: [...]\cordova_9_testcase\platforms\android\cordova\version
Finding scripts for "before_plugin_install" hook from plugin cordova-plugin-whitelist on android platform only.
No scripts found for hook "before_plugin_install".
Install start for "cordova-plugin-whitelist" on android.
Beginning processing of action stack for android project...
Action stack processing complete.
Install complete for cordova-plugin-whitelist on android.
This plugin is only applicable for versions of cordova-android greater than 4.0. If you have a previous platform version, you do *not* need this plugin since the whitelist will be built in.
Finding scripts for "after_plugin_install" hook from plugin cordova-plugin-whitelist on android platform only.
No scripts found for hook "after_plugin_install".
No scripts found for hook "after_plugin_add".
Checking for any plugins added to the project that have not been installed in android platform
No differences found between plugins added to project and installed in android platform. Continuing...
Cannot find module './src/ConfigParser/ConfigParser'
Error: Cannot find module './src/ConfigParser/ConfigParser'
at Function.Module._resolveFilename (internal/modules/cjs/loader.js:580:15)
at Function.Module._load (internal/modules/cjs/loader.js:506:25)
at Module.require (internal/modules/cjs/loader.js:636:17)
at Object.get ([...]\cordova_9_testcase\node_modules\cordova-common\src\util\addProperty.js:27:44)
at Object. ([...]\cordova_9_testcase\platforms\android\cordova\lib\prepare.js:30:45)
at Module._compile (internal/modules/cjs/loader.js:688:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:699:10)
at Module.load (internal/modules/cjs/loader.js:598:32)
at tryModuleLoad (internal/modules/cjs/loader.js:537:12)
at Function.Module._load (internal/modules/cjs/loader.js:529:3)
cordova build android --verbose
No scripts found for hook "before_build".
No scripts found for hook "before_prepare".
Checking config.xml and package.json for saved platforms that haven't been added to the project
Config.xml and package.json platforms are the same. No pkg.json modification.
Package.json and config.xml platforms are different. Updating config.xml with most current list of platforms.
Unable to load PlatformApi from platform. Error: Cannot find module 'q'
Unhandled error. (The platform "android" does not appear to be a valid cordova platform. It is missing API.js. android not supported.)
Error [ERR_UNHANDLED_ERROR]: Unhandled error. (The platform "android" does not appear to be a valid cordova platform. It is missing API.js. android not supported.)
at CordovaEventEmitter.emit (events.js:171:17)
at CordovaEventEmitter.emit ([...]\AppData\Roaming\npm\node_modules\cordova\node_modules\cordova-common\src\events.js:69:22)
at Object.getPlatformApiFunction ([...]\AppData\Roaming\npm\node_modules\cordova\node_modules\cordova-lib\src\cordova\util.js:384:20)
at Object.getPlatformApi ([...]\AppData\Roaming\npm\node_modules\cordova\node_modules\cordova-lib\src\platforms\platforms.js:55:32)
at[...]\AppData\Roaming\npm\node_modules\cordova\node_modules\cordova-lib\src\cordova\prepare.js:52:38
at Array.map ()
at [...]\AppData\Roaming\npm\node_modules\cordova\node_modules\cordova-lib\src\cordova\prepare.js:50:47
at process._tickCallback (internal/process/next_tick.js:68:7)
at Function.Module.runMain (internal/modules/cjs/loader.js:744:11)
at startup (internal/bootstrap/node.js:285:19)
Resulting project: resulting_project.zip
If you repeat this, but remove the line in config.xml that refers to cordova-plugin-whitelist then the project will restore correctly.
Environment, Platform, Device
Window 10 - Command prompt Also occurring on Ubuntu server
Version information
cordova -v
9.0.0 ([email protected])
node -v
v10.13.0
Checklist
- [x] I searched for existing GitHub issues
- [x] I updated all Cordova tooling to most recent version
- [x] I included all the necessary information above
Do you use --no-save anywhere? Because that is currently broken by courtesy of npm.
In other words: make sure all cordova dependencies, like cordova-android for example, are listed as dependencies in package.json.
Does that solve it for you?
The only configuration data is included in the config.xml, we don't use npm or a package.json directly. Looking at the output from cordova prepare above it would appear that cordova is using --no-save on the 11th line though.
Thanks for the detailed issue report! Thanks for pointing out that we are using --no-save internally. We should probably look into that.
To help you solve your immediate problem, I suggest you add a package.json file to your project. Every modern Cordova project should have one. Whether you use npm or not, Cordova does.
The simplest way I can think of right now, would be to do the following in your project:
- create a
package.jsonfile with these contents:{ "name": "<APP_NAME>", "private": true } - run
cordova platform add <PLATFORM>@<SPEC>for each platform you have inconfig.xml
Please let me know if that solves your problem for the moment.
Your workaround seems to bypass the dependency problems, but it looks like the version numbers in the package.json are being purposefully modified by cordova during the cordova platform add android to include the "^" character. Is there any way to avoid this?
Frankly, I can't tell exactly where that happens, but it does not surprise me.
Just to make myself clear: I suggest to keep package.json under version control, so you should only need to do above procedure once. Does Cordova mess with your versions in package.json if you only run prepare?
Keeping it under version control isn't really an option, we use cordova as part of a pipeline. The contents of the www folder and some custom configuration information is all that get passed in. After the resulting APK has been built the project is deleted.
I've just run a new set of tests, provided I have the minimal package.json file you specified in the stub project "prepare" seems to work correctly. Additionally it creates the correct version references ( "=1.0.0" instead of "^1.0.0" ). So I think if I modify our pipeline to create a minimal package.json in it's stub archive it should be good.
I see. That sounds like a viable approach to me. Especially since package.json is the primary source for specifying dependencies in Cordova 9. Specifying them in config.xml will probably be not supported anymore in Cordova 10.
Thanks for the quick feedback on this. We'll have to review this closer and will probably fork off one or two different bug reports from this issue.
No worries, thanks for your prompt help finding a workaround for our pipeline. It's very much appreciated. I may have further comments once I've rolled out the change, but it all looks good for now.
It should be fairly easy for us to move to using the package.json for dependencies in future, so long as we can still define specific versions of plugins.
I will try to keep an eye on this post in case you have any other questions.
I have gotten this quite a few times as well. I have a package.json.
It happened for me on ios as well I believe.
I believe I found a stackoverflow answer that recommended removing and re-adding the platform. It worked if I remember correctly.
If it happens again, I'll add some details.
but it looks like the version numbers in the package.json are being purposefully modified by cordova during the cordova platform add android to include the "^" character.
This is the NPM default. You can edit your npm config to change this, or add an .npmrc file to configure npm specifically for your cordova project. You can change this by modifying the save-prefix config.
https://docs.npmjs.com/misc/config#save-prefix
@Lindsay-Needs-Sleep there's a different situation which it happens, even with an existing package.json. I'm in the process of moving to using local packages, specified via the package.json and this issue seems to be happening every time with that change. I'm trying to figure out the magic combination which will allow that to work... But had to move onto other work.
@breautek thanks for mentioning that NPM flag, it might come in handy. It might be the NPM default, but not the cordova. Either way the issue was more that adding a platform would modify the revision of existing dependencies, which is unhelpful if your expecting your server to produce predictable builds.
It might be the NPM default, but not the cordova.
Cordova uses NPM, so cordova default will be whatever NPM default is.
Either way the issue was more that adding a platform would modify the revision of existing dependencies, which is unhelpful if your expecting your server to produce predictable builds.
Again, this is how NPM works. ^ means that any version in that major is satisfactory.
E.g. ^1.0.0 will install the latest version of 1.x. This means one day you may install a package and it may be version 1.2.0, thus NPM will write (by default) ^1.2.0 in your package.json. Another day that package may have a 1.3.0 update and running npm install again may upgrade your dependency to version 1.3.0.
If you don't like this behaviour, then NPM has a ~ range, which means any patch of the specified version is satisfactory. Or you can use the save-exact=true config to use exact versions.
Keep in mind though, these version match settings only applies to your dependencies. Sub dependencies will not follow these settings. They'll match the version matching defined in its dependant. So using save-exact still will not guarantee you a consistent dependency tree.
which is unhelpful if your expecting your server to produce predictable builds.
I know how you feel. It's frustrating that sometimes I need to git tag to an older version, only to find out that it no longer builds because of some dependency being too relax on it's version matching.
The "best" way with NPM to get a consistent dependency graph is to use the package-lock.json file, but even then I find that hardly works because often enough, on my server-side node applications, my servers npm install ends up modifying the package-lock.json file which shouldn't be happening.
I am facing this issue as well on cordova 9 only
How to reproduce (At least in my case) :-
- Create a new cordova project :-
cordova create neuproject - Change directory to the project :-
cd neuproject - Install any random npm library. I used random-js. :-
npm i random-js - This causes package-lock.json to get created.
- Now add
<engine name="android"/>to your config.xml. This issue happens on both ios and android. But for the sake of the example I am using android for now. - Do
cordova prepare - Do
cordova build androidto get this error :-
Unable to load PlatformApi from platform. Error: Cannot find module 'q'
Unhandled error. (The platform "android" does not appear to be a valid cordova platform. It is missing API.js. android not supported.)
Note :- I have noticed that deleting package-lock.json before cordova-prepare fixes this issue. But I prefer not to use this "hacky" way. Let me know if there is another way to do this..
Thank you :))
@ChrisTomAlx your workflow never includes installing the Android platform, i.e., running cordova platform add android before the prepare step. AFAIK, prepare only copies the required assets, icons, etc, but does not add missing platforms.
Hey @timbru31 Thank you for your quick response. Up until cordova 8, this wasn't an issue. Platforms would get added on cordova prepare as long as engine was specified in config.xml.
If there is no other way out, my plan is to do cordova platform add <platform> instead of prepare.
Thank you
I solved this problem by upgrading the nodejs version to the latest! The specific reason is that the node version required by fs-extra must be greater than 10. Means that the wrong node version will also cause this message output.