Compare commits

...

61 Commits
6.2.x ... 6.3.x

Author SHA1 Message Date
Joe Bowser
021c9c19e8 CB-12895: Temporarily disabling eslint since cordova-js does not have eslint yet. 2017-09-25 14:01:16 -07:00
Joe Bowser
0c475b5ec4 Set VERSION to 6.3.0 (via coho) 2017-09-25 11:37:50 -07:00
Joe Bowser
2a2a11a729 Update JS snapshot to version 6.3.0 (via coho) 2017-09-25 11:37:50 -07:00
Joe Bowser
7203b740fd CB-13323 Updated RELEASENOTES and Version for release 6.3.0 2017-09-25 11:35:59 -07:00
Joe Bowser
97aab900da CB-13323 Updated checked-in node_modules 2017-09-25 11:17:24 -07:00
Joe Bowser
dddb2837dd CB-6936: Merge pull request #304 from uareurapid/master
CB-6936: fix crash when calling methods on a destroyed webview
2017-09-15 13:26:21 -07:00
filmaj
1637937664 CB-12981: handle SDK 26.0.2 slightly different AVD list output for Android 8+ AVDs. Would cause "cannot read property replace of undefined" errors when trying to deploy an Android 8 emulator. 2017-09-14 11:26:18 -07:00
filmaj
03144eb160 Add a node and npm package.json script for running java unit tests. Include them in the top-level npm test script. Run java unit tests in travis. Small refactor in gradlebuilder to support building gradle wrapper more easily. Don't explicitly build gradlew on appveyor CI as now npm test will do it for you. 2017-09-14 09:57:37 -07:00
Joe Bowser
53210710ba Merge pull request #404 from mediathand/maven-repo-fix
Update maven repo to include most recent lib versions
2017-09-11 10:04:48 -07:00
Steve Gill
e7a972df77 updated bundled node_modules 2017-09-05 11:04:12 -07:00
Gil Pedersen
cb2f396e33 Update maven repo to include most recent lib versions 2017-08-29 17:27:02 +02:00
Joe Bowser
2377aa7ac2 Merge pull request #403 from infil00p/api26
CB-13177: Updating to API Level 26
2017-08-15 14:17:09 -07:00
Joe Bowser
dce3b7ed6c Merge pull request #402 from macdonst/master
Revert CB-12015: initial-scale values less than 1.0 are ignored on Android
2017-08-15 13:22:22 -07:00
Simon MacDonald
5bda4df7fa Revert CB-12015: initial-scale values less than 1.0 are ignored on Android 2017-08-14 20:57:44 -04:00
Joe Bowser
1a8e36ccd3 CB-13177: Updating to API Level 26 2017-08-14 11:41:46 -07:00
Simon MacDonald
14816c7c81 Merge pull request #399 from macdonst/CB-12730
CB-12730: Compat - INTEGRATE
2017-08-11 10:38:06 -04:00
Simon MacDonald
d364f46baa CB-12730: Compat - INTEGRATE 2017-07-28 08:56:19 -04:00
Nikita Matrosov
2b53c98cf5 CB-12453: Remove unnecessary double quotes from .bat files which are the causes of crash if project path contains spaces
this closes #362
2017-07-28 14:23:53 +03:00
Nikita Matrosov
36d07d7a15 CB-13031: Fix bug with case-sensitivity of android-packageName
this closes #397
2017-07-28 14:05:53 +03:00
filmaj
458e479681 Trying AppVeyor-supplied workaround for current appveyor time out issues. See http://help.appveyor.com/discussions/problems/7159-builds-timing-out-after-an-hour 2017-07-27 14:33:16 -07:00
filmaj
c3ce2f8a07 Closes #398 2017-07-24 10:11:21 -07:00
Joe Bowser
893356abcd CB-13034: Fixing eslint error 2017-07-18 14:13:53 -07:00
Darryl Pogue
22645d9158 [CB-10916] Support display name for Android 2017-07-14 17:37:15 -02:30
filmaj
76dd8613ca CB-12423: make explicit JDK 1.8 or greater is needed in the README. 2017-07-14 11:13:52 -05:00
filmaj
5917d4ef0b CB-13006: removed create and update end-to-end tests, and instead added more unit test coverage. tweaked code coverage invocation so that we get coverage details on the create.js module. slight changes to the create.js module so that it is slightly easier to test. 2017-07-11 12:54:15 -05:00
filmaj
90053eb9df CB-12950: lots of tweaks for end-to-end test runs, especially on CI:
- rename npm tasks to reflect what they do (npm run unit-tests, npm run e2e-tests). main `npm test` runs linter, unit tests and e2e tests now.
- locked jasmine down to ~2.6.0.
- consolidate gitignores.
- updated travis to run `npm test`. add android sdk installation to appveyor ci run.align android dpendencies across travis and appveyor. have appveyor install gradle. force gradle to version 3.4.1 in appveyor, as that seems to be the only version choco has. explicitly invoke sdkmanager to move license accepting process along.
2017-06-27 15:59:16 -05:00
Nikita Matrosov
540929c6a0 CB-9971: Suppressed unwanted java stderr output when running gradle wrapper
this closes #388
2017-06-26 21:17:58 +03:00
filmaj
3ff32092a3 CB-12954 🔪 remove jshint leftovers 2017-06-26 12:26:53 -05:00
Audrey So
55d7cf3865 CB-12895 : updated .eslintrc file in spec dir and set jasmine true and removed root is true 2017-06-23 08:44:08 -07:00
Audrey So
ac4ac935f6 CB-12895 : added .eslintrc files to set up jasmine environment 2017-06-23 08:07:08 -07:00
Audrey So
d83d49d83b CB-12895 : fixed eslint errors 2017-06-23 08:07:08 -07:00
Audrey So
e36158a0da CB-12895 : added eslint and removed jshint 2017-06-23 08:07:07 -07:00
Jesse MacFadyen
5cc14b8031 CB-12605 In Windows get Android studio path from the registry
This closes #387
2017-06-22 10:55:39 -07:00
Audrey So
3a6e898b12 CB-12762 : pointed package.json repo items to github mirrors instead of apache repos site
This closes #383
2017-06-07 09:28:22 -07:00
filmaj
0cc3df3747 CB-12859: document how to run the native tests. add a README to the test/ project. 2017-06-06 14:37:57 -05:00
Joe Bowser
2bc842a2b3 This commit should exist on the Crosswalk Plugin, Close #357 2017-05-29 13:04:17 -07:00
Joe Bowser
1c6f640026 Commit already merged, Close #378 2017-05-29 13:02:35 -07:00
Joe Bowser
6daad829cc Close #283 2017-05-29 09:53:49 -07:00
Darryl Pogue
7d926822ed CB-8980: Ensure copied resource-files are cleaned
This closes #377
2017-05-25 16:11:20 -07:00
Audrey So
d4dcbb13fc CB-12617 : removed node0.x support for platforms and added engineStrict
This closes #372
2017-05-25 14:09:55 -07:00
filmaj
f396712f59 CB-12847: added bugs entry to package.json. 2017-05-24 00:54:03 +02:00
filmaj
d97250f968 Update JS snapshot to version 6.3.0-dev (via coho) 2017-05-02 16:19:21 -07:00
filmaj
e7e8e95242 Set VERSION to 6.3.0-dev (via coho) 2017-05-02 16:19:19 -07:00
filmaj
d518a655a8 CB-12746: updated release notes for impending 6.2.3 release, since some of the reported changes did not make it into 6.2.2. 2017-05-02 16:04:20 -07:00
filmaj
b6a5844027 CB-12746: decrement working dev version on master to 6.2.4-dev, to align with impending patch release. 2017-05-02 16:04:20 -07:00
filmaj
9d9abea157 Start of GradleBuilder.js specs - puts it on the code coverage radar. 2017-05-02 15:31:26 -07:00
Simon MacDonald
ee1165ea33 CB-12015: initial-scale values less than 1.0 are ignored on Android
This closes #376
2017-05-01 11:20:25 -07:00
Joe Bowser
2704ee54cf This closes #374 2017-05-01 10:51:50 -07:00
Steve Gill
ad01d28351 Set VERSION to 6.3.0-dev (via coho) 2017-04-24 22:05:43 -07:00
Steve Gill
a215c1cf30 CB-12697 Updated RELEASENOTES and Version for release 6.2.2 2017-04-24 22:04:56 -07:00
Steve Gill
cadea2f6c3 CB-12697 Updated checked-in node_modules 2017-04-24 21:56:28 -07:00
filmaj
e13e15d3e9 CB-12640: better handling of unrecognized commands on windows. removed error checking in emulator image listing when shelling out, as we already defensively dont shell out if the program is not on the PATH / not recognized. added additional test for windows unrecognized command errors for target listing. fixed up spying in a test. 2017-04-10 12:12:46 -07:00
filmaj
6ef2f67ae8 CB-12640: flipped avd parsing logic so that it always tries to use avdmanager to retrieve avds first, then falls back to android command if avdmanager cannot be found (and errors with ENOENT). updated tests - and added explicit tests to ensure to shell out to singular forms of sub-commands when executing android 2017-04-10 12:12:46 -07:00
filmaj
765c4ee9f6 CB-12640: support for android sdk tools 26.0.1. simplified target parsing by using avdmanager instead of sdkmanager. flipped target parsing logic so that it always tries to use avdmanager to retrieve targets first, then falls back to android command if avdmanager cannot be found (and errors with ENOENT). updated tests. 2017-04-10 12:12:46 -07:00
Steve Gill
a4103d8dc8 updated version in build.gradle 2017-04-07 10:56:39 -07:00
Steve Gill
8e0f021cad Set VERSION to 6.3.0-dev (via coho) 2017-04-02 17:43:13 -05:00
Steve Gill
002ab85f76 CB-12627 Updated RELEASENOTES and Version for release 6.2.1 2017-04-02 17:40:35 -05:00
Steve Gill
4a0f69a3f0 CB-12621: reverted elementtree dep to 0.1.6 2017-04-02 17:11:17 -05:00
Steve Gill
8a2e96d995 Update JS snapshot to version 6.3.0-dev (via coho) 2017-03-28 15:23:50 -07:00
Steve Gill
13dbd2f5d4 Set VERSION to 6.3.0-dev (via coho) 2017-03-28 15:18:35 -07:00
PC Dreams (Paulo Cristo)
bcf3f8611a fix crash when calling methods on a destroyed webview 2016-05-04 08:42:14 +01:00
206 changed files with 18234 additions and 5646 deletions

10
.eslintrc.yml Normal file
View File

@@ -0,0 +1,10 @@
root: true
extends: semistandard
rules:
indent:
- error
- 4
camelcase: off
padded-blocks: off
operator-linebreak: off
no-throw-literal: off

36
.gitignore vendored
View File

@@ -1,11 +1,21 @@
.DS_Store
.gradle
.metadata
Thumbs.db
Desktop.ini
*.tmp
*.bak
*.swp
*.class
*.jar
default.properties
gen
assets/www/cordova.js
local.properties
proguard.cfg
proguard.cfg
proguard-project.txt
example
/coverage
/framework/lib
/framework/build
/framework/bin
@@ -15,30 +25,23 @@ proguard-project.txt
/framework/libs
/framework/javadoc-public
/framework/javadoc-private
/test/libs
example
/test/bin
/test/assets/www/.tmp*
/test/assets/www/cordova.js
/test/.externalNativeBuild
/test/build.gradle
/test/gradle
/test/gradlew
/test/gradlew.bat
/test/assets/www/.tmp*
/test/assets/www/cordova.js
/test/bin
/test/build
.gradle
/test/captures
/test/libs
tmp/**
.metadata
tmp/**/*
Thumbs.db
Desktop.ini
*.tmp
*.bak
*.swp
*.class
*.jar
!/spec/fixtures/org.test.plugins.dummyplugin/src/android/TestLib.jar
# IntelliJ IDEA files
**/.idea/**/*
*.iml
.idea
npm-debug.log
node_modules/jshint
node_modules/promise-matchers
@@ -130,4 +133,3 @@ node_modules/wordwrap/
node_modules/yargs/
node_modules/jasmine-core/
node_modules/fs.realpath/
/coverage

View File

@@ -1,3 +0,0 @@
bin/node_modules/*
bin/templates/project/*
spec/fixtures/*

View File

@@ -1,10 +0,0 @@
{
"node": true
, "bitwise": true
, "undef": true
, "trailing": true
, "quotmark": true
, "indent": 4
, "unused": "vars"
, "latedef": "nofunc"
}

View File

@@ -2,21 +2,27 @@ language: android
sudo: false
jdk:
- oraclejdk8
env:
global:
- ANDROID_TOOLS=${ANDROID_HOME}/tools
before_install:
- nvm install 6
# ensure at least gradle 3.3 is in place.
- wget http://services.gradle.org/distributions/gradle-3.3-bin.zip
- unzip gradle-3.3-bin.zip
- export GRADLE_HOME=$PWD/gradle-3.3
- export PATH=${GRADLE_HOME}/bin:${ANDROID_HOME}:${ANDROID_HOME}/emulator:${ANDROID_TOOLS}:${ANDROID_TOOLS}/bin:${ANDROID_HOME}/platform-tools:$PATH
- node --version
- gradle --version
install:
- npm install
- npm install -g codecov
- echo y | android update sdk -u --filter android-22,android-23,android-24,android-25
- echo y | android --silent update sdk --no-ui --all --filter platform-tools,tools,build-tools-26.0.0,android-26,android-25,extra-google-m2repository,extra-android-m2repository
android:
components:
- tools
- tools
install:
- npm install
- npm install -g codecov
script:
- npm run jshint
- npm test
- npm run cover
- npm run test-build
after_script:
- codecov

View File

@@ -36,7 +36,7 @@ at the core, applications written with web technology: HTML, CSS and JavaScript.
## Requires
- Java JDK 1.6 or greater
- Java JDK 1.8 or greater
- Android SDK [http://developer.android.com](http://developer.android.com)
@@ -62,3 +62,9 @@ These commands live in a generated Cordova Android project. Any interactions wit
1. Create a project
2. Import it via "Non-Android Studio Project"
## Running the Native Tests
The `test/` directory in this project contains an Android test project that can
be used to run different kinds of native tests. Check out the
[README contained therein](test/README.md) for more details!

View File

@@ -20,6 +20,34 @@
-->
## Release Notes for Cordova (Android) ##
### 6.3.0 (Sep 25, 2017)
* [CB-6936](https://issues.apache.org/jira/browse/CB-6936) fix crash when calling methods on a destroyed webview
* [CB-12981](https://issues.apache.org/jira/browse/CB-12981) handle SDK 26.0.2 slightly different AVD list output for **Android** 8+ AVDs. Would cause "cannot read property replace of undefined" errors when trying to deploy an **Android** 8 emulator.
* Updated maven repo to include most recent lib versions
* [CB-13177](https://issues.apache.org/jira/browse/CB-13177) Updating to API Level 26
* Revert [CB-12015](https://issues.apache.org/jira/browse/CB-12015) initial-scale values less than 1.0 are ignored on **Android**
* [CB-12730](https://issues.apache.org/jira/browse/CB-12730) The Cordova Compatibility Plugin is now integrated into cordova-android
* [CB-12453](https://issues.apache.org/jira/browse/CB-12453) Remove unnecessary double quotes from .bat files which are the causes of crash if project path contains spaces
* [CB-13031](https://issues.apache.org/jira/browse/CB-13031) Fix bug with case-sensitivity of **Android**-packageName
* [CB-10916](https://issues.apache.org/jira/browse/CB-10916) Support display name for **Android**
* [CB-12423](https://issues.apache.org/jira/browse/CB-12423) make explicit JDK 1.8 or greater is needed in the `README`, we require 1.8 for compilation, but do not have 1.8 Java features yet
* [CB-13006](https://issues.apache.org/jira/browse/CB-13006) removed create and update end-to-end tests, and instead added more unit test coverage. tweaked code coverage invocation so that we get coverage details on the create.js module. slight changes to the create.js module so that it is slightly easier to test.
* [CB-12950](https://issues.apache.org/jira/browse/CB-12950) lots of tweaks for end-to-end test runs, especially on CI: - rename npm tasks to reflect what they do (npm run unit-tests, npm run e2e-tests). main `npm test` runs linter, unit tests and e2e tests now. - locked jasmine down to ~2.6.0. - consolidate gitignores. - updated travis to run `npm test`. add **Android** sdk installation to appveyor ci run.align **Android** dpendencies across travis and appveyor. have appveyor install gradle. force gradle to version 3.4.1 in appveyor, as that seems to be the only version choco has. explicitly invoke sdkmanager to move license accepting process along.
* [CB-12605](https://issues.apache.org/jira/browse/CB-12605) In **Windows** get **Android** studio path from the registry
* [CB-12762](https://issues.apache.org/jira/browse/CB-12762) : pointed `package.json` repo items to github mirrors instead of apache repos site
* [CB-12617](https://issues.apache.org/jira/browse/CB-12617) : removed node0.x support for platforms and added engineStrict
### 6.2.3 (May 2, 2017)
* [CB-12640](https://issues.apache.org/jira/browse/CB-12640) better handling of unrecognized Android SDK commands on **Windows**.
* [CB-12640](https://issues.apache.org/jira/browse/CB-12640) flipped avd parsing logic so that it always tries to use avdmanager to retrieve avds first, then falls back to android command if avdmanager cannot be found (and errors with ENOENT). updated tests - and added explicit tests to ensure to shell out to singular forms of sub-commands when executing `android`
* [CB-12640](https://issues.apache.org/jira/browse/CB-12640) support for android sdk tools 26.0.1.
### 6.2.2 (Apr 24, 2017)
* [CB-12697](https://issues.apache.org/jira/browse/CB-12697) Updated checked-in `node_modules`
### 6.2.1 (Apr 02, 2017)
* [CB-12621](https://issues.apache.org/jira/browse/CB-12621) reverted elementtree dep to 0.1.6
### 6.2.0 (Mar 28, 2017)
* [CB-12614](https://issues.apache.org/jira/browse/CB-12614) Adding headers to tests
* [CB-8978](https://issues.apache.org/jira/browse/CB-8978) Prepare copy `resource-files` from `config.xml`

View File

@@ -1 +1 @@
6.2.0-dev
6.3.0

View File

@@ -1,20 +1,37 @@
image:
- Previous Visual Studio 2015
environment:
ANDROID_HOME: "C:\\android"
matrix:
- nodejs_version: "0.10"
- nodejs_version: "0.12"
- nodejs_version: "4"
- nodejs_version: "6"
init:
- mkdir "%ANDROID_HOME%
- cd "%ANDROID_HOME%"
- appveyor DownloadFile "https://dl.google.com/android/repository/tools_r25.2.3-windows.zip"
- 7z x "tools_r25.2.3-windows.zip" > nul
- cd "C:\projects\cordova-android"
install:
# - cinst android-sdk
# - echo y | android update sdk -u --filter android-22,android-23
- choco install gradle -version 3.4.1
- gradle -version
- echo y | "%ANDROID_HOME%\tools\android.bat" --silent update sdk --no-ui --all --filter platform-tools,tools,build-tools-26.0.0,android-26,android-25,extra-google-m2repository,extra-android-m2repository
# on windows we need to accept sublicenses for the new tooling, wee. 30 is an arbitrary number,
# but should be the maximum number of licenses we explicitly need to type "Y ENTER" for.
# also, the sdkmanager in all its glory leaks a bit of output to stderr, and powershell
# and appveyor interpret that as errors, and blows up. so, when piping in our "Y ENTER"
# responses, we invoke cmd so we can redirect stderr to stdout, and tell it to --update itself.
- ps: for($i=0;$i -lt 30;$i++) { $response += "y`n"}; $response | cmd /c 'C:\android\tools\bin\sdkmanager.bat 2>&1' --update
- ps: Install-Product node $env:nodejs_version
- npm install
# below is a workaround on using gradle installed via choco on appveyor
- set path=C:\ProgramData\chocolatey\lib\gradle\tools\gradle-3.4.1\bin;%path%
build: off
test_script:
- node --version
- npm --version
- npm run test
# - npm run test-build
- npm test

View File

@@ -18,7 +18,7 @@
@ECHO OFF
SET script_path="%~dp0android_sdk_version"
IF EXIST %script_path% (
node "%script_path%" %*
node %script_path% %*
) ELSE (
ECHO.
ECHO ERROR: Could not find 'android_sdk_version' script in 'bin' folder, aborting...>&2

View File

@@ -18,7 +18,7 @@
@ECHO OFF
SET script_path="%~dp0check_reqs"
IF EXIST %script_path% (
node "%script_path%" %*
node %script_path% %*
) ELSE (
ECHO.
ECHO ERROR: Could not find 'check_reqs' script in 'bin' folder, aborting...>&2

View File

@@ -19,30 +19,42 @@
under the License.
*/
var shell = require('shelljs'),
Q = require('q'),
path = require('path'),
fs = require('fs'),
check_reqs = require('./../templates/cordova/lib/check_reqs'),
ROOT = path.join(__dirname, '..', '..');
var shell = require('shelljs');
var Q = require('q');
var path = require('path');
var fs = require('fs');
var check_reqs = require('./../templates/cordova/lib/check_reqs');
var ROOT = path.join(__dirname, '..', '..');
var MIN_SDK_VERSION = 16;
var CordovaError = require('cordova-common').CordovaError;
var AndroidManifest = require('../templates/cordova/lib/AndroidManifest');
function setShellFatal(value, func) {
// Export all helper functions, and make sure internally within this module, we
// reference these methods via the `exports` object - this helps with testing
// (since we can then mock and control behaviour of all of these functions)
exports.validatePackageName = validatePackageName;
exports.validateProjectName = validateProjectName;
exports.setShellFatal = setShellFatal;
exports.copyJsAndLibrary = copyJsAndLibrary;
exports.copyScripts = copyScripts;
exports.copyBuildRules = copyBuildRules;
exports.writeProjectProperties = writeProjectProperties;
exports.prepBuildFiles = prepBuildFiles;
function setShellFatal (value, func) {
var oldVal = shell.config.fatal;
shell.config.fatal = value;
func();
shell.config.fatal = oldVal;
}
function getFrameworkDir(projectPath, shared) {
function getFrameworkDir (projectPath, shared) {
return shared ? path.join(ROOT, 'framework') : path.join(projectPath, 'CordovaLib');
}
function copyJsAndLibrary(projectPath, shared, projectName) {
function copyJsAndLibrary (projectPath, shared, projectName) {
var nestedCordovaLibPath = getFrameworkDir(projectPath, false);
var srcCordovaJsPath = path.join(ROOT, 'bin', 'templates', 'project', 'assets', 'www', 'cordova.js');
shell.cp('-f', srcCordovaJsPath, path.join(projectPath, 'assets', 'www', 'cordova.js'));
@@ -57,8 +69,8 @@ function copyJsAndLibrary(projectPath, shared, projectName) {
shell.cp('-rf', path.join(ROOT, 'cordova-js-src'), path.join(projectPath, 'platform_www'));
// Don't fail if there are no old jars.
setShellFatal(false, function() {
shell.ls(path.join(projectPath, 'libs', 'cordova-*.jar')).forEach(function(oldJar) {
exports.setShellFatal(false, function () {
shell.ls(path.join(projectPath, 'libs', 'cordova-*.jar')).forEach(function (oldJar) {
console.log('Deleting ' + oldJar);
shell.rm('-f', oldJar);
});
@@ -90,7 +102,7 @@ function copyJsAndLibrary(projectPath, shared, projectName) {
}
}
function extractSubProjectPaths(data) {
function extractSubProjectPaths (data) {
var ret = {};
var r = /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg;
var m;
@@ -100,7 +112,7 @@ function extractSubProjectPaths(data) {
return Object.keys(ret);
}
function writeProjectProperties(projectPath, target_api) {
function writeProjectProperties (projectPath, target_api) {
var dstPath = path.join(projectPath, 'project.properties');
var templatePath = path.join(ROOT, 'bin', 'templates', 'project', 'project.properties');
var srcPath = fs.existsSync(dstPath) ? dstPath : templatePath;
@@ -108,11 +120,10 @@ function writeProjectProperties(projectPath, target_api) {
var data = fs.readFileSync(srcPath, 'utf8');
data = data.replace(/^target=.*/m, 'target=' + target_api);
var subProjects = extractSubProjectPaths(data);
subProjects = subProjects.filter(function(p) {
subProjects = subProjects.filter(function (p) {
return !(/^CordovaLib$/m.exec(p) ||
/[\\\/]cordova-android[\\\/]framework$/m.exec(p) ||
/^(\.\.[\\\/])+framework$/m.exec(p)
);
/[\\/]cordova-android[\\/]framework$/m.exec(p) ||
/^(\.\.[\\/])+framework$/m.exec(p));
});
subProjects.unshift('CordovaLib');
data = data.replace(/^\s*android\.library\.reference\.\d+=.*\n/mg, '');
@@ -120,24 +131,24 @@ function writeProjectProperties(projectPath, target_api) {
data += '\n';
}
for (var i = 0; i < subProjects.length; ++i) {
data += 'android.library.reference.' + (i+1) + '=' + subProjects[i] + '\n';
data += 'android.library.reference.' + (i + 1) + '=' + subProjects[i] + '\n';
}
fs.writeFileSync(dstPath, data);
}
function prepBuildFiles(projectPath) {
function prepBuildFiles (projectPath) {
var buildModule = require(path.resolve(projectPath, 'cordova/lib/builders/builders'));
buildModule.getBuilder('gradle').prepBuildFiles();
}
function copyBuildRules(projectPath) {
function copyBuildRules (projectPath) {
var srcDir = path.join(ROOT, 'bin', 'templates', 'project');
shell.cp('-f', path.join(srcDir, 'build.gradle'), projectPath);
shell.cp('-f', path.join(srcDir, 'wrapper.gradle'), projectPath);
}
function copyScripts(projectPath) {
function copyScripts (projectPath) {
var bin = path.join(ROOT, 'bin');
var srcScriptsDir = path.join(bin, 'templates', 'cordova');
var destScriptsDir = path.join(projectPath, 'cordova');
@@ -164,17 +175,18 @@ function copyScripts(projectPath) {
* Returns a promise, fulfilled if the package name is acceptable; rejected
* otherwise.
*/
function validatePackageName(package_name) {
//Make the package conform to Java package types
//http://developer.android.com/guide/topics/manifest/manifest-element.html#package
//Enforce underscore limitation
function validatePackageName (package_name) {
// Make the package conform to Java package types
// http://developer.android.com/guide/topics/manifest/manifest-element.html#package
// Enforce underscore limitation
var msg = 'Error validating package name. ';
if (!/^[a-zA-Z][a-zA-Z0-9_]+(\.[a-zA-Z][a-zA-Z0-9_]*)+$/.test(package_name)) {
return Q.reject(new CordovaError(msg + 'Package name must look like: com.company.Name'));
}
//Class is a reserved word
if(/\b[Cc]lass\b/.test(package_name)) {
// Class is a reserved word
if (/\b[Cc]lass\b/.test(package_name)) {
return Q.reject(new CordovaError(msg + '"class" is a reserved word'));
}
@@ -186,19 +198,19 @@ function validatePackageName(package_name) {
* Returns a promise, fulfilled if the project name is acceptable; rejected
* otherwise.
*/
function validateProjectName(project_name) {
function validateProjectName (project_name) {
var msg = 'Error validating project name. ';
//Make sure there's something there
// Make sure there's something there
if (project_name === '') {
return Q.reject(new CordovaError(msg + 'Project name cannot be empty'));
}
//Enforce stupid name error
// Enforce stupid name error
if (project_name === 'CordovaActivity') {
return Q.reject(new CordovaError(msg + 'Project name cannot be CordovaActivity'));
}
//Classes in Java don't begin with numbers
// Classes in Java don't begin with numbers
if (/^[0-9]/.test(project_name)) {
return Q.reject(new CordovaError(msg + 'Project name must not begin with a number'));
}
@@ -224,83 +236,83 @@ function validateProjectName(project_name) {
*
* @return {Promise<String>} Directory where application has been created
*/
exports.create = function(project_path, config, options, events) {
exports.create = function (project_path, config, options, events) {
options = options || {};
// Set default values for path, package and name
project_path = path.relative(process.cwd(), (project_path || 'CordovaExample'));
// Check if project already exists
if(fs.existsSync(project_path)) {
if (fs.existsSync(project_path)) {
return Q.reject(new CordovaError('Project already exists! Delete and recreate'));
}
var package_name = config.packageName() || 'my.cordova.project';
var package_name = config.android_packageName() || config.packageName() || 'my.cordova.project';
var project_name = config.name() ?
config.name().replace(/[^\w.]/g,'_') : 'CordovaExample';
config.name().replace(/[^\w.]/g, '_') : 'CordovaExample';
var safe_activity_name = config.android_activityName() || options.activityName || 'MainActivity';
var target_api = check_reqs.get_target();
var target_api = check_reqs.get_target();
//Make the package conform to Java package types
return validatePackageName(package_name)
.then(function() {
validateProjectName(project_name);
}).then(function() {
// Make the package conform to Java package types
return exports.validatePackageName(package_name)
.then(function () {
exports.validateProjectName(project_name);
}).then(function () {
// Log the given values for the project
events.emit('log', 'Creating Cordova project for the Android platform:');
events.emit('log', '\tPath: ' + project_path);
events.emit('log', '\tPackage: ' + package_name);
events.emit('log', '\tName: ' + project_name);
events.emit('log', '\tActivity: ' + safe_activity_name);
events.emit('log', '\tAndroid target: ' + target_api);
events.emit('log', 'Creating Cordova project for the Android platform:');
events.emit('log', '\tPath: ' + project_path);
events.emit('log', '\tPackage: ' + package_name);
events.emit('log', '\tName: ' + project_name);
events.emit('log', '\tActivity: ' + safe_activity_name);
events.emit('log', '\tAndroid target: ' + target_api);
events.emit('verbose', 'Copying android template project to ' + project_path);
events.emit('verbose', 'Copying android template project to ' + project_path);
setShellFatal(true, function() {
var project_template_dir = options.customTemplate || path.join(ROOT, 'bin', 'templates', 'project');
// copy project template
shell.cp('-r', path.join(project_template_dir, 'assets'), project_path);
shell.cp('-r', path.join(project_template_dir, 'res'), project_path);
shell.cp(path.join(project_template_dir, 'gitignore'), path.join(project_path, '.gitignore'));
exports.setShellFatal(true, function () {
var project_template_dir = options.customTemplate || path.join(ROOT, 'bin', 'templates', 'project');
// copy project template
shell.cp('-r', path.join(project_template_dir, 'assets'), project_path);
shell.cp('-r', path.join(project_template_dir, 'res'), project_path);
shell.cp(path.join(project_template_dir, 'gitignore'), path.join(project_path, '.gitignore'));
// Manually create directories that would be empty within the template (since git doesn't track directories).
shell.mkdir(path.join(project_path, 'libs'));
// Manually create directories that would be empty within the template (since git doesn't track directories).
shell.mkdir(path.join(project_path, 'libs'));
// copy cordova.js, cordova.jar
copyJsAndLibrary(project_path, options.link, safe_activity_name);
// copy cordova.js, cordova.jar
exports.copyJsAndLibrary(project_path, options.link, safe_activity_name);
// interpolate the activity name and package
var packagePath = package_name.replace(/\./g, path.sep);
var activity_dir = path.join(project_path, 'src', packagePath);
var activity_path = path.join(activity_dir, safe_activity_name + '.java');
shell.mkdir('-p', activity_dir);
shell.cp('-f', path.join(project_template_dir, 'Activity.java'), activity_path);
shell.sed('-i', /__ACTIVITY__/, safe_activity_name, activity_path);
shell.sed('-i', /__NAME__/, project_name, path.join(project_path, 'res', 'values', 'strings.xml'));
shell.sed('-i', /__ID__/, package_name, activity_path);
// interpolate the activity name and package
var packagePath = package_name.replace(/\./g, path.sep);
var activity_dir = path.join(project_path, 'src', packagePath);
var activity_path = path.join(activity_dir, safe_activity_name + '.java');
shell.mkdir('-p', activity_dir);
shell.cp('-f', path.join(project_template_dir, 'Activity.java'), activity_path);
shell.sed('-i', /__ACTIVITY__/, safe_activity_name, activity_path);
shell.sed('-i', /__NAME__/, project_name, path.join(project_path, 'res', 'values', 'strings.xml'));
shell.sed('-i', /__ID__/, package_name, activity_path);
var manifest = new AndroidManifest(path.join(project_template_dir, 'AndroidManifest.xml'));
manifest.setPackageId(package_name)
.setTargetSdkVersion(target_api.split('-')[1])
.getActivity().setName(safe_activity_name);
var manifest = new AndroidManifest(path.join(project_template_dir, 'AndroidManifest.xml'));
manifest.setPackageId(package_name)
.setTargetSdkVersion(target_api.split('-')[1])
.getActivity().setName(safe_activity_name);
var manifest_path = path.join(project_path, 'AndroidManifest.xml');
manifest.write(manifest_path);
var manifest_path = path.join(project_path, 'AndroidManifest.xml');
manifest.write(manifest_path);
copyScripts(project_path);
copyBuildRules(project_path);
});
// Link it to local android install.
writeProjectProperties(project_path, target_api);
prepBuildFiles(project_path);
events.emit('log', generateDoneMessage('create', options.link));
}).thenResolve(project_path);
exports.copyScripts(project_path);
exports.copyBuildRules(project_path);
});
// Link it to local android install.
exports.writeProjectProperties(project_path, target_api);
exports.prepBuildFiles(project_path);
events.emit('log', generateDoneMessage('create', options.link));
}).thenResolve(project_path);
};
function generateDoneMessage(type, link) {
function generateDoneMessage (type, link) {
var pkg = require('../../package');
var msg = 'Android project ' + (type == 'update' ? 'updated ' : 'created ') + 'with ' + pkg.name + '@' + pkg.version;
var msg = 'Android project ' + (type === 'update' ? 'updated ' : 'created ') + 'with ' + pkg.name + '@' + pkg.version;
if (link) {
msg += ' and has a linked CordovaLib';
}
@@ -308,34 +320,29 @@ function generateDoneMessage(type, link) {
}
// Returns a promise.
exports.update = function(projectPath, options, events) {
exports.update = function (projectPath, options, events) {
options = options || {};
return Q()
.then(function() {
.then(function () {
var manifest = new AndroidManifest(path.join(projectPath, 'AndroidManifest.xml'));
var manifest = new AndroidManifest(path.join(projectPath, 'AndroidManifest.xml'));
if (Number(manifest.getMinSdkVersion()) < MIN_SDK_VERSION) {
events.emit('verbose', 'Updating minSdkVersion to ' + MIN_SDK_VERSION + ' in AndroidManifest.xml');
manifest.setMinSdkVersion(MIN_SDK_VERSION);
}
if (Number(manifest.getMinSdkVersion()) < MIN_SDK_VERSION) {
events.emit('verbose', 'Updating minSdkVersion to ' + MIN_SDK_VERSION + ' in AndroidManifest.xml');
manifest.setMinSdkVersion(MIN_SDK_VERSION);
}
manifest.setDebuggable(false).write();
manifest.setDebuggable(false).write();
var projectName = manifest.getActivity().getName();
var target_api = check_reqs.get_target();
var projectName = manifest.getActivity().getName();
var target_api = check_reqs.get_target();
copyJsAndLibrary(projectPath, options.link, projectName);
copyScripts(projectPath);
copyBuildRules(projectPath);
writeProjectProperties(projectPath, target_api);
prepBuildFiles(projectPath);
events.emit('log', generateDoneMessage('update', options.link));
}).thenResolve(projectPath);
exports.copyJsAndLibrary(projectPath, options.link, projectName);
exports.copyScripts(projectPath);
exports.copyBuildRules(projectPath);
exports.writeProjectProperties(projectPath, target_api);
exports.prepBuildFiles(projectPath);
events.emit('log', generateDoneMessage('update', options.link));
}).thenResolve(projectPath);
};
// For testing
exports.validatePackageName = validatePackageName;
exports.validateProjectName = validateProjectName;

View File

@@ -1,10 +0,0 @@
{
"node": true
, "bitwise": true
, "undef": true
, "trailing": true
, "quotmark": true
, "indent": 4
, "unused": "vars"
, "latedef": "nofunc"
}

View File

@@ -29,8 +29,7 @@ var selfEvents = require('cordova-common').events;
var PLATFORM = 'android';
function setupEvents(externalEventEmitter) {
function setupEvents (externalEventEmitter) {
if (externalEventEmitter) {
// This will make the platform internal events visible outside
selfEvents.forwardEventsTo(externalEventEmitter);
@@ -43,7 +42,6 @@ function setupEvents(externalEventEmitter) {
return selfEvents;
}
/**
* Class, that acts as abstraction over particular platform. Encapsulates the
* platform's properties and methods.
@@ -55,7 +53,7 @@ function setupEvents(externalEventEmitter) {
*
* * platform: String that defines a platform name.
*/
function Api(platform, platformRootDir, events) {
function Api (platform, platformRootDir, events) {
this.platform = PLATFORM;
this.root = path.resolve(__dirname, '..');
@@ -79,14 +77,14 @@ function Api(platform, platformRootDir, events) {
};
// XXX Override some locations for Android Studio projects
if(AndroidStudio.isAndroidStudioProject(self.root) === true) {
selfEvents.emit('log', 'Android Studio project detected');
this.android_studio = true;
this.locations.configXml = path.join(self.root, 'app/src/main/res/xml/config.xml');
this.locations.strings = path.join(self.root, 'app/src/main/res/xml/strings.xml');
this.locations.manifest = path.join(self.root, 'app/src/main/AndroidManifest.xml');
this.locations.www = path.join(self.root, 'app/src/main/assets/www');
this.locations.res = path.join(self.root, 'app/src/main/res');
if (AndroidStudio.isAndroidStudioProject(self.root) === true) {
selfEvents.emit('log', 'Android Studio project detected');
this.android_studio = true;
this.locations.configXml = path.join(self.root, 'app/src/main/res/xml/config.xml');
this.locations.strings = path.join(self.root, 'app/src/main/res/xml/strings.xml');
this.locations.manifest = path.join(self.root, 'app/src/main/AndroidManifest.xml');
this.locations.www = path.join(self.root, 'app/src/main/assets/www');
this.locations.res = path.join(self.root, 'app/src/main/res');
}
}
@@ -112,16 +110,13 @@ Api.createPlatform = function (destination, config, options, events) {
events = setupEvents(events);
var result;
try {
result = require('../../lib/create')
.create(destination, config, options, events)
.then(function (destination) {
result = require('../../lib/create').create(destination, config, options, events).then(function (destination) {
var PlatformApi = require(path.resolve(destination, 'cordova/Api'));
return new PlatformApi(PLATFORM, destination, events);
});
}
catch (e) {
events.emit('error','createPlatform is not callable from the android project API.');
throw(e);
} catch (e) {
events.emit('error', 'createPlatform is not callable from the android project API.');
throw (e);
}
return result;
};
@@ -146,16 +141,13 @@ Api.updatePlatform = function (destination, options, events) {
events = setupEvents(events);
var result;
try {
result = require('../../lib/create')
.update(destination, options, events)
.then(function (destination) {
result = require('../../lib/create').update(destination, options, events).then(function (destination) {
var PlatformApi = require(path.resolve(destination, 'cordova/Api'));
return new PlatformApi('android', destination, events);
});
}
catch (e) {
events.emit('error','updatePlatform is not callable from the android project API, you will need to do this manually.');
throw(e);
} catch (e) {
events.emit('error', 'updatePlatform is not callable from the android project API, you will need to do this manually.');
throw (e);
}
return result;
};
@@ -226,40 +218,36 @@ Api.prototype.addPlugin = function (plugin, installOptions) {
installOptions.variables.PACKAGE_NAME = project.getPackageName();
}
if(this.android_studio === true) {
installOptions.android_studio = true;
if (this.android_studio === true) {
installOptions.android_studio = true;
}
return Q()
.then(function () {
//CB-11964: Do a clean when installing the plugin code to get around
//the Gradle bug introduced by the Android Gradle Plugin Version 2.2
//TODO: Delete when the next version of Android Gradle plugin comes out
return Q().then(function () {
// CB-11964: Do a clean when installing the plugin code to get around
// the Gradle bug introduced by the Android Gradle Plugin Version 2.2
// TODO: Delete when the next version of Android Gradle plugin comes out
// Since clean doesn't just clean the build, it also wipes out www, we need
// to pass additional options.
// Since clean doesn't just clean the build, it also wipes out www, we need
// to pass additional options.
// Do some basic argument parsing
var opts = {};
// Do some basic argument parsing
var opts = {};
// Skip cleaning prepared files when not invoking via cordova CLI.
opts.noPrepare = true;
// Skip cleaning prepared files when not invoking via cordova CLI.
opts.noPrepare = true;
if(!AndroidStudio.isAndroidStudioProject(self.root) && !project.isClean()) {
return self.clean(opts);
}
})
.then(function () {
return PluginManager.get(self.platform, self.locations, project)
.addPlugin(plugin, installOptions);
})
.then(function () {
if (plugin.getFrameworks(this.platform).length === 0) return;
if (!AndroidStudio.isAndroidStudioProject(self.root) && !project.isClean()) {
return self.clean(opts);
}
}).then(function () {
return PluginManager.get(self.platform, self.locations, project).addPlugin(plugin, installOptions);
}).then(function () {
if (plugin.getFrameworks(this.platform).length === 0) return;
selfEvents.emit('verbose', 'Updating build files since android plugin contained <framework>');
require('./lib/builders/builders').getBuilder('gradle').prepBuildFiles();
}.bind(this))
// CB-11022 Return truthy value to prevent running prepare after
selfEvents.emit('verbose', 'Updating build files since android plugin contained <framework>');
require('./lib/builders/builders').getBuilder('gradle').prepBuildFiles();
}.bind(this))
// CB-11022 Return truthy value to prevent running prepare after
.thenResolve(true);
};
@@ -279,9 +267,9 @@ Api.prototype.addPlugin = function (plugin, installOptions) {
Api.prototype.removePlugin = function (plugin, uninstallOptions) {
var project = AndroidProject.getProjectFile(this.root);
if(uninstallOptions && uninstallOptions.usePlatformWww === true && this.android_studio === true) {
uninstallOptions.usePlatformWww = false;
uninstallOptions.android_studio = true;
if (uninstallOptions && uninstallOptions.usePlatformWww === true && this.android_studio === true) {
uninstallOptions.usePlatformWww = false;
uninstallOptions.android_studio = true;
}
return PluginManager.get(this.platform, this.locations, project)
@@ -343,11 +331,9 @@ Api.prototype.removePlugin = function (plugin, uninstallOptions) {
*/
Api.prototype.build = function (buildOptions) {
var self = this;
return require('./lib/check_reqs').run()
.then(function () {
return require('./lib/check_reqs').run().then(function () {
return require('./lib/build').run.call(self, buildOptions);
})
.then(function (buildResults) {
}).then(function (buildResults) {
// Cast build result to array of build artifacts
return buildResults.apkPaths.map(function (apkPath) {
return {
@@ -372,10 +358,9 @@ Api.prototype.build = function (buildOptions) {
* @return {Promise} A promise either fulfilled if package was built and ran
* successfully, or rejected with CordovaError.
*/
Api.prototype.run = function(runOptions) {
Api.prototype.run = function (runOptions) {
var self = this;
return require('./lib/check_reqs').run()
.then(function () {
return require('./lib/check_reqs').run().then(function () {
return require('./lib/run').run.call(self, runOptions);
});
};
@@ -387,19 +372,15 @@ Api.prototype.run = function(runOptions) {
* @return {Promise} Return a promise either fulfilled, or rejected with
* CordovaError.
*/
Api.prototype.clean = function(cleanOptions) {
Api.prototype.clean = function (cleanOptions) {
var self = this;
return require('./lib/check_reqs').run()
.then(function () {
return require('./lib/build').runClean.call(self, cleanOptions);
})
.then(function () {
return require('./lib/prepare').clean.call(self, cleanOptions);
});
return require('./lib/check_reqs').run().then(function () {
return require('./lib/build').runClean.call(self, cleanOptions);
}).then(function () {
return require('./lib/prepare').clean.call(self, cleanOptions);
});
};
/**
* Performs a requirements check for current platform. Each platform defines its
* own set of requirements, which should be resolved before platform can be
@@ -408,7 +389,7 @@ Api.prototype.clean = function(cleanOptions) {
* @return {Promise<Requirement[]>} Promise, resolved with set of Requirement
* objects for current platform.
*/
Api.prototype.requirements = function() {
Api.prototype.requirements = function () {
return require('./lib/check_reqs').check_all();
};

View File

@@ -25,11 +25,11 @@ var CordovaError = require('cordova-common').CordovaError;
var Adb = {};
function isDevice(line) {
function isDevice (line) {
return line.match(/\w+\tdevice/) && !line.match(/emulator/);
}
function isEmulator(line) {
function isEmulator (line) {
return line.match(/device/) && line.match(/emulator/);
}
@@ -44,8 +44,7 @@ function isEmulator(line) {
* devices/emulators
*/
Adb.devices = function (opts) {
return spawn('adb', ['devices'], {cwd: os.tmpdir()})
.then(function(output) {
return spawn('adb', ['devices'], {cwd: os.tmpdir()}).then(function (output) {
return output.split('\n').filter(function (line) {
// Filter out either real devices or emulators, depending on options
return (line && opts && opts.emulators) ? isEmulator(line) : isDevice(line);
@@ -59,8 +58,7 @@ Adb.install = function (target, packagePath, opts) {
events.emit('verbose', 'Installing apk ' + packagePath + ' on target ' + target + '...');
var args = ['-s', target, 'install'];
if (opts && opts.replace) args.push('-r');
return spawn('adb', args.concat(packagePath), {cwd: os.tmpdir()})
.then(function(output) {
return spawn('adb', args.concat(packagePath), {cwd: os.tmpdir()}).then(function (output) {
// 'adb install' seems to always returns no error, even if installation fails
// so we catching output to detect installation failure
if (output.match(/Failure/)) {
@@ -86,8 +84,7 @@ Adb.shell = function (target, shellCommand) {
events.emit('verbose', 'Running adb shell command "' + shellCommand + '" on target ' + target + '...');
var args = ['-s', target, 'shell'];
shellCommand = shellCommand.split(/\s+/);
return spawn('adb', args.concat(shellCommand), {cwd: os.tmpdir()})
.catch(function (output) {
return spawn('adb', args.concat(shellCommand), {cwd: os.tmpdir()}).catch(function (output) {
return Q.reject(new CordovaError('Failed to execute shell command "' +
shellCommand + '"" on device: ' + output));
});
@@ -95,8 +92,7 @@ Adb.shell = function (target, shellCommand) {
Adb.start = function (target, activityName) {
events.emit('verbose', 'Starting application "' + activityName + '" on target ' + target + '...');
return Adb.shell(target, 'am start -W -a android.intent.action.MAIN -n' + activityName)
.catch(function (output) {
return Adb.shell(target, 'am start -W -a android.intent.action.MAIN -n' + activityName).catch(function (output) {
return Q.reject(new CordovaError('Failed to start application "' +
activityName + '"" on device: ' + output));
});

View File

@@ -19,12 +19,12 @@
var fs = require('fs');
var et = require('elementtree');
var xml= require('cordova-common').xmlHelpers;
var xml = require('cordova-common').xmlHelpers;
var DEFAULT_ORIENTATION = 'default';
/** Wraps an AndroidManifest file */
function AndroidManifest(path) {
function AndroidManifest (path) {
this.path = path;
this.doc = xml.parseElementtreeSync(path);
if (this.doc.getroot().tag !== 'manifest') {
@@ -32,38 +32,38 @@ function AndroidManifest(path) {
}
}
AndroidManifest.prototype.getVersionName = function() {
AndroidManifest.prototype.getVersionName = function () {
return this.doc.getroot().attrib['android:versionName'];
};
AndroidManifest.prototype.setVersionName = function(versionName) {
AndroidManifest.prototype.setVersionName = function (versionName) {
this.doc.getroot().attrib['android:versionName'] = versionName;
return this;
};
AndroidManifest.prototype.getVersionCode = function() {
AndroidManifest.prototype.getVersionCode = function () {
return this.doc.getroot().attrib['android:versionCode'];
};
AndroidManifest.prototype.setVersionCode = function(versionCode) {
AndroidManifest.prototype.setVersionCode = function (versionCode) {
this.doc.getroot().attrib['android:versionCode'] = versionCode;
return this;
};
AndroidManifest.prototype.getPackageId = function() {
/*jshint -W069 */
AndroidManifest.prototype.getPackageId = function () {
/* jshint -W069 */
return this.doc.getroot().attrib['package'];
/*jshint +W069 */
/* jshint +W069 */
};
AndroidManifest.prototype.setPackageId = function(pkgId) {
/*jshint -W069 */
AndroidManifest.prototype.setPackageId = function (pkgId) {
/* jshint -W069 */
this.doc.getroot().attrib['package'] = pkgId;
/*jshint +W069 */
/* jshint +W069 */
return this;
};
AndroidManifest.prototype.getActivity = function() {
AndroidManifest.prototype.getActivity = function () {
var activity = this.doc.getroot().find('./application/activity');
return {
getName: function () {
@@ -102,17 +102,16 @@ AndroidManifest.prototype.getActivity = function() {
};
};
['minSdkVersion', 'maxSdkVersion', 'targetSdkVersion']
.forEach(function(sdkPrefName) {
['minSdkVersion', 'maxSdkVersion', 'targetSdkVersion'].forEach(function (sdkPrefName) {
// Copy variable reference to avoid closure issues
var prefName = sdkPrefName;
AndroidManifest.prototype['get' + capitalize(prefName)] = function() {
AndroidManifest.prototype['get' + capitalize(prefName)] = function () {
var usesSdk = this.doc.getroot().find('./uses-sdk');
return usesSdk && usesSdk.attrib['android:' + prefName];
};
AndroidManifest.prototype['set' + capitalize(prefName)] = function(prefValue) {
AndroidManifest.prototype['set' + capitalize(prefName)] = function (prefValue) {
var usesSdk = this.doc.getroot().find('./uses-sdk');
if (!usesSdk && prefValue) { // if there is no required uses-sdk element, we should create it first
@@ -128,11 +127,11 @@ AndroidManifest.prototype.getActivity = function() {
};
});
AndroidManifest.prototype.getDebuggable = function() {
AndroidManifest.prototype.getDebuggable = function () {
return this.doc.getroot().find('./application').attrib['android:debuggable'] === 'true';
};
AndroidManifest.prototype.setDebuggable = function(value) {
AndroidManifest.prototype.setDebuggable = function (value) {
var application = this.doc.getroot().find('./application');
if (value) {
application.attrib['android:debuggable'] = 'true';
@@ -150,7 +149,7 @@ AndroidManifest.prototype.setDebuggable = function(value) {
* @param {String} [destPath] File to write manifest to. If omitted,
* manifest will be written to file it has been read from.
*/
AndroidManifest.prototype.write = function(destPath) {
AndroidManifest.prototype.write = function (destPath) {
fs.writeFileSync(destPath || this.path, this.doc.write({indent: 4}), 'utf-8');
};

View File

@@ -26,16 +26,15 @@ var pluginHandlers = require('./pluginHandlers');
var projectFileCache = {};
function addToPropertyList(projectProperties, key, value) {
function addToPropertyList (projectProperties, key, value) {
var i = 1;
while (projectProperties.get(key + '.' + i))
i++;
while (projectProperties.get(key + '.' + i)) { i++; }
projectProperties.set(key + '.' + i, value);
projectProperties.dirty = true;
}
function removeFromPropertyList(projectProperties, key, value) {
function removeFromPropertyList (projectProperties, key, value) {
var i = 1;
var currentValue;
while ((currentValue = projectProperties.get(key + '.' + i))) {
@@ -54,18 +53,18 @@ function removeFromPropertyList(projectProperties, key, value) {
function getRelativeLibraryPath (parentDir, subDir) {
var libraryPath = path.relative(parentDir, subDir);
return (path.sep == '\\') ? libraryPath.replace(/\\/g, '/') : libraryPath;
return (path.sep === '\\') ? libraryPath.replace(/\\/g, '/') : libraryPath;
}
function AndroidProject(projectDir) {
function AndroidProject (projectDir) {
this._propertiesEditors = {};
this._subProjectDirs = {};
this._dirty = false;
this.projectDir = projectDir;
this.platformWww = path.join(this.projectDir, 'platform_www');
this.www = path.join(this.projectDir, 'assets/www');
if(AndroidStudio.isAndroidStudioProject(projectDir) === true) {
this.www = path.join(this.projectDir, 'app/src/main/assets/www');
if (AndroidStudio.isAndroidStudioProject(projectDir) === true) {
this.www = path.join(this.projectDir, 'app/src/main/assets/www');
}
}
@@ -92,15 +91,15 @@ AndroidProject.purgeCache = function (projectDir) {
*
* @return {String} The name of the package
*/
AndroidProject.prototype.getPackageName = function() {
AndroidProject.prototype.getPackageName = function () {
var manifestPath = path.join(this.projectDir, 'AndroidManifest.xml');
if(AndroidStudio.isAndroidStudioProject(this.projectDir) === true) {
manifestPath = path.join(this.projectDir, 'app/src/main/AndroidManifest.xml');
if (AndroidStudio.isAndroidStudioProject(this.projectDir) === true) {
manifestPath = path.join(this.projectDir, 'app/src/main/AndroidManifest.xml');
}
return new AndroidManifest(manifestPath).getPackageId();
};
AndroidProject.prototype.getCustomSubprojectRelativeDir = function(plugin_id, src) {
AndroidProject.prototype.getCustomSubprojectRelativeDir = function (plugin_id, src) {
// All custom subprojects are prefixed with the last portion of the package id.
// This is to avoid collisions when opening multiple projects in Eclipse that have subprojects with the same name.
var packageName = this.getPackageName();
@@ -110,7 +109,7 @@ AndroidProject.prototype.getCustomSubprojectRelativeDir = function(plugin_id, sr
return subRelativeDir;
};
AndroidProject.prototype.addSubProject = function(parentDir, subDir) {
AndroidProject.prototype.addSubProject = function (parentDir, subDir) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var subProjectFile = path.resolve(subDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
@@ -126,7 +125,7 @@ AndroidProject.prototype.addSubProject = function(parentDir, subDir) {
this._dirty = true;
};
AndroidProject.prototype.removeSubProject = function(parentDir, subDir) {
AndroidProject.prototype.removeSubProject = function (parentDir, subDir) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
removeFromPropertyList(parentProperties, 'android.library.reference', getRelativeLibraryPath(parentDir, subDir));
@@ -134,35 +133,35 @@ AndroidProject.prototype.removeSubProject = function(parentDir, subDir) {
this._dirty = true;
};
AndroidProject.prototype.addGradleReference = function(parentDir, subDir) {
AndroidProject.prototype.addGradleReference = function (parentDir, subDir) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
addToPropertyList(parentProperties, 'cordova.gradle.include', getRelativeLibraryPath(parentDir, subDir));
this._dirty = true;
};
AndroidProject.prototype.removeGradleReference = function(parentDir, subDir) {
AndroidProject.prototype.removeGradleReference = function (parentDir, subDir) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
removeFromPropertyList(parentProperties, 'cordova.gradle.include', getRelativeLibraryPath(parentDir, subDir));
this._dirty = true;
};
AndroidProject.prototype.addSystemLibrary = function(parentDir, value) {
AndroidProject.prototype.addSystemLibrary = function (parentDir, value) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
addToPropertyList(parentProperties, 'cordova.system.library', value);
this._dirty = true;
};
AndroidProject.prototype.removeSystemLibrary = function(parentDir, value) {
AndroidProject.prototype.removeSystemLibrary = function (parentDir, value) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
removeFromPropertyList(parentProperties, 'cordova.system.library', value);
this._dirty = true;
};
AndroidProject.prototype.write = function() {
AndroidProject.prototype.write = function () {
if (!this._dirty) {
return;
}
@@ -201,9 +200,9 @@ AndroidProject.prototype.getUninstaller = function (type) {
* This checks if an Android project is clean or has old build artifacts
*/
AndroidProject.prototype.isClean = function() {
AndroidProject.prototype.isClean = function () {
var build_path = path.join(this.projectDir, 'build');
//If the build directory doesn't exist, it's clean
// If the build directory doesn't exist, it's clean
return !(fs.existsSync(build_path));
};

View File

@@ -4,13 +4,13 @@
* @param {String} root Root folder of the project
*/
/*jshint esnext: false */
/* jshint esnext: false */
var path = require('path');
var fs = require('fs');
var CordovaError = require('cordova-common').CordovaError;
module.exports.isAndroidStudioProject = function isAndroidStudioProject(root) {
module.exports.isAndroidStudioProject = function isAndroidStudioProject (root) {
var eclipseFiles = ['AndroidManifest.xml', 'libs', 'res', 'project.properties', 'platform_www'];
var androidStudioFiles = ['app', 'gradle', 'app/src/main/res'];
@@ -18,21 +18,21 @@ module.exports.isAndroidStudioProject = function isAndroidStudioProject(root) {
var isEclipse = false;
var isAS = true;
if(!fs.existsSync(root)) {
if (!fs.existsSync(root)) {
throw new CordovaError('AndroidStudio.js:inAndroidStudioProject root does not exist: ' + root);
}
// if any of the following exists, then we are not an ASProj
eclipseFiles.forEach(function(file) {
if(fs.existsSync(path.join(root, file))) {
eclipseFiles.forEach(function (file) {
if (fs.existsSync(path.join(root, file))) {
isEclipse = true;
}
});
// if it is NOT an eclipse project, check that all required files exist
if(!isEclipse) {
androidStudioFiles.forEach(function(file){
if(!fs.existsSync(path.join(root, file))) {
if (!isEclipse) {
androidStudioFiles.forEach(function (file) {
if (!fs.existsSync(path.join(root, file))) {
console.log('missing file :: ' + file);
isAS = false;
}

View File

@@ -1,5 +1,3 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -19,8 +17,8 @@
under the License.
*/
var Q = require('q'),
superspawn = require('cordova-common').superspawn;
var Q = require('q');
var superspawn = require('cordova-common').superspawn;
var suffix_number_regex = /(\d+)$/;
// Used for sorting Android targets, example strings to sort:
@@ -31,7 +29,7 @@ var suffix_number_regex = /(\d+)$/;
// The idea is to sort based on largest "suffix" number - meaning the bigger
// the number at the end, the more recent the target, the closer to the
// start of the array.
function sort_by_largest_numerical_suffix(a, b) {
function sort_by_largest_numerical_suffix (a, b) {
var suffix_a = a.match(suffix_number_regex);
var suffix_b = b.match(suffix_number_regex);
if (suffix_a && suffix_b) {
@@ -45,9 +43,8 @@ function sort_by_largest_numerical_suffix(a, b) {
}
}
module.exports.print_newest_available_sdk_target = function() {
return module.exports.list_targets()
.then(function(targets) {
module.exports.print_newest_available_sdk_target = function () {
return module.exports.list_targets().then(function (targets) {
targets.sort(sort_by_largest_numerical_suffix);
console.log(targets[0]);
});
@@ -68,65 +65,34 @@ module.exports.version_string_to_api_level = {
'7.1.1': 25
};
module.exports.list_targets_with_android = function() {
return superspawn.spawn('android', ['list', 'targets'])
.then(function(stdout) {
var target_out = stdout.split('\n');
var targets = [];
for (var i = target_out.length - 1; i >= 0; i--) {
if(target_out[i].match(/id:/)) {
targets.push(target_out[i].match(/"(.+)"/)[1]);
}
function parse_targets (output) {
var target_out = output.split('\n');
var targets = [];
for (var i = target_out.length - 1; i >= 0; i--) {
if (target_out[i].match(/id:/)) { // if "id:" is in the line...
targets.push(target_out[i].match(/"(.+)"/)[1]); // .. match whatever is in quotes.
}
return targets;
});
}
return targets;
}
module.exports.list_targets_with_android = function () {
return superspawn.spawn('android', ['list', 'target']).then(parse_targets);
};
module.exports.list_targets_with_sdkmanager = function() {
return superspawn.spawn('sdkmanager', ['--list'])
.then(function(stdout) {
var parsing_installed_packages = false;
var lines = stdout.split('\n');
var targets = [];
for (var i = 0, l = lines.length; i < l; i++) {
var line = lines[i];
if (line.match(/Installed packages/)) {
parsing_installed_packages = true;
} else if (line.match(/Available Packages/) || line.match(/Available Updates/)) {
// we are done working through installed packages, exit
break;
}
if (parsing_installed_packages) {
// Match stock android platform
if (line.match(/platforms;android-\d+/)) {
targets.push(line.match(/(android-\d+)/)[1]);
}
// Match Google APIs
if (line.match(/addon-google_apis-google-\d+/)) {
var description = lines[i + 1];
// munge description to match output from old android sdk tooling
var api_level = description.match(/Android (\d+)/); //[1];
if (api_level) {
targets.push('Google Inc.:Google APIs:' + api_level[1]);
}
}
// TODO: match anything else?
}
}
return targets;
});
module.exports.list_targets_with_avdmanager = function () {
return superspawn.spawn('avdmanager', ['list', 'target']).then(parse_targets);
};
module.exports.list_targets = function() {
return module.exports.list_targets_with_android()
.catch(function(err) {
// there's a chance `android` no longer works.
// lets see if `sdkmanager` is available and we can figure it out
var avail_regex = /"?android"? command is no longer available/;
if (err.code && ((err.stdout && err.stdout.match(avail_regex)) || (err.stderr && err.stderr.match(avail_regex)))) {
return module.exports.list_targets_with_sdkmanager();
module.exports.list_targets = function () {
return module.exports.list_targets_with_avdmanager().catch(function (err) {
// If there's an error, like avdmanager could not be found, we can try
// as a last resort, to run `android`, in case this is a super old
// SDK installation.
if (err && (err.code === 'ENOENT' || (err.stderr && err.stderr.match(/not recognized/)))) {
return module.exports.list_targets_with_android();
} else throw err;
}).then(function(targets) {
}).then(function (targets) {
if (targets.length === 0) {
return Q.reject(new Error('No android targets (SDKs) installed!'));
}

View File

@@ -19,10 +19,10 @@
under the License.
*/
var Q = require('q'),
path = require('path'),
fs = require('fs'),
nopt = require('nopt');
var Q = require('q');
var path = require('path');
var fs = require('fs');
var nopt = require('nopt');
var Adb = require('./Adb');
@@ -31,7 +31,7 @@ var events = require('cordova-common').events;
var spawn = require('cordova-common').superspawn.spawn;
var CordovaError = require('cordova-common').CordovaError;
function parseOpts(options, resolvedTarget, projectRoot) {
function parseOpts (options, resolvedTarget, projectRoot) {
options = options || {};
options.argv = nopt({
gradle: Boolean,
@@ -55,16 +55,13 @@ function parseOpts(options, resolvedTarget, projectRoot) {
extraArgs: []
};
if (options.argv.ant || options.argv.gradle)
ret.buildMethod = options.argv.ant ? 'ant' : 'gradle';
if (options.argv.ant || options.argv.gradle) { ret.buildMethod = options.argv.ant ? 'ant' : 'gradle'; }
if (options.nobuild) ret.buildMethod = 'none';
if (options.argv.versionCode)
ret.extraArgs.push('-PcdvVersionCode=' + options.argv.versionCode);
if (options.argv.versionCode) { ret.extraArgs.push('-PcdvVersionCode=' + options.argv.versionCode); }
if (options.argv.minSdkVersion)
ret.extraArgs.push('-PcdvMinSdkVersion=' + options.argv.minSdkVersion);
if (options.argv.minSdkVersion) { ret.extraArgs.push('-PcdvMinSdkVersion=' + options.argv.minSdkVersion); }
if (options.argv.gradleArg) {
ret.extraArgs = ret.extraArgs.concat(options.argv.gradleArg);
@@ -72,12 +69,10 @@ function parseOpts(options, resolvedTarget, projectRoot) {
var packageArgs = {};
if (options.argv.keystore)
packageArgs.keystore = path.relative(projectRoot, path.resolve(options.argv.keystore));
if (options.argv.keystore) { packageArgs.keystore = path.relative(projectRoot, path.resolve(options.argv.keystore)); }
['alias','storePassword','password','keystoreType'].forEach(function (flagName) {
if (options.argv[flagName])
packageArgs[flagName] = options.argv[flagName];
['alias', 'storePassword', 'password', 'keystoreType'].forEach(function (flagName) {
if (options.argv[flagName]) { packageArgs[flagName] = options.argv[flagName]; }
});
var buildConfig = options.buildConfig;
@@ -88,20 +83,20 @@ function parseOpts(options, resolvedTarget, projectRoot) {
if (!fs.existsSync(buildConfig)) {
throw new Error('Specified build config file does not exist: ' + buildConfig);
}
events.emit('log', 'Reading build config file: '+ path.resolve(buildConfig));
events.emit('log', 'Reading build config file: ' + path.resolve(buildConfig));
var buildjson = fs.readFileSync(buildConfig, 'utf8');
var config = JSON.parse(buildjson.replace(/^\ufeff/, '')); // Remove BOM
if (config.android && config.android[ret.buildType]) {
var androidInfo = config.android[ret.buildType];
if(androidInfo.keystore && !packageArgs.keystore) {
if(androidInfo.keystore.substr(0,1) === '~') {
if (androidInfo.keystore && !packageArgs.keystore) {
if (androidInfo.keystore.substr(0, 1) === '~') {
androidInfo.keystore = process.env.HOME + androidInfo.keystore.substr(1);
}
packageArgs.keystore = path.resolve(path.dirname(buildConfig), androidInfo.keystore);
events.emit('log', 'Reading the keystore from: ' + packageArgs.keystore);
}
['alias', 'storePassword', 'password','keystoreType'].forEach(function (key){
['alias', 'storePassword', 'password', 'keystoreType'].forEach(function (key) {
packageArgs[key] = packageArgs[key] || androidInfo[key];
});
}
@@ -112,8 +107,8 @@ function parseOpts(options, resolvedTarget, projectRoot) {
packageArgs.password, packageArgs.keystoreType);
}
if(!ret.packageInfo) {
if(Object.keys(packageArgs).length > 0) {
if (!ret.packageInfo) {
if (Object.keys(packageArgs).length > 0) {
events.emit('warn', '\'keystore\' and \'alias\' need to be specified to generate a signed archive.');
}
}
@@ -125,11 +120,10 @@ function parseOpts(options, resolvedTarget, projectRoot) {
* Builds the project with the specifed options
* Returns a promise.
*/
module.exports.runClean = function(options) {
module.exports.runClean = function (options) {
var opts = parseOpts(options, null, this.root);
var builder = builders.getBuilder(opts.buildMethod);
return builder.prepEnv(opts)
.then(function() {
return builder.prepEnv(opts).then(function () {
return builder.clean(opts);
});
};
@@ -146,17 +140,15 @@ module.exports.runClean = function(options) {
* @return {Promise<Object>} Promise, resolved with built packages
* information.
*/
module.exports.run = function(options, optResolvedTarget) {
module.exports.run = function (options, optResolvedTarget) {
var opts = parseOpts(options, optResolvedTarget, this.root);
var builder = builders.getBuilder(opts.buildMethod);
return builder.prepEnv(opts)
.then(function() {
return builder.prepEnv(opts).then(function () {
if (opts.prepEnv) {
events.emit('verbose', 'Build file successfully prepared.');
return;
}
return builder.build(opts)
.then(function() {
return builder.build(opts).then(function () {
var apkPaths = builder.findOutputApks(opts.buildType, opts.arch);
events.emit('log', 'Built the following apk(s): \n\t' + apkPaths.join('\n\t'));
return {
@@ -172,38 +164,31 @@ module.exports.run = function(options, optResolvedTarget) {
* Detects the architecture of a device/emulator
* Returns "arm" or "x86".
*/
module.exports.detectArchitecture = function(target) {
function helper() {
return Adb.shell(target, 'cat /proc/cpuinfo')
.then(function(output) {
module.exports.detectArchitecture = function (target) {
function helper () {
return Adb.shell(target, 'cat /proc/cpuinfo').then(function (output) {
return /intel/i.exec(output) ? 'x86' : 'arm';
});
}
// It sometimes happens (at least on OS X), that this command will hang forever.
// To fix it, either unplug & replug device, or restart adb server.
return helper()
.timeout(1000, new CordovaError('Device communication timed out. Try unplugging & replugging the device.'))
.then(null, function(err) {
return helper().timeout(1000, new CordovaError('Device communication timed out. Try unplugging & replugging the device.')).then(null, function (err) {
if (/timed out/.exec('' + err)) {
// adb kill-server doesn't seem to do the trick.
// Could probably find a x-platform version of killall, but I'm not actually
// sure that this scenario even happens on non-OSX machines.
events.emit('verbose', 'adb timed out while detecting device/emulator architecture. Killing adb and trying again.');
return spawn('killall', ['adb'])
.then(function() {
return helper()
.then(null, function() {
return spawn('killall', ['adb']).then(function () {
return helper().then(null, function () {
// The double kill is sadly often necessary, at least on mac.
events.emit('warn', 'adb timed out a second time while detecting device/emulator architecture. Killing adb and trying again.');
return spawn('killall', ['adb'])
.then(function() {
return helper()
.then(null, function() {
return spawn('killall', ['adb']).then(function () {
return helper().then(null, function () {
return Q.reject(new CordovaError('adb timed out a third time while detecting device/emulator architecture. Try unplugging & replugging the device.'));
});
});
});
}, function() {
}, function () {
// For non-killall OS's.
return Q.reject(err);
});
@@ -212,10 +197,10 @@ module.exports.detectArchitecture = function(target) {
});
};
module.exports.findBestApkForArchitecture = function(buildResults, arch) {
var paths = buildResults.apkPaths.filter(function(p) {
module.exports.findBestApkForArchitecture = function (buildResults, arch) {
var paths = buildResults.apkPaths.filter(function (p) {
var apkName = path.basename(p);
if (buildResults.buildType == 'debug') {
if (buildResults.buildType === 'debug') {
return /-debug/.exec(apkName);
}
return !/-debug/.exec(apkName);
@@ -235,7 +220,7 @@ module.exports.findBestApkForArchitecture = function(buildResults, arch) {
throw new Error('Could not find apk architecture: ' + arch + ' build-type: ' + buildResults.buildType);
};
function PackageInfo(keystore, alias, storePassword, password, keystoreType) {
function PackageInfo (keystore, alias, storePassword, password, keystoreType) {
this.keystore = {
'name': 'key.store',
'value': keystore
@@ -265,10 +250,10 @@ function PackageInfo(keystore, alias, storePassword, password, keystoreType) {
}
PackageInfo.prototype = {
toProperties: function() {
toProperties: function () {
var self = this;
var result = '';
Object.keys(self).forEach(function(key) {
Object.keys(self).forEach(function (key) {
result += self[key].name;
result += '=';
result += self[key].value.replace(/\\/g, '\\\\');
@@ -278,7 +263,7 @@ PackageInfo.prototype = {
}
};
module.exports.help = function() {
module.exports.help = function () {
console.log('Usage: ' + path.relative(process.cwd(), path.join('../build')) + ' [flags] [Signed APK flags]');
console.log('Flags:');
console.log(' \'--debug\': will build project in debug mode (default)');

View File

@@ -16,6 +16,7 @@
specific language governing permissions and limitations
under the License.
*/
/* eslint no-unused-vars: 0 */
var Q = require('q');
var fs = require('fs');
@@ -42,30 +43,29 @@ function AntBuilder (projectRoot) {
util.inherits(AntBuilder, GenericBuilder);
AntBuilder.prototype.getArgs = function(cmd, opts) {
AntBuilder.prototype.getArgs = function (cmd, opts) {
var args = [cmd, '-f', path.join(this.root, 'build.xml')];
// custom_rules.xml is required for incremental builds.
if (hasCustomRules(this.root)) {
args.push('-Dout.dir=ant-build', '-Dgen.absolute.dir=ant-gen');
}
if(opts.packageInfo) {
if (opts.packageInfo) {
args.push('-propertyfile=' + path.join(this.root, opts.buildType + SIGNING_PROPERTIES));
}
return args;
};
AntBuilder.prototype.prepEnv = function(opts) {
AntBuilder.prototype.prepEnv = function (opts) {
var self = this;
return check_reqs.check_ant()
.then(function() {
return check_reqs.check_ant().then(function () {
// Copy in build.xml on each build so that:
// A) we don't require the Android SDK at project creation time, and
// B) we always use the SDK's latest version of it.
/*jshint -W069 */
/* jshint -W069 */
var sdkDir = process.env['ANDROID_HOME'];
/*jshint +W069 */
/* jshint +W069 */
var buildTemplate = fs.readFileSync(path.join(sdkDir, 'tools', 'lib', 'build.template'), 'utf8');
function writeBuildXml(projectPath) {
function writeBuildXml (projectPath) {
var newData = buildTemplate.replace('PROJECT_NAME', self.extractRealProjectNameFromManifest());
fs.writeFileSync(path.join(projectPath, 'build.xml'), newData);
if (!fs.existsSync(path.join(projectPath, 'local.properties'))) {
@@ -86,7 +86,7 @@ AntBuilder.prototype.prepEnv = function(opts) {
var propertiesFilePath = path.join(self.root, propertiesFile);
if (opts.packageInfo) {
fs.writeFileSync(propertiesFilePath, TEMPLATE + opts.packageInfo.toProperties());
} else if(isAutoGenerated(propertiesFilePath)) {
} else if (isAutoGenerated(propertiesFilePath)) {
shell.rm('-f', propertiesFilePath);
}
});
@@ -96,7 +96,7 @@ AntBuilder.prototype.prepEnv = function(opts) {
* Builds the project with ant.
* Returns a promise.
*/
AntBuilder.prototype.build = function(opts) {
AntBuilder.prototype.build = function (opts) {
// Without our custom_rules.xml, we need to clean before building.
var ret = Q();
if (!hasCustomRules(this.root)) {
@@ -104,11 +104,10 @@ AntBuilder.prototype.build = function(opts) {
ret = this.clean(opts);
}
var args = this.getArgs(opts.buildType == 'debug' ? 'debug' : 'release', opts);
return check_reqs.check_ant()
.then(function() {
var args = this.getArgs(opts.buildType === 'debug' ? 'debug' : 'release', opts);
return check_reqs.check_ant().then(function () {
return spawn('ant', args, {stdio: 'pipe'});
}).progress(function (stdio){
}).progress(function (stdio) {
if (stdio.stderr) {
process.stderr.write(stdio.stderr);
} else {
@@ -116,7 +115,7 @@ AntBuilder.prototype.build = function(opts) {
}
}).catch(function (error) {
if (error.toString().indexOf('Unable to resolve project target') >= 0) {
return check_reqs.check_android_target(error).then(function() {
return check_reqs.check_android_target(error).then(function () {
// If due to some odd reason - check_android_target succeeds
// we should still fail here.
return Q.reject(error);
@@ -126,19 +125,17 @@ AntBuilder.prototype.build = function(opts) {
});
};
AntBuilder.prototype.clean = function(opts) {
AntBuilder.prototype.clean = function (opts) {
var args = this.getArgs('clean', opts);
var self = this;
return check_reqs.check_ant()
.then(function() {
return check_reqs.check_ant().then(function () {
return spawn('ant', args, {stdio: 'inherit'});
})
.then(function () {
}).then(function () {
shell.rm('-rf', path.join(self.root, 'out'));
['debug', 'release'].forEach(function(config) {
['debug', 'release'].forEach(function (config) {
var propertiesFilePath = path.join(self.root, config + SIGNING_PROPERTIES);
if(isAutoGenerated(propertiesFilePath)){
if (isAutoGenerated(propertiesFilePath)) {
shell.rm('-f', propertiesFilePath);
}
});
@@ -147,10 +144,10 @@ AntBuilder.prototype.clean = function(opts) {
module.exports = AntBuilder;
function hasCustomRules(projectRoot) {
function hasCustomRules (projectRoot) {
return fs.existsSync(path.join(projectRoot, 'custom_rules.xml'));
}
function isAutoGenerated(file) {
function isAutoGenerated (file) {
return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0;
}

View File

@@ -16,6 +16,8 @@
specific language governing permissions and limitations
under the License.
*/
/* eslint no-self-assign: 0 */
/* eslint no-unused-vars: 0 */
var Q = require('q');
var fs = require('fs');
@@ -32,35 +34,33 @@ function GenericBuilder (projectDir) {
};
}
function hasCustomRules(projectRoot) {
function hasCustomRules (projectRoot) {
return fs.existsSync(path.join(projectRoot, 'custom_rules.xml'));
}
GenericBuilder.prototype.prepEnv = function() {
GenericBuilder.prototype.prepEnv = function () {
return Q();
};
GenericBuilder.prototype.build = function() {
GenericBuilder.prototype.build = function () {
events.emit('log', 'Skipping build...');
return Q(null);
};
GenericBuilder.prototype.clean = function() {
GenericBuilder.prototype.clean = function () {
return Q();
};
GenericBuilder.prototype.findOutputApks = function(build_type, arch) {
GenericBuilder.prototype.findOutputApks = function (build_type, arch) {
var self = this;
return Object.keys(this.binDirs)
.reduce(function (result, builderName) {
return Object.keys(this.binDirs).reduce(function (result, builderName) {
var binDir = self.binDirs[builderName];
return result.concat(findOutputApksHelper(binDir, build_type, builderName === 'ant' ? null : arch));
}, [])
.sort(apkSorter);
}, []).sort(apkSorter);
};
GenericBuilder.prototype.readProjectProperties = function () {
function findAllUniq(data, r) {
function findAllUniq (data, r) {
var s = {};
var m;
while ((m = r.exec(data))) {
@@ -85,14 +85,14 @@ GenericBuilder.prototype.extractRealProjectNameFromManifest = function () {
throw new CordovaError('Could not find package name in ' + manifestPath);
}
var packageName=m[1];
var packageName = m[1];
var lastDotIndex = packageName.lastIndexOf('.');
return packageName.substring(lastDotIndex + 1);
};
module.exports = GenericBuilder;
function apkSorter(fileA, fileB) {
function apkSorter (fileA, fileB) {
// De-prioritize unsigned builds
var unsignedRE = /-unsigned/;
if (unsignedRE.exec(fileA)) {
@@ -105,12 +105,11 @@ function apkSorter(fileA, fileB) {
return timeDiff === 0 ? fileA.length - fileB.length : timeDiff;
}
function findOutputApksHelper(dir, build_type, arch) {
function findOutputApksHelper (dir, build_type, arch) {
var shellSilent = shell.config.silent;
shell.config.silent = true;
var ret = shell.ls(path.join(dir, '*.apk'))
.filter(function(candidate) {
var ret = shell.ls(path.join(dir, '*.apk')).filter(function (candidate) {
var apkName = path.basename(candidate);
// Need to choose between release and debug .apk.
if (build_type === 'debug') {
@@ -120,8 +119,7 @@ function findOutputApksHelper(dir, build_type, arch) {
return /-release/.exec(apkName) && !/-unaligned/.exec(apkName);
}
return true;
})
.sort(apkSorter);
}).sort(apkSorter);
shellSilent = shellSilent;
@@ -131,15 +129,15 @@ function findOutputApksHelper(dir, build_type, arch) {
// Assume arch-specific build if newest apk has -x86 or -arm.
var archSpecific = !!/-x86|-arm/.exec(path.basename(ret[0]));
// And show only arch-specific ones (or non-arch-specific)
ret = ret.filter(function(p) {
/*jshint -W018 */
return !!/-x86|-arm/.exec(path.basename(p)) == archSpecific;
/*jshint +W018 */
ret = ret.filter(function (p) {
/* jshint -W018 */
return !!/-x86|-arm/.exec(path.basename(p)) === archSpecific;
/* jshint +W018 */
});
if (archSpecific && ret.length > 1 && arch) {
ret = ret.filter(function(p) {
return path.basename(p).indexOf('-' + arch) != -1;
ret = ret.filter(function (p) {
return path.basename(p).indexOf('-' + arch) !== -1;
});
}

View File

@@ -22,7 +22,7 @@ var fs = require('fs');
var util = require('util');
var path = require('path');
var shell = require('shelljs');
var spawn = require('cordova-common').superspawn.spawn;
var superspawn = require('cordova-common').superspawn;
var CordovaError = require('cordova-common').CordovaError;
var check_reqs = require('../check_reqs');
@@ -37,15 +37,15 @@ var TEMPLATE =
function GradleBuilder (projectRoot) {
GenericBuilder.call(this, projectRoot);
this.binDirs = {gradle: this.binDirs.gradle};
this.binDirs = { gradle: this.binDirs.gradle };
}
util.inherits(GradleBuilder, GenericBuilder);
GradleBuilder.prototype.getArgs = function(cmd, opts) {
if (cmd == 'release') {
GradleBuilder.prototype.getArgs = function (cmd, opts) {
if (cmd === 'release') {
cmd = 'cdvBuildRelease';
} else if (cmd == 'debug') {
} else if (cmd === 'debug') {
cmd = 'cdvBuildDebug';
}
var args = [cmd, '-b', path.join(this.root, 'build.gradle')];
@@ -69,46 +69,47 @@ GradleBuilder.prototype.getArgs = function(cmd, opts) {
* This returns a promise
*/
GradleBuilder.prototype.runGradleWrapper = function(gradle_cmd) {
GradleBuilder.prototype.runGradleWrapper = function (gradle_cmd, gradle_file) {
var gradlePath = path.join(this.root, 'gradlew');
var wrapperGradle = path.join(this.root, 'wrapper.gradle');
if(fs.existsSync(gradlePath)) {
//Literally do nothing, for some reason this works, while !fs.existsSync didn't on Windows
gradle_file = path.join(this.root, (gradle_file || 'wrapper.gradle'));
if (fs.existsSync(gradlePath)) {
// Literally do nothing, for some reason this works, while !fs.existsSync didn't on Windows
} else {
return spawn(gradle_cmd, ['-p', this.root, 'wrapper', '-b', wrapperGradle], {stdio: 'inherit'});
return superspawn.spawn(gradle_cmd, ['-p', this.root, 'wrapper', '-b', gradle_file], { stdio: 'pipe' })
.progress(function (stdio) {
suppressJavaOptionsInfo(stdio);
});
}
};
// Makes the project buildable, minus the gradle wrapper.
GradleBuilder.prototype.prepBuildFiles = function() {
GradleBuilder.prototype.prepBuildFiles = function () {
// Update the version of build.gradle in each dependent library.
var pluginBuildGradle = path.join(this.root, 'cordova', 'lib', 'plugin-build.gradle');
var propertiesObj = this.readProjectProperties();
var subProjects = propertiesObj.libs;
var checkAndCopy = function(subProject, root) {
var subProjectGradle = path.join(root, subProject, 'build.gradle');
// This is the future-proof way of checking if a file exists
// This must be synchronous to satisfy a Travis test
try {
fs.accessSync(subProjectGradle, fs.F_OK);
} catch (e) {
shell.cp('-f', pluginBuildGradle, subProjectGradle);
}
var checkAndCopy = function (subProject, root) {
var subProjectGradle = path.join(root, subProject, 'build.gradle');
// This is the future-proof way of checking if a file exists
// This must be synchronous to satisfy a Travis test
try {
fs.accessSync(subProjectGradle, fs.F_OK);
} catch (e) {
shell.cp('-f', pluginBuildGradle, subProjectGradle);
}
};
for (var i = 0; i < subProjects.length; ++i) {
if (subProjects[i] !== 'CordovaLib') {
checkAndCopy(subProjects[i], this.root);
checkAndCopy(subProjects[i], this.root);
}
}
var name = this.extractRealProjectNameFromManifest();
//Remove the proj.id/name- prefix from projects: https://issues.apache.org/jira/browse/CB-9149
var settingsGradlePaths = subProjects.map(function(p){
var realDir=p.replace(/[/\\]/g, ':');
var libName=realDir.replace(name+'-','');
var str='include ":'+libName+'"\n';
if(realDir.indexOf(name+'-')!==-1)
str+='project(":'+libName+'").projectDir = new File("'+p+'")\n';
// Remove the proj.id/name- prefix from projects: https://issues.apache.org/jira/browse/CB-9149
var settingsGradlePaths = subProjects.map(function (p) {
var realDir = p.replace(/[/\\]/g, ':');
var libName = realDir.replace(name + '-', '');
var str = 'include ":' + libName + '"\n';
if (realDir.indexOf(name + '-') !== -1) { str += 'project(":' + libName + '").projectDir = new File("' + p + '")\n'; }
return str;
});
@@ -120,19 +121,18 @@ GradleBuilder.prototype.prepBuildFiles = function() {
var buildGradle = fs.readFileSync(path.join(this.root, 'build.gradle'), 'utf8');
var depsList = '';
var root = this.root;
var insertExclude = function(p) {
var gradlePath = path.join(root, p, 'build.gradle');
var projectGradleFile = fs.readFileSync(gradlePath, 'utf-8');
if(projectGradleFile.indexOf('CordovaLib') != -1) {
var insertExclude = function (p) {
var gradlePath = path.join(root, p, 'build.gradle');
var projectGradleFile = fs.readFileSync(gradlePath, 'utf-8');
if (projectGradleFile.indexOf('CordovaLib') !== -1) {
depsList += '{\n exclude module:("CordovaLib")\n }\n';
}
else {
depsList +='\n';
}
} else {
depsList += '\n';
}
};
subProjects.forEach(function(p) {
subProjects.forEach(function (p) {
console.log('Subproject Path: ' + p);
var libName=p.replace(/[/\\]/g, ':').replace(name+'-','');
var libName = p.replace(/[/\\]/g, ':').replace(name + '-', '');
depsList += ' debugCompile(project(path: "' + libName + '", configuration: "debug"))';
insertExclude(p);
depsList += ' releaseCompile(project(path: "' + libName + '", configuration: "release"))';
@@ -143,7 +143,7 @@ GradleBuilder.prototype.prepBuildFiles = function() {
[/^\/?extras\/android\/support\/(.*)$/, 'com.android.support:support-$1:+'],
[/^\/?google\/google_play_services\/libproject\/google-play-services_lib\/?$/, 'com.google.android.gms:play-services:+']
];
propertiesObj.systemLibs.forEach(function(p) {
propertiesObj.systemLibs.forEach(function (p) {
var mavenRef;
// It's already in gradle form if it has two ':'s
if (/:.*:/.exec(p)) {
@@ -164,21 +164,20 @@ GradleBuilder.prototype.prepBuildFiles = function() {
});
buildGradle = buildGradle.replace(/(SUB-PROJECT DEPENDENCIES START)[\s\S]*(\/\/ SUB-PROJECT DEPENDENCIES END)/, '$1\n' + depsList + ' $2');
var includeList = '';
propertiesObj.gradleIncludes.forEach(function(includePath) {
propertiesObj.gradleIncludes.forEach(function (includePath) {
includeList += 'apply from: "' + includePath + '"\n';
});
buildGradle = buildGradle.replace(/(PLUGIN GRADLE EXTENSIONS START)[\s\S]*(\/\/ PLUGIN GRADLE EXTENSIONS END)/, '$1\n' + includeList + '$2');
fs.writeFileSync(path.join(this.root, 'build.gradle'), buildGradle);
};
GradleBuilder.prototype.prepEnv = function(opts) {
GradleBuilder.prototype.prepEnv = function (opts) {
var self = this;
return check_reqs.check_gradle()
.then(function(gradlePath) {
return check_reqs.check_gradle().then(function (gradlePath) {
return self.runGradleWrapper(gradlePath);
}).then(function() {
return self.prepBuildFiles();
}).then(function() {
}).then(function () {
return self.prepBuildFiles();
}).then(function () {
// We now copy the gradle out of the framework
// This is a dirty patch to get the build working
/*
@@ -198,12 +197,12 @@ GradleBuilder.prototype.prepEnv = function(opts) {
// If it's not set, do nothing, assuming that we're using a future version of gradle that we don't want to mess with.
// For some reason, using ^ and $ don't work. This does the job, though.
var distributionUrlRegex = /distributionUrl.*zip/;
/*jshint -W069 */
/* jshint -W069 */
var distributionUrl = process.env['CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL'] || 'https\\://services.gradle.org/distributions/gradle-3.3-all.zip';
/*jshint +W069 */
/* jshint +W069 */
var gradleWrapperPropertiesPath = path.join(self.root, 'gradle', 'wrapper', 'gradle-wrapper.properties');
shell.chmod('u+w', gradleWrapperPropertiesPath);
shell.sed('-i', distributionUrlRegex, 'distributionUrl='+distributionUrl, gradleWrapperPropertiesPath);
shell.sed('-i', distributionUrlRegex, 'distributionUrl=' + distributionUrl, gradleWrapperPropertiesPath);
var propertiesFile = opts.buildType + SIGNING_PROPERTIES;
var propertiesFilePath = path.join(self.root, propertiesFile);
@@ -219,53 +218,37 @@ GradleBuilder.prototype.prepEnv = function(opts) {
* Builds the project with gradle.
* Returns a promise.
*/
GradleBuilder.prototype.build = function(opts) {
GradleBuilder.prototype.build = function (opts) {
var wrapper = path.join(this.root, 'gradlew');
var args = this.getArgs(opts.buildType == 'debug' ? 'debug' : 'release', opts);
var args = this.getArgs(opts.buildType === 'debug' ? 'debug' : 'release', opts);
return spawn(wrapper, args, {stdio: 'pipe'})
.progress(function (stdio){
if (stdio.stderr) {
/*
* Workaround for the issue with Java printing some unwanted information to
* stderr instead of stdout.
* This function suppresses 'Picked up _JAVA_OPTIONS' message from being
* printed to stderr. See https://issues.apache.org/jira/browse/CB-9971 for
* explanation.
*/
var suppressThisLine = /^Picked up _JAVA_OPTIONS: /i.test(stdio.stderr.toString());
if (suppressThisLine) {
return;
return superspawn.spawn(wrapper, args, { stdio: 'pipe' })
.progress(function (stdio) {
suppressJavaOptionsInfo(stdio);
}).catch(function (error) {
if (error.toString().indexOf('failed to find target with hash string') >= 0) {
return check_reqs.check_android_target(error).then(function () {
// If due to some odd reason - check_android_target succeeds
// we should still fail here.
return Q.reject(error);
});
}
process.stderr.write(stdio.stderr);
} else {
process.stdout.write(stdio.stdout);
}
}).catch(function (error) {
if (error.toString().indexOf('failed to find target with hash string') >= 0) {
return check_reqs.check_android_target(error).then(function() {
// If due to some odd reason - check_android_target succeeds
// we should still fail here.
return Q.reject(error);
});
}
return Q.reject(error);
});
return Q.reject(error);
});
};
GradleBuilder.prototype.clean = function(opts) {
GradleBuilder.prototype.clean = function (opts) {
var builder = this;
var wrapper = path.join(this.root, 'gradlew');
var args = builder.getArgs('clean', opts);
return Q().then(function() {
return spawn(wrapper, args, {stdio: 'inherit'});
})
.then(function () {
return Q().then(function () {
return superspawn.spawn(wrapper, args, { stdio: 'inherit' });
}).then(function () {
shell.rm('-rf', path.join(builder.root, 'out'));
['debug', 'release'].forEach(function(config) {
['debug', 'release'].forEach(function (config) {
var propertiesFilePath = path.join(builder.root, config + SIGNING_PROPERTIES);
if(isAutoGenerated(propertiesFilePath)){
if (isAutoGenerated(propertiesFilePath)) {
shell.rm('-f', propertiesFilePath);
}
});
@@ -274,6 +257,25 @@ GradleBuilder.prototype.clean = function(opts) {
module.exports = GradleBuilder;
function isAutoGenerated(file) {
function suppressJavaOptionsInfo (stdio) {
if (stdio.stderr) {
/*
* Workaround for the issue with Java printing some unwanted information to
* stderr instead of stdout.
* This function suppresses 'Picked up _JAVA_OPTIONS' message from being
* printed to stderr. See https://issues.apache.org/jira/browse/CB-9971 for
* explanation.
*/
var suppressThisLine = /^Picked up _JAVA_OPTIONS: /i.test(stdio.stderr.toString());
if (suppressThisLine) {
return;
}
process.stderr.write(stdio.stderr);
} else {
process.stdout.write(stdio.stdout);
}
}
function isAutoGenerated (file) {
return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0;
}

View File

@@ -35,8 +35,7 @@ var knownBuilders = {
* @return {Builder} A builder instance for specified build type.
*/
module.exports.getBuilder = function (builderType, projectRoot) {
if (!knownBuilders[builderType])
throw new CordovaError('Builder ' + builderType + ' is not supported.');
if (!knownBuilders[builderType]) { throw new CordovaError('Builder ' + builderType + ' is not supported.'); }
try {
var Builder = require('./' + knownBuilders[builderType]);

View File

@@ -21,19 +21,19 @@
/* jshint sub:true */
var shelljs = require('shelljs'),
child_process = require('child_process'),
Q = require('q'),
path = require('path'),
fs = require('fs'),
os = require('os'),
REPO_ROOT = path.join(__dirname, '..', '..', '..', '..'),
PROJECT_ROOT = path.join(__dirname, '..', '..');
var shelljs = require('shelljs');
var child_process = require('child_process');
var Q = require('q');
var path = require('path');
var fs = require('fs');
var os = require('os');
var REPO_ROOT = path.join(__dirname, '..', '..', '..', '..');
var PROJECT_ROOT = path.join(__dirname, '..', '..');
var CordovaError = require('cordova-common').CordovaError;
var superspawn = require('cordova-common').superspawn;
var android_sdk = require('./android_sdk');
function forgivingWhichSync(cmd) {
function forgivingWhichSync (cmd) {
try {
return fs.realpathSync(shelljs.which(cmd));
} catch (e) {
@@ -41,9 +41,9 @@ function forgivingWhichSync(cmd) {
}
}
function tryCommand(cmd, errMsg, catchStderr) {
function tryCommand (cmd, errMsg, catchStderr) {
var d = Q.defer();
child_process.exec(cmd, function(err, stdout, stderr) {
child_process.exec(cmd, function (err, stdout, stderr) {
if (err) d.reject(new CordovaError(errMsg));
// Sometimes it is necessary to return an stderr instead of stdout in case of success, since
// some commands prints theirs output to stderr instead of stdout. 'javac' is the example
@@ -52,18 +52,18 @@ function tryCommand(cmd, errMsg, catchStderr) {
return d.promise;
}
module.exports.isWindows = function() {
return (os.platform() == 'win32');
module.exports.isWindows = function () {
return (os.platform() === 'win32');
};
module.exports.isDarwin = function() {
return (os.platform() == 'darwin');
module.exports.isDarwin = function () {
return (os.platform() === 'darwin');
};
// Get valid target from framework/project.properties if run from this repo
// Otherwise get target from project.properties file within a generated cordova-android project
module.exports.get_target = function() {
function extractFromFile(filePath) {
module.exports.get_target = function () {
function extractFromFile (filePath) {
var target = shelljs.grep(/\btarget=/, filePath);
if (!target) {
throw new Error('Could not find android target within: ' + filePath);
@@ -83,17 +83,18 @@ module.exports.get_target = function() {
};
// Returns a promise. Called only by build and clean commands.
module.exports.check_ant = function() {
return superspawn.spawn('ant', ['-version'])
.then(function(output) {
module.exports.check_ant = function () {
return superspawn.spawn('ant', ['-version']).then(function (output) {
// Parse Ant version from command output
return /version ((?:\d+\.)+(?:\d+))/i.exec(output)[1];
}).catch(function(err) {
throw new CordovaError('Failed to run `ant -version`. Make sure you have `ant` on your $PATH.');
}).catch(function (err) {
if (err) {
throw new CordovaError('Failed to run `ant -version`. Make sure you have `ant` on your $PATH.');
}
});
};
module.exports.get_gradle_wrapper = function() {
module.exports.get_gradle_wrapper = function () {
var androidStudioPath;
var i = 0;
var foundStudio = false;
@@ -102,58 +103,70 @@ module.exports.get_gradle_wrapper = function() {
program_dir = fs.readdirSync('/Applications');
while (i < program_dir.length && !foundStudio) {
if (program_dir[i].startsWith('Android Studio')) {
//TODO: Check for a specific Android Studio version, make sure it's not Canary
// TODO: Check for a specific Android Studio version, make sure it's not Canary
androidStudioPath = path.join('/Applications', program_dir[i], 'Contents', 'gradle');
foundStudio = true;
} else { ++i; }
}
} else if (module.exports.isWindows()) {
var androidPath = path.join(process.env['ProgramFiles'], 'Android') + '/';
if (fs.existsSync(androidPath)) {
program_dir = fs.readdirSync(androidPath);
while (i < program_dir.length && !foundStudio) {
if (program_dir[i].startsWith('Android Studio')) {
foundStudio = true;
androidStudioPath = path.join(process.env['ProgramFiles'], 'Android', program_dir[i], 'gradle');
} else { ++i; }
var result = child_process.spawnSync(path.join(__dirname, 'getASPath.bat'));
// console.log('result.stdout =' + result.stdout.toString());
// console.log('result.stderr =' + result.stderr.toString());
if (result.stderr.toString().length > 0) {
var androidPath = path.join(process.env['ProgramFiles'], 'Android') + '/';
if (fs.existsSync(androidPath)) {
program_dir = fs.readdirSync(androidPath);
while (i < program_dir.length && !foundStudio) {
if (program_dir[i].startsWith('Android Studio')) {
foundStudio = true;
androidStudioPath = path.join(process.env['ProgramFiles'], 'Android', program_dir[i], 'gradle');
} else { ++i; }
}
}
} else {
// console.log('got android studio path from registry');
// remove the (os independent) new line char at the end of stdout
// add gradle to match the above.
androidStudioPath = path.join(result.stdout.toString().split('\r\n')[0], 'gradle');
}
}
if (androidStudioPath !== null && fs.existsSync(androidStudioPath)) {
var dirs = fs.readdirSync(androidStudioPath);
if(dirs[0].split('-')[0] == 'gradle') {
if (dirs[0].split('-')[0] === 'gradle') {
return path.join(androidStudioPath, dirs[0], 'bin', 'gradle');
}
} else {
//OK, let's try to check for Gradle!
// OK, let's try to check for Gradle!
return forgivingWhichSync('gradle');
}
};
// Returns a promise. Called only by build and clean commands.
module.exports.check_gradle = function() {
module.exports.check_gradle = function () {
var sdkDir = process.env['ANDROID_HOME'];
var d = Q.defer();
if (!sdkDir)
if (!sdkDir) {
return Q.reject(new CordovaError('Could not find gradle wrapper within Android SDK. Could not find Android SDK directory.\n' +
'Might need to install Android SDK or set up \'ANDROID_HOME\' env variable.'));
}
var gradlePath = module.exports.get_gradle_wrapper();
if (gradlePath.length !== 0)
d.resolve(gradlePath);
else
if (gradlePath.length !== 0) { d.resolve(gradlePath); } else {
d.reject(new CordovaError('Could not find an installed version of Gradle either in Android Studio,\n' +
'or on your system to install the gradle wrapper. Please include gradle \n' +
'in your path, or install Android Studio'));
}
return d.promise;
};
// Returns a promise.
module.exports.check_java = function() {
module.exports.check_java = function () {
var javacPath = forgivingWhichSync('javac');
var hasJavaHome = !!process.env['JAVA_HOME'];
return Q().then(function() {
return Q().then(function () {
if (hasJavaHome) {
// Windows java installer doesn't add javac to PATH, nor set JAVA_HOME (ugh).
if (!javacPath) {
@@ -165,11 +178,12 @@ module.exports.check_java = function() {
var find_java = '/usr/libexec/java_home';
var default_java_error_msg = 'Failed to find \'JAVA_HOME\' environment variable. Try setting setting it manually.';
if (fs.existsSync(find_java)) {
return superspawn.spawn(find_java)
.then(function(stdout) {
return superspawn.spawn(find_java).then(function (stdout) {
process.env['JAVA_HOME'] = stdout.trim();
}).catch(function(err) {
throw new CordovaError(default_java_error_msg);
}).catch(function (err) {
if (err) {
throw new CordovaError(default_java_error_msg);
}
});
} else {
// See if we can derive it from javac's location.
@@ -200,7 +214,7 @@ module.exports.check_java = function() {
}
}
}
}).then(function() {
}).then(function () {
var msg =
'Failed to run "javac -version", make sure that you have a JDK installed.\n' +
'You can get it from: http://www.oracle.com/technetwork/java/javase/downloads.\n';
@@ -209,9 +223,8 @@ module.exports.check_java = function() {
}
// We use tryCommand with catchStderr = true, because
// javac writes version info to stderr instead of stdout
return tryCommand('javac -version', msg, true)
.then(function (output) {
//Let's check for at least Java 8, and keep it future proof so we can support Java 10
return tryCommand('javac -version', msg, true).then(function (output) {
// Let's check for at least Java 8, and keep it future proof so we can support Java 10
var match = /javac ((?:1\.)(?:[8-9]\.)(?:\d+))|((?:1\.)(?:[1-9]\d+\.)(?:\d+))/i.exec(output);
return match && match[1];
});
@@ -219,13 +232,13 @@ module.exports.check_java = function() {
};
// Returns a promise.
module.exports.check_android = function() {
return Q().then(function() {
module.exports.check_android = function () {
return Q().then(function () {
var androidCmdPath = forgivingWhichSync('android');
var adbInPath = forgivingWhichSync('adb');
var avdmanagerInPath = forgivingWhichSync('avdmanager');
var hasAndroidHome = !!process.env['ANDROID_HOME'] && fs.existsSync(process.env['ANDROID_HOME']);
function maybeSetAndroidHome(value) {
function maybeSetAndroidHome (value) {
if (!hasAndroidHome && fs.existsSync(value)) {
hasAndroidHome = true;
process.env['ANDROID_HOME'] = value;
@@ -265,7 +278,7 @@ module.exports.check_android = function() {
if (androidCmdPath) {
parentDir = path.dirname(androidCmdPath);
grandParentDir = path.dirname(parentDir);
if (path.basename(parentDir) == 'tools' || fs.existsSync(path.join(grandParentDir, 'tools', 'android'))) {
if (path.basename(parentDir) === 'tools' || fs.existsSync(path.join(grandParentDir, 'tools', 'android'))) {
maybeSetAndroidHome(grandParentDir);
} else {
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting setting it manually.\n' +
@@ -276,7 +289,7 @@ module.exports.check_android = function() {
if (adbInPath) {
parentDir = path.dirname(adbInPath);
grandParentDir = path.dirname(parentDir);
if (path.basename(parentDir) == 'platform-tools') {
if (path.basename(parentDir) === 'platform-tools') {
maybeSetAndroidHome(grandParentDir);
} else {
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting setting it manually.\n' +
@@ -287,7 +300,7 @@ module.exports.check_android = function() {
if (avdmanagerInPath) {
parentDir = path.dirname(avdmanagerInPath);
grandParentDir = path.dirname(parentDir);
if (path.basename(parentDir) == 'bin' && path.basename(grandParentDir) == 'tools') {
if (path.basename(parentDir) === 'bin' && path.basename(grandParentDir) === 'tools') {
maybeSetAndroidHome(path.dirname(grandParentDir));
} else {
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting setting it manually.\n' +
@@ -330,20 +343,19 @@ module.exports.getAbsoluteAndroidCmd = function () {
return cmd.replace(/(\s)/g, '\\$1');
};
module.exports.check_android_target = function(originalError) {
module.exports.check_android_target = function (originalError) {
// valid_target can look like:
// android-19
// android-L
// Google Inc.:Google APIs:20
// Google Inc.:Glass Development Kit Preview:20
var desired_api_level = module.exports.get_target();
return android_sdk.list_targets()
.then(function(targets) {
return android_sdk.list_targets().then(function (targets) {
if (targets.indexOf(desired_api_level) >= 0) {
return targets;
}
var androidCmd = module.exports.getAbsoluteAndroidCmd();
var msg = 'Please install Android target / API level: "' + desired_api_level + '".\n\n' +
var msg = 'Please install Android target / API level: "' + desired_api_level + '".\n\n' +
'Hint: Open the SDK manager by running: ' + androidCmd + '\n' +
'You will require:\n' +
'1. "SDK Platform" for API level ' + desired_api_level + '\n' +
@@ -357,23 +369,21 @@ module.exports.check_android_target = function(originalError) {
};
// Returns a promise.
module.exports.run = function() {
return Q.all([this.check_java(), this.check_android()])
.then(function(values) {
console.log('ANDROID_HOME=' + process.env['ANDROID_HOME']);
console.log('JAVA_HOME=' + process.env['JAVA_HOME']);
module.exports.run = function () {
return Q.all([this.check_java(), this.check_android()]).then(function (values) {
console.log('ANDROID_HOME=' + process.env['ANDROID_HOME']);
console.log('JAVA_HOME=' + process.env['JAVA_HOME']);
if (!values[0]) {
if (!values[0]) {
throw new CordovaError('Requirements check failed for JDK 1.8 or greater');
}
}
if (!values[1]) {
if (!values[1]) {
throw new CordovaError('Requirements check failed for Android SDK');
}
});
}
});
};
/**
* Object thar represents one of requirements for current platform.
* @param {String} id The unique identifier for this requirements.
@@ -387,7 +397,7 @@ var Requirement = function (id, name, version, installed) {
this.name = name;
this.installed = installed || false;
this.metadata = {
version: version,
version: version
};
};
@@ -397,7 +407,7 @@ var Requirement = function (id, name, version, installed) {
*
* @return Promise<Requirement[]> Array of requirements. Due to implementation, promise is always fulfilled.
*/
module.exports.check_all = function() {
module.exports.check_all = function () {
var requirements = [
new Requirement('java', 'Java JDK'),
@@ -417,15 +427,13 @@ module.exports.check_all = function() {
return checkFns.reduce(function (promise, checkFn, idx) {
// Update each requirement with results
var requirement = requirements[idx];
return promise.then(checkFn)
.then(function (version) {
return promise.then(checkFn).then(function (version) {
requirement.installed = true;
requirement.metadata.version = version;
}, function (err) {
requirement.metadata.reason = err instanceof Error ? err.message : err;
});
}, Q())
.then(function () {
}, Q()).then(function () {
// When chain is completed, return requirements array to upstream API
return requirements;
});

View File

@@ -19,8 +19,8 @@
under the License.
*/
var Q = require('q'),
build = require('./build');
var Q = require('q');
var build = require('./build');
var path = require('path');
var Adb = require('./Adb');
var AndroidManifest = require('./AndroidManifest');
@@ -32,18 +32,16 @@ var events = require('cordova-common').events;
* Returns a promise for the list of the device ID's found
* @param lookHarder When true, try restarting adb if no devices are found.
*/
module.exports.list = function(lookHarder) {
return Adb.devices()
.then(function(list) {
module.exports.list = function (lookHarder) {
return Adb.devices().then(function (list) {
if (list.length === 0 && lookHarder) {
// adb kill-server doesn't seem to do the trick.
// Could probably find a x-platform version of killall, but I'm not actually
// sure that this scenario even happens on non-OSX machines.
return spawn('killall', ['adb'])
.then(function() {
return spawn('killall', ['adb']).then(function () {
events.emit('verbose', 'Restarting adb to see if more devices are detected.');
return Adb.devices();
}, function() {
}, function () {
// For non-killall OS's.
return list;
});
@@ -52,9 +50,8 @@ module.exports.list = function(lookHarder) {
});
};
module.exports.resolveTarget = function(target) {
return this.list(true)
.then(function(device_list) {
module.exports.resolveTarget = function (target) {
return this.list(true).then(function (device_list) {
if (!device_list || !device_list.length) {
return Q.reject(new CordovaError('Failed to deploy to device, no devices found.'));
}
@@ -65,8 +62,7 @@ module.exports.resolveTarget = function(target) {
return Q.reject('ERROR: Unable to find target \'' + target + '\'.');
}
return build.detectArchitecture(target)
.then(function(arch) {
return build.detectArchitecture(target).then(function (arch) {
return { target: target, arch: arch, isEmulator: false };
});
});
@@ -77,13 +73,13 @@ module.exports.resolveTarget = function(target) {
* and launches it.
* Returns a promise.
*/
module.exports.install = function(target, buildResults) {
return Q().then(function() {
if (target && typeof target == 'object') {
module.exports.install = function (target, buildResults) {
return Q().then(function () {
if (target && typeof target === 'object') {
return target;
}
return module.exports.resolveTarget(target);
}).then(function(resolvedTarget) {
}).then(function (resolvedTarget) {
var apk_path = build.findBestApkForArchitecture(buildResults, resolvedTarget.arch);
var manifest = new AndroidManifest(path.join(__dirname, '../../AndroidManifest.xml'));
var pkgName = manifest.getPackageId();
@@ -91,29 +87,25 @@ module.exports.install = function(target, buildResults) {
events.emit('log', 'Using apk: ' + apk_path);
events.emit('log', 'Package name: ' + pkgName);
return Adb.install(resolvedTarget.target, apk_path, {replace: true})
.catch(function (error) {
return Adb.install(resolvedTarget.target, apk_path, {replace: true}).catch(function (error) {
// CB-9557 CB-10157 only uninstall and reinstall app if the one that
// is already installed on device was signed w/different certificate
if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString()))
throw error;
if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString())) { throw error; }
events.emit('warn', 'Uninstalling app from device and reinstalling it again because the ' +
'installed app already signed with different key');
// This promise is always resolved, even if 'adb uninstall' fails to uninstall app
// or the app doesn't installed at all, so no error catching needed.
return Adb.uninstall(resolvedTarget.target, pkgName)
.then(function() {
return Adb.uninstall(resolvedTarget.target, pkgName).then(function () {
return Adb.install(resolvedTarget.target, apk_path, {replace: true});
});
})
.then(function() {
//unlock screen
}).then(function () {
// unlock screen
return Adb.shell(resolvedTarget.target, 'input keyevent 82');
}).then(function() {
}).then(function () {
return Adb.start(resolvedTarget.target, launchName);
}).then(function() {
}).then(function () {
events.emit('log', 'LAUNCH SUCCESS');
});
});

View File

@@ -21,8 +21,9 @@
/* jshint sub:true */
var retry = require('./retry');
var build = require('./build');
var android_versions = require('android-versions');
var retry = require('./retry');
var build = require('./build');
var path = require('path');
var Adb = require('./Adb');
var AndroidManifest = require('./AndroidManifest');
@@ -33,20 +34,20 @@ var shelljs = require('shelljs');
var android_sdk = require('./android_sdk');
var check_reqs = require('./check_reqs');
var Q = require('q');
var os = require('os');
var fs = require('fs');
var Q = require('q');
var os = require('os');
var fs = require('fs');
var child_process = require('child_process');
// constants
var ONE_SECOND = 1000; // in milliseconds
var ONE_MINUTE = 60 * ONE_SECOND; // in milliseconds
var ONE_SECOND = 1000; // in milliseconds
var ONE_MINUTE = 60 * ONE_SECOND; // in milliseconds
var INSTALL_COMMAND_TIMEOUT = 5 * ONE_MINUTE; // in milliseconds
var NUM_INSTALL_RETRIES = 3;
var CHECK_BOOTED_INTERVAL = 3 * ONE_SECOND; // in milliseconds
var EXEC_KILL_SIGNAL = 'SIGKILL';
var NUM_INSTALL_RETRIES = 3;
var CHECK_BOOTED_INTERVAL = 3 * ONE_SECOND; // in milliseconds
var EXEC_KILL_SIGNAL = 'SIGKILL';
function forgivingWhichSync(cmd) {
function forgivingWhichSync (cmd) {
try {
return fs.realpathSync(shelljs.which(cmd));
} catch (e) {
@@ -55,8 +56,7 @@ function forgivingWhichSync(cmd) {
}
module.exports.list_images_using_avdmanager = function () {
return superspawn.spawn('avdmanager', ['list', 'avd'])
.then(function(output) {
return superspawn.spawn('avdmanager', ['list', 'avd']).then(function (output) {
var response = output.split('\n');
var emulator_list = [];
for (var i = 1; i < response.length; i++) {
@@ -108,16 +108,15 @@ module.exports.list_images_using_avdmanager = function () {
/* To just return a list of names use this
if (response[i].match(/Name:\s/)) {
emulator_list.push(response[i].split('Name: ')[1].replace('\r', '');
}*/
} */
}
return emulator_list;
});
};
module.exports.list_images_using_android = function() {
return superspawn.spawn('android', ['list', 'avds'])
.then(function(output) {
module.exports.list_images_using_android = function () {
return superspawn.spawn('android', ['list', 'avd']).then(function (output) {
var response = output.split('\n');
var emulator_list = [];
for (var i = 1; i < response.length; i++) {
@@ -152,7 +151,7 @@ module.exports.list_images_using_android = function() {
/* To just return a list of names use this
if (response[i].match(/Name:\s/)) {
emulator_list.push(response[i].split('Name: ')[1].replace('\r', '');
}*/
} */
}
return emulator_list;
@@ -170,26 +169,30 @@ module.exports.list_images_using_android = function() {
skin : <skin>
}
*/
module.exports.list_images = function() {
if (forgivingWhichSync('android')) {
return module.exports.list_images_using_android()
.catch(function(err) {
// try to use `avdmanager` in case `android` reports it is no longer available.
// this likely means the target machine is using a newer version of
// the android sdk, and possibly `avdmanager` is available.
if (err.code == 1 && err.stdout.indexOf('android command is no longer available')) {
return module.exports.list_images_using_avdmanager();
} else {
throw err;
module.exports.list_images = function () {
return Q.fcall(function () {
if (forgivingWhichSync('avdmanager')) {
return module.exports.list_images_using_avdmanager();
} else if (forgivingWhichSync('android')) {
return module.exports.list_images_using_android();
} else {
return Q().then(function () {
throw new CordovaError('Could not find either `android` or `avdmanager` on your $PATH! Are you sure the Android SDK is installed and available?');
});
}
}).then(function (avds) {
// In case we're missing the Android OS version string from the target description, add it.
return avds.map(function (avd) {
if (avd.target && avd.target.indexOf('Android API') > -1 && avd.target.indexOf('API level') < 0) {
var api_level = avd.target.match(/\d+/);
if (api_level) {
var level = android_versions.get(api_level);
avd.target = 'Android ' + level.semver + ' (API level ' + api_level + ')';
}
}
return avd;
});
} else if (forgivingWhichSync('avdmanager')) {
return module.exports.list_images_using_avdmanager();
} else {
return Q().then(function() {
throw new CordovaError('Could not find either `android` or `avdmanager` on your $PATH! Are you sure the Android SDK is installed and available?');
});
}
});
};
/**
@@ -197,9 +200,8 @@ module.exports.list_images = function() {
* or undefined if no avds exist.
* Returns a promise.
*/
module.exports.best_image = function() {
return this.list_images()
.then(function(images) {
module.exports.best_image = function () {
return this.list_images().then(function (images) {
// Just return undefined if there is no images
if (images.length === 0) return;
@@ -208,9 +210,9 @@ module.exports.best_image = function() {
var project_target = check_reqs.get_target().replace('android-', '');
for (var i in images) {
var target = images[i].target;
if(target) {
if (target) {
var num = target.split('(API level ')[1].replace(')', '');
if (num == project_target) {
if (num === project_target) {
return images[i];
} else if (project_target - num < closest && project_target > num) {
closest = project_target - num;
@@ -223,18 +225,18 @@ module.exports.best_image = function() {
};
// Returns a promise.
module.exports.list_started = function() {
module.exports.list_started = function () {
return Adb.devices({emulators: true});
};
// Returns a promise.
module.exports.list_targets = function() {
return superspawn.spawn('android', ['list', 'targets'], {cwd: os.tmpdir()})
.then(function(output) {
// TODO: we should remove this, there's a more robust method under android_sdk.js
module.exports.list_targets = function () {
return superspawn.spawn('android', ['list', 'targets'], {cwd: os.tmpdir()}).then(function (output) {
var target_out = output.split('\n');
var targets = [];
for (var i = target_out.length; i >= 0; i--) {
if(target_out[i].match(/id:/)) {
if (target_out[i].match(/id:/)) {
targets.push(targets[i].split(' ')[1]);
}
}
@@ -249,9 +251,8 @@ module.exports.list_targets = function() {
module.exports.get_available_port = function () {
var self = this;
return self.list_started()
.then(function (emulators) {
for (var p = 5584; p >= 5554; p-=2) {
return self.list_started().then(function (emulators) {
for (var p = 5584; p >= 5554; p -= 2) {
if (emulators.indexOf('emulator-' + p) === -1) {
events.emit('verbose', 'Found available port: ' + p);
return p;
@@ -271,14 +272,13 @@ module.exports.get_available_port = function () {
*
* Returns a promise.
*/
module.exports.start = function(emulator_ID, boot_timeout) {
module.exports.start = function (emulator_ID, boot_timeout) {
var self = this;
return Q().then(function() {
return Q().then(function () {
if (emulator_ID) return Q(emulator_ID);
return self.best_image()
.then(function(best) {
return self.best_image().then(function (best) {
if (best && best.name) {
events.emit('warn', 'No emulator specified, defaulting to ' + best.name);
return best.name;
@@ -290,9 +290,8 @@ module.exports.start = function(emulator_ID, boot_timeout) {
'2. Create an AVD by running: ' + androidCmd + ' avd\n' +
'HINT: For a faster emulator, use an Intel System Image and install the HAXM device driver\n'));
});
}).then(function(emulatorId) {
return self.get_available_port()
.then(function (port) {
}).then(function (emulatorId) {
return self.get_available_port().then(function (port) {
// Figure out the directory the emulator binary runs in, and set the cwd to that directory.
// Workaround for https://code.google.com/p/android/issues/detail?id=235461
var emulator_dir = path.dirname(shelljs.which('emulator'));
@@ -306,20 +305,17 @@ module.exports.start = function(emulator_ID, boot_timeout) {
events.emit('log', 'Waiting for emulator to start...');
return self.wait_for_emulator(port);
});
}).then(function(emulatorId) {
if (!emulatorId)
return Q.reject(new CordovaError('Failed to start emulator'));
}).then(function (emulatorId) {
if (!emulatorId) { return Q.reject(new CordovaError('Failed to start emulator')); }
//wait for emulator to boot up
// wait for emulator to boot up
process.stdout.write('Waiting for emulator to boot (this may take a while)...');
return self.wait_for_boot(emulatorId, boot_timeout)
.then(function(success) {
return self.wait_for_boot(emulatorId, boot_timeout).then(function (success) {
if (success) {
events.emit('log','BOOT COMPLETE');
//unlock screen
return Adb.shell(emulatorId, 'input keyevent 82')
.then(function() {
//return the new emulator id for the started emulators
events.emit('log', 'BOOT COMPLETE');
// unlock screen
return Adb.shell(emulatorId, 'input keyevent 82').then(function () {
// return the new emulator id for the started emulators
return emulatorId;
});
} else {
@@ -334,20 +330,19 @@ module.exports.start = function(emulator_ID, boot_timeout) {
* Waits for an emulator to boot on a given port.
* Returns this emulator's ID in a promise.
*/
module.exports.wait_for_emulator = function(port) {
module.exports.wait_for_emulator = function (port) {
var self = this;
return Q().then(function() {
return Q().then(function () {
var emulator_id = 'emulator-' + port;
return Adb.shell(emulator_id, 'getprop dev.bootcomplete')
.then(function (output) {
return Adb.shell(emulator_id, 'getprop dev.bootcomplete').then(function (output) {
if (output.indexOf('1') >= 0) {
return emulator_id;
}
return self.wait_for_emulator(port);
}, function (error) {
if (error && error.message &&
(error.message.indexOf('not found') > -1) ||
error.message.indexOf('device offline') > -1) {
if ((error && error.message &&
(error.message.indexOf('not found') > -1)) ||
(error.message.indexOf('device offline') > -1)) {
// emulator not yet started, continue waiting
return self.wait_for_emulator(port);
} else {
@@ -355,7 +350,7 @@ module.exports.wait_for_emulator = function(port) {
throw error;
}
});
});
});
};
/*
@@ -363,10 +358,9 @@ module.exports.wait_for_emulator = function(port) {
* promise that resolves to a boolean indicating success. Not specifying a
* time_remaining or passing a negative value will cause it to wait forever
*/
module.exports.wait_for_boot = function(emulator_id, time_remaining) {
module.exports.wait_for_boot = function (emulator_id, time_remaining) {
var self = this;
return Adb.shell(emulator_id, 'ps')
.then(function(output) {
return Adb.shell(emulator_id, 'ps').then(function (output) {
if (output.match(/android\.process\.acore/)) {
return true;
} else if (time_remaining === 0) {
@@ -375,7 +369,7 @@ module.exports.wait_for_boot = function(emulator_id, time_remaining) {
process.stdout.write('.');
// Check at regular intervals
return Q.delay(time_remaining < CHECK_BOOTED_INTERVAL ? time_remaining : CHECK_BOOTED_INTERVAL).then(function() {
return Q.delay(time_remaining < CHECK_BOOTED_INTERVAL ? time_remaining : CHECK_BOOTED_INTERVAL).then(function () {
var updated_time = time_remaining >= 0 ? Math.max(time_remaining - CHECK_BOOTED_INTERVAL, 0) : time_remaining;
return self.wait_for_boot(emulator_id, updated_time);
});
@@ -388,33 +382,31 @@ module.exports.wait_for_boot = function(emulator_id, time_remaining) {
* TODO : Enter the stdin input required to complete the creation of an avd.
* Returns a promise.
*/
module.exports.create_image = function(name, target) {
module.exports.create_image = function (name, target) {
console.log('Creating new avd named ' + name);
if (target) {
return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', target])
.then(null, function(error) {
return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', target]).then(null, function (error) {
console.error('ERROR : Failed to create emulator image : ');
console.error(' Do you have the latest android targets including ' + target + '?');
console.error(error);
});
} else {
console.log('WARNING : Project target not found, creating avd with a different target but the project may fail to install.');
return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', this.list_targets()[0]])
.then(function() {
// TODO: there's a more robust method for finding targets in android_sdk.js
return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', this.list_targets()[0]]).then(function () {
// TODO: This seems like another error case, even though it always happens.
console.error('ERROR : Unable to create an avd emulator, no targets found.');
console.error('Ensure you have targets available by running the "android" command');
return Q.reject();
}, function(error) {
}, function (error) {
console.error('ERROR : Failed to create emulator image : ');
console.error(error);
});
}
};
module.exports.resolveTarget = function(target) {
return this.list_started()
.then(function(emulator_list) {
module.exports.resolveTarget = function (target) {
return this.list_started().then(function (emulator_list) {
if (emulator_list.length < 1) {
return Q.reject('No running Android emulators found, please start an emulator before deploying your project.');
}
@@ -425,9 +417,8 @@ module.exports.resolveTarget = function(target) {
return Q.reject('Unable to find target \'' + target + '\'. Failed to deploy to emulator.');
}
return build.detectArchitecture(target)
.then(function(arch) {
return {target:target, arch:arch, isEmulator:true};
return build.detectArchitecture(target).then(function (arch) {
return {target: target, arch: arch, isEmulator: true};
});
});
};
@@ -438,7 +429,7 @@ module.exports.resolveTarget = function(target) {
* If no started emulators are found, error out.
* Returns a promise.
*/
module.exports.install = function(givenTarget, buildResults) {
module.exports.install = function (givenTarget, buildResults) {
var target;
var manifest = new AndroidManifest(path.join(__dirname, '../../AndroidManifest.xml'));
@@ -446,7 +437,7 @@ module.exports.install = function(givenTarget, buildResults) {
// resolve the target emulator
return Q().then(function () {
if (givenTarget && typeof givenTarget == 'object') {
if (givenTarget && typeof givenTarget === 'object') {
return givenTarget;
} else {
return module.exports.resolveTarget(givenTarget);
@@ -460,13 +451,12 @@ module.exports.install = function(givenTarget, buildResults) {
}).then(function () {
// This promise is always resolved, even if 'adb uninstall' fails to uninstall app
// or the app doesn't installed at all, so no error catching needed.
return Q.when()
.then(function() {
return Q.when().then(function () {
var apk_path = build.findBestApkForArchitecture(buildResults, target.arch);
var execOptions = {
cwd: os.tmpdir(),
timeout: INSTALL_COMMAND_TIMEOUT, // in milliseconds
timeout: INSTALL_COMMAND_TIMEOUT, // in milliseconds
killSignal: EXEC_KILL_SIGNAL
};
@@ -477,12 +467,12 @@ module.exports.install = function(givenTarget, buildResults) {
// A special function to call adb install in specific environment w/ specific options.
// Introduced as a part of fix for http://issues.apache.org/jira/browse/CB-9119
// to workaround sporadic emulator hangs
function adbInstallWithOptions(target, apk, opts) {
function adbInstallWithOptions (target, apk, opts) {
events.emit('verbose', 'Installing apk ' + apk + ' on ' + target + '...');
var command = 'adb -s ' + target + ' install -r "' + apk + '"';
return Q.promise(function (resolve, reject) {
child_process.exec(command, opts, function(err, stdout, stderr) {
child_process.exec(command, opts, function (err, stdout, stderr) {
if (err) reject(new CordovaError('Error executing "' + command + '": ' + stderr));
// adb does not return an error code even if installation fails. Instead it puts a specific
// message to stdout, so we have to use RegExp matching to detect installation failure.
@@ -502,27 +492,23 @@ module.exports.install = function(givenTarget, buildResults) {
}
function installPromise () {
return adbInstallWithOptions(target.target, apk_path, execOptions)
.catch(function (error) {
return adbInstallWithOptions(target.target, apk_path, execOptions).catch(function (error) {
// CB-9557 CB-10157 only uninstall and reinstall app if the one that
// is already installed on device was signed w/different certificate
if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString()))
throw error;
if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString())) { throw error; }
events.emit('warn', 'Uninstalling app from device and reinstalling it because the ' +
'currently installed app was signed with different key');
// This promise is always resolved, even if 'adb uninstall' fails to uninstall app
// or the app doesn't installed at all, so no error catching needed.
return Adb.uninstall(target.target, pkgName)
.then(function() {
return Adb.uninstall(target.target, pkgName).then(function () {
return adbInstallWithOptions(target.target, apk_path, execOptions);
});
});
}
return retry.retryPromise(NUM_INSTALL_RETRIES, installPromise)
.then(function (output) {
return retry.retryPromise(NUM_INSTALL_RETRIES, installPromise).then(function (output) {
events.emit('log', 'INSTALL SUCCESS');
});
});

View File

@@ -0,0 +1,3 @@
@ECHO OFF
for /f "tokens=2*" %%a in ('REG QUERY "HKEY_LOCAL_MACHINE\SOFTWARE\Android Studio" /v Path') do set "ASPath=%%~b"
ECHO %ASPath%

View File

@@ -18,7 +18,7 @@
@ECHO OFF
SET script_path="%~dp0install-device"
IF EXIST %script_path% (
node "%script_path%" %*
node %script_path% %*
) ELSE (
ECHO.
ECHO ERROR: Could not find 'install-device' script in 'cordova\lib' folder, aborting...>&2

View File

@@ -18,7 +18,7 @@
@ECHO OFF
SET script_path="%~dp0install-emulator"
IF EXIST %script_path% (
node "%script_path%" %*
node %script_path% %*
) ELSE (
ECHO.
ECHO ERROR: Could not find 'install-emulator' script in 'cordova\lib' folder, aborting...>&2

View File

@@ -18,7 +18,7 @@
@ECHO OFF
SET script_path="%~dp0list-devices"
IF EXIST %script_path% (
node "%script_path%" %*
node %script_path% %*
) ELSE (
ECHO.
ECHO ERROR: Could not find 'list-devices' script in 'cordova\lib' folder, aborting...>&2

View File

@@ -18,7 +18,7 @@
@ECHO OFF
SET script_path="%~dp0list-emulator-images"
IF EXIST %script_path% (
node "%script_path%" %*
node %script_path% %*
) ELSE (
ECHO.
ECHO ERROR: Could not find 'list-emulator-images' script in 'cordova\lib' folder, aborting...>&2

View File

@@ -18,7 +18,7 @@
@ECHO OFF
SET script_path="%~dp0list-started-emulators"
IF EXIST %script_path% (
node "%script_path%" %*
node %script_path% %*
) ELSE (
ECHO.
ECHO ERROR: Could not find 'list-started-emulators' script in 'cordova\lib' folder, aborting...>&2

View File

@@ -19,28 +19,28 @@
under the License.
*/
var path = require('path'),
os = require('os'),
Q = require('q'),
child_process = require('child_process'),
ROOT = path.join(__dirname, '..', '..');
var path = require('path');
var os = require('os');
var Q = require('q');
var child_process = require('child_process');
var ROOT = path.join(__dirname, '..', '..');
/*
* Starts running logcat in the shell.
* Returns a promise.
*/
module.exports.run = function() {
module.exports.run = function () {
var d = Q.defer();
var adb = child_process.spawn('adb', ['logcat'], {cwd: os.tmpdir()});
adb.stdout.on('data', function(data) {
adb.stdout.on('data', function (data) {
var lines = data ? data.toString().split('\n') : [];
var out = lines.filter(function(x) { return x.indexOf('nativeGetEnabledTags') < 0; });
var out = lines.filter(function (x) { return x.indexOf('nativeGetEnabledTags') < 0; });
console.log(out.join('\n'));
});
adb.stderr.on('data', console.error);
adb.on('close', function(code) {
adb.on('close', function (code) {
if (code > 0) {
d.reject('Failed to run logcat command.');
} else d.resolve();
@@ -49,7 +49,7 @@ module.exports.run = function() {
return d.promise;
};
module.exports.help = function() {
module.exports.help = function () {
console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'cordova', 'log')));
console.log('Gives the logcat output on the command line.');
process.exit(0);

View File

@@ -20,8 +20,10 @@
buildscript {
repositories {
mavenCentral()
jcenter()
maven {
url "https://maven.google.com"
}
}
// Switch the Android Gradle plugin version requirement depending on the

View File

@@ -26,15 +26,15 @@ var events = require('cordova-common').events;
var CordovaError = require('cordova-common').CordovaError;
var handlers = {
'source-file':{
install:function(obj, plugin, project, options) {
'source-file': {
install: function (obj, plugin, project, options) {
if (!obj.src) throw new CordovaError(generateAttributeError('src', 'source-file', plugin.id));
if (!obj.targetDir) throw new CordovaError(generateAttributeError('target-dir', 'source-file', plugin.id));
var dest = path.join(obj.targetDir, path.basename(obj.src));
if(options && options.android_studio === true) {
dest = path.join('app/src/main/java', obj.targetDir.substring(4), path.basename(obj.src));
if (options && options.android_studio === true) {
dest = path.join('app/src/main/java', obj.targetDir.substring(4), path.basename(obj.src));
}
if (options && options.force) {
@@ -43,42 +43,42 @@ var handlers = {
copyNewFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
}
},
uninstall:function(obj, plugin, project, options) {
uninstall: function (obj, plugin, project, options) {
var dest = path.join(obj.targetDir, path.basename(obj.src));
if(options && options.android_studio === true) {
dest = path.join('app/src/main/java', obj.targetDir.substring(4), path.basename(obj.src));
if (options && options.android_studio === true) {
dest = path.join('app/src/main/java', obj.targetDir.substring(4), path.basename(obj.src));
}
deleteJava(project.projectDir, dest);
}
},
'lib-file':{
install:function(obj, plugin, project, options) {
'lib-file': {
install: function (obj, plugin, project, options) {
var dest = path.join('libs', path.basename(obj.src));
if(options && options.android_studio === true) {
dest = path.join('app/libs', path.basename(obj.src));
if (options && options.android_studio === true) {
dest = path.join('app/libs', path.basename(obj.src));
}
copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
},
uninstall:function(obj, plugin, project, options) {
uninstall: function (obj, plugin, project, options) {
var dest = path.join('libs', path.basename(obj.src));
if(options && options.android_studio === true) {
dest = path.join('app/libs', path.basename(obj.src));
if (options && options.android_studio === true) {
dest = path.join('app/libs', path.basename(obj.src));
}
removeFile(project.projectDir, dest);
}
},
'resource-file':{
install:function(obj, plugin, project, options) {
'resource-file': {
install: function (obj, plugin, project, options) {
copyFile(plugin.dir, obj.src, project.projectDir, path.normalize(obj.target), !!(options && options.link));
},
uninstall:function(obj, plugin, project, options) {
uninstall: function (obj, plugin, project, options) {
removeFile(project.projectDir, path.normalize(obj.target));
}
},
'framework': {
install:function(obj, plugin, project, options) {
install: function (obj, plugin, project, options) {
var src = obj.src;
if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id));
@@ -95,15 +95,15 @@ var handlers = {
subDir = src;
}
if (obj.type == 'gradleReference') {
if (obj.type === 'gradleReference') {
project.addGradleReference(parentDir, subDir);
} else if (obj.type == 'sys') {
} else if (obj.type === 'sys') {
project.addSystemLibrary(parentDir, subDir);
} else {
project.addSubProject(parentDir, subDir);
}
},
uninstall:function(obj, plugin, project, options) {
uninstall: function (obj, plugin, project, options) {
var src = obj.src;
if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id));
@@ -125,17 +125,17 @@ var handlers = {
subDir = src;
}
if (obj.type == 'gradleReference') {
if (obj.type === 'gradleReference') {
project.removeGradleReference(parentDir, subDir);
} else if (obj.type == 'sys') {
} else if (obj.type === 'sys') {
project.removeSystemLibrary(parentDir, subDir);
} else {
project.removeSubProject(parentDir, subDir);
}
}
},
asset:{
install:function(obj, plugin, project, options) {
asset: {
install: function (obj, plugin, project, options) {
if (!obj.src) {
throw new CordovaError(generateAttributeError('src', 'asset', plugin.id));
}
@@ -149,7 +149,7 @@ var handlers = {
copyFile(plugin.dir, obj.src, project.platformWww, obj.target);
}
},
uninstall:function(obj, plugin, project, options) {
uninstall: function (obj, plugin, project, options) {
var target = obj.target || obj.src;
if (!target) throw new CordovaError(generateAttributeError('target', 'asset', plugin.id));
@@ -167,7 +167,7 @@ var handlers = {
install: function (obj, plugin, project, options) {
// Copy the plugin's files into the www directory.
var moduleSource = path.resolve(plugin.dir, obj.src);
var moduleName = plugin.id + '.' + (obj.name || path.basename(obj.src, path.extname (obj.src)));
var moduleName = plugin.id + '.' + (obj.name || path.basename(obj.src, path.extname(obj.src)));
// Read in the file, prepend the cordova.define, and write it back out.
var scriptContent = fs.readFileSync(moduleSource, 'utf-8').replace(/^\ufeff/, ''); // Window BOM
@@ -206,7 +206,7 @@ module.exports.getInstaller = function (type) {
events.emit('verbose', '<' + type + '> is not supported for android plugins');
};
module.exports.getUninstaller = function(type) {
module.exports.getUninstaller = function (type) {
if (handlers[type] && handlers[type].uninstall) {
return handlers[type].uninstall;
}
@@ -221,21 +221,19 @@ function copyFile (plugin_dir, src, project_dir, dest, link) {
// check that src path is inside plugin directory
var real_path = fs.realpathSync(src);
var real_plugin_path = fs.realpathSync(plugin_dir);
if (real_path.indexOf(real_plugin_path) !== 0)
throw new CordovaError('File "' + src + '" is located outside the plugin directory "' + plugin_dir + '"');
if (real_path.indexOf(real_plugin_path) !== 0) { throw new CordovaError('File "' + src + '" is located outside the plugin directory "' + plugin_dir + '"'); }
dest = path.resolve(project_dir, dest);
// check that dest path is located in project directory
if (dest.indexOf(project_dir) !== 0)
throw new CordovaError('Destination "' + dest + '" for source file "' + src + '" is located outside the project');
if (dest.indexOf(project_dir) !== 0) { throw new CordovaError('Destination "' + dest + '" for source file "' + src + '" is located outside the project'); }
shell.mkdir('-p', path.dirname(dest));
if (link) {
symlinkFileOrDirTree(src, dest);
} else if (fs.statSync(src).isDirectory()) {
// XXX shelljs decides to create a directory when -R|-r is used which sucks. http://goo.gl/nbsjq
shell.cp('-Rf', src+'/*', dest);
shell.cp('-Rf', src + '/*', dest);
} else {
shell.cp('-f', src, dest);
}
@@ -244,24 +242,22 @@ function copyFile (plugin_dir, src, project_dir, dest, link) {
// Same as copy file but throws error if target exists
function copyNewFile (plugin_dir, src, project_dir, dest, link) {
var target_path = path.resolve(project_dir, dest);
if (fs.existsSync(target_path))
throw new CordovaError('"' + target_path + '" already exists!');
if (fs.existsSync(target_path)) { throw new CordovaError('"' + target_path + '" already exists!'); }
copyFile(plugin_dir, src, project_dir, dest, !!link);
}
function symlinkFileOrDirTree(src, dest) {
function symlinkFileOrDirTree (src, dest) {
if (fs.existsSync(dest)) {
shell.rm('-Rf', dest);
}
if (fs.statSync(src).isDirectory()) {
shell.mkdir('-p', dest);
fs.readdirSync(src).forEach(function(entry) {
fs.readdirSync(src).forEach(function (entry) {
symlinkFileOrDirTree(path.join(src, entry), path.join(dest, entry));
});
}
else {
} else {
fs.symlinkSync(path.relative(fs.realpathSync(path.dirname(dest)), src), dest);
}
}
@@ -292,8 +288,8 @@ function removeFileAndParents (baseDir, destFile, stopper) {
// check if directory is empty
var curDir = path.dirname(file);
while(curDir !== path.resolve(baseDir, stopper)) {
if(fs.existsSync(curDir) && fs.readdirSync(curDir).length === 0) {
while (curDir !== path.resolve(baseDir, stopper)) {
if (fs.existsSync(curDir) && fs.readdirSync(curDir).length === 0) {
fs.rmdirSync(curDir);
curDir = path.resolve(curDir, '..');
} else {
@@ -303,6 +299,6 @@ function removeFileAndParents (baseDir, destFile, stopper) {
}
}
function generateAttributeError(attribute, element, id) {
function generateAttributeError (attribute, element, id) {
return 'Required attribute "' + attribute + '" not specified in <' + element + '> element from plugin: ' + id;
}

View File

@@ -16,6 +16,7 @@
specific language governing permissions and limitations
under the License.
*/
/* eslint no-useless-escape: 0 */
var Q = require('q');
var fs = require('fs');
@@ -23,6 +24,7 @@ var path = require('path');
var shell = require('shelljs');
var events = require('cordova-common').events;
var AndroidManifest = require('./AndroidManifest');
var checkReqs = require('./check_reqs');
var xmlHelpers = require('cordova-common').xmlHelpers;
var CordovaError = require('cordova-common').CordovaError;
var ConfigParser = require('cordova-common').ConfigParser;
@@ -40,17 +42,14 @@ module.exports.prepare = function (cordovaProject, options) {
this._config = updateConfigFilesFrom(cordovaProject.projectConfig, munger, this.locations);
// Update own www dir with project's www assets and plugins' assets and js-files
return Q.when(updateWww(cordovaProject, this.locations))
.then(function () {
return Q.when(updateWww(cordovaProject, this.locations)).then(function () {
// update project according to config.xml changes.
return updateProjectAccordingTo(self._config, self.locations);
})
.then(function () {
}).then(function () {
updateIcons(cordovaProject, path.relative(cordovaProject.root, self.locations.res));
updateSplashes(cordovaProject, path.relative(cordovaProject.root, self.locations.res));
updateFileResources(cordovaProject, path.relative(cordovaProject.root, self.locations.root));
})
.then(function () {
}).then(function () {
events.emit('verbose', 'Prepared android project successfully');
});
};
@@ -91,7 +90,7 @@ module.exports.clean = function (options) {
* represents current project's configuration. When returned, the
* configuration is already dumped to appropriate config.xml file.
*/
function updateConfigFilesFrom(sourceConfig, configMunger, locations) {
function updateConfigFilesFrom (sourceConfig, configMunger, locations) {
events.emit('verbose', 'Generating platform-specific config.xml from defaults for android at ' + locations.configXml);
// First cleanup current config and merge project's one into own
@@ -106,7 +105,7 @@ function updateConfigFilesFrom(sourceConfig, configMunger, locations) {
// Merge changes from app's config.xml into platform's one
var config = new ConfigParser(locations.configXml);
xmlHelpers.mergeXml(sourceConfig.doc.getroot(),
config.doc.getroot(), 'android', /*clobber=*/true);
config.doc.getroot(), 'android', /* clobber= */true);
config.write();
return config;
@@ -115,7 +114,7 @@ function updateConfigFilesFrom(sourceConfig, configMunger, locations) {
/**
* Logs all file operations via the verbose event stream, indented.
*/
function logFileOp(message) {
function logFileOp (message) {
events.emit('verbose', ' ' + message);
}
@@ -128,7 +127,7 @@ function logFileOp(message) {
* @param {Object} destinations An object that contains destination
* paths for www files.
*/
function updateWww(cordovaProject, destinations) {
function updateWww (cordovaProject, destinations) {
var sourceDirs = [
path.relative(cordovaProject.root, cordovaProject.locations.www),
path.relative(cordovaProject.root, destinations.platformWww)
@@ -151,7 +150,7 @@ function updateWww(cordovaProject, destinations) {
/**
* Cleans all files from the platform 'www' directory.
*/
function cleanWww(projectRoot, locations) {
function cleanWww (projectRoot, locations) {
var targetDir = path.relative(projectRoot, locations.www);
events.emit('verbose', 'Cleaning ' + targetDir);
@@ -167,19 +166,26 @@ function cleanWww(projectRoot, locations) {
* be used to update project
* @param {Object} locations A map of locations for this platform
*/
function updateProjectAccordingTo(platformConfig, locations) {
function updateProjectAccordingTo (platformConfig, locations) {
// Update app name by editing res/values/strings.xml
var name = platformConfig.name();
var strings = xmlHelpers.parseElementtreeSync(locations.strings);
var name = platformConfig.name();
strings.find('string[@name="app_name"]').text = name.replace(/\'/g, '\\\'');
var shortName = platformConfig.shortName && platformConfig.shortName();
if (shortName && shortName !== name) {
strings.find('string[@name="launcher_name"]').text = shortName.replace(/\'/g, '\\\'');
}
fs.writeFileSync(locations.strings, strings.write({indent: 4}), 'utf-8');
events.emit('verbose', 'Wrote out android application name "' + name + '" to ' + locations.strings);
// Java packages cannot support dashes
var pkg = (platformConfig.android_packageName() || platformConfig.packageName()).replace(/-/g, '_');
var androidPkgName = (platformConfig.android_packageName() || platformConfig.packageName()).replace(/-/g, '_');
var manifest = new AndroidManifest(locations.manifest);
var orig_pkg = manifest.getPackageId();
var manifestId = manifest.getPackageId();
manifest.getActivity()
.setOrientation(platformConfig.getPreference('orientation'))
@@ -187,36 +193,40 @@ function updateProjectAccordingTo(platformConfig, locations) {
manifest.setVersionName(platformConfig.version())
.setVersionCode(platformConfig.android_versionCode() || default_versionCode(platformConfig.version()))
.setPackageId(pkg)
.setPackageId(androidPkgName)
.setMinSdkVersion(platformConfig.getPreference('android-minSdkVersion', 'android'))
.setMaxSdkVersion(platformConfig.getPreference('android-maxSdkVersion', 'android'))
.setTargetSdkVersion(platformConfig.getPreference('android-targetSdkVersion', 'android'))
.write();
var javaPattern = path.join(locations.root, 'src', orig_pkg.replace(/\./g, '/'), '*.java');
var java_files = shell.ls(javaPattern).filter(function(f) {
var javaPattern = path.join(locations.root, 'src', manifestId.replace(/\./g, '/'), '*.java');
var java_files = shell.ls(javaPattern).filter(function (f) {
return shell.grep(/extends\s+CordovaActivity/g, f);
});
if (java_files.length === 0) {
throw new CordovaError('No Java files found that extend CordovaActivity.');
} else if(java_files.length > 1) {
} else if (java_files.length > 1) {
events.emit('log', 'Multiple candidate Java files that extend CordovaActivity found. Guessing at the first one, ' + java_files[0]);
}
var destFile = path.join(locations.root, 'src', pkg.replace(/\./g, '/'), path.basename(java_files[0]));
var destFile = path.join(locations.root, 'src', androidPkgName.replace(/\./g, '/'), path.basename(java_files[0]));
shell.mkdir('-p', path.dirname(destFile));
shell.sed(/package [\w\.]*;/, 'package ' + pkg + ';', java_files[0]).to(destFile);
events.emit('verbose', 'Wrote out Android package name "' + pkg + '" to ' + destFile);
shell.sed(/package [\w\.]*;/, 'package ' + androidPkgName + ';', java_files[0]).to(destFile);
events.emit('verbose', 'Wrote out Android package name "' + androidPkgName + '" to ' + destFile);
if (orig_pkg !== pkg) {
var removeOrigPkg = checkReqs.isWindows() || checkReqs.isDarwin() ?
manifestId.toUpperCase() !== androidPkgName.toUpperCase() :
manifestId !== androidPkgName;
if (removeOrigPkg) {
// If package was name changed we need to remove old java with main activity
shell.rm('-Rf',java_files[0]);
shell.rm('-Rf', java_files[0]);
// remove any empty directories
var currentDir = path.dirname(java_files[0]);
var sourcesRoot = path.resolve(locations.root, 'src');
while(currentDir !== sourcesRoot) {
if(fs.existsSync(currentDir) && fs.readdirSync(currentDir).length === 0) {
while (currentDir !== sourcesRoot) {
if (fs.existsSync(currentDir) && fs.readdirSync(currentDir).length === 0) {
fs.rmdirSync(currentDir);
currentDir = path.resolve(currentDir, '..');
} else {
@@ -229,7 +239,7 @@ function updateProjectAccordingTo(platformConfig, locations) {
// Consturct the default value for versionCode as
// PATCH + MINOR * 100 + MAJOR * 10000
// see http://developer.android.com/tools/publishing/versioning.html
function default_versionCode(version) {
function default_versionCode (version) {
var nums = version.split('-')[0].split('.');
var versionCode = 0;
if (+nums[0]) {
@@ -246,7 +256,7 @@ function default_versionCode(version) {
return versionCode;
}
function getImageResourcePath(resourcesDir, type, density, name, sourceName) {
function getImageResourcePath (resourcesDir, type, density, name, sourceName) {
if (/\.9\.png$/.test(sourceName)) {
name = name.replace(/\.png$/, '.9.png');
}
@@ -254,7 +264,7 @@ function getImageResourcePath(resourcesDir, type, density, name, sourceName) {
return resourcePath;
}
function updateSplashes(cordovaProject, platformResourcesDir) {
function updateSplashes (cordovaProject, platformResourcesDir) {
var resources = cordovaProject.projectConfig.getSplashScreens('android');
// if there are "splash" elements in config.xml
@@ -270,7 +280,7 @@ function updateSplashes(cordovaProject, platformResourcesDir) {
if (!resource.density) {
return;
}
if (resource.density == 'mdpi') {
if (resource.density === 'mdpi') {
hadMdpi = true;
}
var targetPath = getImageResourcePath(
@@ -290,7 +300,7 @@ function updateSplashes(cordovaProject, platformResourcesDir) {
resourceMap, { rootDir: cordovaProject.root }, logFileOp);
}
function cleanSplashes(projectRoot, projectConfig, platformResourcesDir) {
function cleanSplashes (projectRoot, projectConfig, platformResourcesDir) {
var resources = projectConfig.getSplashScreens('android');
if (resources.length > 0) {
var resourceMap = mapImageResources(projectRoot, platformResourcesDir, 'drawable', 'screen.png');
@@ -302,7 +312,7 @@ function cleanSplashes(projectRoot, projectConfig, platformResourcesDir) {
}
}
function updateIcons(cordovaProject, platformResourcesDir) {
function updateIcons (cordovaProject, platformResourcesDir) {
var icons = cordovaProject.projectConfig.getIcons('android');
// if there are icon elements in config.xml
@@ -326,7 +336,7 @@ function updateIcons(cordovaProject, platformResourcesDir) {
};
// find the best matching icon for a given density or size
// @output android_icons
var parseIcon = function(icon, icon_size) {
var parseIcon = function (icon, icon_size) {
// do I have a platform icon for that density already
var density = icon.density || sizeToDensityMap[icon_size];
if (!density) {
@@ -341,7 +351,7 @@ function updateIcons(cordovaProject, platformResourcesDir) {
};
// iterate over all icon elements to find the default icon and call parseIcon
for (var i=0; i<icons.length; i++) {
for (var i = 0; i < icons.length; i++) {
var icon = icons[i];
var size = icon.width;
if (!size) {
@@ -378,7 +388,7 @@ function updateIcons(cordovaProject, platformResourcesDir) {
resourceMap, { rootDir: cordovaProject.root }, logFileOp);
}
function cleanIcons(projectRoot, projectConfig, platformResourcesDir) {
function cleanIcons (projectRoot, projectConfig, platformResourcesDir) {
var icons = projectConfig.getIcons('android');
if (icons.length > 0) {
var resourceMap = mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'icon.png');
@@ -393,18 +403,16 @@ function cleanIcons(projectRoot, projectConfig, platformResourcesDir) {
/**
* Gets a map containing resources of a specified name from all drawable folders in a directory.
*/
function mapImageResources(rootDir, subDir, type, resourceName) {
function mapImageResources (rootDir, subDir, type, resourceName) {
var pathMap = {};
shell.ls(path.join(rootDir, subDir, type + '-*'))
.forEach(function (drawableFolder) {
shell.ls(path.join(rootDir, subDir, type + '-*')).forEach(function (drawableFolder) {
var imagePath = path.join(subDir, path.basename(drawableFolder), resourceName);
pathMap[imagePath] = null;
});
return pathMap;
}
function updateFileResources(cordovaProject, platformDir) {
function updateFileResources (cordovaProject, platformDir) {
var files = cordovaProject.projectConfig.getFileResources('android');
// if there are resource-file elements in config.xml
@@ -414,7 +422,7 @@ function updateFileResources(cordovaProject, platformDir) {
}
var resourceMap = {};
files.forEach(function(res) {
files.forEach(function (res) {
var targetPath = path.join(platformDir, res.target);
resourceMap[targetPath] = res.src;
});
@@ -424,20 +432,20 @@ function updateFileResources(cordovaProject, platformDir) {
resourceMap, { rootDir: cordovaProject.root }, logFileOp);
}
function cleanFileResources(projectRoot, projectConfig, platformDir) {
var files = projectConfig.getFileResources('android');
function cleanFileResources (projectRoot, projectConfig, platformDir) {
var files = projectConfig.getFileResources('android', true);
if (files.length > 0) {
events.emit('verbose', 'Cleaning resource files at ' + platformDir);
var resourceMap = {};
files.forEach(function(res) {
files.forEach(function (res) {
var filePath = path.join(platformDir, res.target);
resourceMap[filePath] = null;
});
FileUpdater.updatePaths(
resourceMap, { rootDir: projectRoot, all: true}, logFileOp);
resourceMap, {
rootDir: projectRoot, all: true}, logFileOp);
}
}
@@ -452,7 +460,7 @@ function cleanFileResources(projectRoot, projectConfig, platformDir) {
* default value, if there is no such preference. The default value is
* 'singleTop'
*/
function findAndroidLaunchModePreference(platformConfig) {
function findAndroidLaunchModePreference (platformConfig) {
var launchMode = platformConfig.getPreference('AndroidLaunchMode');
if (!launchMode) {
// Return a default value

View File

@@ -45,12 +45,12 @@ module.exports.retryPromise = function (attemts_left, promiseFunction) {
return promiseFunction.apply(undefined, promiseFunctionArguments).then(
// on success pass results through
function onFulfilled(value) {
function onFulfilled (value) {
return value;
},
// on rejection either retry, or throw the error
function onRejected(error) {
function onRejected (error) {
attemts_left -= 1;

View File

@@ -21,14 +21,14 @@
/* jshint loopfunc:true */
var path = require('path'),
build = require('./build'),
emulator = require('./emulator'),
device = require('./device'),
Q = require('q'),
events = require('cordova-common').events;
var path = require('path');
var build = require('./build');
var emulator = require('./emulator');
var device = require('./device');
var Q = require('q');
var events = require('cordova-common').events;
function getInstallTarget(runOptions) {
function getInstallTarget (runOptions) {
var install_target;
if (runOptions.target) {
install_target = runOptions.target;
@@ -51,17 +51,15 @@ function getInstallTarget(runOptions) {
*
* @return {Promise}
*/
module.exports.run = function(runOptions) {
module.exports.run = function (runOptions) {
var self = this;
var install_target = getInstallTarget(runOptions);
return Q()
.then(function() {
return Q().then(function () {
if (!install_target) {
// no target given, deploy to device if available, otherwise use the emulator.
return device.list()
.then(function(device_list) {
return device.list().then(function (device_list) {
if (device_list.length > 0) {
events.emit('warn', 'No target specified, deploying to device \'' + device_list[0] + '\'.');
install_target = device_list[0];
@@ -71,36 +69,31 @@ function getInstallTarget(runOptions) {
}
});
}
}).then(function() {
if (install_target == '--device') {
}).then(function () {
if (install_target === '--device') {
return device.resolveTarget(null);
} else if (install_target == '--emulator') {
} else if (install_target === '--emulator') {
// Give preference to any already started emulators. Else, start one.
return emulator.list_started()
.then(function(started) {
return emulator.list_started().then(function (started) {
return started && started.length > 0 ? started[0] : emulator.start();
}).then(function(emulatorId) {
}).then(function (emulatorId) {
return emulator.resolveTarget(emulatorId);
});
}
// They specified a specific device/emulator ID.
return device.list()
.then(function(devices) {
return device.list().then(function (devices) {
if (devices.indexOf(install_target) > -1) {
return device.resolveTarget(install_target);
}
return emulator.list_started()
.then(function(started_emulators) {
return emulator.list_started().then(function (started_emulators) {
if (started_emulators.indexOf(install_target) > -1) {
return emulator.resolveTarget(install_target);
}
return emulator.list_images()
.then(function(avds) {
return emulator.list_images().then(function (avds) {
// if target emulator isn't started, then start it.
for (var avd in avds) {
if (avds[avd].name == install_target) {
return emulator.start(install_target)
.then(function(emulatorId) {
if (avds[avd].name === install_target) {
return emulator.start(install_target).then(function (emulatorId) {
return emulator.resolveTarget(emulatorId);
});
}
@@ -109,16 +102,14 @@ function getInstallTarget(runOptions) {
});
});
});
}).then(function(resolvedTarget) {
}).then(function (resolvedTarget) {
// Better just call self.build, but we're doing some processing of
// build results (according to platformApi spec) so they are in different
// format than emulator.install expects.
// TODO: Update emulator/device.install to handle this change
return build.run.call(self, runOptions, resolvedTarget)
.then(function(buildResults) {
return build.run.call(self, runOptions, resolvedTarget).then(function (buildResults) {
if (resolvedTarget.isEmulator) {
return emulator.wait_for_boot(resolvedTarget.target)
.then(function () {
return emulator.wait_for_boot(resolvedTarget.target).then(function () {
return emulator.install(resolvedTarget, buildResults);
});
}
@@ -127,7 +118,7 @@ function getInstallTarget(runOptions) {
});
};
module.exports.help = function() {
module.exports.help = function () {
console.log('Usage: ' + path.relative(process.cwd(), process.argv[1]) + ' [options]');
console.log('Build options :');
console.log(' --debug : Builds project in debug mode');

View File

@@ -18,7 +18,7 @@
@ECHO OFF
SET script_path="%~dp0start-emulator"
IF EXIST %script_path% (
node "%script_path%" %*
node %script_path% %*
) ELSE (
ECHO.
ECHO ERROR: Could not find 'start-emulator' script in 'cordova\lib' folder, aborting...>&2

View File

@@ -20,7 +20,7 @@
*/
// Coho updates this line:
var VERSION = "6.2.0-dev";
var VERSION = "6.3.0";
module.exports.version = VERSION;

View File

@@ -1,5 +1,6 @@
/* eslint-disable */
// Platform: android
// 7c5fcc5a5adfbf3fb8ceaf36fbdd4bd970bd9c20
// 74a4adc2d0fddb1e0cfb9be1961494ef0afc9893
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -19,7 +20,7 @@
under the License.
*/
;(function() {
var PLATFORM_VERSION_BUILD_LABEL = '6.2.0-dev';
var PLATFORM_VERSION_BUILD_LABEL = '6.3.0';
// file: src/scripts/require.js
/*jshint -W079 */
@@ -330,7 +331,7 @@ module.exports = cordova;
});
// file: /Users/steveng/repo/cordova/cordova-android/cordova-js-src/android/nativeapiprovider.js
// file: /Users/jbowser/cordova/cordova-android/cordova-js-src/android/nativeapiprovider.js
define("cordova/android/nativeapiprovider", function(require, exports, module) {
/**
@@ -353,7 +354,7 @@ module.exports = {
});
// file: /Users/steveng/repo/cordova/cordova-android/cordova-js-src/android/promptbasednativeapi.js
// file: /Users/jbowser/cordova/cordova-android/cordova-js-src/android/promptbasednativeapi.js
define("cordova/android/promptbasednativeapi", function(require, exports, module) {
/**
@@ -886,7 +887,7 @@ module.exports = channel;
});
// file: /Users/steveng/repo/cordova/cordova-android/cordova-js-src/exec.js
// file: /Users/jbowser/cordova/cordova-android/cordova-js-src/exec.js
define("cordova/exec", function(require, exports, module) {
/**
@@ -1649,7 +1650,7 @@ exports.reset();
});
// file: /Users/steveng/repo/cordova/cordova-android/cordova-js-src/platform.js
// file: /Users/jbowser/cordova/cordova-android/cordova-js-src/platform.js
define("cordova/platform", function(require, exports, module) {
// The last resume event that was received that had the result of a plugin call.
@@ -1759,7 +1760,7 @@ function onMessageFromNative(msg) {
});
// file: /Users/steveng/repo/cordova/cordova-android/cordova-js-src/plugin/android/app.js
// file: /Users/jbowser/cordova/cordova-android/cordova-js-src/plugin/android/app.js
define("cordova/plugin/android/app", function(require, exports, module) {
var exec = require('cordova/exec');
@@ -2205,4 +2206,4 @@ window.cordova = require('cordova');
require('cordova/init');
})();
})();

View File

@@ -18,25 +18,25 @@
*/
var app = {
// Application Constructor
initialize: function() {
initialize: function () {
this.bindEvents();
},
// Bind Event Listeners
//
// Bind any events that are required on startup. Common events are:
// 'load', 'deviceready', 'offline', and 'online'.
bindEvents: function() {
bindEvents: function () {
document.addEventListener('deviceready', this.onDeviceReady, false);
},
// deviceready Event Handler
//
// The scope of 'this' is the event. In order to call the 'receivedEvent'
// function, we must explicitly call 'app.receivedEvent(...);'
onDeviceReady: function() {
onDeviceReady: function () {
app.receivedEvent('deviceready');
},
// Update DOM on a Received Event
receivedEvent: function(id) {
receivedEvent: function (id) {
var parentElement = document.getElementById(id);
var listeningElement = parentElement.querySelector('.listening');
var receivedElement = parentElement.querySelector('.received');
@@ -48,4 +48,4 @@ var app = {
}
};
app.initialize();
app.initialize();

View File

@@ -21,8 +21,10 @@ apply plugin: 'com.android.application'
buildscript {
repositories {
mavenCentral()
jcenter()
maven {
url "https://maven.google.com"
}
}
// Switch the Android Gradle plugin version requirement depending on the
@@ -37,8 +39,10 @@ buildscript {
// Allow plugins to declare Maven dependencies via build-extras.gradle.
allprojects {
repositories {
mavenCentral();
jcenter()
maven {
url "https://maven.google.com"
}
}
}

View File

@@ -24,8 +24,10 @@ ext {
buildscript {
repositories {
mavenCentral()
jcenter()
maven {
url "https://maven.google.com"
}
}
dependencies {
@@ -40,7 +42,7 @@ apply plugin: 'com.github.dcendents.android-maven'
apply plugin: 'com.jfrog.bintray'
group = 'org.apache.cordova'
version = '6.2.0'
version = '6.3.0'
android {
compileSdkVersion cdvCompileSdkVersion
@@ -127,9 +129,9 @@ bintray {
licenses = ['Apache-2.0']
labels = ['android', 'cordova', 'phonegap']
version {
name = '6.2.0'
name = '6.3.0'
released = new Date()
vcsTag = '6.2.0'
vcsTag = '6.3.0'
}
}
}

View File

@@ -10,7 +10,7 @@
# Indicates whether an apk should be generated for each density.
split.density=false
# Project target.
target=android-25
target=android-26
apk-configurations=
renderscript.opt.level=O0
android.library=true

View File

@@ -0,0 +1,70 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.cordova;
/*
* This is a utility class that allows us to get the BuildConfig variable, which is required
* for the use of different providers. This is not guaranteed to work, and it's better for this
* to be set in the build step in config.xml
*
*/
import android.app.Activity;
import android.content.Context;
import java.lang.reflect.Field;
public class BuildHelper {
private static String TAG="BuildHelper";
/*
* This needs to be implemented if you wish to use the Camera Plugin or other plugins
* that read the Build Configuration.
*
* Thanks to Phil@Medtronic and Graham Borland for finding the answer and posting it to
* StackOverflow. This is annoying as hell! However, this method does not work with
* ProGuard, and you should use the config.xml to define the application_id
*
*/
public static Object getBuildConfigValue(Context ctx, String key)
{
try
{
Class<?> clazz = Class.forName(ctx.getPackageName() + ".BuildConfig");
Field field = clazz.getField(key);
return field.get(null);
} catch (ClassNotFoundException e) {
LOG.d(TAG, "Unable to get the BuildConfig, is this built with ANT?");
e.printStackTrace();
} catch (NoSuchFieldException e) {
LOG.d(TAG, key + " is not a valid field. Check your build.gradle");
} catch (IllegalAccessException e) {
LOG.d(TAG, "Illegal Access Exception: Let's print a stack trace.");
e.printStackTrace();
}
return null;
}
}

View File

@@ -31,7 +31,7 @@ import android.webkit.WebChromeClient.CustomViewCallback;
* are not expected to implement it.
*/
public interface CordovaWebView {
public static final String CORDOVA_VERSION = "6.2.0-dev";
public static final String CORDOVA_VERSION = "6.3.0";
void init(CordovaInterface cordova, List<PluginEntry> pluginEntries, CordovaPreferences preferences);

View File

@@ -0,0 +1,87 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.cordova;
import java.util.Arrays;
import org.json.JSONException;
import android.content.pm.PackageManager;
/**
* This class provides reflective methods for permission requesting and checking so that plugins
* written for cordova-android 5.0.0+ can still compile with earlier cordova-android versions.
*/
public class PermissionHelper {
private static final String LOG_TAG = "CordovaPermissionHelper";
/**
* Requests a "dangerous" permission for the application at runtime. This is a helper method
* alternative to cordovaInterface.requestPermission() that does not require the project to be
* built with cordova-android 5.0.0+
*
* @param plugin The plugin the permission is being requested for
* @param requestCode A requestCode to be passed to the plugin's onRequestPermissionResult()
* along with the result of the permission request
* @param permission The permission to be requested
*/
public static void requestPermission(CordovaPlugin plugin, int requestCode, String permission) {
PermissionHelper.requestPermissions(plugin, requestCode, new String[] {permission});
}
/**
* Requests "dangerous" permissions for the application at runtime. This is a helper method
* alternative to cordovaInterface.requestPermissions() that does not require the project to be
* built with cordova-android 5.0.0+
*
* @param plugin The plugin the permissions are being requested for
* @param requestCode A requestCode to be passed to the plugin's onRequestPermissionResult()
* along with the result of the permissions request
* @param permissions The permissions to be requested
*/
public static void requestPermissions(CordovaPlugin plugin, int requestCode, String[] permissions) {
plugin.cordova.requestPermissions(plugin, requestCode, permissions);
}
/**
* Checks at runtime to see if the application has been granted a permission. This is a helper
* method alternative to cordovaInterface.hasPermission() that does not require the project to
* be built with cordova-android 5.0.0+
*
* @param plugin The plugin the permission is being checked against
* @param permission The permission to be checked
*
* @return True if the permission has already been granted and false otherwise
*/
public static boolean hasPermission(CordovaPlugin plugin, String permission) {
return plugin.cordova.hasPermission(permission);
}
private static void deliverPermissionResult(CordovaPlugin plugin, int requestCode, String[] permissions) {
// Generate the request results
int[] requestResults = new int[permissions.length];
Arrays.fill(requestResults, PackageManager.PERMISSION_GRANTED);
try {
plugin.onRequestPermissionResult(requestCode, permissions, requestResults);
} catch (JSONException e) {
LOG.e(LOG_TAG, "JSONException when delivering permissions results", e);
}
}
}

View File

@@ -110,7 +110,11 @@ public class SystemWebViewEngine implements CordovaWebViewEngine {
nativeToJsMessageQueue.addBridgeMode(new NativeToJsMessageQueue.OnlineEventsBridgeMode(new NativeToJsMessageQueue.OnlineEventsBridgeMode.OnlineEventsBridgeModeDelegate() {
@Override
public void setNetworkAvailable(boolean value) {
webView.setNetworkAvailable(value);
//sometimes this can be called after calling webview.destroy() on destroy()
//thus resulting in a NullPointerException
if(webView!=null) {
webView.setNetworkAvailable(value);
}
}
@Override
public void runOnUiThread(Runnable r) {

6
node_modules/abbrev/package.json generated vendored
View File

@@ -10,7 +10,7 @@
"spec": ">=1.0.0 <2.0.0",
"type": "range"
},
"/Users/steveng/repo/cordova/cordova-android/node_modules/nopt"
"/Users/jbowser/cordova/cordova-android/node_modules/nopt"
]
],
"_from": "abbrev@>=1.0.0 <2.0.0",
@@ -40,11 +40,11 @@
"_requiredBy": [
"/nopt"
],
"_resolved": "http://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz",
"_resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz",
"_shasum": "d0554c2256636e2f56e7c2e5ad183f859428d81f",
"_shrinkwrap": null,
"_spec": "abbrev@1",
"_where": "/Users/steveng/repo/cordova/cordova-android/node_modules/nopt",
"_where": "/Users/jbowser/cordova/cordova-android/node_modules/nopt",
"author": {
"name": "Isaac Z. Schlueter",
"email": "i@izs.me"

8
node_modules/android-versions/.jshintignore generated vendored Normal file
View File

@@ -0,0 +1,8 @@
.git/
node_modules/
coverage/
build/
assets/
dist/
docs/
tests/

28
node_modules/android-versions/.jshintrc generated vendored Normal file
View File

@@ -0,0 +1,28 @@
{
"indent": 2,
"forin": true,
"noarg": true,
"bitwise": true,
"nonew": true,
"strict": true,
"browser": true,
"devel": true,
"node": false,
"jquery": false,
"esnext": false,
"moz": false,
"es3": false,
"asi": true,
"eqnull": true,
"debug": true,
"boss": true,
"evil": true,
"loopfunc": true,
"laxbreak": true,
"unused": true,
"undef": true
}

3
node_modules/android-versions/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,3 @@
language: node_js
node_js:
- "6.1.0"

87
node_modules/android-versions/README.md generated vendored Normal file
View File

@@ -0,0 +1,87 @@
Android Versions
================
A node module to get Android versions by API level, NDK level, semantic version, or version name.
Versions are referenced from [source.android.com/source/build-numbers.html](https://source.android.com/source/build-numbers.html#platform-code-names-versions-api-levels-and-ndk-releases). The version for "Current Development Build" (`"CUR_DEVELOPMENT"`) is not included in the list of `VERSIONS`.
[![NPM version][npm-image]][npm-url]
[![build status][travis-image]][travis-url]
[npm-image]: https://img.shields.io/npm/v/android-versions.svg?style=flat-square
[npm-url]: https://npmjs.org/package/android-versions
[travis-image]: https://img.shields.io/travis/dvoiss/android-versions.svg?style=flat-square
[travis-url]: https://travis-ci.org/dvoiss/android-versions
## Install
```bash
# NPM
npm install android-versions --save
# YARN
yarn add android-versions
```
## Usage
View the tests for more advanced usage.
```javascript
const android = require('android-versions')
```
#### Get by API level:
```javascript
console.log(android.get(23))
=> { api: 23, ndk: 8, semver: "6.0", name: "Marshmallow", versionCode: "M" }
```
#### Get by version:
```javascript
console.log(android.get("2.3.3"))
=> { api: 10, ndk: 5, semver: "2.3.3", name: "Gingerbread", versionCode: "GINGERBREAD_MR1" }
```
#### Get all by predicate:
```
android.getAll((version) => {
return version.ndk > 5 && version.api < 15
}).map((version) => version.versionCode)
=> [ "HONEYCOMB_MR1", "HONEYCOMB_MR2", "ICE_CREAM_SANDWICH" ]
```
#### Access a specific version with all info:
```
android.LOLLIPOP
=> { api: 21, ndk: 8, semver: "5.0", name: "Lollipop", versionCode: "LOLLIPOP" }
```
#### Access the complete reference of Android versions with all info:
```javascript
android.VERSIONS
=> {
BASE: { api: 1, ndk: 0, semver: "1.0", name: "(no code name)", versionCode: "BASE" },
...
N: { api: 24, ndk: 8, semver: "7.0", name: "Nougat", versionCode: "N" }
...
}
```
## Test
```bash
npm run test
```
## License
MIT

153
node_modules/android-versions/index.js generated vendored Normal file
View File

@@ -0,0 +1,153 @@
/**
* Copyright (c) 2016, David Voiss <davidvoiss@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose
* with or without fee is hereby granted, provided that the above copyright notice
* and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
* THIS SOFTWARE.
*/
/* jshint node: true */
"use strict";
/**
* A module to get Android versions by API level, NDK level, semantic version, or version name.
*
* Versions are referenced from here:
* {@link https://source.android.com/source/build-numbers.html#platform-code-names-versions-api-levels-and-ndk-releases}
* {@link https://github.com/android/platform_frameworks_base/blob/master/core/java/android/os/Build.java}
*
* The version for "Current Development Build" ("CUR_DEVELOPMENT") is not included.
*
* @module android-versions
*/
var VERSIONS = {
BASE: { api: 1, ndk: 0, semver: "1.0", name: "(no code name)", versionCode: "BASE" },
BASE_1_1: { api: 2, ndk: 0, semver: "1.1", name: "(no code name)", versionCode: "BASE_1_1" },
CUPCAKE: { api: 3, ndk: 1, semver: "1.5", name: "Cupcake", versionCode: "CUPCAKE" },
DONUT: { api: 4, ndk: 2, semver: "1.6", name: "Donut", versionCode: "DONUT" },
ECLAIR: { api: 5, ndk: 2, semver: "2.0", name: "Eclair", versionCode: "ECLAIR" },
ECLAIR_0_1: { api: 6, ndk: 2, semver: "2.0.1", name: "Eclair", versionCode: "ECLAIR_0_1" },
ECLAIR_MR1: { api: 7, ndk: 3, semver: "2.1", name: "Eclair", versionCode: "ECLAIR_MR1" },
FROYO: { api: 8, ndk: 4, semver: "2.2", name: "Froyo", versionCode: "FROYO" },
GINGERBREAD: { api: 9, ndk: 5, semver: "2.3", name: "Gingerbread", versionCode: "GINGERBREAD" },
GINGERBREAD_MR1: { api: 10, ndk: 5, semver: "2.3.3", name: "Gingerbread", versionCode: "GINGERBREAD_MR1" },
HONEYCOMB: { api: 11, ndk: 5, semver: "3.0", name: "Honeycomb", versionCode: "HONEYCOMB" },
HONEYCOMB_MR1: { api: 12, ndk: 6, semver: "3.1", name: "Honeycomb", versionCode: "HONEYCOMB_MR1" },
HONEYCOMB_MR2: { api: 13, ndk: 6, semver: "3.2", name: "Honeycomb", versionCode: "HONEYCOMB_MR2" },
ICE_CREAM_SANDWICH: { api: 14, ndk: 7, semver: "4.0", name: "Ice Cream Sandwich", versionCode: "ICE_CREAM_SANDWICH" },
ICE_CREAM_SANDWICH_MR1: { api: 15, ndk: 8, semver: "4.0.3", name: "Ice Cream Sandwich", versionCode: "ICE_CREAM_SANDWICH_MR1" },
JELLY_BEAN: { api: 16, ndk: 8, semver: "4.1", name: "Jellybean", versionCode: "JELLY_BEAN" },
JELLY_BEAN_MR1: { api: 17, ndk: 8, semver: "4.2", name: "Jellybean", versionCode: "JELLY_BEAN_MR1" },
JELLY_BEAN_MR2: { api: 18, ndk: 8, semver: "4.3", name: "Jellybean", versionCode: "JELLY_BEAN_MR2" },
KITKAT: { api: 19, ndk: 8, semver: "4.4", name: "KitKat", versionCode: "KITKAT" },
KITKAT_WATCH: { api: 20, ndk: 8, semver: "4.4", name: "KitKat Watch", versionCode: "KITKAT_WATCH" },
LOLLIPOP: { api: 21, ndk: 8, semver: "5.0", name: "Lollipop", versionCode: "LOLLIPOP" },
LOLLIPOP_MR1: { api: 22, ndk: 8, semver: "5.1", name: "Lollipop", versionCode: "LOLLIPOP_MR1" },
M: { api: 23, ndk: 8, semver: "6.0", name: "Marshmallow", versionCode: "M" },
N: { api: 24, ndk: 8, semver: "7.0", name: "Nougat", versionCode: "N" },
N_MR1: { api: 25, ndk: 8, semver: "7.1", name: "Nougat", versionCode: "N_MR1" },
O: { api: 26, ndk: 8, semver: "8.0.0", name: "Oreo", versionCode: "O" }
}
// This altSemVer accomodates the variations of semantic versions in the table above.
// For instance, Oreo is 8.0.0 while N is 7.0, searching for "8.0" or "8.0.0" will
// return Oreo, or searching for "7.0" or "7.0.0" will return N. "2.2.0" will return Froyo.
function getAlternateSemVer(semver) {
if (semver.match(/\d+.\d+.0/)) {
return semver.replace(/.\d+$/, '')
} else if (semver.match(/^\d+.\d+$/)) {
return semver + '.0'
} else {
return semver
}
}
// The default predicate compares against API level, semver, name, or code.
function getFromDefaultPredicate(arg) {
// Coerce arg to string for comparisons below.
arg = arg.toString()
return getFromPredicate(function(version) {
// Check API level before all else.
if (arg === version.api.toString()) {
return true
}
// Compare semver and alternate semver (see above).
var altSemVer = getAlternateSemVer(arg)
if (version.semver === arg || version.semver === altSemVer) {
return true
}
// Compare version name and code.
return arg === version.name || arg === version.versionCode
})
}
// The function to allow passing a predicate.
function getFromPredicate(predicate) {
if (predicate === null) {
return null
}
return Object.keys(VERSIONS).filter(function(version) {
return predicate(VERSIONS[version])
}).map(function(key) { return VERSIONS[key] })
}
/**
* The Android version codes available as keys for easier look-up.
*/
Object.keys(VERSIONS).forEach(function(name) {
exports[name] = VERSIONS[name]
})
/**
* The complete reference of Android versions for easier look-up.
*/
exports.VERSIONS = VERSIONS
/**
* Retrieve a single Android version.
*
* @param {object | Function} arg - The value or predicate to use to retrieve values.
*
* @return {object} An object representing the version found or null if none found.
*/
exports.get = function(arg) {
var result = exports.getAll(arg)
if (result === null || result.length === 0) {
return null
}
return result[0]
}
/**
* Retrieve all Android versions that meet the criteria of the argument.
*
* @param {object | Function} arg - The value or predicate to use to retrieve values.
*
* @return {object} An object representing the version found or null if none found.
*/
exports.getAll = function(arg) {
if (arg === null) {
return null
}
if (typeof arg === "function") {
return getFromPredicate(arg)
} else {
return getFromDefaultPredicate(arg)
}
}

103
node_modules/android-versions/package.json generated vendored Normal file
View File

@@ -0,0 +1,103 @@
{
"_args": [
[
{
"raw": "android-versions@^1.2.0",
"scope": null,
"escapedName": "android-versions",
"name": "android-versions",
"rawSpec": "^1.2.0",
"spec": ">=1.2.0 <2.0.0",
"type": "range"
},
"/Users/jbowser/cordova/cordova-android"
]
],
"_from": "android-versions@>=1.2.0 <2.0.0",
"_id": "android-versions@1.2.1",
"_inCache": true,
"_location": "/android-versions",
"_nodeVersion": "8.0.0",
"_npmOperationalInternal": {
"host": "s3://npm-registry-packages",
"tmp": "tmp/android-versions-1.2.1.tgz_1505373302036_0.5689644906669855"
},
"_npmUser": {
"name": "dvoiss",
"email": "davidvoiss@gmail.com"
},
"_npmVersion": "5.4.0",
"_phantomChildren": {},
"_requested": {
"raw": "android-versions@^1.2.0",
"scope": null,
"escapedName": "android-versions",
"name": "android-versions",
"rawSpec": "^1.2.0",
"spec": ">=1.2.0 <2.0.0",
"type": "range"
},
"_requiredBy": [
"/"
],
"_resolved": "https://registry.npmjs.org/android-versions/-/android-versions-1.2.1.tgz",
"_shasum": "3f50baf693e73a512c3c5403542291cead900063",
"_shrinkwrap": null,
"_spec": "android-versions@^1.2.0",
"_where": "/Users/jbowser/cordova/cordova-android",
"author": {
"name": "dvoiss"
},
"bugs": {
"url": "https://github.com/dvoiss/android-versions/issues"
},
"dependencies": {},
"description": "Get the name, API level, version level, NDK level, or version code from any version of Android.",
"devDependencies": {
"jsdoc": "^3.4.0",
"jshint": "^2.9.2",
"tape": "^4.6.0"
},
"directories": {},
"dist": {
"integrity": "sha512-k6zlrtWbJ3tx1ZsyyJ0Bo3r6cqPA3JUnFGv7pnIaLr1XVxSi2Tcem2lg3kBebFp27v/A40tZqdlouPyakpyKrw==",
"shasum": "3f50baf693e73a512c3c5403542291cead900063",
"tarball": "https://registry.npmjs.org/android-versions/-/android-versions-1.2.1.tgz"
},
"gitHead": "7e2def6e70634a4ebcaaa639a4c4955ae2a566e7",
"homepage": "https://github.com/dvoiss/android-versions#readme",
"keywords": [
"android",
"version",
"versions",
"ndk",
"nougat",
"marshmallow",
"api",
"level"
],
"license": "MIT",
"main": "index.js",
"maintainers": [
{
"name": "dvoiss",
"email": "davidvoiss@gmail.com"
}
],
"name": "android-versions",
"optionalDependencies": {},
"pre-commit": [
"jshint"
],
"readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git+https://github.com/dvoiss/android-versions.git"
},
"scripts": {
"docs": "jsdoc index.js -d ./docs/ -R README.md --debug",
"jshint": "jshint .",
"test": "tape tests/**/*.js"
},
"version": "1.2.1"
}

6
node_modules/ansi/package.json generated vendored
View File

@@ -10,7 +10,7 @@
"spec": ">=0.3.1 <0.4.0",
"type": "range"
},
"/Users/steveng/repo/cordova/cordova-android/node_modules/cordova-common"
"/Users/jbowser/cordova/cordova-android/node_modules/cordova-common"
]
],
"_from": "ansi@>=0.3.1 <0.4.0",
@@ -36,11 +36,11 @@
"_requiredBy": [
"/cordova-common"
],
"_resolved": "http://registry.npmjs.org/ansi/-/ansi-0.3.1.tgz",
"_resolved": "https://registry.npmjs.org/ansi/-/ansi-0.3.1.tgz",
"_shasum": "0c42d4fb17160d5a9af1e484bace1c66922c1b21",
"_shrinkwrap": null,
"_spec": "ansi@^0.3.1",
"_where": "/Users/steveng/repo/cordova/cordova-android/node_modules/cordova-common",
"_where": "/Users/jbowser/cordova/cordova-android/node_modules/cordova-common",
"author": {
"name": "Nathan Rajlich",
"email": "nathan@tootallnate.net",

View File

@@ -1,3 +1,4 @@
'use strict';
module.exports = balanced;
function balanced(a, b, str) {
if (a instanceof RegExp) a = maybeMatch(a, str);

View File

@@ -2,49 +2,49 @@
"_args": [
[
{
"raw": "balanced-match@^0.4.1",
"raw": "balanced-match@^1.0.0",
"scope": null,
"escapedName": "balanced-match",
"name": "balanced-match",
"rawSpec": "^0.4.1",
"spec": ">=0.4.1 <0.5.0",
"rawSpec": "^1.0.0",
"spec": ">=1.0.0 <2.0.0",
"type": "range"
},
"/Users/steveng/repo/cordova/cordova-android/node_modules/brace-expansion"
"/Users/jbowser/cordova/cordova-android/node_modules/brace-expansion"
]
],
"_from": "balanced-match@>=0.4.1 <0.5.0",
"_id": "balanced-match@0.4.2",
"_from": "balanced-match@>=1.0.0 <2.0.0",
"_id": "balanced-match@1.0.0",
"_inCache": true,
"_location": "/balanced-match",
"_nodeVersion": "4.4.7",
"_nodeVersion": "7.8.0",
"_npmOperationalInternal": {
"host": "packages-16-east.internal.npmjs.com",
"tmp": "tmp/balanced-match-0.4.2.tgz_1468834991581_0.6590619895141572"
"host": "s3://npm-registry-packages",
"tmp": "tmp/balanced-match-1.0.0.tgz_1497251909645_0.8755026108119637"
},
"_npmUser": {
"name": "juliangruber",
"email": "julian@juliangruber.com"
},
"_npmVersion": "2.15.8",
"_npmVersion": "4.2.0",
"_phantomChildren": {},
"_requested": {
"raw": "balanced-match@^0.4.1",
"raw": "balanced-match@^1.0.0",
"scope": null,
"escapedName": "balanced-match",
"name": "balanced-match",
"rawSpec": "^0.4.1",
"spec": ">=0.4.1 <0.5.0",
"rawSpec": "^1.0.0",
"spec": ">=1.0.0 <2.0.0",
"type": "range"
},
"_requiredBy": [
"/brace-expansion"
],
"_resolved": "http://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz",
"_shasum": "cb3f3e3c732dc0f01ee70b403f302e61d7709838",
"_resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"_shasum": "89b4d199ab2bee49de164ea02b89ce462d71b767",
"_shrinkwrap": null,
"_spec": "balanced-match@^0.4.1",
"_where": "/Users/steveng/repo/cordova/cordova-android/node_modules/brace-expansion",
"_spec": "balanced-match@^1.0.0",
"_where": "/Users/jbowser/cordova/cordova-android/node_modules/brace-expansion",
"author": {
"name": "Julian Gruber",
"email": "mail@juliangruber.com",
@@ -56,14 +56,15 @@
"dependencies": {},
"description": "Match balanced character pairs, like \"{\" and \"}\"",
"devDependencies": {
"matcha": "^0.7.0",
"tape": "^4.6.0"
},
"directories": {},
"dist": {
"shasum": "cb3f3e3c732dc0f01ee70b403f302e61d7709838",
"tarball": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz"
"shasum": "89b4d199ab2bee49de164ea02b89ce462d71b767",
"tarball": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz"
},
"gitHead": "57c2ea29d89a2844ae3bdcc637c6e2cbb73725e2",
"gitHead": "d701a549a7653a874eebce7eca25d3577dc868ac",
"homepage": "https://github.com/juliangruber/balanced-match",
"keywords": [
"match",
@@ -88,6 +89,7 @@
"url": "git://github.com/juliangruber/balanced-match.git"
},
"scripts": {
"bench": "make bench",
"test": "make test"
},
"testling": {
@@ -106,5 +108,5 @@
"android-browser/4.2..latest"
]
},
"version": "0.4.2"
"version": "1.0.0"
}

View File

@@ -10,7 +10,7 @@
"spec": "0.0.8",
"type": "version"
},
"/Users/steveng/repo/cordova/cordova-android/node_modules/plist"
"/Users/jbowser/cordova/cordova-android/node_modules/plist"
]
],
"_from": "base64-js@0.0.8",
@@ -36,11 +36,11 @@
"_requiredBy": [
"/plist"
],
"_resolved": "http://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz",
"_resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz",
"_shasum": "1101e9544f4a76b1bc3b26d452ca96d7a35e7978",
"_shrinkwrap": null,
"_spec": "base64-js@0.0.8",
"_where": "/Users/steveng/repo/cordova/cordova-android/node_modules/plist",
"_where": "/Users/jbowser/cordova/cordova-android/node_modules/plist",
"author": {
"name": "T. Jameson Little",
"email": "t.jameson.little@gmail.com"

2364
node_modules/big-integer/BigInteger.d.ts generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -118,7 +118,7 @@ var bigInt = (function (undefined) {
}
BigInteger.prototype.add = function (v) {
var value, n = parseValue(v);
var n = parseValue(v);
if (this.sign !== n.sign) {
return this.subtract(n.negate());
}
@@ -177,7 +177,7 @@ var bigInt = (function (undefined) {
}
function subtractAny(a, b, sign) {
var value, isSmall;
var value;
if (compareAbs(a, b) >= 0) {
value = subtract(a,b);
} else {
@@ -326,7 +326,7 @@ var bigInt = (function (undefined) {
}
BigInteger.prototype.multiply = function (v) {
var value, n = parseValue(v),
var n = parseValue(v),
a = this.value, b = n.value,
sign = this.sign !== n.sign,
abs;
@@ -467,6 +467,7 @@ var bigInt = (function (undefined) {
guess, xlen, highx, highy, check;
while (a_l) {
part.unshift(a[--a_l]);
trim(part);
if (compareAbs(part, b) < 0) {
result.push(0);
continue;
@@ -825,20 +826,24 @@ var bigInt = (function (undefined) {
BigInteger.prototype.modInv = function (n) {
var t = bigInt.zero, newT = bigInt.one, r = parseValue(n), newR = this.abs(), q, lastT, lastR;
while (!newR.equals(bigInt.zero)) {
q = r.divide(newR);
lastT = t;
lastR = r;
t = newT;
r = newR;
newT = lastT.subtract(q.multiply(newT));
newR = lastR.subtract(q.multiply(newR));
q = r.divide(newR);
lastT = t;
lastR = r;
t = newT;
r = newR;
newT = lastT.subtract(q.multiply(newT));
newR = lastR.subtract(q.multiply(newR));
}
if (!r.equals(1)) throw new Error(this.toString() + " and " + n.toString() + " are not co-prime");
if (t.compare(0) === -1) {
t = t.add(n);
t = t.add(n);
}
if (this.isNegative()) {
return t.negate();
}
return t;
}
};
SmallInteger.prototype.modInv = BigInteger.prototype.modInv;
BigInteger.prototype.next = function () {
@@ -977,7 +982,7 @@ var bigInt = (function (undefined) {
b = parseValue(b);
return a.greater(b) ? a : b;
}
function min(a,b) {
function min(a, b) {
a = parseValue(a);
b = parseValue(b);
return a.lesser(b) ? a : b;
@@ -1032,16 +1037,32 @@ var bigInt = (function (undefined) {
return low.add(typeof result === "number" ? new SmallInteger(result) : new BigInteger(result, false));
}
var parseBase = function (text, base) {
var val = Integer[0], pow = Integer[1],
length = text.length;
var length = text.length;
var i;
var absBase = Math.abs(base);
for(var i = 0; i < length; i++) {
var c = text[i].toLowerCase();
if(c === "-") continue;
if(/[a-z0-9]/.test(c)) {
if(/[0-9]/.test(c) && +c >= absBase) {
if(c === "1" && absBase === 1) continue;
throw new Error(c + " is not a valid digit in base " + base + ".");
} else if(c.charCodeAt(0) - 87 >= absBase) {
throw new Error(c + " is not a valid digit in base " + base + ".");
}
}
}
if (2 <= base && base <= 36) {
if (length <= LOG_MAX_INT / Math.log(base)) {
var result = parseInt(text, base);
if(isNaN(result)) {
throw new Error(c + " is not a valid digit in base " + base + ".");
}
return new SmallInteger(parseInt(text, base));
}
}
base = parseValue(base);
var digits = [];
var i;
var isNegative = text[0] === "-";
for (i = isNegative ? 1 : 0; i < text.length; i++) {
var c = text[i].toLowerCase(),
@@ -1055,13 +1076,17 @@ var bigInt = (function (undefined) {
}
else throw new Error(c + " is not a valid character");
}
digits.reverse();
for (i = 0; i < digits.length; i++) {
return parseBaseFromArray(digits, base, isNegative);
};
function parseBaseFromArray(digits, base, isNegative) {
var val = Integer[0], pow = Integer[1], i;
for (i = digits.length - 1; i >= 0; i--) {
val = val.add(digits[i].times(pow));
pow = pow.times(base);
}
return isNegative ? val.negate() : val;
};
}
function stringify(digit) {
var v = digit.value;
@@ -1118,11 +1143,13 @@ var bigInt = (function (undefined) {
var sign = this.sign ? "-" : "";
return sign + str;
};
SmallInteger.prototype.toString = function (radix) {
if (radix === undefined) radix = 10;
if (radix != 10) return toBase(this, radix);
return String(this.value);
};
BigInteger.prototype.toJSON = SmallInteger.prototype.toJSON = function() { return this.toString(); }
BigInteger.prototype.valueOf = function () {
return +this.toString();
@@ -1205,6 +1232,11 @@ var bigInt = (function (undefined) {
Integer.lcm = lcm;
Integer.isInstance = function (x) { return x instanceof BigInteger || x instanceof SmallInteger; };
Integer.randBetween = randBetween;
Integer.fromArray = function (digits, base, isNegative) {
return parseBaseFromArray(digits.map(parseValue), parseValue(base || 10), isNegative);
};
return Integer;
})();
@@ -1212,3 +1244,10 @@ var bigInt = (function (undefined) {
if (typeof module !== "undefined" && module.hasOwnProperty("exports")) {
module.exports = bigInt;
}
//amd check
if ( typeof define === "function" && define.amd ) {
define( "big-integer", [], function() {
return bigInt;
});
}

File diff suppressed because one or more lines are too long

11
node_modules/big-integer/README.md generated vendored
View File

@@ -211,7 +211,7 @@ Returns `true` if the number is prime, `false` otherwise.
#### `isProbablePrime([iterations])`
Returns `true` if the number is very likely to be positive, `false` otherwise.
Returns `true` if the number is very likely to be prime, `false` otherwise.
Argument is optional and determines the amount of iterations of the test (default: `5`). The more iterations, the lower chance of getting a false positive.
This uses the [Fermat primality test](https://en.wikipedia.org/wiki/Fermat_primality_test).
@@ -421,6 +421,13 @@ Performs the bitwise XOR operation. The operands are treated as if they were rep
### Static Methods
#### `fromArray(digits, base = 10, isNegative?)`
Constructs a bigInt from an array of digits in base `base`. The optional `isNegative` flag will make the number negative.
- `bigInt.fromArray([1, 2, 3, 4, 5], 10)` => `12345`
- `bigInt.fromArray([1, 0, 0], 2, true)` => `-4`
#### `gcd(a, b)`
Finds the greatest common denominator of `a` and `b`.
@@ -510,4 +517,4 @@ There are performance benchmarks that can be viewed from the `benchmarks/index.h
## License
This project is public domain. For more details, read about the [Unlicense](http://unlicense.org/).
This project is public domain. For more details, read about the [Unlicense](http://unlicense.org/).

View File

@@ -24,7 +24,6 @@
"bower_components",
"test",
"coverage",
"spec",
"tests"
]
}

View File

@@ -10,23 +10,23 @@
"spec": ">=1.6.7 <2.0.0",
"type": "range"
},
"/Users/steveng/repo/cordova/cordova-android/node_modules/bplist-parser"
"/Users/jbowser/cordova/cordova-android/node_modules/bplist-parser"
]
],
"_from": "big-integer@>=1.6.7 <2.0.0",
"_id": "big-integer@1.6.17",
"_id": "big-integer@1.6.25",
"_inCache": true,
"_location": "/big-integer",
"_nodeVersion": "4.4.5",
"_nodeVersion": "6.10.3",
"_npmOperationalInternal": {
"host": "packages-12-west.internal.npmjs.com",
"tmp": "tmp/big-integer-1.6.17.tgz_1478721202721_0.8068355675786734"
"host": "s3://npm-registry-packages",
"tmp": "tmp/big-integer-1.6.25.tgz_1504748727289_0.9231066561769694"
},
"_npmUser": {
"name": "peterolson",
"email": "peter.e.c.olson+npm@gmail.com"
},
"_npmVersion": "2.15.5",
"_npmVersion": "3.10.10",
"_phantomChildren": {},
"_requested": {
"raw": "big-integer@^1.6.7",
@@ -40,11 +40,11 @@
"_requiredBy": [
"/bplist-parser"
],
"_resolved": "http://registry.npmjs.org/big-integer/-/big-integer-1.6.17.tgz",
"_shasum": "f0dcf5109a949e42a993ee3e8fb2070452817b51",
"_resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.25.tgz",
"_shasum": "1de45a9f57542ac20121c682f8d642220a34e823",
"_shrinkwrap": null,
"_spec": "big-integer@^1.6.7",
"_where": "/Users/steveng/repo/cordova/cordova-android/node_modules/bplist-parser",
"_where": "/Users/jbowser/cordova/cordova-android/node_modules/bplist-parser",
"author": {
"name": "Peter Olson",
"email": "peter.e.c.olson+npm@gmail.com"
@@ -57,23 +57,28 @@
"dependencies": {},
"description": "An arbitrary length integer library for Javascript",
"devDependencies": {
"@types/lodash": "^4.14.64",
"@types/node": "^7.0.22",
"coveralls": "^2.11.4",
"jasmine": "2.1.x",
"jasmine-core": "^2.3.4",
"karma": "^0.13.3",
"karma-coverage": "^0.4.2",
"karma-jasmine": "^0.3.6",
"karma-phantomjs-launcher": "~0.1"
"karma-phantomjs-launcher": "^1.0.4",
"lodash": "^4.17.4",
"typescript": "^2.3.3",
"uglifyjs": "^2.4.10"
},
"directories": {},
"dist": {
"shasum": "f0dcf5109a949e42a993ee3e8fb2070452817b51",
"tarball": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.17.tgz"
"shasum": "1de45a9f57542ac20121c682f8d642220a34e823",
"tarball": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.25.tgz"
},
"engines": {
"node": ">=0.6"
},
"gitHead": "d25d0bfcd96f31001ec8572c8d01de4770d99e63",
"gitHead": "a0c10d68aae8f5df56a67b3e3eb353b428abf170",
"homepage": "https://github.com/peterolson/BigInteger.js#readme",
"keywords": [
"math",
@@ -102,7 +107,9 @@
"url": "git+ssh://git@github.com/peterolson/BigInteger.js.git"
},
"scripts": {
"test": "karma start my.conf.js"
"minify": "uglifyjs BigInteger.js -o BigInteger.min.js",
"test": "tsc && node_modules/.bin/karma start my.conf.js && node spec/tsDefinitions.js"
},
"version": "1.6.17"
"typings": "./BigInteger.d.ts",
"version": "1.6.25"
}

25
node_modules/big-integer/tsconfig.json generated vendored Normal file
View File

@@ -0,0 +1,25 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": false,
"baseUrl": "./",
"moduleResolution": "node",
"allowJs": true,
"typeRoots": [
"./"
],
"types": [
"node"
],
"forceConsistentCasingInFileNames": true
},
"files": [
"BigInteger.d.ts",
"spec/tsDefinitions.ts"
]
}

View File

@@ -10,7 +10,7 @@
"spec": ">=0.1.0 <0.2.0",
"type": "range"
},
"/Users/steveng/repo/cordova/cordova-android/node_modules/cordova-common"
"/Users/jbowser/cordova/cordova-android/node_modules/cordova-common"
]
],
"_from": "bplist-parser@>=0.1.0 <0.2.0",
@@ -36,11 +36,11 @@
"_requiredBy": [
"/cordova-common"
],
"_resolved": "http://registry.npmjs.org/bplist-parser/-/bplist-parser-0.1.1.tgz",
"_resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.1.1.tgz",
"_shasum": "d60d5dcc20cba6dc7e1f299b35d3e1f95dafbae6",
"_shrinkwrap": null,
"_spec": "bplist-parser@^0.1.0",
"_where": "/Users/steveng/repo/cordova/cordova-android/node_modules/cordova-common",
"_where": "/Users/jbowser/cordova/cordova-android/node_modules/cordova-common",
"author": {
"name": "Joe Ferner",
"email": "joe.ferner@nearinfinity.com"

View File

@@ -5,6 +5,7 @@ as known from sh/bash, in JavaScript.
[![build status](https://secure.travis-ci.org/juliangruber/brace-expansion.svg)](http://travis-ci.org/juliangruber/brace-expansion)
[![downloads](https://img.shields.io/npm/dm/brace-expansion.svg)](https://www.npmjs.org/package/brace-expansion)
[![Greenkeeper badge](https://badges.greenkeeper.io/juliangruber/brace-expansion.svg)](https://greenkeeper.io/)
[![testling badge](https://ci.testling.com/juliangruber/brace-expansion.png)](https://ci.testling.com/juliangruber/brace-expansion)

View File

@@ -106,7 +106,7 @@ function expand(str, isTop) {
var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
var isSequence = isNumericSequence || isAlphaSequence;
var isOptions = /^(.*,)+(.+)?$/.test(m.body);
var isOptions = m.body.indexOf(',') >= 0;
if (!isSequence && !isOptions) {
// {a},b}
if (m.post.match(/,.*\}/)) {

View File

@@ -2,49 +2,49 @@
"_args": [
[
{
"raw": "brace-expansion@^1.0.0",
"raw": "brace-expansion@^1.1.7",
"scope": null,
"escapedName": "brace-expansion",
"name": "brace-expansion",
"rawSpec": "^1.0.0",
"spec": ">=1.0.0 <2.0.0",
"rawSpec": "^1.1.7",
"spec": ">=1.1.7 <2.0.0",
"type": "range"
},
"/Users/steveng/repo/cordova/cordova-android/node_modules/minimatch"
"/Users/jbowser/cordova/cordova-android/node_modules/minimatch"
]
],
"_from": "brace-expansion@>=1.0.0 <2.0.0",
"_id": "brace-expansion@1.1.6",
"_from": "brace-expansion@>=1.1.7 <2.0.0",
"_id": "brace-expansion@1.1.8",
"_inCache": true,
"_location": "/brace-expansion",
"_nodeVersion": "4.4.7",
"_nodeVersion": "7.8.0",
"_npmOperationalInternal": {
"host": "packages-16-east.internal.npmjs.com",
"tmp": "tmp/brace-expansion-1.1.6.tgz_1469047715600_0.9362958471756428"
"host": "s3://npm-registry-packages",
"tmp": "tmp/brace-expansion-1.1.8.tgz_1497251980593_0.6575565172825009"
},
"_npmUser": {
"name": "juliangruber",
"email": "julian@juliangruber.com"
},
"_npmVersion": "2.15.8",
"_npmVersion": "4.2.0",
"_phantomChildren": {},
"_requested": {
"raw": "brace-expansion@^1.0.0",
"raw": "brace-expansion@^1.1.7",
"scope": null,
"escapedName": "brace-expansion",
"name": "brace-expansion",
"rawSpec": "^1.0.0",
"spec": ">=1.0.0 <2.0.0",
"rawSpec": "^1.1.7",
"spec": ">=1.1.7 <2.0.0",
"type": "range"
},
"_requiredBy": [
"/minimatch"
],
"_resolved": "http://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz",
"_shasum": "7197d7eaa9b87e648390ea61fc66c84427420df9",
"_resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
"_shasum": "c07b211c7c952ec1f8efd51a77ef0d1d3990a292",
"_shrinkwrap": null,
"_spec": "brace-expansion@^1.0.0",
"_where": "/Users/steveng/repo/cordova/cordova-android/node_modules/minimatch",
"_spec": "brace-expansion@^1.1.7",
"_where": "/Users/jbowser/cordova/cordova-android/node_modules/minimatch",
"author": {
"name": "Julian Gruber",
"email": "mail@juliangruber.com",
@@ -54,19 +54,20 @@
"url": "https://github.com/juliangruber/brace-expansion/issues"
},
"dependencies": {
"balanced-match": "^0.4.1",
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
},
"description": "Brace expansion as known from sh/bash",
"devDependencies": {
"matcha": "^0.7.0",
"tape": "^4.6.0"
},
"directories": {},
"dist": {
"shasum": "7197d7eaa9b87e648390ea61fc66c84427420df9",
"tarball": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz"
"shasum": "c07b211c7c952ec1f8efd51a77ef0d1d3990a292",
"tarball": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz"
},
"gitHead": "791262fa06625e9c5594cde529a21d82086af5f2",
"gitHead": "8f59e68bd5c915a0d624e8e39354e1ccf672edf6",
"homepage": "https://github.com/juliangruber/brace-expansion",
"keywords": [],
"license": "MIT",
@@ -89,6 +90,7 @@
"url": "git://github.com/juliangruber/brace-expansion.git"
},
"scripts": {
"bench": "matcha test/perf/bench.js",
"gentest": "bash test/generate.sh",
"test": "tape test/*.js"
},
@@ -108,5 +110,5 @@
"android-browser/4.2..latest"
]
},
"version": "1.1.6"
"version": "1.1.8"
}

View File

@@ -10,7 +10,7 @@
"spec": "0.0.1",
"type": "version"
},
"/Users/steveng/repo/cordova/cordova-android/node_modules/brace-expansion"
"/Users/jbowser/cordova/cordova-android/node_modules/brace-expansion"
]
],
"_from": "concat-map@0.0.1",
@@ -35,11 +35,11 @@
"_requiredBy": [
"/brace-expansion"
],
"_resolved": "http://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"_resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"_shasum": "d8a96bd77fd68df7793a73036a3ba0d5405d477b",
"_shrinkwrap": null,
"_spec": "concat-map@0.0.1",
"_where": "/Users/steveng/repo/cordova/cordova-android/node_modules/brace-expansion",
"_where": "/Users/jbowser/cordova/cordova-android/node_modules/brace-expansion",
"author": {
"name": "James Halliday",
"email": "mail@substack.net",

1
node_modules/cordova-common/.eslintignore generated vendored Normal file
View File

@@ -0,0 +1 @@
spec/fixtures/*

11
node_modules/cordova-common/.eslintrc.yml generated vendored Normal file
View File

@@ -0,0 +1,11 @@
root: true
extends: semistandard
rules:
indent:
- error
- 4
camelcase: off
padded-blocks: off
operator-linebreak: off
no-throw-literal: off

View File

@@ -1 +0,0 @@
spec/fixtures/*

View File

@@ -1,3 +1,4 @@
fixtures
coverage
jasmine.json
appveyor.yml

15
node_modules/cordova-common/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,15 @@
language: node_js
sudo: false
git:
depth: 10
node_js:
- "4"
- "6"
install:
- npm install
- npm install -g codecov
script:
- npm test
- npm run cover
after_script:
- codecov

View File

@@ -19,6 +19,10 @@
#
-->
[![Build status](https://ci.appveyor.com/api/projects/status/wxkmo0jalsr8gane?svg=true)](https://ci.appveyor.com/project/ApacheSoftwareFoundation/cordova-common/branch/master)
[![Build Status](https://travis-ci.org/apache/cordova-common.svg?branch=master)](https://travis-ci.org/apache/cordova-common)
[![NPM](https://nodei.co/npm/cordova-common.png)](https://nodei.co/npm/cordova-common/)
# cordova-common
Expoeses shared functionality used by [cordova-lib](https://github.com/apache/cordova-lib/) and Cordova platforms.
## Exposed APIs

View File

@@ -20,6 +20,28 @@
-->
# Cordova-common Release Notes
### 2.1.0 (August 30, 2017)
* [CB-13145](https://issues.apache.org/jira/browse/CB-13145) added variable replacing to `framework` tag
* [CB-13211](https://issues.apache.org/jira/browse/CB-13211) Add `allows-arbitrary-loads-for-media` attribute parsing for `getAccesses`
* [CB-11968](https://issues.apache.org/jira/browse/CB-11968) Added support for `<config-file>` in `config.xml`
* [CB-12895](https://issues.apache.org/jira/browse/CB-12895) set up `eslint` and removed `jshint`
* [CB-12785](https://issues.apache.org/jira/browse/CB-12785) added `.gitignore`, `travis`, and `appveyor` support
* [CB-12250](https://issues.apache.org/jira/browse/CB-12250) & [CB-12409](https://issues.apache.org/jira/browse/CB-12409) *iOS*: Fix bug with escaping properties from `plist` file
* [CB-12762](https://issues.apache.org/jira/browse/CB-12762) updated `common`, `fetch`, and `serve` `pkgJson` to point `pkgJson` repo items to github mirrors
* [CB-12766](https://issues.apache.org/jira/browse/CB-12766) Consistently write `JSON` with 2 spaces indentation
### 2.0.3 (May 02, 2017)
* [CB-8978](https://issues.apache.org/jira/browse/CB-8978) Add option to get `resource-file` from `root`
* [CB-11908](https://issues.apache.org/jira/browse/CB-11908) Add tests for `edit-config` in `config.xml`
* [CB-12665](https://issues.apache.org/jira/browse/CB-12665) removed `enginestrict` since it is deprecated
### 2.0.2 (Apr 14, 2017)
* [CB-11233](https://issues.apache.org/jira/browse/CB-11233) - Support installing frameworks into 'Embedded Binaries' section of the Xcode project
* [CB-10438](https://issues.apache.org/jira/browse/CB-10438) - Install correct dependency version. Removed shell.remove, added pkg.json to dependency tests 1-3, and updated install.js (.replace) to fix tests in uninstall.spec.js and update to workw with jasmine 2.0
* [CB-11120](https://issues.apache.org/jira/browse/CB-11120) - Allow short/display name in config.xml
* [CB-11346](https://issues.apache.org/jira/browse/CB-11346) - Remove known platforms check
* [CB-11977](https://issues.apache.org/jira/browse/CB-11977) - updated engines and enginescript for common, fetch, and serve
### 2.0.1 (Mar 09, 2017)
* [CB-12557](https://issues.apache.org/jira/browse/CB-12557) add both stdout and stderr properties to the error object passed to superspawn reject handler.

18
node_modules/cordova-common/appveyor.yml generated vendored Normal file
View File

@@ -0,0 +1,18 @@
# appveyor file
# http://www.appveyor.com/docs/appveyor-yml
environment:
matrix:
- nodejs_version: "4"
- nodejs_version: "6"
install:
- ps: Install-Product node $env:nodejs_version
- npm install
build: off
test_script:
- node --version
- npm --version
- npm test

View File

@@ -2,49 +2,49 @@
"_args": [
[
{
"raw": "cordova-common@^2.0.1",
"raw": "cordova-common@^2.1.0",
"scope": null,
"escapedName": "cordova-common",
"name": "cordova-common",
"rawSpec": "^2.0.1",
"spec": ">=2.0.1 <3.0.0",
"rawSpec": "^2.1.0",
"spec": ">=2.1.0 <3.0.0",
"type": "range"
},
"/Users/steveng/repo/cordova/cordova-android"
"/Users/jbowser/cordova/cordova-android"
]
],
"_from": "cordova-common@>=2.0.1 <3.0.0",
"_id": "cordova-common@2.0.1",
"_from": "cordova-common@>=2.1.0 <3.0.0",
"_id": "cordova-common@2.1.0",
"_inCache": true,
"_location": "/cordova-common",
"_nodeVersion": "6.9.4",
"_nodeVersion": "6.6.0",
"_npmOperationalInternal": {
"host": "packages-18-east.internal.npmjs.com",
"tmp": "tmp/cordova-common-2.0.1.tgz_1489432932737_0.5238456283695996"
"host": "s3://npm-registry-packages",
"tmp": "tmp/cordova-common-2.1.0.tgz_1504306462859_0.04064799007028341"
},
"_npmUser": {
"name": "filmaj",
"email": "maj.fil@gmail.com"
"name": "audreyso",
"email": "audreyeso@gmail.com"
},
"_npmVersion": "3.10.10",
"_npmVersion": "4.6.1",
"_phantomChildren": {},
"_requested": {
"raw": "cordova-common@^2.0.1",
"raw": "cordova-common@^2.1.0",
"scope": null,
"escapedName": "cordova-common",
"name": "cordova-common",
"rawSpec": "^2.0.1",
"spec": ">=2.0.1 <3.0.0",
"rawSpec": "^2.1.0",
"spec": ">=2.1.0 <3.0.0",
"type": "range"
},
"_requiredBy": [
"/"
],
"_resolved": "http://registry.npmjs.org/cordova-common/-/cordova-common-2.0.1.tgz",
"_shasum": "99af318d7cb8988047cfe37bb9f25ea881d52815",
"_resolved": "https://registry.npmjs.org/cordova-common/-/cordova-common-2.1.0.tgz",
"_shasum": "bb357ee1b9825031ed9db3c56b592efe973d1640",
"_shrinkwrap": null,
"_spec": "cordova-common@^2.0.1",
"_where": "/Users/steveng/repo/cordova/cordova-android",
"_spec": "cordova-common@^2.1.0",
"_where": "/Users/jbowser/cordova/cordova-android",
"author": {
"name": "Apache Software Foundation"
},
@@ -57,7 +57,7 @@
"ansi": "^0.3.1",
"bplist-parser": "^0.1.0",
"cordova-registry-mapper": "^1.1.8",
"elementtree": "^0.1.6",
"elementtree": "0.1.6",
"glob": "^5.0.13",
"minimatch": "^3.0.0",
"osenv": "^0.1.3",
@@ -70,50 +70,65 @@
},
"description": "Apache Cordova tools and platforms shared routines",
"devDependencies": {
"eslint": "^4.0.0",
"eslint-config-semistandard": "^11.0.0",
"eslint-config-standard": "^10.2.1",
"eslint-plugin-import": "^2.3.0",
"eslint-plugin-node": "^5.0.0",
"eslint-plugin-promise": "^3.5.0",
"eslint-plugin-standard": "^3.0.1",
"istanbul": "^0.4.5",
"jasmine": "^2.5.2",
"jshint": "^2.8.0",
"promise-matchers": "^0.9.6",
"rewire": "^2.5.1"
},
"directories": {},
"dist": {
"shasum": "99af318d7cb8988047cfe37bb9f25ea881d52815",
"tarball": "https://registry.npmjs.org/cordova-common/-/cordova-common-2.0.1.tgz"
"shasum": "bb357ee1b9825031ed9db3c56b592efe973d1640",
"tarball": "https://registry.npmjs.org/cordova-common/-/cordova-common-2.1.0.tgz"
},
"engines": {
"node": ">=0.9.9"
"node": ">=4.0.0"
},
"homepage": "https://github.com/apache/cordova-lib#readme",
"license": "Apache-2.0",
"main": "cordova-common.js",
"maintainers": [
{
"name": "bowserj",
"email": "bowserj@apache.org"
"name": "audreyso",
"email": "audreyeso@gmail.com"
},
{
"name": "apachebuilds",
"email": "root@apache.org"
},
{
"name": "filmaj",
"email": "maj.fil@gmail.com"
},
{
"name": "kotikov.vladimir",
"email": "kotikov.vladimir@gmail.com"
},
{
"name": "purplecabbage",
"email": "purplecabbage@gmail.com"
"name": "timbarham",
"email": "npmjs@barhams.info"
},
{
"name": "shazron",
"email": "shazron@gmail.com"
},
{
"name": "bowserj",
"email": "bowserj@apache.org"
},
{
"name": "purplecabbage",
"email": "purplecabbage@gmail.com"
},
{
"name": "stevegill",
"email": "stevengill97@gmail.com"
},
{
"name": "timbarham",
"email": "npmjs@barhams.info"
"name": "kotikov.vladimir",
"email": "kotikov.vladimir@gmail.com"
}
],
"name": "cordova-common",
@@ -121,13 +136,13 @@
"readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git://git-wip-us.apache.org/repos/asf/cordova-common.git"
"url": "git+https://github.com/apache/cordova-lib.git"
},
"scripts": {
"cover": "istanbul cover --root src --print detail jasmine",
"jasmine": "jasmine --captureExceptions --color",
"jshint": "jshint src && jshint spec",
"test": "npm run jshint && npm run jasmine"
"eslint": "eslint src && eslint spec",
"jasmine": "jasmine JASMINE_CONFIG_PATH=spec/support/jasmine.json",
"test": "npm run eslint && npm run jasmine"
},
"version": "2.0.1"
"version": "2.1.0"
}

View File

@@ -1,10 +0,0 @@
{
"node": true
, "bitwise": true
, "undef": true
, "trailing": true
, "quotmark": true
, "indent": 4
, "unused": "vars"
, "latedef": "nofunc"
}

View File

@@ -19,32 +19,32 @@
/* jshint quotmark:false */
var events = require('./events'),
Q = require('q');
var events = require('./events');
var Q = require('q');
function ActionStack() {
function ActionStack () {
this.stack = [];
this.completed = [];
}
ActionStack.prototype = {
createAction:function(handler, action_params, reverter, revert_params) {
createAction: function (handler, action_params, reverter, revert_params) {
return {
handler:{
run:handler,
params:action_params
handler: {
run: handler,
params: action_params
},
reverter:{
run:reverter,
params:revert_params
reverter: {
run: reverter,
params: revert_params
}
};
},
push:function(tx) {
push: function (tx) {
this.stack.push(tx);
},
// Returns a promise.
process:function(platform) {
process: function (platform) {
events.emit('verbose', 'Beginning processing of action stack for ' + platform + ' project...');
while (this.stack.length) {
@@ -54,19 +54,19 @@ ActionStack.prototype = {
try {
handler.apply(null, action_params);
} catch(e) {
} catch (e) {
events.emit('warn', 'Error during processing of action! Attempting to revert...');
this.stack.unshift(action);
var issue = 'Uh oh!\n';
// revert completed tasks
while(this.completed.length) {
while (this.completed.length) {
var undo = this.completed.shift();
var revert = undo.reverter.run;
var revert_params = undo.reverter.params;
try {
revert.apply(null, revert_params);
} catch(err) {
} catch (err) {
events.emit('warn', 'Error during reversion of action! We probably really messed up your project now, sorry! D:');
issue += 'A reversion action failed: ' + err.message + '\n';
}

View File

@@ -15,7 +15,7 @@
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
*/
/*
* This module deals with shared configuration / dependency "stuff". That is:
@@ -29,19 +29,17 @@
* reference counts.
*/
/* jshint sub:true */
var path = require('path'),
et = require('elementtree'),
ConfigKeeper = require('./ConfigKeeper'),
CordovaLogger = require('../CordovaLogger');
var path = require('path');
var et = require('elementtree');
var ConfigKeeper = require('./ConfigKeeper');
var CordovaLogger = require('../CordovaLogger');
var mungeutil = require('./munge-util');
var xml_helpers = require('../util/xml-helpers');
exports.PlatformMunger = PlatformMunger;
exports.process = function(plugins_dir, project_dir, platform, platformJson, pluginInfoProvider) {
exports.process = function (plugins_dir, project_dir, platform, platformJson, pluginInfoProvider) {
var munger = new PlatformMunger(platform, project_dir, platformJson, pluginInfoProvider);
munger.process(plugins_dir);
munger.save_all();
@@ -53,7 +51,7 @@ exports.process = function(plugins_dir, project_dir, platform, platformJson, plu
* Can deal with config file of a single project.
* Parsed config files are cached in a ConfigKeeper object.
******************************************************************************/
function PlatformMunger(platform, project_dir, platformJson, pluginInfoProvider) {
function PlatformMunger (platform, project_dir, platformJson, pluginInfoProvider) {
this.platform = platform;
this.project_dir = project_dir;
this.config_keeper = new ConfigKeeper(project_dir);
@@ -63,7 +61,7 @@ function PlatformMunger(platform, project_dir, platformJson, pluginInfoProvider)
// Write out all unsaved files.
PlatformMunger.prototype.save_all = PlatformMunger_save_all;
function PlatformMunger_save_all() {
function PlatformMunger_save_all () {
this.config_keeper.save_all();
this.platformJson.save();
}
@@ -71,7 +69,7 @@ function PlatformMunger_save_all() {
// Apply a munge object to a single config file.
// The remove parameter tells whether to add the change or remove it.
PlatformMunger.prototype.apply_file_munge = PlatformMunger_apply_file_munge;
function PlatformMunger_apply_file_munge(file, munge, remove) {
function PlatformMunger_apply_file_munge (file, munge, remove) {
var self = this;
for (var selector in munge.parents) {
@@ -86,16 +84,15 @@ function PlatformMunger_apply_file_munge(file, munge, remove) {
}
}
PlatformMunger.prototype.remove_plugin_changes = remove_plugin_changes;
function remove_plugin_changes(pluginInfo, is_top_level) {
function remove_plugin_changes (pluginInfo, is_top_level) {
var self = this;
var platform_config = self.platformJson.root;
var plugin_vars = is_top_level ?
platform_config.installed_plugins[pluginInfo.id] :
platform_config.dependent_plugins[pluginInfo.id];
var edit_config_changes = null;
if(pluginInfo.getEditConfigs) {
if (pluginInfo.getEditConfigs) {
edit_config_changes = pluginInfo.getEditConfigs(self.platform);
}
@@ -114,14 +111,13 @@ function remove_plugin_changes(pluginInfo, is_top_level) {
return self;
}
PlatformMunger.prototype.add_plugin_changes = add_plugin_changes;
function add_plugin_changes(pluginInfo, plugin_vars, is_top_level, should_increment, plugin_force) {
function add_plugin_changes (pluginInfo, plugin_vars, is_top_level, should_increment, plugin_force) {
var self = this;
var platform_config = self.platformJson.root;
var edit_config_changes = null;
if(pluginInfo.getEditConfigs) {
if (pluginInfo.getEditConfigs) {
edit_config_changes = pluginInfo.getEditConfigs(self.platform);
}
@@ -130,8 +126,7 @@ function add_plugin_changes(pluginInfo, plugin_vars, is_top_level, should_increm
if (!edit_config_changes || edit_config_changes.length === 0) {
// get config munge, aka how should this plugin change various config files
config_munge = self.generate_plugin_config_munge(pluginInfo, plugin_vars);
}
else {
} else {
var isConflictingInfo = is_conflicting(edit_config_changes, platform_config.config_munge, self, plugin_force);
if (isConflictingInfo.conflictWithConfigxml) {
@@ -149,13 +144,11 @@ function add_plugin_changes(pluginInfo, plugin_vars, is_top_level, should_increm
// force add new munges
config_munge = self.generate_plugin_config_munge(pluginInfo, plugin_vars, edit_config_changes);
}
else if(isConflictingInfo.conflictFound) {
} else if (isConflictingInfo.conflictFound) {
throw new Error('There was a conflict trying to modify attributes with <edit-config> in plugin ' + pluginInfo.id +
'. The conflicting plugin, ' + isConflictingInfo.conflictingPlugin + ', already modified the same attributes. The conflict must be resolved before ' +
pluginInfo.id + ' can be added. You may use --force to add the plugin and overwrite the conflicting attributes.');
}
else {
} else {
// no conflicts, will handle edit-config
config_munge = self.generate_plugin_config_munge(pluginInfo, plugin_vars, edit_config_changes);
}
@@ -168,27 +161,32 @@ function add_plugin_changes(pluginInfo, plugin_vars, is_top_level, should_increm
return self;
}
// Handle edit-config changes from config.xml
PlatformMunger.prototype.add_config_changes = add_config_changes;
function add_config_changes(config, should_increment) {
function add_config_changes (config, should_increment) {
var self = this;
var platform_config = self.platformJson.root;
var config_munge;
var edit_config_changes = null;
if(config.getEditConfigs) {
edit_config_changes = config.getEditConfigs(self.platform);
var changes = [];
if (config.getEditConfigs) {
var edit_config_changes = config.getEditConfigs(self.platform);
if (edit_config_changes) {
changes = changes.concat(edit_config_changes);
}
}
if (!edit_config_changes || edit_config_changes.length === 0) {
// There are no edit-config changes to add, return here
return self;
if (config.getConfigFiles) {
var config_files_changes = config.getConfigFiles(self.platform);
if (config_files_changes) {
changes = changes.concat(config_files_changes);
}
}
else {
var isConflictingInfo = is_conflicting(edit_config_changes, platform_config.config_munge, self, true /*always force overwrite other edit-config*/);
if(isConflictingInfo.conflictFound) {
if (changes && changes.length > 0) {
var isConflictingInfo = is_conflicting(changes, platform_config.config_munge, self, true /* always force overwrite other edit-config */);
if (isConflictingInfo.conflictFound) {
var conflict_munge;
var conflict_file;
@@ -209,17 +207,17 @@ function add_config_changes(config, should_increment) {
}
}
}
// Add config.xml edit-config munges
config_munge = self.generate_config_xml_munge(config, edit_config_changes, 'config.xml');
}
// Add config.xml edit-config and config-file munges
config_munge = self.generate_config_xml_munge(config, changes, 'config.xml');
self = munge_helper(should_increment, self, platform_config, config_munge);
// Move to installed/dependent_plugins
return self;
}
function munge_helper(should_increment, self, platform_config, config_munge) {
function munge_helper (should_increment, self, platform_config, config_munge) {
// global munge looks at all changes to config files
// TODO: The should_increment param is only used by cordova-cli and is going away soon.
@@ -241,11 +239,10 @@ function munge_helper(should_increment, self, platform_config, config_munge) {
return self;
}
// Load the global munge from platform json and apply all of it.
// Used by cordova prepare to re-generate some config file from platform
// defaults and the global munge.
PlatformMunger.prototype.reapply_global_munge = reapply_global_munge ;
PlatformMunger.prototype.reapply_global_munge = reapply_global_munge;
function reapply_global_munge () {
var self = this;
@@ -261,58 +258,56 @@ function reapply_global_munge () {
// generate_plugin_config_munge
// Generate the munge object from config.xml
PlatformMunger.prototype.generate_config_xml_munge = generate_config_xml_munge;
function generate_config_xml_munge(config, edit_config_changes, type) {
function generate_config_xml_munge (config, config_changes, type) {
var munge = { files: {} };
var changes = edit_config_changes;
var id;
if(!changes) {
if (!config_changes) {
return munge;
}
if (type === 'config.xml') {
id = type;
}
else {
} else {
id = config.id;
}
changes.forEach(function(change) {
change.xmls.forEach(function(xml) {
config_changes.forEach(function (change) {
change.xmls.forEach(function (xml) {
// 1. stringify each xml
var stringified = (new et.ElementTree(xml)).write({xml_declaration:false});
var stringified = (new et.ElementTree(xml)).write({xml_declaration: false});
// 2. add into munge
if (change.mode) {
mungeutil.deep_add(munge, change.file, change.target, { xml: stringified, count: 1, mode: change.mode, id: id });
} else {
mungeutil.deep_add(munge, change.target, change.parent, { xml: stringified, count: 1, after: change.after });
}
});
});
return munge;
}
// generate_plugin_config_munge
// Generate the munge object from plugin.xml + vars
PlatformMunger.prototype.generate_plugin_config_munge = generate_plugin_config_munge;
function generate_plugin_config_munge(pluginInfo, vars, edit_config_changes) {
function generate_plugin_config_munge (pluginInfo, vars, edit_config_changes) {
var self = this;
vars = vars || {};
var munge = { files: {} };
var changes = pluginInfo.getConfigFiles(self.platform);
if(edit_config_changes) {
if (edit_config_changes) {
Array.prototype.push.apply(changes, edit_config_changes);
}
changes.forEach(function(change) {
change.xmls.forEach(function(xml) {
changes.forEach(function (change) {
change.xmls.forEach(function (xml) {
// 1. stringify each xml
var stringified = (new et.ElementTree(xml)).write({xml_declaration:false});
var stringified = (new et.ElementTree(xml)).write({xml_declaration: false});
// interp vars
if (vars) {
Object.keys(vars).forEach(function(key) {
Object.keys(vars).forEach(function (key) {
var regExp = new RegExp('\\$' + key, 'g');
stringified = stringified.replace(regExp, vars[key]);
});
@@ -322,8 +317,7 @@ function generate_plugin_config_munge(pluginInfo, vars, edit_config_changes) {
if (change.mode !== 'remove') {
mungeutil.deep_add(munge, change.file, change.target, { xml: stringified, count: 1, mode: change.mode, plugin: pluginInfo.id });
}
}
else {
} else {
mungeutil.deep_add(munge, change.target, change.parent, { xml: stringified, count: 1, after: change.after });
}
});
@@ -331,7 +325,7 @@ function generate_plugin_config_munge(pluginInfo, vars, edit_config_changes) {
return munge;
}
function is_conflicting(editchanges, config_munge, self, force) {
function is_conflicting (editchanges, config_munge, self, force) {
var files = config_munge.files;
var conflictFound = false;
var conflictWithConfigxml = false;
@@ -340,7 +334,7 @@ function is_conflicting(editchanges, config_munge, self, force) {
var conflictingParent;
var conflictingPlugin;
editchanges.forEach(function(editchange) {
editchanges.forEach(function (editchange) {
if (files[editchange.file]) {
var parents = files[editchange.file].parents;
var target = parents[editchange.target];
@@ -361,8 +355,7 @@ function is_conflicting(editchanges, config_munge, self, force) {
}
}
}
}
else {
} else {
conflictingParent = editchange.target;
}
@@ -374,13 +367,11 @@ function is_conflicting(editchanges, config_munge, self, force) {
if (target[0].id === 'config.xml') {
// Keep track of config.xml/config.xml edit-config conflicts
mungeutil.deep_add(configxmlMunge, editchange.file, conflictingParent, target[0]);
}
else {
} else {
// Keep track of config.xml x plugin.xml edit-config conflicts
mungeutil.deep_add(conflictingMunge, editchange.file, conflictingParent, target[0]);
}
}
else {
} else {
if (target[0].id === 'config.xml') {
// plugin.xml cannot overwrite config.xml changes even if --force is used
conflictWithConfigxml = true;
@@ -390,36 +381,38 @@ function is_conflicting(editchanges, config_munge, self, force) {
if (force) {
// Need to find all conflicts when --force is used, track conflicting munges
mungeutil.deep_add(conflictingMunge, editchange.file, conflictingParent, target[0]);
}
else {
} else {
// plugin cannot overwrite other plugin changes without --force
conflictingPlugin = target[0].plugin;
return;
}
}
}
}
});
return {conflictFound: conflictFound, conflictingPlugin: conflictingPlugin, conflictingMunge: conflictingMunge,
configxmlMunge: configxmlMunge, conflictWithConfigxml:conflictWithConfigxml};
return {conflictFound: conflictFound,
conflictingPlugin: conflictingPlugin,
conflictingMunge: conflictingMunge,
configxmlMunge: configxmlMunge,
conflictWithConfigxml: conflictWithConfigxml};
}
// Go over the prepare queue and apply the config munges for each plugin
// that has been (un)installed.
PlatformMunger.prototype.process = PlatformMunger_process;
function PlatformMunger_process(plugins_dir) {
function PlatformMunger_process (plugins_dir) {
var self = this;
var platform_config = self.platformJson.root;
// Uninstallation first
platform_config.prepare_queue.uninstalled.forEach(function(u) {
platform_config.prepare_queue.uninstalled.forEach(function (u) {
var pluginInfo = self.pluginInfoProvider.get(path.join(plugins_dir, u.plugin));
self.remove_plugin_changes(pluginInfo, u.topLevel);
});
// Now handle installation
platform_config.prepare_queue.installed.forEach(function(u) {
platform_config.prepare_queue.installed.forEach(function (u) {
var pluginInfo = self.pluginInfoProvider.get(path.join(plugins_dir, u.plugin));
self.add_plugin_changes(pluginInfo, u.vars, u.topLevel, true, u.force);
});
@@ -428,4 +421,4 @@ function PlatformMunger_process(plugins_dir) {
platform_config.prepare_queue.uninstalled = [];
platform_config.prepare_queue.installed = [];
}
/**** END of PlatformMunger ****/
/** ** END of PlatformMunger ****/

View File

@@ -14,6 +14,8 @@
*
*/
/* eslint no-control-regex: 0 */
var fs = require('fs');
var path = require('path');
@@ -42,7 +44,7 @@ addProperty(module, 'xml_helpers', '../util/xml-helpers', modules);
* TODO: Consider moving it out to a separate file and maybe partially with
* overrides in platform handlers.
******************************************************************************/
function ConfigFile(project_dir, platform, file_tag) {
function ConfigFile (project_dir, platform, file_tag) {
this.project_dir = project_dir;
this.platform = platform;
this.file_tag = file_tag;
@@ -53,13 +55,13 @@ function ConfigFile(project_dir, platform, file_tag) {
// ConfigFile.load()
ConfigFile.prototype.load = ConfigFile_load;
function ConfigFile_load() {
function ConfigFile_load () {
var self = this;
// config file may be in a place not exactly specified in the target
var filepath = self.filepath = resolveConfigFilePath(self.project_dir, self.platform, self.file_tag);
if ( !filepath || !fs.existsSync(filepath) ) {
if (!filepath || !fs.existsSync(filepath)) {
self.exists = false;
return;
}
@@ -69,7 +71,7 @@ function ConfigFile_load() {
var ext = path.extname(filepath);
// Windows8 uses an appxmanifest, and wp8 will likely use
// the same in a future release
if (ext == '.xml' || ext == '.appxmanifest') {
if (ext === '.xml' || ext === '.appxmanifest') {
self.type = 'xml';
self.data = modules.xml_helpers.parseElementtreeSync(filepath);
} else {
@@ -80,12 +82,12 @@ function ConfigFile_load() {
// Do we still need to support binary plist?
// If yes, use plist.parseStringSync() and read the file once.
self.data = isBinaryPlist(filepath) ?
modules.bplist.parseBuffer(fs.readFileSync(filepath)) :
modules.plist.parse(fs.readFileSync(filepath, 'utf8'));
modules.bplist.parseBuffer(fs.readFileSync(filepath)) :
modules.plist.parse(fs.readFileSync(filepath, 'utf8'));
}
}
ConfigFile.prototype.save = function ConfigFile_save() {
ConfigFile.prototype.save = function ConfigFile_save () {
var self = this;
if (self.type === 'xml') {
fs.writeFileSync(self.filepath, self.data.write({indent: 4}), 'utf-8');
@@ -97,54 +99,54 @@ ConfigFile.prototype.save = function ConfigFile_save() {
self.is_changed = false;
};
ConfigFile.prototype.graft_child = function ConfigFile_graft_child(selector, xml_child) {
ConfigFile.prototype.graft_child = function ConfigFile_graft_child (selector, xml_child) {
var self = this;
var filepath = self.filepath;
var result;
if (self.type === 'xml') {
var xml_to_graft = [modules.et.XML(xml_child.xml)];
switch (xml_child.mode) {
case 'merge':
result = modules.xml_helpers.graftXMLMerge(self.data, xml_to_graft, selector, xml_child);
break;
case 'overwrite':
result = modules.xml_helpers.graftXMLOverwrite(self.data, xml_to_graft, selector, xml_child);
break;
case 'remove':
result= true;
break;
default:
result = modules.xml_helpers.graftXML(self.data, xml_to_graft, selector, xml_child.after);
case 'merge':
result = modules.xml_helpers.graftXMLMerge(self.data, xml_to_graft, selector, xml_child);
break;
case 'overwrite':
result = modules.xml_helpers.graftXMLOverwrite(self.data, xml_to_graft, selector, xml_child);
break;
case 'remove':
result = modules.xml_helpers.pruneXMLRemove(self.data, selector, xml_to_graft);
break;
default:
result = modules.xml_helpers.graftXML(self.data, xml_to_graft, selector, xml_child.after);
}
if ( !result) {
if (!result) {
throw new Error('Unable to graft xml at selector "' + selector + '" from "' + filepath + '" during config install');
}
} else {
// plist file
result = modules.plist_helpers.graftPLIST(self.data, xml_child.xml, selector);
if ( !result ) {
if (!result) {
throw new Error('Unable to graft plist "' + filepath + '" during config install');
}
}
self.is_changed = true;
};
ConfigFile.prototype.prune_child = function ConfigFile_prune_child(selector, xml_child) {
ConfigFile.prototype.prune_child = function ConfigFile_prune_child (selector, xml_child) {
var self = this;
var filepath = self.filepath;
var result;
if (self.type === 'xml') {
var xml_to_graft = [modules.et.XML(xml_child.xml)];
switch (xml_child.mode) {
case 'merge':
case 'overwrite':
result = modules.xml_helpers.pruneXMLRestore(self.data, selector, xml_child);
break;
case 'remove':
result = modules.xml_helpers.prunXMLRemove(self.data, selector, xml_to_graft);
break;
default:
result = modules.xml_helpers.pruneXML(self.data, xml_to_graft, selector);
case 'merge':
case 'overwrite':
result = modules.xml_helpers.pruneXMLRestore(self.data, selector, xml_child);
break;
case 'remove':
result = modules.xml_helpers.pruneXMLRemove(self.data, selector, xml_to_graft);
break;
default:
result = modules.xml_helpers.pruneXML(self.data, xml_to_graft, selector);
}
} else {
// plist file
@@ -160,7 +162,7 @@ ConfigFile.prototype.prune_child = function ConfigFile_prune_child(selector, xml
// Some config-file target attributes are not qualified with a full leading directory, or contain wildcards.
// Resolve to a real path in this function.
// TODO: getIOSProjectname is slow because of glob, try to avoid calling it several times per project.
function resolveConfigFilePath(project_dir, platform, file) {
function resolveConfigFilePath (project_dir, platform, file) {
var filepath = path.join(project_dir, file);
var matches;
@@ -170,10 +172,10 @@ function resolveConfigFilePath(project_dir, platform, file) {
if (matches.length) filepath = matches[0];
// [CB-5989] multiple Info.plist files may exist. default to $PROJECT_NAME-Info.plist
if(matches.length > 1 && file.indexOf('-Info.plist')>-1){
var plistName = getIOSProjectname(project_dir)+'-Info.plist';
for (var i=0; i < matches.length; i++) {
if(matches[i].indexOf(plistName) > -1){
if (matches.length > 1 && file.indexOf('-Info.plist') > -1) {
var plistName = getIOSProjectname(project_dir) + '-Info.plist';
for (var i = 0; i < matches.length; i++) {
if (matches[i].indexOf(plistName) > -1) {
filepath = matches[i];
break;
}
@@ -184,13 +186,13 @@ function resolveConfigFilePath(project_dir, platform, file) {
// special-case config.xml target that is just "config.xml". This should be resolved to the real location of the file.
// TODO: move the logic that contains the locations of config.xml from cordova CLI into plugman.
if (file == 'config.xml') {
if (platform == 'ubuntu') {
if (file === 'config.xml') {
if (platform === 'ubuntu') {
filepath = path.join(project_dir, 'config.xml');
} else if (platform == 'ios') {
} else if (platform === 'ios') {
var iospath = getIOSProjectname(project_dir);
filepath = path.join(project_dir,iospath, 'config.xml');
} else if (platform == 'android') {
filepath = path.join(project_dir, iospath, 'config.xml');
} else if (platform === 'android') {
filepath = path.join(project_dir, 'res', 'xml', 'config.xml');
} else {
matches = modules.glob.sync(path.join(project_dir, '**', 'config.xml'));
@@ -201,8 +203,8 @@ function resolveConfigFilePath(project_dir, platform, file) {
// XXX this checks for android studio projects
// only if none of the options above are satisfied does this get called
if(platform === 'android' && !fs.existsSync(filepath)) {
filepath = path.join(project_dir, 'app', 'src', 'main', 'res', 'xml', 'config.xml');
if (platform === 'android' && !fs.existsSync(filepath)) {
filepath = path.join(project_dir, 'app', 'src', 'main', 'res', 'xml', 'config.xml');
}
// None of the special cases matched, returning project_dir/file.
@@ -211,11 +213,11 @@ function resolveConfigFilePath(project_dir, platform, file) {
// Find out the real name of an iOS project
// TODO: glob is slow, need a better way or caching, or avoid using more than once.
function getIOSProjectname(project_dir) {
function getIOSProjectname (project_dir) {
var matches = modules.glob.sync(path.join(project_dir, '*.xcodeproj'));
var iospath;
if (matches.length === 1) {
iospath = path.basename(matches[0],'.xcodeproj');
iospath = path.basename(matches[0], '.xcodeproj');
} else {
var msg;
if (matches.length === 0) {
@@ -229,7 +231,7 @@ function getIOSProjectname(project_dir) {
}
// determine if a plist file is binary
function isBinaryPlist(filename) {
function isBinaryPlist (filename) {
// I wish there was a synchronous way to read only the first 6 bytes of a
// file. This is wasteful :/
var buf = '' + fs.readFileSync(filename, 'utf8');

View File

@@ -28,18 +28,18 @@ var ConfigFile = require('./ConfigFile');
* project_dir/platform/file
* where file is the name used for the file in config munges.
******************************************************************************/
function ConfigKeeper(project_dir, plugins_dir) {
function ConfigKeeper (project_dir, plugins_dir) {
this.project_dir = project_dir;
this.plugins_dir = plugins_dir;
this._cached = {};
}
ConfigKeeper.prototype.get = function ConfigKeeper_get(project_dir, platform, file) {
ConfigKeeper.prototype.get = function ConfigKeeper_get (project_dir, platform, file) {
var self = this;
// This fixes a bug with older plugins - when specifying config xml instead of res/xml/config.xml
// https://issues.apache.org/jira/browse/CB-6414
if(file == 'config.xml' && platform == 'android'){
if (file === 'config.xml' && platform === 'android') {
file = 'res/xml/config.xml';
}
var fake_path = path.join(project_dir, platform, file);
@@ -53,8 +53,7 @@ ConfigKeeper.prototype.get = function ConfigKeeper_get(project_dir, platform, fi
return config_file;
};
ConfigKeeper.prototype.save_all = function ConfigKeeper_save_all() {
ConfigKeeper.prototype.save_all = function ConfigKeeper_save_all () {
var self = this;
Object.keys(self._cached).forEach(function (fake_path) {
var config_file = self._cached[fake_path];

View File

@@ -19,14 +19,14 @@ var _ = require('underscore');
// add the count of [key1][key2]...[keyN] to obj
// return true if it didn't exist before
exports.deep_add = function deep_add(obj, keys /* or key1, key2 .... */ ) {
if ( !Array.isArray(keys) ) {
exports.deep_add = function deep_add (obj, keys /* or key1, key2 .... */) {
if (!Array.isArray(keys)) {
keys = Array.prototype.slice.call(arguments, 1);
}
return exports.process_munge(obj, true/*createParents*/, function (parentArray, k) {
var found = _.find(parentArray, function(element) {
return element.xml == k.xml;
return exports.process_munge(obj, true/* createParents */, function (parentArray, k) {
var found = _.find(parentArray, function (element) {
return element.xml === k.xml;
});
if (found) {
found.after = found.after || k.after;
@@ -40,16 +40,16 @@ exports.deep_add = function deep_add(obj, keys /* or key1, key2 .... */ ) {
// decrement the count of [key1][key2]...[keyN] from obj and remove if it reaches 0
// return true if it was removed or not found
exports.deep_remove = function deep_remove(obj, keys /* or key1, key2 .... */ ) {
if ( !Array.isArray(keys) ) {
exports.deep_remove = function deep_remove (obj, keys /* or key1, key2 .... */) {
if (!Array.isArray(keys)) {
keys = Array.prototype.slice.call(arguments, 1);
}
var result = exports.process_munge(obj, false/*createParents*/, function (parentArray, k) {
var result = exports.process_munge(obj, false/* createParents */, function (parentArray, k) {
var index = -1;
var found = _.find(parentArray, function (element) {
index++;
return element.xml == k.xml;
return element.xml === k.xml;
});
if (found) {
if (parentArray[index].oldAttrib) {
@@ -58,8 +58,7 @@ exports.deep_remove = function deep_remove(obj, keys /* or key1, key2 .... */ )
found.count -= k.count;
if (found.count > 0) {
return false;
}
else {
} else {
parentArray.splice(index, 1);
}
}
@@ -71,14 +70,14 @@ exports.deep_remove = function deep_remove(obj, keys /* or key1, key2 .... */ )
// search for [key1][key2]...[keyN]
// return the object or undefined if not found
exports.deep_find = function deep_find(obj, keys /* or key1, key2 .... */ ) {
if ( !Array.isArray(keys) ) {
exports.deep_find = function deep_find (obj, keys /* or key1, key2 .... */) {
if (!Array.isArray(keys)) {
keys = Array.prototype.slice.call(arguments, 1);
}
return exports.process_munge(obj, false/*createParents?*/, function (parentArray, k) {
return exports.process_munge(obj, false/* createParents? */, function (parentArray, k) {
return _.find(parentArray, function (element) {
return element.xml == (k.xml || k);
return element.xml === (k.xml || k);
});
}, keys);
};
@@ -87,20 +86,20 @@ exports.deep_find = function deep_find(obj, keys /* or key1, key2 .... */ ) {
// When createParents is true, add the file and parent items they are missing
// When createParents is false, stop and return undefined if the file and/or parent items are missing
exports.process_munge = function process_munge(obj, createParents, func, keys /* or key1, key2 .... */ ) {
if ( !Array.isArray(keys) ) {
exports.process_munge = function process_munge (obj, createParents, func, keys /* or key1, key2 .... */) {
if (!Array.isArray(keys)) {
keys = Array.prototype.slice.call(arguments, 1);
}
var k = keys[0];
if (keys.length == 1) {
if (keys.length === 1) {
return func(obj, k);
} else if (keys.length == 2) {
} else if (keys.length === 2) {
if (!obj.parents[k] && !createParents) {
return undefined;
}
obj.parents[k] = obj.parents[k] || [];
return exports.process_munge(obj.parents[k], createParents, func, keys.slice(1));
} else if (keys.length == 3){
} else if (keys.length === 3) {
if (!obj.files[k] && !createParents) {
return undefined;
}
@@ -115,7 +114,7 @@ exports.process_munge = function process_munge(obj, createParents, func, keys /*
// base[file][selector][child] += munge[file][selector][child]
// Returns a munge object containing values that exist in munge
// but not in base.
exports.increment_munge = function increment_munge(base, munge) {
exports.increment_munge = function increment_munge (base, munge) {
var diff = { files: {} };
for (var file in munge.files) {
@@ -138,7 +137,7 @@ exports.increment_munge = function increment_munge(base, munge) {
// base[file][selector][child] -= munge[file][selector][child]
// nodes that reached zero value are removed from base and added to the returned munge
// object.
exports.decrement_munge = function decrement_munge(base, munge) {
exports.decrement_munge = function decrement_munge (base, munge) {
var zeroed = { files: {} };
for (var file in munge.files) {
@@ -158,6 +157,6 @@ exports.decrement_munge = function decrement_munge(base, munge) {
};
// For better readability where used
exports.clone_munge = function clone_munge(munge) {
exports.clone_munge = function clone_munge (munge) {
return exports.increment_munge({}, munge);
};

View File

@@ -17,24 +17,21 @@
under the License.
*/
/* jshint sub:true */
var et = require('elementtree'),
xml= require('../util/xml-helpers'),
CordovaError = require('../CordovaError/CordovaError'),
fs = require('fs'),
events = require('../events');
var et = require('elementtree');
var xml = require('../util/xml-helpers');
var CordovaError = require('../CordovaError/CordovaError');
var fs = require('fs');
var events = require('../events');
/** Wraps a config.xml file */
function ConfigParser(path) {
function ConfigParser (path) {
this.path = path;
try {
this.doc = xml.parseElementtreeSync(path);
this.cdvNamespacePrefix = getCordovaNamespacePrefix(this.doc);
et.register_namespace(this.cdvNamespacePrefix, 'http://cordova.apache.org/ns/1.0');
} catch (e) {
console.error('Parsing '+path+' failed');
console.error('Parsing ' + path + ' failed');
throw e;
}
var r = this.doc.getroot();
@@ -43,11 +40,11 @@ function ConfigParser(path) {
}
}
function getNodeTextSafe(el) {
function getNodeTextSafe (el) {
return el && el.text && el.text.trim();
}
function findOrCreate(doc, name) {
function findOrCreate (doc, name) {
var ret = doc.find(name);
if (!ret) {
ret = new et.Element(name);
@@ -56,12 +53,12 @@ function findOrCreate(doc, name) {
return ret;
}
function getCordovaNamespacePrefix(doc){
function getCordovaNamespacePrefix (doc) {
var rootAtribs = Object.getOwnPropertyNames(doc.getroot().attrib);
var prefix = 'cdv';
for (var j = 0; j < rootAtribs.length; j++ ) {
if(rootAtribs[j].indexOf('xmlns:') === 0 &&
doc.getroot().attrib[rootAtribs[j]] === 'http://cordova.apache.org/ns/1.0'){
for (var j = 0; j < rootAtribs.length; j++) {
if (rootAtribs[j].indexOf('xmlns:') === 0 &&
doc.getroot().attrib[rootAtribs[j]] === 'http://cordova.apache.org/ns/1.0') {
var strings = rootAtribs[j].split(':');
prefix = strings[1];
break;
@@ -76,7 +73,7 @@ function getCordovaNamespacePrefix(doc){
* @param {Array} elems An array of ElementTree nodes
* @return {String}
*/
function findElementAttributeValue(attributeName, elems) {
function findElementAttributeValue (attributeName, elems) {
elems = Array.isArray(elems) ? elems : [ elems ];
@@ -86,59 +83,69 @@ function findElementAttributeValue(attributeName, elems) {
return filteredElems.attrib.value;
}).pop();
return value ? value : '';
return value || '';
}
ConfigParser.prototype = {
getAttribute: function(attr) {
getAttribute: function (attr) {
return this.doc.getroot().attrib[attr];
},
packageName: function(id) {
packageName: function (id) {
return this.getAttribute('id');
},
setPackageName: function(id) {
setPackageName: function (id) {
this.doc.getroot().attrib['id'] = id;
},
android_packageName: function() {
android_packageName: function () {
return this.getAttribute('android-packageName');
},
android_activityName: function() {
android_activityName: function () {
return this.getAttribute('android-activityName');
},
ios_CFBundleIdentifier: function() {
ios_CFBundleIdentifier: function () {
return this.getAttribute('ios-CFBundleIdentifier');
},
name: function() {
name: function () {
return getNodeTextSafe(this.doc.find('name'));
},
setName: function(name) {
setName: function (name) {
var el = findOrCreate(this.doc, 'name');
el.text = name;
},
description: function() {
shortName: function () {
return this.doc.find('name').attrib['short'] || this.name();
},
setShortName: function (shortname) {
var el = findOrCreate(this.doc, 'name');
if (!el.text) {
el.text = shortname;
}
el.attrib['short'] = shortname;
},
description: function () {
return getNodeTextSafe(this.doc.find('description'));
},
setDescription: function(text) {
setDescription: function (text) {
var el = findOrCreate(this.doc, 'description');
el.text = text;
},
version: function() {
version: function () {
return this.getAttribute('version');
},
windows_packageVersion: function() {
windows_packageVersion: function () {
return this.getAttribute('windows-packageVersion');
},
android_versionCode: function() {
android_versionCode: function () {
return this.getAttribute('android-versionCode');
},
ios_CFBundleVersion: function() {
ios_CFBundleVersion: function () {
return this.getAttribute('ios-CFBundleVersion');
},
setVersion: function(value) {
setVersion: function (value) {
this.doc.getroot().attrib['version'] = value;
},
author: function() {
author: function () {
return getNodeTextSafe(this.doc.find('author'));
},
getGlobalPreference: function (name) {
@@ -156,7 +163,7 @@ ConfigParser.prototype = {
getPlatformPreference: function (name, platform) {
return findElementAttributeValue(name, this.doc.findall('platform[@name=\'' + platform + '\']/preference'));
},
getPreference: function(name, platform) {
getPreference: function (name, platform) {
var platformPreference = '';
@@ -164,7 +171,7 @@ ConfigParser.prototype = {
platformPreference = this.getPlatformPreference(name, platform);
}
return platformPreference ? platformPreference : this.getGlobalPreference(name);
return platformPreference || this.getGlobalPreference(name);
},
/**
@@ -174,11 +181,11 @@ ConfigParser.prototype = {
* "icon" and "splash" currently supported.
* @return {Array} Resources for the platform specified.
*/
getStaticResources: function(platform, resourceName) {
var ret = [],
staticResources = [];
getStaticResources: function (platform, resourceName) {
var ret = [];
var staticResources = [];
if (platform) { // platform specific icons
this.doc.findall('platform[@name=\'' + platform + '\']/' + resourceName).forEach(function(elt){
this.doc.findall('platform[@name=\'' + platform + '\']/' + resourceName).forEach(function (elt) {
elt.platform = platform; // mark as platform specific resource
staticResources.push(elt);
});
@@ -191,7 +198,7 @@ ConfigParser.prototype = {
var res = {};
res.src = elt.attrib.src;
res.target = elt.attrib.target || undefined;
res.density = elt.attrib['density'] || elt.attrib[that.cdvNamespacePrefix+':density'] || elt.attrib['gap:density'];
res.density = elt.attrib['density'] || elt.attrib[that.cdvNamespacePrefix + ':density'] || elt.attrib['gap:density'];
res.platform = elt.platform || null; // null means icon represents default icon (shared between platforms)
res.width = +elt.attrib.width || undefined;
res.height = +elt.attrib.height || undefined;
@@ -209,13 +216,13 @@ ConfigParser.prototype = {
* @param {number} height Height of resource.
* @return {Resource} Resource object or null if not found.
*/
ret.getBySize = function(width, height) {
return ret.filter(function(res) {
ret.getBySize = function (width, height) {
return ret.filter(function (res) {
if (!res.width && !res.height) {
return false;
}
return ((!res.width || (width == res.width)) &&
(!res.height || (height == res.height)));
return ((!res.width || (width === res.width)) &&
(!res.height || (height === res.height)));
})[0] || null;
};
@@ -224,14 +231,14 @@ ConfigParser.prototype = {
* @param {string} density Density of resource.
* @return {Resource} Resource object or null if not found.
*/
ret.getByDensity = function(density) {
return ret.filter(function(res) {
return res.density == density;
ret.getByDensity = function (density) {
return ret.filter(function (res) {
return res.density === density;
})[0] || null;
};
/** Returns default icons */
ret.getDefault = function() {
ret.getDefault = function () {
return ret.defaultResource;
};
@@ -243,7 +250,7 @@ ConfigParser.prototype = {
* @param {string} platform Platform name
* @return {Resource[]} Array of icon objects.
*/
getIcons: function(platform) {
getIcons: function (platform) {
return this.getStaticResources(platform, 'icon');
},
@@ -252,20 +259,22 @@ ConfigParser.prototype = {
* @param {string} platform Platform name
* @return {Resource[]} Array of Splash objects.
*/
getSplashScreens: function(platform) {
getSplashScreens: function (platform) {
return this.getStaticResources(platform, 'splash');
},
/**
* Returns all resource-files for a specific platform.
* @param {string} platform Platform name
* @param {boolean} includeGlobal Whether to return resource-files at the
* root level.
* @return {Resource[]} Array of resource file objects.
*/
getFileResources: function(platform) {
getFileResources: function (platform, includeGlobal) {
var fileResources = [];
if (platform) { // platform specific resources
fileResources = this.doc.findall('platform[@name=\'' + platform + '\']/resource-file').map(function(tag) {
fileResources = this.doc.findall('platform[@name=\'' + platform + '\']/resource-file').map(function (tag) {
return {
platform: platform,
src: tag.attrib.src,
@@ -277,6 +286,19 @@ ConfigParser.prototype = {
});
}
if (includeGlobal) {
this.doc.findall('resource-file').forEach(function (tag) {
fileResources.push({
platform: platform || null,
src: tag.attrib.src,
target: tag.attrib.target,
versions: tag.attrib.versions,
deviceTarget: tag.attrib['device-target'],
arch: tag.attrib.arch
});
});
}
return fileResources;
},
@@ -286,23 +308,23 @@ ConfigParser.prototype = {
* @param {Array} platforms Platforms to look for scripts into (root scripts will be included as well).
* @return {Array} Script elements.
*/
getHookScripts: function(hook, platforms) {
getHookScripts: function (hook, platforms) {
var self = this;
var scriptElements = self.doc.findall('./hook');
if(platforms) {
if (platforms) {
platforms.forEach(function (platform) {
scriptElements = scriptElements.concat(self.doc.findall('./platform[@name="' + platform + '"]/hook'));
});
}
function filterScriptByHookType(el) {
function filterScriptByHookType (el) {
return el.attrib.src && el.attrib.type && el.attrib.type.toLowerCase() === hook;
}
return scriptElements.filter(filterScriptByHookType);
},
/**
/**
* Returns a list of plugin (IDs).
*
* This function also returns any plugin's that
@@ -311,13 +333,13 @@ ConfigParser.prototype = {
*/
getPluginIdList: function () {
var plugins = this.doc.findall('plugin');
var result = plugins.map(function(plugin){
var result = plugins.map(function (plugin) {
return plugin.attrib.name;
});
var features = this.doc.findall('feature');
features.forEach(function(element ){
features.forEach(function (element) {
var idTag = element.find('./param[@name="id"]');
if(idTag){
if (idTag) {
result.push(idTag.attrib.value);
}
});
@@ -346,9 +368,9 @@ ConfigParser.prototype = {
// support arbitrary object as variables source
if (variables && typeof variables === 'object' && !Array.isArray(variables)) {
variables = Object.keys(variables)
.map(function (variableName) {
return {name: variableName, value: variables[variableName]};
});
.map(function (variableName) {
return {name: variableName, value: variables[variableName]};
});
}
if (variables) {
@@ -368,15 +390,15 @@ ConfigParser.prototype = {
* @param {String} id
* @returns {object} plugin including any variables
*/
getPlugin: function(id){
if(!id){
getPlugin: function (id) {
if (!id) {
return undefined;
}
var pluginElement = this.doc.find('./plugin/[@name="' + id + '"]');
if (null === pluginElement) {
var legacyFeature = this.doc.find('./feature/param[@name="id"][@value="' + id + '"]/..');
if(legacyFeature){
events.emit('log', 'Found deprecated feature entry for ' + id +' in config.xml.');
if (pluginElement === null) {
var legacyFeature = this.doc.find('./feature/param[@name="id"][@value="' + id + '"]/..');
if (legacyFeature) {
events.emit('log', 'Found deprecated feature entry for ' + id + ' in config.xml.');
return featureToPlugin(legacyFeature);
}
return undefined;
@@ -387,10 +409,10 @@ ConfigParser.prototype = {
plugin.spec = pluginElement.attrib.spec || pluginElement.attrib.src || pluginElement.attrib.version;
plugin.variables = {};
var variableElements = pluginElement.findall('variable');
variableElements.forEach(function(varElement){
variableElements.forEach(function (varElement) {
var name = varElement.attrib.name;
var value = varElement.attrib.value;
if(name){
if (name) {
plugin.variables[name] = value;
}
});
@@ -405,8 +427,8 @@ ConfigParser.prototype = {
* @function
* @param id name of the plugin
*/
removePlugin: function(id){
if(id){
removePlugin: function (id) {
if (id) {
var plugins = this.doc.findall('./plugin/[@name="' + id + '"]')
.concat(this.doc.findall('./feature/param[@name="id"][@value="' + id + '"]/..'));
var children = this.doc.getroot().getchildren();
@@ -420,7 +442,7 @@ ConfigParser.prototype = {
},
// Add any element to the root
addElement: function(name, attributes) {
addElement: function (name, attributes) {
var el = et.Element(name);
for (var a in attributes) {
el.attrib[a] = attributes[a];
@@ -433,11 +455,11 @@ ConfigParser.prototype = {
* @param {String} name the engine name
* @param {String} spec engine source location or version (optional)
*/
addEngine: function(name, spec){
if(!name) return;
addEngine: function (name, spec) {
if (!name) return;
var el = et.Element('engine');
el.attrib.name = name;
if(spec){
if (spec) {
el.attrib.spec = spec;
}
this.doc.getroot().append(el);
@@ -446,52 +468,54 @@ ConfigParser.prototype = {
* Removes all the engines with given name
* @param {String} name the engine name.
*/
removeEngine: function(name){
var engines = this.doc.findall('./engine/[@name="' +name+'"]');
for(var i=0; i < engines.length; i++){
removeEngine: function (name) {
var engines = this.doc.findall('./engine/[@name="' + name + '"]');
for (var i = 0; i < engines.length; i++) {
var children = this.doc.getroot().getchildren();
var idx = children.indexOf(engines[i]);
if(idx > -1){
children.splice(idx,1);
if (idx > -1) {
children.splice(idx, 1);
}
}
},
getEngines: function(){
getEngines: function () {
var engines = this.doc.findall('./engine');
return engines.map(function(engine){
return engines.map(function (engine) {
var spec = engine.attrib.spec || engine.attrib.version;
return {
'name': engine.attrib.name,
'spec': spec ? spec : null
'spec': spec || null
};
});
},
/* Get all the access tags */
getAccesses: function() {
getAccesses: function () {
var accesses = this.doc.findall('./access');
return accesses.map(function(access){
return accesses.map(function (access) {
var minimum_tls_version = access.attrib['minimum-tls-version']; /* String */
var requires_forward_secrecy = access.attrib['requires-forward-secrecy']; /* Boolean */
var requires_certificate_transparency = access.attrib['requires-certificate-transparency']; /* Boolean */
var allows_arbitrary_loads_in_web_content = access.attrib['allows-arbitrary-loads-in-web-content']; /* Boolean */
var allows_arbitrary_loads_in_media = access.attrib['allows-arbitrary-loads-in-media']; /* Boolean */
var allows_arbitrary_loads_in_media = access.attrib['allows-arbitrary-loads-in-media']; /* Boolean (DEPRECATED) */
var allows_arbitrary_loads_for_media = access.attrib['allows-arbitrary-loads-for-media']; /* Boolean */
var allows_local_networking = access.attrib['allows-local-networking']; /* Boolean */
return {
'origin': access.attrib.origin,
'minimum_tls_version': minimum_tls_version,
'requires_forward_secrecy' : requires_forward_secrecy,
'requires_certificate_transparency' : requires_certificate_transparency,
'allows_arbitrary_loads_in_web_content' : allows_arbitrary_loads_in_web_content,
'allows_arbitrary_loads_in_media' : allows_arbitrary_loads_in_media,
'allows_local_networking' : allows_local_networking
'requires_forward_secrecy': requires_forward_secrecy,
'requires_certificate_transparency': requires_certificate_transparency,
'allows_arbitrary_loads_in_web_content': allows_arbitrary_loads_in_web_content,
'allows_arbitrary_loads_in_media': allows_arbitrary_loads_in_media,
'allows_arbitrary_loads_for_media': allows_arbitrary_loads_for_media,
'allows_local_networking': allows_local_networking
};
});
},
/* Get all the allow-navigation tags */
getAllowNavigations: function() {
getAllowNavigations: function () {
var allow_navigations = this.doc.findall('./allow-navigation');
return allow_navigations.map(function(allow_navigation){
return allow_navigations.map(function (allow_navigation) {
var minimum_tls_version = allow_navigation.attrib['minimum-tls-version']; /* String */
var requires_forward_secrecy = allow_navigation.attrib['requires-forward-secrecy']; /* Boolean */
var requires_certificate_transparency = allow_navigation.attrib['requires-certificate-transparency']; /* Boolean */
@@ -499,45 +523,68 @@ ConfigParser.prototype = {
return {
'href': allow_navigation.attrib.href,
'minimum_tls_version': minimum_tls_version,
'requires_forward_secrecy' : requires_forward_secrecy,
'requires_certificate_transparency' : requires_certificate_transparency
'requires_forward_secrecy': requires_forward_secrecy,
'requires_certificate_transparency': requires_certificate_transparency
};
});
},
/* Get all the allow-intent tags */
getAllowIntents: function() {
getAllowIntents: function () {
var allow_intents = this.doc.findall('./allow-intent');
return allow_intents.map(function(allow_intent){
return allow_intents.map(function (allow_intent) {
return {
'href': allow_intent.attrib.href
};
});
},
/* Get all edit-config tags */
getEditConfigs: function(platform) {
getEditConfigs: function (platform) {
var platform_tag = this.doc.find('./platform[@name="' + platform + '"]');
var platform_edit_configs = platform_tag ? platform_tag.findall('edit-config') : [];
var edit_configs = this.doc.findall('edit-config').concat(platform_edit_configs);
return edit_configs.map(function(tag) {
return edit_configs.map(function (tag) {
var editConfig =
{
file : tag.attrib['file'],
target : tag.attrib['target'],
mode : tag.attrib['mode'],
id : 'config.xml',
xmls : tag.getchildren()
file: tag.attrib['file'],
target: tag.attrib['target'],
mode: tag.attrib['mode'],
id: 'config.xml',
xmls: tag.getchildren()
};
return editConfig;
});
},
write:function() {
/* Get all config-file tags */
getConfigFiles: function (platform) {
var platform_tag = this.doc.find('./platform[@name="' + platform + '"]');
var platform_config_files = platform_tag ? platform_tag.findall('config-file') : [];
var config_files = this.doc.findall('config-file').concat(platform_config_files);
return config_files.map(function (tag) {
var configFile =
{
target: tag.attrib['target'],
parent: tag.attrib['parent'],
after: tag.attrib['after'],
xmls: tag.getchildren(),
// To support demuxing via versions
versions: tag.attrib['versions'],
deviceTarget: tag.attrib['device-target']
};
return configFile;
});
},
write: function () {
fs.writeFileSync(this.path, this.doc.write({indent: 4}), 'utf-8');
}
};
function featureToPlugin(featureElement) {
function featureToPlugin (featureElement) {
var plugin = {};
plugin.variables = [];
var pluginVersion,

View File

@@ -17,10 +17,10 @@
under the License.
*/
var fs = require('fs'),
path = require('path');
var fs = require('fs');
var path = require('path');
function isRootDir(dir) {
function isRootDir (dir) {
if (fs.existsSync(path.join(dir, 'www'))) {
if (fs.existsSync(path.join(dir, 'config.xml'))) {
// For sure is.
@@ -41,12 +41,12 @@ function isRootDir(dir) {
// Runs up the directory chain looking for a .cordova directory.
// IF it is found we are in a Cordova project.
// Omit argument to use CWD.
function isCordova(dir) {
function isCordova (dir) {
if (!dir) {
// Prefer PWD over cwd so that symlinked dirs within your PWD work correctly (CB-5687).
var pwd = process.env.PWD;
var cwd = process.cwd();
if (pwd && pwd != cwd && pwd != 'undefined') {
if (pwd && pwd !== cwd && pwd !== 'undefined') {
return isCordova(pwd) || isCordova(cwd);
}
return isCordova(cwd);
@@ -62,7 +62,7 @@ function isCordova(dir) {
}
var parentDir = path.normalize(path.join(dir, '..'));
// Detect fs root.
if (parentDir == dir) {
if (parentDir === dir) {
return bestReturnValueSoFar;
}
dir = parentDir;
@@ -72,5 +72,5 @@ function isCordova(dir) {
}
module.exports = {
findProjectRoot : isCordova
findProjectRoot: isCordova
};

View File

@@ -17,7 +17,7 @@
under the License.
*/
/* jshint proto:true */
/* eslint no-proto: 0 */
var EOL = require('os').EOL;
@@ -30,7 +30,7 @@ var EOL = require('os').EOL;
* @param {CordovaExternalToolErrorContext} [context] External tool error context object
* @constructor
*/
function CordovaError(message, code, context) {
function CordovaError (message, code, context) {
Error.captureStackTrace(this, this.constructor);
this.name = this.constructor.name;
this.message = message;
@@ -47,10 +47,10 @@ CordovaError.EXTERNAL_TOOL_ERROR = 1;
* Translates instance's error code number into error code name, e.g. 0 -> UNKNOWN_ERROR
* @returns {string} Error code string name
*/
CordovaError.prototype.getErrorCodeName = function() {
for(var key in CordovaError) {
if(CordovaError.hasOwnProperty(key)) {
if(CordovaError[key] === this.code) {
CordovaError.prototype.getErrorCodeName = function () {
for (var key in CordovaError) {
if (CordovaError.hasOwnProperty(key)) {
if (CordovaError[key] === this.code) {
return key;
}
}
@@ -63,16 +63,17 @@ CordovaError.prototype.getErrorCodeName = function() {
* details including information about error code name and context
* @return {String} Stringified error representation
*/
CordovaError.prototype.toString = function(isVerbose) {
var message = '', codePrefix = '';
CordovaError.prototype.toString = function (isVerbose) {
var message = '';
var codePrefix = '';
if(this.code !== CordovaError.UNKNOWN_ERROR) {
if (this.code !== CordovaError.UNKNOWN_ERROR) {
codePrefix = 'code: ' + this.code + (isVerbose ? (' (' + this.getErrorCodeName() + ')') : '') + ' ';
}
if(this.code === CordovaError.EXTERNAL_TOOL_ERROR) {
if(typeof this.context !== 'undefined') {
if(isVerbose) {
if (this.code === CordovaError.EXTERNAL_TOOL_ERROR) {
if (typeof this.context !== 'undefined') {
if (isVerbose) {
message = codePrefix + EOL + this.context.toString(isVerbose) + '\n failed with an error: ' +
this.message + EOL + 'Stack trace: ' + this.stack;
} else {

View File

@@ -27,7 +27,7 @@ var path = require('path');
* @param {String} [cwd] Command working directory
* @constructor
*/
function CordovaExternalToolErrorContext(cmd, args, cwd) {
function CordovaExternalToolErrorContext (cmd, args, cwd) {
this.cmd = cmd;
// Helper field for readability
this.cmdShortName = path.basename(cmd);
@@ -35,8 +35,8 @@ function CordovaExternalToolErrorContext(cmd, args, cwd) {
this.cwd = cwd;
}
CordovaExternalToolErrorContext.prototype.toString = function(isVerbose) {
if(isVerbose) {
CordovaExternalToolErrorContext.prototype.toString = function (isVerbose) {
if (isVerbose) {
return 'External tool \'' + this.cmdShortName + '\'' +
'\nCommand full path: ' + this.cmd + '\nCommand args: ' + this.args +
(typeof this.cwd !== 'undefined' ? '\nCommand cwd: ' + this.cwd : '');

View File

@@ -41,11 +41,11 @@ function CordovaLogger () {
this.stderrCursor = ansi(this.stderr);
this.addLevel('verbose', 1000, 'grey');
this.addLevel('normal' , 2000);
this.addLevel('warn' , 2000, 'yellow');
this.addLevel('info' , 3000, 'blue');
this.addLevel('error' , 5000, 'red');
this.addLevel('results' , 10000);
this.addLevel('normal', 2000);
this.addLevel('warn', 2000, 'yellow');
this.addLevel('info', 3000, 'blue');
this.addLevel('error', 5000, 'red');
this.addLevel('results', 10000);
this.setLevel('normal');
}
@@ -82,9 +82,10 @@ CordovaLogger.RESULTS = 'results';
CordovaLogger.prototype.log = function (logLevel, message) {
// if there is no such logLevel defined, or provided level has
// less severity than active level, then just ignore this call and return
if (!this.levels[logLevel] || this.levels[logLevel] < this.levels[this.logLevel])
if (!this.levels[logLevel] || this.levels[logLevel] < this.levels[this.logLevel]) {
// return instance to allow to chain calls
return this;
}
var isVerbose = this.logLevel === 'verbose';
var cursor = this.stdoutCursor;
@@ -179,8 +180,7 @@ CordovaLogger.prototype.adjustLevel = function (opts) {
*/
CordovaLogger.prototype.subscribe = function (eventEmitter) {
if (!(eventEmitter instanceof EventEmitter))
throw new Error('Subscribe method only accepts an EventEmitter instance as argument');
if (!(eventEmitter instanceof EventEmitter)) { throw new Error('Subscribe method only accepts an EventEmitter instance as argument'); }
eventEmitter.on('verbose', this.verbose)
.on('log', this.normal)
@@ -193,7 +193,7 @@ CordovaLogger.prototype.subscribe = function (eventEmitter) {
return this;
};
function formatError(error, isVerbose) {
function formatError (error, isVerbose) {
var message = '';
if (error instanceof CordovaError) {

View File

@@ -17,12 +17,12 @@
under the License.
*/
"use strict";
'use strict';
var fs = require("fs");
var path = require("path");
var shell = require("shelljs");
var minimatch = require("minimatch");
var fs = require('fs');
var path = require('path');
var shell = require('shelljs');
var minimatch = require('minimatch');
/**
* Logging callback used in the FileUpdater methods.
@@ -55,27 +55,27 @@ var minimatch = require("minimatch");
* @return {boolean} true if any changes were made, or false if the force flag is not set
* and everything was up to date
*/
function updatePathWithStats(sourcePath, sourceStats, targetPath, targetStats, options, log) {
function updatePathWithStats (sourcePath, sourceStats, targetPath, targetStats, options, log) {
var updated = false;
var rootDir = (options && options.rootDir) || "";
var rootDir = (options && options.rootDir) || '';
var copyAll = (options && options.all) || false;
var targetFullPath = path.join(rootDir || "", targetPath);
var targetFullPath = path.join(rootDir || '', targetPath);
if (sourceStats) {
var sourceFullPath = path.join(rootDir || "", sourcePath);
var sourceFullPath = path.join(rootDir || '', sourcePath);
if (targetStats) {
// The target exists. But if the directory status doesn't match the source, delete it.
if (targetStats.isDirectory() && !sourceStats.isDirectory()) {
log("rmdir " + targetPath + " (source is a file)");
shell.rm("-rf", targetFullPath);
log('rmdir ' + targetPath + ' (source is a file)');
shell.rm('-rf', targetFullPath);
targetStats = null;
updated = true;
} else if (!targetStats.isDirectory() && sourceStats.isDirectory()) {
log("delete " + targetPath + " (source is a directory)");
shell.rm("-f", targetFullPath);
log('delete ' + targetPath + ' (source is a directory)');
shell.rm('-f', targetFullPath);
targetStats = null;
updated = true;
}
@@ -84,21 +84,21 @@ function updatePathWithStats(sourcePath, sourceStats, targetPath, targetStats, o
if (!targetStats) {
if (sourceStats.isDirectory()) {
// The target directory does not exist, so it should be created.
log("mkdir " + targetPath);
shell.mkdir("-p", targetFullPath);
log('mkdir ' + targetPath);
shell.mkdir('-p', targetFullPath);
updated = true;
} else if (sourceStats.isFile()) {
// The target file does not exist, so it should be copied from the source.
log("copy " + sourcePath + " " + targetPath + (copyAll ? "" : " (new file)"));
shell.cp("-f", sourceFullPath, targetFullPath);
log('copy ' + sourcePath + ' ' + targetPath + (copyAll ? '' : ' (new file)'));
shell.cp('-f', sourceFullPath, targetFullPath);
updated = true;
}
} else if (sourceStats.isFile() && targetStats.isFile()) {
// The source and target paths both exist and are files.
if (copyAll) {
// The caller specified all files should be copied.
log("copy " + sourcePath + " " + targetPath);
shell.cp("-f", sourceFullPath, targetFullPath);
log('copy ' + sourcePath + ' ' + targetPath);
shell.cp('-f', sourceFullPath, targetFullPath);
updated = true;
} else {
// Copy if the source has been modified since it was copied to the target, or if
@@ -107,8 +107,8 @@ function updatePathWithStats(sourcePath, sourceStats, targetPath, targetStats, o
// for timestamps lacking sub-second precision in some filesystems.
if (sourceStats.mtime.getTime() >= targetStats.mtime.getTime() ||
sourceStats.size !== targetStats.size) {
log("copy " + sourcePath + " " + targetPath + " (updated file)");
shell.cp("-f", sourceFullPath, targetFullPath);
log('copy ' + sourcePath + ' ' + targetPath + ' (updated file)');
shell.cp('-f', sourceFullPath, targetFullPath);
updated = true;
}
}
@@ -116,11 +116,11 @@ function updatePathWithStats(sourcePath, sourceStats, targetPath, targetStats, o
} else if (targetStats) {
// The target exists but the source is null, so the target should be deleted.
if (targetStats.isDirectory()) {
log("rmdir " + targetPath + (copyAll ? "" : " (no source)"));
shell.rm("-rf", targetFullPath);
log('rmdir ' + targetPath + (copyAll ? '' : ' (no source)'));
shell.rm('-rf', targetFullPath);
} else {
log("delete " + targetPath + (copyAll ? "" : " (no source)"));
shell.rm("-f", targetFullPath);
log('delete ' + targetPath + (copyAll ? '' : ' (no source)'));
shell.rm('-f', targetFullPath);
}
updated = true;
}
@@ -132,8 +132,8 @@ function updatePathWithStats(sourcePath, sourceStats, targetPath, targetStats, o
* Helper for updatePath and updatePaths functions. Queries stats for source and target
* and ensures target directory exists before copying a file.
*/
function updatePathInternal(sourcePath, targetPath, options, log) {
var rootDir = (options && options.rootDir) || "";
function updatePathInternal (sourcePath, targetPath, options, log) {
var rootDir = (options && options.rootDir) || '';
var targetFullPath = path.join(rootDir, targetPath);
var targetStats = fs.existsSync(targetFullPath) ? fs.statSync(targetFullPath) : null;
var sourceStats = null;
@@ -142,7 +142,7 @@ function updatePathInternal(sourcePath, targetPath, options, log) {
// A non-null source path was specified. It should exist.
var sourceFullPath = path.join(rootDir, sourcePath);
if (!fs.existsSync(sourceFullPath)) {
throw new Error("Source path does not exist: " + sourcePath);
throw new Error('Source path does not exist: ' + sourcePath);
}
sourceStats = fs.statSync(sourceFullPath);
@@ -150,7 +150,7 @@ function updatePathInternal(sourcePath, targetPath, options, log) {
// Create the target's parent directory if it doesn't exist.
var parentDir = path.dirname(targetFullPath);
if (!fs.existsSync(parentDir)) {
shell.mkdir("-p", parentDir);
shell.mkdir('-p', parentDir);
}
}
@@ -177,16 +177,16 @@ function updatePathInternal(sourcePath, targetPath, options, log) {
* @return {boolean} true if any changes were made, or false if the force flag is not set
* and everything was up to date
*/
function updatePath(sourcePath, targetPath, options, log) {
if (sourcePath !== null && typeof sourcePath !== "string") {
throw new Error("A source path (or null) is required.");
function updatePath (sourcePath, targetPath, options, log) {
if (sourcePath !== null && typeof sourcePath !== 'string') {
throw new Error('A source path (or null) is required.');
}
if (!targetPath || typeof targetPath !== "string") {
throw new Error("A target path is required.");
if (!targetPath || typeof targetPath !== 'string') {
throw new Error('A target path is required.');
}
log = log || function(message) { };
log = log || function (message) { };
return updatePathInternal(sourcePath, targetPath, options, log);
}
@@ -208,12 +208,12 @@ function updatePath(sourcePath, targetPath, options, log) {
* @return {boolean} true if any changes were made, or false if the force flag is not set
* and everything was up to date
*/
function updatePaths(pathMap, options, log) {
if (!pathMap || typeof pathMap !== "object" || Array.isArray(pathMap)) {
throw new Error("An object mapping from target paths to source paths is required.");
function updatePaths (pathMap, options, log) {
if (!pathMap || typeof pathMap !== 'object' || Array.isArray(pathMap)) {
throw new Error('An object mapping from target paths to source paths is required.');
}
log = log || function(message) { };
log = log || function (message) { };
var updated = false;
@@ -255,44 +255,44 @@ function updatePaths(pathMap, options, log) {
* @return {boolean} true if any changes were made, or false if the force flag is not set
* and everything was up to date
*/
function mergeAndUpdateDir(sourceDirs, targetDir, options, log) {
if (sourceDirs && typeof sourceDirs === "string") {
function mergeAndUpdateDir (sourceDirs, targetDir, options, log) {
if (sourceDirs && typeof sourceDirs === 'string') {
sourceDirs = [ sourceDirs ];
} else if (!Array.isArray(sourceDirs)) {
throw new Error("A source directory path or array of paths is required.");
throw new Error('A source directory path or array of paths is required.');
}
if (!targetDir || typeof targetDir !== "string") {
throw new Error("A target directory path is required.");
if (!targetDir || typeof targetDir !== 'string') {
throw new Error('A target directory path is required.');
}
log = log || function(message) { };
log = log || function (message) { };
var rootDir = (options && options.rootDir) || "";
var rootDir = (options && options.rootDir) || '';
var include = (options && options.include) || [ "**" ];
if (typeof include === "string") {
var include = (options && options.include) || [ '**' ];
if (typeof include === 'string') {
include = [ include ];
} else if (!Array.isArray(include)) {
throw new Error("Include parameter must be a glob string or array of glob strings.");
throw new Error('Include parameter must be a glob string or array of glob strings.');
}
var exclude = (options && options.exclude) || [];
if (typeof exclude === "string") {
if (typeof exclude === 'string') {
exclude = [ exclude ];
} else if (!Array.isArray(exclude)) {
throw new Error("Exclude parameter must be a glob string or array of glob strings.");
throw new Error('Exclude parameter must be a glob string or array of glob strings.');
}
// Scan the files in each of the source directories.
var sourceMaps = sourceDirs.map(function (sourceDir) {
return path.join(rootDir, sourceDir);
}).map(function (sourcePath) {
if (!fs.existsSync(sourcePath)) {
throw new Error("Source directory does not exist: " + sourcePath);
}
return mapDirectory(rootDir, path.relative(rootDir, sourcePath), include, exclude);
});
return path.join(rootDir, sourceDir);
}).map(function (sourcePath) {
if (!fs.existsSync(sourcePath)) {
throw new Error('Source directory does not exist: ' + sourcePath);
}
return mapDirectory(rootDir, path.relative(rootDir, sourcePath), include, exclude);
});
// Scan the files in the target directory, if it exists.
var targetMap = {};
@@ -323,18 +323,18 @@ function mergeAndUpdateDir(sourceDirs, targetDir, options, log) {
/**
* Creates a dictionary map of all files and directories under a path.
*/
function mapDirectory(rootDir, subDir, include, exclude) {
var dirMap = { "": { subDir: subDir, stats: fs.statSync(path.join(rootDir, subDir)) } };
mapSubdirectory(rootDir, subDir, "", include, exclude, dirMap);
function mapDirectory (rootDir, subDir, include, exclude) {
var dirMap = { '': { subDir: subDir, stats: fs.statSync(path.join(rootDir, subDir)) } };
mapSubdirectory(rootDir, subDir, '', include, exclude, dirMap);
return dirMap;
function mapSubdirectory(rootDir, subDir, relativeDir, include, exclude, dirMap) {
function mapSubdirectory (rootDir, subDir, relativeDir, include, exclude, dirMap) {
var itemMapped = false;
var items = fs.readdirSync(path.join(rootDir, subDir, relativeDir));
items.forEach(function(item) {
items.forEach(function (item) {
var relativePath = path.join(relativeDir, item);
if(!matchGlobArray(relativePath, exclude)) {
if (!matchGlobArray(relativePath, exclude)) {
// Stats obtained here (required at least to know where to recurse in directories)
// are saved for later, where the modified times may also be used. This minimizes
// the number of file I/O operations performed.
@@ -361,9 +361,9 @@ function mapDirectory(rootDir, subDir, include, exclude) {
return itemMapped;
}
function matchGlobArray(path, globs) {
return globs.some(function(elem) {
return minimatch(path, elem, {dot:true});
function matchGlobArray (path, globs) {
return globs.some(function (elem) {
return minimatch(path, elem, {dot: true});
});
}
}
@@ -372,13 +372,13 @@ function mapDirectory(rootDir, subDir, include, exclude) {
* Merges together multiple source maps and a target map into a single mapping from
* relative paths to objects with target and source paths and stats.
*/
function mergePathMaps(sourceMaps, targetMap, targetDir) {
function mergePathMaps (sourceMaps, targetMap, targetDir) {
// Merge multiple source maps together, along with target path info.
// Entries in later source maps override those in earlier source maps.
// Target stats will be filled in below for targets that exist.
var pathMap = {};
sourceMaps.forEach(function (sourceMap) {
Object.keys(sourceMap).forEach(function(sourceSubPath){
Object.keys(sourceMap).forEach(function (sourceSubPath) {
var sourceEntry = sourceMap[sourceSubPath];
pathMap[sourceSubPath] = {
targetPath: path.join(targetDir, sourceSubPath),
@@ -391,7 +391,7 @@ function mergePathMaps(sourceMaps, targetMap, targetDir) {
// Fill in target stats for targets that exist, and create entries
// for targets that don't have any corresponding sources.
Object.keys(targetMap).forEach(function(subPath){
Object.keys(targetMap).forEach(function (subPath) {
var entry = pathMap[subPath];
if (entry) {
entry.targetStats = targetMap[subPath].stats;
@@ -413,4 +413,3 @@ module.exports = {
updatePaths: updatePaths,
mergeAndUpdateDir: mergeAndUpdateDir
};

View File

@@ -13,7 +13,6 @@
* under the License.
*
*/
/* jshint sub:true */
var fs = require('fs');
var path = require('path');
@@ -22,13 +21,13 @@ var mungeutil = require('./ConfigChanges/munge-util');
var pluginMappernto = require('cordova-registry-mapper').newToOld;
var pluginMapperotn = require('cordova-registry-mapper').oldToNew;
function PlatformJson(filePath, platform, root) {
function PlatformJson (filePath, platform, root) {
this.filePath = filePath;
this.platform = platform;
this.root = fix_munge(root || {});
}
PlatformJson.load = function(plugins_dir, platform) {
PlatformJson.load = function (plugins_dir, platform) {
var filePath = path.join(plugins_dir, platform + '.json');
var root = null;
if (fs.existsSync(filePath)) {
@@ -37,9 +36,9 @@ PlatformJson.load = function(plugins_dir, platform) {
return new PlatformJson(filePath, platform, root);
};
PlatformJson.prototype.save = function() {
PlatformJson.prototype.save = function () {
shelljs.mkdir('-p', path.dirname(this.filePath));
fs.writeFileSync(this.filePath, JSON.stringify(this.root, null, 4), 'utf-8');
fs.writeFileSync(this.filePath, JSON.stringify(this.root, null, 2), 'utf-8');
};
/**
@@ -49,7 +48,7 @@ PlatformJson.prototype.save = function() {
* @param {String} pluginId A plugin id to check for.
* @return {Boolean} true if plugin installed as top-level, otherwise false.
*/
PlatformJson.prototype.isPluginTopLevel = function(pluginId) {
PlatformJson.prototype.isPluginTopLevel = function (pluginId) {
var installedPlugins = this.root.installed_plugins;
return installedPlugins[pluginId] ||
installedPlugins[pluginMappernto[pluginId]] ||
@@ -63,7 +62,7 @@ PlatformJson.prototype.isPluginTopLevel = function(pluginId) {
* @param {String} pluginId A plugin id to check for.
* @return {Boolean} true if plugin installed as a dependency, otherwise false.
*/
PlatformJson.prototype.isPluginDependent = function(pluginId) {
PlatformJson.prototype.isPluginDependent = function (pluginId) {
var dependentPlugins = this.root.dependent_plugins;
return dependentPlugins[pluginId] ||
dependentPlugins[pluginMappernto[pluginId]] ||
@@ -76,12 +75,12 @@ PlatformJson.prototype.isPluginDependent = function(pluginId) {
* @param {String} pluginId A plugin id to check for.
* @return {Boolean} true if plugin installed, otherwise false.
*/
PlatformJson.prototype.isPluginInstalled = function(pluginId) {
PlatformJson.prototype.isPluginInstalled = function (pluginId) {
return this.isPluginTopLevel(pluginId) ||
this.isPluginDependent(pluginId);
};
PlatformJson.prototype.addPlugin = function(pluginId, variables, isTopLevel) {
PlatformJson.prototype.addPlugin = function (pluginId, variables, isTopLevel) {
var pluginsList = isTopLevel ?
this.root.installed_plugins :
this.root.dependent_plugins;
@@ -107,13 +106,13 @@ PlatformJson.prototype.addPluginMetadata = function (pluginInfo) {
});
var modulesToInstall = pluginInfo.getJsModules(this.platform)
.map(function (module) {
return new ModuleMetadata(pluginInfo.id, module);
})
.filter(function (metadata) {
// Filter out modules which are already added to metadata
return installedPaths.indexOf(metadata.file) === -1;
});
.map(function (module) {
return new ModuleMetadata(pluginInfo.id, module);
})
.filter(function (metadata) {
// Filter out modules which are already added to metadata
return installedPaths.indexOf(metadata.file) === -1;
});
this.root.modules = installedModules.concat(modulesToInstall);
@@ -123,7 +122,7 @@ PlatformJson.prototype.addPluginMetadata = function (pluginInfo) {
return this;
};
PlatformJson.prototype.removePlugin = function(pluginId, isTopLevel) {
PlatformJson.prototype.removePlugin = function (pluginId, isTopLevel) {
var pluginsList = isTopLevel ?
this.root.installed_plugins :
this.root.dependent_plugins;
@@ -144,16 +143,16 @@ PlatformJson.prototype.removePlugin = function(pluginId, isTopLevel) {
*/
PlatformJson.prototype.removePluginMetadata = function (pluginInfo) {
var modulesToRemove = pluginInfo.getJsModules(this.platform)
.map(function (jsModule) {
return ['plugins', pluginInfo.id, jsModule.src].join('/');
});
.map(function (jsModule) {
return ['plugins', pluginInfo.id, jsModule.src].join('/');
});
var installedModules = this.root.modules || [];
this.root.modules = installedModules
.filter(function (installedModule) {
// Leave only those metadatas which 'file' is not in removed modules
return (modulesToRemove.indexOf(installedModule.file) === -1);
});
.filter(function (installedModule) {
// Leave only those metadatas which 'file' is not in removed modules
return (modulesToRemove.indexOf(installedModule.file) === -1);
});
if (this.root.plugin_metadata) {
delete this.root.plugin_metadata[pluginInfo.id];
@@ -162,12 +161,12 @@ PlatformJson.prototype.removePluginMetadata = function (pluginInfo) {
return this;
};
PlatformJson.prototype.addInstalledPluginToPrepareQueue = function(pluginDirName, vars, is_top_level, force) {
this.root.prepare_queue.installed.push({'plugin':pluginDirName, 'vars':vars, 'topLevel':is_top_level, 'force':force});
PlatformJson.prototype.addInstalledPluginToPrepareQueue = function (pluginDirName, vars, is_top_level, force) {
this.root.prepare_queue.installed.push({'plugin': pluginDirName, 'vars': vars, 'topLevel': is_top_level, 'force': force});
};
PlatformJson.prototype.addUninstalledPluginToPrepareQueue = function(pluginId, is_top_level) {
this.root.prepare_queue.uninstalled.push({'plugin':pluginId, 'id':pluginId, 'topLevel':is_top_level});
PlatformJson.prototype.addUninstalledPluginToPrepareQueue = function (pluginId, is_top_level) {
this.root.prepare_queue.uninstalled.push({'plugin': pluginId, 'id': pluginId, 'topLevel': is_top_level});
};
/**
@@ -177,7 +176,7 @@ PlatformJson.prototype.addUninstalledPluginToPrepareQueue = function(pluginId, i
* @param {String} pluginId A plugin id to make top-level.
* @return {PlatformJson} PlatformJson instance.
*/
PlatformJson.prototype.makeTopLevel = function(pluginId) {
PlatformJson.prototype.makeTopLevel = function (pluginId) {
var plugin = this.root.dependent_plugins[pluginId];
if (plugin) {
delete this.root.dependent_plugins[pluginId];
@@ -195,10 +194,10 @@ PlatformJson.prototype.makeTopLevel = function(pluginId) {
PlatformJson.prototype.generateMetadata = function () {
return [
'cordova.define(\'cordova/plugin_list\', function(require, exports, module) {',
'module.exports = ' + JSON.stringify(this.root.modules, null, 4) + ';',
'module.exports = ' + JSON.stringify(this.root.modules, null, 2) + ';',
'module.exports.metadata = ',
'// TOP OF METADATA',
JSON.stringify(this.root.plugin_metadata, null, 4) + ';',
JSON.stringify(this.root.plugin_metadata, null, 2) + ';',
'// BOTTOM OF METADATA',
'});' // Close cordova.define.
].join('\n');
@@ -220,8 +219,8 @@ PlatformJson.prototype.generateAndSaveMetadata = function (destination) {
};
// convert a munge from the old format ([file][parent][xml] = count) to the current one
function fix_munge(root) {
root.prepare_queue = root.prepare_queue || {installed:[], uninstalled:[]};
function fix_munge (root) {
root.prepare_queue = root.prepare_queue || {installed: [], uninstalled: []};
root.config_munge = root.config_munge || {files: {}};
root.installed_plugins = root.installed_plugins || {};
root.dependent_plugins = root.dependent_plugins || {};
@@ -260,15 +259,15 @@ function ModuleMetadata (pluginId, jsModule) {
if (!pluginId) throw new TypeError('pluginId argument must be a valid plugin id');
if (!jsModule.src && !jsModule.name) throw new TypeError('jsModule argument must contain src or/and name properties');
this.id = pluginId + '.' + ( jsModule.name || jsModule.src.match(/([^\/]+)\.js/)[1] );
this.id = pluginId + '.' + (jsModule.name || jsModule.src.match(/([^\/]+)\.js/)[1]); /* eslint no-useless-escape: 0 */
this.file = ['plugins', pluginId, jsModule.src].join('/');
this.pluginId = pluginId;
if (jsModule.clobbers && jsModule.clobbers.length > 0) {
this.clobbers = jsModule.clobbers.map(function(o) { return o.target; });
this.clobbers = jsModule.clobbers.map(function (o) { return o.target; });
}
if (jsModule.merges && jsModule.merges.length > 0) {
this.merges = jsModule.merges.map(function(o) { return o.target; });
this.merges = jsModule.merges.map(function (o) { return o.target; });
}
if (jsModule.runs) {
this.runs = true;

View File

@@ -17,8 +17,6 @@
under the License.
*/
/* jshint sub:true, laxcomma:true, laxbreak:true */
/*
A class for holidng the information currently stored in plugin.xml
It should also be able to answer questions like whether the plugin
@@ -27,14 +25,12 @@ is compatible with a given engine version.
TODO (kamrik): refactor this to not use sync functions and return promises.
*/
var path = require('path');
var fs = require('fs');
var xml_helpers = require('../util/xml-helpers');
var CordovaError = require('../CordovaError/CordovaError');
var path = require('path')
, fs = require('fs')
, xml_helpers = require('../util/xml-helpers')
, CordovaError = require('../CordovaError/CordovaError')
;
function PluginInfo(dirname) {
function PluginInfo (dirname) {
var self = this;
// METHODS
@@ -45,15 +41,15 @@ function PluginInfo(dirname) {
// Used to require a variable to be specified via --variable when installing the plugin.
// returns { key : default | null}
self.getPreferences = getPreferences;
function getPreferences(platform) {
function getPreferences (platform) {
return _getTags(self._et, 'preference', platform, _parsePreference)
.reduce(function (preferences, pref) {
preferences[pref.preference] = pref.default;
return preferences;
}, {});
.reduce(function (preferences, pref) {
preferences[pref.preference] = pref.default;
return preferences;
}, {});
}
function _parsePreference(prefTag) {
function _parsePreference (prefTag) {
var name = prefTag.attrib.name.toUpperCase();
var def = prefTag.attrib.default || null;
return {preference: name, default: def};
@@ -61,16 +57,16 @@ function PluginInfo(dirname) {
// <asset>
self.getAssets = getAssets;
function getAssets(platform) {
function getAssets (platform) {
var assets = _getTags(self._et, 'asset', platform, _parseAsset);
return assets;
}
function _parseAsset(tag) {
function _parseAsset (tag) {
var src = tag.attrib.src;
var target = tag.attrib.target;
if ( !src || !target) {
if (!src || !target) {
var msg =
'Malformed <asset> tag. Both "src" and "target" attributes'
+ 'must be specified in\n'
@@ -87,7 +83,6 @@ function PluginInfo(dirname) {
return asset;
}
// <dependency>
// Example:
// <dependency id="com.plugin.id"
@@ -95,27 +90,28 @@ function PluginInfo(dirname) {
// commit="428931ada3891801"
// subdir="some/path/here" />
self.getDependencies = getDependencies;
function getDependencies(platform) {
function getDependencies (platform) {
var deps = _getTags(
self._et,
'dependency',
platform,
_parseDependency
self._et,
'dependency',
platform,
_parseDependency
);
return deps;
}
function _parseDependency(tag) {
function _parseDependency (tag) {
var dep =
{ id : tag.attrib.id
, url : tag.attrib.url || ''
, subdir : tag.attrib.subdir || ''
, commit : tag.attrib.commit
{ id: tag.attrib.id,
version: tag.attrib.version || '',
url: tag.attrib.url || '',
subdir: tag.attrib.subdir || '',
commit: tag.attrib.commit
};
dep.git_ref = dep.commit;
if ( !dep.id ) {
if (!dep.id) {
var msg =
'<dependency> tag is missing id attribute in '
+ self.filepath
@@ -125,52 +121,51 @@ function PluginInfo(dirname) {
return dep;
}
// <config-file> tag
self.getConfigFiles = getConfigFiles;
function getConfigFiles(platform) {
function getConfigFiles (platform) {
var configFiles = _getTags(self._et, 'config-file', platform, _parseConfigFile);
return configFiles;
}
function _parseConfigFile(tag) {
function _parseConfigFile (tag) {
var configFile =
{ target : tag.attrib['target']
, parent : tag.attrib['parent']
, after : tag.attrib['after']
, xmls : tag.getchildren()
// To support demuxing via versions
, versions : tag.attrib['versions']
, deviceTarget: tag.attrib['device-target']
{ target: tag.attrib['target'],
parent: tag.attrib['parent'],
after: tag.attrib['after'],
xmls: tag.getchildren(),
// To support demuxing via versions
versions: tag.attrib['versions'],
deviceTarget: tag.attrib['device-target']
};
return configFile;
}
self.getEditConfigs = getEditConfigs;
function getEditConfigs(platform) {
function getEditConfigs (platform) {
var editConfigs = _getTags(self._et, 'edit-config', platform, _parseEditConfigs);
return editConfigs;
}
function _parseEditConfigs(tag) {
function _parseEditConfigs (tag) {
var editConfig =
{ file : tag.attrib['file']
, target : tag.attrib['target']
, mode : tag.attrib['mode']
, xmls : tag.getchildren()
};
{ file: tag.attrib['file'],
target: tag.attrib['target'],
mode: tag.attrib['mode'],
xmls: tag.getchildren()
};
return editConfig;
}
// <info> tags, both global and within a <platform>
// TODO (kamrik): Do we ever use <info> under <platform>? Example wanted.
self.getInfo = getInfo;
function getInfo(platform) {
function getInfo (platform) {
var infos = _getTags(
self._et,
'info',
platform,
function(elem) { return elem.text; }
self._et,
'info',
platform,
function (elem) { return elem.text; }
);
// Filter out any undefined or empty strings.
infos = infos.filter(Boolean);
@@ -182,12 +177,12 @@ function PluginInfo(dirname) {
// <source-file src="src/ios/someLib.a" framework="true" />
// <source-file src="src/ios/someLib.a" compiler-flags="-fno-objc-arc" />
self.getSourceFiles = getSourceFiles;
function getSourceFiles(platform) {
function getSourceFiles (platform) {
var sourceFiles = _getTagsInPlatform(self._et, 'source-file', platform, _parseSourceFile);
return sourceFiles;
}
function _parseSourceFile(tag) {
function _parseSourceFile (tag) {
return {
itemType: 'source-file',
src: tag.attrib.src,
@@ -202,8 +197,8 @@ function PluginInfo(dirname) {
// Example:
// <header-file src="CDVFoo.h" />
self.getHeaderFiles = getHeaderFiles;
function getHeaderFiles(platform) {
var headerFiles = _getTagsInPlatform(self._et, 'header-file', platform, function(tag) {
function getHeaderFiles (platform) {
var headerFiles = _getTagsInPlatform(self._et, 'header-file', platform, function (tag) {
return {
itemType: 'header-file',
src: tag.attrib.src,
@@ -217,8 +212,8 @@ function PluginInfo(dirname) {
// Example:
// <resource-file src="FooPluginStrings.xml" target="res/values/FooPluginStrings.xml" device-target="win" arch="x86" versions="&gt;=8.1" />
self.getResourceFiles = getResourceFiles;
function getResourceFiles(platform) {
var resourceFiles = _getTagsInPlatform(self._et, 'resource-file', platform, function(tag) {
function getResourceFiles (platform) {
var resourceFiles = _getTagsInPlatform(self._et, 'resource-file', platform, function (tag) {
return {
itemType: 'resource-file',
src: tag.attrib.src,
@@ -236,8 +231,8 @@ function PluginInfo(dirname) {
// Example:
// <lib-file src="src/BlackBerry10/native/device/libfoo.so" arch="device" />
self.getLibFiles = getLibFiles;
function getLibFiles(platform) {
var libFiles = _getTagsInPlatform(self._et, 'lib-file', platform, function(tag) {
function getLibFiles (platform) {
var libFiles = _getTagsInPlatform(self._et, 'lib-file', platform, function (tag) {
return {
itemType: 'lib-file',
src: tag.attrib.src,
@@ -254,16 +249,16 @@ function PluginInfo(dirname) {
// Example:
// <hook type="before_build" src="scripts/beforeBuild.js" />
self.getHookScripts = getHookScripts;
function getHookScripts(hook, platforms) {
var scriptElements = self._et.findall('./hook');
function getHookScripts (hook, platforms) {
var scriptElements = self._et.findall('./hook');
if(platforms) {
if (platforms) {
platforms.forEach(function (platform) {
scriptElements = scriptElements.concat(self._et.findall('./platform[@name="' + platform + '"]/hook'));
});
}
function filterScriptByHookType(el) {
function filterScriptByHookType (el) {
return el.attrib.src && el.attrib.type && el.attrib.type.toLowerCase() === hook;
}
@@ -271,26 +266,26 @@ function PluginInfo(dirname) {
}
self.getJsModules = getJsModules;
function getJsModules(platform) {
function getJsModules (platform) {
var modules = _getTags(self._et, 'js-module', platform, _parseJsModule);
return modules;
}
function _parseJsModule(tag) {
function _parseJsModule (tag) {
var ret = {
itemType: 'js-module',
name: tag.attrib.name,
src: tag.attrib.src,
clobbers: tag.findall('clobbers').map(function(tag) { return { target: tag.attrib.target }; }),
merges: tag.findall('merges').map(function(tag) { return { target: tag.attrib.target }; }),
clobbers: tag.findall('clobbers').map(function (tag) { return { target: tag.attrib.target }; }),
merges: tag.findall('merges').map(function (tag) { return { target: tag.attrib.target }; }),
runs: tag.findall('runs').length > 0
};
return ret;
}
self.getEngines = function() {
return self._et.findall('engines/engine').map(function(n) {
self.getEngines = function () {
return self._et.findall('engines/engine').map(function (n) {
return {
name: n.attrib.name,
version: n.attrib.version,
@@ -300,25 +295,40 @@ function PluginInfo(dirname) {
});
};
self.getPlatforms = function() {
return self._et.findall('platform').map(function(n) {
self.getPlatforms = function () {
return self._et.findall('platform').map(function (n) {
return { name: n.attrib.name };
});
};
self.getPlatformsArray = function() {
return self._et.findall('platform').map(function(n) {
self.getPlatformsArray = function () {
return self._et.findall('platform').map(function (n) {
return n.attrib.name;
});
};
self.getFrameworks = function(platform) {
return _getTags(self._et, 'framework', platform, function(el) {
self.getFrameworks = function (platform, options) {
return _getTags(self._et, 'framework', platform, function (el) {
var src = el.attrib.src;
if (options) {
var vars = options.cli_variables || {};
var regExp;
// Iterate over plugin variables.
// Replace them in framework src if they exist
Object.keys(vars).forEach(function (name) {
if (vars[name]) {
regExp = new RegExp('\\$' + name, 'g');
src = src.replace(regExp, vars[name]);
}
});
}
var ret = {
itemType: 'framework',
type: el.attrib.type,
parent: el.attrib.parent,
custom: isStrTrue(el.attrib.custom),
src: el.attrib.src,
embed: isStrTrue(el.attrib.embed),
src: src,
spec: el.attrib.spec,
weak: isStrTrue(el.attrib.weak),
versions: el.attrib.versions,
@@ -332,22 +342,21 @@ function PluginInfo(dirname) {
};
self.getFilesAndFrameworks = getFilesAndFrameworks;
function getFilesAndFrameworks(platform) {
function getFilesAndFrameworks (platform, options) {
// Please avoid changing the order of the calls below, files will be
// installed in this order.
var items = [].concat(
self.getSourceFiles(platform),
self.getHeaderFiles(platform),
self.getResourceFiles(platform),
self.getFrameworks(platform),
self.getFrameworks(platform, options),
self.getLibFiles(platform)
);
return items;
}
///// End of PluginInfo methods /////
/// // End of PluginInfo methods /////
///// PluginInfo Constructor logic /////
/// // PluginInfo Constructor logic /////
self.filepath = path.join(dirname, 'plugin.xml');
if (!fs.existsSync(self.filepath)) {
throw new CordovaError('Cannot find plugin.xml for plugin "' + path.basename(dirname) + '". Please try adding it again.');
@@ -368,18 +377,18 @@ function PluginInfo(dirname) {
self.keywords = pelem.findtext('keywords');
self.info = pelem.findtext('info');
if (self.keywords) {
self.keywords = self.keywords.split(',').map( function(s) { return s.trim(); } );
self.keywords = self.keywords.split(',').map(function (s) { return s.trim(); });
}
self.getKeywordsAndPlatforms = function () {
var ret = self.keywords || [];
return ret.concat('ecosystem:cordova').concat(addCordova(self.getPlatformsArray()));
};
} // End of PluginInfo constructor.
} // End of PluginInfo constructor.
// Helper function used to prefix every element of an array with cordova-
// Useful when we want to modify platforms to be cordova-platform
function addCordova(someArray) {
var newArray = someArray.map(function(element) {
function addCordova (someArray) {
var newArray = someArray.map(function (element) {
return 'cordova-' + element;
});
return newArray;
@@ -389,37 +398,37 @@ function addCordova(someArray) {
// Get all elements of a given name. Both in root and in platform sections
// for the given platform. If transform is given and is a function, it is
// applied to each element.
function _getTags(pelem, tag, platform, transform) {
function _getTags (pelem, tag, platform, transform) {
var platformTag = pelem.find('./platform[@name="' + platform + '"]');
var tagsInRoot = pelem.findall(tag);
tagsInRoot = tagsInRoot || [];
var tagsInPlatform = platformTag ? platformTag.findall(tag) : [];
var tags = tagsInRoot.concat(tagsInPlatform);
if ( typeof transform === 'function' ) {
if (typeof transform === 'function') {
tags = tags.map(transform);
}
return tags;
}
// Same as _getTags() but only looks inside a platform section.
function _getTagsInPlatform(pelem, tag, platform, transform) {
function _getTagsInPlatform (pelem, tag, platform, transform) {
var platformTag = pelem.find('./platform[@name="' + platform + '"]');
var tags = platformTag ? platformTag.findall(tag) : [];
if ( typeof transform === 'function' ) {
if (typeof transform === 'function') {
tags = tags.map(transform);
}
return tags;
}
// Check if x is a string 'true'.
function isStrTrue(x) {
return String(x).toLowerCase() == 'true';
function isStrTrue (x) {
return String(x).toLowerCase() === 'true';
}
module.exports = PluginInfo;
// Backwards compat:
PluginInfo.PluginInfo = PluginInfo;
PluginInfo.loadPluginsDir = function(dir) {
PluginInfo.loadPluginsDir = function (dir) {
var PluginInfoProvider = require('./PluginInfoProvider');
return new PluginInfoProvider().getAllWithinSearchPath(dir);
};

View File

@@ -24,12 +24,12 @@ var path = require('path');
var PluginInfo = require('./PluginInfo');
var events = require('../events');
function PluginInfoProvider() {
function PluginInfoProvider () {
this._cache = {};
this._getAllCache = {};
}
PluginInfoProvider.prototype.get = function(dirName) {
PluginInfoProvider.prototype.get = function (dirName) {
var absPath = path.resolve(dirName);
if (!this._cache[absPath]) {
this._cache[absPath] = new PluginInfo(dirName);
@@ -39,7 +39,7 @@ PluginInfoProvider.prototype.get = function(dirName) {
// Normally you don't need to put() entries, but it's used
// when copying plugins, and in unit tests.
PluginInfoProvider.prototype.put = function(pluginInfo) {
PluginInfoProvider.prototype.put = function (pluginInfo) {
var absPath = path.resolve(pluginInfo.dir);
this._cache[absPath] = pluginInfo;
};
@@ -48,7 +48,7 @@ PluginInfoProvider.prototype.put = function(pluginInfo) {
// Given a dir containing multiple plugins, create a PluginInfo object for
// each of them and return as array.
// Should load them all in parallel and return a promise, but not yet.
PluginInfoProvider.prototype.getAllWithinSearchPath = function(dirName) {
PluginInfoProvider.prototype.getAllWithinSearchPath = function (dirName) {
var absPath = path.resolve(dirName);
if (!this._getAllCache[absPath]) {
this._getAllCache[absPath] = getAllHelper(absPath, this);
@@ -56,8 +56,8 @@ PluginInfoProvider.prototype.getAllWithinSearchPath = function(dirName) {
return this._getAllCache[absPath];
};
function getAllHelper(absPath, provider) {
if (!fs.existsSync(absPath)){
function getAllHelper (absPath, provider) {
if (!fs.existsSync(absPath)) {
return [];
}
// If dir itself is a plugin, return it in an array with one element.
@@ -66,7 +66,7 @@ function getAllHelper(absPath, provider) {
}
var subdirs = fs.readdirSync(absPath);
var plugins = [];
subdirs.forEach(function(subdir) {
subdirs.forEach(function (subdir) {
var d = path.join(absPath, subdir);
if (fs.existsSync(path.join(d, 'plugin.xml'))) {
try {

View File

@@ -36,7 +36,7 @@ var PluginInfoProvider = require('./PluginInfo/PluginInfoProvider');
* @param {Object} locations - Platform files and directories
* @param {IDEProject} ideProject The IDE project to add/remove plugin changes to/from
*/
function PluginManager(platform, locations, ideProject) {
function PluginManager (platform, locations, ideProject) {
this.platform = platform;
this.locations = locations;
this.project = ideProject;
@@ -45,7 +45,6 @@ function PluginManager(platform, locations, ideProject) {
this.munger = new PlatformMunger(platform, locations.root, platformJson, new PluginInfoProvider());
}
/**
* @constructs PluginManager
* A convenience shortcut to new PluginManager(...)
@@ -55,7 +54,7 @@ function PluginManager(platform, locations, ideProject) {
* @param {IDEProject} ideProject The IDE project to add/remove plugin changes to/from
* @returns new PluginManager instance
*/
PluginManager.get = function(platform, locations, ideProject) {
PluginManager.get = function (platform, locations, ideProject) {
return new PluginManager(platform, locations, ideProject);
};
@@ -82,11 +81,9 @@ module.exports = PluginManager;
* @returns {Promise} Returns a Q promise, either resolved in case of success, rejected otherwise.
*/
PluginManager.prototype.doOperation = function (operation, plugin, options) {
if (operation !== PluginManager.INSTALL && operation !== PluginManager.UNINSTALL)
return Q.reject(new CordovaError('The parameter is incorrect. The opeation must be either "add" or "remove"'));
if (operation !== PluginManager.INSTALL && operation !== PluginManager.UNINSTALL) { return Q.reject(new CordovaError('The parameter is incorrect. The opeation must be either "add" or "remove"')); }
if (!plugin || plugin.constructor.name !== 'PluginInfo')
return Q.reject(new CordovaError('The parameter is incorrect. The first parameter should be a PluginInfo instance'));
if (!plugin || plugin.constructor.name !== 'PluginInfo') { return Q.reject(new CordovaError('The parameter is incorrect. The first parameter should be a PluginInfo instance')); }
// Set default to empty object to play safe when accesing properties
options = options || {};
@@ -95,52 +92,52 @@ PluginManager.prototype.doOperation = function (operation, plugin, options) {
var actions = new ActionStack();
// gather all files need to be handled during operation ...
plugin.getFilesAndFrameworks(this.platform)
plugin.getFilesAndFrameworks(this.platform, options)
.concat(plugin.getAssets(this.platform))
.concat(plugin.getJsModules(this.platform))
// ... put them into stack ...
.forEach(function(item) {
var installer = self.project.getInstaller(item.itemType);
var uninstaller = self.project.getUninstaller(item.itemType);
var actionArgs = [item, plugin, self.project, options];
// ... put them into stack ...
.forEach(function (item) {
var installer = self.project.getInstaller(item.itemType);
var uninstaller = self.project.getUninstaller(item.itemType);
var actionArgs = [item, plugin, self.project, options];
var action;
if (operation === PluginManager.INSTALL) {
action = actions.createAction.apply(actions, [installer, actionArgs, uninstaller, actionArgs]);
} else /* op === PluginManager.UNINSTALL */{
action = actions.createAction.apply(actions, [uninstaller, actionArgs, installer, actionArgs]);
}
actions.push(action);
});
var action;
if (operation === PluginManager.INSTALL) {
action = actions.createAction.apply(actions, [installer, actionArgs, uninstaller, actionArgs]); /* eslint no-useless-call: 0 */
} else /* op === PluginManager.UNINSTALL */{
action = actions.createAction.apply(actions, [uninstaller, actionArgs, installer, actionArgs]); /* eslint no-useless-call: 0 */
}
actions.push(action);
});
// ... and run through the action stack
return actions.process(this.platform)
.then(function () {
if (self.project.write) {
self.project.write();
}
.then(function () {
if (self.project.write) {
self.project.write();
}
if (operation === PluginManager.INSTALL) {
// Ignore passed `is_top_level` option since platform itself doesn't know
// anything about managing dependencies - it's responsibility of caller.
self.munger.add_plugin_changes(plugin, options.variables, /*is_top_level=*/true, /*should_increment=*/true, options.force);
self.munger.platformJson.addPluginMetadata(plugin);
} else {
self.munger.remove_plugin_changes(plugin, /*is_top_level=*/true);
self.munger.platformJson.removePluginMetadata(plugin);
}
if (operation === PluginManager.INSTALL) {
// Ignore passed `is_top_level` option since platform itself doesn't know
// anything about managing dependencies - it's responsibility of caller.
self.munger.add_plugin_changes(plugin, options.variables, /* is_top_level= */true, /* should_increment= */true, options.force);
self.munger.platformJson.addPluginMetadata(plugin);
} else {
self.munger.remove_plugin_changes(plugin, /* is_top_level= */true);
self.munger.platformJson.removePluginMetadata(plugin);
}
// Save everything (munge and plugin/modules metadata)
self.munger.save_all();
// Save everything (munge and plugin/modules metadata)
self.munger.save_all();
var metadata = self.munger.platformJson.generateMetadata();
fs.writeFileSync(path.join(self.locations.www, 'cordova_plugins.js'), metadata, 'utf-8');
var metadata = self.munger.platformJson.generateMetadata();
fs.writeFileSync(path.join(self.locations.www, 'cordova_plugins.js'), metadata, 'utf-8');
// CB-11022 save plugin metadata to both www and platform_www if options.usePlatformWww is specified
if (options.usePlatformWww) {
fs.writeFileSync(path.join(self.locations.platformWww, 'cordova_plugins.js'), metadata, 'utf-8');
}
});
// CB-11022 save plugin metadata to both www and platform_www if options.usePlatformWww is specified
if (options.usePlatformWww) {
fs.writeFileSync(path.join(self.locations.platformWww, 'cordova_plugins.js'), metadata, 'utf-8');
}
});
};
PluginManager.prototype.addPlugin = function (plugin, installOptions) {

View File

@@ -20,6 +20,7 @@
var EventEmitter = require('events').EventEmitter;
var INSTANCE = new EventEmitter();
INSTANCE.setMaxListeners(20);
var EVENTS_RECEIVER;
module.exports = INSTANCE;
@@ -39,8 +40,7 @@ module.exports.forwardEventsTo = function (eventEmitter) {
return;
}
if (!(eventEmitter instanceof EventEmitter))
throw new Error('Cordova events can be redirected to another EventEmitter instance only');
if (!(eventEmitter instanceof EventEmitter)) { throw new Error('Cordova events can be redirected to another EventEmitter instance only'); }
// CB-10940 Skipping forwarding to self to avoid infinite recursion.
// This is the case when the modules are npm-linked.

View File

@@ -24,12 +24,12 @@ var _ = require('underscore');
var Q = require('q');
var shell = require('shelljs');
var events = require('./events');
var iswin32 = process.platform == 'win32';
var iswin32 = process.platform === 'win32';
// On Windows, spawn() for batch files requires absolute path & having the extension.
function resolveWindowsExe(cmd) {
function resolveWindowsExe (cmd) {
var winExtensions = ['.exe', '.bat', '.cmd', '.js', '.vbs'];
function isValidExe(c) {
function isValidExe (c) {
return winExtensions.indexOf(path.extname(c)) !== -1 && fs.existsSync(c);
}
if (isValidExe(cmd)) {
@@ -37,7 +37,7 @@ function resolveWindowsExe(cmd) {
}
cmd = shell.which(cmd) || cmd;
if (!isValidExe(cmd)) {
winExtensions.some(function(ext) {
winExtensions.some(function (ext) {
if (fs.existsSync(cmd + ext)) {
cmd = cmd + ext;
return true;
@@ -47,7 +47,7 @@ function resolveWindowsExe(cmd) {
return cmd;
}
function maybeQuote(a) {
function maybeQuote (a) {
if (/^[^"].*[ &].*[^"]/.test(a)) return '"' + a + '"';
return a;
}
@@ -85,7 +85,7 @@ function maybeQuote(a) {
* 'stderr': ...
* }
*/
exports.spawn = function(cmd, args, opts) {
exports.spawn = function (cmd, args, opts) {
args = args || [];
opts = opts || {};
var spawnOpts = {};
@@ -95,7 +95,7 @@ exports.spawn = function(cmd, args, opts) {
cmd = resolveWindowsExe(cmd);
// If we couldn't find the file, likely we'll end up failing,
// but for things like "del", cmd will do the trick.
if (path.extname(cmd) != '.exe') {
if (path.extname(cmd) !== '.exe') {
var cmdArgs = '"' + [cmd].concat(args).map(maybeQuote).join(' ') + '"';
// We need to use /s to ensure that spaces are parsed properly with cmd spawned content
args = [['/s', '/c', cmdArgs].join(' ')];
@@ -137,7 +137,7 @@ exports.spawn = function(cmd, args, opts) {
if (child.stdout) {
child.stdout.setEncoding('utf8');
child.stdout.on('data', function(data) {
child.stdout.on('data', function (data) {
capturedOut += data;
d.notify({'stdout': data});
});
@@ -145,7 +145,7 @@ exports.spawn = function(cmd, args, opts) {
if (child.stderr) {
child.stderr.setEncoding('utf8');
child.stderr.on('data', function(data) {
child.stderr.on('data', function (data) {
capturedErr += data;
d.notify({'stderr': data});
});
@@ -153,10 +153,10 @@ exports.spawn = function(cmd, args, opts) {
child.on('close', whenDone);
child.on('error', whenDone);
function whenDone(arg) {
function whenDone (arg) {
child.removeListener('close', whenDone);
child.removeListener('error', whenDone);
var code = typeof arg == 'number' ? arg : arg && arg.code;
var code = typeof arg === 'number' ? arg : arg && arg.code;
events.emit('verbose', 'Command finished with error code ' + code + ': ' + cmd + ' ' + args);
if (code === 0) {
@@ -181,10 +181,9 @@ exports.spawn = function(cmd, args, opts) {
return d.promise;
};
exports.maybeSpawn = function(cmd, args, opts) {
exports.maybeSpawn = function (cmd, args, opts) {
if (fs.existsSync(cmd)) {
return exports.spawn(cmd, args, opts);
}
return Q(null);
};

Some files were not shown because too many files have changed in this diff Show More