@joinezco/markdown-editor 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.vitest-attachments/191c5ac00ef68f41dab054113c3cfe7c5fa0f29c.png +0 -0
  3. package/.vitest-attachments/2d964ac96925db03e3f5b6f229ad2728d612b3fb.png +0 -0
  4. package/.vitest-attachments/4acd142a2d0a0c0540fc4d74925054d52b537980.png +0 -0
  5. package/.vitest-attachments/5cc628ad0cab3b550b2e25b88de53ab9ec8d4f11.png +0 -0
  6. package/.vitest-attachments/8f6e01234099b8ca51ff0aa029923ff59ba00b0f.png +0 -0
  7. package/.vitest-attachments/a9caa7012ce500be895d1cc484dd577dc3162ada.png +0 -0
  8. package/.vitest-attachments/c110ea1993512d3fc47f8b3768b5adaa5221c21f.png +0 -0
  9. package/.vitest-attachments/c1a5f1644502d507eba379d7a6e188e64475b059.png +0 -0
  10. package/dist/editor/extensions/codeblock.js +70 -29
  11. package/dist/editor/index.js +23 -0
  12. package/dist/editor/styles.js +8 -0
  13. package/package.json +21 -21
  14. package/public/fonts/UbuntuMonoNerdFont-Regular.ttf +0 -0
  15. package/public/snapshot.bin +0 -0
  16. package/src/lib/editor/extensions/bullet-to-task.test.ts +5 -14
  17. package/src/lib/editor/extensions/codeblock.ts +77 -32
  18. package/src/lib/editor/index.ts +23 -0
  19. package/src/lib/editor/styles.ts +8 -0
  20. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Markdown-Formatting-should-handle-code-blocks-1.png +0 -0
  21. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Selection-and-Cursor-Management-should-handle-cursor-positioning-1.png +0 -0
  22. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Code-Block-Extension-should-handle-code-blocks-without-language-specification-1.png +0 -0
  23. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Code-Block-Extension-should-handle-different-programming-languages-1.png +0 -0
  24. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Code-Block-Extension-should-render-code-blocks-with-syntax-highlighting-1.png +0 -0
  25. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Extension-Interactions-should-handle-multiple-extensions-working-together-1.png +0 -0
  26. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-File-System-Integration-should-handle-file-references-in-code-blocks-1.png +0 -0
  27. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Table-Extension-should-render-tables-correctly-1.png +0 -0
  28. package/src/test/editor.test.ts +12 -8
  29. package/src/test/extensions.test.ts +33 -44
  30. package/src/test/multiview-sync.test.ts +137 -0
  31. package/vitest.config.ts +56 -9
  32. package/TEST_README.md +0 -359
  33. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Basic-Editor-Functionality-should-be-focusable-1.png +0 -0
  34. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Basic-Editor-Functionality-should-have-initial-content-1.png +0 -0
  35. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Basic-Editor-Functionality-should-render-in-the-DOM-1.png +0 -0
  36. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Browser-specific-Features-should-handle-copy-and-paste-operations-1.png +0 -0
  37. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Browser-specific-Features-should-handle-undo-and-redo-1.png +0 -0
  38. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Editor-State-and-Updates-should-maintain-state-across-content-changes-1.png +0 -0
  39. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Editor-State-and-Updates-should-trigger-update-callbacks-1.png +0 -0
  40. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Error-Handling-should-handle-invalid-markdown-gracefully-1.png +0 -0
  41. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Error-Handling-should-handle-very-long-content-1.png +0 -0
  42. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Keyboard-Shortcuts-should-handle-Ctrl-B-for-bold-1.png +0 -0
  43. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Keyboard-Shortcuts-should-handle-Ctrl-I-for-italic-1.png +0 -0
  44. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Markdown-Content-Management-should-get-markdown-content-1.png +0 -0
  45. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Markdown-Content-Management-should-handle-empty-content-1.png +0 -0
  46. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Markdown-Content-Management-should-set-markdown-content-1.png +0 -0
  47. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Markdown-Formatting-should-handle-bold-text-1.png +0 -0
  48. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Markdown-Formatting-should-handle-headings-1.png +0 -0
  49. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Markdown-Formatting-should-handle-inline-code-1.png +0 -0
  50. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Markdown-Formatting-should-handle-italic-text-1.png +0 -0
  51. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Markdown-Formatting-should-handle-lists-1.png +0 -0
  52. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Markdown-Formatting-should-handle-task-lists-1.png +0 -0
  53. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Selection-and-Cursor-Management-should-set-and-get-selection-1.png +0 -0
  54. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Text-Input-and-Editing-should-handle-line-breaks-1.png +0 -0
  55. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Text-Input-and-Editing-should-handle-typing-at-different-positions-1.png +0 -0
  56. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Text-Input-and-Editing-should-insert-text-at-cursor-position-1.png +0 -0
  57. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Code-Block-Extension-should-handle-inline-code-1.png +0 -0
  58. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Extension-Interactions-should-maintain-editor-state-across-complex-operations-1.png +0 -0
  59. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-File-System-Integration-should-maintain-file-system-state-1.png +0 -0
  60. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Link-Extension-should-auto-detect-URLs-1.png +0 -0
  61. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Link-Extension-should-handle-email-links-1.png +0 -0
  62. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Link-Extension-should-render-links-correctly-1.png +0 -0
  63. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Markdown-Storage-should-provide-markdown-storage-interface-1.png +0 -0
  64. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Markdown-Storage-should-sync-markdown-content-with-storage-1.png +0 -0
  65. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Slash-Commands-Extension-should-handle-heading-commands-1.png +0 -0
  66. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Slash-Commands-Extension-should-trigger-slash-commands-1.png +0 -0
  67. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Table-Extension-should-handle-table-navigation-1.png +0 -0
  68. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Task-List-Extension-should-handle-nested-task-lists-1.png +0 -0
  69. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Task-List-Extension-should-toggle-task-completion-1.png +0 -0
  70. /package/{src/test/__screenshots__/editor.test.ts/MarkdownEditor-Basic-Editor-Functionality-should-create-an-editor-instance-1.png → .vitest-attachments/1ffc226577cf810f92353973baca01fa249e8090.png} +0 -0
  71. /package/{src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Task-List-Extension-should-render-task-lists-correctly-1.png → .vitest-attachments/caef333e04ddcbe3101c10bd6058ff3b8da894d1.png} +0 -0
