Skip to content

Instantly share code, notes, and snippets.

@lxsmnsyc
Created February 4, 2023 15:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lxsmnsyc/a1da7be99cfd3b191dd19e4794d9db35 to your computer and use it in GitHub Desktop.
Save lxsmnsyc/a1da7be99cfd3b191dd19e4794d9db35 to your computer and use it in GitHub Desktop.
JS serializer
function serialize(source) {
const refs = [];
const assignments = [];
function checkRef(current) {
const index = refs.indexOf(current);
if (index === -1) {
refs.push(current)
return { first: true, index: refs.length - 1};
}
return { first: false, index };
}
function getRef(index) {
return `r[${index}]`;
}
function createAssignment(line) {
assignments.push(line + ',');
}
function traverse(current) {
if (current === true) {
return '!0';
}
if (current === false) {
return '!1';
}
if (typeof current === 'number' || current instanceof RegExp) {
return `${current}`;
}
if (current === undefined) {
return 'void 0';
}
if (current === null) {
return 'null';
}
if (typeof current === 'string') {
return JSON.stringify(current);
}
const { first, index } = checkRef(current);
const ref = getRef(index);
if (!first) {
return ref;
}
if (current instanceof Date) {
createAssignment(`${ref}=new Date("${current.toISOString()}")`);
return ref;
}
if (current instanceof Set) {
createAssignment(`${ref}=new Set()`);
for (const value of current.keys()) {
createAssignment(`${ref}.add(${traverse(value)})`);
}
return ref;
}
if (current instanceof Map) {
createAssignment(`${ref}=new Map()`);
for (const [key, value] of current.entries()) {
createAssignment(`${ref}.set(${traverse(key)},${traverse(value)})`);
}
return ref;
}
if (Array.isArray(current)) {
createAssignment(`${ref}=new Array(${current.length})`);
for (let i = 0, len = current.length; i < len; i += 1) {
if (i in current) {
createAssignment(`${ref}[${i}]=${traverse(current[i])}`);
}
}
return ref;
}
if (current && typeof current === 'object') {
createAssignment(`${ref}={}`);
for (const [key, value] of Object.entries(current)) {
const check = Number(key);
// Test if key is a valid number or JS identifier
// so that we don't have to serialize the key and wrap with brackets
if (check === check || /^([$A-Z_][0-9A-Z_$]*)$/i.test(key)) {
createAssignment(`${ref}.${key}=${traverse(value)}`);
} else {
createAssignment(`${ref}[${JSON.stringify(key)}]=${traverse(value)}`);
}
}
return ref;
}
}
const result = traverse(source);
const header = assignments.join('');
return `(r=>(${header}${result}))([])`;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment