1 | const path = require('path');
|
2 | const R = require('ramda');
|
3 |
|
4 | const SmapiClient = require('@src/clients/smapi-client');
|
5 | const { AbstractCommand } = require('@src/commands/abstract-command');
|
6 | const optionModel = require('@src/commands/option-model');
|
7 | const HostedSkillController = require('@src/controllers/hosted-skill-controller');
|
8 | const CliWarn = require('@src/exceptions/cli-warn');
|
9 | const CONSTANTS = require('@src/utils/constants');
|
10 | const profileHelper = require('@src/utils/profile-helper');
|
11 | const stringUtils = require('@src/utils/string-utils');
|
12 | const jsonView = require('@src/view/json-view');
|
13 | const Messenger = require('@src/view/messenger');
|
14 |
|
15 | const helper = require('./helper');
|
16 | const ui = require('./ui');
|
17 |
|
18 | class InitCommand extends AbstractCommand {
|
19 | name() {
|
20 | return 'init';
|
21 | }
|
22 |
|
23 | description() {
|
24 | return 'setup a new or existing Alexa skill project';
|
25 | }
|
26 |
|
27 | requiredOptions() {
|
28 | return [];
|
29 | }
|
30 |
|
31 | optionalOptions() {
|
32 | return ['hosted-skill-id', 'profile', 'debug'];
|
33 | }
|
34 |
|
35 | handle(cmd, cb) {
|
36 | let profile;
|
37 | try {
|
38 | profile = profileHelper.runtimeProfile(cmd.profile);
|
39 | } catch (err) {
|
40 | Messenger.getInstance().error(err);
|
41 | return cb(err);
|
42 | }
|
43 |
|
44 | const rootPath = process.cwd();
|
45 | if (!cmd.hostedSkillId) {
|
46 | initNonHostedSkill(rootPath, cmd, profile, (nhsErr) => {
|
47 | cb(nhsErr);
|
48 | });
|
49 | } else {
|
50 | initAlexaHostedSkill(rootPath, cmd, profile, (ahsErr) => {
|
51 | cb(ahsErr);
|
52 | });
|
53 | }
|
54 | }
|
55 | }
|
56 |
|
57 | function initAlexaHostedSkill(rootPath, cmd, profile, cb) {
|
58 | const smapiClient = new SmapiClient({ profile, doDebug: cmd.debug });
|
59 | const hostedSkillController = new HostedSkillController({ profile, doDebug: cmd.debug });
|
60 | _getSkillName(smapiClient, cmd.hostedSkillId, (nameErr, skillName) => {
|
61 | if (nameErr) {
|
62 | Messenger.getInstance().error(nameErr);
|
63 | return cb(nameErr);
|
64 | }
|
65 | _confirmProjectFolderName(skillName, (confirmErr, folderName) => {
|
66 | if (confirmErr) {
|
67 | Messenger.getInstance().error(confirmErr);
|
68 | return cb(confirmErr);
|
69 | }
|
70 | const projectPath = path.join(rootPath, folderName);
|
71 | hostedSkillController.clone(cmd.hostedSkillId, skillName, projectPath, (cloneErr) => {
|
72 | if (cloneErr) {
|
73 | Messenger.getInstance().error(cloneErr);
|
74 | return cb(cloneErr);
|
75 | }
|
76 | const templateUrl = CONSTANTS.HOSTED_SKILL.GIT_HOOKS_TEMPLATES.PRE_PUSH.URL;
|
77 | const filePath = path.join(folderName, '.git', 'hooks', 'pre-push');
|
78 | hostedSkillController.downloadGitHooksTemplate(templateUrl, filePath, (hooksErr) => {
|
79 | if (hooksErr) {
|
80 | Messenger.getInstance().error(hooksErr);
|
81 | return cb(hooksErr);
|
82 | }
|
83 | Messenger.getInstance().info(`\n${skillName} successfully initialized.\n`);
|
84 | cb();
|
85 | });
|
86 | });
|
87 | });
|
88 | });
|
89 | }
|
90 |
|
91 | function initNonHostedSkill(rootPath, cmd, profile, cb) {
|
92 | helper.preInitCheck(rootPath, profile, (preCheckErr) => {
|
93 | if (preCheckErr) {
|
94 | if (preCheckErr instanceof CliWarn) {
|
95 | Messenger.getInstance().warn(preCheckErr.message);
|
96 | } else {
|
97 | Messenger.getInstance().error(preCheckErr);
|
98 | }
|
99 | return cb(preCheckErr);
|
100 | }
|
101 | _collectAskResources((inputErr, userInput) => {
|
102 | if (inputErr) {
|
103 | Messenger.getInstance().error(inputErr);
|
104 | return cb(inputErr);
|
105 | }
|
106 | helper.previewAndWriteAskResources(rootPath, userInput, profile, (previewErr) => {
|
107 | if (previewErr) {
|
108 | if (previewErr instanceof CliWarn) {
|
109 | Messenger.getInstance().warn(previewErr.message);
|
110 | } else {
|
111 | Messenger.getInstance().error(previewErr);
|
112 | }
|
113 | return cb(previewErr);
|
114 | }
|
115 | helper.bootstrapSkillInfra(rootPath, profile, cmd.debug, (postErr) => {
|
116 | if (postErr) {
|
117 | Messenger.getInstance().error(postErr);
|
118 | return cb(postErr);
|
119 | }
|
120 | Messenger.getInstance().info('\nSuccess! Run "ask deploy" to deploy your skill.');
|
121 | cb();
|
122 | });
|
123 | });
|
124 | });
|
125 | });
|
126 | }
|
127 |
|
128 |
|
129 |
|
130 |
|
131 |
|
132 |
|
133 | function _collectAskResources(callback) {
|
134 | helper.getSkillIdUserInput((skillIdErr, skillId) => {
|
135 | if (skillIdErr) {
|
136 | return callback(skillIdErr);
|
137 | }
|
138 | helper.getSkillMetadataUserInput((metaErr, skillMeta) => {
|
139 | if (metaErr) {
|
140 | return callback(metaErr);
|
141 | }
|
142 | helper.getSkillCodeUserInput((codeErr, skillCode) => {
|
143 | if (codeErr) {
|
144 | return callback(codeErr);
|
145 | }
|
146 | if (!skillCode) {
|
147 |
|
148 | return callback(null, { skillId, skillMeta });
|
149 | }
|
150 | helper.getSkillInfraUserInput((infraErr, skillInfra) => {
|
151 | if (infraErr) {
|
152 | return callback(infraErr);
|
153 | }
|
154 | callback(null, { skillId, skillMeta, skillCode, skillInfra });
|
155 | });
|
156 | });
|
157 | });
|
158 | });
|
159 | }
|
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 | function _getSkillName(smapiClient, skillId, callback) {
|
168 | _getSkillManifest(smapiClient, skillId, (manifestErr, manifest) => {
|
169 | if (manifestErr) {
|
170 | return callback(manifestErr);
|
171 | }
|
172 | const locales = R.view(R.lensPath(['manifest', 'publishingInformation', 'locales']), manifest);
|
173 | if (!locales) {
|
174 | return callback('No skill name found.');
|
175 | }
|
176 | const name = locales['en-US'] ? locales['en-US'].name : Object.keys(locales)[0].name;
|
177 | callback(null, name);
|
178 | });
|
179 | }
|
180 |
|
181 |
|
182 |
|
183 |
|
184 |
|
185 |
|
186 |
|
187 | function _getSkillManifest(smapiClient, skillId, callback) {
|
188 | smapiClient.skill.manifest.getManifest(skillId, CONSTANTS.SKILL.STAGE.DEVELOPMENT, (err, res) => {
|
189 | if (err) {
|
190 | return callback(err);
|
191 | }
|
192 | if (res.statusCode >= 300) {
|
193 | const error = jsonView.toString(res.body);
|
194 | return callback(error);
|
195 | }
|
196 | callback(null, res.body);
|
197 | });
|
198 | }
|
199 |
|
200 |
|
201 |
|
202 |
|
203 |
|
204 |
|
205 |
|
206 | function _confirmProjectFolderName(skillName, callback) {
|
207 | const suggestedProjectName = stringUtils.filterNonAlphanumeric(skillName);
|
208 | ui.getProjectFolderName(suggestedProjectName, (getFolderNameErr, folderName) => {
|
209 | if (getFolderNameErr) {
|
210 | return callback(getFolderNameErr);
|
211 | }
|
212 | callback(null, folderName);
|
213 | });
|
214 | }
|
215 |
|
216 | module.exports = InitCommand;
|
217 | module.exports.createCommand = new InitCommand(optionModel).createCommand();
|