@capgo/cli 4.12.14 → 4.13.2

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.
@@ -1,6 +1,6 @@
1
- import process from 'node:process'
1
+ import { exit } from 'node:process'
2
2
  import { program } from 'commander'
3
- import * as p from '@clack/prompts'
3
+ import { intro, log } from '@clack/prompts'
4
4
  import { checkAppExistsAndHasPermissionOrgErr } from '../api/app'
5
5
  import type { OptionsBase } from '../utils'
6
6
  import { OrganizationPerm, createSupabaseClient, findSavedKey, getConfig, verifyUser } from '../utils'
@@ -20,18 +20,18 @@ export async function currentBundle(channel: string, appId: string, options: Opt
20
20
  const { quiet } = options
21
21
 
22
22
  if (!quiet)
23
- p.intro(`List current bundle`)
23
+ intro(`List current bundle`)
24
24
 
25
25
  options.apikey = options.apikey || findSavedKey(quiet)
26
- const config = await getConfig()
27
- appId = appId || config?.app?.appId
26
+ const extConfig = await getConfig()
27
+ appId = appId || extConfig?.config?.appId
28
28
 
29
29
  if (!options.apikey) {
30
- p.log.error('Missing API key, you need to provide a API key to upload your bundle')
30
+ log.error('Missing API key, you need to provide a API key to upload your bundle')
31
31
  program.error('')
32
32
  }
33
33
  if (!appId) {
34
- p.log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
34
+ log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
35
35
  program.error('')
36
36
  }
37
37
  const supabase = await createSupabaseClient(options.apikey)
@@ -41,7 +41,7 @@ export async function currentBundle(channel: string, appId: string, options: Opt
41
41
  await checkAppExistsAndHasPermissionOrgErr(supabase, options.apikey, appId, OrganizationPerm.read)
42
42
 
43
43
  if (!channel) {
44
- p.log.error(`Please provide a channel to get the bundle from.`)
44
+ log.error(`Please provide a channel to get the bundle from.`)
45
45
  program.error('')
46
46
  }
47
47
 
@@ -53,20 +53,20 @@ export async function currentBundle(channel: string, appId: string, options: Opt
53
53
  .limit(1)
54
54
 
55
55
  if (error || supabaseChannel.length === 0) {
56
- p.log.error(`Error retrieving channel ${channel} for app ${appId}. Perhaps the channel does not exists?`)
56
+ log.error(`Error retrieving channel ${channel} for app ${appId}. Perhaps the channel does not exists?`)
57
57
  program.error('')
58
58
  }
59
59
 
60
60
  const { version } = supabaseChannel[0] as any as Channel
61
61
  if (!version) {
62
- p.log.error(`Error retrieving channel ${channel} for app ${appId}. Perhaps the channel does not exists?`)
62
+ log.error(`Error retrieving channel ${channel} for app ${appId}. Perhaps the channel does not exists?`)
63
63
  program.error('')
64
64
  }
65
65
 
66
66
  if (!quiet)
67
- p.log.info(`Current bundle for channel ${channel} is ${version.name}`)
67
+ log.info(`Current bundle for channel ${channel} is ${version.name}`)
68
68
  else
69
- p.log.info(version.name)
69
+ log.info(version.name)
70
70
 
71
- process.exit()
71
+ exit()
72
72
  }
@@ -1,24 +1,24 @@
1
- import process from 'node:process'
1
+ import { exit } from 'node:process'
2
2
  import { program } from 'commander'
3
- import * as p from '@clack/prompts'
3
+ import { intro, log, outro } from '@clack/prompts'
4
4
  import { checkAppExistsAndHasPermissionOrgErr } from '../api/app'
5
5
  import { delChannel } from '../api/channels'
6
6
  import type { OptionsBase } from '../utils'
7
7
  import { OrganizationPerm, createSupabaseClient, findSavedKey, formatError, getConfig, getOrganizationId, useLogSnag, verifyUser } from '../utils'
8
8
 
9
9
  export async function deleteChannel(channelId: string, appId: string, options: OptionsBase) {
10
- p.intro(`Delete channel`)
10
+ intro(`Delete channel`)
11
11
  options.apikey = options.apikey || findSavedKey()
12
- const config = await getConfig()
13
- appId = appId || config?.app?.appId
12
+ const extConfig = await getConfig()
13
+ appId = appId || extConfig?.config?.appId
14
14
  const snag = useLogSnag()
15
15
 
16
16
  if (!options.apikey) {
17
- p.log.error('Missing API key, you need to provide a API key to upload your bundle')
17
+ log.error('Missing API key, you need to provide a API key to upload your bundle')
18
18
  program.error('')
19
19
  }
20
20
  if (!appId) {
21
- p.log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
21
+ log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
22
22
  program.error('')
23
23
  }
24
24
  const supabase = await createSupabaseClient(options.apikey)
@@ -27,15 +27,15 @@ export async function deleteChannel(channelId: string, appId: string, options: O
27
27
  // Check we have app access to this appId
28
28
  await checkAppExistsAndHasPermissionOrgErr(supabase, options.apikey, appId, OrganizationPerm.admin)
29
29
 
30
- p.log.info(`Deleting channel ${appId}#${channelId} from Capgo`)
30
+ log.info(`Deleting channel ${appId}#${channelId} from Capgo`)
31
31
  try {
32
32
  const deleteStatus = await delChannel(supabase, channelId, appId, userId)
33
33
  if (deleteStatus.error) {
34
- p.log.error(`Cannot delete Channel 🙀 ${formatError(deleteStatus.error)}`)
34
+ log.error(`Cannot delete Channel 🙀 ${formatError(deleteStatus.error)}`)
35
35
  program.error('')
36
36
  }
37
37
  const orgId = await getOrganizationId(supabase, appId)
38
- p.log.success(`Channel deleted`)
38
+ log.success(`Channel deleted`)
39
39
  await snag.track({
40
40
  channel: 'channel',
41
41
  event: 'Delete channel',
@@ -50,8 +50,8 @@ export async function deleteChannel(channelId: string, appId: string, options: O
50
50
  }).catch()
51
51
  }
52
52
  catch (error) {
53
- p.log.error(`Cannot delete Channel 🙀`)
53
+ log.error(`Cannot delete Channel 🙀`)
54
54
  }
55
- p.outro(`Done ✅`)
56
- process.exit()
55
+ outro(`Done ✅`)
56
+ exit()
57
57
  }
@@ -1,23 +1,23 @@
1
- import process from 'node:process'
1
+ import { exit } from 'node:process'
2
2
  import { program } from 'commander'
3
- import * as p from '@clack/prompts'
3
+ import { intro, log, outro } from '@clack/prompts'
4
4
  import { checkAppExistsAndHasPermissionOrgErr } from '../api/app'
5
5
  import { displayChannels, getActiveChannels } from '../api/channels'
6
6
  import type { OptionsBase } from '../utils'
7
7
  import { OrganizationPerm, createSupabaseClient, findSavedKey, getConfig, useLogSnag, verifyUser } from '../utils'
8
8
 
9
9
  export async function listChannels(appId: string, options: OptionsBase) {
10
- p.intro(`List channels`)
10
+ intro(`List channels`)
11
11
  options.apikey = options.apikey || findSavedKey()
12
- const config = await getConfig()
13
- appId = appId || config?.app?.appId
12
+ const extConfig = await getConfig()
13
+ appId = appId || extConfig?.config?.appId
14
14
  const snag = useLogSnag()
15
15
 
16
16
  if (!options.apikey)
17
- p.log.error('Missing API key, you need to provide a API key to upload your bundle')
17
+ log.error('Missing API key, you need to provide a API key to upload your bundle')
18
18
 
19
19
  if (!appId) {
20
- p.log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
20
+ log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
21
21
  program.error('')
22
22
  }
23
23
  const supabase = await createSupabaseClient(options.apikey)
@@ -26,12 +26,12 @@ export async function listChannels(appId: string, options: OptionsBase) {
26
26
  // Check we have app access to this appId
27
27
  await checkAppExistsAndHasPermissionOrgErr(supabase, options.apikey, appId, OrganizationPerm.read)
28
28
 
29
- p.log.info(`Querying available channels in Capgo`)
29
+ log.info(`Querying available channels in Capgo`)
30
30
 
31
31
  // Get all active app versions we might possibly be able to cleanup
32
32
  const allVersions = await getActiveChannels(supabase, appId)
33
33
 
34
- p.log.info(`Active channels in Capgo: ${allVersions?.length}`)
34
+ log.info(`Active channels in Capgo: ${allVersions?.length}`)
35
35
 
36
36
  displayChannels(allVersions)
37
37
  await snag.track({
@@ -44,6 +44,6 @@ export async function listChannels(appId: string, options: OptionsBase) {
44
44
  },
45
45
  notify: false,
46
46
  }).catch()
47
- p.outro(`Done ✅`)
48
- process.exit()
47
+ outro(`Done ✅`)
48
+ exit()
49
49
  }
@@ -1,6 +1,6 @@
1
- import process from 'node:process'
1
+ import { exit } from 'node:process'
2
2
  import { program } from 'commander'
3
- import * as p from '@clack/prompts'
3
+ import { intro, log, outro } from '@clack/prompts'
4
4
  import type { Database } from '../types/supabase.types'
5
5
  import { checkAppExistsAndHasPermissionOrgErr } from '../api/app'
6
6
  import type {
@@ -14,6 +14,7 @@ import {
14
14
  formatError,
15
15
  getConfig,
16
16
  getOrganizationId,
17
+ readPackageJson,
17
18
  updateOrCreateChannel,
18
19
  useLogSnag,
19
20
  verifyUser,
@@ -36,18 +37,18 @@ interface Options extends OptionsBase {
36
37
  const disableAutoUpdatesPossibleOptions = ['major', 'minor', 'metadata', 'patch', 'none']
37
38
 
38
39
  export async function setChannel(channel: string, appId: string, options: Options) {
39
- p.intro(`Set channel`)
40
+ intro(`Set channel`)
40
41
  options.apikey = options.apikey || findSavedKey()
41
- const config = await getConfig()
42
- appId = appId || config?.app?.appId
42
+ const extConfig = await getConfig()
43
+ appId = appId || extConfig?.config?.appId
43
44
  const snag = useLogSnag()
44
45
 
45
46
  if (!options.apikey) {
46
- p.log.error('Missing API key, you need to provide a API key to upload your bundle')
47
+ log.error('Missing API key, you need to provide a API key to upload your bundle')
47
48
  program.error('')
48
49
  }
49
50
  if (!appId) {
50
- p.log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
51
+ log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
51
52
  program.error('')
52
53
  }
53
54
  const supabase = await createSupabaseClient(options.apikey)
@@ -59,11 +60,11 @@ export async function setChannel(channel: string, appId: string, options: Option
59
60
 
60
61
  const { bundle, state, downgrade, latest, upgrade, ios, android, selfAssign, disableAutoUpdate, dev, emulator } = options
61
62
  if (!channel) {
62
- p.log.error('Missing argument, you need to provide a channel')
63
+ log.error('Missing argument, you need to provide a channel')
63
64
  program.error('')
64
65
  }
65
66
  if (latest && bundle) {
66
- p.log.error('Cannot set latest and bundle at the same time')
67
+ log.error('Cannot set latest and bundle at the same time')
67
68
  program.error('')
68
69
  }
69
70
  if (bundle == null
@@ -77,7 +78,7 @@ export async function setChannel(channel: string, appId: string, options: Option
77
78
  && dev == null
78
79
  && emulator == null
79
80
  && disableAutoUpdate == null) {
80
- p.log.error('Missing argument, you need to provide a option to set')
81
+ log.error('Missing argument, you need to provide a option to set')
81
82
  program.error('')
82
83
  }
83
84
  try {
@@ -89,7 +90,8 @@ export async function setChannel(channel: string, appId: string, options: Option
89
90
  owner_org: orgId,
90
91
  version: undefined as any,
91
92
  }
92
- const bundleVersion = latest ? config?.app?.package?.version : bundle
93
+ const pack = await readPackageJson()
94
+ const bundleVersion = latest ? pack?.version : bundle
93
95
  if (bundleVersion != null) {
94
96
  const { data, error: vError } = await supabase
95
97
  .from('app_versions')
@@ -100,33 +102,33 @@ export async function setChannel(channel: string, appId: string, options: Option
100
102
  .eq('deleted', false)
101
103
  .single()
102
104
  if (vError || !data) {
103
- p.log.error(`Cannot find version ${bundleVersion}`)
105
+ log.error(`Cannot find version ${bundleVersion}`)
104
106
  program.error('')
105
107
  }
106
- p.log.info(`Set ${appId} channel: ${channel} to @${bundleVersion}`)
108
+ log.info(`Set ${appId} channel: ${channel} to @${bundleVersion}`)
107
109
  channelPayload.version = data.id
108
110
  }
109
111
  if (state != null) {
110
112
  if (state === 'public' || state === 'private')
111
- p.log.info(`Set ${appId} channel: ${channel} to public or private is deprecated, use default or normal instead`)
113
+ log.info(`Set ${appId} channel: ${channel} to public or private is deprecated, use default or normal instead`)
112
114
 
113
- p.log.info(`Set ${appId} channel: ${channel} to ${state === 'public' || state === 'default' ? 'default' : 'normal'}`)
115
+ log.info(`Set ${appId} channel: ${channel} to ${state === 'public' || state === 'default' ? 'default' : 'normal'}`)
114
116
  channelPayload.public = state === 'public' || state === 'default'
115
117
  }
116
118
  if (downgrade != null) {
117
- p.log.info(`Set ${appId} channel: ${channel} to ${downgrade ? 'allow' : 'disallow'} downgrade`)
119
+ log.info(`Set ${appId} channel: ${channel} to ${downgrade ? 'allow' : 'disallow'} downgrade`)
118
120
  channelPayload.disableAutoUpdateUnderNative = !downgrade
119
121
  }
120
122
  if (ios != null) {
121
- p.log.info(`Set ${appId} channel: ${channel} to ${ios ? 'allow' : 'disallow'} ios update`)
123
+ log.info(`Set ${appId} channel: ${channel} to ${ios ? 'allow' : 'disallow'} ios update`)
122
124
  channelPayload.ios = !!ios
123
125
  }
124
126
  if (android != null) {
125
- p.log.info(`Set ${appId} channel: ${channel} to ${android ? 'allow' : 'disallow'} android update`)
127
+ log.info(`Set ${appId} channel: ${channel} to ${android ? 'allow' : 'disallow'} android update`)
126
128
  channelPayload.android = !!android
127
129
  }
128
130
  if (selfAssign != null) {
129
- p.log.info(`Set ${appId} channel: ${channel} to ${selfAssign ? 'allow' : 'disallow'} self assign to this channel`)
131
+ log.info(`Set ${appId} channel: ${channel} to ${selfAssign ? 'allow' : 'disallow'} self assign to this channel`)
130
132
  channelPayload.allow_device_self_set = !!selfAssign
131
133
  }
132
134
  if (disableAutoUpdate != null) {
@@ -134,7 +136,7 @@ export async function setChannel(channel: string, appId: string, options: Option
134
136
 
135
137
  // The user passed an unimplemented strategy
136
138
  if (!disableAutoUpdatesPossibleOptions.includes(finalDisableAutoUpdate)) {
137
- p.log.error(`Channel strategy ${finalDisableAutoUpdate} is not known. The possible values are: ${disableAutoUpdatesPossibleOptions.join(', ')}.`)
139
+ log.error(`Channel strategy ${finalDisableAutoUpdate} is not known. The possible values are: ${disableAutoUpdatesPossibleOptions.join(', ')}.`)
138
140
  program.error('')
139
141
  }
140
142
 
@@ -144,17 +146,17 @@ export async function setChannel(channel: string, appId: string, options: Option
144
146
 
145
147
  // This cast is safe, look above
146
148
  channelPayload.disableAutoUpdate = finalDisableAutoUpdate as any
147
- p.log.info(`Set ${appId} channel: ${channel} to ${finalDisableAutoUpdate} disable update strategy to this channel`)
149
+ log.info(`Set ${appId} channel: ${channel} to ${finalDisableAutoUpdate} disable update strategy to this channel`)
148
150
  }
149
151
  try {
150
152
  const { error: dbError } = await updateOrCreateChannel(supabase, channelPayload)
151
153
  if (dbError) {
152
- p.log.error(`Cannot set channel the upload key is not allowed to do that, use the "all" for this.`)
154
+ log.error(`Cannot set channel the upload key is not allowed to do that, use the "all" for this.`)
153
155
  program.error('')
154
156
  }
155
157
  }
156
158
  catch (e) {
157
- p.log.error(`Cannot set channel the upload key is not allowed to do that, use the "all" for this.`)
159
+ log.error(`Cannot set channel the upload key is not allowed to do that, use the "all" for this.`)
158
160
  program.error('')
159
161
  }
160
162
  await snag.track({
@@ -169,9 +171,9 @@ export async function setChannel(channel: string, appId: string, options: Option
169
171
  }).catch()
170
172
  }
171
173
  catch (err) {
172
- p.log.error(`Unknow error ${formatError(err)}`)
174
+ log.error(`Unknow error ${formatError(err)}`)
173
175
  program.error('')
174
176
  }
175
- p.outro(`Done ✅`)
176
- process.exit()
177
+ outro(`Done ✅`)
178
+ exit()
177
179
  }
@@ -0,0 +1,156 @@
1
+ import { resolve } from 'node:path'
2
+ import { cwd } from 'node:process'
3
+ import { accessSync, constants, readFileSync, writeFileSync } from 'node:fs'
4
+
5
+ export const CONFIG_FILE_NAME_TS = 'capacitor.config.ts'
6
+ export const CONFIG_FILE_NAME_JSON = 'capacitor.config.json'
7
+
8
+ export interface CapacitorConfig {
9
+ appId: string
10
+ appName: string
11
+ webDir: string
12
+ plugins?: Record<string, any>
13
+ android?: Record<string, any>
14
+ [key: string]: any
15
+ }
16
+
17
+ interface ExtConfigPairs {
18
+ config: CapacitorConfig
19
+ path: string
20
+ }
21
+
22
+ function parseConfigObject(configString: string): CapacitorConfig {
23
+ // Remove comments
24
+ const noComments = configString.replace(/\/\/.*|\/\*[\s\S]*?\*\//g, '')
25
+
26
+ // Parse the object
27
+ // eslint-disable-next-line no-new-func
28
+ const configObject = Function(`return ${noComments.trim()}`)()
29
+
30
+ return configObject as CapacitorConfig
31
+ }
32
+
33
+ function loadConfigTs(content: string): CapacitorConfig {
34
+ const configRegex = /const\s+config\s*:\s*CapacitorConfig\s*=\s*(\{[\s\S]*?\n\})/
35
+ const match = content.match(configRegex)
36
+
37
+ if (!match) {
38
+ throw new Error('Unable to find config object in TypeScript file')
39
+ }
40
+
41
+ return parseConfigObject(match[1])
42
+ }
43
+
44
+ function loadConfigJson(content: string): CapacitorConfig {
45
+ return JSON.parse(content) as CapacitorConfig
46
+ }
47
+
48
+ export async function loadConfig(): Promise<ExtConfigPairs | undefined> {
49
+ const appRootDir = cwd()
50
+ const extConfigFilePathTS = resolve(appRootDir, CONFIG_FILE_NAME_TS)
51
+ const extConfigFilePathJSON = resolve(appRootDir, CONFIG_FILE_NAME_JSON)
52
+
53
+ try {
54
+ accessSync(extConfigFilePathTS, constants.R_OK)
55
+ const configContentTS = readFileSync(extConfigFilePathTS, 'utf-8')
56
+ return {
57
+ config: loadConfigTs(configContentTS),
58
+ path: extConfigFilePathTS,
59
+ }
60
+ }
61
+ catch (err) {
62
+ try {
63
+ accessSync(extConfigFilePathJSON, constants.R_OK)
64
+ const configContentJSON = readFileSync(extConfigFilePathJSON, 'utf-8')
65
+ return {
66
+ config: loadConfigJson(configContentJSON),
67
+ path: extConfigFilePathJSON,
68
+ }
69
+ }
70
+ catch (err) {
71
+ console.error('Cannot find capacitor.config.ts or capacitor.config.json')
72
+ return undefined
73
+ }
74
+ }
75
+ }
76
+
77
+ export async function writeConfig(config: ExtConfigPairs): Promise<void> {
78
+ const { config: newConfig, path } = config
79
+ const content = readFileSync(path, 'utf-8')
80
+
81
+ const updatedContent = path.endsWith('.json')
82
+ ? updateJsonContent(content, newConfig)
83
+ : updateTsContent(content, newConfig)
84
+
85
+ writeFileSync(path, updatedContent)
86
+ }
87
+
88
+ function updateJsonContent(content: string, newConfig: CapacitorConfig): string {
89
+ const jsonObj = JSON.parse(content)
90
+ if (!jsonObj.plugins)
91
+ jsonObj.plugins = {}
92
+ if (!jsonObj.plugins.CapacitorUpdater)
93
+ jsonObj.plugins.CapacitorUpdater = {}
94
+
95
+ Object.assign(jsonObj.plugins.CapacitorUpdater, newConfig.plugins?.CapacitorUpdater)
96
+
97
+ return JSON.stringify(jsonObj, null, detectIndentation(content))
98
+ }
99
+
100
+ function updateTsContent(content: string, newConfig: CapacitorConfig): string {
101
+ const capUpdaterRegex = /(\bCapacitorUpdater\s*:\s*\{[^}]*\})/g
102
+
103
+ return content.replace(capUpdaterRegex, (match) => {
104
+ const updatedSection = updateCapacitorUpdaterSection(match, newConfig.plugins?.CapacitorUpdater || {})
105
+ return updatedSection
106
+ })
107
+ }
108
+
109
+ function updateCapacitorUpdaterSection(section: string, newConfig: Record<string, any>): string {
110
+ const lines = section.split('\n')
111
+ const updatedLines = lines.map((line) => {
112
+ // eslint-disable-next-line regexp/no-super-linear-backtracking
113
+ const keyValueMatch = line.match(/^\s*(\w+)\s*:\s*(.+?),?\s*$/)
114
+ if (keyValueMatch) {
115
+ const [, key] = keyValueMatch
116
+ if (key in newConfig) {
117
+ const newValue = formatValue(newConfig[key])
118
+ return line.replace(/:\s*.+/, `: ${newValue},`)
119
+ }
120
+ }
121
+ return line
122
+ })
123
+
124
+ // Add new properties
125
+ Object.entries(newConfig).forEach(([key, value]) => {
126
+ if (!updatedLines.some(line => line.includes(`${key}:`))) {
127
+ const indent = detectIndentation(section)
128
+ updatedLines.splice(-1, 0, `${' '.repeat(indent)}${key}: ${formatValue(value)},`)
129
+ }
130
+ })
131
+
132
+ return updatedLines.join('\n')
133
+ }
134
+
135
+ function formatValue(value: any): string {
136
+ if (typeof value === 'string') {
137
+ // Convert multiline strings (like RSA keys) to single line
138
+ if (value.includes('\n')) {
139
+ return `'${value.replace(/\n/g, '\\n')}'`
140
+ }
141
+ return `'${value}'`
142
+ }
143
+ if (typeof value === 'number' || typeof value === 'boolean')
144
+ return String(value)
145
+ if (Array.isArray(value))
146
+ return `[${value.map(formatValue).join(', ')}]`
147
+ if (typeof value === 'object' && value !== null) {
148
+ return `{ ${Object.entries(value).map(([k, v]) => `${k}: ${formatValue(v)}`).join(', ')} }`
149
+ }
150
+ return 'null'
151
+ }
152
+
153
+ function detectIndentation(content: string): number {
154
+ const match = content.match(/^( +)/m)
155
+ return match ? match[1].length : 2
156
+ }
package/src/init.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs'
2
- import path from 'node:path'
3
2
  import type { ExecSyncOptions } from 'node:child_process'
4
3
  import { execSync, spawnSync } from 'node:child_process'
5
- import process from 'node:process'
4
+ import { exit } from 'node:process'
5
+ import { join } from 'node:path'
6
6
  import * as p from '@clack/prompts'
7
7
  import type LogSnag from 'logsnag'
8
8
  import semver from 'semver'
@@ -16,7 +16,7 @@ import { addAppInternal } from './app/add'
16
16
  import { checkLatest } from './api/update'
17
17
  import type { Options } from './api/app'
18
18
  import type { Organization } from './utils'
19
- import { convertAppName, createSupabaseClient, findBuildCommandForProjectType, findMainFile, findMainFileForProjectType, findProjectType, findSavedKey, getConfig, getOrganization, getPMAndCommand, useLogSnag, verifyUser } from './utils'
19
+ import { convertAppName, createSupabaseClient, findBuildCommandForProjectType, findMainFile, findMainFileForProjectType, findProjectType, findSavedKey, getConfig, getOrganization, getPMAndCommand, readPackageJson, useLogSnag, verifyUser } from './utils'
20
20
 
21
21
  interface SuperOptions extends Options {
22
22
  local: boolean
@@ -88,7 +88,7 @@ function cleanupStepsDone() {
88
88
  async function cancelCommand(command: boolean | symbol, orgId: string, snag: LogSnag) {
89
89
  if (p.isCancel(command)) {
90
90
  await markSnag('onboarding-v2', orgId, snag, 'canceled', '🤷')
91
- process.exit()
91
+ exit()
92
92
  }
93
93
  }
94
94
 
@@ -148,20 +148,20 @@ async function step4(orgId: string, snag: LogSnag, apikey: string, appId: string
148
148
  const s = p.spinner()
149
149
  s.start(`Checking if @capgo/capacitor-updater is installed`)
150
150
  let versionToInstall = 'latest'
151
- const pack = JSON.parse(readFileSync('package.json').toString())
151
+ const pack = await readPackageJson()
152
152
  let coreVersion = pack.dependencies['@capacitor/core'] || pack.devDependencies['@capacitor/core']
153
153
  coreVersion = coreVersion?.replace('^', '').replace('~', '')
154
154
  if (!coreVersion) {
155
155
  s.stop('Error')
156
156
  p.log.warn(`Cannot find @capacitor/core in package.json, please run \`capgo init\` in a capacitor project`)
157
157
  p.outro(`Bye 👋`)
158
- process.exit()
158
+ exit()
159
159
  }
160
160
  else if (semver.lt(coreVersion, '5.0.0')) {
161
161
  s.stop('Error')
162
162
  p.log.warn(`@capacitor/core version is ${coreVersion}, please update to Capacitor v5 first: ${urlMigrateV5}`)
163
163
  p.outro(`Bye 👋`)
164
- process.exit()
164
+ exit()
165
165
  }
166
166
  else if (semver.lt(coreVersion, '6.0.0')) {
167
167
  s.stop(`@capacitor/core version is ${coreVersion}, please update to Capacitor v6: ${urlMigrateV6} to access the best features of Capgo`)
@@ -171,7 +171,7 @@ async function step4(orgId: string, snag: LogSnag, apikey: string, appId: string
171
171
  s.stop('Error')
172
172
  p.log.warn(`Cannot reconize package manager, please run \`capgo init\` in a capacitor project with npm, pnpm, bun or yarn`)
173
173
  p.outro(`Bye 👋`)
174
- process.exit()
174
+ exit()
175
175
  }
176
176
  // // use pm to install capgo
177
177
  // // run command pm install @capgo/capacitor-updater@latest
@@ -201,16 +201,16 @@ async function step5(orgId: string, snag: LogSnag, apikey: string, appId: string
201
201
  const projectType = await findProjectType()
202
202
  if (projectType === 'nuxtjs-js' || projectType === 'nuxtjs-ts') {
203
203
  // Nuxt.js specific logic
204
- const nuxtDir = path.join('plugins')
204
+ const nuxtDir = join('plugins')
205
205
  if (!existsSync(nuxtDir)) {
206
206
  mkdirSync(nuxtDir, { recursive: true })
207
207
  }
208
208
  let nuxtFilePath
209
209
  if (projectType === 'nuxtjs-ts') {
210
- nuxtFilePath = path.join(nuxtDir, 'capacitorUpdater.client.ts')
210
+ nuxtFilePath = join(nuxtDir, 'capacitorUpdater.client.ts')
211
211
  }
212
212
  else {
213
- nuxtFilePath = path.join(nuxtDir, 'capacitorUpdater.client.js')
213
+ nuxtFilePath = join(nuxtDir, 'capacitorUpdater.client.js')
214
214
  }
215
215
  const nuxtFileContent = `
216
216
  import { CapacitorUpdater } from '@capgo/capacitor-updater'
@@ -257,7 +257,7 @@ async function step5(orgId: string, snag: LogSnag, apikey: string, appId: string
257
257
  p.log.warn(`Cannot find the latest version of ${projectType}, you might need to upgrade to the latest version of ${projectType}`)
258
258
  }
259
259
  p.outro(`Bye 👋`)
260
- process.exit()
260
+ exit()
261
261
  }
262
262
 
263
263
  // Open main file and inject codeInject
@@ -270,7 +270,7 @@ async function step5(orgId: string, snag: LogSnag, apikey: string, appId: string
270
270
  s.stop('Error')
271
271
  p.log.warn(`Cannot find import line in main file, use manual installation: https://capgo.app/docs/plugin/installation/`)
272
272
  p.outro(`Bye 👋`)
273
- process.exit()
273
+ exit()
274
274
  }
275
275
 
276
276
  if (mainFileContent.includes(codeInject)) {
@@ -302,7 +302,7 @@ async function step6(orgId: string, snag: LogSnag, apikey: string, appId: string
302
302
  s.stop('Error')
303
303
  p.log.warn(`Cannot create key ❌`)
304
304
  p.outro(`Bye 👋`)
305
- process.exit(1)
305
+ exit(1)
306
306
  }
307
307
  else {
308
308
  s.stop(`key created 🔑`)
@@ -321,13 +321,13 @@ async function step7(orgId: string, snag: LogSnag, apikey: string, appId: string
321
321
  const projectType = await findProjectType()
322
322
  const buildCommand = await findBuildCommandForProjectType(projectType)
323
323
  s.start(`Running: ${pm.pm} run ${buildCommand} && ${pm.runner} cap sync`)
324
- const pack = JSON.parse(readFileSync('package.json').toString())
324
+ const pack = await readPackageJson()
325
325
  // check in script build exist
326
326
  if (!pack.scripts[buildCommand]) {
327
327
  s.stop('Error')
328
328
  p.log.warn(`Cannot find ${buildCommand} script in package.json, please add it and run \`capgo init\` again`)
329
329
  p.outro(`Bye 👋`)
330
- process.exit()
330
+ exit()
331
331
  }
332
332
  execSync(`${pm.pm} run ${buildCommand} && ${pm.runner} cap sync`, execOption as ExecSyncOptions)
333
333
  s.stop(`Build & Sync Done ✅`)
@@ -353,7 +353,7 @@ async function step8(orgId: string, snag: LogSnag, apikey: string, appId: string
353
353
  s.stop('Error')
354
354
  p.log.warn(`Upload failed ❌`)
355
355
  p.outro(`Bye 👋`)
356
- process.exit()
356
+ exit()
357
357
  }
358
358
  else {
359
359
  s.stop(`Upload Done ✅`)
@@ -379,7 +379,7 @@ async function step9(orgId: string, snag: LogSnag) {
379
379
  })
380
380
  if (p.isCancel(plaformType)) {
381
381
  p.outro(`Bye 👋`)
382
- process.exit()
382
+ exit()
383
383
  }
384
384
 
385
385
  const platform = plaformType as 'ios' | 'android'
@@ -413,8 +413,8 @@ export async function initApp(apikeyCommand: string, appId: string, options: Sup
413
413
  p.intro(`Capgo onboarding 🛫`)
414
414
  await checkLatest()
415
415
  const snag = useLogSnag()
416
- const config = await getConfig()
417
- appId = appId || config?.app?.appId
416
+ const extConfig = await getConfig()
417
+ appId = appId || extConfig?.config?.appId
418
418
  const apikey = apikeyCommand || findSavedKey()
419
419
 
420
420
  const log = p.spinner()
@@ -483,7 +483,7 @@ export async function initApp(apikeyCommand: string, appId: string, options: Sup
483
483
  catch (e) {
484
484
  console.error(e)
485
485
  p.log.error(`Error during onboarding, please try again later`)
486
- process.exit(1)
486
+ exit(1)
487
487
  }
488
488
 
489
489
  p.log.info(`Welcome onboard ✈️!`)
@@ -491,5 +491,5 @@ export async function initApp(apikeyCommand: string, appId: string, options: Sup
491
491
  p.log.info(`Next time use \`${pm.runner} @capgo/cli@latest bundle upload\` to only upload your bundle`)
492
492
  p.log.info(`If you have any issue try to use the debug command \`${pm.runner} @capgo/cli@latest app debug\``)
493
493
  p.outro(`Bye 👋`)
494
- process.exit()
494
+ exit()
495
495
  }