forceios 9.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +110 -0
- package/forceios.js +36 -0
- package/package.json +30 -0
- package/shared/commandLineUtils.js +245 -0
- package/shared/configHelper.js +206 -0
- package/shared/constants.js +329 -0
- package/shared/createHelper.js +368 -0
- package/shared/example.userstore.json +24 -0
- package/shared/example.usersyncs.json +190 -0
- package/shared/jsonChecker.js +75 -0
- package/shared/oclifAdapter.js +199 -0
- package/shared/outputColors.js +9 -0
- package/shared/store.schema.json +38 -0
- package/shared/syncs.schema.json +295 -0
- package/shared/templateHelper.js +104 -0
- package/shared/utils.js +453 -0
package/README.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Salesforce Mobile SDK for iOS Package
|
|
2
|
+
|
|
3
|
+
The **forceios** npm package allows users to create iOS mobile applications to interface with the [Salesforce Platform](http://www.salesforce.com/platform/overview/), leveraging [Salesforce Mobile SDK for iOS](https://github.com/forcedotcom/SalesforceMobileSDK-iOS).
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
If you're new to mobile development or the force.com platform, you may want to start at the [Mobile SDK landing page](http://wiki.developerforce.com/page/Mobile_SDK). This page offers a variety of resources to help you determine the best technology path for creating your app, as well as many guides and blog posts detailing how to work with the Mobile SDK.
|
|
8
|
+
|
|
9
|
+
But assuming you're all read up, here's how to get started with the **forceios** package to create the starting point for your mobile application.
|
|
10
|
+
|
|
11
|
+
## Install the forceios Package
|
|
12
|
+
|
|
13
|
+
Because forceios is a command-line utility, we recommend installing it globally, so that it's easily accessible on your path:
|
|
14
|
+
|
|
15
|
+
sudo npm install forceios -g
|
|
16
|
+
|
|
17
|
+
You're of course welcome to install it locally as well:
|
|
18
|
+
|
|
19
|
+
npm install forceios
|
|
20
|
+
|
|
21
|
+
In local installations, you can access the forceios app at `[Install Directory]/node_modules/.bin/forceios`.
|
|
22
|
+
|
|
23
|
+
## Using forceios
|
|
24
|
+
|
|
25
|
+
For the rest of this document, we'll assume that `forceios` is on your path.
|
|
26
|
+
|
|
27
|
+
Typing `forceios` with no arguments gives you a breakdown of the usage:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
-> forceios
|
|
31
|
+
forceios: Tool for building an iOS native mobile application using Salesforce Mobile SDK
|
|
32
|
+
|
|
33
|
+
Usage:
|
|
34
|
+
|
|
35
|
+
# create an iOS native mobile application
|
|
36
|
+
forceios create
|
|
37
|
+
[--apptype=application type (native_swift or native, leave empty for native_swift)]
|
|
38
|
+
--appname=application name
|
|
39
|
+
--packagename=app package identifier (e.g. com.mycompany.myapp)
|
|
40
|
+
--organization=organization name (your company's/organization's name)
|
|
41
|
+
[--outputdir=output directory (leave empty for current directory)]
|
|
42
|
+
|
|
43
|
+
OR
|
|
44
|
+
|
|
45
|
+
# create an iOS native mobile application from a template
|
|
46
|
+
forceios createwithtemplate
|
|
47
|
+
--templaterepouri=template repo URI or Mobile SDK template name
|
|
48
|
+
--appname=application name
|
|
49
|
+
--packagename=app package identifier (e.g. com.mycompany.myapp)
|
|
50
|
+
--organization=organization name (your company's/organization's name)
|
|
51
|
+
[--outputdir=output directory (leave empty for current directory)]
|
|
52
|
+
|
|
53
|
+
OR
|
|
54
|
+
|
|
55
|
+
# list available Mobile SDK templates
|
|
56
|
+
forceios listtemplates
|
|
57
|
+
|
|
58
|
+
OR
|
|
59
|
+
|
|
60
|
+
# validate store or syncs configuration
|
|
61
|
+
forceios checkconfig
|
|
62
|
+
--configpath=path to store or syncs config to validate
|
|
63
|
+
--configtype=type of config to validate (store or syncs)
|
|
64
|
+
|
|
65
|
+
OR
|
|
66
|
+
|
|
67
|
+
# show version of Mobile SDK
|
|
68
|
+
forceios version
|
|
69
|
+
|
|
70
|
+
OR
|
|
71
|
+
|
|
72
|
+
forceios
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Note:** You can specify any or all of the arguments as command line options as specified in the usage. If you run `forceios create` with missing arguments, it prompts you for each missing option interactively.
|
|
76
|
+
|
|
77
|
+
Once the creation script completes, you'll have a fully functioning basic application of the type you specified. The new application has an Xcode workspace that you can peruse, run, and debug.
|
|
78
|
+
|
|
79
|
+
### forceios create options
|
|
80
|
+
|
|
81
|
+
**App Type:** \( *Optional* \) The type of application you wish to develop:
|
|
82
|
+
|
|
83
|
+
- **native\_swift** (default) — A fully native iOS application written in Swift
|
|
84
|
+
- **native** — A fully native iOS application written in Objective C
|
|
85
|
+
|
|
86
|
+
**App Name:** The name of your application
|
|
87
|
+
|
|
88
|
+
**App Package Identifier:** An identifier for your company, similar to a Java package (e.g. `com.acme.MobileApps`). This concatenates with the app name to form the unique identifier for your app in the App Store.
|
|
89
|
+
|
|
90
|
+
**Organization:** The name of your company or organization. For example, `Acme Widgets, Inc.`
|
|
91
|
+
|
|
92
|
+
**Output Directory:** \( *Optional* \) The folder where you want your app to be created.
|
|
93
|
+
|
|
94
|
+
## More information
|
|
95
|
+
|
|
96
|
+
- After your app has been created, you will see some on-screen instructions for next steps, such as building and running your app, importing the project into XCode, and changing the default Connected App (sample) configuration values to match your own Connected App.
|
|
97
|
+
|
|
98
|
+
- You can find the `forcedroid` npm package [here](https://npmjs.org/package/forcedroid) to develop Mobile SDK apps for Android.
|
|
99
|
+
|
|
100
|
+
- You can find the `forcehybrid` npm package [here](https://npmjs.org/package/forcehybrid) to develop Mobile SDK hybrid apps for iOS and Android.
|
|
101
|
+
|
|
102
|
+
- You can find the `forcereact` npm package [here](https://npmjs.org/package/forcereact) to develop Mobile SDK react native apps for iOS and Android.
|
|
103
|
+
|
|
104
|
+
- The Salesforce Mobile SDK for iOS source repository lives [here](https://github.com/forcedotcom/SalesforceMobileSDK-iOS).
|
|
105
|
+
|
|
106
|
+
- The Salesforce Mobile SDK for Android source repository lives [here](https://github.com/forcedotcom/SalesforceMobileSDK-Android).
|
|
107
|
+
|
|
108
|
+
- See [our developerforce site](http://wiki.developerforce.com/page/Mobile_SDK) for more information about how you can leverage Salesforce Mobile SDK with the force.com platform.
|
|
109
|
+
|
|
110
|
+
- If you would like to make suggestions, have questions, or encounter any issues, we'd love to hear from you. Post any feedback you have on [Salesforce StackExchange](https://salesforce.stackexchange.com/questions/tagged/mobilesdk).
|
package/forceios.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Copyright (c) 2016-present, salesforce.com, inc.
|
|
5
|
+
* All rights reserved.
|
|
6
|
+
* Redistribution and use of this software in source and binary forms, with or
|
|
7
|
+
* without modification, are permitted provided that the following conditions
|
|
8
|
+
* are met:
|
|
9
|
+
* - Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
* list of conditions and the following disclaimer.
|
|
11
|
+
* - Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
* this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
* and/or other materials provided with the distribution.
|
|
14
|
+
* - Neither the name of salesforce.com, inc. nor the names of its contributors
|
|
15
|
+
* may be used to endorse or promote products derived from this software without
|
|
16
|
+
* specific prior written permission of salesforce.com, inc.
|
|
17
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
18
|
+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
19
|
+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
20
|
+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
21
|
+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
22
|
+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
23
|
+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
24
|
+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
25
|
+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
26
|
+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
27
|
+
* POSSIBILITY OF SUCH DAMAGE.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
// Dependencies
|
|
31
|
+
var SDK = require('./shared/constants'),
|
|
32
|
+
createHelper = require('./shared/createHelper');
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
// Do everything
|
|
36
|
+
createHelper.createApp(SDK.forceclis.forceios);
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "forceios",
|
|
3
|
+
"version": "9.2.0",
|
|
4
|
+
"description": "Utilities for creating mobile apps based on the Salesforce Mobile SDK for iOS",
|
|
5
|
+
"keywords": [ "mobilesdk", "ios", "salesforce", "mobile", "sdk" ],
|
|
6
|
+
"homepage": "https://github.com/forcedotcom/SalesforceMobileSDK-iOS",
|
|
7
|
+
"bugs": "https://github.com/forcedotcom/SalesforceMobileSDK-iOS/issues",
|
|
8
|
+
"licenses" : [
|
|
9
|
+
{ "type": "Salesforce.com Mobile SDK License", "url": "https://github.com/forcedotcom/SalesforceMobileSDK-iOS/blob/master/LICENSE.md" }
|
|
10
|
+
],
|
|
11
|
+
"os" : [ "darwin" ],
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": ">=7.0.0"
|
|
14
|
+
},
|
|
15
|
+
"bin" : { "forceios" : "forceios.js" },
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"shelljs": "0.8.4",
|
|
18
|
+
"ajv": "^6.10.2",
|
|
19
|
+
"jsonlint": "^1.6.3"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type" : "git",
|
|
23
|
+
"url" : "https://github.com/forcedotcom/SalesforceMobileSDK-Package.git"
|
|
24
|
+
},
|
|
25
|
+
"author": { "name": "Kevin Hawkins", "email": "khawkins@salesforce.com" },
|
|
26
|
+
"contributors": [
|
|
27
|
+
{ "name": "Wolfgang Mathurin", "email": "wmathurin@salesforce.com" },
|
|
28
|
+
{ "name": "Bharath Hariharan", "email": "bhariharan@salesforce.com" }
|
|
29
|
+
]
|
|
30
|
+
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2013-present, salesforce.com, inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* Redistribution and use of this software in source and binary forms, with or
|
|
5
|
+
* without modification, are permitted provided that the following conditions
|
|
6
|
+
* are met:
|
|
7
|
+
* - Redistributions of source code must retain the above copyright notice, this
|
|
8
|
+
* list of conditions and the following disclaimer.
|
|
9
|
+
* - Redistributions in binary form must reproduce the above copyright notice,
|
|
10
|
+
* this list of conditions and the following disclaimer in the documentation
|
|
11
|
+
* and/or other materials provided with the distribution.
|
|
12
|
+
* - Neither the name of salesforce.com, inc. nor the names of its contributors
|
|
13
|
+
* may be used to endorse or promote products derived from this software without
|
|
14
|
+
* specific prior written permission of salesforce.com, inc.
|
|
15
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
16
|
+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
17
|
+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
18
|
+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
19
|
+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
20
|
+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
21
|
+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
22
|
+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
23
|
+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
24
|
+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
25
|
+
* POSSIBILITY OF SUCH DAMAGE.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
// Helper module for parsing the command line arguments to our package front-end.
|
|
29
|
+
|
|
30
|
+
var readline = require('readline');
|
|
31
|
+
var outputColors = require('./outputColors');
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Parses an array of command line arguments, each of the form '--argName=argValue', or
|
|
35
|
+
* optionally, '--argName'. Treats '--argName=' as '--argName'.
|
|
36
|
+
*
|
|
37
|
+
* @param {Array} argsArray The array of String arguments of the specified format.
|
|
38
|
+
* @return A map in the form of { 'argName': 'argValue' [, ...] }
|
|
39
|
+
*/
|
|
40
|
+
var parseArgs = function(argsArray) {
|
|
41
|
+
var argMap = {};
|
|
42
|
+
for (var i = 0; i < argsArray.length; i++) {
|
|
43
|
+
var fullArg = argsArray[i];
|
|
44
|
+
var argSplitRegExp = /^--([^=]+)(=(.*))?$/;
|
|
45
|
+
if (!argSplitRegExp.test(fullArg)) {
|
|
46
|
+
throw new Error('Illegal argument: ' + fullArg);
|
|
47
|
+
}
|
|
48
|
+
var argName = fullArg.replace(argSplitRegExp, "$1");
|
|
49
|
+
argName = argName.toLocaleLowerCase();
|
|
50
|
+
var argVal = fullArg.replace(argSplitRegExp, "$3");
|
|
51
|
+
argMap[argName] = argVal;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return argMap;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* A method to process command line arguments interactively. Any arguments that were not specified
|
|
59
|
+
* on the command line will be prompted for on stdin, for the user to configure.
|
|
60
|
+
*
|
|
61
|
+
* @param {Array} argsArray The array of command line arguments (if any), of the form --argName=argValue, or --argName.
|
|
62
|
+
* @param {ArgProcessorList} argProcessorList A list of arguments and their various processing characteristics.
|
|
63
|
+
* @param {Function} callback The callback method to invoke once all arguments have been processed.
|
|
64
|
+
*/
|
|
65
|
+
var processArgsInteractive = function(argsArray, argProcessorList, callback) {
|
|
66
|
+
// Get any initial arguments from the command line.
|
|
67
|
+
var argsMap = parseArgs(argsArray);
|
|
68
|
+
|
|
69
|
+
processArgsInteractiveHelper(argsMap, argProcessorList, callback, 0);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* (Recursive) helper function for processArgsInteractive.
|
|
74
|
+
* @param argsMap A map of arguments in the form of { 'argName': 'argValue' [, ...] }.
|
|
75
|
+
* @param {ArgProcessorList} argProcessorList A list of arguments and their various processing characteristics.
|
|
76
|
+
* @param {Function} callback The callback method to invoke once all arguments have been processed.
|
|
77
|
+
* @param {Number} currentIndex The index of the current argument being processed.
|
|
78
|
+
*/
|
|
79
|
+
var processArgsInteractiveHelper = function(argsMap, argProcessorList, callback, currentIndex) {
|
|
80
|
+
if (currentIndex === argProcessorList.processorList.length)
|
|
81
|
+
return callback(argsMap);
|
|
82
|
+
|
|
83
|
+
var argProcessor = argProcessorList.processorList[currentIndex];
|
|
84
|
+
var initialArgValue = argsMap[argProcessor.argName];
|
|
85
|
+
|
|
86
|
+
// Arg preprocessors determine whether an arg should even be presented as an option.
|
|
87
|
+
if (typeof argProcessor.preprocessorFunction === 'undefined') {
|
|
88
|
+
// By default, process each argument.
|
|
89
|
+
processArgument(initialArgValue, argsMap, argProcessor, function(resultArgValue) {
|
|
90
|
+
argsMap[argProcessor.argName] = resultArgValue;
|
|
91
|
+
processArgsInteractiveHelper(argsMap, argProcessorList, callback, currentIndex + 1);
|
|
92
|
+
});
|
|
93
|
+
} else {
|
|
94
|
+
var shouldProcessArgument = argProcessor.preprocessorFunction(argsMap);
|
|
95
|
+
if (shouldProcessArgument) {
|
|
96
|
+
processArgument(initialArgValue, argsMap, argProcessor, function(resultArgValue) {
|
|
97
|
+
argsMap[argProcessor.argName] = resultArgValue;
|
|
98
|
+
processArgsInteractiveHelper(argsMap, argProcessorList, callback, currentIndex + 1);
|
|
99
|
+
});
|
|
100
|
+
} else {
|
|
101
|
+
// If the user specified a value already, warn them that it won't be used.
|
|
102
|
+
if (typeof initialArgValue !== 'undefined') {
|
|
103
|
+
console.log(outputColors.yellow + 'WARNING: ' + outputColors.reset
|
|
104
|
+
+ '\'' + argProcessor.argName + '\' is not a valid argument in this scenario, and its value will be ignored.');
|
|
105
|
+
argsMap[argProcessor.argName] = undefined;
|
|
106
|
+
}
|
|
107
|
+
processArgsInteractiveHelper(argsMap, argProcessorList, callback, currentIndex + 1);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Evaluates an argument value against its argument processor
|
|
114
|
+
* @param {String} argValue The specified value of the argument.
|
|
115
|
+
* @param argsMap A map of arguments in the form of { 'argName': 'argValue' [, ...] }.
|
|
116
|
+
* @param {ArgProcessor} argProcessor The argument processor, used to evaluate the arg value.
|
|
117
|
+
* @param {Function} postProcessingCallback The callback to invoke once a valid arg value has been determined.
|
|
118
|
+
*/
|
|
119
|
+
var processArgument = function(argValue, argsMap, argProcessor, postProcessingCallback) {
|
|
120
|
+
// NB: If argValue is undefined (i.e. no initial command line input), even for optional arguments the user must
|
|
121
|
+
// be prompted at least once.
|
|
122
|
+
var processingResult = null;
|
|
123
|
+
if (typeof argValue !== 'undefined') {
|
|
124
|
+
processingResult = argProcessor.processorFunction(argValue, argsMap);
|
|
125
|
+
if (!(processingResult instanceof ArgProcessorOutput)) {
|
|
126
|
+
throw new Error ('Expecting processing result of type ArgProcessorOutput. Got \'' + (typeof processingResult) + '\'.');
|
|
127
|
+
}
|
|
128
|
+
if (processingResult.isValid) {
|
|
129
|
+
if (typeof processingResult.replacementValue !== 'undefined') {
|
|
130
|
+
return postProcessingCallback(processingResult.replacementValue);
|
|
131
|
+
} else {
|
|
132
|
+
return postProcessingCallback(argValue);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Otherwise, arg value was either not present, or not valid. If the user actually entered an invalid value, show the error prompt.
|
|
138
|
+
if (processingResult && !processingResult.isValid) {
|
|
139
|
+
if (typeof processingResult.message !== 'undefined') {
|
|
140
|
+
console.log(outputColors.red + processingResult.message + outputColors.reset);
|
|
141
|
+
} else {
|
|
142
|
+
console.log(outputColors.red + 'Invalid value for \'' + argProcessor.argName + '\'.' + outputColors.reset);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// If we have an inputPrompt, prompt the user
|
|
146
|
+
if (argProcessor.inputPrompt) {
|
|
147
|
+
var rl = readline.createInterface({
|
|
148
|
+
input: process.stdin,
|
|
149
|
+
output: process.stdout
|
|
150
|
+
});
|
|
151
|
+
rl.question(argProcessor.inputPrompt + ' ', function(answer) {
|
|
152
|
+
rl.close();
|
|
153
|
+
processArgument(answer, argsMap, argProcessor, postProcessingCallback);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
processArgument('', argsMap, argProcessor, postProcessingCallback);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Creates an instance of an ArgProcessorList.
|
|
163
|
+
*
|
|
164
|
+
* @constructor
|
|
165
|
+
*/
|
|
166
|
+
var ArgProcessorList = function() {
|
|
167
|
+
this.processorList = [];
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Adds an ArgProcessor to the list of processors.
|
|
172
|
+
*
|
|
173
|
+
* @param {String} argName The name of the argument.
|
|
174
|
+
* @param {String} inputPrompt The prompt to show the user, when interactively configuring the arg value. If null, the user will not be prompted interactively.
|
|
175
|
+
* @param {Function} processorFunction The function used to validate the arg value.
|
|
176
|
+
* @param {Function} preprocessorFunction An optional function that can be used to determine whether the argument should be configured.
|
|
177
|
+
* @return {ArgProcessorList} The updated ArgProcessorList, for chaining calls.
|
|
178
|
+
*/
|
|
179
|
+
ArgProcessorList.prototype.addArgProcessor = function(argName, inputPrompt, processorFunction, preprocessorFunction) {
|
|
180
|
+
var argProcessor = new ArgProcessor(argName, inputPrompt, processorFunction, preprocessorFunction);
|
|
181
|
+
this.processorList.push(argProcessor);
|
|
182
|
+
return this;
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Creates an instance of the ArgProcessor object.
|
|
187
|
+
*
|
|
188
|
+
* @constructor
|
|
189
|
+
* @param {String} argName The name of the argument.
|
|
190
|
+
* @param {String} inputPrompt The prompt to show the user, when interactively configuring the arg value. If null, the user will not be prompted interactively.
|
|
191
|
+
* @param {Function} processorFunction The function used to validate the arg value.
|
|
192
|
+
* @param {Function} preprocessorFunction An optional function that can be used to determine whether the argument should be configured.
|
|
193
|
+
*/
|
|
194
|
+
var ArgProcessor = function(argName, inputPrompt, processorFunction, preprocessorFunction) {
|
|
195
|
+
if ((typeof argName !== 'string') || argName.trim() === '') {
|
|
196
|
+
throw new Error('Invalid value for argName: \'' + argName + '\'.');
|
|
197
|
+
}
|
|
198
|
+
if (typeof processorFunction !== 'function') {
|
|
199
|
+
throw new Error('processorFunction should be a function.');
|
|
200
|
+
}
|
|
201
|
+
if ((typeof preprocessorFunction !== 'undefined') && (typeof preprocessorFunction !== 'function')) {
|
|
202
|
+
throw new Error('If defined, preprocessorFunction should be a function.');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
this.argName = argName;
|
|
206
|
+
this.inputPrompt = inputPrompt;
|
|
207
|
+
this.processorFunction = processorFunction;
|
|
208
|
+
this.preprocessorFunction = preprocessorFunction;
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Creates an instance of the ArgProcessorOutput object, used to return the result of processing an arg value.
|
|
213
|
+
*
|
|
214
|
+
* @constructor
|
|
215
|
+
* @param {Boolean} isValid Whether or not the arg value was a valid value.
|
|
216
|
+
* @param {String} messageOrReplacementValue Optional value. If arg is not valid, a message explaining the failure. If valid, an optional replacment value for the argument.
|
|
217
|
+
*/
|
|
218
|
+
var ArgProcessorOutput = function(isValid, messageOrReplacementValue) {
|
|
219
|
+
|
|
220
|
+
// If an argument is valid (isValid == true), there won't need to be a message
|
|
221
|
+
// assocated with it. And if it's not valid, you wouldn't provide a replacement
|
|
222
|
+
// value for it (at least in the workflow as defined—replacement values imply a
|
|
223
|
+
// "valid" alternative argument value). Hence the single second argument serving
|
|
224
|
+
// two masters. But we'll normalize the value for consumption. NB: In either
|
|
225
|
+
// use case, this argument can be optional.
|
|
226
|
+
|
|
227
|
+
if (typeof isValid !== 'boolean') {
|
|
228
|
+
throw new Error('isValid should be a boolean value.');
|
|
229
|
+
}
|
|
230
|
+
this.isValid = isValid;
|
|
231
|
+
|
|
232
|
+
if (typeof messageOrReplacementValue !== 'undefined') {
|
|
233
|
+
if (this.isValid)
|
|
234
|
+
this.replacementValue = messageOrReplacementValue;
|
|
235
|
+
else
|
|
236
|
+
this.message = messageOrReplacementValue;
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
// Exports
|
|
241
|
+
|
|
242
|
+
module.exports.parseArgs = parseArgs;
|
|
243
|
+
module.exports.ArgProcessorList = ArgProcessorList;
|
|
244
|
+
module.exports.processArgsInteractive = processArgsInteractive;
|
|
245
|
+
module.exports.ArgProcessorOutput = ArgProcessorOutput;
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2016-present, salesforce.com, inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* Redistribution and use of this software in source and binary forms, with or
|
|
5
|
+
* without modification, are permitted provided that the following conditions
|
|
6
|
+
* are met:
|
|
7
|
+
* - Redistributions of source code must retain the above copyright notice, this
|
|
8
|
+
* list of conditions and the following disclaimer.
|
|
9
|
+
* - Redistributions in binary form must reproduce the above copyright notice,
|
|
10
|
+
* this list of conditions and the following disclaimer in the documentation
|
|
11
|
+
* and/or other materials provided with the distribution.
|
|
12
|
+
* - Neither the name of salesforce.com, inc. nor the names of its contributors
|
|
13
|
+
* may be used to endorse or promote products derived from this software without
|
|
14
|
+
* specific prior written permission of salesforce.com, inc.
|
|
15
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
16
|
+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
17
|
+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
18
|
+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
19
|
+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
20
|
+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
21
|
+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
22
|
+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
23
|
+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
24
|
+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
25
|
+
* POSSIBILITY OF SUCH DAMAGE.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
// Dependencies
|
|
29
|
+
var path = require('path'),
|
|
30
|
+
shelljs = require('shelljs'),
|
|
31
|
+
SDK = require('./constants'),
|
|
32
|
+
COLOR = require('./outputColors'),
|
|
33
|
+
commandLineUtils = require('./commandLineUtils'),
|
|
34
|
+
logInfo = require('./utils').logInfo,
|
|
35
|
+
getTemplates = require('./templateHelper').getTemplates,
|
|
36
|
+
validateJson = require('./jsonChecker').validateJson;
|
|
37
|
+
|
|
38
|
+
function applyCli(f, cli) {
|
|
39
|
+
return typeof f === 'function' ? f(cli): f;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function getArgsExpanded(cli, commandName) {
|
|
43
|
+
var argNames = applyCli(SDK.commands[commandName].args, cli);
|
|
44
|
+
return argNames
|
|
45
|
+
.map(argName => SDK.args[argName])
|
|
46
|
+
.map(arg =>
|
|
47
|
+
({
|
|
48
|
+
name: arg.name,
|
|
49
|
+
'char': arg.char,
|
|
50
|
+
description: applyCli(arg.description, cli),
|
|
51
|
+
longDescription: applyCli(arg.longDescription, cli),
|
|
52
|
+
prompt: applyCli(arg.prompt, cli),
|
|
53
|
+
error: applyCli(arg.error, cli),
|
|
54
|
+
validate: applyCli(arg.validate, cli),
|
|
55
|
+
promptIf: arg.promptIf,
|
|
56
|
+
required: arg.required === undefined ? true : arg.required,
|
|
57
|
+
hasValue: arg.hasValue === undefined ? true : arg.hasValue,
|
|
58
|
+
hidden: applyCli(arg.hidden, cli),
|
|
59
|
+
type: arg.type
|
|
60
|
+
})
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function getCommandExpanded(cli, commandName) {
|
|
66
|
+
var command = SDK.commands[commandName];
|
|
67
|
+
return {
|
|
68
|
+
name: command.name,
|
|
69
|
+
args: getArgsExpanded(cli, commandName),
|
|
70
|
+
description: applyCli(command.description, cli),
|
|
71
|
+
longDescription: applyCli(command.longDescription, cli),
|
|
72
|
+
help: applyCli(command.help, cli)
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function readConfig(args, cli, handler) {
|
|
77
|
+
var commandLineArgs = args.slice(2, args.length);
|
|
78
|
+
var commandName = commandLineArgs.shift();
|
|
79
|
+
commandName = commandName ? commandName.toLowerCase() : commandName;
|
|
80
|
+
|
|
81
|
+
var processorList = null;
|
|
82
|
+
|
|
83
|
+
switch (commandName || '') {
|
|
84
|
+
case SDK.commands.version.name:
|
|
85
|
+
printVersion(cli);
|
|
86
|
+
process.exit(0);
|
|
87
|
+
break;
|
|
88
|
+
case SDK.commands.create.name:
|
|
89
|
+
case SDK.commands.createwithtemplate.name:
|
|
90
|
+
processorList = buildArgsProcessorList(cli, commandName);
|
|
91
|
+
commandLineUtils.processArgsInteractive(commandLineArgs, processorList, handler);
|
|
92
|
+
break;
|
|
93
|
+
case SDK.commands.checkconfig.name:
|
|
94
|
+
processorList = buildArgsProcessorList(cli, commandName);
|
|
95
|
+
commandLineUtils.processArgsInteractive(commandLineArgs, processorList, function (config) {
|
|
96
|
+
validateJson(config.configpath, config.configtype);
|
|
97
|
+
});
|
|
98
|
+
break;
|
|
99
|
+
case SDK.commands.listtemplates.name:
|
|
100
|
+
listTemplates(cli);
|
|
101
|
+
process.exit(0);
|
|
102
|
+
break;
|
|
103
|
+
default:
|
|
104
|
+
usage(cli);
|
|
105
|
+
process.exit(1);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function printVersion(cli) {
|
|
112
|
+
logInfo(cli.name + ' version ' + SDK.version);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function printArgs(cli, commandName) {
|
|
116
|
+
getArgsExpanded(cli, commandName)
|
|
117
|
+
.filter(arg => !arg.hidden)
|
|
118
|
+
.forEach(arg => logInfo(' ' + (!arg.required ? '[' : '') + '--' + arg.name + '=' + arg.description + (!arg.required ? ']' : ''), COLOR.magenta));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function listTemplates(cli) {
|
|
122
|
+
var cliName = cli.name;
|
|
123
|
+
var applicableTemplates = getTemplates(cli);
|
|
124
|
+
|
|
125
|
+
logInfo('\nAvailable templates:\n', COLOR.cyan);
|
|
126
|
+
for (var i=0; i<applicableTemplates.length; i++) {
|
|
127
|
+
var template = applicableTemplates[i];
|
|
128
|
+
logInfo((i+1) + ') ' + template.description, COLOR.cyan);
|
|
129
|
+
logInfo(cliName + ' ' + SDK.commands.createwithtemplate.name + ' --' + SDK.args.templateRepoUri.name + '=' + template.path, COLOR.magenta);
|
|
130
|
+
}
|
|
131
|
+
logInfo('');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function usage(cli) {
|
|
135
|
+
var cliName = cli.name;
|
|
136
|
+
var cliVersion = SDK.version;
|
|
137
|
+
var appTypes = cli.appTypes;
|
|
138
|
+
var platforms = cli.platforms;
|
|
139
|
+
|
|
140
|
+
logInfo('\n' + cliName + ': Tool for building ' + cli.purpose + ' using Salesforce Mobile SDK', COLOR.cyan);
|
|
141
|
+
logInfo('\nUsage:\n', COLOR.cyan);
|
|
142
|
+
for (var i=0; i<cli.commands.length; i++) {
|
|
143
|
+
if (i>0) {
|
|
144
|
+
logInfo('\n OR \n', COLOR.cyan);
|
|
145
|
+
}
|
|
146
|
+
var commandName = cli.commands[i];
|
|
147
|
+
var command = getCommandExpanded(cli, commandName);
|
|
148
|
+
logInfo('# ' + command.description, COLOR.magenta);
|
|
149
|
+
logInfo(cliName + ' ' + commandName, COLOR.magenta);
|
|
150
|
+
printArgs(cli, commandName);
|
|
151
|
+
}
|
|
152
|
+
logInfo('\n OR \n', COLOR.cyan);
|
|
153
|
+
logInfo(cliName, COLOR.magenta);
|
|
154
|
+
logInfo('\nWe also offer:', COLOR.cyan);
|
|
155
|
+
for (var otherCliName in SDK.forceclis) {
|
|
156
|
+
var otherCli = SDK.forceclis[otherCliName];
|
|
157
|
+
if (otherCli.name != cli.name) {
|
|
158
|
+
logInfo('- ' + otherCli.name + ': Tool for building ' + otherCli.purpose + ' using Salesforce Mobile SDK', COLOR.cyan);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
logInfo('\n');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
//
|
|
165
|
+
// Processor list
|
|
166
|
+
//
|
|
167
|
+
function buildArgsProcessorList(cli, commandName) {
|
|
168
|
+
var argProcessorList = new commandLineUtils.ArgProcessorList();
|
|
169
|
+
|
|
170
|
+
for (var arg of getArgsExpanded(cli, commandName)) {
|
|
171
|
+
addProcessorFor(argProcessorList, arg.name, arg.prompt, arg.error, arg.validate, arg.promptIf);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return argProcessorList;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
//
|
|
178
|
+
// Helper function to add arg processor
|
|
179
|
+
// * argProcessorList: ArgProcessorList
|
|
180
|
+
// * argName: string, name of argument
|
|
181
|
+
// * prompt: string for prompt
|
|
182
|
+
// * error: function
|
|
183
|
+
// * validation: function or null (no validation)
|
|
184
|
+
// * preprocessor: function or null
|
|
185
|
+
//
|
|
186
|
+
function addProcessorFor(argProcessorList, argName, prompt, error, validation, preprocessor) {
|
|
187
|
+
argProcessorList.addArgProcessor(argName, prompt, function(val) {
|
|
188
|
+
val = val.trim();
|
|
189
|
+
|
|
190
|
+
// validation is either a function or null
|
|
191
|
+
if (validation == null || validation(val)) {
|
|
192
|
+
return new commandLineUtils.ArgProcessorOutput(true, val);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
return new commandLineUtils.ArgProcessorOutput(false, error(val));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
}, preprocessor);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
module.exports = {
|
|
202
|
+
readConfig: readConfig,
|
|
203
|
+
printVersion: printVersion,
|
|
204
|
+
getArgsExpanded: getArgsExpanded,
|
|
205
|
+
getCommandExpanded: getCommandExpanded
|
|
206
|
+
};
|