CB-5706 convert some of the bash scripts to nodejs (closes #118)
Project: http://git-wip-us.apache.org/repos/asf/cordova-ios/repo Commit: http://git-wip-us.apache.org/repos/asf/cordova-ios/commit/dd54d96b Tree: http://git-wip-us.apache.org/repos/asf/cordova-ios/tree/dd54d96b Diff: http://git-wip-us.apache.org/repos/asf/cordova-ios/diff/dd54d96b Branch: refs/heads/master Commit: dd54d96bd0eddb7f8967d0d1fcfb90f2635ed22a Parents: 5769d58 Author: Edna Morales <[email protected]> Authored: Mon Nov 3 15:18:05 2014 -0500 Committer: Shazron Abdullah <[email protected]> Committed: Wed Nov 19 16:26:28 2014 -0800 ---------------------------------------------------------------------- bin/apple_ios_version | 47 +- bin/apple_ios_version.bat | 26 + bin/apple_osx_version | 47 +- bin/apple_osx_version.bat | 26 + bin/apple_xcode_version | 47 +- bin/apple_xcode_version.bat | 26 + bin/create | 205 +- bin/create.bat | 26 + bin/lib/create.js | 255 +++ bin/lib/update.js | 72 + bin/lib/versions.js | 101 + bin/node_modules/q/CONTRIBUTING.md | 40 + bin/node_modules/q/LICENSE | 18 + bin/node_modules/q/README.md | 820 ++++++++ .../q/benchmark/compare-with-callbacks.js | 71 + bin/node_modules/q/benchmark/scenarios.js | 36 + bin/node_modules/q/package.json | 112 ++ bin/node_modules/q/q.js | 1904 ++++++++++++++++++ bin/node_modules/q/queue.js | 35 + bin/node_modules/which/LICENSE | 23 + bin/node_modules/which/README.md | 5 + bin/node_modules/which/bin/which | 14 + bin/node_modules/which/package.json | 31 + bin/node_modules/which/which.js | 104 + bin/replaces | 28 - bin/templates/scripts/cordova/version | 51 +- bin/templates/scripts/cordova/version.bat | 26 + bin/update | 95 +- bin/update.bat | 26 + bin/update_cordova_subproject | 137 +- bin/update_cordova_subproject.bat | 26 + 31 files changed, 4019 insertions(+), 461 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/dd54d96b/bin/apple_ios_version ---------------------------------------------------------------------- diff --git a/bin/apple_ios_version b/bin/apple_ios_version index 8a5aa7d..d397bb6 100755 --- a/bin/apple_ios_version +++ b/bin/apple_ios_version @@ -1,24 +1,27 @@ -#! /bin/sh +#!/usr/bin/env node -# -# 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. -# +/* + 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 -# outputs the highest level of iOS sdk installed -iOS_X_VERSIONS=$(xcodebuild -showsdks | sed -e '/./{H;$!d;}' -e 'x;/iOS SDKs/!d;' | grep -o '[0-9]*\.[0-9]* '); -echo $iOS_X_VERSIONS | tr " " "\n" | sort -g | tail -1; \ No newline at end of file + 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. +*/ + +var versions = require('./lib/versions.js'); + +versions.get_apple_ios_version().done(null, function(err) { + console.log(err); + process.exit(2); +}); http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/dd54d96b/bin/apple_ios_version.bat ---------------------------------------------------------------------- diff --git a/bin/apple_ios_version.bat b/bin/apple_ios_version.bat new file mode 100644 index 0000000..8219312 --- /dev/null +++ b/bin/apple_ios_version.bat @@ -0,0 +1,26 @@ +:: 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. + +@ECHO OFF +SET script="%~dp0apple_ios_version" +IF EXIST %script% ( + node %script% %* +) ELSE ( + ECHO. + ECHO ERROR: Could not find 'apple_ios_version' script in 'bin' folder, aborting...>&2 + EXIT /B 1 +) http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/dd54d96b/bin/apple_osx_version ---------------------------------------------------------------------- diff --git a/bin/apple_osx_version b/bin/apple_osx_version index 401e9cb..6e697ad 100755 --- a/bin/apple_osx_version +++ b/bin/apple_osx_version @@ -1,24 +1,27 @@ -#! /bin/sh +#!/usr/bin/env node -# -# 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. -# +/* + 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 -# outputs the highest level of OS X sdk installed -OS_X_VERSIONS=$(xcodebuild -showsdks | sed -e '/./{H;$!d;}' -e 'x;/OS X SDKs/!d;' | grep -o '[0-9]*\.[0-9]* '); -echo $OS_X_VERSIONS | tr " " "\n" | sort -g | tail -1; \ No newline at end of file + 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. +*/ + +var versions = require('./lib/versions.js'); + +versions.get_apple_osx_version().done(null, function(err) { + console.log(err); + process.exit(2); +}); http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/dd54d96b/bin/apple_osx_version.bat ---------------------------------------------------------------------- diff --git a/bin/apple_osx_version.bat b/bin/apple_osx_version.bat new file mode 100644 index 0000000..d2acec7 --- /dev/null +++ b/bin/apple_osx_version.bat @@ -0,0 +1,26 @@ +:: 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. + +@ECHO OFF +SET script="%~dp0apple_osx_version" +IF EXIST %script% ( + node %script% %* +) ELSE ( + ECHO. + ECHO ERROR: Could not find 'apple_osx_version' script in 'bin' folder, aborting...>&2 + EXIT /B 1 +) http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/dd54d96b/bin/apple_xcode_version ---------------------------------------------------------------------- diff --git a/bin/apple_xcode_version b/bin/apple_xcode_version index d459b30..5dfe501 100755 --- a/bin/apple_xcode_version +++ b/bin/apple_xcode_version @@ -1,24 +1,27 @@ -#! /bin/sh +#!/usr/bin/env node -# -# 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. -# +/* + 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 -# outputs which version of XCODE is installed -XCODEBUILD_VERSION=$(xcodebuild -version | head -n 1 | sed -e 's/Xcode //') -echo $XCODEBUILD_VERSION \ No newline at end of file + 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. +*/ + +var versions = require('./lib/versions.js'); + +versions.get_apple_xcode_version().done(null, function(err) { + console.log(err); + process.exit(2); +}); http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/dd54d96b/bin/apple_xcode_version.bat ---------------------------------------------------------------------- diff --git a/bin/apple_xcode_version.bat b/bin/apple_xcode_version.bat new file mode 100644 index 0000000..c19f911 --- /dev/null +++ b/bin/apple_xcode_version.bat @@ -0,0 +1,26 @@ +:: 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. + +@ECHO OFF +SET script="%~dp0apple_xcode_version" +IF EXIST %script% ( + node %script% %* +) ELSE ( + ECHO. + ECHO ERROR: Could not find 'apple_xcode_version' script in 'bin' folder, aborting...>&2 + EXIT /B 1 +) http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/dd54d96b/bin/create ---------------------------------------------------------------------- diff --git a/bin/create b/bin/create index b652adf..eeffc10 100755 --- a/bin/create +++ b/bin/create @@ -1,165 +1,42 @@ -#! /bin/sh - -# -# 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. -# - -# -# create a Cordova/iOS project -# -# USAGE -# ./create <path_to_new_project> <package_name> <project_name> -# -# EXAMPLE -# ./create ~/Desktop/radness org.apache.cordova.radness Radness -# -set -e - -function usage() { - echo "Usage: $0 [--shared] [--cli] <path_to_new_project> <package_name> <project_name> [<project_template_dir>]" - echo " --shared (optional): Link directly against the shared copy of the CordovaLib instead of a copy of it." - echo " --cli (optional): Use the CLI-project template." - echo " <path_to_new_project>: Path to your new Cordova iOS project" - echo " <package_name>: Package name, following reverse-domain style convention" - echo " <project_name>: Project name" - echo " <project_template_dir>: Path to project template (override)." - exit 1 +#!/usr/bin/env node + +/* + 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. +*/ + +/* + * create a Cordova/iOS project + * + * USAGE + * ./create <path_to_new_project> <package_name> <project_name> + * + * EXAMPLE + * ./create ~/Desktop/radness org.apache.cordova.radness Radness + */ + +var create = require('./lib/create'); + +if (process.argv.length < 3 || process.argv[2].indexOf('--help') > -1) { + create.createHelp(); +} +else { + create.createProject(process.argv).done(null, function(err) { + console.error('Failed to create project because of error: ' + err); + process.exit(2); + }); } - -USE_SHARED=0 -USE_CLI=0 -while [ $# -gt 0 ]; do - case "$1" in - --shared) USE_SHARED=1 ;; - --cli) USE_CLI=1 ;; - -*) ;; - *) - if [[ -z "$PROJECT_PATH" ]]; then - PROJECT_PATH="$1" - elif [[ -z "$PACKAGE" ]]; then - PACKAGE="$1" - elif [[ -z "$PROJECT_NAME" ]]; then - PROJECT_NAME="$1" - elif [[ -z "$PROJECT_TEMPLATE_DIR" ]]; then - PROJECT_TEMPLATE_DIR="$1" - else - echo "Too many arguments to $0". >&2 - usage - fi - esac - shift -done - -# check whether it is a proper create command (at least 3 arguments) -if [[ -z "$PROJECT_NAME" ]]; then - usage -fi - -# the two lines below are to get the current folder, and resolve symlinks -SCRIPT="$0" -# need this for relative symlinks -while [ -h "$SCRIPT" ] ; do - SCRIPT=`readlink "$SCRIPT"` -done - -BINDIR=$( cd "$( dirname "$SCRIPT" )" && pwd ) -CORDOVALIB_DIR="$BINDIR/../CordovaLib" -CDV_VER=$(cat "$CORDOVALIB_DIR/VERSION") - -PROJECT_PARENT=$(dirname "$PROJECT_PATH") -PROJECT_TEMPLATE_DIR=${PROJECT_TEMPLATE_DIR:-"$BINDIR/templates/project"} -SCRIPT_TEMPLATE_DIR=$BINDIR/templates/scripts - -"$BINDIR/check_reqs" || exit $? - -# check whether the project path exists and is not empty -if [ -d "$PROJECT_PATH" ]; then - if [ "$(ls -1A "$PROJECT_PATH")" ]; then - echo "\033[31mError: $PROJECT_PATH is not empty. Please specify an empty folder.\033[m" - exit 1 - fi -fi - -#Ensure the parent directory exists so cp -r will not fail -if [ ! -d "$PROJECT_PARENT" ]; then - echo "\033[31mError: $PROJECT_PARENT does not exist. Please specify an existing parent folder.\033[m" - exit 1 -fi - -mkdir -p "$PROJECT_PATH" -cp -r "$PROJECT_TEMPLATE_DIR/www" "$PROJECT_PATH"/www -cp "$CORDOVALIB_DIR/cordova.js" "$PROJECT_PATH/www/cordova.js" -if (($USE_CLI)); then - cp -r "$PROJECT_TEMPLATE_DIR/__CLI__.xcodeproj" "$PROJECT_PATH/$PROJECT_NAME.xcodeproj" -else - cp -r "$PROJECT_TEMPLATE_DIR/__NON-CLI__.xcodeproj" "$PROJECT_PATH/$PROJECT_NAME.xcodeproj" -fi -cp -r "$PROJECT_TEMPLATE_DIR/__PROJECT_NAME__" "$PROJECT_PATH/$PROJECT_NAME" - -R=$PROJECT_PATH/$PROJECT_NAME -mv "$R/__PROJECT_NAME__-Info.plist" "$R/$PROJECT_NAME-Info.plist" -mv "$R/__PROJECT_NAME__-Prefix.pch" "$R/$PROJECT_NAME-Prefix.pch" -mv "$R/gitignore" "$R/.gitignore" - -# replace __PROJECT_NAME__ and --ID-- with ACTIVITY and ID strings, respectively, in: -# -# - ./__PROJECT_NAME__.xcodeproj/project.pbxproj -# - ./__PROJECT_NAME__/Classes/AppDelegate.h -# - ./__PROJECT_NAME__/Classes/AppDelegate.m -# - ./__PROJECT_NAME__/Resources/main.m -# - ./__PROJECT_NAME__/Resources/__PROJECT_NAME__-info.plist -# - ./__PROJECT_NAME__/Resources/__PROJECT_NAME__-Prefix.plist - -PROJECT_NAME_ESC="${PROJECT_NAME//&/\\&}" -"$BINDIR/replaces" "$R.xcodeproj/project.pbxproj" __PROJECT_NAME__ "$PROJECT_NAME_ESC" -"$BINDIR/replaces" "$R/Classes/AppDelegate.h" __PROJECT_NAME__ "$PROJECT_NAME_ESC" -"$BINDIR/replaces" "$R/Classes/AppDelegate.m" __PROJECT_NAME__ "$PROJECT_NAME_ESC" -"$BINDIR/replaces" "$R/Classes/MainViewController.h" __PROJECT_NAME__ "$PROJECT_NAME_ESC" -"$BINDIR/replaces" "$R/Classes/MainViewController.m" __PROJECT_NAME__ "$PROJECT_NAME_ESC" -"$BINDIR/replaces" "$R/main.m" __PROJECT_NAME__ "$PROJECT_NAME_ESC" -"$BINDIR/replaces" "$R/$PROJECT_NAME-Info.plist" __PROJECT_NAME__ "$PROJECT_NAME_ESC" -"$BINDIR/replaces" "$R/$PROJECT_NAME-Prefix.pch" __PROJECT_NAME__ "$PROJECT_NAME_ESC" - -"$BINDIR/replaces" "$R/$PROJECT_NAME-Info.plist" --ID-- $PACKAGE - -if [[ $USE_SHARED = 1 ]]; then - # Make the sub-project reference to Cordova have the correct path. - "$BINDIR/update_cordova_subproject" "$R.xcodeproj/project.pbxproj" > /dev/null -else - # Copy in the CordovaLib directory. - mkdir -p "$PROJECT_PATH/CordovaLib/CordovaLib.xcodeproj" - cp "$R/.gitignore" "$PROJECT_PATH/" - cp -r "$BINDIR/../CordovaLib/Classes" "$PROJECT_PATH/CordovaLib" - cp "$BINDIR/../CordovaLib/VERSION" "$PROJECT_PATH/CordovaLib" - cp "$BINDIR/../CordovaLib/cordova.js" "$PROJECT_PATH/CordovaLib" - cp "$BINDIR/../CordovaLib/CordovaLib_Prefix.pch" "$PROJECT_PATH/CordovaLib" - cp "$BINDIR/../CordovaLib/CordovaLib.xcodeproj/project.pbxproj" "$PROJECT_PATH/CordovaLib/CordovaLib.xcodeproj" - # Make the sub-project reference to Cordova have the correct path. - "$BINDIR/update_cordova_subproject" "$R.xcodeproj/project.pbxproj" "$PROJECT_PATH/CordovaLib/CordovaLib.xcodeproj/project.pbxproj" > /dev/null -fi - -# Finally copy the scripts -cp -r "$SCRIPT_TEMPLATE_DIR"/* "$PROJECT_PATH/" - -# copy the check_reqs script -cp "$BINDIR/check_reqs" "$PROJECT_PATH"/cordova - -# copy the version scripts script -cp "$BINDIR/apple_ios_version" "$PROJECT_PATH"/cordova -cp "$BINDIR/apple_osx_version" "$PROJECT_PATH"/cordova -cp "$BINDIR/apple_xcode_version" "$PROJECT_PATH"/cordova http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/dd54d96b/bin/create.bat ---------------------------------------------------------------------- diff --git a/bin/create.bat b/bin/create.bat new file mode 100644 index 0000000..43172a6 --- /dev/null +++ b/bin/create.bat @@ -0,0 +1,26 @@ +:: 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. + +@ECHO OFF +SET script="%~dp0create" +IF EXIST %script% ( + node %script% %* +) ELSE ( + ECHO. + ECHO ERROR: Could not find 'create' script in 'bin' folder, aborting...>&2 + EXIT /B 1 +) http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/dd54d96b/bin/lib/create.js ---------------------------------------------------------------------- diff --git a/bin/lib/create.js b/bin/lib/create.js new file mode 100755 index 0000000..8c37c80 --- /dev/null +++ b/bin/lib/create.js @@ -0,0 +1,255 @@ +#!/usr/bin/env node + +/* + 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. +*/ + +var shell = require('shelljs'), + Q = require ('q'), + path = require('path'), + fs = require('fs'), + root = path.join(__dirname, '..', '..'); + +function createHelp() { + console.log("Usage: $0 [--shared] [--cli] <path_to_new_project> <package_name> <project_name> [<project_template_dir>]"); + console.log(" --shared (optional): Link directly against the shared copy of the CordovaLib instead of a copy of it."); + console.log(" --cli (optional): Use the CLI-project template."); + console.log(" <path_to_new_project>: Path to your new Cordova iOS project"); + console.log(" <package_name>: Package name, following reverse-domain style convention"); + console.log(" <project_name>: Project name"); + console.log(" <project_template_dir>: Path to project template (override)."); +} + +function updateSubprojectHelp() { + console.log('Updates the subproject path of the CordovaLib entry to point to this script\'s version of Cordova.') + console.log("Usage: CordovaVersion/bin/update_cordova_project path/to/your/app.xcodeproj [path/to/CordovaLib.xcodeproj]"); +} + +function AbsParentPath(_path) { + return path.resolve(path.dirname(_path)); +} + +function AbsProjectPath(relative_path) { + var absolute_path = path.resolve(relative_path); + if (/.pbxproj$/.test(absolute_path)) { + absolute_path = AbsParentPath(absolute_path); + } + else if (!(/.xcodeproj$/.test(absolute_path))) { + throw new Error('The following is not a valid path to an Xcode project' + absolute_path); + } + return absolute_path; +} + +function relpath(_path, start) { + start = start || process.cwd(); + return path.relative(path.resolve(start), path.resolve(_path)); +} + +/* + * Creates a new iOS project with the following options: + * + * - --shared (optional): Link directly against the shared copy of the CordovaLib instead of a copy of it + * - --cli (optional): Use the CLI-project template + * - <path_to_new_project>: Path to your new Cordova iOS project + * - <package_name>: Package name, following reverse-domain style convention + * - <project_name>: Project name + * - <project_template_dir>: Path to a project template (override) + * + */ + +exports.createProject = function(argv) { + var project_path, + package_name, + project_name, + project_template_dir, + use_shared = false, + use_cli = false; + + //get arguments + var args = argv.slice(2); + + //check and set arguments + if (args.length < 3) + { + createHelp(); + return Q.reject('Too few arguments'); + } + + for (var i = 0; i < args.length; i++) { + if (args[i] === '--shared') + use_shared = true; + else if (args[i] === '--cli') + use_cli = true; + else + { + if (!project_path) + project_path = args[i]; + else if (!package_name) + package_name = args[i]; + else if (!project_name) + project_name = args[i]; + else if (!project_template_dir) + project_template_dir = args[i]; + else + { + createHelp(); + return Q.reject('Too many arguments'); + } + } + } + + var bin_dir = path.join(root, 'bin'), + cordovalib_dir = path.join(root, 'CordovaLib'), + cordovalib_ver = fs.readFileSync(path.join(cordovalib_dir, 'VERSION'), 'utf-8').trim(); + project_parent = path.dirname(project_path), + project_template_dir = project_template_dir ? project_template_dir : path.join(bin_dir, 'templates', 'project'), + script_template_dir = path.join(bin_dir, 'templates', 'scripts'); + + //check that project path doesn't exist + if (fs.existsSync(project_path)) { + return Q.reject('Project already exists'); + } + + //check that parent directory does exist so cp -r will not fail + if (!fs.existsSync(project_parent)) { + return Q.reject(project_parent + ' does not exist. Please specify an existing parent folder'); + } + + //create the project directory and copy over files + shell.mkdir('-p', project_path); + shell.cp('-rf', path.join(project_template_dir, 'www'), project_path); + shell.cp('-f', path.join(cordovalib_dir, 'cordova.js'), path.join(project_path, 'www', 'cordova.js')); + if (use_cli) { + shell.cp('-rf', path.join(project_template_dir, '__CLI__.xcodeproj'), project_path); + shell.mv(path.join(project_path, '__CLI__.xcodeproj'), path.join(project_path, project_name+'.xcodeproj')); + } + else { + shell.cp('-rf', path.join(project_template_dir, '__NON-CLI__.xcodeproj'), project_path); + shell.mv(path.join(project_path, '__NON-CLI__.xcodeproj'), path.join(project_path, project_name+'.xcodeproj')); + } + shell.cp('-rf', path.join(project_template_dir, '__PROJECT_NAME__'), project_path); + shell.mv(path.join(project_path, '__PROJECT_NAME__'), path.join(project_path, project_name)); + + var r = path.join(project_path, project_name); + shell.mv(path.join(r, '__PROJECT_NAME__-Info.plist'), path.join(r, project_name+'-Info.plist')); + shell.mv(path.join(r, '__PROJECT_NAME__-Prefix.pch'), path.join(r, project_name+'-Prefix.pch')); + shell.mv(path.join(r, 'gitignore'), path.join(r, '.gitignore')); + + /*replace __PROJECT_NAME__ and --ID-- with ACTIVITY and ID strings, respectively, in: + * + * - ./__PROJECT_NAME__.xcodeproj/project.pbxproj + * - ./__PROJECT_NAME__/Classes/AppDelegate.h + * - ./__PROJECT_NAME__/Classes/AppDelegate.m + * - ./__PROJECT_NAME__/Resources/main.m + * - ./__PROJECT_NAME__/Resources/__PROJECT_NAME__-info.plist + * - ./__PROJECT_NAME__/Resources/__PROJECT_NAME__-Prefix.plist + */ + var project_name_esc = project_name.replace(/&/g, '\\&'); + shell.sed('-i', /__PROJECT_NAME__/g, project_name_esc, path.join(r+'.xcodeproj', 'project.pbxproj')); + shell.sed('-i', /__PROJECT_NAME__/g, project_name_esc, path.join(r, 'Classes', 'AppDelegate.h')); + shell.sed('-i', /__PROJECT_NAME__/g, project_name_esc, path.join(r, 'Classes', 'AppDelegate.m')); + shell.sed('-i', /__PROJECT_NAME__/g, project_name_esc, path.join(r, 'Classes', 'MainViewController.h')); + shell.sed('-i', /__PROJECT_NAME__/g, project_name_esc, path.join(r, 'Classes', 'MainViewController.m')); + shell.sed('-i', /__PROJECT_NAME__/g, project_name_esc, path.join(r, 'main.m')); + shell.sed('-i', /__PROJECT_NAME__/g, project_name_esc, path.join(r, project_name+'-Info.plist')); + shell.sed('-i', /__PROJECT_NAME__/g, project_name_esc, path.join(r, project_name+'-Prefix.pch')); + shell.sed('-i', /--ID--/g, package_name, path.join(r, project_name+'-Info.plist')); + + //CordovaLib stuff + if (use_shared) { + update_cordova_subproject([path.join(r+'.xcodeproj', 'project.pbxproj')]); + } + else { + //copy in the CordovaLib directory + shell.mkdir('-p', path.join(project_path, 'CordovaLib', 'CordovaLib.xcodeproj')); + shell.cp('-f', path.join(r, '.gitignore'), project_path); + shell.cp('-rf', path.join(bin_dir, '..', 'CordovaLib', 'Classes'), path.join(project_path, 'CordovaLib')); + shell.cp('-f', path.join(bin_dir, '..', 'CordovaLib', 'VERSION'), path.join(project_path, 'CordovaLib')); + shell.cp('-f', path.join(bin_dir, '..', 'CordovaLib', 'cordova.js'), path.join(project_path, 'CordovaLib')); + shell.cp('-f', path.join(bin_dir, '..', 'CordovaLib', 'CordovaLib_Prefix.pch'), path.join(project_path, 'CordovaLib')); + shell.cp('-f', path.join(bin_dir, '..', 'CordovaLib', 'CordovaLib.xcodeproj', 'project.pbxproj'), path.join(project_path, 'CordovaLib', 'CordovaLib.xcodeproj')); + update_cordova_subproject([path.join(r+'.xcodeproj', 'project.pbxproj'), path.join(project_path, 'CordovaLib', 'CordovaLib.xcodeproj', 'project.pbxproj')]); + } + + //Finally copy the scripts + shell.cp('-r', path.join(script_template_dir, '*'), project_path); + shell.cp('-r', path.join(bin_dir, 'node_modules'), path.join(project_path, 'cordova')); + + //copy the check_reqs script + shell.cp(path.join(bin_dir, 'check_reqs'), path.join(project_path, 'cordova')); + + //copy the version scripts script + shell.cp(path.join(bin_dir, 'apple_ios_version'), path.join(project_path, 'cordova')); + shell.cp(path.join(bin_dir, 'apple_osx_version'), path.join(project_path, 'cordova')); + shell.cp(path.join(bin_dir, 'apple_xcode_version'), path.join(project_path, 'cordova')); + shell.cp(path.join(bin_dir, 'lib', 'versions.js'), path.join(project_path, 'cordova', 'lib')); + + //Make scripts executable + shell.find(path.join(project_path, 'cordova')).forEach(function(entry) { + shell.chmod(755, entry); + }); + + return Q.resolve(); +} + + +function update_cordova_subproject(argv) { + if (argv.length < 1 || argv.length > 2) + { + updateSubprojectHelp(); + throw new Error('Usage error for update_cordova_subproject'); + } + + var projectPath = AbsProjectPath(argv[0]), + cordovaLibXcodePath; + if (argv.length < 2) { + cordovaLibXcodePath = path.join(root, 'CordovaLib', 'CordovaLib.xcodeproj'); + } + else { + cordovaLibXcodePath = AbsProjectPath(argv[1]); + } + + var parentProjectPath = AbsParentPath(projectPath), + subprojectPath = relpath(cordovaLibXcodePath, parentProjectPath), + REGEX = /(.+PBXFileReference.+wrapper.pb-project.+)(path = .+?;)(.*)(sourceTree.+;)(.+)/, + newLine, + lines = shell.grep('CordovaLib.xcodeproj', path.join(projectPath, 'project.pbxproj')), + found = false; + + subprojectPath = subprojectPath.replace(/\\/g, '/'); + lines = lines.split('\n'); + for (var i = 0; i < lines.length; ++i) { + if (lines[i].match(REGEX)) { + found = true; + newLine = lines[i].replace(/path = .+?;/, 'path = ' + subprojectPath + ';'); + newLine = newLine.replace(/sourceTree.+?;/, 'sourceTree = \"<group>\";'); + if (!newLine.match('name')) { + newLine = newLine.replace('path = ', 'name = CordovaLib.xcodeproj; path = '); + } + shell.sed('-i', lines[i], newLine, path.join(projectPath, 'project.pbxproj')); + } + } + + if (!found) { + throw new Error('Subproject: ' + subprojectPath + ' entry not found in project file'); + } +} + +exports.createHelp = createHelp; +exports.updateSubprojectHelp = updateSubprojectHelp; +exports.update_cordova_subproject = update_cordova_subproject; http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/dd54d96b/bin/lib/update.js ---------------------------------------------------------------------- diff --git a/bin/lib/update.js b/bin/lib/update.js new file mode 100755 index 0000000..e023e58 --- /dev/null +++ b/bin/lib/update.js @@ -0,0 +1,72 @@ +#!/usr/bin/env node + +/* + 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. +*/ +var shell = require('shelljs'), + path = require('path'), + fs = require('fs'), + ROOT = path.join(__dirname, '..', '..'); + +function setShellFatal(value, func) { + var oldVal = shell.config.fatal; + shell.config.fatal = value; + func(); + shell.config.fatal = oldVal; +} + +function copyJsAndCordovaLib(projectPath) { + shell.cp('-f', path.join(ROOT, 'CordovaLib', 'cordova.js'), path.join(projectPath, 'www')); + shell.rm('-rf', path.join(projectPath, 'CordovaLib')); + shell.cp('-r', path.join(ROOT, 'CordovaLib'), projectPath); + // Ensure no workspace files got copied over. + var entries = fs.readdirSync(path.join(projectPath, 'CordovaLib', 'CordovaLib.xcodeproj')); + entries.forEach(function(p) { + if (/.*xc.*/.test(p)) { + shell.rm('-rf', path.join(projectPath, 'CordovaLib', 'CordovaLib.xcodeproj', p)); + } + }); +} + +function copyScripts(projectPath) { + var srcScriptsDir = path.join(ROOT, 'bin', 'templates', 'scripts', 'cordova'); + var destScriptsDir = path.join(projectPath, 'cordova'); + // Delete old scripts directory. + shell.rm('-rf', destScriptsDir); + // Copy in the new ones. + shell.cp('-r', srcScriptsDir, projectPath); + shell.cp('-r', path.join(ROOT, 'bin', 'node_modules'), destScriptsDir); + shell.cp(path.join(ROOT, 'bin', 'check_reqs'), path.join(destScriptsDir, 'check_reqs')); + shell.cp(path.join(ROOT, 'bin', 'apple_ios_version'), destScriptsDir); + shell.cp(path.join(ROOT, 'bin', 'apple_osx_version'), destScriptsDir); + shell.cp(path.join(ROOT, 'bin', 'apple_xcode_version'), destScriptsDir); + shell.cp(path.join(ROOT, 'bin', 'lib', 'versions.js'), path.join(destScriptsDir, 'lib')); + // Make sure they are executable. + shell.find(destScriptsDir).forEach(function(entry) { + shell.chmod(755, entry); + }); +} + +exports.updateProject = function(projectPath) { + var version = fs.readFileSync(path.join(ROOT, 'CordovaLib', 'VERSION'), 'utf-8').trim(); + setShellFatal(true, function() { + copyJsAndCordovaLib(projectPath); + copyScripts(projectPath); + console.log('iOS project is now at version ' + version); + }); +}; http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/dd54d96b/bin/lib/versions.js ---------------------------------------------------------------------- diff --git a/bin/lib/versions.js b/bin/lib/versions.js new file mode 100755 index 0000000..9a8eaec --- /dev/null +++ b/bin/lib/versions.js @@ -0,0 +1,101 @@ +#!/usr/bin/env node + +/* + 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. +*/ + +var child_process = require('child_process'), + Q = require('q'); + +exports.get_apple_ios_version = function() { + var d = Q.defer(); + child_process.exec('xcodebuild -showsdks', function(error, stdout, stderr) { + if (error) { + d.reject(stderr); + } + else { + d.resolve(stdout); + } + }); + + return d.promise.then(function(output) { + var regex = /[0-9]*\.[0-9]*/, + versions = [], + regexIOS = /^iOS \d+/; + output = output.split('\n'); + for (var i = 0; i < output.length; i++) { + if (output[i].trim().match(regexIOS)) { + versions[versions.length] = parseFloat(output[i].match(regex)[0]); + } + } + versions.sort(); + console.log(versions[0]); + return Q(); + }, function(stderr) { + return Q.reject(stderr); + }); +} + +exports.get_apple_osx_version = function() { + var d = Q.defer(); + child_process.exec('xcodebuild -showsdks', function(error, stdout, stderr) { + if (error) { + d.reject(stderr); + } + else { + d.resolve(stdout); + } + }); + + return d.promise.then(function(output) { + var regex = /[0-9]*\.[0-9]*/, + versions = [], + regexOSX = /^OS X \d+/; + output = output.split('\n'); + for (var i = 0; i < output.length; i++) { + if (output[i].trim().match(regexOSX)) { + versions[versions.length] = parseFloat(output[i].match(regex)[0]); + } + } + versions.sort(); + console.log(versions[0]); + return Q(); + }, function(stderr) { + return Q.reject(stderr); + }); +} + +exports.get_apple_xcode_version = function() { + var d = Q.defer(); + child_process.exec('xcodebuild -version', function(error, stdout, stderr) { + if (error) { + d.reject(stderr); + } + else { + d.resolve(stdout); + } + }); + + return d.promise.then(function(output) { + output = output.split('\n'); + console.log(output[0].slice(6)); + return Q(); + }, function(stderr) { + return Q.reject(stderr); + }); +} http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/dd54d96b/bin/node_modules/q/CONTRIBUTING.md ---------------------------------------------------------------------- diff --git a/bin/node_modules/q/CONTRIBUTING.md b/bin/node_modules/q/CONTRIBUTING.md new file mode 100644 index 0000000..500ab17 --- /dev/null +++ b/bin/node_modules/q/CONTRIBUTING.md @@ -0,0 +1,40 @@ + +For pull requests: + +- Be consistent with prevalent style and design decisions. +- Add a Jasmine spec to `specs/q-spec.js`. +- Use `npm test` to avoid regressions. +- Run tests in `q-spec/run.html` in as many supported browsers as you + can find the will to deal with. +- Do not build minified versions; we do this each release. +- If you would be so kind, add a note to `CHANGES.md` in an + appropriate section: + + - `Next Major Version` if it introduces backward incompatibilities + to code in the wild using documented features. + - `Next Minor Version` if it adds a new feature. + - `Next Patch Version` if it fixes a bug. + +For releases: + +- Run `npm test`. +- Run tests in `q-spec/run.html` in a representative sample of every + browser under the sun. +- Run `npm run cover` and make sure you're happy with the results. +- Run `npm run minify` and be sure to commit the resulting `q.min.js`. +- Note the Gzipped size output by the previous command, and update + `README.md` if it has changed to 1 significant digit. +- Stash any local changes. +- Update `CHANGES.md` to reflect all changes in the differences + between `HEAD` and the previous tagged version. Give credit where + credit is due. +- Update `README.md` to address all new, non-experimental features. +- Update the API reference on the Wiki to reflect all non-experimental + features. +- Use `npm version major|minor|patch` to update `package.json`, + commit, and tag the new version. +- Use `npm publish` to send up a new release. +- Send an email to the q-continuum mailing list announcing the new + release and the notes from the change log. This helps folks + maintaining other package ecosystems. + http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/dd54d96b/bin/node_modules/q/LICENSE ---------------------------------------------------------------------- diff --git a/bin/node_modules/q/LICENSE b/bin/node_modules/q/LICENSE new file mode 100644 index 0000000..8a706b5 --- /dev/null +++ b/bin/node_modules/q/LICENSE @@ -0,0 +1,18 @@ +Copyright 2009â2014 Kristopher Michael Kowal. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/dd54d96b/bin/node_modules/q/README.md ---------------------------------------------------------------------- diff --git a/bin/node_modules/q/README.md b/bin/node_modules/q/README.md new file mode 100644 index 0000000..bdd4168 --- /dev/null +++ b/bin/node_modules/q/README.md @@ -0,0 +1,820 @@ +[](http://travis-ci.org/kriskowal/q) + +<a href="http://promises-aplus.github.com/promises-spec"> + <img src="http://promises-aplus.github.com/promises-spec/assets/logo-small.png" + align="right" alt="Promises/A+ logo" /> +</a> + +*This is Q version 1, from the `v1` branch in Git. This documentation applies to +the latest of both the version 1 and version 0.9 release trains. These releases +are stable. There will be no further releases of 0.9 after 0.9.7 which is nearly +equivalent to version 1.0.0. All further releases of `q@~1.0` will be backward +compatible. The version 2 release train introduces significant but +backward-incompatible changes and is experimental at this time.* + +If a function cannot return a value or throw an exception without +blocking, it can return a promise instead. A promise is an object +that represents the return value or the thrown exception that the +function may eventually provide. A promise can also be used as a +proxy for a [remote object][Q-Connection] to overcome latency. + +[Q-Connection]: https://github.com/kriskowal/q-connection + +On the first pass, promises can mitigate the â[Pyramid of +Doom][POD]â: the situation where code marches to the right faster +than it marches forward. + +[POD]: http://calculist.org/blog/2011/12/14/why-coroutines-wont-work-on-the-web/ + +```javascript +step1(function (value1) { + step2(value1, function(value2) { + step3(value2, function(value3) { + step4(value3, function(value4) { + // Do something with value4 + }); + }); + }); +}); +``` + +With a promise library, you can flatten the pyramid. + +```javascript +Q.fcall(promisedStep1) +.then(promisedStep2) +.then(promisedStep3) +.then(promisedStep4) +.then(function (value4) { + // Do something with value4 +}) +.catch(function (error) { + // Handle any error from all above steps +}) +.done(); +``` + +With this approach, you also get implicit error propagation, just like `try`, +`catch`, and `finally`. An error in `promisedStep1` will flow all the way to +the `catch` function, where itâs caught and handled. (Here `promisedStepN` is +a version of `stepN` that returns a promise.) + +The callback approach is called an âinversion of controlâ. +A function that accepts a callback instead of a return value +is saying, âDonât call me, Iâll call you.â. Promises +[un-invert][IOC] the inversion, cleanly separating the input +arguments from control flow arguments. This simplifies the +use and creation of APIâs, particularly variadic, +rest and spread arguments. + +[IOC]: http://www.slideshare.net/domenicdenicola/callbacks-promises-and-coroutines-oh-my-the-evolution-of-asynchronicity-in-javascript + + +## Getting Started + +The Q module can be loaded as: + +- A ``<script>`` tag (creating a ``Q`` global variable): ~2.5 KB minified and + gzipped. +- A Node.js and CommonJS module, available in [npm](https://npmjs.org/) as + the [q](https://npmjs.org/package/q) package +- An AMD module +- A [component](https://github.com/component/component) as ``microjs/q`` +- Using [bower](http://bower.io/) as ``q`` +- Using [NuGet](http://nuget.org/) as [Q](https://nuget.org/packages/q) + +Q can exchange promises with jQuery, Dojo, When.js, WinJS, and more. + +## Resources + +Our [wiki][] contains a number of useful resources, including: + +- A method-by-method [Q API reference][reference]. +- A growing [examples gallery][examples], showing how Q can be used to make + everything better. From XHR to database access to accessing the Flickr API, + Q is there for you. +- There are many libraries that produce and consume Q promises for everything + from file system/database access or RPC to templating. For a list of some of + the more popular ones, see [Libraries][]. +- If you want materials that introduce the promise concept generally, and the + below tutorial isn't doing it for you, check out our collection of + [presentations, blog posts, and podcasts][resources]. +- A guide for those [coming from jQuery's `$.Deferred`][jquery]. + +We'd also love to have you join the Q-Continuum [mailing list][]. + +[wiki]: https://github.com/kriskowal/q/wiki +[reference]: https://github.com/kriskowal/q/wiki/API-Reference +[examples]: https://github.com/kriskowal/q/wiki/Examples-Gallery +[Libraries]: https://github.com/kriskowal/q/wiki/Libraries +[resources]: https://github.com/kriskowal/q/wiki/General-Promise-Resources +[jquery]: https://github.com/kriskowal/q/wiki/Coming-from-jQuery +[mailing list]: https://groups.google.com/forum/#!forum/q-continuum + + +## Tutorial + +Promises have a ``then`` method, which you can use to get the eventual +return value (fulfillment) or thrown exception (rejection). + +```javascript +promiseMeSomething() +.then(function (value) { +}, function (reason) { +}); +``` + +If ``promiseMeSomething`` returns a promise that gets fulfilled later +with a return value, the first function (the fulfillment handler) will be +called with the value. However, if the ``promiseMeSomething`` function +gets rejected later by a thrown exception, the second function (the +rejection handler) will be called with the exception. + +Note that resolution of a promise is always asynchronous: that is, the +fulfillment or rejection handler will always be called in the next turn of the +event loop (i.e. `process.nextTick` in Node). This gives you a nice +guarantee when mentally tracing the flow of your code, namely that +``then`` will always return before either handler is executed. + +In this tutorial, we begin with how to consume and work with promises. We'll +talk about how to create them, and thus create functions like +`promiseMeSomething` that return promises, [below](#the-beginning). + + +### Propagation + +The ``then`` method returns a promise, which in this example, Iâm +assigning to ``outputPromise``. + +```javascript +var outputPromise = getInputPromise() +.then(function (input) { +}, function (reason) { +}); +``` + +The ``outputPromise`` variable becomes a new promise for the return +value of either handler. Since a function can only either return a +value or throw an exception, only one handler will ever be called and it +will be responsible for resolving ``outputPromise``. + +- If you return a value in a handler, ``outputPromise`` will get + fulfilled. + +- If you throw an exception in a handler, ``outputPromise`` will get + rejected. + +- If you return a **promise** in a handler, ``outputPromise`` will + âbecomeâ that promise. Being able to become a new promise is useful + for managing delays, combining results, or recovering from errors. + +If the ``getInputPromise()`` promise gets rejected and you omit the +rejection handler, the **error** will go to ``outputPromise``: + +```javascript +var outputPromise = getInputPromise() +.then(function (value) { +}); +``` + +If the input promise gets fulfilled and you omit the fulfillment handler, the +**value** will go to ``outputPromise``: + +```javascript +var outputPromise = getInputPromise() +.then(null, function (error) { +}); +``` + +Q promises provide a ``fail`` shorthand for ``then`` when you are only +interested in handling the error: + +```javascript +var outputPromise = getInputPromise() +.fail(function (error) { +}); +``` + +If you are writing JavaScript for modern engines only or using +CoffeeScript, you may use `catch` instead of `fail`. + +Promises also have a ``fin`` function that is like a ``finally`` clause. +The final handler gets called, with no arguments, when the promise +returned by ``getInputPromise()`` either returns a value or throws an +error. The value returned or error thrown by ``getInputPromise()`` +passes directly to ``outputPromise`` unless the final handler fails, and +may be delayed if the final handler returns a promise. + +```javascript +var outputPromise = getInputPromise() +.fin(function () { + // close files, database connections, stop servers, conclude tests +}); +``` + +- If the handler returns a value, the value is ignored +- If the handler throws an error, the error passes to ``outputPromise`` +- If the handler returns a promise, ``outputPromise`` gets postponed. The + eventual value or error has the same effect as an immediate return + value or thrown error: a value would be ignored, an error would be + forwarded. + +If you are writing JavaScript for modern engines only or using +CoffeeScript, you may use `finally` instead of `fin`. + +### Chaining + +There are two ways to chain promises. You can chain promises either +inside or outside handlers. The next two examples are equivalent. + +```javascript +return getUsername() +.then(function (username) { + return getUser(username) + .then(function (user) { + // if we get here without an error, + // the value returned here + // or the exception thrown here + // resolves the promise returned + // by the first line + }) +}); +``` + +```javascript +return getUsername() +.then(function (username) { + return getUser(username); +}) +.then(function (user) { + // if we get here without an error, + // the value returned here + // or the exception thrown here + // resolves the promise returned + // by the first line +}); +``` + +The only difference is nesting. Itâs useful to nest handlers if you +need to capture multiple input values in your closure. + +```javascript +function authenticate() { + return getUsername() + .then(function (username) { + return getUser(username); + }) + // chained because we will not need the user name in the next event + .then(function (user) { + return getPassword() + // nested because we need both user and password next + .then(function (password) { + if (user.passwordHash !== hash(password)) { + throw new Error("Can't authenticate"); + } + }); + }); +} +``` + + +### Combination + +You can turn an array of promises into a promise for the whole, +fulfilled array using ``all``. + +```javascript +return Q.all([ + eventualAdd(2, 2), + eventualAdd(10, 20) +]); +``` + +If you have a promise for an array, you can use ``spread`` as a +replacement for ``then``. The ``spread`` function âspreadsâ the +values over the arguments of the fulfillment handler. The rejection handler +will get called at the first sign of failure. That is, whichever of +the recived promises fails first gets handled by the rejection handler. + +```javascript +function eventualAdd(a, b) { + return Q.spread([a, b], function (a, b) { + return a + b; + }) +} +``` + +But ``spread`` calls ``all`` initially, so you can skip it in chains. + +```javascript +return getUsername() +.then(function (username) { + return [username, getUser(username)]; +}) +.spread(function (username, user) { +}); +``` + +The ``all`` function returns a promise for an array of values. When this +promise is fulfilled, the array contains the fulfillment values of the original +promises, in the same order as those promises. If one of the given promises +is rejected, the returned promise is immediately rejected, not waiting for the +rest of the batch. If you want to wait for all of the promises to either be +fulfilled or rejected, you can use ``allSettled``. + +```javascript +Q.allSettled(promises) +.then(function (results) { + results.forEach(function (result) { + if (result.state === "fulfilled") { + var value = result.value; + } else { + var reason = result.reason; + } + }); +}); +``` + + +### Sequences + +If you have a number of promise-producing functions that need +to be run sequentially, you can of course do so manually: + +```javascript +return foo(initialVal).then(bar).then(baz).then(qux); +``` + +However, if you want to run a dynamically constructed sequence of +functions, you'll want something like this: + +```javascript +var funcs = [foo, bar, baz, qux]; + +var result = Q(initialVal); +funcs.forEach(function (f) { + result = result.then(f); +}); +return result; +``` + +You can make this slightly more compact using `reduce`: + +```javascript +return funcs.reduce(function (soFar, f) { + return soFar.then(f); +}, Q(initialVal)); +``` + +Or, you could use th ultra-compact version: + +```javascript +return funcs.reduce(Q.when, Q()); +``` + +### Handling Errors + +One sometimes-unintuive aspect of promises is that if you throw an +exception in the fulfillment handler, it will not be be caught by the error +handler. + +```javascript +return foo() +.then(function (value) { + throw new Error("Can't bar."); +}, function (error) { + // We only get here if "foo" fails +}); +``` + +To see why this is, consider the parallel between promises and +``try``/``catch``. We are ``try``-ing to execute ``foo()``: the error +handler represents a ``catch`` for ``foo()``, while the fulfillment handler +represents code that happens *after* the ``try``/``catch`` block. +That code then needs its own ``try``/``catch`` block. + +In terms of promises, this means chaining your rejection handler: + +```javascript +return foo() +.then(function (value) { + throw new Error("Can't bar."); +}) +.fail(function (error) { + // We get here with either foo's error or bar's error +}); +``` + +### Progress Notification + +It's possible for promises to report their progress, e.g. for tasks that take a +long time like a file upload. Not all promises will implement progress +notifications, but for those that do, you can consume the progress values using +a third parameter to ``then``: + +```javascript +return uploadFile() +.then(function () { + // Success uploading the file +}, function (err) { + // There was an error, and we get the reason for error +}, function (progress) { + // We get notified of the upload's progress as it is executed +}); +``` + +Like `fail`, Q also provides a shorthand for progress callbacks +called `progress`: + +```javascript +return uploadFile().progress(function (progress) { + // We get notified of the upload's progress +}); +``` + +### The End + +When you get to the end of a chain of promises, you should either +return the last promise or end the chain. Since handlers catch +errors, itâs an unfortunate pattern that the exceptions can go +unobserved. + +So, either return it, + +```javascript +return foo() +.then(function () { + return "bar"; +}); +``` + +Or, end it. + +```javascript +foo() +.then(function () { + return "bar"; +}) +.done(); +``` + +Ending a promise chain makes sure that, if an error doesnât get +handled before the end, it will get rethrown and reported. + +This is a stopgap. We are exploring ways to make unhandled errors +visible without any explicit handling. + + +### The Beginning + +Everything above assumes you get a promise from somewhere else. This +is the common case. Every once in a while, you will need to create a +promise from scratch. + +#### Using ``Q.fcall`` + +You can create a promise from a value using ``Q.fcall``. This returns a +promise for 10. + +```javascript +return Q.fcall(function () { + return 10; +}); +``` + +You can also use ``fcall`` to get a promise for an exception. + +```javascript +return Q.fcall(function () { + throw new Error("Can't do it"); +}); +``` + +As the name implies, ``fcall`` can call functions, or even promised +functions. This uses the ``eventualAdd`` function above to add two +numbers. + +```javascript +return Q.fcall(eventualAdd, 2, 2); +``` + + +#### Using Deferreds + +If you have to interface with asynchronous functions that are callback-based +instead of promise-based, Q provides a few shortcuts (like ``Q.nfcall`` and +friends). But much of the time, the solution will be to use *deferreds*. + +```javascript +var deferred = Q.defer(); +FS.readFile("foo.txt", "utf-8", function (error, text) { + if (error) { + deferred.reject(new Error(error)); + } else { + deferred.resolve(text); + } +}); +return deferred.promise; +``` + +Note that a deferred can be resolved with a value or a promise. The +``reject`` function is a shorthand for resolving with a rejected +promise. + +```javascript +// this: +deferred.reject(new Error("Can't do it")); + +// is shorthand for: +var rejection = Q.fcall(function () { + throw new Error("Can't do it"); +}); +deferred.resolve(rejection); +``` + +This is a simplified implementation of ``Q.delay``. + +```javascript +function delay(ms) { + var deferred = Q.defer(); + setTimeout(deferred.resolve, ms); + return deferred.promise; +} +``` + +This is a simplified implementation of ``Q.timeout`` + +```javascript +function timeout(promise, ms) { + var deferred = Q.defer(); + Q.when(promise, deferred.resolve); + delay(ms).then(function () { + deferred.reject(new Error("Timed out")); + }); + return deferred.promise; +} +``` + +Finally, you can send a progress notification to the promise with +``deferred.notify``. + +For illustration, this is a wrapper for XML HTTP requests in the browser. Note +that a more [thorough][XHR] implementation would be in order in practice. + +[XHR]: https://github.com/montagejs/mr/blob/71e8df99bb4f0584985accd6f2801ef3015b9763/browser.js#L29-L73 + +```javascript +function requestOkText(url) { + var request = new XMLHttpRequest(); + var deferred = Q.defer(); + + request.open("GET", url, true); + request.onload = onload; + request.onerror = onerror; + request.onprogress = onprogress; + request.send(); + + function onload() { + if (request.status === 200) { + deferred.resolve(request.responseText); + } else { + deferred.reject(new Error("Status code was " + request.status)); + } + } + + function onerror() { + deferred.reject(new Error("Can't XHR " + JSON.stringify(url))); + } + + function onprogress(event) { + deferred.notify(event.loaded / event.total); + } + + return deferred.promise; +} +``` + +Below is an example of how to use this ``requestOkText`` function: + +```javascript +requestOkText("http://localhost:3000") +.then(function (responseText) { + // If the HTTP response returns 200 OK, log the response text. + console.log(responseText); +}, function (error) { + // If there's an error or a non-200 status code, log the error. + console.error(error); +}, function (progress) { + // Log the progress as it comes in. + console.log("Request progress: " + Math.round(progress * 100) + "%"); +}); +``` + +### The Middle + +If you are using a function that may return a promise, but just might +return a value if it doesnât need to defer, you can use the âstaticâ +methods of the Q library. + +The ``when`` function is the static equivalent for ``then``. + +```javascript +return Q.when(valueOrPromise, function (value) { +}, function (error) { +}); +``` + +All of the other methods on a promise have static analogs with the +same name. + +The following are equivalent: + +```javascript +return Q.all([a, b]); +``` + +```javascript +return Q.fcall(function () { + return [a, b]; +}) +.all(); +``` + +When working with promises provided by other libraries, you should +convert it to a Q promise. Not all promise libraries make the same +guarantees as Q and certainly donât provide all of the same methods. +Most libraries only provide a partially functional ``then`` method. +This thankfully is all we need to turn them into vibrant Q promises. + +```javascript +return Q($.ajax(...)) +.then(function () { +}); +``` + +If there is any chance that the promise you receive is not a Q promise +as provided by your library, you should wrap it using a Q function. +You can even use ``Q.invoke`` as a shorthand. + +```javascript +return Q.invoke($, 'ajax', ...) +.then(function () { +}); +``` + + +### Over the Wire + +A promise can serve as a proxy for another object, even a remote +object. There are methods that allow you to optimistically manipulate +properties or call functions. All of these interactions return +promises, so they can be chained. + +``` +direct manipulation using a promise as a proxy +-------------------------- ------------------------------- +value.foo promise.get("foo") +value.foo = value promise.put("foo", value) +delete value.foo promise.del("foo") +value.foo(...args) promise.post("foo", [args]) +value.foo(...args) promise.invoke("foo", ...args) +value(...args) promise.fapply([args]) +value(...args) promise.fcall(...args) +``` + +If the promise is a proxy for a remote object, you can shave +round-trips by using these functions instead of ``then``. To take +advantage of promises for remote objects, check out [Q-Connection][]. + +[Q-Connection]: https://github.com/kriskowal/q-connection + +Even in the case of non-remote objects, these methods can be used as +shorthand for particularly-simple fulfillment handlers. For example, you +can replace + +```javascript +return Q.fcall(function () { + return [{ foo: "bar" }, { foo: "baz" }]; +}) +.then(function (value) { + return value[0].foo; +}); +``` + +with + +```javascript +return Q.fcall(function () { + return [{ foo: "bar" }, { foo: "baz" }]; +}) +.get(0) +.get("foo"); +``` + + +### Adapting Node + +If you're working with functions that make use of the Node.js callback pattern, +where callbacks are in the form of `function(err, result)`, Q provides a few +useful utility functions for converting between them. The most straightforward +are probably `Q.nfcall` and `Q.nfapply` ("Node function call/apply") for calling +Node.js-style functions and getting back a promise: + +```javascript +return Q.nfcall(FS.readFile, "foo.txt", "utf-8"); +return Q.nfapply(FS.readFile, ["foo.txt", "utf-8"]); +``` + +If you are working with methods, instead of simple functions, you can easily +run in to the usual problems where passing a method to another functionâlike +`Q.nfcall`â"un-binds" the method from its owner. To avoid this, you can either +use `Function.prototype.bind` or some nice shortcut methods we provide: + +```javascript +return Q.ninvoke(redisClient, "get", "user:1:id"); +return Q.npost(redisClient, "get", ["user:1:id"]); +``` + +You can also create reusable wrappers with `Q.denodeify` or `Q.nbind`: + +```javascript +var readFile = Q.denodeify(FS.readFile); +return readFile("foo.txt", "utf-8"); + +var redisClientGet = Q.nbind(redisClient.get, redisClient); +return redisClientGet("user:1:id"); +``` + +Finally, if you're working with raw deferred objects, there is a +`makeNodeResolver` method on deferreds that can be handy: + +```javascript +var deferred = Q.defer(); +FS.readFile("foo.txt", "utf-8", deferred.makeNodeResolver()); +return deferred.promise; +``` + +### Long Stack Traces + +Q comes with optional support for âlong stack traces,â wherein the `stack` +property of `Error` rejection reasons is rewritten to be traced along +asynchronous jumps instead of stopping at the most recent one. As an example: + +```js +function theDepthsOfMyProgram() { + Q.delay(100).done(function explode() { + throw new Error("boo!"); + }); +} + +theDepthsOfMyProgram(); +``` + +usually would give a rather unhelpful stack trace looking something like + +``` +Error: boo! + at explode (/path/to/test.js:3:11) + at _fulfilled (/path/to/test.js:q:54) + at resolvedValue.promiseDispatch.done (/path/to/q.js:823:30) + at makePromise.promise.promiseDispatch (/path/to/q.js:496:13) + at pending (/path/to/q.js:397:39) + at process.startup.processNextTick.process._tickCallback (node.js:244:9) +``` + +But, if you turn this feature on by setting + +```js +Q.longStackSupport = true; +``` + +then the above code gives a nice stack trace to the tune of + +``` +Error: boo! + at explode (/path/to/test.js:3:11) +From previous event: + at theDepthsOfMyProgram (/path/to/test.js:2:16) + at Object.<anonymous> (/path/to/test.js:7:1) +``` + +Note how you can see the the function that triggered the async operation in the +stack trace! This is very helpful for debugging, as otherwise you end up getting +only the first line, plus a bunch of Q internals, with no sign of where the +operation started. + +This feature does come with somewhat-serious performance and memory overhead, +however. If you're working with lots of promises, or trying to scale a server +to many users, you should probably keep it off. But in development, go for it! + +## Tests + +You can view the results of the Q test suite [in your browser][tests]! + +[tests]: https://rawgithub.com/kriskowal/q/v1/spec/q-spec.html + +## License + +Copyright 2009â2014 Kristopher Michael Kowal +MIT License (enclosed) + http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/dd54d96b/bin/node_modules/q/benchmark/compare-with-callbacks.js ---------------------------------------------------------------------- diff --git a/bin/node_modules/q/benchmark/compare-with-callbacks.js b/bin/node_modules/q/benchmark/compare-with-callbacks.js new file mode 100644 index 0000000..97f1298 --- /dev/null +++ b/bin/node_modules/q/benchmark/compare-with-callbacks.js @@ -0,0 +1,71 @@ +"use strict"; + +var Q = require("../q"); +var fs = require("fs"); + +suite("A single simple async operation", function () { + bench("with an immediately-fulfilled promise", function (done) { + Q().then(done); + }); + + bench("with direct setImmediate usage", function (done) { + setImmediate(done); + }); + + bench("with direct setTimeout(â¦, 0)", function (done) { + setTimeout(done, 0); + }); +}); + +suite("A fs.readFile", function () { + var denodeified = Q.denodeify(fs.readFile); + + set("iterations", 1000); + set("delay", 1000); + + bench("directly, with callbacks", function (done) { + fs.readFile(__filename, done); + }); + + bench("with Q.nfcall", function (done) { + Q.nfcall(fs.readFile, __filename).then(done); + }); + + bench("with a Q.denodeify'ed version", function (done) { + denodeified(__filename).then(done); + }); + + bench("with manual usage of deferred.makeNodeResolver", function (done) { + var deferred = Q.defer(); + fs.readFile(__filename, deferred.makeNodeResolver()); + deferred.promise.then(done); + }); +}); + +suite("1000 operations in parallel", function () { + function makeCounter(desiredCount, ultimateCallback) { + var soFar = 0; + return function () { + if (++soFar === desiredCount) { + ultimateCallback(); + } + }; + } + var numberOfOps = 1000; + + bench("with immediately-fulfilled promises", function (done) { + var counter = makeCounter(numberOfOps, done); + + for (var i = 0; i < numberOfOps; ++i) { + Q().then(counter); + } + }); + + bench("with direct setImmediate usage", function (done) { + var counter = makeCounter(numberOfOps, done); + + for (var i = 0; i < numberOfOps; ++i) { + setImmediate(counter); + } + }); +}); http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/dd54d96b/bin/node_modules/q/benchmark/scenarios.js ---------------------------------------------------------------------- diff --git a/bin/node_modules/q/benchmark/scenarios.js b/bin/node_modules/q/benchmark/scenarios.js new file mode 100644 index 0000000..7c18564 --- /dev/null +++ b/bin/node_modules/q/benchmark/scenarios.js @@ -0,0 +1,36 @@ +"use strict"; + +var Q = require("../q"); + +suite("Chaining", function () { + var numberToChain = 1000; + + bench("Chaining many already-fulfilled promises together", function (done) { + var currentPromise = Q(); + for (var i = 0; i < numberToChain; ++i) { + currentPromise = currentPromise.then(function () { + return Q(); + }); + } + + currentPromise.then(done); + }); + + bench("Chaining and then fulfilling the end of the chain", function (done) { + var deferred = Q.defer(); + + var currentPromise = deferred.promise; + for (var i = 0; i < numberToChain; ++i) { + (function () { + var promiseToReturn = currentPromise; + currentPromise = Q().then(function () { + return promiseToReturn; + }); + }()); + } + + currentPromise.then(done); + + deferred.resolve(); + }); +}); http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/dd54d96b/bin/node_modules/q/package.json ---------------------------------------------------------------------- diff --git a/bin/node_modules/q/package.json b/bin/node_modules/q/package.json new file mode 100644 index 0000000..7b7f3c6 --- /dev/null +++ b/bin/node_modules/q/package.json @@ -0,0 +1,112 @@ +{ + "name": "q", + "version": "1.0.1", + "description": "A library for promises (CommonJS/Promises/A,B,D)", + "homepage": "https://github.com/kriskowal/q", + "author": { + "name": "Kris Kowal", + "email": "[email protected]", + "url": "https://github.com/kriskowal" + }, + "keywords": [ + "q", + "promise", + "promises", + "promises-a", + "promises-aplus", + "deferred", + "future", + "async", + "flow control", + "fluent", + "browser", + "node" + ], + "contributors": [ + { + "name": "Kris Kowal", + "email": "[email protected]", + "url": "https://github.com/kriskowal" + }, + { + "name": "Irakli Gozalishvili", + "email": "[email protected]", + "url": "http://jeditoolkit.com" + }, + { + "name": "Domenic Denicola", + "email": "[email protected]", + "url": "http://domenicdenicola.com" + } + ], + "bugs": { + "url": "http://github.com/kriskowal/q/issues" + }, + "license": { + "type": "MIT", + "url": "http://github.com/kriskowal/q/raw/master/LICENSE" + }, + "main": "q.js", + "repository": { + "type": "git", + "url": "git://github.com/kriskowal/q.git" + }, + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + }, + "dependencies": {}, + "devDependencies": { + "jshint": "~2.1.9", + "cover": "*", + "jasmine-node": "1.11.0", + "opener": "*", + "promises-aplus-tests": "1.x", + "grunt": "~0.4.1", + "grunt-cli": "~0.1.9", + "grunt-contrib-uglify": "~0.2.2", + "matcha": "~0.2.0" + }, + "scripts": { + "test": "jasmine-node spec && promises-aplus-tests spec/aplus-adapter", + "test-browser": "opener spec/q-spec.html", + "benchmark": "matcha", + "lint": "jshint q.js", + "cover": "cover run node_modules/jasmine-node/bin/jasmine-node spec && cover report html && opener cover_html/index.html", + "minify": "grunt", + "prepublish": "grunt" + }, + "overlay": { + "teleport": { + "dependencies": { + "system": ">=0.0.4" + } + } + }, + "directories": { + "test": "./spec" + }, + "_id": "[email protected]", + "dist": { + "shasum": "11872aeedee89268110b10a718448ffb10112a14", + "tarball": "http://registry.npmjs.org/q/-/q-1.0.1.tgz" + }, + "_from": "q@", + "_npmVersion": "1.4.4", + "_npmUser": { + "name": "kriskowal", + "email": "[email protected]" + }, + "maintainers": [ + { + "name": "kriskowal", + "email": "[email protected]" + }, + { + "name": "domenic", + "email": "[email protected]" + } + ], + "_shasum": "11872aeedee89268110b10a718448ffb10112a14", + "_resolved": "https://registry.npmjs.org/q/-/q-1.0.1.tgz" +} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
