返回 slidev
snippet.test.ts
根目录 / packages / slidev / node / syntax / snippet.test.ts
1 import MarkdownExit from 'markdown-exit'
2 import path from 'pathe'
3 import { expect, it } from 'vitest'
4 import { monacoWriterWhitelist } from '../vite/monacoWrite'
5 import MarkdownItSnippet, { resolveSnippetImport } from './snippet'
6
7 const fixturesRoot = path.join(__dirname, '../../../../test/fixtures/')
8
9 const options = {
10 userRoot: fixturesRoot,
11 userWorkspaceRoot: fixturesRoot,
12 roots: [fixturesRoot],
13 data: {
14 watchFiles: {},
15 slides: [{
16 index: 0,
17 source: { filepath: path.join(fixturesRoot, 'test.md') },
18 }],
19 },
20 } as any
21
22 it('snippet import', async () => {
23 const md = MarkdownExit()
24 md.use(MarkdownItSnippet, options)
25
26 const result = await md.renderAsync('<<< @/snippets/snippet.ts#snippet', { id: 'slides.md__slidev_1.md' })
27
28 expect(result).toMatchInlineSnapshot(`
29 "<pre><code class="language-ts">function _foo() {
30 // ...
31 }
32 </code></pre>
33 "
34 `)
35 })
36
37 it('snippet in indented block', async () => {
38 const md = MarkdownExit()
39 md.use(MarkdownItSnippet, options)
40
41 const result = await md.renderAsync('- item\n <<< @/snippets/snippet.ts#snippet', { id: 'slides.md__slidev_1.md' })
42
43 expect(result).toMatchInlineSnapshot(`
44 "<ul>
45 <li>item<pre><code class="language-ts">function _foo() {
46 // ...
47 }
48 </code></pre>
49 </li>
50 </ul>
51 "
52 `)
53 })
54
55 it('not transform in code block', async () => {
56 const md = MarkdownExit()
57 md.use(MarkdownItSnippet, options)
58
59 const result = await md.renderAsync('```\n<<< @/snippets/snippet.ts\n```', { id: 'slides.md__slidev_1.md' })
60
61 expect(result).toMatchInlineSnapshot(`
62 "<pre><code>&lt;&lt;&lt; @/snippets/snippet.ts
63 </code></pre>
64 "
65 `)
66 })
67
68 it('resolves a snippet path that stays inside the allowed roots', () => {
69 const slide = { source: { filepath: path.join(fixturesRoot, 'test.md') } } as any
70 const result = resolveSnippetImport('<<< @/snippets/snippet.ts#snippet', fixturesRoot, slide, [fixturesRoot])
71 expect(result?.src).toBe(path.join(fixturesRoot, 'snippets/snippet.ts').replaceAll('\\', '/'))
72 })
73
74 it('throws when a snippet path escapes the allowed roots', () => {
75 const slide = { source: { filepath: path.join(fixturesRoot, 'test.md') } } as any
76 expect(() => resolveSnippetImport('<<< ../../../../../outside.ts', fixturesRoot, slide, [fixturesRoot]))
77 .toThrow(/escapes the project root/)
78 })
79
80 it('whitelists a {monaco-write} snippet under the path the writer resolves', async () => {
81 monacoWriterWhitelist.clear()
82 const md = MarkdownExit()
83 md.use(MarkdownItSnippet, options)
84
85 const result = await md.renderAsync('<<< @/snippets/snippet.ts {monaco-write}', { id: 'slides.md__slidev_1.md' })
86
87 // createMonacoWriterPlugin does path.resolve(userRoot, file), so the value it
88 // is handed has to resolve back to the file that was read.
89 expect([...monacoWriterWhitelist]).toEqual(['snippets/snippet.ts'])
90 expect(result).toContain('writable="snippets/snippet.ts"')
91 // pathe normalises separators, so this is the writer's path.resolve verbatim.
92 expect(path.resolve(fixturesRoot, [...monacoWriterWhitelist][0]))
93 .toBe(path.join(fixturesRoot, 'snippets/snippet.ts'))
94 })
95
95 lines TYPESCRIPT