@@ -96,25 +96,18 @@ describe('MarkdownEditor Extensions', () => {
96
96
  const table = editor.view.dom.querySelector('table')
97
97
  expect(table).toBeTruthy()
98
98
 
99
- const thead = table?.querySelector('thead')
100
- const tbody = table?.querySelector('tbody')
101
- expect(thead).toBeTruthy()
102
- expect(tbody).toBeTruthy()
103
-
104
- const headers = thead?.querySelectorAll('th')
99
+ // Tiptap renders header cells as <th> directly in <tr>, without <thead>/<tbody> wrappers
100
+ const headers = table?.querySelectorAll('th')
105
101
  expect(headers?.length).toBe(3)
106
102
  expect(headers?.[0].textContent?.trim()).toBe('Header 1')
107
103
  expect(headers?.[1].textContent?.trim()).toBe('Header 2')
108
104
  expect(headers?.[2].textContent?.trim()).toBe('Header 3')
109
105
 
110
- const rows = tbody?.querySelectorAll('tr')
111
- expect(rows?.length).toBe(2)
112
-
113
- const firstRowCells = rows?.[0].querySelectorAll('td')
114
- expect(firstRowCells?.length).toBe(3)
115
- expect(firstRowCells?.[0].textContent?.trim()).toBe('Cell 1')
116
- expect(firstRowCells?.[1].textContent?.trim()).toBe('Cell 2')
117
- expect(firstRowCells?.[2].textContent?.trim()).toBe('Cell 3')
106
+ const dataCells = table?.querySelectorAll('td')
107
+ expect(dataCells?.length).toBe(6)
108
+ expect(dataCells?.[0].textContent?.trim()).toBe('Cell 1')
109
+ expect(dataCells?.[1].textContent?.trim()).toBe('Cell 2')
110
+ expect(dataCells?.[2].textContent?.trim()).toBe('Cell 3')
118
111
  })
