apache / cordova-cli

Apache Cordova CLI
Apache License 2.0
940 stars 339 forks source link

Unable to "prepare" some projects [9.0.0] #425

Open shortercode opened 5 years ago

shortercode commented 5 years ago

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

  1. Unzip example project stub_project.zip
  2. Navigate to project in terminal
  3. 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 cordova-android@8.0.0
Saving android@8.0.0 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 <= latest or an upper bound)
Calling plugman.fetch on plugin "cordova-plugin-whitelist@1.3.3"
fetch: Installing cordova-plugin-whitelist@1.3.3 to [...]\cordova_9_testcase
Running command: npm install cordova-plugin-whitelist@1.3.3 --no-save
Command finished with error code 0: npm install,cordova-plugin-whitelist@1.3.3,--no-save
Copying plugin "[...]\cordova_9_testcase\node_modules\cordova-plugin-whitelist" => "[...]\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 (cordova-lib@9.0.1)
node -v
v10.13.0

Checklist

raphinesse commented 5 years ago

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?

shortercode commented 5 years ago

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.

raphinesse commented 5 years ago

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:

Please let me know if that solves your problem for the moment.

shortercode commented 5 years ago

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?

raphinesse commented 5 years ago

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?

shortercode commented 5 years ago

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.

raphinesse commented 5 years ago

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.

shortercode commented 5 years ago

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.

Lindsay-Needs-Sleep commented 4 years ago

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.

breautek commented 4 years ago

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

shortercode commented 4 years ago

@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.

breautek commented 4 years ago

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.

ChrisTomAlx commented 4 years ago

I am facing this issue as well on cordova 9 only How to reproduce (At least in my case) :-

  1. Create a new cordova project :- cordova create neuproject
  2. Change directory to the project :- cd neuproject
  3. Install any random npm library. I used random-js. :- npm i random-js
  4. This causes package-lock.json to get created.
  5. 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.
  6. Do cordova prepare
  7. Do cordova build android to 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 :))

timbru31 commented 4 years ago

@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.

ChrisTomAlx commented 4 years ago

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

ferrinweb commented 4 years ago

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.