@symbo.ls/cli 2.33.11 → 2.33.13
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/bin/collab.js +334 -0
- package/bin/fetch.js +77 -54
- package/bin/fs.js +150 -61
- package/bin/index.js +1 -0
- package/bin/install.js +6 -19
- package/bin/login.js +82 -62
- package/bin/push.js +157 -117
- package/bin/require.js +140 -6
- package/bin/socket-server.js +5 -10
- package/bin/sync.js +313 -112
- package/helpers/apiUtils.js +60 -58
- package/helpers/buildMessages.js +76 -0
- package/helpers/changesUtils.js +474 -0
- package/helpers/compareUtils.js +136 -2
- package/helpers/config.js +131 -18
- package/helpers/credentialManager.js +66 -0
- package/helpers/fileUtils.js +8 -2
- package/helpers/symbolsConfig.js +35 -0
- package/helpers/transportUtils.js +58 -0
- package/package.json +7 -5
package/bin/collab.js
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'fs'
|
|
4
|
+
import path from 'path'
|
|
5
|
+
import chalk from 'chalk'
|
|
6
|
+
import { program } from './program.js'
|
|
7
|
+
import { CredentialManager } from '../helpers/credentialManager.js'
|
|
8
|
+
import { loadSymbolsConfig } from '../helpers/symbolsConfig.js'
|
|
9
|
+
import { loadCliConfig, readLock, writeLock, getConfigPaths } from '../helpers/config.js'
|
|
10
|
+
import { stringifyFunctionsForTransport } from '../helpers/transportUtils.js'
|
|
11
|
+
import { getCurrentProjectData } from '../helpers/apiUtils.js'
|
|
12
|
+
import { computeCoarseChanges, computeOrdersForTuples, preprocessChanges } from '../helpers/changesUtils.js'
|
|
13
|
+
import { createFs } from './fs.js'
|
|
14
|
+
|
|
15
|
+
// Lazy import socket.io-client and chokidar to avoid adding cost for non-collab users
|
|
16
|
+
async function importDeps() {
|
|
17
|
+
const [{ default: io }, { default: chokidar }] = await Promise.all([
|
|
18
|
+
import('socket.io-client'),
|
|
19
|
+
import('chokidar')
|
|
20
|
+
])
|
|
21
|
+
return { io, chokidar }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function debounce(fn, wait) {
|
|
25
|
+
let t = null
|
|
26
|
+
const debounced = (...args) => {
|
|
27
|
+
clearTimeout(t)
|
|
28
|
+
t = setTimeout(() => fn(...args), wait)
|
|
29
|
+
}
|
|
30
|
+
debounced.cancel = () => {
|
|
31
|
+
clearTimeout(t)
|
|
32
|
+
t = null
|
|
33
|
+
}
|
|
34
|
+
return debounced
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function startCollab(options) {
|
|
38
|
+
const credManager = new CredentialManager()
|
|
39
|
+
const authToken = credManager.ensureAuthToken()
|
|
40
|
+
if (!authToken) {
|
|
41
|
+
console.log(chalk.yellow('\nAuthentication required. Please run: smbls login\n'))
|
|
42
|
+
process.exit(1)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const symbolsConfig = await loadSymbolsConfig()
|
|
46
|
+
const cliConfig = loadCliConfig()
|
|
47
|
+
const lock = readLock()
|
|
48
|
+
const branch = options.branch || cliConfig.branch || symbolsConfig.branch || 'main'
|
|
49
|
+
const appKey = cliConfig.projectKey || symbolsConfig.key
|
|
50
|
+
|
|
51
|
+
if (!appKey) {
|
|
52
|
+
console.log(chalk.red('Missing project key. Add it to symbols.json or .symbols/config.json'))
|
|
53
|
+
process.exit(1)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const { io, chokidar } = await importDeps()
|
|
57
|
+
const baseUrl = cliConfig.apiBaseUrl
|
|
58
|
+
|
|
59
|
+
console.log(chalk.dim(`\nConnecting to realtime collab on ${baseUrl} ...`))
|
|
60
|
+
|
|
61
|
+
// Maintain in-memory base state and a guard to suppress local echoes
|
|
62
|
+
let currentBase = {}
|
|
63
|
+
let suppressLocalChanges = false
|
|
64
|
+
let suppressUntil = 0
|
|
65
|
+
const suppressionWindowMs = Math.max(1500, (options.debounceMs || 200) * 8)
|
|
66
|
+
|
|
67
|
+
function isSuppressed() {
|
|
68
|
+
return suppressLocalChanges || Date.now() < suppressUntil
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function setByPath(root, pathArr = [], value) {
|
|
72
|
+
if (!Array.isArray(pathArr) || !pathArr.length) return
|
|
73
|
+
let cur = root
|
|
74
|
+
for (let i = 0; i < pathArr.length - 1; i++) {
|
|
75
|
+
const seg = pathArr[i]
|
|
76
|
+
if (!cur[seg] || typeof cur[seg] !== 'object') cur[seg] = {}
|
|
77
|
+
cur = cur[seg]
|
|
78
|
+
}
|
|
79
|
+
cur[pathArr[pathArr.length - 1]] = value
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function deleteByPath(root, pathArr = []) {
|
|
83
|
+
if (!Array.isArray(pathArr) || !pathArr.length) return
|
|
84
|
+
let cur = root
|
|
85
|
+
for (let i = 0; i < pathArr.length - 1; i++) {
|
|
86
|
+
const seg = pathArr[i]
|
|
87
|
+
if (!cur || typeof cur !== 'object') return
|
|
88
|
+
cur = cur[seg]
|
|
89
|
+
}
|
|
90
|
+
if (cur && typeof cur === 'object') {
|
|
91
|
+
delete cur[pathArr[pathArr.length - 1]]
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function applyTuples(root, tuples = []) {
|
|
96
|
+
if (!root || typeof root !== 'object') return root
|
|
97
|
+
if (!Array.isArray(tuples)) return root
|
|
98
|
+
for (let i = 0; i < tuples.length; i++) {
|
|
99
|
+
const t = tuples[i]
|
|
100
|
+
if (!Array.isArray(t) || t.length < 2) continue
|
|
101
|
+
const [action, pathArr, value] = t
|
|
102
|
+
if (action === 'update' || action === 'set') {
|
|
103
|
+
setByPath(root, pathArr, value)
|
|
104
|
+
} else if (action === 'delete' || action === 'del') {
|
|
105
|
+
deleteByPath(root, pathArr)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return root
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function applyOrders(root, orders = []) {
|
|
112
|
+
if (!root || typeof root !== 'object') return
|
|
113
|
+
if (!Array.isArray(orders)) return
|
|
114
|
+
for (let i = 0; i < orders.length; i++) {
|
|
115
|
+
const o = orders[i]
|
|
116
|
+
if (!o || !Array.isArray(o.path) || !Array.isArray(o.keys)) continue
|
|
117
|
+
// Ensure container exists
|
|
118
|
+
setByPath(root, o.path, (function ensure(obj) {
|
|
119
|
+
return obj && typeof obj === 'object' ? obj : {}
|
|
120
|
+
})(pathArrGet(root, o.path)))
|
|
121
|
+
// Now set __order on that container
|
|
122
|
+
const container = pathArrGet(root, o.path)
|
|
123
|
+
if (container && typeof container === 'object') {
|
|
124
|
+
container.__order = o.keys.slice()
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function pathArrGet(root, pathArr = []) {
|
|
130
|
+
if (!Array.isArray(pathArr) || !pathArr.length) return root
|
|
131
|
+
let cur = root
|
|
132
|
+
for (let i = 0; i < pathArr.length; i++) {
|
|
133
|
+
if (!cur || typeof cur !== 'object') return undefined
|
|
134
|
+
cur = cur[pathArr[i]]
|
|
135
|
+
}
|
|
136
|
+
return cur
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async function writeProjectAndFs(fullObj) {
|
|
140
|
+
const { projectPath } = getConfigPaths()
|
|
141
|
+
try {
|
|
142
|
+
await fs.promises.writeFile(projectPath, JSON.stringify(fullObj, null, 2))
|
|
143
|
+
} catch (_) {}
|
|
144
|
+
// Avoid echoing the changes we are about to materialize
|
|
145
|
+
suppressLocalChanges = true
|
|
146
|
+
// Cancel any pending local send
|
|
147
|
+
if (typeof sendLocalChanges.cancel === 'function') {
|
|
148
|
+
sendLocalChanges.cancel()
|
|
149
|
+
}
|
|
150
|
+
try {
|
|
151
|
+
await createFs(fullObj, path.join(process.cwd(), 'smbls'), { update: true, metadata: false })
|
|
152
|
+
} finally {
|
|
153
|
+
// Extend suppression window to allow file events to settle fully
|
|
154
|
+
suppressUntil = Date.now() + suppressionWindowMs
|
|
155
|
+
suppressLocalChanges = false
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Prime local base with latest snapshot
|
|
160
|
+
const prime = await getCurrentProjectData(
|
|
161
|
+
{ projectKey: appKey, projectId: lock.projectId },
|
|
162
|
+
authToken,
|
|
163
|
+
{ branch, includePending: true }
|
|
164
|
+
)
|
|
165
|
+
const initialData = prime?.data || {}
|
|
166
|
+
const etag = prime?.etag || null
|
|
167
|
+
writeLock({
|
|
168
|
+
etag,
|
|
169
|
+
version: initialData.version,
|
|
170
|
+
branch,
|
|
171
|
+
projectId: initialData?.projectInfo?.id || lock.projectId,
|
|
172
|
+
pulledAt: new Date().toISOString()
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
// Persist base snapshot
|
|
176
|
+
try {
|
|
177
|
+
const { projectPath } = getConfigPaths()
|
|
178
|
+
await fs.promises.mkdir(path.dirname(projectPath), { recursive: true })
|
|
179
|
+
await fs.promises.writeFile(projectPath, JSON.stringify(initialData, null, 2))
|
|
180
|
+
} catch (_) {}
|
|
181
|
+
currentBase = { ...(initialData || {}) }
|
|
182
|
+
|
|
183
|
+
// Connect to the collab namespace with required auth fields
|
|
184
|
+
const socket = io(`${baseUrl}`, {
|
|
185
|
+
path: '/collab-socket',
|
|
186
|
+
transports: ['websocket', 'polling'],
|
|
187
|
+
// Server expects these fields on handshake.auth
|
|
188
|
+
auth: {
|
|
189
|
+
token: authToken,
|
|
190
|
+
projectId: (lock.projectId || null),
|
|
191
|
+
projectKey: appKey,
|
|
192
|
+
key: appKey,
|
|
193
|
+
branch,
|
|
194
|
+
live: !!options.live,
|
|
195
|
+
clientType: 'cli'
|
|
196
|
+
},
|
|
197
|
+
// Some servers verify headers; keep Authorization for completeness
|
|
198
|
+
extraHeaders: { Authorization: `Bearer ${authToken}` },
|
|
199
|
+
reconnection: true,
|
|
200
|
+
reconnectionAttempts: Infinity,
|
|
201
|
+
reconnectionDelay: 1000,
|
|
202
|
+
timeout: 10000
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
socket.on('connect', () => {
|
|
206
|
+
console.log(chalk.green('Connected to collab server'))
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
socket.on('disconnect', (reason) => {
|
|
210
|
+
console.log(chalk.yellow(`Disconnected: ${reason}`))
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
socket.on('connect_error', (err) => {
|
|
214
|
+
console.log(chalk.red(`Connection error: ${err?.message || err}`))
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
// Receive snapshot and update local files + lock/base
|
|
218
|
+
socket.on('snapshot', async ({ version, branch: srvBranch, data, schema }) => {
|
|
219
|
+
const full = { ...(data || {}), schema: schema || {}, version, branch: srvBranch }
|
|
220
|
+
currentBase = full
|
|
221
|
+
await writeProjectAndFs(currentBase)
|
|
222
|
+
// Update lock’s version; ETag will change next pull
|
|
223
|
+
writeLock({ version, branch: srvBranch, pulledAt: new Date().toISOString() })
|
|
224
|
+
console.log(chalk.gray(`Snapshot applied. Version: ${chalk.cyan(version)}`))
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
// Receive granular ops from other clients/commits and apply to local files
|
|
228
|
+
socket.on('ops', async (payload) => {
|
|
229
|
+
// Apply incoming tuples directly to local base; do not re-emit builds triggered by this apply
|
|
230
|
+
try {
|
|
231
|
+
if (typeof sendLocalChanges.cancel === 'function') {
|
|
232
|
+
sendLocalChanges.cancel()
|
|
233
|
+
}
|
|
234
|
+
const tuples = Array.isArray(payload?.granularChanges) && payload.granularChanges.length
|
|
235
|
+
? payload.granularChanges
|
|
236
|
+
: preprocessChanges(currentBase || {}, payload?.changes || []).granularChanges
|
|
237
|
+
if (!Array.isArray(tuples) || !tuples.length) return
|
|
238
|
+
applyTuples(currentBase, tuples)
|
|
239
|
+
if (Array.isArray(payload?.orders) && payload.orders.length) {
|
|
240
|
+
applyOrders(currentBase, payload.orders)
|
|
241
|
+
}
|
|
242
|
+
await writeProjectAndFs(currentBase)
|
|
243
|
+
writeLock({ pulledAt: new Date().toISOString() })
|
|
244
|
+
if (options.verbose) console.log(chalk.gray('Applied incoming ops to local workspace'))
|
|
245
|
+
} catch (e) {
|
|
246
|
+
if (options.verbose) console.error('Failed to apply incoming ops', e)
|
|
247
|
+
}
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
socket.on('commit', ({ version }) => {
|
|
251
|
+
writeLock({ version, pulledAt: new Date().toISOString() })
|
|
252
|
+
console.log(chalk.gray(`Server committed new version: ${chalk.cyan(version)}`))
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
// Watch local dist output and push coarse per-key changes
|
|
256
|
+
const distDir = path.join(process.cwd(), 'smbls')
|
|
257
|
+
const outputDir = path.join(distDir, 'dist')
|
|
258
|
+
const outputFile = path.join(outputDir, 'index.js')
|
|
259
|
+
|
|
260
|
+
// Build loader
|
|
261
|
+
async function loadLocalProject() {
|
|
262
|
+
try {
|
|
263
|
+
// Reuse build flow from push/sync
|
|
264
|
+
const { buildDirectory } = await import('../helpers/fileUtils.js')
|
|
265
|
+
const { loadModule } = await import('./require.js')
|
|
266
|
+
await buildDirectory(distDir, outputDir)
|
|
267
|
+
const loaded = await loadModule(outputFile, { silent: true, noCache: true })
|
|
268
|
+
return loaded
|
|
269
|
+
} catch (e) {
|
|
270
|
+
if (options.verbose) console.error('Build failed while watching:', e.message)
|
|
271
|
+
return null
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const sendLocalChanges = debounce(async () => {
|
|
276
|
+
if (suppressLocalChanges) return
|
|
277
|
+
const local = await loadLocalProject()
|
|
278
|
+
if (!local) return
|
|
279
|
+
// Prepare safe, JSON-serialisable snapshots for diffing & transport
|
|
280
|
+
const base = currentBase || {}
|
|
281
|
+
const safeBase = stringifyFunctionsForTransport(base)
|
|
282
|
+
const safeLocal = stringifyFunctionsForTransport(local)
|
|
283
|
+
// Base snapshot is our last pulled .symbols/project.json
|
|
284
|
+
const changes = computeCoarseChanges(safeBase, safeLocal)
|
|
285
|
+
if (!changes.length) return
|
|
286
|
+
if (options.verbose) {
|
|
287
|
+
const byType = changes.reduce((acc, [t]) => ((acc[t] = (acc[t] || 0) + 1), acc), {})
|
|
288
|
+
console.log(chalk.gray(`Emitting local ops: ${JSON.stringify(byType)}`))
|
|
289
|
+
}
|
|
290
|
+
// Generate granular changes against base to ensure downstream consumers have fine ops
|
|
291
|
+
const { granularChanges } = preprocessChanges(safeBase, changes)
|
|
292
|
+
const orders = computeOrdersForTuples(safeLocal, granularChanges)
|
|
293
|
+
console.log(chalk.gray(`Emitting local ops: ${JSON.stringify({ changes, granularChanges, orders, branch })}`))
|
|
294
|
+
socket.emit('ops', {
|
|
295
|
+
changes,
|
|
296
|
+
granularChanges,
|
|
297
|
+
orders,
|
|
298
|
+
branch
|
|
299
|
+
})
|
|
300
|
+
}, options.debounceMs || 200)
|
|
301
|
+
|
|
302
|
+
const watcher = chokidar.watch(distDir, {
|
|
303
|
+
ignored: (p) => {
|
|
304
|
+
// Ignore hidden files, build output directory, and temporary files
|
|
305
|
+
if (/(^|[\/\\])\./.test(p)) return true
|
|
306
|
+
if (p.includes(`${path.sep}dist${path.sep}`) || p.endsWith(`${path.sep}dist`)) return true
|
|
307
|
+
return false
|
|
308
|
+
},
|
|
309
|
+
ignoreInitial: true,
|
|
310
|
+
persistent: true
|
|
311
|
+
})
|
|
312
|
+
const onFsEvent = () => {
|
|
313
|
+
if (isSuppressed()) return
|
|
314
|
+
sendLocalChanges()
|
|
315
|
+
}
|
|
316
|
+
watcher
|
|
317
|
+
.on('add', onFsEvent)
|
|
318
|
+
.on('change', onFsEvent)
|
|
319
|
+
.on('unlink', onFsEvent)
|
|
320
|
+
|
|
321
|
+
console.log(chalk.green('Watching local changes and syncing over socket...'))
|
|
322
|
+
console.log(chalk.gray('Press Ctrl+C to exit'))
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
program
|
|
326
|
+
.command('collab')
|
|
327
|
+
.description('Connect to realtime collaboration socket and live-sync changes')
|
|
328
|
+
.option('-b, --branch <branch>', 'Branch to collaborate on')
|
|
329
|
+
.option('-l, --live', 'Enable live collaboration mode', false)
|
|
330
|
+
.option('-d, --debounce-ms <ms>', 'Local changes debounce milliseconds', (v) => parseInt(v, 10), 200)
|
|
331
|
+
.option('-v, --verbose', 'Show verbose output', false)
|
|
332
|
+
.action(startCollab)
|
|
333
|
+
|
|
334
|
+
|
package/bin/fetch.js
CHANGED
|
@@ -1,79 +1,101 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import fs from 'fs'
|
|
4
|
+
import path from 'path'
|
|
4
5
|
import chalk from 'chalk'
|
|
5
|
-
import { loadModule } from './require.js'
|
|
6
6
|
import { program } from './program.js'
|
|
7
|
-
import * as fetch from '@symbo.ls/fetch'
|
|
8
7
|
import * as utils from '@domql/utils'
|
|
9
8
|
import { convertFromCli } from './convert.js'
|
|
10
9
|
import { createFs } from './fs.js'
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
import { CredentialManager } from '../helpers/credentialManager.js'
|
|
11
|
+
import { getCurrentProjectData } from '../helpers/apiUtils.js'
|
|
12
|
+
import { showAuthRequiredMessages } from '../helpers/buildMessages.js'
|
|
13
|
+
import { loadSymbolsConfig } from '../helpers/symbolsConfig.js'
|
|
14
|
+
import { loadCliConfig, readLock, writeLock, updateLegacySymbolsJson, getConfigPaths } from '../helpers/config.js'
|
|
15
|
+
const { isObjectLike } = (utils.default || utils)
|
|
13
16
|
|
|
14
17
|
const RC_PATH = process.cwd() + '/symbols.json'
|
|
15
18
|
const LOCAL_CONFIG_PATH =
|
|
16
19
|
process.cwd() + '/node_modules/@symbo.ls/init/dynamic.json'
|
|
17
20
|
const DEFAULT_REMOTE_REPOSITORY = 'https://github.com/symbo-ls/default-config/'
|
|
18
|
-
const DEFAULT_REMOTE_CONFIG_PATH = 'https://api.symbols.app/' // eslint-disable-line
|
|
19
|
-
|
|
20
|
-
const API_URL_LOCAL = 'http://localhost:8080/get'
|
|
21
|
-
const API_URL = 'https://api.symbols.app/get'
|
|
22
|
-
|
|
23
|
-
const rcFile = loadModule(RC_PATH) // eslint-disable-line
|
|
24
|
-
const localConfig = loadModule(LOCAL_CONFIG_PATH) // eslint-disable-line
|
|
25
21
|
|
|
26
22
|
const debugMsg = chalk.dim(
|
|
27
23
|
'Use --verbose to debug the error or open the issue at https://github.com/symbo-ls/smbls'
|
|
28
24
|
)
|
|
29
25
|
|
|
30
|
-
let rc = {}
|
|
31
|
-
try {
|
|
32
|
-
rc = loadModule(RC_PATH) // eslint-disable-line
|
|
33
|
-
} catch (e) {
|
|
34
|
-
console.error('Please include symbols.json to your root of respository')
|
|
35
|
-
}
|
|
36
|
-
|
|
37
26
|
export const fetchFromCli = async (opts) => {
|
|
38
|
-
const {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
27
|
+
const { dev, verbose, prettify, convert: convertOpt, metadata: metadataOpt, update, force } = opts
|
|
28
|
+
|
|
29
|
+
const credManager = new CredentialManager()
|
|
30
|
+
const authToken = credManager.ensureAuthToken()
|
|
31
|
+
|
|
32
|
+
if (!authToken) {
|
|
33
|
+
showAuthRequiredMessages()
|
|
34
|
+
|
|
35
|
+
process.exit(1)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const symbolsConfig = await loadSymbolsConfig()
|
|
39
|
+
const cliConfig = loadCliConfig()
|
|
40
|
+
const projectKey = cliConfig.projectKey || symbolsConfig.key
|
|
41
|
+
const branch = cliConfig.branch || symbolsConfig.branch || 'main'
|
|
42
|
+
const { framework, distDir, metadata } = symbolsConfig
|
|
43
|
+
|
|
44
|
+
console.log('\nFetching project data...\n')
|
|
45
|
+
|
|
46
|
+
let payload
|
|
47
|
+
try {
|
|
48
|
+
const lock = readLock()
|
|
49
|
+
const result = await getCurrentProjectData(
|
|
50
|
+
{ projectKey, projectId: lock.projectId },
|
|
51
|
+
authToken,
|
|
52
|
+
{ branch, includePending: true, etag: lock.etag }
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
if (result.notModified) {
|
|
56
|
+
console.log(chalk.bold.green('Already up to date (ETag matched)'))
|
|
57
|
+
return
|
|
61
58
|
}
|
|
62
|
-
})
|
|
63
59
|
|
|
64
|
-
|
|
65
|
-
|
|
60
|
+
payload = result.data || {}
|
|
61
|
+
const etag = result.etag || null
|
|
66
62
|
|
|
67
|
-
|
|
63
|
+
// Update lock.json
|
|
64
|
+
writeLock({
|
|
65
|
+
etag,
|
|
66
|
+
version: payload.version,
|
|
67
|
+
branch,
|
|
68
|
+
projectId: payload?.projectInfo?.id || lock.projectId,
|
|
69
|
+
pulledAt: new Date().toISOString()
|
|
70
|
+
})
|
|
68
71
|
|
|
69
|
-
|
|
72
|
+
// Update legacy symbols.json with version and branch
|
|
73
|
+
updateLegacySymbolsJson({ ...(symbolsConfig || {}), version: payload.version, branch })
|
|
74
|
+
|
|
75
|
+
if (verbose) {
|
|
76
|
+
console.log(chalk.gray(`Version: ${chalk.cyan(payload.version)}`))
|
|
77
|
+
console.log(chalk.gray(`Branch: ${chalk.cyan(branch)}\n`))
|
|
78
|
+
}
|
|
79
|
+
} catch (e) {
|
|
80
|
+
console.log(chalk.red('Failed to fetch:'), projectKey)
|
|
81
|
+
if (verbose) console.error(e)
|
|
82
|
+
else console.log(debugMsg)
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Persist base snapshot for future rebases
|
|
87
|
+
try {
|
|
88
|
+
const { projectPath } = getConfigPaths()
|
|
89
|
+
await fs.promises.mkdir(path.dirname(projectPath), { recursive: true })
|
|
90
|
+
await fs.promises.writeFile(projectPath, JSON.stringify(payload, null, 2))
|
|
91
|
+
} catch (_) {}
|
|
70
92
|
|
|
71
93
|
if (verbose) {
|
|
72
|
-
if (
|
|
94
|
+
if (projectKey) {
|
|
73
95
|
console.log(
|
|
74
96
|
chalk.bold('Symbols'),
|
|
75
97
|
'data fetched for',
|
|
76
|
-
chalk.green(
|
|
98
|
+
chalk.green(payload.name)
|
|
77
99
|
)
|
|
78
100
|
} else {
|
|
79
101
|
console.log(
|
|
@@ -86,6 +108,8 @@ export const fetchFromCli = async (opts) => {
|
|
|
86
108
|
console.log()
|
|
87
109
|
}
|
|
88
110
|
|
|
111
|
+
const { version: fetchedVersion, ...config } = payload
|
|
112
|
+
|
|
89
113
|
for (const t in config) {
|
|
90
114
|
const type = config[t]
|
|
91
115
|
const arr = []
|
|
@@ -101,7 +125,7 @@ export const fetchFromCli = async (opts) => {
|
|
|
101
125
|
}
|
|
102
126
|
|
|
103
127
|
if (!distDir) {
|
|
104
|
-
const bodyString = JSON.stringify(
|
|
128
|
+
const bodyString = JSON.stringify(payload, null, prettify ?? 2)
|
|
105
129
|
|
|
106
130
|
try {
|
|
107
131
|
await fs.writeFileSync(LOCAL_CONFIG_PATH, bodyString)
|
|
@@ -125,16 +149,15 @@ export const fetchFromCli = async (opts) => {
|
|
|
125
149
|
return {}
|
|
126
150
|
}
|
|
127
151
|
|
|
128
|
-
if (
|
|
129
|
-
convertFromCli(
|
|
152
|
+
if (payload.components && convertOpt && framework) {
|
|
153
|
+
convertFromCli(payload.components, { ...opts, framework })
|
|
130
154
|
}
|
|
131
155
|
|
|
132
156
|
if (update || force) {
|
|
133
|
-
createFs(
|
|
157
|
+
createFs(payload, distDir, { update: true, metadata: false })
|
|
134
158
|
} else {
|
|
135
|
-
createFs(
|
|
159
|
+
createFs(payload, distDir, { metadata: false })
|
|
136
160
|
}
|
|
137
|
-
})
|
|
138
161
|
}
|
|
139
162
|
|
|
140
163
|
program
|