mlcl 1.0.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.
Files changed (195) hide show
  1. package/.eslintignore +31 -0
  2. package/.eslintrc.json +54 -0
  3. package/LICENSE +200 -0
  4. package/README.md +101 -0
  5. package/babel.config.json +10 -0
  6. package/build/API/authorization.js +107 -0
  7. package/build/API/client.js +11 -0
  8. package/build/API/index.js +8 -0
  9. package/build/API/molecule/index.js +11 -0
  10. package/build/API/molecule/types.js +6 -0
  11. package/build/API/resource/device/index.js +12 -0
  12. package/build/API/resource/device/types.js +6 -0
  13. package/build/API/resource/device/utilities/confirmSshKey.js +20 -0
  14. package/build/API/resource/device/utilities/index.js +7 -0
  15. package/build/API/resource/device/utilities/sendSshKey.js +64 -0
  16. package/build/API/resource/index.js +10 -0
  17. package/build/API/resource/molecule/index.js +12 -0
  18. package/build/API/resource/molecule/types.js +6 -0
  19. package/build/API/resource/molecule/utilities/getPlan.js +15 -0
  20. package/build/API/resource/molecule/utilities/index.js +6 -0
  21. package/build/API/resource/payment/index.js +9 -0
  22. package/build/API/resource/payment/plans.js +28 -0
  23. package/build/API/resource/payment/platform.js +7 -0
  24. package/build/API/resource/payment/platforms/apple/index.js +21 -0
  25. package/build/API/resource/payment/platforms/apple/plans/appleMonthly.js +17 -0
  26. package/build/API/resource/payment/platforms/apple/plans/appleYearly.js +18 -0
  27. package/build/API/resource/payment/platforms/apple/plans/index.js +7 -0
  28. package/build/API/resource/payment/platforms/google/index.js +21 -0
  29. package/build/API/resource/payment/platforms/google/plans/googleMonthly.js +17 -0
  30. package/build/API/resource/payment/platforms/google/plans/googleYearly.js +18 -0
  31. package/build/API/resource/payment/platforms/google/plans/index.js +7 -0
  32. package/build/API/resource/payment/platforms/index.js +8 -0
  33. package/build/API/resource/payment/platforms/stripe/index.js +25 -0
  34. package/build/API/resource/payment/platforms/stripe/plans/index.js +11 -0
  35. package/build/API/resource/payment/platforms/stripe/plans/stripeBusinessMolecule.js +21 -0
  36. package/build/API/resource/payment/platforms/stripe/plans/stripeMonthly.js +18 -0
  37. package/build/API/resource/payment/platforms/stripe/plans/stripeMonthlyMolecule.js +18 -0
  38. package/build/API/resource/payment/platforms/stripe/plans/stripeSingleUseMolecule.js +19 -0
  39. package/build/API/resource/payment/platforms/stripe/plans/stripeUnlimitedUseMolecule.js +19 -0
  40. package/build/API/resource/payment/platforms/stripe/plans/stripeYearly.js +19 -0
  41. package/build/API/resource/payment/types.js +6 -0
  42. package/build/API/resource/types.js +6 -0
  43. package/build/API/resource/user/index.js +15 -0
  44. package/build/API/resource/user/types.js +6 -0
  45. package/build/API/resource/user/utilities/getPlan.js +12 -0
  46. package/build/API/resource/user/utilities/index.js +7 -0
  47. package/build/API/resource/user/utilities/logIn.js +60 -0
  48. package/build/cli.js +15 -0
  49. package/build/commands/add-ssh-key.js +24 -0
  50. package/build/commands/buy.js +70 -0
  51. package/build/commands/clone.js +329 -0
  52. package/build/commands/index.js +17 -0
  53. package/build/commands/info.js +33 -0
  54. package/build/commands/login.js +20 -0
  55. package/build/commands/logout.js +23 -0
  56. package/build/commands/molecule/index.js +11 -0
  57. package/build/commands/molecule/types.js +6 -0
  58. package/build/commands/remove-ssh-key.js +36 -0
  59. package/build/commands/select.js +98 -0
  60. package/build/commands/set-api-key.js +73 -0
  61. package/build/commands/subscribe.js +74 -0
  62. package/build/commands/unsubscribe.js +59 -0
  63. package/build/commands/utilities/asyncExec.js +6 -0
  64. package/build/commands/utilities/branchExists.js +13 -0
  65. package/build/commands/utilities/branchExistsSync.js +12 -0
  66. package/build/commands/utilities/checkoutSelectionBranch.js +49 -0
  67. package/build/commands/utilities/cherryPick.js +20 -0
  68. package/build/commands/utilities/cloneRepo.js +9 -0
  69. package/build/commands/utilities/convertNpmToYarn.js +60 -0
  70. package/build/commands/utilities/convertTypeDocToJSDoc.js +51 -0
  71. package/build/commands/utilities/convertTypeScriptToJavaScript.js +451 -0
  72. package/build/commands/utilities/enableMergeDrivers.js +50 -0
  73. package/build/commands/utilities/getCurrentBranch.js +11 -0
  74. package/build/commands/utilities/getCurrentBranchSync.js +7 -0
  75. package/build/commands/utilities/getRepoRootPath.js +7 -0
  76. package/build/commands/utilities/getRepoRootPathSync.js +13 -0
  77. package/build/commands/utilities/index.js +16 -0
  78. package/build/commands/utilities/npm-to-yarn.js +41 -0
  79. package/build/commands/utilities/ts-to-js.js +344 -0
  80. package/build/commands/version.js +14 -0
  81. package/build/commands/x-merge-json.js +33 -0
  82. package/build/config.js +5 -0
  83. package/build/environment.js +80 -0
  84. package/build/index.js +23 -0
  85. package/build/logger.js +12 -0
  86. package/build/pkg.js +5 -0
  87. package/build/repoConfig copy.js +5 -0
  88. package/build/repoConfig.js +5 -0
  89. package/build/types/global.js +6 -0
  90. package/build/types/index.js +6 -0
  91. package/build/userConfig.js +5 -0
  92. package/build/utilities/__tests__/getErrorMessage.js +26 -0
  93. package/build/utilities/__tests__/timeAgo.js +39 -0
  94. package/build/utilities/checkVersion.js +29 -0
  95. package/build/utilities/getErrorMessage.js +15 -0
  96. package/build/utilities/index.js +13 -0
  97. package/build/utilities/readConfig.js +19 -0
  98. package/build/utilities/readRepoConfig copy.js +20 -0
  99. package/build/utilities/readRepoConfig.js +19 -0
  100. package/build/utilities/readUserConfig.js +20 -0
  101. package/build/utilities/synchronizeUserConfig.js +25 -0
  102. package/build/utilities/timeAgo.js +42 -0
  103. package/build/utilities/userLoggedIn.js +24 -0
  104. package/build/utilities/writeConfig.js +8 -0
  105. package/build/utilities/writeRepoConfig copy.js +9 -0
  106. package/build/utilities/writeRepoConfig.js +10 -0
  107. package/build/utilities/writeUserConfig.js +11 -0
  108. package/jest.config.json +11 -0
  109. package/package.json +56 -0
  110. package/src/API/authorization.ts +143 -0
  111. package/src/API/client.ts +12 -0
  112. package/src/API/index.ts +10 -0
  113. package/src/API/resource/device/index.ts +27 -0
  114. package/src/API/resource/device/types.ts +80 -0
  115. package/src/API/resource/device/utilities/confirmSshKey.ts +25 -0
  116. package/src/API/resource/device/utilities/index.ts +8 -0
  117. package/src/API/resource/device/utilities/sendSshKey.ts +80 -0
  118. package/src/API/resource/index.ts +12 -0
  119. package/src/API/resource/molecule/index.ts +30 -0
  120. package/src/API/resource/molecule/types.ts +332 -0
  121. package/src/API/resource/molecule/utilities/getPlan.ts +21 -0
  122. package/src/API/resource/molecule/utilities/index.ts +7 -0
  123. package/src/API/resource/payment/index.ts +12 -0
  124. package/src/API/resource/payment/plans.ts +32 -0
  125. package/src/API/resource/payment/platform.ts +11 -0
  126. package/src/API/resource/payment/platforms/apple/index.ts +25 -0
  127. package/src/API/resource/payment/platforms/apple/plans/appleMonthly.ts +19 -0
  128. package/src/API/resource/payment/platforms/apple/plans/appleYearly.ts +20 -0
  129. package/src/API/resource/payment/platforms/apple/plans/index.ts +9 -0
  130. package/src/API/resource/payment/platforms/google/index.ts +25 -0
  131. package/src/API/resource/payment/platforms/google/plans/googleMonthly.ts +19 -0
  132. package/src/API/resource/payment/platforms/google/plans/googleYearly.ts +20 -0
  133. package/src/API/resource/payment/platforms/google/plans/index.ts +9 -0
  134. package/src/API/resource/payment/platforms/index.ts +10 -0
  135. package/src/API/resource/payment/platforms/stripe/index.ts +29 -0
  136. package/src/API/resource/payment/platforms/stripe/plans/index.ts +12 -0
  137. package/src/API/resource/payment/platforms/stripe/plans/stripeBusinessMolecule.ts +23 -0
  138. package/src/API/resource/payment/platforms/stripe/plans/stripeMonthly.ts +20 -0
  139. package/src/API/resource/payment/platforms/stripe/plans/stripeMonthlyMolecule.ts +20 -0
  140. package/src/API/resource/payment/platforms/stripe/plans/stripeSingleUseMolecule.ts +21 -0
  141. package/src/API/resource/payment/platforms/stripe/plans/stripeUnlimitedUseMolecule.ts +21 -0
  142. package/src/API/resource/payment/platforms/stripe/plans/stripeYearly.ts +21 -0
  143. package/src/API/resource/payment/types.ts +201 -0
  144. package/src/API/resource/types.ts +169 -0
  145. package/src/API/resource/user/index.ts +29 -0
  146. package/src/API/resource/user/types.ts +131 -0
  147. package/src/API/resource/user/utilities/getPlan.ts +16 -0
  148. package/src/API/resource/user/utilities/index.ts +8 -0
  149. package/src/API/resource/user/utilities/logIn.ts +71 -0
  150. package/src/commands/add-ssh-key.ts +30 -0
  151. package/src/commands/buy.ts +83 -0
  152. package/src/commands/clone.ts +363 -0
  153. package/src/commands/index.ts +19 -0
  154. package/src/commands/info.ts +40 -0
  155. package/src/commands/login.ts +24 -0
  156. package/src/commands/logout.ts +27 -0
  157. package/src/commands/remove-ssh-key.ts +42 -0
  158. package/src/commands/subscribe.ts +84 -0
  159. package/src/commands/unsubscribe.ts +67 -0
  160. package/src/commands/utilities/asyncExec.ts +7 -0
  161. package/src/commands/utilities/branchExists.ts +15 -0
  162. package/src/commands/utilities/checkoutSelectionBranch.ts +62 -0
  163. package/src/commands/utilities/cherryPick.ts +23 -0
  164. package/src/commands/utilities/cloneRepo.ts +10 -0
  165. package/src/commands/utilities/convertNpmToYarn.ts +71 -0
  166. package/src/commands/utilities/convertTypeDocToJSDoc.ts +55 -0
  167. package/src/commands/utilities/convertTypeScriptToJavaScript.ts +526 -0
  168. package/src/commands/utilities/enableMergeDrivers.ts +55 -0
  169. package/src/commands/utilities/getCurrentBranch.ts +14 -0
  170. package/src/commands/utilities/getRepoRootPathSync.ts +13 -0
  171. package/src/commands/utilities/index.ts +17 -0
  172. package/src/commands/version.ts +22 -0
  173. package/src/commands/x-merge-json.ts +41 -0
  174. package/src/environment.ts +88 -0
  175. package/src/index.ts +30 -0
  176. package/src/logger.ts +29 -0
  177. package/src/pkg.ts +11 -0
  178. package/src/repoConfig.ts +6 -0
  179. package/src/types/global.ts +26 -0
  180. package/src/types/index.ts +53 -0
  181. package/src/types/process.env.d.ts +56 -0
  182. package/src/userConfig.ts +6 -0
  183. package/src/utilities/__tests__/getErrorMessage.ts +32 -0
  184. package/src/utilities/__tests__/timeAgo.ts +46 -0
  185. package/src/utilities/checkVersion.ts +31 -0
  186. package/src/utilities/getErrorMessage.ts +30 -0
  187. package/src/utilities/index.ts +14 -0
  188. package/src/utilities/readRepoConfig.ts +22 -0
  189. package/src/utilities/readUserConfig.ts +24 -0
  190. package/src/utilities/synchronizeUserConfig.ts +26 -0
  191. package/src/utilities/timeAgo.ts +39 -0
  192. package/src/utilities/userLoggedIn.ts +28 -0
  193. package/src/utilities/writeRepoConfig.ts +11 -0
  194. package/src/utilities/writeUserConfig.ts +13 -0
  195. package/tsconfig.json +25 -0
