@symbo.ls/cli 2.33.26 → 2.33.28

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 CHANGED
@@ -5,7 +5,7 @@ import path from 'path'
5
5
  import chalk from 'chalk'
6
6
  import { program } from './program.js'
7
7
  import { CredentialManager } from '../helpers/credentialManager.js'
8
- import { loadSymbolsConfig } from '../helpers/symbolsConfig.js'
8
+ import { loadSymbolsConfig, resolveDistDir } from '../helpers/symbolsConfig.js'
9
9
  import { loadCliConfig, readLock, writeLock, getConfigPaths } from '../helpers/config.js'
10
10
  import { stringifyFunctionsForTransport } from '../helpers/transportUtils.js'
11
11
  import { getCurrentProjectData } from '../helpers/apiUtils.js'
@@ -48,6 +48,10 @@ export async function startCollab(options) {
48
48
  const branch = options.branch || cliConfig.branch || symbolsConfig.branch || 'main'
49
49
  const appKey = cliConfig.projectKey || symbolsConfig.key
50
50
 
51
+ const distDir =
52
+ resolveDistDir(symbolsConfig) ||
53
+ path.join(process.cwd(), 'smbls')
54
+
51
55
  if (!appKey) {
52
56
  console.log(chalk.red('Missing project key. Add it to symbols.json or .symbols/config.json'))
53
57
  process.exit(1)
@@ -148,7 +152,7 @@ export async function startCollab(options) {
148
152
  sendLocalChanges.cancel()
149
153
  }
150
154
  try {
151
- await createFs(fullObj, path.join(process.cwd(), 'smbls'), { update: true, metadata: false })
155
+ await createFs(fullObj, distDir, { update: true, metadata: false })
152
156
  } finally {
153
157
  // Extend suppression window to allow file events to settle fully
154
158
  suppressUntil = Date.now() + suppressionWindowMs
@@ -253,7 +257,6 @@ export async function startCollab(options) {
253
257
  })
254
258
 
255
259
  // Watch local dist output and push coarse per-key changes
256
- const distDir = path.join(process.cwd(), 'smbls')
257
260
  const outputDir = path.join(distDir, 'dist')
258
261
  const outputFile = path.join(outputDir, 'index.js')
259
262
 
@@ -272,10 +275,77 @@ export async function startCollab(options) {
272
275
  }
273
276
  }
274
277
 
278
+ /**
279
+ * Augment the loaded project object with any new items that exist as
280
+ * filesystem files (e.g. newly created component/page/method/function/snippet
281
+ * files) but are not yet present in the in‑memory data object.
282
+ *
283
+ * This bridges the gap between the one‑way createFs → files mapping so that
284
+ * adding a new file like components/MainFooter.js is seen as a new
285
+ * `components.MainFooter` data item and can be sent to the server.
286
+ */
287
+ async function augmentLocalWithNewFsItems(local) {
288
+ if (!local || typeof local !== 'object') return
289
+
290
+ const TYPES = ['components', 'pages', 'snippets', 'methods', 'functions']
291
+
292
+ for (let i = 0; i < TYPES.length; i++) {
293
+ const type = TYPES[i]
294
+ const srcDir = path.join(distDir, type)
295
+
296
+ let entries
297
+ try {
298
+ entries = await fs.promises.readdir(srcDir)
299
+ } catch {
300
+ continue
301
+ }
302
+ if (!Array.isArray(entries) || !entries.length) continue
303
+
304
+ const container = local[type] && typeof local[type] === 'object' && !Array.isArray(local[type])
305
+ ? local[type]
306
+ : (local[type] = {})
307
+ const baseSection = currentBase && currentBase[type] && typeof currentBase[type] === 'object'
308
+ ? currentBase[type]
309
+ : {}
310
+
311
+ for (let j = 0; j < entries.length; j++) {
312
+ const filename = entries[j]
313
+ if (!filename.endsWith('.js') || filename === 'index.js') continue
314
+
315
+ const key = filename.slice(0, -3)
316
+ if (Object.prototype.hasOwnProperty.call(container, key)) continue
317
+ if (Object.prototype.hasOwnProperty.call(baseSection, key)) continue
318
+
319
+ const compiledPath = path.join(outputDir, type, filename)
320
+ let mod
321
+ try {
322
+ const { loadModule } = await import('./require.js')
323
+ mod = await loadModule(compiledPath, { silent: true })
324
+ } catch {
325
+ if (options.verbose) {
326
+ console.error(`Failed to load new ${type} item from`, compiledPath)
327
+ }
328
+ continue
329
+ }
330
+
331
+ if (!mod) continue
332
+
333
+ let value = null
334
+ if (mod && typeof mod === 'object') {
335
+ value = mod.default || mod[key] || null
336
+ }
337
+ if (!value || typeof value !== 'object') continue
338
+
339
+ container[key] = value
340
+ }
341
+ }
342
+ }
343
+
275
344
  const sendLocalChanges = debounce(async () => {
276
345
  if (suppressLocalChanges) return
277
346
  const local = await loadLocalProject()
278
347
  if (!local) return
348
+ await augmentLocalWithNewFsItems(local)
279
349
  // Prepare safe, JSON-serialisable snapshots for diffing & transport
280
350
  const base = currentBase || {}
281
351
  const safeBase = stringifyFunctionsForTransport(base)
@@ -291,6 +361,25 @@ export async function startCollab(options) {
291
361
  const { granularChanges } = preprocessChanges(safeBase, changes)
292
362
  const orders = computeOrdersForTuples(safeLocal, granularChanges)
293
363
  console.log(chalk.gray(`Emitting local ops: ${JSON.stringify({ changes, granularChanges, orders, branch })}`))
364
+
365
+ // Optimistically apply our own ops to the in-memory base so that
366
+ // subsequent diffs are computed against the latest local state.
367
+ // This avoids repeatedly re-emitting the same changes when the
368
+ // server does not immediately send back a fresh snapshot.
369
+ try {
370
+ const tuplesToApply = Array.isArray(granularChanges) && granularChanges.length
371
+ ? granularChanges
372
+ : changes
373
+ applyTuples(currentBase, tuplesToApply)
374
+ if (Array.isArray(orders) && orders.length) {
375
+ applyOrders(currentBase, orders)
376
+ }
377
+ } catch (e) {
378
+ if (options.verbose) {
379
+ console.error('Failed to apply local ops to in-memory base', e)
380
+ }
381
+ }
382
+
294
383
  socket.emit('ops', {
295
384
  changes,
296
385
  granularChanges,
package/bin/config.js ADDED
@@ -0,0 +1,85 @@
1
+ 'use strict'
2
+
3
+ import fs from 'fs'
4
+ import path from 'path'
5
+ import inquirer from 'inquirer'
6
+ import chalk from 'chalk'
7
+ import { program } from './program.js'
8
+ import { loadSymbolsConfig } from '../helpers/symbolsConfig.js'
9
+ import { loadCliConfig, saveCliConfig, updateLegacySymbolsJson } from '../helpers/config.js'
10
+
11
+ program
12
+ .command('config')
13
+ .description('Interactively configure Symbols project settings')
14
+ .option('--dist-dir <dir>', 'Set distDir non-interactively')
15
+ .action(async (options) => {
16
+ // Load existing configs (do not fail if missing/malformed)
17
+ const symbolsConfig =
18
+ (await loadSymbolsConfig({ required: false, validateKey: false, silent: true })) || {}
19
+ const cliConfig = loadCliConfig()
20
+
21
+ // Fast path: only update distDir when provided explicitly
22
+ if (options.distDir) {
23
+ const next = { ...symbolsConfig, distDir: options.distDir }
24
+ updateLegacySymbolsJson(next)
25
+ console.log(chalk.green(`Updated symbols.json distDir to "${options.distDir}"`))
26
+ return
27
+ }
28
+
29
+ const questions = [
30
+ {
31
+ type: 'input',
32
+ name: 'key',
33
+ message: 'App key (symbols.json.key):',
34
+ default: symbolsConfig.key || '',
35
+ filter: (v) => v.trim()
36
+ },
37
+ {
38
+ type: 'input',
39
+ name: 'branch',
40
+ message: 'Default branch:',
41
+ default: cliConfig.branch || symbolsConfig.branch || 'main',
42
+ filter: (v) => v.trim() || 'main'
43
+ },
44
+ {
45
+ type: 'input',
46
+ name: 'distDir',
47
+ message: 'Directory for generated files (distDir):',
48
+ default: symbolsConfig.distDir || './smbls',
49
+ filter: (v) => v.trim() || './smbls'
50
+ },
51
+ {
52
+ type: 'input',
53
+ name: 'apiBaseUrl',
54
+ message: 'API base URL:',
55
+ default: cliConfig.apiBaseUrl || 'https://api.symbols.app',
56
+ filter: (v) => v.trim() || 'https://api.symbols.app'
57
+ }
58
+ ]
59
+
60
+ const answers = await inquirer.prompt(questions)
61
+
62
+ // Update symbols.json (legacy project config)
63
+ const nextSymbols = updateLegacySymbolsJson({
64
+ ...symbolsConfig,
65
+ key: answers.key || undefined,
66
+ branch: answers.branch,
67
+ distDir: answers.distDir
68
+ })
69
+
70
+ // Update .symbols/config.json (runtime CLI config)
71
+ const _nextCli = saveCliConfig({
72
+ apiBaseUrl: answers.apiBaseUrl,
73
+ projectKey: answers.key || cliConfig.projectKey || nextSymbols.key,
74
+ branch: answers.branch
75
+ })
76
+
77
+ console.log(`\n`)
78
+ console.log(chalk.green('Configuration updated successfully.'))
79
+ const symbolsPath = path.join(process.cwd(), 'symbols.json')
80
+ const cliConfigPath = path.join(process.cwd(), '.symbols', 'config.json')
81
+ console.log(chalk.gray(`symbols.json: ${chalk.cyan(symbolsPath)}`))
82
+ console.log(chalk.gray(`.symbols/config.json: ${chalk.cyan(cliConfigPath)}`))
83
+ })
84
+
85
+
package/bin/fetch.js CHANGED
@@ -10,15 +10,10 @@ import { createFs } from './fs.js'
10
10
  import { CredentialManager } from '../helpers/credentialManager.js'
11
11
  import { getCurrentProjectData } from '../helpers/apiUtils.js'
12
12
  import { showAuthRequiredMessages } from '../helpers/buildMessages.js'
13
- import { loadSymbolsConfig } from '../helpers/symbolsConfig.js'
13
+ import { loadSymbolsConfig, resolveDistDir } from '../helpers/symbolsConfig.js'
14
14
  import { loadCliConfig, readLock, writeLock, updateLegacySymbolsJson, getConfigPaths } from '../helpers/config.js'
15
15
  const { isObjectLike } = (utils.default || utils)
16
16
 
17
- const RC_PATH = process.cwd() + '/symbols.json'
18
- const LOCAL_CONFIG_PATH =
19
- process.cwd() + '/node_modules/@symbo.ls/init/dynamic.json'
20
- const DEFAULT_REMOTE_REPOSITORY = 'https://github.com/symbo-ls/default-config/'
21
-
22
17
  const debugMsg = chalk.dim(
23
18
  'Use --verbose to debug the error or open the issue at https://github.com/symbo-ls/smbls'
24
19
  )
@@ -38,7 +33,7 @@ export const fetchFromCli = async (opts) => {
38
33
 
39
34
  const projectKey = cliConfig.projectKey || symbolsConfig.key
40
35
  const branch = cliConfig.branch || symbolsConfig.branch || 'main'
41
- const { framework, distDir, metadata } = symbolsConfig
36
+ const { framework } = symbolsConfig
42
37
 
43
38
  console.log('\nFetching project data...\n')
44
39
 
@@ -79,7 +74,7 @@ export const fetchFromCli = async (opts) => {
79
74
  console.log(chalk.red('Failed to fetch:'), projectKey)
80
75
  if (verbose) console.error(e)
81
76
  else console.log(debugMsg)
82
- return
77
+ process.exit(1)
83
78
  }
84
79
 
85
80
  // Persist base snapshot for future rebases
@@ -87,24 +82,11 @@ export const fetchFromCli = async (opts) => {
87
82
  const { projectPath } = getConfigPaths()
88
83
  await fs.promises.mkdir(path.dirname(projectPath), { recursive: true })
89
84
  await fs.promises.writeFile(projectPath, JSON.stringify(payload, null, 2))
90
- } catch (_) {}
91
-
92
- if (verbose) {
93
- if (projectKey) {
94
- console.log(
95
- chalk.bold('Symbols'),
96
- 'data fetched for',
97
- chalk.green(payload.name)
98
- )
99
- } else {
100
- console.log(
101
- chalk.bold('Symbols'),
102
- 'config fetched from',
103
- chalk.bold('default-config from:'),
104
- chalk.dim.underline(DEFAULT_REMOTE_REPOSITORY)
105
- )
106
- }
107
- console.log()
85
+ } catch (e) {
86
+ console.error(chalk.bold.red('\nError writing file'))
87
+ if (verbose) console.error(e)
88
+ else console.log(debugMsg)
89
+ process.exit(1)
108
90
  }
109
91
 
110
92
  const { version: fetchedVersion, ...config } = payload
@@ -123,30 +105,12 @@ export const fetchFromCli = async (opts) => {
123
105
  } else console.log(chalk.dim(t + ':'), chalk.bold(type))
124
106
  }
125
107
 
126
- if (!distDir) {
127
- const bodyString = JSON.stringify(payload, null, prettify ?? 2)
128
-
129
- try {
130
- await fs.writeFileSync(LOCAL_CONFIG_PATH, bodyString)
131
-
132
- if (verbose) {
133
- console.log(chalk.dim('\ndynamic.json has been updated:'))
134
- console.log(chalk.dim.underline(LOCAL_CONFIG_PATH))
135
- }
136
-
137
- console.log(chalk.bold.green('\nSuccessfully wrote file'))
138
- } catch (e) {
139
- console.log(chalk.bold.red('\nError writing file'))
140
- if (verbose) console.error(e)
141
- else console.log(debugMsg)
142
- }
143
-
144
- console.log()
145
- console.warn(
146
- 'No --dist-dir option or "distDir" in symbols.json provided. Saving in ./node_modules/@symbo.ls/init/dynamic.json.'
147
- )
148
- return {}
149
- }
108
+ // Resolve effective distDir from CLI flag or symbols.json, with a sane default
109
+ const distDir =
110
+ resolveDistDir(symbolsConfig, {
111
+ distDirOverride: opts.distDir
112
+ }) ||
113
+ path.join(process.cwd(), 'smbls')
150
114
 
151
115
  if (payload.components && convertOpt && framework) {
152
116
  convertFromCli(payload.components, { ...opts, framework })
@@ -169,5 +133,5 @@ program
169
133
  .option('--force', 'Force overriding changes from platform')
170
134
  .option('--update', 'Overriding changes from platform')
171
135
  .option('--verbose-code', 'Verbose errors and warnings')
172
- .option('--dist-dir', 'Directory to import files to.')
136
+ .option('--dist-dir <dir>', 'Directory to import files to.')
173
137
  .action(fetchFromCli)
package/bin/index.js CHANGED
@@ -5,6 +5,7 @@ import 'v8-compile-cache'
5
5
  import { program } from './program.js'
6
6
  import './install.js'
7
7
  import './init.js'
8
+ import './config.js'
8
9
  import './fetch.js'
9
10
  import './sync.js'
10
11
  import './clean.js'
package/bin/push.js CHANGED
@@ -12,13 +12,12 @@ import { generateDiffDisplay, showDiffPager } from '../helpers/diffUtils.js'
12
12
  import { getCurrentProjectData, postProjectChanges } from '../helpers/apiUtils.js'
13
13
  import { computeCoarseChanges, computeOrdersForTuples, preprocessChanges } from '../helpers/changesUtils.js'
14
14
  import { showAuthRequiredMessages, showProjectNotFoundMessages, showBuildErrorMessages } from '../helpers/buildMessages.js'
15
- import { loadSymbolsConfig } from '../helpers/symbolsConfig.js'
15
+ import { loadSymbolsConfig, resolveDistDir } from '../helpers/symbolsConfig.js'
16
16
  import { loadCliConfig, readLock, writeLock, updateLegacySymbolsJson } from '../helpers/config.js'
17
17
 
18
18
 
19
- async function buildLocalProject () {
19
+ async function buildLocalProject (distDir) {
20
20
  try {
21
- const distDir = path.join(process.cwd(), 'smbls')
22
21
  const outputDirectory = path.join(distDir, 'dist')
23
22
 
24
23
  await buildDirectory(distDir, outputDirectory)
@@ -114,11 +113,15 @@ export async function pushProjectChanges(options) {
114
113
  const appKey = cliConfig.projectKey || symbolsConfig.key
115
114
  const branch = cliConfig.branch || symbolsConfig.branch || 'main'
116
115
 
116
+ const distDir =
117
+ resolveDistDir(symbolsConfig) ||
118
+ path.join(process.cwd(), 'smbls')
119
+
117
120
  // Build and load local project
118
121
  console.log(chalk.dim('Building local project...'))
119
122
  let localProject
120
123
  try {
121
- localProject = await buildLocalProject()
124
+ localProject = await buildLocalProject(distDir)
122
125
  console.log(chalk.gray('Local project built successfully'))
123
126
  } catch (buildError) {
124
127
  showBuildErrorMessages(buildError)
package/bin/sync.js CHANGED
@@ -11,15 +11,13 @@ import { buildDirectory } from '../helpers/fileUtils.js'
11
11
  import { generateDiffDisplay, showDiffPager } from '../helpers/diffUtils.js'
12
12
  import { normalizeKeys } from '../helpers/compareUtils.js'
13
13
  import { getCurrentProjectData, postProjectChanges } from '../helpers/apiUtils.js'
14
- import { computeCoarseChanges, threeWayRebase, computeOrdersForTuples, preprocessChanges } from '../helpers/changesUtils.js'
14
+ import { threeWayRebase, computeOrdersForTuples, preprocessChanges } from '../helpers/changesUtils.js'
15
15
  import { createFs } from './fs.js'
16
16
  import { showAuthRequiredMessages, showBuildErrorMessages } from '../helpers/buildMessages.js'
17
- import { loadSymbolsConfig } from '../helpers/symbolsConfig.js'
17
+ import { loadSymbolsConfig, resolveDistDir } from '../helpers/symbolsConfig.js'
18
18
  import { loadCliConfig, readLock, writeLock, getConfigPaths, updateLegacySymbolsJson } from '../helpers/config.js'
19
- const RC_PATH = process.cwd() + '/symbols.json'
20
- const distDir = path.join(process.cwd(), 'smbls')
21
19
 
22
- async function buildLocalProject() {
20
+ async function buildLocalProject(distDir) {
23
21
  try {
24
22
  const outputDirectory = path.join(distDir, 'dist')
25
23
  await buildDirectory(distDir, outputDirectory)
@@ -165,6 +163,10 @@ export async function syncProjectChanges(options) {
165
163
  const appKey = cliConfig.projectKey || legacyKey
166
164
  const localBranch = cliConfig.branch || symbolsConfig.branch || 'main'
167
165
 
166
+ const distDir =
167
+ resolveDistDir(symbolsConfig) ||
168
+ path.join(process.cwd(), 'smbls')
169
+
168
170
  if (options.verbose) {
169
171
  console.log(chalk.dim('\nSync configuration:'))
170
172
  console.log(chalk.gray(`App Key: ${chalk.cyan(appKey)}`))
@@ -179,7 +181,7 @@ export async function syncProjectChanges(options) {
179
181
  console.log(chalk.dim('Building local project...'))
180
182
  let localProject
181
183
  try {
182
- localProject = await buildLocalProject()
184
+ localProject = await buildLocalProject(distDir)
183
185
  console.log(chalk.gray('Local project built successfully'))
184
186
  } catch (buildError) {
185
187
  showBuildErrorMessages(buildError)
@@ -1,11 +1,16 @@
1
+ import * as utils from '@domql/utils'
1
2
  import { normalizeKeys } from './compareUtils.js'
2
3
 
4
+ const { objectToString } = utils.default || utils
5
+
3
6
  // Keys managed by the CLI filesystem representation (exclude settings/schema/key/thumbnail/etc.)
4
7
  export const DATA_KEYS = [
5
8
  'designSystem','components','state','pages','snippets',
6
9
  'methods','functions','dependencies','files'
7
10
  ]
8
11
 
12
+ const SCHEMA_CODE_TYPES = new Set(['pages', 'components', 'functions', 'methods', 'snippets'])
13
+
9
14
  function stripMetaDeep(val) {
10
15
  if (Array.isArray(val)) {
11
16
  return val.map(stripMetaDeep)
@@ -61,9 +66,12 @@ export function computeCoarseChanges(base, local, keys = DATA_KEYS) {
61
66
 
62
67
  // Generate per-item granular changes one level deeper for each data type.
63
68
  // Ignore 'schema' comparisons; manage schema side-effects below.
69
+ const baseSchema = a?.schema || {}
70
+
64
71
  for (const typeKey of [...keys]) {
65
72
  const aSection = a?.[typeKey] || {}
66
73
  const bSection = b?.[typeKey] || {}
74
+ const aSchemaSection = baseSchema?.[typeKey] || {}
67
75
 
68
76
  // If sections are not plain objects (or are arrays), fallback to coarse replacement on the section itself
69
77
  const aIsObject = aSection && typeof aSection === 'object' && !Array.isArray(aSection)
@@ -90,11 +98,17 @@ export function computeCoarseChanges(base, local, keys = DATA_KEYS) {
90
98
  for (const itemKey of Object.keys(bSection)) {
91
99
  const aVal = aSection[itemKey]
92
100
  const bVal = bSection[itemKey]
93
- if (aVal === undefined) {
101
+ const hadPrev = typeof aVal !== 'undefined'
102
+ if (!hadPrev) {
94
103
  // New item
95
104
  changes.push(['update', [typeKey, itemKey], bVal])
96
- // Ensure any stale schema code is removed (safety)
97
- changes.push(['delete', ['schema', typeKey, itemKey, 'code']])
105
+ const hadSchema = aSchemaSection && Object.prototype.hasOwnProperty.call(aSchemaSection, itemKey)
106
+ if (SCHEMA_CODE_TYPES.has(typeKey) && !hadSchema) {
107
+ const schemaItem = buildSchemaItemFromData(typeKey, itemKey, bVal)
108
+ if (schemaItem) {
109
+ changes.push(['update', ['schema', typeKey, itemKey], schemaItem])
110
+ }
111
+ }
98
112
  } else if (!equal(aVal, bVal)) {
99
113
  // Updated item
100
114
  changes.push(['update', [typeKey, itemKey], bVal])
@@ -112,8 +126,18 @@ export function threeWayRebase(base, local, remote, keys = DATA_KEYS) {
112
126
  const theirsKeys = computeChangedKeys(base, remote, keys)
113
127
  const conflicts = oursKeys.filter(k => theirsKeys.includes(k))
114
128
 
115
- const ours = computeCoarseChanges(base, local, keys).filter(([_, [k]]) => oursKeys.includes(k))
116
- const theirs = computeCoarseChanges(base, remote, keys).filter(([_, [k]]) => theirsKeys.includes(k))
129
+ const allOurs = computeCoarseChanges(base, local, keys)
130
+ const allTheirs = computeCoarseChanges(base, remote, keys)
131
+
132
+ const ownsKey = (path, set) => {
133
+ if (!Array.isArray(path) || !path.length) return false
134
+ const [rootKey, typeKey] = path
135
+ const primary = rootKey === 'schema' ? typeKey : rootKey
136
+ return set.includes(primary)
137
+ }
138
+
139
+ const ours = allOurs.filter(([, path]) => ownsKey(path, oursKeys))
140
+ const theirs = allTheirs.filter(([, path]) => ownsKey(path, theirsKeys))
117
141
 
118
142
  let finalChanges = []
119
143
  if (conflicts.length === 0) {
@@ -224,6 +248,13 @@ function normaliseSchemaCode(code) {
224
248
  .replaceAll('/////tilde', '`')
225
249
  }
226
250
 
251
+ function encodeSchemaCode(code) {
252
+ if (typeof code !== 'string' || !code.length) return ''
253
+ return code
254
+ .replaceAll('\n', '/////n')
255
+ .replaceAll('`', '/////tilde')
256
+ }
257
+
227
258
  function parseExportedObject(code) {
228
259
  const src = normaliseSchemaCode(code)
229
260
  if (!src) return null
@@ -236,6 +267,46 @@ function parseExportedObject(code) {
236
267
  }
237
268
  }
238
269
 
270
+ export function buildSchemaCodeFromObject(obj) {
271
+ if (!obj || typeof obj !== 'object') return ''
272
+ const body = objectToString(obj, 2)
273
+ const src = `export default ${body}`
274
+ return encodeSchemaCode(src)
275
+ }
276
+
277
+ function buildSchemaItemFromData(type, key, value) {
278
+ const schemaType = type
279
+
280
+ const base = {
281
+ title: key,
282
+ key,
283
+ type: schemaType
284
+ }
285
+
286
+ if (['pages', 'components'].includes(schemaType)) {
287
+ base.settings = {
288
+ gridOptions: {}
289
+ }
290
+ base.props = {}
291
+ base.interactivity = []
292
+ base.dataTypes = []
293
+ base.error = null
294
+ }
295
+
296
+ if (SCHEMA_CODE_TYPES.has(schemaType)) {
297
+ try {
298
+ const code = buildSchemaCodeFromObject(value)
299
+ if (code) {
300
+ base.code = code
301
+ }
302
+ } catch (_) {
303
+ // Fallback: omit code field if serialisation fails
304
+ }
305
+ }
306
+
307
+ return base
308
+ }
309
+
239
310
  function extractTopLevelKeysFromCode(code) {
240
311
  const obj = parseExportedObject(code)
241
312
  if (!obj || typeof obj !== 'object') return []
@@ -32,4 +32,35 @@ export const loadSymbolsConfig = async (options = {}) => {
32
32
  if (required) process.exit(1)
33
33
  return null
34
34
  }
35
- }
35
+ }
36
+
37
+ /**
38
+ * Resolve the effective dist directory for the current workspace.
39
+ *
40
+ * Precedence:
41
+ * - explicit override (e.g. CLI flag)
42
+ * - symbols.json.distDir
43
+ *
44
+ * Returns an absolute path when possible; null when no distDir
45
+ * configuration is available so callers can decide on a default.
46
+ */
47
+ export function resolveDistDir (symbolsConfig, options = {}) {
48
+ const cfg = symbolsConfig || {}
49
+ const {
50
+ // Prefer explicit override from callers (e.g. CLI flag)
51
+ distDirOverride,
52
+ distDir: overrideDistDir,
53
+ cwd = process.cwd()
54
+ } = options
55
+
56
+ const raw =
57
+ distDirOverride ||
58
+ overrideDistDir ||
59
+ cfg.distDir
60
+
61
+ if (!raw) return null
62
+
63
+ // If already absolute, return as-is; otherwise resolve against cwd
64
+ if (path.isAbsolute(raw)) return raw
65
+ return path.resolve(cwd, raw)
66
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symbo.ls/cli",
3
- "version": "2.33.26",
3
+ "version": "2.33.28",
4
4
  "description": "Fetch your Symbols configuration",
5
5
  "main": "bin/fetch.js",
6
6
  "author": "Symbols",
@@ -15,9 +15,9 @@
15
15
  "vpatch": "npm version patch && npm publish"
16
16
  },
17
17
  "dependencies": {
18
- "@symbo.ls/fetch": "^2.33.26",
19
- "@symbo.ls/init": "^2.33.26",
20
- "@symbo.ls/socket": "^2.33.26",
18
+ "@symbo.ls/fetch": "^2.33.28",
19
+ "@symbo.ls/init": "^2.33.28",
20
+ "@symbo.ls/socket": "^2.33.28",
21
21
  "chalk": "^5.4.1",
22
22
  "chokidar": "^4.0.3",
23
23
  "commander": "^13.1.0",
@@ -28,5 +28,5 @@
28
28
  "socket.io-client": "^4.8.1",
29
29
  "v8-compile-cache": "^2.4.0"
30
30
  },
31
- "gitHead": "60d701cca225200ddb1e15d6a3a31da25dfc3bc0"
31
+ "gitHead": "58b2f9f34b228712ccc4352e15913c52d6007afb"
32
32
  }