forceios 13.0.2 → 13.1.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/package.json +1 -1
- package/shared/configHelper.js +113 -49
- package/shared/constants.js +71 -10
- package/shared/createHelper.js +66 -5
- package/shared/oclifAdapter.js +33 -12
- package/shared/templateHelper.js +249 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "forceios",
|
|
3
|
-
"version": "13.0
|
|
3
|
+
"version": "13.1.0",
|
|
4
4
|
"description": "Utilities for creating mobile apps based on the Salesforce Mobile SDK for iOS",
|
|
5
5
|
"keywords": [ "mobilesdk", "ios", "salesforce", "mobile", "sdk" ],
|
|
6
6
|
"homepage": "https://github.com/forcedotcom/SalesforceMobileSDK-iOS",
|
package/shared/configHelper.js
CHANGED
|
@@ -32,33 +32,37 @@ var path = require('path'),
|
|
|
32
32
|
COLOR = require('./outputColors'),
|
|
33
33
|
commandLineUtils = require('./commandLineUtils'),
|
|
34
34
|
logInfo = require('./utils').logInfo,
|
|
35
|
+
separateRepoUrlPathBranch = require('./utils').separateRepoUrlPathBranch,
|
|
35
36
|
getTemplates = require('./templateHelper').getTemplates,
|
|
37
|
+
getTemplate = require('./templateHelper').getTemplate,
|
|
38
|
+
displayTemplateList = require('./templateHelper').displayTemplateList,
|
|
39
|
+
displayTemplateDetail = require('./templateHelper').displayTemplateDetail,
|
|
36
40
|
validateJson = require('./jsonChecker').validateJson;
|
|
37
41
|
|
|
38
42
|
function applyCli(f, cli) {
|
|
39
|
-
return typeof f === 'function' ? f(cli): f;
|
|
43
|
+
return typeof f === 'function' ? f(cli) : f;
|
|
40
44
|
}
|
|
41
45
|
|
|
42
46
|
function getArgsExpanded(cli, commandName) {
|
|
43
47
|
var argNames = applyCli(SDK.commands[commandName].args, cli);
|
|
44
48
|
return argNames
|
|
45
49
|
.map(argName => SDK.args[argName])
|
|
46
|
-
.map(arg =>
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
50
|
+
.map(arg =>
|
|
51
|
+
({
|
|
52
|
+
name: arg.name,
|
|
53
|
+
'char': arg.char,
|
|
54
|
+
description: applyCli(arg.description, cli),
|
|
55
|
+
longDescription: applyCli(arg.longDescription, cli),
|
|
56
|
+
prompt: applyCli(arg.prompt, cli),
|
|
57
|
+
error: applyCli(arg.error, cli),
|
|
58
|
+
validate: applyCli(arg.validate, cli),
|
|
59
|
+
promptIf: arg.promptIf,
|
|
60
|
+
required: arg.required === undefined ? true : arg.required,
|
|
61
|
+
hasValue: arg.hasValue === undefined ? true : arg.hasValue,
|
|
62
|
+
hidden: applyCli(arg.hidden, cli),
|
|
63
|
+
type: arg.type
|
|
64
|
+
})
|
|
65
|
+
);
|
|
62
66
|
|
|
63
67
|
}
|
|
64
68
|
|
|
@@ -81,28 +85,32 @@ function readConfig(args, cli, handler) {
|
|
|
81
85
|
var processorList = null;
|
|
82
86
|
|
|
83
87
|
switch (commandName || '') {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
88
|
+
case SDK.commands.version.name:
|
|
89
|
+
printVersion(cli);
|
|
90
|
+
process.exit(0);
|
|
91
|
+
break;
|
|
92
|
+
case SDK.commands.create.name:
|
|
93
|
+
case SDK.commands.createwithtemplate.name:
|
|
94
|
+
processorList = buildArgsProcessorList(cli, commandName);
|
|
95
|
+
commandLineUtils.processArgsInteractive(commandLineArgs, processorList, handler);
|
|
96
|
+
break;
|
|
97
|
+
case SDK.commands.checkconfig.name:
|
|
98
|
+
processorList = buildArgsProcessorList(cli, commandName);
|
|
99
|
+
commandLineUtils.processArgsInteractive(commandLineArgs, processorList, function (config) {
|
|
100
|
+
validateJson(config.configpath, config.configtype);
|
|
101
|
+
});
|
|
102
|
+
break;
|
|
103
|
+
case SDK.commands.listtemplates.name:
|
|
104
|
+
listTemplates(cli, commandLineArgs);
|
|
105
|
+
process.exit(0);
|
|
106
|
+
break;
|
|
107
|
+
case SDK.commands.describetemplate.name:
|
|
108
|
+
describeTemplate(cli, commandLineArgs);
|
|
109
|
+
process.exit(0);
|
|
110
|
+
break;
|
|
111
|
+
default:
|
|
112
|
+
usage(cli);
|
|
113
|
+
process.exit(1);
|
|
106
114
|
};
|
|
107
115
|
|
|
108
116
|
|
|
@@ -115,20 +123,76 @@ function printVersion(cli) {
|
|
|
115
123
|
function printArgs(cli, commandName) {
|
|
116
124
|
getArgsExpanded(cli, commandName)
|
|
117
125
|
.filter(arg => !arg.hidden)
|
|
118
|
-
.forEach(arg => logInfo(' ' + (!arg.required
|
|
126
|
+
.forEach(arg => logInfo(' ' + (!arg.required ? '[' : '') + '--' + arg.name + '=' + arg.description + (!arg.required ? ']' : ''), COLOR.magenta));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function listTemplates(cli, commandLineArgs) {
|
|
130
|
+
var cliName = cli.name;
|
|
131
|
+
|
|
132
|
+
// Parse command line arguments to extract templatesource or templaterepouri
|
|
133
|
+
var templateSource = null;
|
|
134
|
+
var templateRepoUri = null;
|
|
135
|
+
var includeDescriptions = false;
|
|
136
|
+
var outputJson = false;
|
|
137
|
+
if (commandLineArgs && commandLineArgs.length > 0) {
|
|
138
|
+
try {
|
|
139
|
+
var argsMap = commandLineUtils.parseArgs(commandLineArgs);
|
|
140
|
+
templateSource = argsMap[SDK.args.templateSource.name];
|
|
141
|
+
templateRepoUri = argsMap[SDK.args.templateRepoUri.name];
|
|
142
|
+
includeDescriptions = argsMap.hasOwnProperty(SDK.args.doc.name);
|
|
143
|
+
outputJson = argsMap.hasOwnProperty(SDK.args.json.name);
|
|
144
|
+
} catch (error) {
|
|
145
|
+
// If argument parsing fails, continue without templateRepoUri
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
var source = templateSource || templateRepoUri;
|
|
150
|
+
var applicableTemplates = getTemplates(cli, source, includeDescriptions);
|
|
151
|
+
|
|
152
|
+
// Use shared display function
|
|
153
|
+
var commandPrefix = cliName + ' ' + SDK.commands.createwithtemplate.name;
|
|
154
|
+
displayTemplateList(applicableTemplates, source, cliName, commandPrefix, includeDescriptions, null, outputJson);
|
|
119
155
|
}
|
|
120
156
|
|
|
121
|
-
function
|
|
157
|
+
function describeTemplate(cli, commandLineArgs) {
|
|
122
158
|
var cliName = cli.name;
|
|
123
|
-
var applicableTemplates = getTemplates(cli);
|
|
124
159
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
160
|
+
// Parse command line arguments to extract templatesource, template, and doc
|
|
161
|
+
var templateSource = null;
|
|
162
|
+
var templateRepoUri = null;
|
|
163
|
+
var templateName = null;
|
|
164
|
+
var includeDescriptions = false;
|
|
165
|
+
var outputJson = false;
|
|
166
|
+
if (commandLineArgs && commandLineArgs.length > 0) {
|
|
167
|
+
try {
|
|
168
|
+
var argsMap = commandLineUtils.parseArgs(commandLineArgs);
|
|
169
|
+
templateSource = argsMap[SDK.args.templateSource.name];
|
|
170
|
+
templateRepoUri = argsMap[SDK.args.templateRepoUri.name];
|
|
171
|
+
templateName = argsMap[SDK.args.template.name];
|
|
172
|
+
includeDescriptions = argsMap.hasOwnProperty(SDK.args.doc.name);
|
|
173
|
+
outputJson = argsMap.hasOwnProperty(SDK.args.json.name);
|
|
174
|
+
} catch (error) {
|
|
175
|
+
// If argument parsing fails, continue without templateRepoUri
|
|
176
|
+
}
|
|
130
177
|
}
|
|
131
|
-
|
|
178
|
+
|
|
179
|
+
// Check if template name is provided
|
|
180
|
+
if (!templateName) {
|
|
181
|
+
logInfo('Error: Template name is required. Use --template to specify the template name.', COLOR.red);
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
var source = templateSource || templateRepoUri;
|
|
186
|
+
var template = getTemplate(templateName, source, includeDescriptions);
|
|
187
|
+
|
|
188
|
+
if (!template) {
|
|
189
|
+
logInfo('Error: Template "' + templateName + '" not found.', COLOR.red);
|
|
190
|
+
process.exit(1);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Use shared display function
|
|
194
|
+
var commandPrefix = cliName + ' ' + SDK.commands.createwithtemplate.name;
|
|
195
|
+
displayTemplateDetail(template, source, cliName, commandPrefix, includeDescriptions, null, outputJson);
|
|
132
196
|
}
|
|
133
197
|
|
|
134
198
|
function usage(cli) {
|
|
@@ -184,7 +248,7 @@ function buildArgsProcessorList(cli, commandName) {
|
|
|
184
248
|
// * preprocessor: function or null
|
|
185
249
|
//
|
|
186
250
|
function addProcessorFor(argProcessorList, argName, prompt, error, validation, preprocessor) {
|
|
187
|
-
argProcessorList.addArgProcessor(argName, prompt, function(val) {
|
|
251
|
+
argProcessorList.addArgProcessor(argName, prompt, function (val) {
|
|
188
252
|
val = val.trim();
|
|
189
253
|
|
|
190
254
|
// validation is either a function or null
|
package/shared/constants.js
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
var path = require('path'),
|
|
29
29
|
shelljs = require('shelljs');
|
|
30
30
|
|
|
31
|
-
var VERSION= '13.0
|
|
31
|
+
var VERSION= '13.1.0';
|
|
32
32
|
|
|
33
33
|
module.exports = {
|
|
34
34
|
version: VERSION,
|
|
@@ -65,7 +65,7 @@ module.exports = {
|
|
|
65
65
|
pluginRepoUri: 'salesforce-mobilesdk-cordova-plugin@v' + VERSION, // GA
|
|
66
66
|
platformVersion: {
|
|
67
67
|
ios: '7.1.1',
|
|
68
|
-
android: '
|
|
68
|
+
android: '14.0.1'
|
|
69
69
|
}
|
|
70
70
|
},
|
|
71
71
|
sf: {
|
|
@@ -95,7 +95,7 @@ module.exports = {
|
|
|
95
95
|
'native': 'iOSNativeTemplate',
|
|
96
96
|
'native_swift': 'iOSNativeSwiftTemplate'
|
|
97
97
|
},
|
|
98
|
-
commands: ['create', 'createwithtemplate', 'version', 'listtemplates', 'checkconfig']
|
|
98
|
+
commands: ['create', 'createwithtemplate', 'version', 'listtemplates', 'describetemplate', 'checkconfig']
|
|
99
99
|
},
|
|
100
100
|
forcedroid: {
|
|
101
101
|
name: 'forcedroid',
|
|
@@ -109,7 +109,7 @@ module.exports = {
|
|
|
109
109
|
'native': 'AndroidNativeTemplate',
|
|
110
110
|
'native_kotlin': 'AndroidNativeKotlinTemplate'
|
|
111
111
|
},
|
|
112
|
-
commands: ['create', 'createwithtemplate', 'version', 'listtemplates', 'checkconfig']
|
|
112
|
+
commands: ['create', 'createwithtemplate', 'version', 'listtemplates', 'describetemplate', 'checkconfig']
|
|
113
113
|
},
|
|
114
114
|
forcehybrid: {
|
|
115
115
|
name: 'forcehybrid',
|
|
@@ -123,7 +123,7 @@ module.exports = {
|
|
|
123
123
|
'hybrid_local': 'HybridLocalTemplate',
|
|
124
124
|
'hybrid_remote': 'HybridRemoteTemplate'
|
|
125
125
|
},
|
|
126
|
-
commands: ['create', 'createwithtemplate', 'version', 'listtemplates', 'checkconfig']
|
|
126
|
+
commands: ['create', 'createwithtemplate', 'version', 'listtemplates', 'describetemplate', 'checkconfig']
|
|
127
127
|
},
|
|
128
128
|
forcereact: {
|
|
129
129
|
name: 'forcereact',
|
|
@@ -137,7 +137,7 @@ module.exports = {
|
|
|
137
137
|
'react_native': 'ReactNativeTemplate',
|
|
138
138
|
'react_native_typescript': 'ReactNativeTypeScriptTemplate'
|
|
139
139
|
},
|
|
140
|
-
commands: ['create', 'createwithtemplate', 'version', 'listtemplates', 'checkconfig']
|
|
140
|
+
commands: ['create', 'createwithtemplate', 'version', 'listtemplates', 'describetemplate', 'checkconfig']
|
|
141
141
|
}
|
|
142
142
|
},
|
|
143
143
|
|
|
@@ -171,6 +171,34 @@ module.exports = {
|
|
|
171
171
|
prompt: 'Enter URI of repo containing template application or a Mobile SDK template name:',
|
|
172
172
|
error: cli => val => 'Invalid value for template repo uri: \'' + val + '\'.',
|
|
173
173
|
validate: cli => val => /^\S+$/.test(val),
|
|
174
|
+
promptIf: otherArgs => !otherArgs.templatesource,
|
|
175
|
+
required: false,
|
|
176
|
+
type: 'string'
|
|
177
|
+
},
|
|
178
|
+
templateSource: {
|
|
179
|
+
name: 'templatesource',
|
|
180
|
+
'char': 'S',
|
|
181
|
+
description: 'git repo URL (optionally with #branch) or local path to a templates suite (root must contain templates.json)',
|
|
182
|
+
longDescription: 'Location of a suite of templates. Can be a git URL with optional #branch suffix or a local filesystem path whose root contains templates.json.',
|
|
183
|
+
prompt: 'Enter git URL or local path to your templates suite:',
|
|
184
|
+
error: cli => val => 'Invalid value for template source: \'' + val + '\'.',
|
|
185
|
+
validate: cli => val => /\S+/.test(val),
|
|
186
|
+
// Process only when explicitly provided to avoid prompting during interactive flows
|
|
187
|
+
promptIf: otherArgs => typeof otherArgs.templatesource !== 'undefined',
|
|
188
|
+
required: false,
|
|
189
|
+
type: 'string'
|
|
190
|
+
},
|
|
191
|
+
template: {
|
|
192
|
+
name: 'template',
|
|
193
|
+
'char': 'm',
|
|
194
|
+
description: 'template name within the templates suite (e.g. ReactNativeTemplate)',
|
|
195
|
+
longDescription: 'Name of the template to use from the suite specified by --templatesource. Should match the directory name or the path field in templates.json.',
|
|
196
|
+
prompt: 'Enter the template name from your template source:',
|
|
197
|
+
error: cli => val => 'Invalid value for template: \'' + val + '\'.',
|
|
198
|
+
validate: cli => val => /\S+/.test(val),
|
|
199
|
+
// Only prompt for template when a templatesource is provided
|
|
200
|
+
promptIf: otherArgs => !!otherArgs.templatesource,
|
|
201
|
+
required: false,
|
|
174
202
|
type: 'string'
|
|
175
203
|
},
|
|
176
204
|
appName: {
|
|
@@ -277,7 +305,31 @@ module.exports = {
|
|
|
277
305
|
required: false,
|
|
278
306
|
type: 'string',
|
|
279
307
|
hidden: true
|
|
280
|
-
|
|
308
|
+
},
|
|
309
|
+
doc: {
|
|
310
|
+
name: 'doc',
|
|
311
|
+
'char': 'D',
|
|
312
|
+
description: 'include verbose documentation from template.json files',
|
|
313
|
+
longDescription: 'When specified, includes detailed metadata from each template\'s template.json file if available (displayName, description, useCase, features, complexity).',
|
|
314
|
+
prompt: null,
|
|
315
|
+
error: cli => val => 'Invalid value for doc flag: \'' + val + '\'.',
|
|
316
|
+
validate: cli => val => true, // Boolean flag, no validation needed
|
|
317
|
+
required: false,
|
|
318
|
+
type: 'boolean',
|
|
319
|
+
hidden: false
|
|
320
|
+
},
|
|
321
|
+
json: {
|
|
322
|
+
name: 'json',
|
|
323
|
+
'char': 'j',
|
|
324
|
+
description: 'output response in JSON format',
|
|
325
|
+
longDescription: 'When specified, outputs the response in JSON format instead of human-readable text. Useful for programmatic consumption.',
|
|
326
|
+
prompt: null,
|
|
327
|
+
error: cli => val => 'Invalid value for json flag: \'' + val + '\'.',
|
|
328
|
+
validate: cli => val => true, // Boolean flag, no validation needed
|
|
329
|
+
required: false,
|
|
330
|
+
type: 'boolean',
|
|
331
|
+
hidden: false
|
|
332
|
+
}
|
|
281
333
|
},
|
|
282
334
|
|
|
283
335
|
commands: {
|
|
@@ -301,7 +353,9 @@ module.exports = {
|
|
|
301
353
|
createwithtemplate: {
|
|
302
354
|
name: 'createwithtemplate',
|
|
303
355
|
args: cli => [cli.platforms.length > 1 ? 'platform' : null,
|
|
356
|
+
'templateSource',
|
|
304
357
|
'templateRepoUri',
|
|
358
|
+
'template',
|
|
305
359
|
'appName',
|
|
306
360
|
'packageName',
|
|
307
361
|
'organization',
|
|
@@ -309,7 +363,7 @@ module.exports = {
|
|
|
309
363
|
'outputDir',
|
|
310
364
|
'verbose',
|
|
311
365
|
cli.name === 'forcehybrid' ? 'pluginRepoUri' : null,
|
|
312
|
-
|
|
366
|
+
'sdkDependencies'
|
|
313
367
|
].filter(x=>x!=null),
|
|
314
368
|
description: cli => 'create ' + cli.purpose + ' from a template',
|
|
315
369
|
longDescription: cli => 'Create ' + cli.purpose + ' from a template.',
|
|
@@ -324,10 +378,17 @@ module.exports = {
|
|
|
324
378
|
},
|
|
325
379
|
listtemplates: {
|
|
326
380
|
name: 'listtemplates',
|
|
327
|
-
args: [],
|
|
381
|
+
args: ['templateSource', 'doc', 'json'],
|
|
328
382
|
description: cli => 'list available Mobile SDK templates to create ' + cli.purpose,
|
|
329
383
|
longDescription: cli => 'List available Mobile SDK templates to create ' + cli.purpose + '.',
|
|
330
|
-
help: 'This command displays the list of available Mobile SDK templates. You can copy repo paths from the output for use with the createwithtemplate command.'
|
|
384
|
+
help: 'This command displays the list of available Mobile SDK templates. You can copy repo paths from the output for use with the createwithtemplate command. Use --templatesource to specify a custom template repository or leave blank to use the default template repository. Use --doc to include detailed metadata from template.json files (displayName, description, useCase, features, complexity). Use --json to output the response in JSON format.'
|
|
385
|
+
},
|
|
386
|
+
describetemplate: {
|
|
387
|
+
name: 'describetemplate',
|
|
388
|
+
args: ['templateSource', 'template', 'doc', 'json'],
|
|
389
|
+
description: cli => 'list details for a specific Mobile SDK template to create ' + cli.purpose,
|
|
390
|
+
longDescription: cli => 'List details for a specific Mobile SDK template to create ' + cli.purpose + '.',
|
|
391
|
+
help: 'This command displays detailed information about a specific Mobile SDK template. Use --templatesource to specify a custom template repository or leave blank to use the default template repository. Use --template to specify the template name. Use --doc to include verbose metadata from template.json files. Use --json to output the response in JSON format.'
|
|
331
392
|
},
|
|
332
393
|
checkconfig: {
|
|
333
394
|
name: 'checkconfig',
|
package/shared/createHelper.js
CHANGED
|
@@ -109,10 +109,38 @@ function createHybridApp(config) {
|
|
|
109
109
|
// Run cordova prepare
|
|
110
110
|
utils.runProcessThrowError('cordova prepare', config.projectDir);
|
|
111
111
|
|
|
112
|
+
// Add theme for Android API 35
|
|
113
|
+
if (config.platform.split(',').includes('android')) {
|
|
114
|
+
createAndroidAPI35Theme(config.projectDir);
|
|
115
|
+
}
|
|
116
|
+
|
|
112
117
|
// Done
|
|
113
118
|
return prepareResult;
|
|
114
119
|
}
|
|
115
120
|
|
|
121
|
+
//
|
|
122
|
+
// Add Android API 35 theme file
|
|
123
|
+
//
|
|
124
|
+
function createAndroidAPI35Theme(projectDir) {
|
|
125
|
+
const dirPath = path.join(projectDir, 'platforms', 'android', 'app', 'src', 'main', 'res', 'values-v35');
|
|
126
|
+
const filePath = path.join(dirPath, 'themes.xml');
|
|
127
|
+
const fileContents = `<?xml version='1.0' encoding='utf-8'?>
|
|
128
|
+
<resources>
|
|
129
|
+
<!-- Override for API 35+ to fix white status bar with white icons issue -->
|
|
130
|
+
<style name="SalesforceSDK_SplashScreen" parent="Theme.SplashScreen.IconBackground">
|
|
131
|
+
<item name="postSplashScreenTheme">@style/Theme.AppCompat.NoActionBar</item>
|
|
132
|
+
<!-- Use dark icons on light status bar background -->
|
|
133
|
+
<item name="android:windowLightStatusBar">true</item>
|
|
134
|
+
</style>
|
|
135
|
+
</resources>`
|
|
136
|
+
|
|
137
|
+
// Ensure the directory exists
|
|
138
|
+
utils.mkDirIfNeeded(dirPath);
|
|
139
|
+
|
|
140
|
+
// Write the file
|
|
141
|
+
fs.writeFileSync(filePath, fileContents, 'utf8');
|
|
142
|
+
}
|
|
143
|
+
|
|
116
144
|
//
|
|
117
145
|
// Print details
|
|
118
146
|
//
|
|
@@ -324,13 +352,41 @@ function actuallyCreateApp(forcecli, config) {
|
|
|
324
352
|
config.version = SDK.version;
|
|
325
353
|
|
|
326
354
|
// Figuring out template repo uri and path
|
|
327
|
-
|
|
328
|
-
|
|
355
|
+
let localTemplatesRoot;
|
|
356
|
+
if (config.templatesource) {
|
|
357
|
+
const source = config.templatesource;
|
|
358
|
+
if (fs.existsSync(source)) {
|
|
359
|
+
// Local path to templates suite
|
|
360
|
+
localTemplatesRoot = path.resolve(source);
|
|
361
|
+
if (!config.template) {
|
|
362
|
+
throw new Error('Missing --template when using --templatesource pointing to a local path');
|
|
363
|
+
}
|
|
364
|
+
config.templatepath = config.template;
|
|
365
|
+
// For display purposes
|
|
366
|
+
config.templaterepouri = source;
|
|
367
|
+
} else {
|
|
368
|
+
// Git URL with optional #branch
|
|
369
|
+
const parsed = utils.separateRepoUrlPathBranch(source);
|
|
370
|
+
config.templaterepouri = parsed.repo + '#' + parsed.branch;
|
|
371
|
+
config.templatepath = config.template || parsed.path;
|
|
372
|
+
if (!config.templatepath) {
|
|
373
|
+
throw new Error('Missing template name. Use --template to specify a template within your --templatesource repository.');
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
else if (config.templaterepouri) {
|
|
378
|
+
if (fs.existsSync(config.templaterepouri)) {
|
|
379
|
+
// Local path directly to a specific template directory
|
|
380
|
+
localTemplatesRoot = path.resolve(config.templaterepouri);
|
|
381
|
+
config.templaterepouri = localTemplatesRoot;
|
|
382
|
+
// Use the directory itself as the template root
|
|
383
|
+
config.templatepath = '';
|
|
384
|
+
} else if (!config.templaterepouri.startsWith("https://")) {
|
|
329
385
|
// Given a Mobile SDK template name
|
|
330
386
|
config.templatepath = config.templaterepouri;
|
|
331
387
|
config.templaterepouri = SDK.templatesRepoUri;
|
|
332
388
|
} else {
|
|
333
|
-
// Given a full URI
|
|
389
|
+
// Given a full URI to a specific template path
|
|
334
390
|
var templateUriParsed = utils.separateRepoUrlPathBranch(config.templaterepouri);
|
|
335
391
|
config.templaterepouri = templateUriParsed.repo + '#' + templateUriParsed.branch;
|
|
336
392
|
config.templatepath = templateUriParsed.path;
|
|
@@ -344,8 +400,13 @@ function actuallyCreateApp(forcecli, config) {
|
|
|
344
400
|
// Creating tmp dir for template clone
|
|
345
401
|
var tmpDir = utils.mkTmpDir();
|
|
346
402
|
|
|
347
|
-
//
|
|
348
|
-
var repoDir
|
|
403
|
+
// Resolve template source directory (clone if needed)
|
|
404
|
+
var repoDir;
|
|
405
|
+
if (localTemplatesRoot) {
|
|
406
|
+
repoDir = localTemplatesRoot;
|
|
407
|
+
} else {
|
|
408
|
+
repoDir = utils.cloneRepo(tmpDir, config.templaterepouri);
|
|
409
|
+
}
|
|
349
410
|
config.templateLocalPath = path.join(repoDir, config.templatepath);
|
|
350
411
|
|
|
351
412
|
// Override sdk dependencies in package.json if any were provided
|
package/shared/oclifAdapter.js
CHANGED
|
@@ -30,9 +30,11 @@ const SDK = require('./constants');
|
|
|
30
30
|
const configHelper = require('./configHelper');
|
|
31
31
|
const createHelper = require('./createHelper');
|
|
32
32
|
const templateHelper = require('./templateHelper');
|
|
33
|
+
const { getTemplates, getTemplate, displayTemplateList, displayTemplateDetail } = require('./templateHelper');
|
|
33
34
|
const jsonChecker = require('./jsonChecker');
|
|
34
35
|
const logInfo = require('./utils').logInfo;
|
|
35
36
|
const logError = require('./utils').logError;
|
|
37
|
+
const separateRepoUrlPathBranch = require('./utils').separateRepoUrlPathBranch;
|
|
36
38
|
const os = require('os');
|
|
37
39
|
|
|
38
40
|
const { SfdxError } = require('@salesforce/core');
|
|
@@ -46,21 +48,36 @@ class OclifAdapter extends Command {
|
|
|
46
48
|
return `${description}${os.EOL}${os.EOL}${help}`;
|
|
47
49
|
}
|
|
48
50
|
|
|
49
|
-
static listTemplates(cli) {
|
|
50
|
-
const applicableTemplates =
|
|
51
|
+
static listTemplates(cli, templateSourceOrRepoUri, includeDescriptions, outputJson) {
|
|
52
|
+
const applicableTemplates = getTemplates(cli, templateSourceOrRepoUri, includeDescriptions);
|
|
51
53
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
// Use shared display function
|
|
55
|
+
const commandPrefix = 'sf ' + [namespace, cli.topic, SDK.commands.createwithtemplate.name].join(':');
|
|
56
|
+
const usageExample = '--' + SDK.args.appName.name + '=<YOUR_APP_NAME> --' + SDK.args.packageName.name + '=<YOUR_PACKAGE_NAME> --' + SDK.args.organization.name + '=<YOUR_ORGANIZATION_NAME>';
|
|
57
|
+
displayTemplateList(applicableTemplates, templateSourceOrRepoUri, cli.name, commandPrefix, includeDescriptions, usageExample, outputJson);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
static describeTemplate(cli, templateSourceOrRepoUri, templateName, includeDescriptions, outputJson) {
|
|
61
|
+
if (!templateName) {
|
|
62
|
+
logError('Error: Template name is required. Use --template to specify the template name.');
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const template = getTemplate(templateName, templateSourceOrRepoUri, includeDescriptions);
|
|
67
|
+
|
|
68
|
+
if (!template) {
|
|
69
|
+
logError('Error: Template "' + templateName + '" not found.');
|
|
70
|
+
process.exit(1);
|
|
58
71
|
}
|
|
59
|
-
|
|
72
|
+
|
|
73
|
+
// Use shared display function
|
|
74
|
+
const commandPrefix = 'sf ' + [namespace, cli.topic, SDK.commands.createwithtemplate.name].join(':');
|
|
75
|
+
const usageExample = '--' + SDK.args.appName.name + '=<YOUR_APP_NAME> --' + SDK.args.packageName.name + '=<YOUR_PACKAGE_NAME> --' + SDK.args.organization.name + '=<YOUR_ORGANIZATION_NAME>';
|
|
76
|
+
displayTemplateDetail(template, templateSourceOrRepoUri, cli.name, commandPrefix, includeDescriptions, usageExample, outputJson);
|
|
60
77
|
}
|
|
61
78
|
|
|
62
79
|
static runCommand(cli, commandName, vals) {
|
|
63
|
-
switch(commandName) {
|
|
80
|
+
switch (commandName) {
|
|
64
81
|
case SDK.commands.create.name:
|
|
65
82
|
case SDK.commands.createwithtemplate.name:
|
|
66
83
|
createHelper.createApp(cli, vals);
|
|
@@ -69,7 +86,11 @@ class OclifAdapter extends Command {
|
|
|
69
86
|
configHelper.printVersion(cli);
|
|
70
87
|
break;
|
|
71
88
|
case SDK.commands.listtemplates.name:
|
|
72
|
-
OclifAdapter.listTemplates(cli);
|
|
89
|
+
OclifAdapter.listTemplates(cli, vals.templatesource, vals.doc, vals.json);
|
|
90
|
+
process.exit(0);
|
|
91
|
+
break;
|
|
92
|
+
case SDK.commands.describetemplate.name:
|
|
93
|
+
OclifAdapter.describeTemplate(cli, vals.templatesource, vals.template, vals.doc, vals.json);
|
|
73
94
|
process.exit(0);
|
|
74
95
|
break;
|
|
75
96
|
case SDK.commands.checkconfig.name:
|
|
@@ -189,7 +210,7 @@ class OclifAdapter extends Command {
|
|
|
189
210
|
}
|
|
190
211
|
}
|
|
191
212
|
|
|
192
|
-
OclifAdapter.getCommand = function(cli, commandName) {
|
|
213
|
+
OclifAdapter.getCommand = function (cli, commandName) {
|
|
193
214
|
if (!this._command) {
|
|
194
215
|
this._command = configHelper.getCommandExpanded(cli, commandName);
|
|
195
216
|
}
|
package/shared/templateHelper.js
CHANGED
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
// Dependencies
|
|
29
29
|
var path = require('path'),
|
|
30
30
|
SDK = require('./constants'),
|
|
31
|
-
utils = require('./utils')
|
|
31
|
+
utils = require('./utils'),
|
|
32
|
+
fs = require('fs');
|
|
32
33
|
|
|
33
34
|
//
|
|
34
35
|
// Helper to prepare template
|
|
@@ -36,7 +37,7 @@ var path = require('path'),
|
|
|
36
37
|
function prepareTemplate(config, templateDir) {
|
|
37
38
|
var template = require(path.join(templateDir, 'template.js'));
|
|
38
39
|
return utils.runFunctionThrowError(
|
|
39
|
-
function() {
|
|
40
|
+
function () {
|
|
40
41
|
return template.prepare(config, utils.replaceInFiles, utils.moveFile, utils.removeFile);
|
|
41
42
|
},
|
|
42
43
|
templateDir);
|
|
@@ -45,14 +46,22 @@ function prepareTemplate(config, templateDir) {
|
|
|
45
46
|
//
|
|
46
47
|
// Get templates for the given cli
|
|
47
48
|
//
|
|
48
|
-
function getTemplates(cli) {
|
|
49
|
+
function getTemplates(cli, templateSourceOrRepoUri, includeDescriptions) {
|
|
49
50
|
try {
|
|
50
51
|
|
|
51
52
|
// Creating tmp dir for template clone
|
|
52
53
|
var tmpDir = utils.mkTmpDir();
|
|
53
54
|
|
|
54
|
-
//
|
|
55
|
-
var
|
|
55
|
+
// Use provided source (git URL or local path) or fall back to default
|
|
56
|
+
var source = templateSourceOrRepoUri || SDK.templatesRepoUri;
|
|
57
|
+
var repoDir;
|
|
58
|
+
if (fs.existsSync(source)) {
|
|
59
|
+
// Local path
|
|
60
|
+
repoDir = path.resolve(source);
|
|
61
|
+
} else {
|
|
62
|
+
// Git URL
|
|
63
|
+
repoDir = utils.cloneRepo(tmpDir, source);
|
|
64
|
+
}
|
|
56
65
|
|
|
57
66
|
// Getting list of templates
|
|
58
67
|
var templates = require(path.join(repoDir, 'templates.json'));
|
|
@@ -61,6 +70,13 @@ function getTemplates(cli) {
|
|
|
61
70
|
var applicableTemplates = templates
|
|
62
71
|
.filter(template => cli.appTypes.includes(template.appType) && cli.platforms.filter(platform => template.platforms.includes(platform)).length > 0);
|
|
63
72
|
|
|
73
|
+
// If descriptions are requested, add them to each template
|
|
74
|
+
if (includeDescriptions) {
|
|
75
|
+
applicableTemplates.forEach(function(template) {
|
|
76
|
+
template.metadata = getTemplateMetadata(template.path, repoDir);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
64
80
|
// Cleanup
|
|
65
81
|
utils.removeFile(tmpDir);
|
|
66
82
|
|
|
@@ -96,9 +112,236 @@ function getAppTypeFromTemplate(templateRepoUriWithPossiblePath) {
|
|
|
96
112
|
return appType;
|
|
97
113
|
}
|
|
98
114
|
|
|
115
|
+
//
|
|
116
|
+
// Extract template metadata from template.json file
|
|
117
|
+
//
|
|
118
|
+
function getTemplateMetadata(templatePath, repoDir) {
|
|
119
|
+
try {
|
|
120
|
+
var templateJsonPath = path.join(repoDir, templatePath, 'template.json');
|
|
121
|
+
if (fs.existsSync(templateJsonPath)) {
|
|
122
|
+
var templateJsonContent = fs.readFileSync(templateJsonPath, 'utf8');
|
|
123
|
+
var templateData = JSON.parse(templateJsonContent);
|
|
124
|
+
|
|
125
|
+
// Return all metadata properties from the template.json file
|
|
126
|
+
// This makes the function more flexible for future use cases
|
|
127
|
+
return templateData;
|
|
128
|
+
}
|
|
129
|
+
} catch (error) {
|
|
130
|
+
// If there's any error reading or parsing the template.json, just return null
|
|
131
|
+
// This ensures the command continues to work even if template.json parsing fails
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
//
|
|
137
|
+
// Get a single template by name
|
|
138
|
+
//
|
|
139
|
+
function getTemplate(templateName, templateSourceOrRepoUri, includeDescriptions) {
|
|
140
|
+
try {
|
|
141
|
+
// Creating tmp dir for template clone
|
|
142
|
+
var tmpDir = utils.mkTmpDir();
|
|
143
|
+
|
|
144
|
+
// Use provided source (git URL or local path) or fall back to default
|
|
145
|
+
var source = templateSourceOrRepoUri || SDK.templatesRepoUri;
|
|
146
|
+
var repoDir;
|
|
147
|
+
if (fs.existsSync(source)) {
|
|
148
|
+
// Local path
|
|
149
|
+
repoDir = path.resolve(source);
|
|
150
|
+
} else {
|
|
151
|
+
// Git URL
|
|
152
|
+
repoDir = utils.cloneRepo(tmpDir, source);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Getting list of templates
|
|
156
|
+
var templates = require(path.join(repoDir, 'templates.json'));
|
|
157
|
+
|
|
158
|
+
// Finding the specific template
|
|
159
|
+
var template = templates.find(t => t.path === templateName);
|
|
160
|
+
|
|
161
|
+
if (!template) {
|
|
162
|
+
utils.removeFile(tmpDir);
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// If descriptions are requested, add metadata
|
|
167
|
+
if (includeDescriptions) {
|
|
168
|
+
template.metadata = getTemplateMetadata(template.path, repoDir);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Cleanup
|
|
172
|
+
utils.removeFile(tmpDir);
|
|
173
|
+
|
|
174
|
+
return template;
|
|
175
|
+
} catch (error) {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
//
|
|
181
|
+
// Display template list with optional metadata
|
|
182
|
+
//
|
|
183
|
+
function displayTemplateList(templates, source, cliName, commandPrefix, includeDescriptions, extraRequiredArgs, outputJson) {
|
|
184
|
+
var utils = require('./utils');
|
|
185
|
+
var COLOR = require('./outputColors');
|
|
186
|
+
var logInfo = utils.logInfo;
|
|
187
|
+
var SDK = require('./constants');
|
|
188
|
+
|
|
189
|
+
if (outputJson) {
|
|
190
|
+
// Output in JSON format
|
|
191
|
+
var jsonOutput = {
|
|
192
|
+
repository: source || 'default',
|
|
193
|
+
templates: templates.map(function(template, index) {
|
|
194
|
+
var sourceForCommand = source || SDK.templatesRepoUri;
|
|
195
|
+
var command = commandPrefix + ' --' + SDK.args.templateSource.name + '=' + sourceForCommand
|
|
196
|
+
+ ' --' + SDK.args.template.name + '=' + template.path;
|
|
197
|
+
|
|
198
|
+
if (extraRequiredArgs) {
|
|
199
|
+
command += ` ${extraRequiredArgs}`;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
var jsonTemplate = {
|
|
203
|
+
index: index + 1,
|
|
204
|
+
path: template.path,
|
|
205
|
+
description: template.description,
|
|
206
|
+
appType: template.appType,
|
|
207
|
+
platforms: template.platforms,
|
|
208
|
+
command: command
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
if (includeDescriptions && template.metadata) {
|
|
212
|
+
jsonTemplate.metadata = template.metadata;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return jsonTemplate;
|
|
216
|
+
})
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
logInfo(JSON.stringify(jsonOutput, null, 2), COLOR.white);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Show which template repository is being used
|
|
224
|
+
if (source) {
|
|
225
|
+
logInfo('\nAvailable templates from custom repository:\n', COLOR.cyan);
|
|
226
|
+
logInfo('Repository: ' + source, COLOR.cyan);
|
|
227
|
+
} else {
|
|
228
|
+
logInfo('\nAvailable templates:\n', COLOR.cyan);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
for (var i = 0; i < templates.length; i++) {
|
|
232
|
+
var template = templates[i];
|
|
233
|
+
logInfo((i + 1) + ') ' + template.description, COLOR.cyan);
|
|
234
|
+
|
|
235
|
+
var sourceForCommand = source || SDK.templatesRepoUri;
|
|
236
|
+
var command = commandPrefix + ' --' + SDK.args.templateSource.name + '=' + sourceForCommand
|
|
237
|
+
+ ' --' + SDK.args.template.name + '=' + template.path;
|
|
238
|
+
|
|
239
|
+
// Add additional required args if provided
|
|
240
|
+
if (extraRequiredArgs) {
|
|
241
|
+
command += ` ${extraRequiredArgs}`;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
logInfo(command, COLOR.magenta);
|
|
245
|
+
|
|
246
|
+
// If descriptions are requested and available, show them
|
|
247
|
+
if (includeDescriptions && template.metadata) {
|
|
248
|
+
if (template.metadata.description) {
|
|
249
|
+
logInfo(' Description: ' + template.metadata.description, COLOR.white);
|
|
250
|
+
}
|
|
251
|
+
if (template.metadata.useCase) {
|
|
252
|
+
logInfo(' Use Case: ' + template.metadata.useCase, COLOR.white);
|
|
253
|
+
}
|
|
254
|
+
if (template.metadata.features && Array.isArray(template.metadata.features)) {
|
|
255
|
+
logInfo(' Features: ' + template.metadata.features.join(', '), COLOR.white);
|
|
256
|
+
}
|
|
257
|
+
if (template.metadata.complexity) {
|
|
258
|
+
logInfo(' Complexity: ' + template.metadata.complexity, COLOR.white);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
logInfo('');
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
//
|
|
266
|
+
// Display detailed information about a single template
|
|
267
|
+
//
|
|
268
|
+
function displayTemplateDetail(template, source, cliName, commandPrefix, includeDescriptions, extraRequiredArgs, outputJson) {
|
|
269
|
+
var utils = require('./utils');
|
|
270
|
+
var COLOR = require('./outputColors');
|
|
271
|
+
var logInfo = utils.logInfo;
|
|
272
|
+
var SDK = require('./constants');
|
|
273
|
+
|
|
274
|
+
if (outputJson) {
|
|
275
|
+
// Output in JSON format
|
|
276
|
+
var sourceForCommand = source || SDK.templatesRepoUri;
|
|
277
|
+
var command = commandPrefix + ' --' + SDK.args.templateSource.name + '=' + sourceForCommand
|
|
278
|
+
+ ' --' + SDK.args.template.name + '=' + template.path;
|
|
279
|
+
|
|
280
|
+
if (extraRequiredArgs) {
|
|
281
|
+
command += ` ${extraRequiredArgs}`;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
var jsonOutput = {
|
|
285
|
+
repository: source || 'default',
|
|
286
|
+
template: {
|
|
287
|
+
path: template.path,
|
|
288
|
+
description: template.description,
|
|
289
|
+
appType: template.appType,
|
|
290
|
+
platforms: template.platforms,
|
|
291
|
+
command: command
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
if (includeDescriptions && template.metadata) {
|
|
296
|
+
jsonOutput.template.metadata = template.metadata;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
logInfo(JSON.stringify(jsonOutput, null, 2), COLOR.white);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Show which template repository is being used
|
|
304
|
+
if (source) {
|
|
305
|
+
logInfo('\nTemplate from custom repository:\n', COLOR.cyan);
|
|
306
|
+
logInfo('Repository: ' + source, COLOR.cyan);
|
|
307
|
+
} else {
|
|
308
|
+
logInfo('\nTemplate from default repository:\n', COLOR.cyan);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Display template basic info
|
|
312
|
+
logInfo('Template: ' + template.path, COLOR.cyan);
|
|
313
|
+
logInfo('Description: ' + template.description, COLOR.cyan);
|
|
314
|
+
logInfo('App Type: ' + template.appType, COLOR.cyan);
|
|
315
|
+
logInfo('Platforms: ' + template.platforms.join(', '), COLOR.cyan);
|
|
316
|
+
|
|
317
|
+
// Display command usage
|
|
318
|
+
var sourceForCommand = source || SDK.templatesRepoUri;
|
|
319
|
+
var command = commandPrefix + ' --' + SDK.args.templateSource.name + '=' + sourceForCommand
|
|
320
|
+
+ ' --' + SDK.args.template.name + '=' + template.path;
|
|
321
|
+
|
|
322
|
+
// Add additional required args if provided
|
|
323
|
+
if (extraRequiredArgs) {
|
|
324
|
+
command += ` ${extraRequiredArgs}`;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
logInfo('\nUsage:', COLOR.magenta);
|
|
328
|
+
logInfo(command, COLOR.magenta);
|
|
329
|
+
|
|
330
|
+
// If descriptions are requested and available, show raw JSON metadata
|
|
331
|
+
if (includeDescriptions && template.metadata) {
|
|
332
|
+
logInfo('\nTemplate Metadata (template.json):', COLOR.cyan);
|
|
333
|
+
logInfo(JSON.stringify(template.metadata, null, 2), COLOR.white);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
logInfo('');
|
|
337
|
+
}
|
|
99
338
|
|
|
100
339
|
module.exports = {
|
|
101
340
|
prepareTemplate,
|
|
102
341
|
getTemplates,
|
|
103
|
-
|
|
342
|
+
getTemplate,
|
|
343
|
+
getAppTypeFromTemplate,
|
|
344
|
+
getTemplateMetadata,
|
|
345
|
+
displayTemplateList,
|
|
346
|
+
displayTemplateDetail
|
|
104
347
|
};
|