mirror of https://github.com/vuejs/vitepress
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
99 lines
2.9 KiB
99 lines
2.9 KiB
import {
|
|
deserializeFunctions,
|
|
serializeFunctions
|
|
} from 'node/utils/fnSerialize'
|
|
|
|
// runs the exact code shape that plugin.ts / build.ts emit into the site-data
|
|
// module and the metadata script — the revived value must come back without
|
|
// the deserializer ever compiling a string (new Function is used here only to
|
|
// stand in for the browser executing the emitted file)
|
|
function emitAndRevive(data: any): any {
|
|
const fns: string[] = []
|
|
const serialized = serializeFunctions(data, fns)
|
|
const script = `${deserializeFunctions};return deserializeFunctions(JSON.parse(${JSON.stringify(
|
|
JSON.stringify(serialized)
|
|
)}),[${fns.join(',')}])`
|
|
return new Function(script)()
|
|
}
|
|
|
|
describe('node/utils/fnSerialize', () => {
|
|
test('emitted deserializer does not rely on unsafe-eval', () => {
|
|
expect(deserializeFunctions).not.toContain('new Function')
|
|
expect(deserializeFunctions).not.toContain('eval')
|
|
})
|
|
|
|
test('serializes functions as indexed markers', () => {
|
|
const fns: string[] = []
|
|
const serialized = serializeFunctions(
|
|
{ a: (x: number) => x, b: { c: (x: number) => x * 2 } },
|
|
fns
|
|
)
|
|
expect(serialized).toEqual({ a: '_vp-fn_0', b: { c: '_vp-fn_1' } })
|
|
expect(fns).toHaveLength(2)
|
|
})
|
|
|
|
test('revives functions nested in objects and arrays', () => {
|
|
const data = {
|
|
search: {
|
|
options: {
|
|
miniSearch: {
|
|
options: {
|
|
tokenize: (text: string) => text.split(/\s+/)
|
|
},
|
|
searchOptions: {
|
|
boostDocument: (id: string) => (id === 'index.md' ? 2 : 1)
|
|
}
|
|
}
|
|
}
|
|
},
|
|
list: [(n: number) => n + 1, 'plain', 42]
|
|
}
|
|
|
|
const revived = emitAndRevive(data)
|
|
|
|
expect(revived.search.options.miniSearch.options.tokenize('a b')).toEqual([
|
|
'a',
|
|
'b'
|
|
])
|
|
expect(
|
|
revived.search.options.miniSearch.searchOptions.boostDocument('index.md')
|
|
).toBe(2)
|
|
expect(revived.list[0](1)).toBe(2)
|
|
expect(revived.list[1]).toBe('plain')
|
|
expect(revived.list[2]).toBe(42)
|
|
})
|
|
|
|
test('revives method shorthand and async functions', () => {
|
|
const data = {
|
|
tokenize(text: string) {
|
|
return text.toUpperCase()
|
|
},
|
|
async extractField(doc: { id: string }) {
|
|
return doc.id
|
|
}
|
|
}
|
|
|
|
const revived = emitAndRevive(data)
|
|
|
|
expect(revived.tokenize('abc')).toBe('ABC')
|
|
return expect(revived.extractField({ id: 'x' })).resolves.toBe('x')
|
|
})
|
|
|
|
test('drops underscore-prefixed keys', () => {
|
|
const revived = emitAndRevive({ _render: () => '', keep: 1 })
|
|
expect(revived).toEqual({ keep: 1 })
|
|
})
|
|
|
|
test('leaves data strings resembling markers untouched', () => {
|
|
const data = {
|
|
fn: (x: number) => x,
|
|
note: '_vp-fn_alert(1)'
|
|
}
|
|
|
|
const revived = emitAndRevive(data)
|
|
|
|
expect(revived.fn(1)).toBe(1)
|
|
expect(revived.note).toBe('_vp-fn_alert(1)')
|
|
})
|
|
})
|