119
112
 
120
113
  it('should handle table navigation', () => {
@@ -181,38 +174,33 @@ describe('MarkdownEditor Extensions', () => {
181
174
  const codeBlock = '```javascript\nfunction hello() {\n console.log("Hello, World!");\n}\n```'
182
175
  editor.commands.setContent(codeBlock)
183
176
 
184
- const preElement = editor.view.dom.querySelector('pre')
185
- expect(preElement).toBeTruthy()
186
-
187
- const codeContent = preElement?.textContent || ''
188
- expect(codeContent).toContain('function hello')
189
- expect(codeContent).toContain('console.log')
190
- expect(codeContent).toContain('Hello, World!')
177
+ // Code blocks render via CodeMirror node view, not <pre>
178
+ const json = editor.getJSON()
179
+ const codeNode = json.content?.find((n: any) => n.type === 'ezcodeBlock' || n.type === 'codeBlock')
180
+ expect(codeNode).toBeTruthy()
181
+ expect(codeNode?.attrs?.language).toBe('javascript')
182
+ expect(codeNode?.content?.[0]?.text).toContain('function hello')
191
183
  })
192
184
 
193
185
  it('should handle different programming languages', () => {
194
186
  const pythonCode = '```python\ndef hello():\n print("Hello, World!")\n```'
195
187
  editor.commands.setContent(pythonCode)
196
188
 
197
- const preElement = editor.view.dom.querySelector('pre')
198
- expect(preElement).toBeTruthy()
199
-
200
- const codeContent = preElement?.textContent || ''
201
- expect(codeContent).toContain('def hello')
202
- expect(codeContent).toContain('print')
203
- expect(codeContent).toContain('Hello, World!')
189
+ const json = editor.getJSON()
190
+ const codeNode = json.content?.find((n: any) => n.type === 'ezcodeBlock' || n.type === 'codeBlock')
191
+ expect(codeNode).toBeTruthy()
192
+ expect(codeNode?.attrs?.language).toBe('python')
193
+ expect(codeNode?.content?.[0]?.text).toContain('def hello')
204
194
  })
205
195
 
206
196
  it('should handle code blocks without language specification', () => {
207
197
  const plainCode = '```\nplain text code\nno syntax highlighting\n```'
208
198
  editor.commands.setContent(plainCode)
209
199
 
210
- const preElement = editor.view.dom.querySelector('pre')
211
- expect(preElement).toBeTruthy()
212
-
213
- const codeContent = preElement?.textContent || ''
214
- expect(codeContent).toContain('plain text code')
215
- expect(codeContent).toContain('no syntax highlighting')
200
+ const json = editor.getJSON()
201
+ const codeNode = json.content?.find((n: any) => n.type === 'ezcodeBlock' || n.type === 'codeBlock')
202
+ expect(codeNode).toBeTruthy()
203
+ expect(codeNode?.content?.[0]?.text).toContain('plain text code')
216
204
  })
217
205
 
218
206
  it('should handle inline code', () => {
@@ -242,12 +230,12 @@ describe('MarkdownEditor Extensions', () => {
242
230
  const fileReference = '```src/example.js\nconsole.log("File content");\n```'
243
231
  editor.commands.setContent(fileReference)
244
232
 
245
- const preElement = editor.view.dom.querySelector('pre')
246
- expect(preElement).toBeTruthy()
247
-
248
- const codeContent = preElement?.textContent || ''
249
- expect(codeContent).toContain('console.log')
250
- expect(codeContent).toContain('File content')
233
+ // Verify via JSON — CodeMirror node view doesn't render <pre>
234
+ const json = editor.getJSON()
235
+ const codeNode = json.content?.find((n: any) => n.type === 'ezcodeBlock' || n.type === 'codeBlock')
236
+ expect(codeNode).toBeTruthy()
237
+ expect(codeNode?.content?.[0]?.text).toContain('console.log')
238
+ expect(codeNode?.content?.[0]?.text).toContain('File content')
251
239
  })
252
240
 
253
241
  it('should maintain file system state', () => {
@@ -313,10 +301,11 @@ Visit [our website](https://example.com) for more info.`
313
301
  expect(editor.view.dom.querySelector('[data-checked="false"]')).toBeTruthy()
314
302
  expect(editor.view.dom.querySelector('[data-checked="true"]')).toBeTruthy()
315
303
 
316
- // Code blocks
317
- const preElement = editor.view.dom.querySelector('pre')
318
- expect(preElement).toBeTruthy()
319
- expect(preElement?.textContent).toContain('function example')
304
+ // Code blocks (verified via JSON since CodeMirror node view replaces <pre>)
305
+ const json = editor.getJSON()
306
+ const codeNode = json.content?.find((n: any) => n.type === 'ezcodeBlock' || n.type === 'codeBlock')
307
+ expect(codeNode).toBeTruthy()
308
+ expect(codeNode?.content?.[0]?.text).toContain('function example')
320
309
 
321
310
  // Tables
322
311
  const table = editor.view.dom.querySelector('table')
@@ -0,0 +1,137 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest'
2
+ import { fileChangeBus } from '@joinezco/codeblock'
3
+ import { EditorView } from '@codemirror/view'
4
+ import { EditorState } from '@codemirror/state'
5
+
6
+ describe('FileChangeBus', () => {
7
+ let viewA: EditorView
8
+ let viewB: EditorView
9
+ let containerA: HTMLElement
10
+ let containerB: HTMLElement
11
+
12
+ beforeEach(() => {
13
+ containerA = document.createElement('div')
14
+ containerB = document.createElement('div')
15
+ document.body.appendChild(containerA)
16
+ document.body.appendChild(containerB)
17
+
18
+ viewA = new EditorView({
19
+ state: EditorState.create({ doc: 'initial' }),
20
+ parent: containerA,
21
+ })
22
+ viewB = new EditorView({
23
+ state: EditorState.create({ doc: 'initial' }),
24
+ parent: containerB,
25
+ })
26
+ })
27
+
28
+ afterEach(() => {
29
+ viewA.destroy()
30
+ viewB.destroy()
31
+ containerA.remove()
32
+ containerB.remove()
33
+ })
34
+
35
+ it('should notify other subscribers but not the source', () => {
36
+ const received: { view: string; content: string }[] = []
37
+
38
+ fileChangeBus.subscribe('test.txt', viewA, (content) => {
39
+ received.push({ view: 'A', content })
40
+ })
41
+ fileChangeBus.subscribe('test.txt', viewB, (content) => {
42
+ received.push({ view: 'B', content })
43
+ })
44
+
45
+ // Notify from view A — only B should receive
46
+ fileChangeBus.notify('test.txt', 'hello from A', viewA)
47
+
48
+ expect(received).toEqual([{ view: 'B', content: 'hello from A' }])
49
+ })
50
+
51
+ it('should not notify after unsubscribe', () => {
52
+ const received: string[] = []
53
+
54
+ const unsub = fileChangeBus.subscribe('test.txt', viewA, (content) => {
55
+ received.push(content)
56
+ })
57
+ fileChangeBus.subscribe('test.txt', viewB, () => {})
58
+
59
+ unsub()
60
+ fileChangeBus.notify('test.txt', 'hello', viewB)
61
+
62
+ expect(received).toEqual([])
63
+ })
64
+
65
+ it('should handle multiple files independently', () => {
66
+ const received: string[] = []
67
+
68
+ fileChangeBus.subscribe('a.txt', viewA, (content) => {
69
+ received.push('a:' + content)
70
+ })
71
+ fileChangeBus.subscribe('b.txt', viewA, (content) => {
72
+ received.push('b:' + content)
73
+ })
74
+
75
+ fileChangeBus.notify('a.txt', 'one', viewB)
76
+ fileChangeBus.notify('b.txt', 'two', viewB)
77
+
78
+ expect(received).toEqual(['a:one', 'b:two'])
79
+ })
80
+
81
+ it('should sync document content between views via the bus', () => {
82
+ // Simulate two views on the same file using the bus to sync
83
+
84
+ const unsubA = fileChangeBus.subscribe('shared.txt', viewA, (content) => {
85
+ if (viewA.state.doc.toString() !== content) {
86
+ viewA.dispatch({ changes: { from: 0, to: viewA.state.doc.length, insert: content } })
87
+ }
88
+ })
89
+ const unsubB = fileChangeBus.subscribe('shared.txt', viewB, (content) => {
90
+ if (viewB.state.doc.toString() !== content) {
91
+ viewB.dispatch({ changes: { from: 0, to: viewB.state.doc.length, insert: content } })
92
+ }
93
+ })
94
+
95
+ // Edit view A and "save" (notify the bus)
96
+ viewA.dispatch({ changes: { from: 0, to: viewA.state.doc.length, insert: 'updated content' } })
97
+ fileChangeBus.notify('shared.txt', 'updated content', viewA)
98
+
99
+ // View B should have received the update
100
+ expect(viewB.state.doc.toString()).toBe('updated content')
101
+ // View A should NOT have been re-dispatched (it was the source)
102
+ expect(viewA.state.doc.toString()).toBe('updated content')
103
+
104
+ unsubA()
105
+ unsubB()
106
+ })
107
+
108
+ it('should not create infinite loops when both views subscribe', () => {
109
+ let dispatchCountA = 0
110
+ let dispatchCountB = 0
111
+
112
+ fileChangeBus.subscribe('shared.txt', viewA, (content) => {
113
+ if (viewA.state.doc.toString() !== content) {
114
+ dispatchCountA++
115
+ viewA.dispatch({ changes: { from: 0, to: viewA.state.doc.length, insert: content } })
116
+ // In the real codeblockView, this dispatch would NOT trigger save because
117
+ // receivingExternalUpdate is true. So we do NOT re-notify.
118
+ }
119
+ })
120
+ fileChangeBus.subscribe('shared.txt', viewB, (content) => {
121
+ if (viewB.state.doc.toString() !== content) {
122
+ dispatchCountB++
123
+ viewB.dispatch({ changes: { from: 0, to: viewB.state.doc.length, insert: content } })
124
+ }
125
+ })
126
+
127
+ // Simulate save from A
128
+ viewA.dispatch({ changes: { from: 0, to: viewA.state.doc.length, insert: 'final' } })
129
+ fileChangeBus.notify('shared.txt', 'final', viewA)
130
+
131
+ // Only B should have dispatched once
132
+ expect(dispatchCountA).toBe(0)
133
+ expect(dispatchCountB).toBe(1)
134
+ expect(viewA.state.doc.toString()).toBe('final')
135
+ expect(viewB.state.doc.toString()).toBe('final')
136
+ })
137
+ })
package/vitest.config.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { defineConfig } from 'vitest/config'
2
+ import { playwright } from '@vitest/browser-playwright'
2
3
  import path from 'path'
3
4
 
4
5
  export default defineConfig({
@@ -6,15 +7,17 @@ export default defineConfig({
6
7
  // Enable browser testing
7
8
  browser: {
8
9
  enabled: true,
9
- name: 'chromium',
10
- provider: 'playwright',
11
- // Headless mode for CI, can be disabled for debugging
10
+ provider: playwright(),
12
11
  headless: true,
13
- // Browser viewport
14
- viewport: {
15
- width: 1280,
16
- height: 720,
17
- },
12
+ instances: [
13
+ {
14
+ browser: 'chromium',
15
+ viewport: {
16
+ width: 1280,
17
+ height: 720,
18
+ },
19
+ },
20
+ ],
18
21
  },
19
22
  // Test environment setup
20
23
  environment: 'happy-dom',
@@ -51,9 +54,53 @@ export default defineConfig({
51
54
  '@/lib': path.resolve(__dirname, './src/lib'),
52
55
  },
53
56
  },
54
- // Optimize deps for testing
57
+ // @joinezco/codeblock is a workspace link — exclude it from optimization so
58
+ // Vite serves its source directly. Its transitive deps must be listed with
59
+ // the "package > dep" syntax so Vite can resolve them through the excluded package.
55
60
  optimizeDeps: {
56
61
  exclude: ['@joinezco/codeblock'],
62
+ include: [
63
+ '@joinezco/codeblock > @codemirror/autocomplete',
64
+ '@joinezco/codeblock > @codemirror/commands',
65
+ '@joinezco/codeblock > @codemirror/lang-cpp',
66
+ '@joinezco/codeblock > @codemirror/lang-css',
67
+ '@joinezco/codeblock > @codemirror/lang-html',
68
+ '@joinezco/codeblock > @codemirror/lang-java',
69
+ '@joinezco/codeblock > @codemirror/lang-javascript',
70
+ '@joinezco/codeblock > @codemirror/lang-less',
71
+ '@joinezco/codeblock > @codemirror/lang-markdown',
72
+ '@joinezco/codeblock > @codemirror/lang-php',
73
+ '@joinezco/codeblock > @codemirror/lang-python',
74
+ '@joinezco/codeblock > @codemirror/lang-rust',
75
+ '@joinezco/codeblock > @codemirror/lang-sass',
76
+ '@joinezco/codeblock > @codemirror/lang-sql',
77
+ '@joinezco/codeblock > @codemirror/lang-xml',
78
+ '@joinezco/codeblock > @codemirror/lang-yaml',
79
+ '@joinezco/codeblock > @codemirror/language',
80
+ '@joinezco/codeblock > @codemirror/legacy-modes/mode/clike',
81
+ '@joinezco/codeblock > @codemirror/legacy-modes/mode/cmake',
82
+ '@joinezco/codeblock > @codemirror/legacy-modes/mode/dockerfile',
83
+ '@joinezco/codeblock > @codemirror/legacy-modes/mode/go',
84
+ '@joinezco/codeblock > @codemirror/legacy-modes/mode/haskell',
85
+ '@joinezco/codeblock > @codemirror/legacy-modes/mode/lua',
86
+ '@joinezco/codeblock > @codemirror/legacy-modes/mode/perl',
87
+ '@joinezco/codeblock > @codemirror/legacy-modes/mode/properties',
88
+ '@joinezco/codeblock > @codemirror/legacy-modes/mode/ruby',
89
+ '@joinezco/codeblock > @codemirror/legacy-modes/mode/shell',
90
+ '@joinezco/codeblock > @codemirror/legacy-modes/mode/swift',
91
+ '@joinezco/codeblock > @codemirror/legacy-modes/mode/toml',
92
+ '@joinezco/codeblock > @codemirror/legacy-modes/mode/vb',
93
+ '@joinezco/codeblock > @codemirror/lint',
94
+ '@joinezco/codeblock > @codemirror/search',
95
+ '@joinezco/codeblock > @lezer/highlight',
96
+ '@joinezco/codeblock > @m234/nerd-fonts/fs',
97
+ '@joinezco/codeblock > @volar/language-service',
98
+ '@joinezco/codeblock > comlink',
99
+ '@joinezco/codeblock > lodash',
100
+ '@joinezco/codeblock > minisearch',
101
+ '@joinezco/codeblock > path-browserify',
102
+ 'marked',
103
+ ],
57
104
  },
58
105
  // Server configuration for tests
59
106
  server: {