|
25 | 25 | const TokenStart = '$SJS$'; |
26 | 26 | const TokenEnd = '$SJE$'; |
27 | 27 | const VariablePrefix = '$SJV$_'; |
28 | | -const SymbolKeyRegExps: [RegExp, RegExp] = [/\[Symbol\.\w+\]/, /\[Symbol\.for\('[^']+'\)\]/]; |
| 28 | +const SymbolKeyRegExps: [RegExp, RegExp] = [/\[Symbol\.\w+\]/, /\[Symbol\.for\([\u0027\u0022].+?[\u0027\u0022]\)\]/]; |
29 | 29 | const SymbolKeyPrefixRegExp = /\[/; |
30 | 30 | const SymbolKeySuffixRegExp = /\]/; |
31 | 31 |
|
@@ -138,7 +138,10 @@ function serializeJavascriptRecursively(obj: any, options: InternalSerializeOpti |
138 | 138 | !funcStr.startsWith('async function*') && |
139 | 139 | !funcStr.replace(/\s/g, '').match(/^\(?[^)]+\)?=>/) |
140 | 140 | ) { |
141 | | - funcStr = `function ${value}`; |
| 141 | + // If it's a computed property function, for example: { [Symbol.toPrimitive]() { return 1; } } |
| 142 | + // the funcStr is like: `[Symbol.toPrimitive]() { return 1; }`, so we can safely remove it. |
| 143 | + funcStr = funcStr.replace(/^\[[^\]]+\]/, ''); |
| 144 | + funcStr = `function ${funcStr}`; |
142 | 145 | } |
143 | 146 | funcStr = funcStr.replace(/"/g, "'"); |
144 | 147 | return `${TS}(${funcStr})${TE}`; |
@@ -224,20 +227,25 @@ export function deserializeJavascript(input: string, options?: DeserializeOption |
224 | 227 | } |
225 | 228 |
|
226 | 229 | function directDeserialize(result: SerializedResult, options: InternalDeserializeOptions) { |
227 | | - const { variablePrefix: VP, closure, get = getByPath, debug } = options ?? {}; |
| 230 | + const { variablePrefix: VP, closure, get = getByPath, debug, prettyPrint = true } = options ?? {}; |
228 | 231 | const code = getDeserializeJavascriptCode(result, options); |
229 | | - const printCode = getDeserializeJavascriptCode(result, { ...options, isPrint: true }); |
230 | 232 | if (debug) { |
| 233 | + const printSourceCode = getDeserializeJavascriptCode(result, { ...options, isPrint: true }); |
| 234 | + const prettyPrintCode = `\`${printSourceCode.replace(/`/g, '\\`').replace(/\$\{/g, '\\${')}\``; |
| 235 | + const realCode = `'${printSourceCode.replace(/\n/g, '\\n').replace(/'/g, "\\'")}'`; |
| 236 | + const printCode = prettyPrint ? prettyPrintCode : realCode; |
231 | 237 | console.log( |
232 | 238 | '-------------- deserializeCode --------------\n', |
233 | 239 | `${getByPath.toString()} |
234 | | - new Function('${VP}context', '${VP}options', \`\n${printCode.replace(/`/g, '\\`').replace(/\$\{/g, '\\${')}\`)(${closure ? 'closure' : 'undefined'}, { get: getByPath });`, |
235 | | - 'closure=', |
| 240 | + new Function('${VP}context', '${VP}options', ${printCode})(${closure ? 'closure' : 'undefined'}, { get: getByPath });`, |
| 241 | + 'closure =', |
236 | 242 | closure |
237 | 243 | ); |
238 | 244 | } |
239 | 245 | try { |
240 | | - const deserializeResult = new Function(`${VP}context`, `${VP}options`, code)(closure, { get }); |
| 246 | + const deserializeResult = new Function(`${VP}context`, `${VP}options`, code)(closure, { |
| 247 | + get, |
| 248 | + }); |
241 | 249 | return deserializeResult; |
242 | 250 | } catch (error) { |
243 | 251 | console.error(error); |
@@ -576,6 +584,15 @@ export type DeserializeOptions = Pick<SerializeOptions, 'tokenStart' | 'tokenEnd |
576 | 584 | * functions which use some global variables or modules, it's a good idea to pass them here. |
577 | 585 | */ |
578 | 586 | closure?: Record<string, unknown>; |
| 587 | + /** |
| 588 | + * Whether to pretty print the deserialized object. Default is `true`. |
| 589 | + * |
| 590 | + * - `true`: Pretty print the deserialized code with indentation and new lines, which is more |
| 591 | + * readable, but may be a little different from the real execution code. |
| 592 | + * - `false` - Print the object in a single line, which is more compact and similar to the real |
| 593 | + * execution code. |
| 594 | + */ |
| 595 | + prettyPrint?: boolean; |
579 | 596 | }; |
580 | 597 |
|
581 | 598 | interface InternalDeserializeOptions extends DeserializeOptions { |
|
0 commit comments