@symbo.ls/cli 2.11.514 → 2.11.516
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/create.js +1 -1
- package/bin/fetch.js +0 -3
- package/bin/index.js +2 -0
- package/bin/linking/packages.js +9 -9
- package/bin/login.js +108 -12
- package/bin/push.js +185 -0
- package/helpers/apiUtils.js +73 -0
- package/helpers/compareUtils.js +135 -0
- package/helpers/config.js +27 -0
- package/helpers/diffUtils.js +55 -0
- package/{bin/fs2.js → helpers/fileUtils.js} +16 -34
- package/package.json +4 -4
package/bin/create.js
CHANGED
|
@@ -50,7 +50,7 @@ program
|
|
|
50
50
|
framework = 'vue3'
|
|
51
51
|
}
|
|
52
52
|
const cloneUrl = REPO_URLS[framework]
|
|
53
|
-
const packageManager = options.packageManager || '
|
|
53
|
+
const packageManager = options.packageManager || 'npm'
|
|
54
54
|
|
|
55
55
|
if (folderExists(dest)) {
|
|
56
56
|
console.error(`Folder ${dest} already exists!`)
|
package/bin/fetch.js
CHANGED
|
@@ -8,7 +8,6 @@ import * as fetch from '@symbo.ls/fetch'
|
|
|
8
8
|
import * as utils from '@domql/utils'
|
|
9
9
|
import { convertFromCli } from './convert.js'
|
|
10
10
|
import { createFs } from './fs.js'
|
|
11
|
-
import { fs2js } from './fs2.js'
|
|
12
11
|
const { isObjectLike } = (utils.default || utils)
|
|
13
12
|
const { fetchRemote } = (fetch.default || fetch)
|
|
14
13
|
|
|
@@ -145,5 +144,3 @@ program
|
|
|
145
144
|
.option('--verbose-code', 'Verbose errors and warnings')
|
|
146
145
|
.option('--dist-dir', 'Directory to import files to.')
|
|
147
146
|
.action(fetchFromCli)
|
|
148
|
-
|
|
149
|
-
program.command('push').description('Push changes to platform').action(fs2js)
|
package/bin/index.js
CHANGED
package/bin/linking/packages.js
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
export default [
|
|
2
|
-
'attrs-in-props',
|
|
3
2
|
'create-smbls',
|
|
3
|
+
'@symbo.ls/fetch',
|
|
4
|
+
'attrs-in-props',
|
|
5
|
+
'@symbo.ls/feather-icons',
|
|
6
|
+
'@symbo.ls/fluent-icons',
|
|
4
7
|
'@symbo.ls/emotion',
|
|
5
8
|
'@symbo.ls/default-icons',
|
|
6
|
-
'@symbo.ls/feather-icons',
|
|
7
|
-
'@symbo.ls/fetch',
|
|
8
|
-
'@symbo.ls/material-icons',
|
|
9
9
|
'@symbo.ls/preview',
|
|
10
|
+
'@symbo.ls/material-icons',
|
|
10
11
|
'@symbo.ls/utils',
|
|
11
|
-
'@symbo.ls/fluent-icons',
|
|
12
12
|
'@symbo.ls/default-config',
|
|
13
13
|
'@symbo.ls/scratch',
|
|
14
14
|
'@symbo.ls/init',
|
|
15
15
|
'@symbo.ls/atoms',
|
|
16
16
|
'@symbo.ls/socket',
|
|
17
|
-
'@symbo.ls/icon',
|
|
18
17
|
'css-in-props',
|
|
19
|
-
'@symbo.ls/
|
|
20
|
-
'@symbo.ls/notification',
|
|
18
|
+
'@symbo.ls/icon',
|
|
21
19
|
'@symbo.ls/input',
|
|
22
|
-
'@symbo.ls/
|
|
20
|
+
'@symbo.ls/box',
|
|
23
21
|
'@symbo.ls/link',
|
|
24
22
|
'@symbo.ls/select',
|
|
23
|
+
'@symbo.ls/notification',
|
|
24
|
+
'@symbo.ls/tooltip',
|
|
25
25
|
'@symbo.ls/cli',
|
|
26
26
|
'@symbo.ls/button',
|
|
27
27
|
'@symbo.ls/avatar',
|
package/bin/login.js
CHANGED
|
@@ -1,18 +1,114 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
import inquirer from 'inquirer'
|
|
4
|
+
import fs from 'fs'
|
|
5
|
+
import path from 'path'
|
|
6
|
+
import os from 'os'
|
|
7
|
+
import chalk from 'chalk'
|
|
8
|
+
import { program } from './program.js'
|
|
9
|
+
import { getApiUrl } from '../helpers/config.js'
|
|
10
|
+
|
|
11
|
+
const RC_FILE = '.smblsrc'
|
|
12
|
+
|
|
13
|
+
// Helper to manage credentials
|
|
14
|
+
export class CredentialManager {
|
|
15
|
+
constructor () {
|
|
16
|
+
this.rcPath = path.join(os.homedir(), RC_FILE)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Load credentials from rc file
|
|
20
|
+
loadCredentials () {
|
|
21
|
+
try {
|
|
22
|
+
const data = fs.readFileSync(this.rcPath, 'utf8')
|
|
23
|
+
return JSON.parse(data)
|
|
24
|
+
} catch (err) {
|
|
25
|
+
return {}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Save credentials to rc file
|
|
30
|
+
saveCredentials (credentials) {
|
|
31
|
+
try {
|
|
32
|
+
fs.writeFileSync(this.rcPath, JSON.stringify(credentials, null, 2))
|
|
33
|
+
return true
|
|
34
|
+
} catch (err) {
|
|
35
|
+
console.error('Failed to save credentials:', err)
|
|
36
|
+
return false
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Get stored auth token
|
|
41
|
+
getAuthToken () {
|
|
42
|
+
const creds = this.loadCredentials()
|
|
43
|
+
return creds.authToken
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Clear stored credentials
|
|
47
|
+
clearCredentials () {
|
|
48
|
+
try {
|
|
49
|
+
fs.unlinkSync(this.rcPath)
|
|
50
|
+
return true
|
|
51
|
+
} catch (err) {
|
|
52
|
+
return false
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
5
56
|
|
|
6
57
|
program
|
|
7
|
-
.command('login
|
|
58
|
+
.command('login')
|
|
8
59
|
.description('Sign in to Symbols')
|
|
9
|
-
.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
console.log(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
60
|
+
.action(async () => {
|
|
61
|
+
console.log('yo login')
|
|
62
|
+
|
|
63
|
+
console.log(getApiUrl())
|
|
64
|
+
|
|
65
|
+
// Prompt for credentials
|
|
66
|
+
const answers = await inquirer.prompt([
|
|
67
|
+
{
|
|
68
|
+
type: 'input',
|
|
69
|
+
name: 'email',
|
|
70
|
+
message: 'Enter your email:',
|
|
71
|
+
validate: input => input.includes('@') || 'Please enter a valid email'
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
type: 'password',
|
|
75
|
+
name: 'password',
|
|
76
|
+
message: 'Enter your password:',
|
|
77
|
+
validate: input => input.length >= 6 || 'Password must be at least 6 characters'
|
|
78
|
+
}
|
|
79
|
+
])
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
// Make login request
|
|
83
|
+
const response = await fetch(`${getApiUrl()}/auth/login`, {
|
|
84
|
+
method: 'POST',
|
|
85
|
+
headers: {
|
|
86
|
+
'Content-Type': 'application/json'
|
|
87
|
+
},
|
|
88
|
+
body: JSON.stringify({
|
|
89
|
+
email: answers.email,
|
|
90
|
+
password: answers.password
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
const data = await response.json()
|
|
95
|
+
|
|
96
|
+
if (!response.ok) {
|
|
97
|
+
throw new Error(data.error || 'Authentication failed')
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Save credentials
|
|
101
|
+
const credManager = new CredentialManager()
|
|
102
|
+
credManager.saveCredentials({
|
|
103
|
+
authToken: data.token,
|
|
104
|
+
userId: data.userId,
|
|
105
|
+
email: answers.email
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
console.log(chalk.green('\nSuccessfully logged in!'))
|
|
109
|
+
console.log(chalk.dim(`Credentials saved to ${credManager.rcPath}`))
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error(chalk.red('\nLogin failed:'), error.message)
|
|
112
|
+
process.exit(1)
|
|
113
|
+
}
|
|
18
114
|
})
|
package/bin/push.js
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
import path from 'path'
|
|
4
|
+
import chalk from 'chalk'
|
|
5
|
+
import inquirer from 'inquirer'
|
|
6
|
+
import { loadModule } from './require.js'
|
|
7
|
+
import { program } from './program.js'
|
|
8
|
+
import { CredentialManager } from './login.js'
|
|
9
|
+
import { buildDirectory } from '../helpers/fileUtils.js'
|
|
10
|
+
import { showDiffPager } from '../helpers/diffUtils.js'
|
|
11
|
+
import { normalizeKeys, generateChanges } from '../helpers/compareUtils.js'
|
|
12
|
+
import {
|
|
13
|
+
getServerProjectData,
|
|
14
|
+
updateProjectOnServer
|
|
15
|
+
} from '../helpers/apiUtils.js'
|
|
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'))
|
|
42
|
+
|
|
43
|
+
console.error(chalk.bold.yellow('\nThen try again:'))
|
|
44
|
+
console.error(chalk.cyan('$ smbls push'))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function loadProjectConfiguration () {
|
|
48
|
+
try {
|
|
49
|
+
const config = await loadModule(RC_PATH)
|
|
50
|
+
if (!config.key) {
|
|
51
|
+
throw new Error('Missing app key in symbols.json')
|
|
52
|
+
}
|
|
53
|
+
return config
|
|
54
|
+
} catch (e) {
|
|
55
|
+
if (e.message.includes('Missing app key')) {
|
|
56
|
+
console.error(chalk.bold.red('\nInvalid symbols.json configuration:'))
|
|
57
|
+
console.error(chalk.white('The file must contain a valid app key.'))
|
|
58
|
+
console.error(chalk.bold.yellow('\nExample symbols.json:'))
|
|
59
|
+
console.error(
|
|
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
|
+
)
|
|
66
|
+
}
|
|
67
|
+
process.exit(1)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function buildLocalProject () {
|
|
72
|
+
const distDir = path.join(process.cwd(), 'smbls')
|
|
73
|
+
const outputDirectory = path.join(distDir, 'dist')
|
|
74
|
+
|
|
75
|
+
await buildDirectory(distDir, outputDirectory)
|
|
76
|
+
const outputFile = path.join(outputDirectory, 'index.js')
|
|
77
|
+
return normalizeKeys(await loadModule(outputFile))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function confirmChanges (changes) {
|
|
81
|
+
if (changes.length === 0) {
|
|
82
|
+
console.log(chalk.bold.yellow('No changes detected'))
|
|
83
|
+
return false
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
console.log(chalk.bold.white('\nDetected changes:'))
|
|
87
|
+
const changesByType = changes.reduce((acc, [type, path]) => {
|
|
88
|
+
acc[type] = (acc[type] || 0) + 1
|
|
89
|
+
return acc
|
|
90
|
+
}, {})
|
|
91
|
+
|
|
92
|
+
Object.entries(changesByType).forEach(([type, count]) => {
|
|
93
|
+
console.log(chalk.gray(`- ${type}: ${chalk.cyan(count)} changes`))
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
const { proceed } = await inquirer.prompt([
|
|
97
|
+
{
|
|
98
|
+
type: 'confirm',
|
|
99
|
+
name: 'proceed',
|
|
100
|
+
message: 'Proceed with these changes?',
|
|
101
|
+
default: false
|
|
102
|
+
}
|
|
103
|
+
])
|
|
104
|
+
|
|
105
|
+
return proceed
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export async function pushProjectChanges () {
|
|
109
|
+
const credManager = new CredentialManager()
|
|
110
|
+
const authToken = credManager.getAuthToken()
|
|
111
|
+
|
|
112
|
+
if (!authToken) {
|
|
113
|
+
console.error(chalk.red('Please login first using: smbls login'))
|
|
114
|
+
process.exit(1)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
const config = await loadProjectConfiguration()
|
|
119
|
+
const { key: appKey } = config
|
|
120
|
+
|
|
121
|
+
// Build and load local project
|
|
122
|
+
console.log(chalk.dim('Building local project...'))
|
|
123
|
+
const projectData = await buildLocalProject()
|
|
124
|
+
console.log(chalk.gray('Local project built and loaded successfully'))
|
|
125
|
+
|
|
126
|
+
// Get server data
|
|
127
|
+
try {
|
|
128
|
+
console.log(chalk.dim('Fetching server data...'))
|
|
129
|
+
const serverProjectData = await getServerProjectData(appKey, authToken)
|
|
130
|
+
|
|
131
|
+
if (!serverProjectData) {
|
|
132
|
+
throw new Error('Failed to fetch server data: Empty response')
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const normalizedServerData = normalizeKeys(serverProjectData)
|
|
136
|
+
console.log(chalk.gray('Server data fetched successfully'))
|
|
137
|
+
|
|
138
|
+
// Compare and get changes
|
|
139
|
+
const { changes, diffs } = generateChanges(
|
|
140
|
+
normalizedServerData,
|
|
141
|
+
projectData
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
// Show changes and confirm
|
|
145
|
+
if (changes.length > 0) {
|
|
146
|
+
console.log('\nDetailed changes:')
|
|
147
|
+
await showDiffPager(diffs)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const shouldProceed = await confirmChanges(changes)
|
|
151
|
+
if (!shouldProceed) {
|
|
152
|
+
console.log(chalk.yellow('Operation cancelled'))
|
|
153
|
+
return
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Update server
|
|
157
|
+
console.log(chalk.dim('Updating server...'))
|
|
158
|
+
await updateProjectOnServer(appKey, authToken, changes, projectData)
|
|
159
|
+
|
|
160
|
+
console.log(chalk.bold.green('\nProject updated successfully!'))
|
|
161
|
+
console.log(
|
|
162
|
+
chalk.gray(`Total changes applied: ${chalk.cyan(changes.length)}`)
|
|
163
|
+
)
|
|
164
|
+
} catch (error) {
|
|
165
|
+
if (
|
|
166
|
+
error.message.includes('Failed to fetch server data') ||
|
|
167
|
+
(error.response && error.response.status === 404) ||
|
|
168
|
+
Object.keys(error.response?.data || {}).length === 0
|
|
169
|
+
) {
|
|
170
|
+
printProjectNotFoundGuidance(appKey)
|
|
171
|
+
} else {
|
|
172
|
+
throw error // Re-throw other errors to be caught by outer catch block
|
|
173
|
+
}
|
|
174
|
+
process.exit(1)
|
|
175
|
+
}
|
|
176
|
+
} catch (error) {
|
|
177
|
+
console.error(chalk.bold.red('\nPush failed:'), chalk.white(error.message))
|
|
178
|
+
process.exit(1)
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
program
|
|
183
|
+
.command('push')
|
|
184
|
+
.description('Push changes to platform')
|
|
185
|
+
.action(pushProjectChanges)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { getApiUrl } from './config.js'
|
|
2
|
+
import { ALLOWED_FIELDS } from './compareUtils.js'
|
|
3
|
+
|
|
4
|
+
export async function getServerProjectData (appKey, authToken) {
|
|
5
|
+
try {
|
|
6
|
+
const response = await fetch(`${getApiUrl()}/get/`, {
|
|
7
|
+
method: 'GET',
|
|
8
|
+
headers: {
|
|
9
|
+
'X-AppKey': appKey,
|
|
10
|
+
Authorization: `Bearer ${authToken}`
|
|
11
|
+
}
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const data = await response.json()
|
|
15
|
+
|
|
16
|
+
if (!response.ok) {
|
|
17
|
+
const error = new Error(`Failed to fetch server data: ${response.statusText}`)
|
|
18
|
+
error.response = {
|
|
19
|
+
status: response.status,
|
|
20
|
+
data
|
|
21
|
+
}
|
|
22
|
+
throw error
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Check if response is empty object
|
|
26
|
+
if (data && Object.keys(data).length === 0) {
|
|
27
|
+
const error = new Error('Project not found')
|
|
28
|
+
error.response = {
|
|
29
|
+
status: 404,
|
|
30
|
+
data
|
|
31
|
+
}
|
|
32
|
+
throw error
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return data
|
|
36
|
+
} catch (error) {
|
|
37
|
+
if (!error.response) {
|
|
38
|
+
error.response = {
|
|
39
|
+
status: 500,
|
|
40
|
+
data: {}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
throw error
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function updateProjectOnServer (appKey, authToken, changes, projectData) {
|
|
48
|
+
// Validate changes before sending
|
|
49
|
+
const validChanges = changes.filter(([type, path]) => {
|
|
50
|
+
const normalizedPath = path[0].toLowerCase()
|
|
51
|
+
return ALLOWED_FIELDS.includes(normalizedPath)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
const response = await fetch(`${getApiUrl()}/auth/project/update`, {
|
|
55
|
+
method: 'POST',
|
|
56
|
+
headers: {
|
|
57
|
+
'Content-Type': 'application/json',
|
|
58
|
+
Authorization: `Bearer ${authToken}`
|
|
59
|
+
},
|
|
60
|
+
body: JSON.stringify({
|
|
61
|
+
appKey,
|
|
62
|
+
changes: validChanges,
|
|
63
|
+
projectUpdates: projectData
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
if (!response.ok) {
|
|
68
|
+
const error = await response.json()
|
|
69
|
+
throw new Error(error.error || 'Failed to update project')
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return response
|
|
73
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { generateDiffDisplay } from './diffUtils.js'
|
|
2
|
+
|
|
3
|
+
export const ALLOWED_FIELDS = [
|
|
4
|
+
'designsystem',
|
|
5
|
+
'functions',
|
|
6
|
+
'files',
|
|
7
|
+
'components',
|
|
8
|
+
'dependencies',
|
|
9
|
+
'pages',
|
|
10
|
+
'snippets'
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
export function normalizeKeys (obj) {
|
|
14
|
+
if (!obj || typeof obj !== 'object') return obj
|
|
15
|
+
|
|
16
|
+
if (Array.isArray(obj)) {
|
|
17
|
+
return obj.map(item => normalizeKeys(item))
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return Object.entries(obj).reduce((acc, [key, value]) => {
|
|
21
|
+
const normalizedKey = key.toLowerCase()
|
|
22
|
+
acc[normalizedKey] = value
|
|
23
|
+
return acc
|
|
24
|
+
}, {})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function generateChanges (oldData, newData) {
|
|
28
|
+
const changes = []
|
|
29
|
+
const diffs = []
|
|
30
|
+
|
|
31
|
+
if (!oldData || !newData) {
|
|
32
|
+
throw new Error('Both oldData and newData must be provided')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Filter out non-allowed top-level fields before comparison
|
|
36
|
+
const filteredOldData = Object.keys(oldData)
|
|
37
|
+
.filter(key => ALLOWED_FIELDS.includes(key.toLowerCase()))
|
|
38
|
+
.reduce((obj, key) => {
|
|
39
|
+
obj[key] = oldData[key]
|
|
40
|
+
return obj
|
|
41
|
+
}, {})
|
|
42
|
+
|
|
43
|
+
const filteredNewData = Object.keys(newData)
|
|
44
|
+
.filter(key => ALLOWED_FIELDS.includes(key.toLowerCase()))
|
|
45
|
+
.reduce((obj, key) => {
|
|
46
|
+
obj[key] = newData[key]
|
|
47
|
+
return obj
|
|
48
|
+
}, {})
|
|
49
|
+
|
|
50
|
+
compareObjects(filteredOldData, filteredNewData, [], changes, diffs)
|
|
51
|
+
|
|
52
|
+
return { changes, diffs }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function isEqual (val1, val2) {
|
|
56
|
+
if (val1 === val2) return true
|
|
57
|
+
if (!val1 && !val2) return true
|
|
58
|
+
if (!val1 || !val2) return false
|
|
59
|
+
|
|
60
|
+
if (typeof val1 === 'function' || typeof val2 === 'function') {
|
|
61
|
+
const str1 = typeof val1 === 'function' ? val1.toString() : val1
|
|
62
|
+
const str2 = typeof val2 === 'function' ? val2.toString() : val2
|
|
63
|
+
return str1 === str2
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (Array.isArray(val1) && Array.isArray(val2)) {
|
|
67
|
+
if (val1.length !== val2.length) return false
|
|
68
|
+
return val1.every((item, index) => isEqual(item, val2[index]))
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (typeof val1 !== 'object' || val1 === null || val2 === null) {
|
|
72
|
+
if (typeof val1 === 'number' && typeof val2 === 'number') {
|
|
73
|
+
return val1.toString() === val2.toString()
|
|
74
|
+
}
|
|
75
|
+
return Object.is(val1, val2)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const keys1 = Object.keys(val1).sort()
|
|
79
|
+
const keys2 = Object.keys(val2).sort()
|
|
80
|
+
|
|
81
|
+
if (keys1.length !== keys2.length) return false
|
|
82
|
+
if (!keys1.every((key, index) => key === keys2[index])) return false
|
|
83
|
+
|
|
84
|
+
return keys1.every(key => isEqual(val1[key], val2[key]))
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function compareObjects (oldObj, newObj, currentPath, changes, diffs) {
|
|
88
|
+
oldObj = oldObj || {}
|
|
89
|
+
newObj = newObj || {}
|
|
90
|
+
|
|
91
|
+
if (isEqual(oldObj, newObj)) return
|
|
92
|
+
|
|
93
|
+
if (
|
|
94
|
+
typeof oldObj !== 'object' ||
|
|
95
|
+
typeof newObj !== 'object' ||
|
|
96
|
+
oldObj === null ||
|
|
97
|
+
newObj === null
|
|
98
|
+
) {
|
|
99
|
+
changes.push(['update', currentPath, typeof newObj === 'function' ? newObj.toString() : newObj])
|
|
100
|
+
diffs.push(generateDiffDisplay('update', currentPath, oldObj, newObj))
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
handleDeletions(oldObj, newObj, currentPath, changes, diffs)
|
|
105
|
+
handleAdditionsAndUpdates(oldObj, newObj, currentPath, changes, diffs)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function handleDeletions (oldObj, newObj, currentPath, changes, diffs) {
|
|
109
|
+
const oldKeys = Object.keys(oldObj)
|
|
110
|
+
const newKeys = Object.keys(newObj)
|
|
111
|
+
|
|
112
|
+
for (const key of oldKeys) {
|
|
113
|
+
if (!newKeys.includes(key)) {
|
|
114
|
+
changes.push(['delete', [...currentPath, key]])
|
|
115
|
+
diffs.push(generateDiffDisplay('delete', [...currentPath, key], oldObj[key]))
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function handleAdditionsAndUpdates (oldObj, newObj, currentPath, changes, diffs) {
|
|
121
|
+
const oldKeys = Object.keys(oldObj)
|
|
122
|
+
const newKeys = Object.keys(newObj)
|
|
123
|
+
|
|
124
|
+
for (const key of newKeys) {
|
|
125
|
+
const newValue = newObj[key]
|
|
126
|
+
const oldValue = oldObj[key]
|
|
127
|
+
|
|
128
|
+
if (!oldKeys.includes(key)) {
|
|
129
|
+
changes.push(['update', [...currentPath, key], typeof newValue === 'function' ? newValue.toString() : newValue])
|
|
130
|
+
diffs.push(generateDiffDisplay('add', [...currentPath, key], undefined, newValue))
|
|
131
|
+
} else if (!isEqual(oldValue, newValue)) {
|
|
132
|
+
compareObjects(oldValue, newValue, [...currentPath, key], changes, diffs)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
|
|
4
|
+
export const getApiUrl = () => {
|
|
5
|
+
if (process.env.SMBLS_API_URL) {
|
|
6
|
+
return process.env.SMBLS_API_URL
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const configPath = path.join(process.cwd(), '.smblsrc.json')
|
|
10
|
+
if (fs.existsSync(configPath)) {
|
|
11
|
+
try {
|
|
12
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'))
|
|
13
|
+
if (config.apiUrl) return config.apiUrl
|
|
14
|
+
} catch (err) {
|
|
15
|
+
// Silently fail and use defaults if config file is invalid
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
switch (process.env.NODE_ENV) {
|
|
20
|
+
case 'production':
|
|
21
|
+
return 'https://api.symbols.app'
|
|
22
|
+
case 'staging':
|
|
23
|
+
return 'https://api-staging.symbols.app'
|
|
24
|
+
default:
|
|
25
|
+
return 'http://localhost:13335'
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import chalk from 'chalk'
|
|
2
|
+
import { spawn } from 'child_process'
|
|
3
|
+
|
|
4
|
+
export function formatValue (value) {
|
|
5
|
+
if (typeof value === 'object' && value !== null) {
|
|
6
|
+
return JSON.stringify(value, null, 2)
|
|
7
|
+
}
|
|
8
|
+
return String(value)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function generateDiffDisplay (type, path, oldValue, newValue) {
|
|
12
|
+
const pathStr = path.join('.')
|
|
13
|
+
let output = chalk.dim(`@ ${pathStr}\n`)
|
|
14
|
+
|
|
15
|
+
switch (type) {
|
|
16
|
+
case 'update':
|
|
17
|
+
if (oldValue !== undefined) {
|
|
18
|
+
output += chalk.red(`- ${formatValue(oldValue)}\n`)
|
|
19
|
+
}
|
|
20
|
+
output += chalk.green(`+ ${formatValue(newValue)}\n`)
|
|
21
|
+
break
|
|
22
|
+
case 'delete':
|
|
23
|
+
output += chalk.red(`- ${formatValue(oldValue)}\n`)
|
|
24
|
+
break
|
|
25
|
+
default:
|
|
26
|
+
output += chalk.green(`+ ${formatValue(newValue)}\n`)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return output
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function showDiffPager (diffs) {
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
const formattedDiffs = diffs.join('\n' + chalk.dim('---') + '\n')
|
|
35
|
+
|
|
36
|
+
const less = spawn('less', ['-R'], {
|
|
37
|
+
stdio: ['pipe', process.stdout, process.stderr]
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
less.stdin.on('error', (error) => {
|
|
41
|
+
if (error.code === 'EPIPE') {
|
|
42
|
+
resolve()
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
reject(error)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
less.on('close', () => {
|
|
49
|
+
resolve()
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
less.stdin.write(formattedDiffs)
|
|
53
|
+
less.stdin.end()
|
|
54
|
+
})
|
|
55
|
+
}
|
|
@@ -1,43 +1,23 @@
|
|
|
1
1
|
import fs from 'fs'
|
|
2
2
|
import path from 'path'
|
|
3
3
|
import { build } from 'esbuild'
|
|
4
|
-
import { loadModule } from './require.js'
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
let rc = {}
|
|
9
|
-
try {
|
|
10
|
-
rc = loadModule(RC_PATH); // eslint-disable-line
|
|
11
|
-
} catch (e) {
|
|
12
|
-
console.error('Please include symbols.json to your root of respository')
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export async function fs2js (
|
|
16
|
-
distDir = path.join(process.cwd(), 'smbls')
|
|
17
|
-
) {
|
|
18
|
-
const directoryPath = distDir
|
|
19
|
-
const outputDirectory = distDir + '/dist'
|
|
20
|
-
buildDirectory(directoryPath, outputDirectory)
|
|
21
|
-
.then(() => {
|
|
22
|
-
console.log('All files built successfully')
|
|
23
|
-
})
|
|
24
|
-
.catch((error) => {
|
|
25
|
-
console.error('Error:', error)
|
|
26
|
-
})
|
|
27
|
-
const data = await import(process.cwd() + '/toko/dist/index.js')
|
|
28
|
-
console.log(JSON.stringify(data))
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
async function buildDirectory (directoryPath, outputDirectory) {
|
|
5
|
+
export async function buildDirectory (directoryPath, outputDirectory) {
|
|
32
6
|
try {
|
|
33
7
|
const files = await getFilesRecursively(directoryPath)
|
|
34
8
|
const buildPromises = files.map(async (filePath) => {
|
|
35
9
|
const relativePath = path.relative(directoryPath, filePath)
|
|
36
10
|
const outputFile = path.join(outputDirectory, relativePath)
|
|
11
|
+
|
|
12
|
+
// Ensure output subdirectories exist
|
|
13
|
+
const outputDir = path.dirname(outputFile)
|
|
14
|
+
if (!fs.existsSync(outputDir)) {
|
|
15
|
+
fs.mkdirSync(outputDir, { recursive: true })
|
|
16
|
+
}
|
|
17
|
+
|
|
37
18
|
await buildFromFile(filePath, outputFile)
|
|
38
19
|
})
|
|
39
20
|
await Promise.all(buildPromises)
|
|
40
|
-
console.log('All files built successfully')
|
|
41
21
|
} catch (error) {
|
|
42
22
|
console.error('Error building directory:', error)
|
|
43
23
|
throw error
|
|
@@ -46,7 +26,7 @@ async function buildDirectory (directoryPath, outputDirectory) {
|
|
|
46
26
|
|
|
47
27
|
async function buildFromFile (inputFilePath, outputFilePath) {
|
|
48
28
|
try {
|
|
49
|
-
const fileContents =
|
|
29
|
+
const fileContents = fs.readFileSync(inputFilePath, 'utf8')
|
|
50
30
|
await build({
|
|
51
31
|
stdin: {
|
|
52
32
|
contents: fileContents,
|
|
@@ -58,7 +38,10 @@ async function buildFromFile (inputFilePath, outputFilePath) {
|
|
|
58
38
|
outfile: outputFilePath,
|
|
59
39
|
target: 'node14',
|
|
60
40
|
platform: 'node',
|
|
61
|
-
format: 'cjs'
|
|
41
|
+
format: 'cjs',
|
|
42
|
+
bundle: true,
|
|
43
|
+
mainFields: ['module', 'main'],
|
|
44
|
+
external: ['esbuild']
|
|
62
45
|
})
|
|
63
46
|
} catch (error) {
|
|
64
47
|
console.error('Error building file:', error)
|
|
@@ -69,11 +52,10 @@ async function buildFromFile (inputFilePath, outputFilePath) {
|
|
|
69
52
|
async function getFilesRecursively (directoryPath) {
|
|
70
53
|
const files = []
|
|
71
54
|
async function traverseDirectory (currentPath) {
|
|
72
|
-
const entries =
|
|
55
|
+
const entries = fs.readdirSync(currentPath, { withFileTypes: true })
|
|
73
56
|
for (const entry of entries) {
|
|
74
|
-
if (entry.name
|
|
75
|
-
|
|
76
|
-
}
|
|
57
|
+
if (entry.name === 'dist' || entry.name === 'node_modules') continue
|
|
58
|
+
|
|
77
59
|
const fullPath = path.join(currentPath, entry.name)
|
|
78
60
|
if (entry.isDirectory()) {
|
|
79
61
|
await traverseDirectory(fullPath)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbo.ls/cli",
|
|
3
|
-
"version": "2.11.
|
|
3
|
+
"version": "2.11.516",
|
|
4
4
|
"description": "Fetch your Symbols configuration",
|
|
5
5
|
"main": "bin/fetch.js",
|
|
6
6
|
"author": "Symbols",
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@symbo.ls/fetch": "^2.11.511",
|
|
19
|
-
"@symbo.ls/init": "^2.11.
|
|
20
|
-
"@symbo.ls/socket": "^2.11.
|
|
19
|
+
"@symbo.ls/init": "^2.11.515",
|
|
20
|
+
"@symbo.ls/socket": "^2.11.515",
|
|
21
21
|
"chalk": "^5.0.0",
|
|
22
22
|
"commander": "latest",
|
|
23
23
|
"diff": "^5.2.0",
|
|
@@ -26,5 +26,5 @@
|
|
|
26
26
|
"node-fetch": "^3.1.0",
|
|
27
27
|
"v8-compile-cache": "^2.3.0"
|
|
28
28
|
},
|
|
29
|
-
"gitHead": "
|
|
29
|
+
"gitHead": "7f3d850309dfb9de15ef9ad33eca25e5a2c94073"
|
|
30
30
|
}
|