From 46d8ccc29bbea2d0d9326ddf97779fc3c8f2bb6f Mon Sep 17 00:00:00 2001 From: Yuxuan Zhang Date: Fri, 11 Apr 2025 05:10:23 -0400 Subject: [PATCH] dep: add package `living-object` for object serialization (support symbols) --- package.json | 1 + pnpm-lock.yaml | 8 +++++++ src/node/build/build.ts | 14 +++++------- src/node/plugin.ts | 5 ++--- src/node/utils/fnSerialize.ts | 42 ----------------------------------- 5 files changed, 16 insertions(+), 54 deletions(-) delete mode 100644 src/node/utils/fnSerialize.ts diff --git a/package.json b/package.json index b54ec654..5a81143f 100644 --- a/package.json +++ b/package.json @@ -107,6 +107,7 @@ "@vueuse/core": "^13.0.0", "@vueuse/integrations": "^13.0.0", "focus-trap": "^7.6.4", + "living-object": "0.0.3", "mark.js": "8.11.1", "minisearch": "^7.1.2", "shiki": "^3.2.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b08eb6f5..8d870ac6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -55,6 +55,9 @@ importers: focus-trap: specifier: ^7.6.4 version: 7.6.4 + living-object: + specifier: 0.0.3 + version: 0.0.3 mark.js: specifier: 8.11.1 version: 8.11.1 @@ -1923,6 +1926,9 @@ packages: resolution: {integrity: sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==} engines: {node: '>=18.0.0'} + living-object@0.0.3: + resolution: {integrity: sha512-GBTVXuwoxsU+OF7he9ZbAWh56QhHVtpD5cL+REAq7EFR8rtnkjZIztduJ0hm2rdwRa0XyaGrsNypTqTdrhep6A==} + local-pkg@1.1.1: resolution: {integrity: sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==} engines: {node: '>=14'} @@ -4486,6 +4492,8 @@ snapshots: rfdc: 1.4.1 wrap-ansi: 9.0.0 + living-object@0.0.3: {} + local-pkg@1.1.1: dependencies: mlly: 1.7.4 diff --git a/src/node/build/build.ts b/src/node/build/build.ts index a0a12610..44d3aa04 100644 --- a/src/node/build/build.ts +++ b/src/node/build/build.ts @@ -11,11 +11,11 @@ import type { BuildOptions, Rollup } from 'vite' import { resolveConfig, type SiteConfig } from '../config' import { clearCache } from '../markdownToVue' import { slash, type Awaitable, type HeadConfig } from '../shared' -import { deserializeFunctions, serializeFunctions } from '../utils/fnSerialize' import { task } from '../utils/task' import { bundle } from './bundle' import { generateSitemap } from './generateSitemap' import { renderPage } from './render' +import LivingObject from 'living-object' const require = createRequire(import.meta.url) @@ -202,15 +202,11 @@ function generateMetadataScript( // It's also embedded as a string and JSON.parsed from the client because // it's faster than embedding as JS object literal. const hashMapString = JSON.stringify(JSON.stringify(pageToHashMap)) - const siteDataString = JSON.stringify( - JSON.stringify(serializeFunctions({ ...config.site, head: [] })) - ) + const siteDataString = new LivingObject({ ...config.site, head: [] }) + .compile() + .complete((root) => `window.__VP_SITE_DATA__=${root}`) - const metadataContent = `window.__VP_HASH_MAP__=JSON.parse(${hashMapString});${ - siteDataString.includes('_vp-fn_') - ? `${deserializeFunctions};window.__VP_SITE_DATA__=deserializeFunctions(JSON.parse(${siteDataString}));` - : `window.__VP_SITE_DATA__=JSON.parse(${siteDataString});` - }` + const metadataContent = `{window.__VP_HASH_MAP__=JSON.parse(${hashMapString});${siteDataString};}` if (!config.metaChunk) { return { html: ``, inHead: false } diff --git a/src/node/plugin.ts b/src/node/plugin.ts index 748c4da4..39dfa1da 100644 --- a/src/node/plugin.ts +++ b/src/node/plugin.ts @@ -28,7 +28,7 @@ import { rewritesPlugin } from './plugins/rewritesPlugin' import { staticDataPlugin } from './plugins/staticDataPlugin' import { webFontsPlugin } from './plugins/webFontsPlugin' import { slash, type PageDataPayload } from './shared' -import { deserializeFunctions, serializeFunctions } from './utils/fnSerialize' +import { stringify as serializeSiteData } from 'living-object' declare module 'vite' { interface UserConfig { @@ -198,8 +198,7 @@ export async function createVitePressPlugin( return `export default window.__VP_SITE_DATA__` } } - data = serializeFunctions(data) - return `${deserializeFunctions};export default deserializeFunctions(JSON.parse(${JSON.stringify(JSON.stringify(data))}))` + return serializeSiteData(data, { target: 'module' }) } }, diff --git a/src/node/utils/fnSerialize.ts b/src/node/utils/fnSerialize.ts deleted file mode 100644 index 242ca90d..00000000 --- a/src/node/utils/fnSerialize.ts +++ /dev/null @@ -1,42 +0,0 @@ -export function serializeFunctions(value: any, key?: string): any { - if (Array.isArray(value)) { - return value.map((v) => serializeFunctions(v)) - } else if (typeof value === 'object' && value !== null) { - return Object.keys(value).reduce((acc, key) => { - if (key[0] === '_') return acc - acc[key] = serializeFunctions(value[key], key) - return acc - }, {} as any) - } else if (typeof value === 'function') { - let serialized = value.toString() - if ( - key && - (serialized.startsWith(key) || serialized.startsWith('async ' + key)) - ) { - serialized = serialized.replace(key, 'function') - } - return `_vp-fn_${serialized}` - } else { - return value - } -} - -/* -export function deserializeFunctions(value: any): any { - if (Array.isArray(value)) { - return value.map(deserializeFunctions) - } else if (typeof value === 'object' && value !== null) { - return Object.keys(value).reduce((acc, key) => { - acc[key] = deserializeFunctions(value[key]) - return acc - }, {} as any) - } else if (typeof value === 'string' && value.startsWith('_vp-fn_')) { - return new Function(`return ${value.slice(7)}`)() - } else { - return value - } -} -*/ - -export const deserializeFunctions = - 'function deserializeFunctions(r){return Array.isArray(r)?r.map(deserializeFunctions):typeof r=="object"&&r!==null?Object.keys(r).reduce((t,n)=>(t[n]=deserializeFunctions(r[n]),t),{}):typeof r=="string"&&r.startsWith("_vp-fn_")?new Function(`return ${r.slice(7)}`)():r}'