@@ -0,0 +1,363 @@
1
+ import fs from 'fs'
2
+ import path from 'path'
3
+ import { Command } from 'commander'
4
+ import { userConfig } from '../userConfig.js'
5
+ import { userLoggedIn } from '../utilities/userLoggedIn.js'
6
+ import { getPlan } from '../API/resource/molecule/utilities/getPlan.js'
7
+ import { confirmSshKey } from '../API/resource/device/utilities/confirmSshKey.js'
8
+ import * as API from '../API/index.js'
9
+ import { logger } from '../logger.js'
10
+ import { getErrorMessage } from '../utilities/getErrorMessage.js'
11
+ import { cloneRepo } from './utilities/cloneRepo.js'
12
+ import { checkoutSelectionBranch } from './utilities/checkoutSelectionBranch.js'
13
+ import { enableMergeDrivers } from './utilities/enableMergeDrivers.js'
14
+ import { cherryPick } from './utilities/cherryPick.js'
15
+ import { asyncExec } from './utilities/asyncExec.js'
16
+ import { convertTypeDocToJSDoc } from './utilities/convertTypeDocToJSDoc.js'
17
+ import { convertNpmToYarn } from './utilities/convertNpmToYarn.js'
18
+ import { convertTypeScriptToJavaScript } from './utilities/convertTypeScriptToJavaScript.js'
19
+
20
+ /**
21
+ * Adds the `clone` command to the program.
22
+ */
23
+ export const clone = (program: Command) => {
24
+ program.command(`clone`)
25
+ .description(`clones your Molecule into \`project-name-api\` and/or \`project-name-app\``)
26
+ .argument(`<id>`, `your Molecule's ID`)
27
+ .argument(`[projectName]`, `a directory for your Molecule, usually something like \`project-name\``)
28
+ .action(async (id = ``, projectName = ``) => {
29
+ if (!id) {
30
+ logger.warn(`Molecule ID is required.`)
31
+ return
32
+ }
33
+
34
+ const user = await userLoggedIn()
35
+
36
+ if (!user) {
37
+ return
38
+ }
39
+
40
+ try {
41
+ logger.info(`Fetching your Molecule's clone information...`)
42
+
43
+ const {
44
+ props,
45
+ apiCommits,
46
+ apiRepoUrl,
47
+ appCommits,
48
+ appRepoUrl,
49
+ } = (await API.resource.molecule.readClone(id)).data
50
+
51
+ const {
52
+ name,
53
+ apiSelectionHash,
54
+ appSelectionHash,
55
+ } = props
56
+
57
+ if (((!apiSelectionHash || !apiRepoUrl)) && (!appSelectionHash || !appRepoUrl)) {
58
+ logger.warn(`Failed to fetch the necessary information to clone your Molecule. Please contact developer-support@molecule.dev so we can fix this.`)
59
+ return
60
+ }
61
+
62
+ const moleculePlan = getPlan(props)
63
+
64
+ if (moleculePlan?.capabilities.forMolecules && moleculePlan?.capabilities.premium) {
65
+ const deviceId = userConfig.userIdsToDeviceIds?.[user.id] || ``
66
+
67
+ if (!deviceId) {
68
+ logger.warn(`No device ID for the current user. Try logging out and in again.`)
69
+ return
70
+ }
71
+
72
+ const confirmedSshKey = await confirmSshKey(deviceId, user.username)
73
+
74
+ if (!confirmedSshKey) {
75
+ return
76
+ }
77
+ }
78
+
79
+ projectName = projectName || name?.replace(/ /gi, `-`).replace(/[^A-z0-9-]/gi, ``).toLowerCase() || `your`
80
+
81
+ const apiRepoPath = `${projectName}-api`
82
+ const appRepoPath = `${projectName}-app`
83
+ let apiError = false
84
+ let appError = false
85
+
86
+ if (apiSelectionHash && apiRepoUrl) {
87
+ let resetMergeDrivers: null | (() => void) = null
88
+ const writeNewRepoConfig = async () => {
89
+ const repoConfigPath = path.join(apiRepoPath, `.mlclrc.json`)
90
+
91
+ logger.info(`Creating ${apiRepoPath}/.mlclrc.json...`)
92
+
93
+ try {
94
+ await fs.promises.access(repoConfigPath, fs.constants.R_OK | fs.constants.W_OK)
95
+ logger.info(`${apiRepoPath}/.mlclrc.json already exists on your machine!`)
96
+ apiError = true
97
+ } catch (error) {
98
+ await fs.promises.writeFile(repoConfigPath, JSON.stringify({ id }, null, 2))
99
+ }
100
+ }
101
+
102
+ try {
103
+ await fs.promises.access(apiRepoPath, fs.constants.R_OK | fs.constants.W_OK)
104
+ logger.warn(`Error cloning API: ${apiRepoPath} already exists on your machine!`)
105
+ apiError = true
106
+ } catch (error) {
107
+ try {
108
+ await cloneRepo(apiRepoUrl, apiRepoPath)
109
+ } catch (error) {
110
+ logger.warn(`Error cloning API: ${getErrorMessage(error) || `Unknown error`}`)
111
+ apiError = true
112
+ }
113
+ }
114
+
115
+ try {
116
+ const createdEmptyBranch = await checkoutSelectionBranch(apiSelectionHash, apiRepoPath, `remotes/origin`)
117
+
118
+ if (!createdEmptyBranch) {
119
+ await writeNewRepoConfig()
120
+ logger.info(`API is ready.`)
121
+ } else if (apiCommits?.length) {
122
+ const convert = {
123
+ typeScriptToJavaScript: props.selection.apiLanguage === `JavaScript`,
124
+ typeDocToJSDoc: props.selection.apiDocs === `JSDoc`,
125
+ npmToYarn: props.selection.apiPackageManager === `Yarn`,
126
+ }
127
+
128
+ logger.info(`You are the first to assemble this particular Molecule API!`)
129
+
130
+ resetMergeDrivers = enableMergeDrivers(apiRepoPath)
131
+
132
+ logger.info(`Cherry picking the relevant commits...`)
133
+
134
+ while (apiCommits.length) {
135
+ const commit = apiCommits.shift()
136
+
137
+ if (commit?.hash) {
138
+ logger.info(`Cherry picking ${commit.hash.substring(0, 7)} (${commit.subject || `Unknown commit??? This should never happen!!!`})`)
139
+ await cherryPick(commit.hash, apiRepoPath, `-x`)
140
+ } else {
141
+ logger.info(`No commit to cherry pick??? This should never happen!!!`)
142
+ }
143
+ }
144
+
145
+ resetMergeDrivers()
146
+
147
+ if (convert.typeDocToJSDoc || convert.npmToYarn || convert.typeScriptToJavaScript) {
148
+ // Delete package-lock.json for any of these.
149
+ try {
150
+ const packageLockPath = path.join(apiRepoPath, `package-lock.json`)
151
+ await fs.promises.unlink(packageLockPath)
152
+ } catch (error) {
153
+ }
154
+ }
155
+
156
+ if (convert.typeDocToJSDoc) {
157
+ try {
158
+ logger.info(`Converting TypeDoc to JSDoc...`)
159
+ await convertTypeDocToJSDoc(apiRepoPath)
160
+ await asyncExec(`git -C "${apiRepoPath}" add --all`)
161
+ await asyncExec(`git -C "${apiRepoPath}" commit -m "convert typedoc to jsdoc"`)
162
+ } catch (error) {
163
+ logger.warn(`Error: ${getErrorMessage(error)}`)
164
+ throw error
165
+ }
166
+ }
167
+
168
+ if (convert.npmToYarn) {
169
+ try {
170
+ logger.info(`Converting NPM to Yarn...`)
171
+ await convertNpmToYarn(apiRepoPath)
172
+ await asyncExec(`git -C "${apiRepoPath}" add --all`)
173
+ await asyncExec(`git -C "${apiRepoPath}" commit -m "convert npm to yarn"`)
174
+ } catch (error) {
175
+ logger.warn(`Error: ${getErrorMessage(error)}`)
176
+ throw error
177
+ }
178
+ }
179
+
180
+ if (convert.typeScriptToJavaScript) {
181
+ try {
182
+ logger.info(`Converting TypeScript to JavaScript...`)
183
+ await convertTypeScriptToJavaScript(apiRepoPath)
184
+ await asyncExec(`git -C "${apiRepoPath}" add --all`)
185
+ await asyncExec(`git -C "${apiRepoPath}" commit -m "convert typescript to javascript"`)
186
+ } catch (error) {
187
+ logger.warn(`Error: ${getErrorMessage(error)}`)
188
+ throw error
189
+ }
190
+ }
191
+
192
+ await writeNewRepoConfig()
193
+ logger.info(`API is ready.`)
194
+ } else {
195
+ logger.warn(`Created an empty branch but no commits to cherry pick.`)
196
+ apiError = true
197
+ }
198
+ } catch (error) {
199
+ if (resetMergeDrivers) {
200
+ resetMergeDrivers()
201
+ }
202
+
203
+ logger.warn(`Error assembling API: ${getErrorMessage(error) || `Unknown error`}`)
204
+ apiError = true
205
+ }
206
+ }
207
+
208
+ if (appSelectionHash && appRepoUrl) {
209
+ let resetMergeDrivers: null | (() => void) = null
210
+ const writeNewRepoConfig = async () => {
211
+ const repoConfigPath = path.join(appRepoPath, `.mlclrc.json`)
212
+
213
+ logger.info(`Creating ${appRepoPath}/.mlclrc.json...`)
214
+
215
+ try {
216
+ await fs.promises.access(repoConfigPath, fs.constants.R_OK | fs.constants.W_OK)
217
+ logger.info(`${appRepoPath}/.mlclrc.json already exists on your machine!`)
218
+ appError = true
219
+ } catch (error) {
220
+ await fs.promises.writeFile(repoConfigPath, JSON.stringify({ id }, null, 2))
221
+ }
222
+ }
223
+
224
+ try {
225
+ await fs.promises.access(appRepoPath, fs.constants.R_OK | fs.constants.W_OK)
226
+ logger.warn(`Error cloning app: ${appRepoPath} already exists on your machine!`)
227
+ appError = true
228
+ } catch (error) {
229
+ try {
230
+ await cloneRepo(appRepoUrl, appRepoPath)
231
+ } catch (error) {
232
+ logger.warn(`Error cloning app: ${getErrorMessage(error) || `Unknown error`}`)
233
+ appError = true
234
+ }
235
+ }
236
+
237
+ try {
238
+ const createdEmptyBranch = await checkoutSelectionBranch(appSelectionHash, appRepoPath, `remotes/origin`)
239
+
240
+ if (!createdEmptyBranch) {
241
+ await writeNewRepoConfig()
242
+ logger.info(`App is ready.`)
243
+ } else if (appCommits?.length) {
244
+ const convert = {
245
+ typeScriptToJavaScript: props.selection.appLanguage === `JavaScript`,
246
+ typeDocToJSDoc: props.selection.appDocs === `JSDoc`,
247
+ npmToYarn: props.selection.appPackageManager === `Yarn`,
248
+ }
249
+
250
+ logger.info(`You are the first to assemble this particular Molecule app!`)
251
+
252
+ resetMergeDrivers = enableMergeDrivers(appRepoPath)
253
+
254
+ logger.info(`Cherry picking the relevant commits...`)
255
+
256
+ while (appCommits.length) {
257
+ const commit = appCommits.shift()
258
+
259
+ if (commit?.hash) {
260
+ logger.info(`Cherry picking ${commit.hash.substring(0, 7)} (${commit.subject || `Unknown commit??? This should never happen!!!`})`)
261
+ await cherryPick(commit.hash, appRepoPath, `-x`)
262
+ } else {
263
+ logger.info(`No commit to cherry pick??? This should never happen!!!`)
264
+ }
265
+ }
266
+
267
+ resetMergeDrivers()
268
+
269
+ if (convert.typeDocToJSDoc || convert.npmToYarn || convert.typeScriptToJavaScript) {
270
+ // Delete package-lock.json for any of these.
271
+ try {
272
+ const packageLockPath = path.join(appRepoPath, `package-lock.json`)
273
+ await fs.promises.unlink(packageLockPath)
274
+ } catch (error) {
275
+ }
276
+ }
277
+
278
+ if (convert.typeDocToJSDoc) {
279
+ try {
280
+ logger.info(`Converting TypeDoc to JSDoc...`)
281
+ await convertTypeDocToJSDoc(appRepoPath)
282
+ await asyncExec(`git -C "${appRepoPath}" add --all`)
283
+ await asyncExec(`git -C "${appRepoPath}" commit -m "convert typedoc to jsdoc"`)
284
+ } catch (error) {
285
+ logger.warn(`Error: ${getErrorMessage(error)}`)
286
+ throw error
287
+ }
288
+ }
289
+
290
+ if (convert.npmToYarn) {
291
+ try {
292
+ logger.info(`Converting NPM to Yarn...`)
293
+ await convertNpmToYarn(appRepoPath)
294
+ await asyncExec(`git -C "${appRepoPath}" add --all`)
295
+ await asyncExec(`git -C "${appRepoPath}" commit -m "convert npm to yarn"`)
296
+ } catch (error) {
297
+ logger.warn(`Error: ${getErrorMessage(error)}`)
298
+ throw error
299
+ }
300
+ }
301
+
302
+ if (convert.typeScriptToJavaScript) {
303
+ try {
304
+ logger.info(`Converting TypeScript to JavaScript...`)
305
+ await convertTypeScriptToJavaScript(appRepoPath)
306
+ await asyncExec(`git -C "${appRepoPath}" add --all`)
307
+ await asyncExec(`git -C "${appRepoPath}" commit -m "convert typescript to javascript"`)
308
+ } catch (error) {
309
+ logger.warn(`Error: ${getErrorMessage(error)}`)
310
+ throw error
311
+ }
312
+ }
313
+
314
+ await writeNewRepoConfig()
315
+ logger.info(`App is ready.`)
316
+ } else {
317
+ logger.warn(`Created an empty branch but no commits to cherry pick.`)
318
+ appError = true
319
+ }
320
+ } catch (error) {
321
+ if (resetMergeDrivers) {
322
+ resetMergeDrivers()
323
+ }
324
+
325
+ logger.warn(`Error assembling app: ${getErrorMessage(error) || `Unknown error`}`)
326
+ appError = true
327
+ }
328
+ }
329
+
330
+ if (apiSelectionHash && appSelectionHash) {
331
+ if (apiError || appError) {
332
+ logger.info(`There may have been an error cloning your Molecule.`)
333
+ logger.info(`If everything seems good, see \`${apiRepoPath}/README.md\` and \`${appRepoPath}/README.md\` to get started.`)
334
+ logger.info(`If not, please contact developer-support@molecule.dev so we can fix this.`)
335
+ } else {
336
+ logger.info(`Your Molecule was successfully cloned!`)
337
+ logger.info(`See \`${apiRepoPath}/README.md\` and \`${appRepoPath}/README.md\` to get started.`)
338
+ }
339
+ } else if (apiSelectionHash) {
340
+ if (apiError) {
341
+ logger.info(`There may have been an error cloning your Molecule API.`)
342
+ logger.info(`If everything seems good, see \`${apiRepoPath}/README.md\` to get started.`)
343
+ logger.info(`If not, please contact developer-support@molecule.dev so we can fix this.`)
344
+ } else {
345
+ logger.info(`Your Molecule API was successfully cloned!`)
346
+ logger.info(`See \`${apiRepoPath}/README.md\` to get started.`)
347
+ }
348
+ } else if (appSelectionHash) {
349
+ if (appError) {
350
+ logger.info(`There may have been an error cloning your Molecule app.`)
351
+ logger.info(`If everything seems good, see \`${appRepoPath}/README.md\` to get started.`)
352
+ logger.info(`If not, please contact developer-support@molecule.dev so we can fix this.`)
353
+ } else {
354
+ logger.info(`Your Molecule app was successfully cloned!`)
355
+ logger.info(`See \`${appRepoPath}/README.md\` to get started.`)
356
+ }
357
+ }
358
+ } catch (error) {
359
+ logger.warn(`Error cloning Molecule: ${getErrorMessage(error) || `Unknown error`}`)
360
+ logger.warn(`Please contact developer-support@molecule.dev so we can fix this.`)
361
+ }
362
+ })
363
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Every available command and utilities.
3
+ *
4
+ * @module
5
+ */
6
+
7
+ export * as utilities from './utilities/index.js'
8
+
9
+ export * from './add-ssh-key.js'
10
+ export * from './buy.js'
11
+ export * from './clone.js'
12
+ export * from './info.js'
13
+ export * from './login.js'
14
+ export * from './logout.js'
15
+ export * from './remove-ssh-key.js'
16
+ export * from './subscribe.js'
17
+ export * from './unsubscribe.js'
18
+ export * from './version.js'
19
+ export * from './x-merge-json.js'
@@ -0,0 +1,40 @@
1
+ import open from 'open'
2
+ import { Command } from 'commander'
3
+ import { userLoggedIn } from '../utilities/userLoggedIn.js'
4
+ import { repoConfig } from '../repoConfig.js'
5
+ import { logger } from '../logger.js'
6
+ import { getErrorMessage } from '../utilities/getErrorMessage.js'
7
+
8
+ /**
9
+ * Adds the `info` command to the program.
10
+ */
11
+ export const info = (program: Command) => {
12
+ program.command(`info`)
13
+ .description(`opens your Molecule's information on Molecule.dev`)
14
+ .action(async () => {
15
+ const user = await userLoggedIn()
16
+
17
+ if (!user) {
18
+ return
19
+ }
20
+
21
+ if (!repoConfig?.id) {
22
+ logger.warn(`Not within a Molecule repository or \`.mlclrc.json\` not found.`)
23
+ return
24
+ }
25
+
26
+ const url = `${process.env.WEB_ORIGIN}/#/${encodeURIComponent(repoConfig.id)}`
27
+ const childProcess = await open(url)
28
+
29
+ childProcess.on(`error`, error => {
30
+ logger.warn(`Error opening the URL: ${getErrorMessage(error)}`)
31
+ logger.warn(`If your browser didn't open on its own, visit the following URL: ${url}`)
32
+ })
33
+
34
+ childProcess.on(`close`, code => {
35
+ if (code !== 0) {
36
+ logger.warn(`If your browser didn't open on its own, visit the following URL: ${url}`)
37
+ }
38
+ })
39
+ })
40
+ }
@@ -0,0 +1,24 @@
1
+ import { Command } from 'commander'
2
+ import { userConfig } from '../userConfig.js'
3
+ import { synchronizeUserConfig } from '../utilities/synchronizeUserConfig.js'
4
+ import * as API from '../API/index.js'
5
+ import { logger } from '../logger.js'
6
+
7
+ /**
8
+ * Adds the `login` command to the program.
9
+ */
10
+ export const login = (program: Command) => {
11
+ program.command(`login`)
12
+ .description(`log into mlcl with your Molecule.dev account via the browser`)
13
+ .action(async () => {
14
+ await synchronizeUserConfig()
15
+
16
+ if (userConfig.user) {
17
+ logger.info(`Logged in as ${userConfig.user.username}.`)
18
+ logger.info(`If you would like to log in as another user, run \`mlcl logout\` first.`)
19
+ return
20
+ }
21
+
22
+ await API.resource.user.utilities.logIn()
23
+ })
24
+ }
@@ -0,0 +1,27 @@
1
+ import { Command } from 'commander'
2
+ import { userConfig } from '../userConfig.js'
3
+ import { synchronizeUserConfig } from '../utilities/synchronizeUserConfig.js'
4
+ import { writeUserConfig } from '../utilities/writeUserConfig.js'
5
+ import * as API from '../API/index.js'
6
+ import { logger } from '../logger.js'
7
+
8
+ /**
9
+ * Adds the `logout` command to the program.
10
+ */
11
+ export const logout = (program: Command) => {
12
+ program.command(`logout`)
13
+ .description(`logs out of your Molecule.dev account`)
14
+ .action(async () => {
15
+ await synchronizeUserConfig()
16
+
17
+ if (!userConfig?.user?.id) {
18
+ logger.warn(`Not logged in.`)
19
+ return
20
+ }
21
+
22
+ API.resource.user.logOut()
23
+ delete userConfig.user
24
+ writeUserConfig()
25
+ logger.info(`Logged out.`)
26
+ })
27
+ }
@@ -0,0 +1,42 @@
1
+ import { Command } from 'commander'
2
+ import { userConfig } from '../userConfig.js'
3
+ import { userLoggedIn } from '../utilities/userLoggedIn.js'
4
+ import * as API from '../API/index.js'
5
+ import { logger } from '../logger.js'
6
+ import { getErrorMessage } from '../utilities/getErrorMessage.js'
7
+
8
+ /**
9
+ * Adds the `remove-ssh-key` command to the program.
10
+ */
11
+ export const removeSshKey = (program: Command) => {
12
+ program.command(`remove-ssh-key`)
13
+ .description(`removes your current CLI device's SSH key from your Molecule.dev account`)
14
+ .action(async () => {
15
+ const user = await userLoggedIn()
16
+
17
+ if (!user) {
18
+ return
19
+ }
20
+
21
+ const deviceId = userConfig.userIdsToDeviceIds?.[user.id] || ``
22
+
23
+ if (!deviceId) {
24
+ logger.warn(`No device ID for the current user. Try logging out and in again.`)
25
+ return
26
+ }
27
+
28
+ logger.info(`Removing public SSH key from the API...`)
29
+
30
+ try {
31
+ const { props } = (await API.resource.device.update(deviceId, { sshKey: `` })).data
32
+
33
+ if (props.sshKey === ``) {
34
+ logger.info(`SSH key successfully removed!`)
35
+ } else {
36
+ logger.info(`Failed to remove SSH key.`)
37
+ }
38
+ } catch (error) {
39
+ logger.warn(`Failed to remove SSH key. ${getErrorMessage(error)}`)
40
+ }
41
+ })
42
+ }
@@ -0,0 +1,84 @@
1
+ import open from 'open'
2
+ import { Command } from 'commander'
3
+ import { userLoggedIn } from '../utilities/userLoggedIn.js'
4
+ import { repoConfig } from '../repoConfig.js'
5
+ import * as API from '../API/index.js'
6
+ import { logger } from '../logger.js'
7
+ import { getErrorMessage } from '../utilities/getErrorMessage.js'
8
+ import { plans } from '../API/resource/payment/plans.js'
9
+
10
+ /**
11
+ * Adds the `subscribe` command to the program.
12
+ */
13
+ export const subscribe = (program: Command) => {
14
+ program.command(`subscribe`)
15
+ .description(`enables or renews your Molecule's subscription`)
16
+ .action(async () => {
17
+ const user = await userLoggedIn()
18
+
19
+ if (!user) {
20
+ return
21
+ }
22
+
23
+ if (!repoConfig?.id) {
24
+ logger.warn(`Not within a Molecule repository or \`.mlclrc.json\` not found.`)
25
+ return
26
+ }
27
+
28
+ try {
29
+ const molecule = (await API.resource.molecule.read(repoConfig.id)).data.props
30
+
31
+ if (!molecule) {
32
+ throw new Error(`Failed to fetch Molecule by ID ${repoConfig.id}.`)
33
+ }
34
+
35
+ const planKey = molecule.planKey || ``
36
+ const planAutoRenews = molecule.planAutoRenews
37
+ const planExpiresAt = molecule.planExpiresAt
38
+ const planExpiresDate = planKey
39
+ ? (planAutoRenews || plans[planKey].autoRenews)
40
+ ? planExpiresAt && new Date(planExpiresAt)
41
+ : null
42
+ : undefined
43
+ const planExpiresLocaleString = (planExpiresDate && planExpiresDate.toLocaleString()) || `never`
44
+
45
+ if (planKey && (planAutoRenews || !plans[planKey].autoRenews)) {
46
+ if (planAutoRenews) {
47
+ logger.info(`Your Molecule is already subscribed!`)
48
+ logger.info(`Your ${plans[planKey].title} plan will ${planAutoRenews ? `automatically renew` : `expire`} ${planExpiresLocaleString}.`)
49
+ } else {
50
+ logger.info(`You already have a ${plans[planKey].title} license, so no subscription is necessary.`)
51
+ logger.info(`Thank you!`)
52
+ }
53
+ return
54
+ }
55
+
56
+ logger.info(`Subscribing...`)
57
+
58
+ const { props, checkoutUrl } = (await API.resource.molecule.updatePlan(molecule.id, { planKey: `stripeMonthlyMolecule`, quantity: 1 })).data
59
+
60
+ if (checkoutUrl) {
61
+ logger.info(`Opening your browser to complete checkout via Stripe...`)
62
+
63
+ const childProcess = await open(checkoutUrl)
64
+
65
+ childProcess.on(`error`, error => {
66
+ logger.warn(`Error opening the checkout URL: ${getErrorMessage(error)}`)
67
+ logger.warn(`If your browser didn't open on its own, visit the following URL: ${checkoutUrl}`)
68
+ })
69
+
70
+ childProcess.on(`close`, code => {
71
+ if (code !== 0) {
72
+ logger.warn(`If your browser didn't open on its own, visit the following URL: ${checkoutUrl}`)
73
+ }
74
+ })
75
+ } else if (props?.planKey) {
76
+ logger.info(`Your Molecule is now subscribed! Thank you!`)
77
+ } else {
78
+ throw new Error(`Unexpected response. Please contact developer-support@molecule.dev so we can fix this.`)
79
+ }
80
+ } catch (error) {
81
+ logger.warn(`Failed to subscribe. ${getErrorMessage(error)}`)
82
+ }
83
+ })
84
+ }
@@ -0,0 +1,67 @@
1
+ import { Command } from 'commander'
2
+ import { userLoggedIn } from '../utilities/userLoggedIn.js'
3
+ import { repoConfig } from '../repoConfig.js'
4
+ import * as API from '../API/index.js'
5
+ import { logger } from '../logger.js'
6
+ import { getErrorMessage } from '../utilities/getErrorMessage.js'
7
+ import { plans } from '../API/resource/payment/plans.js'
8
+
9
+ /**
10
+ * Adds the `unsubscribe` command to the program.
11
+ */
12
+ export const unsubscribe = (program: Command) => {
13
+ program.command(`unsubscribe`)
14
+ .description(`cancels your Molecule's subscription`)
15
+ .action(async () => {
16
+ const user = await userLoggedIn()
17
+
18
+ if (!user) {
19
+ return
20
+ }
21
+
22
+ if (!repoConfig?.id) {
23
+ logger.warn(`Not within a Molecule repository or \`.mlclrc.json\` not found.`)
24
+ return
25
+ }
26
+
27
+ try {
28
+ const molecule = (await API.resource.molecule.read(repoConfig.id)).data.props
29
+
30
+ if (!molecule) {
31
+ throw new Error(`Failed to fetch Molecule by ID ${repoConfig.id}.`)
32
+ }
33
+
34
+ const planKey = molecule.planKey || ``
35
+ const planAutoRenews = molecule.planAutoRenews
36
+ const planExpiresAt = molecule.planExpiresAt
37
+ const planExpiresDate = planKey
38
+ ? (planAutoRenews || plans[planKey].autoRenews)
39
+ ? planExpiresAt && new Date(planExpiresAt)
40
+ : null
41
+ : undefined
42
+ const planExpiresLocaleString = (planExpiresDate && planExpiresDate.toLocaleString()) || `never`
43
+
44
+ if (!planAutoRenews) {
45
+ if (planKey && !plans[planKey].autoRenews) {
46
+ logger.info(`You already have a ${plans[planKey].title} license, so there is nothing to unsubscribe.`)
47
+ logger.info(`Thank you!`)
48
+ } else {
49
+ logger.info(`Your Molecule is not subscribed.`)
50
+ }
51
+ return
52
+ }
53
+
54
+ logger.info(`Unsubscribing...`)
55
+
56
+ const { props } = (await API.resource.molecule.updatePlan(molecule.id, { planKey: `` })).data
57
+
58
+ if (props?.planAutoRenews === false) {
59
+ logger.info(`Your Molecule has been unsubscribed and will expire ${planExpiresLocaleString}.`)
60
+ } else {
61
+ throw new Error(`Unexpected response. Please contact developer-support@molecule.dev so we can fix this.`)
62
+ }
63
+ } catch (error) {
64
+ logger.warn(`Failed to unsubscribe. ${getErrorMessage(error)}`)
65
+ }
66
+ })
67
+ }
@@ -0,0 +1,7 @@
1
+ import { promisify } from 'util'
2
+ import { exec } from 'child_process'
3
+
4
+ /**
5
+ * Promisified version of `exec`.
6
+ */
7
+ export const asyncExec = promisify(exec)