@symbo.ls/shorthand 2.34.32
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/docs/SymbolsProps.md +728 -0
- package/docs/SymbolsPropsAbbr.md +721 -0
- package/package.json +27 -0
- package/src/decode.js +275 -0
- package/src/encode.js +260 -0
- package/src/index.js +12 -0
- package/src/registry.js +659 -0
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@symbo.ls/shorthand",
|
|
3
|
+
"description": "Shorthand syntax transpiler for Symbols properties",
|
|
4
|
+
"author": "symbo.ls",
|
|
5
|
+
"version": "2.34.32",
|
|
6
|
+
"repository": "https://github.com/symbo-ls/smbls",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"module": "src/index.js",
|
|
9
|
+
"main": "src/index.js",
|
|
10
|
+
"exports": "./src/index.js",
|
|
11
|
+
"source": "src/index.js",
|
|
12
|
+
"publishConfig": {},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"test": "node --experimental-vm-modules ../../node_modules/.bin/jest",
|
|
15
|
+
"copy:package:cjs": "cp ../../build/package-cjs.json dist/cjs/package.json",
|
|
16
|
+
"build:cjs": "npx cross-env NODE_ENV=$NODE_ENV npx esbuild ./src/*.js --target=node16 --format=cjs --outdir=dist/cjs",
|
|
17
|
+
"build": "npm run build:cjs",
|
|
18
|
+
"prepublish": "rimraf -I dist && npm run build && npm run copy:package:cjs"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"src",
|
|
22
|
+
"dist",
|
|
23
|
+
"docs"
|
|
24
|
+
],
|
|
25
|
+
"license": "ISC",
|
|
26
|
+
"gitHead": "99bd991fb87c092bcfd045ad358c15064a8b8c30"
|
|
27
|
+
}
|
package/src/decode.js
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
abbrToProp,
|
|
5
|
+
PRESERVE_VALUE_KEYS,
|
|
6
|
+
isComponentKey,
|
|
7
|
+
isSelectorKey
|
|
8
|
+
} from './registry.js'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Decode a shorthand string back into a Symbols component object.
|
|
12
|
+
*
|
|
13
|
+
* Syntax:
|
|
14
|
+
* abbr:value — key-value pair
|
|
15
|
+
* _ — decoded to spaces in values
|
|
16
|
+
* , — array values (ext:Flex,Box → extends: ['Flex', 'Box'])
|
|
17
|
+
* bare abbr — boolean true
|
|
18
|
+
* !abbr — boolean false
|
|
19
|
+
*
|
|
20
|
+
* @param {string} str — shorthand string
|
|
21
|
+
* @returns {Object} — Symbols component object
|
|
22
|
+
*/
|
|
23
|
+
export function decode(str) {
|
|
24
|
+
if (!str || typeof str !== 'string') return {}
|
|
25
|
+
|
|
26
|
+
const obj = {}
|
|
27
|
+
const tokens = tokenize(str)
|
|
28
|
+
|
|
29
|
+
for (const token of tokens) {
|
|
30
|
+
// Boolean false: !abbr
|
|
31
|
+
if (token.startsWith('!')) {
|
|
32
|
+
const abbr = token.slice(1)
|
|
33
|
+
const prop = abbrToProp[abbr] || abbr
|
|
34
|
+
obj[prop] = false
|
|
35
|
+
continue
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const colonIdx = token.indexOf(':')
|
|
39
|
+
|
|
40
|
+
// Boolean true: bare abbreviation
|
|
41
|
+
if (colonIdx === -1) {
|
|
42
|
+
const prop = abbrToProp[token] || token
|
|
43
|
+
obj[prop] = true
|
|
44
|
+
continue
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const abbr = token.slice(0, colonIdx)
|
|
48
|
+
const rawVal = token.slice(colonIdx + 1)
|
|
49
|
+
const prop = abbrToProp[abbr] || abbr
|
|
50
|
+
|
|
51
|
+
// Array value: comma-separated
|
|
52
|
+
if (rawVal.includes(',')) {
|
|
53
|
+
obj[prop] = rawVal.split(',').map(decodeValue)
|
|
54
|
+
continue
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
obj[prop] = decodeValue(rawVal)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return obj
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Tokenize a shorthand string by splitting on spaces,
|
|
65
|
+
* respecting that underscores represent spaces within values.
|
|
66
|
+
*/
|
|
67
|
+
function tokenize(str) {
|
|
68
|
+
return str.trim().split(/\s+/).filter(Boolean)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Decode a single value: underscores → spaces, parse numbers and booleans.
|
|
73
|
+
*/
|
|
74
|
+
function decodeValue(val) {
|
|
75
|
+
const str = val.replace(/_/g, ' ')
|
|
76
|
+
|
|
77
|
+
// Try parsing as number
|
|
78
|
+
if (/^-?\d+(\.\d+)?$/.test(str)) {
|
|
79
|
+
return Number(str)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return str
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Decode inline `in` string values preserving string types.
|
|
87
|
+
* Unlike decodeValue, this does NOT convert numeric strings to numbers,
|
|
88
|
+
* since CSS values like '0', '1', '.5' must stay as strings.
|
|
89
|
+
*/
|
|
90
|
+
function decodeInlineValue(val) {
|
|
91
|
+
return val.replace(/_/g, ' ')
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Decode an `in` string (from stringify) back to key-value pairs.
|
|
96
|
+
* Uses string-preserving value decode to avoid lossy type coercion.
|
|
97
|
+
*/
|
|
98
|
+
function decodeInline(str) {
|
|
99
|
+
if (!str || typeof str !== 'string') return {}
|
|
100
|
+
|
|
101
|
+
const obj = {}
|
|
102
|
+
const tokens = tokenize(str)
|
|
103
|
+
|
|
104
|
+
for (const token of tokens) {
|
|
105
|
+
if (token.startsWith('!')) {
|
|
106
|
+
const abbr = token.slice(1)
|
|
107
|
+
const prop = abbrToProp[abbr] || abbr
|
|
108
|
+
obj[prop] = false
|
|
109
|
+
continue
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const colonIdx = token.indexOf(':')
|
|
113
|
+
|
|
114
|
+
if (colonIdx === -1) {
|
|
115
|
+
const prop = abbrToProp[token] || token
|
|
116
|
+
obj[prop] = true
|
|
117
|
+
continue
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const abbr = token.slice(0, colonIdx)
|
|
121
|
+
const rawVal = token.slice(colonIdx + 1)
|
|
122
|
+
const prop = abbrToProp[abbr] || abbr
|
|
123
|
+
|
|
124
|
+
// Array value: comma-separated
|
|
125
|
+
if (rawVal.includes(',')) {
|
|
126
|
+
obj[prop] = rawVal.split(',').map(decodeInlineValue)
|
|
127
|
+
continue
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
obj[prop] = decodeInlineValue(rawVal)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return obj
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Recursively expand abbreviated property names back to full names.
|
|
138
|
+
*
|
|
139
|
+
* Inverse of shorten(). Handles:
|
|
140
|
+
* - PascalCase keys: kept, values recursed
|
|
141
|
+
* - CSS selectors / media / cases: kept, values recursed
|
|
142
|
+
* - Event abbreviations (@ck → onClick): expanded via registry
|
|
143
|
+
* - state, scope, attr, style, data, context, query, class values: preserved as-is
|
|
144
|
+
* - Everything else: key expanded, value recursed if object/array
|
|
145
|
+
*
|
|
146
|
+
* @param {Object} obj — shortened component object
|
|
147
|
+
* @returns {Object} — expanded component object with full property names
|
|
148
|
+
*/
|
|
149
|
+
export function expand(obj) {
|
|
150
|
+
if (!obj || typeof obj !== 'object') return obj
|
|
151
|
+
if (Array.isArray(obj)) {
|
|
152
|
+
return obj.map(function (item) {
|
|
153
|
+
if (item !== null && typeof item === 'object') return expand(item)
|
|
154
|
+
return item
|
|
155
|
+
})
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const result = {}
|
|
159
|
+
|
|
160
|
+
for (const key in obj) {
|
|
161
|
+
const val = obj[key]
|
|
162
|
+
|
|
163
|
+
// PascalCase = child component → keep key, recurse value
|
|
164
|
+
if (isComponentKey(key)) {
|
|
165
|
+
result[key] = expandVal(val)
|
|
166
|
+
continue
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Try to resolve abbreviation first
|
|
170
|
+
const fullKey = abbrToProp[key] || key
|
|
171
|
+
|
|
172
|
+
// Selector/media/case key that is NOT an event abbreviation → keep key, recurse
|
|
173
|
+
if (isSelectorKey(key) && fullKey === key) {
|
|
174
|
+
result[key] = expandVal(val)
|
|
175
|
+
continue
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Preserved keys: value NOT recursed
|
|
179
|
+
if (PRESERVE_VALUE_KEYS.has(fullKey) || PRESERVE_VALUE_KEYS.has(key)) {
|
|
180
|
+
result[fullKey] = val
|
|
181
|
+
continue
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
result[fullKey] = expandVal(val)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return result
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function expandVal(val) {
|
|
191
|
+
if (val === null || val === undefined) return val
|
|
192
|
+
if (typeof val === 'function') return val
|
|
193
|
+
if (Array.isArray(val)) {
|
|
194
|
+
return val.map(function (item) {
|
|
195
|
+
if (item !== null && typeof item === 'object') return expand(item)
|
|
196
|
+
return item
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
if (typeof val === 'object') return expand(val)
|
|
200
|
+
return val
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Recursively convert a stringified object (with `in` properties) back
|
|
205
|
+
* to a full component object with expanded property names.
|
|
206
|
+
*
|
|
207
|
+
* Inverse of stringify(). Decodes `in` strings into flat key-value props,
|
|
208
|
+
* expands all abbreviated keys, and recurses into children/selectors.
|
|
209
|
+
*
|
|
210
|
+
* @param {Object} obj — stringified object with `in` properties
|
|
211
|
+
* @returns {Object} — full Symbols component object
|
|
212
|
+
*/
|
|
213
|
+
export function parse(obj) {
|
|
214
|
+
if (!obj || typeof obj !== 'object') return obj
|
|
215
|
+
if (Array.isArray(obj)) {
|
|
216
|
+
return obj.map(function (item) {
|
|
217
|
+
if (item !== null && typeof item === 'object') return parse(item)
|
|
218
|
+
return item
|
|
219
|
+
})
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const result = {}
|
|
223
|
+
|
|
224
|
+
// Decode the `in` string first so its props come first in key order
|
|
225
|
+
if (typeof obj.in === 'string') {
|
|
226
|
+
const decoded = decodeInline(obj.in)
|
|
227
|
+
for (const prop in decoded) {
|
|
228
|
+
result[prop] = decoded[prop]
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
for (const key in obj) {
|
|
233
|
+
if (key === 'in') continue
|
|
234
|
+
|
|
235
|
+
const val = obj[key]
|
|
236
|
+
|
|
237
|
+
// PascalCase = child component → keep key, recurse value
|
|
238
|
+
if (isComponentKey(key)) {
|
|
239
|
+
result[key] = parseVal(val)
|
|
240
|
+
continue
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Try to resolve abbreviation
|
|
244
|
+
const fullKey = abbrToProp[key] || key
|
|
245
|
+
|
|
246
|
+
// Selector/media/case key that is NOT an event abbreviation → keep key, recurse
|
|
247
|
+
if (isSelectorKey(key) && fullKey === key) {
|
|
248
|
+
result[key] = parseVal(val)
|
|
249
|
+
continue
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Preserved keys: value NOT recursed
|
|
253
|
+
if (PRESERVE_VALUE_KEYS.has(fullKey) || PRESERVE_VALUE_KEYS.has(key)) {
|
|
254
|
+
result[fullKey] = val
|
|
255
|
+
continue
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
result[fullKey] = parseVal(val)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return result
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function parseVal(val) {
|
|
265
|
+
if (val === null || val === undefined) return val
|
|
266
|
+
if (typeof val === 'function') return val
|
|
267
|
+
if (Array.isArray(val)) {
|
|
268
|
+
return val.map(function (item) {
|
|
269
|
+
if (item !== null && typeof item === 'object') return parse(item)
|
|
270
|
+
return item
|
|
271
|
+
})
|
|
272
|
+
}
|
|
273
|
+
if (typeof val === 'object') return parse(val)
|
|
274
|
+
return val
|
|
275
|
+
}
|
package/src/encode.js
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
propToAbbr,
|
|
5
|
+
PRESERVE_VALUE_KEYS,
|
|
6
|
+
SKIP_INLINE_KEYS,
|
|
7
|
+
isComponentKey,
|
|
8
|
+
isSelectorKey
|
|
9
|
+
} from './registry.js'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Encode a Symbols component object into a shorthand string.
|
|
13
|
+
*
|
|
14
|
+
* Syntax:
|
|
15
|
+
* abbr:value — key-value pair
|
|
16
|
+
* _ — replaces spaces inside values
|
|
17
|
+
* , — array separator (extends: ['Flex', 'Box'] → ext:Flex,Box)
|
|
18
|
+
* space — token separator
|
|
19
|
+
* bare abbr — boolean true (e.g. `hid` → hidden: true)
|
|
20
|
+
* !abbr — boolean false (e.g. `!hid` → hidden: false)
|
|
21
|
+
*
|
|
22
|
+
* Functions, objects, and other non-serializable values are skipped.
|
|
23
|
+
*
|
|
24
|
+
* @param {Object} obj — Symbols component object
|
|
25
|
+
* @returns {string} — shorthand string
|
|
26
|
+
*/
|
|
27
|
+
export function encode(obj) {
|
|
28
|
+
if (!obj || typeof obj !== 'object') return ''
|
|
29
|
+
|
|
30
|
+
const tokens = []
|
|
31
|
+
|
|
32
|
+
for (const prop in obj) {
|
|
33
|
+
const val = obj[prop]
|
|
34
|
+
|
|
35
|
+
// Skip functions and nested objects (events, childProps, scope, etc.)
|
|
36
|
+
if (typeof val === 'function') continue
|
|
37
|
+
if (val !== null && typeof val === 'object' && !Array.isArray(val)) continue
|
|
38
|
+
if (val === undefined) continue
|
|
39
|
+
|
|
40
|
+
const abbr = propToAbbr[prop] || prop
|
|
41
|
+
|
|
42
|
+
if (val === true) {
|
|
43
|
+
tokens.push(abbr)
|
|
44
|
+
} else if (val === false) {
|
|
45
|
+
tokens.push('!' + abbr)
|
|
46
|
+
} else if (Array.isArray(val)) {
|
|
47
|
+
const items = val.map((item) => encodeValue(item))
|
|
48
|
+
tokens.push(abbr + ':' + items.join(','))
|
|
49
|
+
} else {
|
|
50
|
+
tokens.push(abbr + ':' + encodeValue(val))
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return tokens.join(' ')
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Encode a single value: replace spaces with underscores.
|
|
59
|
+
*/
|
|
60
|
+
function encodeValue(val) {
|
|
61
|
+
const str = String(val)
|
|
62
|
+
return str.replace(/ /g, '_')
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Recursively shorten property names in a Symbols component object.
|
|
67
|
+
*
|
|
68
|
+
* - PascalCase keys (child components) are kept as-is, values recursed
|
|
69
|
+
* - CSS selectors / media queries / cases are kept as-is, values recursed
|
|
70
|
+
* - Functions are preserved as-is (key is shortened)
|
|
71
|
+
* - state, scope, attr, style, data, context, query, class values are preserved as-is
|
|
72
|
+
* - Everything else: key shortened, value recursed if object/array
|
|
73
|
+
*
|
|
74
|
+
* @param {Object} obj — Symbols component object
|
|
75
|
+
* @returns {Object} — shortened component object
|
|
76
|
+
*/
|
|
77
|
+
export function shorten(obj) {
|
|
78
|
+
if (!obj || typeof obj !== 'object') return obj
|
|
79
|
+
if (Array.isArray(obj)) {
|
|
80
|
+
return obj.map(function (item) {
|
|
81
|
+
if (item !== null && typeof item === 'object') return shorten(item)
|
|
82
|
+
return item
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const result = {}
|
|
87
|
+
|
|
88
|
+
for (const key in obj) {
|
|
89
|
+
const val = obj[key]
|
|
90
|
+
|
|
91
|
+
// PascalCase = child component → keep key, recurse value
|
|
92
|
+
if (isComponentKey(key)) {
|
|
93
|
+
result[key] = shortenVal(val)
|
|
94
|
+
continue
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// CSS selectors, media queries, cases → keep key, recurse value
|
|
98
|
+
if (isSelectorKey(key)) {
|
|
99
|
+
result[key] = shortenVal(val)
|
|
100
|
+
continue
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Shorten the key
|
|
104
|
+
const shortKey = propToAbbr[key] || key
|
|
105
|
+
|
|
106
|
+
// Preserved keys: value NOT recursed (state data, scope fns, etc.)
|
|
107
|
+
if (PRESERVE_VALUE_KEYS.has(key) || PRESERVE_VALUE_KEYS.has(shortKey)) {
|
|
108
|
+
result[shortKey] = val
|
|
109
|
+
continue
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
result[shortKey] = shortenVal(val)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return result
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function shortenVal(val) {
|
|
119
|
+
if (val === null || val === undefined) return val
|
|
120
|
+
if (typeof val === 'function') return val
|
|
121
|
+
if (Array.isArray(val)) {
|
|
122
|
+
return val.map(function (item) {
|
|
123
|
+
if (item !== null && typeof item === 'object') return shorten(item)
|
|
124
|
+
return item
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
if (typeof val === 'object') return shorten(val)
|
|
128
|
+
return val
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Recursively convert a component object into a stringified form.
|
|
133
|
+
*
|
|
134
|
+
* Flat primitive props (strings, numbers, booleans, primitive arrays)
|
|
135
|
+
* are encoded into an `in` string using abbreviated keys.
|
|
136
|
+
* Structural props (functions, objects, arrays of objects, PascalCase children,
|
|
137
|
+
* selectors, state, scope) remain as shortened object keys.
|
|
138
|
+
*
|
|
139
|
+
* @param {Object} obj — Symbols component object
|
|
140
|
+
* @returns {Object} — stringified object with `in` property
|
|
141
|
+
*/
|
|
142
|
+
export function stringify(obj) {
|
|
143
|
+
if (!obj || typeof obj !== 'object') return obj
|
|
144
|
+
if (Array.isArray(obj)) {
|
|
145
|
+
return obj.map(function (item) {
|
|
146
|
+
if (item !== null && typeof item === 'object') return stringify(item)
|
|
147
|
+
return item
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const result = {}
|
|
152
|
+
const tokens = []
|
|
153
|
+
|
|
154
|
+
for (const key in obj) {
|
|
155
|
+
const val = obj[key]
|
|
156
|
+
|
|
157
|
+
// PascalCase = child component → keep key, recurse value
|
|
158
|
+
if (isComponentKey(key)) {
|
|
159
|
+
result[key] = stringifyVal(val)
|
|
160
|
+
continue
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// CSS selectors, media queries, cases → keep key, recurse value
|
|
164
|
+
if (isSelectorKey(key)) {
|
|
165
|
+
result[key] = stringifyVal(val)
|
|
166
|
+
continue
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const shortKey = propToAbbr[key] || key
|
|
170
|
+
|
|
171
|
+
// Preserved keys (state, scope, etc.) → keep as-is
|
|
172
|
+
if (PRESERVE_VALUE_KEYS.has(key) || PRESERVE_VALUE_KEYS.has(shortKey)) {
|
|
173
|
+
result[shortKey] = val
|
|
174
|
+
continue
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Functions → keep as shortened key
|
|
178
|
+
if (typeof val === 'function') {
|
|
179
|
+
result[shortKey] = val
|
|
180
|
+
continue
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// null / undefined → keep as shortened key
|
|
184
|
+
if (val === null || val === undefined) {
|
|
185
|
+
result[shortKey] = val
|
|
186
|
+
continue
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Skip-inline keys (text, html, content, placeholder, src, href) → keep as shortened key
|
|
190
|
+
if (SKIP_INLINE_KEYS.has(key) || SKIP_INLINE_KEYS.has(shortKey)) {
|
|
191
|
+
result[shortKey] = val
|
|
192
|
+
continue
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Arrays of objects → keep as shortened key, recurse items
|
|
196
|
+
if (Array.isArray(val)) {
|
|
197
|
+
const hasObjects = val.some(function (item) {
|
|
198
|
+
return item !== null && typeof item === 'object'
|
|
199
|
+
})
|
|
200
|
+
if (hasObjects) {
|
|
201
|
+
result[shortKey] = val.map(function (item) {
|
|
202
|
+
if (item !== null && typeof item === 'object') return stringify(item)
|
|
203
|
+
return item
|
|
204
|
+
})
|
|
205
|
+
continue
|
|
206
|
+
}
|
|
207
|
+
// Single-element arrays can't round-trip through in (decoded as scalar)
|
|
208
|
+
if (val.length <= 1) {
|
|
209
|
+
result[shortKey] = val
|
|
210
|
+
continue
|
|
211
|
+
}
|
|
212
|
+
// Primitive array → encode into `in` token
|
|
213
|
+
tokens.push(shortKey + ':' + val.map(encodeValue).join(','))
|
|
214
|
+
continue
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Objects → keep as shortened key, recurse
|
|
218
|
+
if (typeof val === 'object') {
|
|
219
|
+
result[shortKey] = stringify(val)
|
|
220
|
+
continue
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Primitives (string, number, boolean) → encode into `in` token
|
|
224
|
+
if (val === true) {
|
|
225
|
+
tokens.push(shortKey)
|
|
226
|
+
} else if (val === false) {
|
|
227
|
+
tokens.push('!' + shortKey)
|
|
228
|
+
} else if (typeof val === 'number') {
|
|
229
|
+
// Numbers must stay as object keys to preserve type
|
|
230
|
+
result[shortKey] = val
|
|
231
|
+
} else if (
|
|
232
|
+
typeof val === 'string' &&
|
|
233
|
+
(val.includes(',') || val.includes('_'))
|
|
234
|
+
) {
|
|
235
|
+
// Strings with commas or underscores can't be safely encoded
|
|
236
|
+
result[shortKey] = val
|
|
237
|
+
} else {
|
|
238
|
+
tokens.push(shortKey + ':' + encodeValue(val))
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (tokens.length) {
|
|
243
|
+
result.in = tokens.join(' ')
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return result
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function stringifyVal(val) {
|
|
250
|
+
if (val === null || val === undefined) return val
|
|
251
|
+
if (typeof val === 'function') return val
|
|
252
|
+
if (Array.isArray(val)) {
|
|
253
|
+
return val.map(function (item) {
|
|
254
|
+
if (item !== null && typeof item === 'object') return stringify(item)
|
|
255
|
+
return item
|
|
256
|
+
})
|
|
257
|
+
}
|
|
258
|
+
if (typeof val === 'object') return stringify(val)
|
|
259
|
+
return val
|
|
260
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
export { encode, shorten, stringify } from './encode.js'
|
|
4
|
+
export { decode, expand, parse } from './decode.js'
|
|
5
|
+
export {
|
|
6
|
+
propToAbbr,
|
|
7
|
+
abbrToProp,
|
|
8
|
+
PRESERVE_VALUE_KEYS,
|
|
9
|
+
SKIP_INLINE_KEYS,
|
|
10
|
+
isComponentKey,
|
|
11
|
+
isSelectorKey
|
|
12
|
+
} from './registry.js'
|