@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/push.js
CHANGED
|
@@ -5,79 +5,58 @@ import chalk from 'chalk'
|
|
|
5
5
|
import inquirer from 'inquirer'
|
|
6
6
|
import { loadModule } from './require.js'
|
|
7
7
|
import { program } from './program.js'
|
|
8
|
-
import { CredentialManager } from '
|
|
8
|
+
import { CredentialManager } from '../helpers/credentialManager.js'
|
|
9
9
|
import { buildDirectory } from '../helpers/fileUtils.js'
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
} from '../helpers/
|
|
16
|
-
|
|
17
|
-
const RC_PATH = process.cwd() + '/symbols.json'
|
|
18
|
-
const CREATE_PROJECT_URL = 'https://symbols.app/create'
|
|
19
|
-
|
|
20
|
-
function printProjectNotFoundGuidance (appKey) {
|
|
21
|
-
console.error(chalk.bold.red('\nProject not found or access denied.'))
|
|
22
|
-
console.error(chalk.bold.yellow('\nPossible reasons:'))
|
|
23
|
-
console.error(chalk.gray('1. The project does not exist'))
|
|
24
|
-
console.error(chalk.gray("2. You don't have access to this project"))
|
|
25
|
-
console.error(chalk.gray('3. The app key in symbols.json might be incorrect'))
|
|
26
|
-
|
|
27
|
-
console.error(chalk.bold.yellow('\nTo resolve this:'))
|
|
28
|
-
console.error(
|
|
29
|
-
chalk.white(
|
|
30
|
-
`1. Visit ${chalk.cyan.underline(
|
|
31
|
-
CREATE_PROJECT_URL
|
|
32
|
-
)} to create a new project`
|
|
33
|
-
)
|
|
34
|
-
)
|
|
35
|
-
console.error(
|
|
36
|
-
chalk.white(
|
|
37
|
-
'2. After creating the project, update your symbols.json with the correct information:'
|
|
38
|
-
)
|
|
39
|
-
)
|
|
40
|
-
console.error(chalk.gray(` - Verify the app key: ${chalk.cyan(appKey)}`))
|
|
41
|
-
console.error(chalk.gray(' - Make sure you have the correct permissions'))
|
|
10
|
+
import { normalizeKeys } from '../helpers/compareUtils.js'
|
|
11
|
+
import { generateDiffDisplay, showDiffPager } from '../helpers/diffUtils.js'
|
|
12
|
+
import { getCurrentProjectData, postProjectChanges } from '../helpers/apiUtils.js'
|
|
13
|
+
import { computeCoarseChanges, computeOrdersForTuples, preprocessChanges } from '../helpers/changesUtils.js'
|
|
14
|
+
import { showAuthRequiredMessages, showProjectNotFoundMessages, showBuildErrorMessages } from '../helpers/buildMessages.js'
|
|
15
|
+
import { loadSymbolsConfig } from '../helpers/symbolsConfig.js'
|
|
16
|
+
import { loadCliConfig, readLock, writeLock, updateLegacySymbolsJson } from '../helpers/config.js'
|
|
42
17
|
|
|
43
|
-
console.error(chalk.bold.yellow('\nThen try again:'))
|
|
44
|
-
console.error(chalk.cyan('$ smbls push'))
|
|
45
|
-
}
|
|
46
18
|
|
|
47
|
-
async function
|
|
19
|
+
async function buildLocalProject () {
|
|
48
20
|
try {
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
chalk.cyan(JSON.stringify({ key: 'your.app.key' }, null, 2))
|
|
61
|
-
)
|
|
62
|
-
} else {
|
|
63
|
-
console.error(
|
|
64
|
-
chalk.bold.red('Please include symbols.json in your repository root')
|
|
65
|
-
)
|
|
21
|
+
const distDir = path.join(process.cwd(), 'smbls')
|
|
22
|
+
const outputDirectory = path.join(distDir, 'dist')
|
|
23
|
+
|
|
24
|
+
await buildDirectory(distDir, outputDirectory)
|
|
25
|
+
const outputFile = path.join(outputDirectory, 'index.js')
|
|
26
|
+
return normalizeKeys(await loadModule(outputFile, { silent: false }))
|
|
27
|
+
} catch (error) {
|
|
28
|
+
// Enhance error with build context
|
|
29
|
+
error.buildContext = {
|
|
30
|
+
command: 'push',
|
|
31
|
+
workspace: process.cwd()
|
|
66
32
|
}
|
|
67
|
-
|
|
33
|
+
throw error
|
|
68
34
|
}
|
|
69
35
|
}
|
|
70
36
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
37
|
+
function getAt(obj, pathArr = []) {
|
|
38
|
+
try {
|
|
39
|
+
return pathArr.reduce((acc, k) => (acc == null ? undefined : acc[k]), obj)
|
|
40
|
+
} catch (_) {
|
|
41
|
+
return undefined
|
|
42
|
+
}
|
|
43
|
+
}
|
|
74
44
|
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
|
|
45
|
+
function buildDiffsFromChanges(changes, base, local) {
|
|
46
|
+
const diffs = []
|
|
47
|
+
for (const [op, path, value] of changes) {
|
|
48
|
+
const oldVal = getAt(base, path)
|
|
49
|
+
if (op === 'delete') {
|
|
50
|
+
diffs.push(generateDiffDisplay('delete', path, oldVal))
|
|
51
|
+
} else {
|
|
52
|
+
const newVal = value !== undefined ? value : getAt(local, path)
|
|
53
|
+
diffs.push(generateDiffDisplay('update', path, oldVal, newVal))
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return diffs
|
|
78
57
|
}
|
|
79
58
|
|
|
80
|
-
async function confirmChanges (changes) {
|
|
59
|
+
async function confirmChanges (changes, base, local) {
|
|
81
60
|
if (changes.length === 0) {
|
|
82
61
|
console.log(chalk.bold.yellow('No changes detected'))
|
|
83
62
|
return false
|
|
@@ -93,6 +72,19 @@ async function confirmChanges (changes) {
|
|
|
93
72
|
console.log(chalk.gray(`- ${type}: ${chalk.cyan(count)} changes`))
|
|
94
73
|
})
|
|
95
74
|
|
|
75
|
+
const { view } = await inquirer.prompt([
|
|
76
|
+
{
|
|
77
|
+
type: 'confirm',
|
|
78
|
+
name: 'view',
|
|
79
|
+
message: 'View full list of changes?',
|
|
80
|
+
default: false
|
|
81
|
+
}
|
|
82
|
+
])
|
|
83
|
+
if (view) {
|
|
84
|
+
const diffs = buildDiffsFromChanges(changes, base, local)
|
|
85
|
+
await showDiffPager(diffs)
|
|
86
|
+
}
|
|
87
|
+
|
|
96
88
|
const { proceed } = await inquirer.prompt([
|
|
97
89
|
{
|
|
98
90
|
type: 'confirm',
|
|
@@ -105,76 +97,123 @@ async function confirmChanges (changes) {
|
|
|
105
97
|
return proceed
|
|
106
98
|
}
|
|
107
99
|
|
|
108
|
-
export async function pushProjectChanges
|
|
100
|
+
export async function pushProjectChanges(options) {
|
|
101
|
+
const { verbose, message, type = 'patch' } = options
|
|
109
102
|
const credManager = new CredentialManager()
|
|
110
|
-
const authToken = credManager.
|
|
103
|
+
const authToken = credManager.ensureAuthToken()
|
|
111
104
|
|
|
112
105
|
if (!authToken) {
|
|
113
|
-
|
|
106
|
+
showAuthRequiredMessages()
|
|
114
107
|
process.exit(1)
|
|
115
108
|
}
|
|
116
109
|
|
|
117
110
|
try {
|
|
118
|
-
const
|
|
119
|
-
const
|
|
111
|
+
const symbolsConfig = await loadSymbolsConfig()
|
|
112
|
+
const cliConfig = loadCliConfig()
|
|
113
|
+
const lock = readLock()
|
|
114
|
+
const appKey = cliConfig.projectKey || symbolsConfig.key
|
|
115
|
+
const branch = cliConfig.branch || symbolsConfig.branch || 'main'
|
|
120
116
|
|
|
121
117
|
// Build and load local project
|
|
122
118
|
console.log(chalk.dim('Building local project...'))
|
|
123
|
-
|
|
124
|
-
console.log(chalk.gray('Local project built and loaded successfully'))
|
|
125
|
-
|
|
126
|
-
// Get server data
|
|
119
|
+
let localProject
|
|
127
120
|
try {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
console.log(chalk.
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
121
|
+
localProject = await buildLocalProject()
|
|
122
|
+
console.log(chalk.gray('Local project built successfully'))
|
|
123
|
+
} catch (buildError) {
|
|
124
|
+
showBuildErrorMessages(buildError)
|
|
125
|
+
process.exit(1)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Get current server state (ETag aware)
|
|
129
|
+
console.log(chalk.dim('Fetching current server state...'))
|
|
130
|
+
const serverResp = await getCurrentProjectData(
|
|
131
|
+
{ projectKey: appKey, projectId: lock.projectId },
|
|
132
|
+
authToken,
|
|
133
|
+
{ branch, includePending: true, etag: lock.etag }
|
|
134
|
+
)
|
|
135
|
+
const serverProject = serverResp.notModified ? null : serverResp.data
|
|
136
|
+
|
|
137
|
+
// Check if server project is empty (not found or no access)
|
|
138
|
+
if (serverProject && Object.keys(serverProject).length === 0) {
|
|
139
|
+
showProjectNotFoundMessages(appKey)
|
|
140
|
+
process.exit(1)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
console.log(chalk.gray('Server state fetched successfully'))
|
|
144
|
+
|
|
145
|
+
// Calculate coarse local changes vs server snapshot (or base)
|
|
146
|
+
const base = normalizeKeys(serverProject || {})
|
|
147
|
+
const changes = computeCoarseChanges(base, localProject)
|
|
148
|
+
|
|
149
|
+
if (!changes.length) {
|
|
150
|
+
console.log(chalk.bold.yellow('\nNo changes to push'))
|
|
151
|
+
return
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Show change summary
|
|
155
|
+
console.log('\nLocal changes to push:')
|
|
156
|
+
const byType = changes.reduce((acc, [t]) => ((acc[t] = (acc[t] || 0) + 1), acc), {})
|
|
157
|
+
Object.entries(byType).forEach(([t, c]) => {
|
|
158
|
+
console.log(chalk.gray(`- ${t}: ${chalk.cyan(c)} changes`))
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
// Confirm push
|
|
162
|
+
const shouldProceed = await confirmChanges(changes, base, localProject)
|
|
163
|
+
if (!shouldProceed) {
|
|
164
|
+
console.log(chalk.yellow('Push cancelled'))
|
|
165
|
+
return
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Push changes
|
|
169
|
+
console.log(chalk.dim('\nPushing changes...'))
|
|
170
|
+
const projectId = lock.projectId || serverProject?.projectInfo?.id
|
|
171
|
+
if (!projectId) {
|
|
172
|
+
console.log(chalk.red('Unable to resolve projectId. Please fetch first to initialize lock.'))
|
|
174
173
|
process.exit(1)
|
|
175
174
|
}
|
|
175
|
+
const operationId = `cli-${Date.now()}`
|
|
176
|
+
// Derive granular changes against server base and compute orders using local for pending children
|
|
177
|
+
const { granularChanges } = preprocessChanges(base, changes)
|
|
178
|
+
const orders = computeOrdersForTuples(localProject, granularChanges)
|
|
179
|
+
const result = await postProjectChanges(projectId, authToken, {
|
|
180
|
+
branch,
|
|
181
|
+
type,
|
|
182
|
+
operationId,
|
|
183
|
+
// Send both forms for compatibility with preprocessors
|
|
184
|
+
changes,
|
|
185
|
+
granularChanges,
|
|
186
|
+
orders
|
|
187
|
+
})
|
|
188
|
+
if (result.noOp) {
|
|
189
|
+
console.log(chalk.bold.yellow('\nNo-op on server'))
|
|
190
|
+
return
|
|
191
|
+
}
|
|
192
|
+
const { id: versionId, value: version } = result.data || {}
|
|
193
|
+
|
|
194
|
+
// Update symbols.json
|
|
195
|
+
updateLegacySymbolsJson({ ...(symbolsConfig || {}), version, versionId })
|
|
196
|
+
|
|
197
|
+
console.log(chalk.bold.green('\nChanges pushed successfully!'))
|
|
198
|
+
console.log(chalk.gray(`New version: ${chalk.cyan(version)}`))
|
|
199
|
+
|
|
200
|
+
// Refresh lock with latest ETag by fetching head
|
|
201
|
+
const latest = await getCurrentProjectData(
|
|
202
|
+
{ projectKey: appKey, projectId },
|
|
203
|
+
authToken,
|
|
204
|
+
{ branch, includePending: true }
|
|
205
|
+
)
|
|
206
|
+
writeLock({
|
|
207
|
+
etag: latest.etag || null,
|
|
208
|
+
version,
|
|
209
|
+
branch,
|
|
210
|
+
projectId,
|
|
211
|
+
pulledAt: new Date().toISOString()
|
|
212
|
+
})
|
|
213
|
+
|
|
176
214
|
} catch (error) {
|
|
177
215
|
console.error(chalk.bold.red('\nPush failed:'), chalk.white(error.message))
|
|
216
|
+
if (verbose) console.error(error.stack)
|
|
178
217
|
process.exit(1)
|
|
179
218
|
}
|
|
180
219
|
}
|
|
@@ -182,4 +221,5 @@ export async function pushProjectChanges () {
|
|
|
182
221
|
program
|
|
183
222
|
.command('push')
|
|
184
223
|
.description('Push changes to platform')
|
|
224
|
+
.option('-m, --message <message>', 'Specify a commit message')
|
|
185
225
|
.action(pushProjectChanges)
|
package/bin/require.js
CHANGED
|
@@ -2,12 +2,146 @@
|
|
|
2
2
|
|
|
3
3
|
import fs from 'fs'
|
|
4
4
|
import { createRequire } from 'module'
|
|
5
|
+
import path from 'path'
|
|
5
6
|
|
|
6
|
-
class ImportError extends Error {
|
|
7
|
-
|
|
7
|
+
class ImportError extends Error {
|
|
8
|
+
constructor(message, path, error) {
|
|
9
|
+
super(message)
|
|
10
|
+
this.name = 'ImportError'
|
|
11
|
+
this.path = path
|
|
12
|
+
this.originalError = error
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const require = createRequire(import.meta.url)
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Loads a module or file content based on file type and options
|
|
20
|
+
* @param {string} modulePath - Path to the module/file to load
|
|
21
|
+
* @param {Object} options - Configuration options
|
|
22
|
+
* @param {boolean} options.json - Force JSON parsing
|
|
23
|
+
* @param {boolean} options.raw - Return raw file contents
|
|
24
|
+
* @param {string} options.encoding - File encoding (default: 'utf8')
|
|
25
|
+
* @param {boolean} options.silent - Don't throw errors, return null instead
|
|
26
|
+
* @param {boolean} options.noCache - Bust Node's require cache before loading
|
|
27
|
+
* @returns {Promise<any>} - Loaded module/file contents
|
|
28
|
+
*/
|
|
29
|
+
export const loadModule = async (modulePath, options = {}) => {
|
|
30
|
+
const {
|
|
31
|
+
json = false,
|
|
32
|
+
raw = false,
|
|
33
|
+
encoding = 'utf8',
|
|
34
|
+
silent = false,
|
|
35
|
+
noCache = false
|
|
36
|
+
} = options
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
if (!fs.existsSync(modulePath)) {
|
|
40
|
+
throw new ImportError(`File not found: ${modulePath}`, modulePath)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Get file extension
|
|
44
|
+
const ext = path.extname(modulePath)
|
|
45
|
+
|
|
46
|
+
// Return raw file contents if requested
|
|
47
|
+
if (raw) {
|
|
48
|
+
return fs.readFileSync(modulePath, encoding)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Handle JSON files or forced JSON parsing
|
|
52
|
+
if (ext === '.json' || json) {
|
|
53
|
+
const content = fs.readFileSync(modulePath, encoding)
|
|
54
|
+
try {
|
|
55
|
+
return JSON.parse(content)
|
|
56
|
+
} catch (e) {
|
|
57
|
+
throw new ImportError(
|
|
58
|
+
`Invalid JSON in ${modulePath}: ${e.message}`,
|
|
59
|
+
modulePath,
|
|
60
|
+
e
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Handle JavaScript/Node modules
|
|
66
|
+
try {
|
|
67
|
+
if (noCache) {
|
|
68
|
+
try {
|
|
69
|
+
const resolved = require.resolve(modulePath)
|
|
70
|
+
delete require.cache[resolved]
|
|
71
|
+
} catch (_) {}
|
|
72
|
+
}
|
|
73
|
+
const module = require(modulePath)
|
|
74
|
+
return module?.default || module
|
|
75
|
+
} catch (e) {
|
|
76
|
+
throw new ImportError(
|
|
77
|
+
`Error loading module ${modulePath}: ${e.message}`,
|
|
78
|
+
modulePath,
|
|
79
|
+
e
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
} catch (error) {
|
|
83
|
+
if (silent) return null
|
|
84
|
+
throw error
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Synchronously loads a module or file content
|
|
90
|
+
* @param {string} modulePath - Path to the module/file to load
|
|
91
|
+
* @param {Object} options - Configuration options (same as loadModule)
|
|
92
|
+
* @returns {any} - Loaded module/file contents
|
|
93
|
+
*/
|
|
94
|
+
export const loadModuleSync = (modulePath, options = {}) => {
|
|
95
|
+
const {
|
|
96
|
+
json = false,
|
|
97
|
+
raw = false,
|
|
98
|
+
encoding = 'utf8',
|
|
99
|
+
silent = false,
|
|
100
|
+
noCache = false
|
|
101
|
+
} = options
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
if (!fs.existsSync(modulePath)) {
|
|
105
|
+
throw new ImportError(`File not found: ${modulePath}`, modulePath)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const ext = path.extname(modulePath)
|
|
109
|
+
|
|
110
|
+
if (raw) {
|
|
111
|
+
return fs.readFileSync(modulePath, encoding)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (ext === '.json' || json) {
|
|
115
|
+
const content = fs.readFileSync(modulePath, encoding)
|
|
116
|
+
try {
|
|
117
|
+
return JSON.parse(content)
|
|
118
|
+
} catch (e) {
|
|
119
|
+
throw new ImportError(
|
|
120
|
+
`Invalid JSON in ${modulePath}: ${e.message}`,
|
|
121
|
+
modulePath,
|
|
122
|
+
e
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
8
126
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
127
|
+
try {
|
|
128
|
+
if (noCache) {
|
|
129
|
+
try {
|
|
130
|
+
const resolved = require.resolve(modulePath)
|
|
131
|
+
delete require.cache[resolved]
|
|
132
|
+
} catch (_) {}
|
|
133
|
+
}
|
|
134
|
+
const module = require(modulePath)
|
|
135
|
+
return module?.default || module
|
|
136
|
+
} catch (e) {
|
|
137
|
+
throw new ImportError(
|
|
138
|
+
`Error loading module ${modulePath}: ${e.message}`,
|
|
139
|
+
modulePath,
|
|
140
|
+
e
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
} catch (error) {
|
|
144
|
+
if (silent) return null
|
|
145
|
+
throw error
|
|
146
|
+
}
|
|
13
147
|
}
|
package/bin/socket-server.js
CHANGED
|
@@ -2,21 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
import { sync } from '@symbo.ls/socket'
|
|
4
4
|
import { program } from './program.js'
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
const RC_PATH = process.cwd() + '/symbols.json'
|
|
8
|
-
let rc = {}
|
|
9
|
-
try {
|
|
10
|
-
rc = loadModule(RC_PATH) // eslint-disable-line
|
|
11
|
-
} catch (e) { console.error('Please include symbols.json to your root of respository') }
|
|
5
|
+
import { loadSymbolsConfig } from '../helpers/symbolsConfig.js'
|
|
12
6
|
|
|
13
7
|
program
|
|
14
8
|
.command('socket-server')
|
|
15
9
|
.description('Realtime sync with Symbols')
|
|
16
10
|
.option('-l, --live', 'Bypass the local build')
|
|
17
11
|
.action(async (options) => {
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
const symbolsConfig = await loadSymbolsConfig()
|
|
13
|
+
if (symbolsConfig) {
|
|
14
|
+
const opts = { ...symbolsConfig, ...options }
|
|
20
15
|
sync(null, opts)
|
|
21
|
-
}
|
|
16
|
+
}
|
|
22
17
|
})
|