nebula-cms 0.1.0

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 (241) hide show
  1. package/.claude/settings.local.json +42 -0
  2. package/.github/workflows/ci.yml +31 -0
  3. package/.mcp.json +12 -0
  4. package/.prettierignore +5 -0
  5. package/.prettierrc.cjs +22 -0
  6. package/AGENTS.md +183 -0
  7. package/LICENSE +201 -0
  8. package/README.md +128 -0
  9. package/package.json +74 -0
  10. package/playground/.claude/settings.local.json +5 -0
  11. package/playground/astro.config.mjs +7 -0
  12. package/playground/node_modules/.bin/astro +21 -0
  13. package/playground/node_modules/.bin/rollup +21 -0
  14. package/playground/node_modules/.bin/tsc +21 -0
  15. package/playground/node_modules/.bin/tsserver +21 -0
  16. package/playground/node_modules/.bin/vite +21 -0
  17. package/playground/node_modules/.vite/_svelte_metadata.json +1 -0
  18. package/playground/node_modules/.vite/deps/@astrojs_svelte_client__js.js +80 -0
  19. package/playground/node_modules/.vite/deps/@astrojs_svelte_client__js.js.map +7 -0
  20. package/playground/node_modules/.vite/deps/_metadata.json +184 -0
  21. package/playground/node_modules/.vite/deps/astro___aria-query.js +6776 -0
  22. package/playground/node_modules/.vite/deps/astro___aria-query.js.map +7 -0
  23. package/playground/node_modules/.vite/deps/astro___axobject-query.js +3754 -0
  24. package/playground/node_modules/.vite/deps/astro___axobject-query.js.map +7 -0
  25. package/playground/node_modules/.vite/deps/astro___html-escaper.js +34 -0
  26. package/playground/node_modules/.vite/deps/astro___html-escaper.js.map +7 -0
  27. package/playground/node_modules/.vite/deps/chunk-AJXJMYAF.js +0 -0
  28. package/playground/node_modules/.vite/deps/chunk-AJXJMYAF.js.map +7 -0
  29. package/playground/node_modules/.vite/deps/chunk-ALJIOON6.js +1005 -0
  30. package/playground/node_modules/.vite/deps/chunk-ALJIOON6.js.map +7 -0
  31. package/playground/node_modules/.vite/deps/chunk-BUSYA2B4.js +8 -0
  32. package/playground/node_modules/.vite/deps/chunk-BUSYA2B4.js.map +7 -0
  33. package/playground/node_modules/.vite/deps/chunk-CNYJBM5F.js +21 -0
  34. package/playground/node_modules/.vite/deps/chunk-CNYJBM5F.js.map +7 -0
  35. package/playground/node_modules/.vite/deps/chunk-DBPNBGEI.js +223 -0
  36. package/playground/node_modules/.vite/deps/chunk-DBPNBGEI.js.map +7 -0
  37. package/playground/node_modules/.vite/deps/chunk-G3C2FXJT.js +204 -0
  38. package/playground/node_modules/.vite/deps/chunk-G3C2FXJT.js.map +7 -0
  39. package/playground/node_modules/.vite/deps/chunk-GKDKFWC5.js +27 -0
  40. package/playground/node_modules/.vite/deps/chunk-GKDKFWC5.js.map +7 -0
  41. package/playground/node_modules/.vite/deps/chunk-HNCLEOC5.js +4376 -0
  42. package/playground/node_modules/.vite/deps/chunk-HNCLEOC5.js.map +7 -0
  43. package/playground/node_modules/.vite/deps/chunk-JICYXBFU.js +688 -0
  44. package/playground/node_modules/.vite/deps/chunk-JICYXBFU.js.map +7 -0
  45. package/playground/node_modules/.vite/deps/chunk-KCUTL6DD.js +5099 -0
  46. package/playground/node_modules/.vite/deps/chunk-KCUTL6DD.js.map +7 -0
  47. package/playground/node_modules/.vite/deps/chunk-ZP4UNCSN.js +23 -0
  48. package/playground/node_modules/.vite/deps/chunk-ZP4UNCSN.js.map +7 -0
  49. package/playground/node_modules/.vite/deps/chunk-ZREFNRZZ.js +148 -0
  50. package/playground/node_modules/.vite/deps/chunk-ZREFNRZZ.js.map +7 -0
  51. package/playground/node_modules/.vite/deps/package.json +3 -0
  52. package/playground/node_modules/.vite/deps/smol-toml.js +843 -0
  53. package/playground/node_modules/.vite/deps/smol-toml.js.map +7 -0
  54. package/playground/node_modules/.vite/deps/svelte.js +55 -0
  55. package/playground/node_modules/.vite/deps/svelte.js.map +7 -0
  56. package/playground/node_modules/.vite/deps/svelte___clsx.js +9 -0
  57. package/playground/node_modules/.vite/deps/svelte___clsx.js.map +7 -0
  58. package/playground/node_modules/.vite/deps/svelte_animate.js +57 -0
  59. package/playground/node_modules/.vite/deps/svelte_animate.js.map +7 -0
  60. package/playground/node_modules/.vite/deps/svelte_attachments.js +15 -0
  61. package/playground/node_modules/.vite/deps/svelte_attachments.js.map +7 -0
  62. package/playground/node_modules/.vite/deps/svelte_easing.js +67 -0
  63. package/playground/node_modules/.vite/deps/svelte_easing.js.map +7 -0
  64. package/playground/node_modules/.vite/deps/svelte_events.js +11 -0
  65. package/playground/node_modules/.vite/deps/svelte_events.js.map +7 -0
  66. package/playground/node_modules/.vite/deps/svelte_internal.js +5 -0
  67. package/playground/node_modules/.vite/deps/svelte_internal.js.map +7 -0
  68. package/playground/node_modules/.vite/deps/svelte_internal_client.js +402 -0
  69. package/playground/node_modules/.vite/deps/svelte_internal_client.js.map +7 -0
  70. package/playground/node_modules/.vite/deps/svelte_internal_disclose-version.js +10 -0
  71. package/playground/node_modules/.vite/deps/svelte_internal_disclose-version.js.map +7 -0
  72. package/playground/node_modules/.vite/deps/svelte_internal_flags_async.js +8 -0
  73. package/playground/node_modules/.vite/deps/svelte_internal_flags_async.js.map +7 -0
  74. package/playground/node_modules/.vite/deps/svelte_internal_flags_legacy.js +8 -0
  75. package/playground/node_modules/.vite/deps/svelte_internal_flags_legacy.js.map +7 -0
  76. package/playground/node_modules/.vite/deps/svelte_internal_flags_tracing.js +8 -0
  77. package/playground/node_modules/.vite/deps/svelte_internal_flags_tracing.js.map +7 -0
  78. package/playground/node_modules/.vite/deps/svelte_legacy.js +35 -0
  79. package/playground/node_modules/.vite/deps/svelte_legacy.js.map +7 -0
  80. package/playground/node_modules/.vite/deps/svelte_motion.js +545 -0
  81. package/playground/node_modules/.vite/deps/svelte_motion.js.map +7 -0
  82. package/playground/node_modules/.vite/deps/svelte_reactivity.js +29 -0
  83. package/playground/node_modules/.vite/deps/svelte_reactivity.js.map +7 -0
  84. package/playground/node_modules/.vite/deps/svelte_reactivity_window.js +127 -0
  85. package/playground/node_modules/.vite/deps/svelte_reactivity_window.js.map +7 -0
  86. package/playground/node_modules/.vite/deps/svelte_store.js +103 -0
  87. package/playground/node_modules/.vite/deps/svelte_store.js.map +7 -0
  88. package/playground/node_modules/.vite/deps/svelte_transition.js +208 -0
  89. package/playground/node_modules/.vite/deps/svelte_transition.js.map +7 -0
  90. package/playground/package.json +16 -0
  91. package/playground/pnpm-lock.yaml +3167 -0
  92. package/playground/src/content/authors/jane-doe.json +8 -0
  93. package/playground/src/content/config/build.toml +2 -0
  94. package/playground/src/content/courses/web-fundamentals.json +29 -0
  95. package/playground/src/content/docs/advanced.mdx +6 -0
  96. package/playground/src/content/docs/intro.md +6 -0
  97. package/playground/src/content/guides/getting-started.mdx +6 -0
  98. package/playground/src/content/posts/hello-world.md +7 -0
  99. package/playground/src/content/products/t-shirt.json +16 -0
  100. package/playground/src/content/recipes/pancakes.mdoc +8 -0
  101. package/playground/src/content/settings/site.yml +2 -0
  102. package/playground/src/content.config.ts +198 -0
  103. package/playground/src/env.d.ts +1 -0
  104. package/playground/src/pages/index.astro +11 -0
  105. package/playground/src/pages/nebula.astro +14 -0
  106. package/pnpm-workspace.yaml +2 -0
  107. package/scripts/subset-icons.mjs +178 -0
  108. package/src/astro/index.ts +295 -0
  109. package/src/client/Admin.svelte +283 -0
  110. package/src/client/components/BackendPicker.svelte +291 -0
  111. package/src/client/components/DraftChip.svelte +46 -0
  112. package/src/client/components/MetadataForm.svelte +56 -0
  113. package/src/client/components/ThemeToggle.svelte +18 -0
  114. package/src/client/components/dialogs/DeleteDraftDialog.svelte +51 -0
  115. package/src/client/components/dialogs/FilenameDialog.svelte +129 -0
  116. package/src/client/components/editor/EditorPane.svelte +227 -0
  117. package/src/client/components/editor/EditorTabs.svelte +81 -0
  118. package/src/client/components/editor/EditorToolbar.svelte +131 -0
  119. package/src/client/components/editor/FormatSelector.svelte +66 -0
  120. package/src/client/components/editor/Toolbar.svelte +17 -0
  121. package/src/client/components/fields/ArrayField.svelte +339 -0
  122. package/src/client/components/fields/ArrayItem.svelte +325 -0
  123. package/src/client/components/fields/BooleanField.svelte +114 -0
  124. package/src/client/components/fields/DateField.svelte +82 -0
  125. package/src/client/components/fields/EnumField.svelte +74 -0
  126. package/src/client/components/fields/FieldWrapper.svelte +96 -0
  127. package/src/client/components/fields/NumberField.svelte +99 -0
  128. package/src/client/components/fields/ObjectField.svelte +121 -0
  129. package/src/client/components/fields/SchemaField.svelte +107 -0
  130. package/src/client/components/fields/StringField.svelte +104 -0
  131. package/src/client/components/sidebar/AdminSidebar.svelte +339 -0
  132. package/src/client/components/sidebar/AdminSidebarSort.svelte +123 -0
  133. package/src/client/css/a11y.css +14 -0
  134. package/src/client/css/btn.css +113 -0
  135. package/src/client/css/dialog.css +29 -0
  136. package/src/client/css/field-input.css +39 -0
  137. package/src/client/css/reset.css +59 -0
  138. package/src/client/css/theme.css +77 -0
  139. package/src/client/index.ts +1 -0
  140. package/src/client/js/drafts/merge.svelte.ts +121 -0
  141. package/src/client/js/drafts/ops.svelte.ts +227 -0
  142. package/src/client/js/drafts/storage.ts +108 -0
  143. package/src/client/js/drafts/workers/diff.ts +40 -0
  144. package/src/client/js/editor/editor.svelte.ts +343 -0
  145. package/src/client/js/editor/languages.ts +98 -0
  146. package/src/client/js/editor/link-wrap.ts +45 -0
  147. package/src/client/js/editor/markdown-shortcuts.ts +261 -0
  148. package/src/client/js/handlers/admin.ts +246 -0
  149. package/src/client/js/state/dialogs.svelte.ts +35 -0
  150. package/src/client/js/state/router.svelte.ts +156 -0
  151. package/src/client/js/state/schema.svelte.ts +140 -0
  152. package/src/client/js/state/state.svelte.ts +334 -0
  153. package/src/client/js/state/theme.svelte.ts +173 -0
  154. package/src/client/js/storage/adapter.ts +102 -0
  155. package/src/client/js/storage/client.ts +150 -0
  156. package/src/client/js/storage/db.ts +36 -0
  157. package/src/client/js/storage/fsa.ts +110 -0
  158. package/src/client/js/storage/github.ts +297 -0
  159. package/src/client/js/storage/storage.ts +83 -0
  160. package/src/client/js/storage/workers/frontmatter.ts +320 -0
  161. package/src/client/js/storage/workers/storage.ts +177 -0
  162. package/src/client/js/storage/workers/toml-parser.ts +106 -0
  163. package/src/client/js/storage/workers/yaml-parser.ts +132 -0
  164. package/src/client/js/utils/file-types.ts +192 -0
  165. package/src/client/js/utils/format.ts +16 -0
  166. package/src/client/js/utils/frontmatter.ts +38 -0
  167. package/src/client/js/utils/schema-utils.ts +295 -0
  168. package/src/client/js/utils/slug.ts +18 -0
  169. package/src/client/js/utils/sort.ts +84 -0
  170. package/src/client/js/utils/stable-stringify.ts +27 -0
  171. package/src/client/js/utils/url-utils.ts +38 -0
  172. package/src/types.ts +25 -0
  173. package/src/virtual.d.ts +22 -0
  174. package/svelte.config.js +4 -0
  175. package/tests/astro/build.test.ts +63 -0
  176. package/tests/astro/index.test.ts +689 -0
  177. package/tests/client/components/Admin.test.ts +446 -0
  178. package/tests/client/components/BackendPicker.test.ts +239 -0
  179. package/tests/client/components/DraftChip.test.ts +53 -0
  180. package/tests/client/components/MetadataForm.test.ts +164 -0
  181. package/tests/client/components/dialogs/DeleteDraftDialog.test.ts +91 -0
  182. package/tests/client/components/dialogs/FilenameDialog.test.ts +209 -0
  183. package/tests/client/components/dialogs/dialog-stubs.ts +19 -0
  184. package/tests/client/components/editor/EditorPane.test.ts +100 -0
  185. package/tests/client/components/editor/EditorTabs.test.ts +253 -0
  186. package/tests/client/components/editor/EditorToolbar.test.ts +252 -0
  187. package/tests/client/components/editor/fixtures.ts +31 -0
  188. package/tests/client/components/fields/ArrayField.test.ts +197 -0
  189. package/tests/client/components/fields/BooleanField.test.ts +206 -0
  190. package/tests/client/components/fields/DateField.test.ts +210 -0
  191. package/tests/client/components/fields/EnumField.test.ts +246 -0
  192. package/tests/client/components/fields/NumberField.test.ts +240 -0
  193. package/tests/client/components/fields/ObjectField.test.ts +157 -0
  194. package/tests/client/components/fields/SchemaField.test.ts +190 -0
  195. package/tests/client/components/fields/StringField.test.ts +223 -0
  196. package/tests/client/components/sidebar/AdminSidebar.test.ts +285 -0
  197. package/tests/client/components/sidebar/AdminSidebarSort.test.ts +135 -0
  198. package/tests/client/components/sidebar/sort-mock.ts +23 -0
  199. package/tests/client/js/drafts/fixtures.ts +22 -0
  200. package/tests/client/js/drafts/merge.test.ts +282 -0
  201. package/tests/client/js/drafts/ops.test.ts +658 -0
  202. package/tests/client/js/drafts/storage.test.ts +200 -0
  203. package/tests/client/js/drafts/workers/diff.test.ts +165 -0
  204. package/tests/client/js/editor/editor.test.ts +616 -0
  205. package/tests/client/js/editor/link-wrap.test.ts +225 -0
  206. package/tests/client/js/editor/markdown-shortcuts.test.ts +370 -0
  207. package/tests/client/js/handlers/admin.test.ts +467 -0
  208. package/tests/client/js/state/router.test.ts +619 -0
  209. package/tests/client/js/state/schema.test.ts +266 -0
  210. package/tests/client/js/state/state.test.ts +328 -0
  211. package/tests/client/js/storage/adapter.test.ts +115 -0
  212. package/tests/client/js/storage/client.test.ts +250 -0
  213. package/tests/client/js/storage/db.test.ts +59 -0
  214. package/tests/client/js/storage/fsa.test.ts +284 -0
  215. package/tests/client/js/storage/github.test.ts +349 -0
  216. package/tests/client/js/storage/mock-port.ts +95 -0
  217. package/tests/client/js/storage/storage.test.ts +77 -0
  218. package/tests/client/js/storage/workers/frontmatter.test.ts +479 -0
  219. package/tests/client/js/storage/workers/storage.test.ts +299 -0
  220. package/tests/client/js/storage/workers/toml-parser.test.ts +169 -0
  221. package/tests/client/js/storage/workers/yaml-parser.test.ts +168 -0
  222. package/tests/client/js/utils/file-types.test.ts +268 -0
  223. package/tests/client/js/utils/frontmatter.test.ts +87 -0
  224. package/tests/client/js/utils/schema-utils.test.ts +318 -0
  225. package/tests/client/js/utils/slug.test.ts +58 -0
  226. package/tests/client/js/utils/sort.test.ts +276 -0
  227. package/tests/client/js/utils/stable-stringify.test.ts +68 -0
  228. package/tests/client/js/utils/url-utils.test.ts +70 -0
  229. package/tests/e2e/backend-connection.test.ts +301 -0
  230. package/tests/e2e/draft-lifecycle.test.ts +388 -0
  231. package/tests/e2e/editing.test.ts +355 -0
  232. package/tests/e2e/github-adapter.test.ts +330 -0
  233. package/tests/e2e/helpers/mock-adapter.ts +166 -0
  234. package/tests/e2e/helpers/test-app.ts +155 -0
  235. package/tests/e2e/navigation.test.ts +358 -0
  236. package/tests/e2e/publishing.test.ts +345 -0
  237. package/tests/e2e/unsaved-changes.test.ts +317 -0
  238. package/tests/setup.ts +2 -0
  239. package/tests/stubs/codemirror.ts +197 -0
  240. package/tsconfig.json +19 -0
  241. package/vitest.config.ts +178 -0
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Stub module for all @codemirror/* and @lezer/* imports used in the component test environment.
3
+ * These packages are runtime-only dependencies not installed as devDependencies.
4
+ * The vitest.config.ts components project redirects every @codemirror/* and @lezer/*
5
+ * specifier here so EditorPane can be imported without resolution failures.
6
+ */
7
+
8
+ /**
9
+ * Noop class standing in for CodeMirror's EditorView.
10
+ * Static members cover all call sites in EditorPane, link-wrap, and markdown-shortcuts.
11
+ */
12
+ export class EditorView {
13
+ static theme = () => ({});
14
+ static lineWrapping = {};
15
+ static updateListener = { of: () => ({}) };
16
+ static contentAttributes = { of: () => ({}) };
17
+ // Used by markdown-shortcuts to register a paste event handler
18
+ static domEventHandlers = () => ({});
19
+ // Used by markdown-shortcuts for bracket/quote wrapping
20
+ static inputHandler = { of: () => ({}) };
21
+
22
+ /**
23
+ * Noop replacement for EditorView.destroy().
24
+ * @return {void}
25
+ */
26
+ destroy() {}
27
+
28
+ /**
29
+ * Noop replacement for EditorView.setState().
30
+ * @param {unknown} _state - Ignored
31
+ * @return {void}
32
+ */
33
+ setState(_state?: unknown) {}
34
+
35
+ /**
36
+ * Noop replacement for EditorView.dispatch().
37
+ * @param {unknown} _tr - Ignored
38
+ * @return {void}
39
+ */
40
+ dispatch(_tr?: unknown) {}
41
+
42
+ /**
43
+ * Stub getter returning a minimal EditorState-like shape.
44
+ * @return {{ selection: { main: { from: number, to: number, empty: boolean } }, changeByRange: Function, sliceDoc: Function }}
45
+ */
46
+ get state() {
47
+ return {
48
+ selection: { main: { from: 0, to: 0, empty: true } },
49
+ changeByRange: () => ({ changes: [], selection: {} }),
50
+ sliceDoc: () => '',
51
+ };
52
+ }
53
+ }
54
+
55
+ // Noop standing in for EditorState
56
+ export const EditorState = {
57
+ create: () => ({}),
58
+ };
59
+
60
+ /**
61
+ * Noop class standing in for CodeMirror's Compartment.
62
+ * Used by EditorPane to isolate the language extension for runtime reconfiguration.
63
+ */
64
+ export class Compartment {
65
+ /**
66
+ * Wraps an extension value for initial configuration.
67
+ * @param {unknown} _ext - Ignored
68
+ * @return {Record<string, never>} Empty object
69
+ */
70
+ of(_ext?: unknown) {
71
+ return {};
72
+ }
73
+
74
+ /**
75
+ * Returns a StateEffect for reconfiguring this compartment.
76
+ * @param {unknown} _ext - Ignored
77
+ * @return {Record<string, never>} Empty object standing in for a StateEffect
78
+ */
79
+ reconfigure(_ext?: unknown) {
80
+ return {};
81
+ }
82
+ }
83
+
84
+ // Minimal keymap stub
85
+ export const keymap = { of: () => ({}) };
86
+
87
+ // Command stubs
88
+ export const defaultKeymap: unknown[] = [];
89
+
90
+ /**
91
+ * Noop replacement for CodeMirror's history extension.
92
+ * @return {Record<string, never>} Empty object
93
+ */
94
+ export function history() {
95
+ return {};
96
+ }
97
+ export const historyKeymap: unknown[] = [];
98
+
99
+ /**
100
+ * Noop replacement for CodeMirror's markdown language extension.
101
+ * @return {Record<string, never>} Empty object
102
+ */
103
+ export function markdown() {
104
+ return {};
105
+ }
106
+ export const markdownLanguage = {};
107
+
108
+ // Language data stub
109
+ export const languages: unknown[] = [];
110
+
111
+ /**
112
+ * Noop replacement for CodeMirror's syntaxHighlighting extension.
113
+ * @return {Record<string, never>} Empty object
114
+ */
115
+ export function syntaxHighlighting() {
116
+ return {};
117
+ }
118
+ export const HighlightStyle = {
119
+ define: () => ({}),
120
+ };
121
+
122
+ // ViewPlugin stub (used by link-wrap and markdown-shortcuts)
123
+ // Both .define() and .fromClass() are used across the editor modules.
124
+ export const ViewPlugin = {
125
+ define: () => ({}),
126
+ fromClass: () => ({}),
127
+ };
128
+
129
+ // Decoration stub (used by link-wrap)
130
+ export const Decoration = {
131
+ mark: () => ({}),
132
+ none: {},
133
+ set: () => ({}),
134
+ };
135
+
136
+ /**
137
+ * Noop replacement for CodeMirror's RangeSetBuilder.
138
+ * Used by link-wrap to build decoration sets.
139
+ */
140
+ export class RangeSetBuilder {
141
+ /**
142
+ * Noop replacement for RangeSetBuilder.add().
143
+ * @param {unknown} _from - Ignored
144
+ * @param {unknown} _to - Ignored
145
+ * @param {unknown} _value - Ignored
146
+ * @return {void}
147
+ */
148
+ add(_from?: unknown, _to?: unknown, _value?: unknown) {}
149
+
150
+ /**
151
+ * Noop replacement for RangeSetBuilder.finish().
152
+ * @return {Record<string, never>} Empty object standing in for a DecorationSet
153
+ */
154
+ finish() {
155
+ return {};
156
+ }
157
+ }
158
+
159
+ // EditorSelection stub (used by markdown-shortcuts)
160
+ export const EditorSelection = {
161
+ cursor: () => ({}),
162
+ range: () => ({}),
163
+ };
164
+
165
+ // Tags stub — returns a proxy so any tag property access is valid
166
+ export const tags = new Proxy(
167
+ {},
168
+ {
169
+ get: () =>
170
+ new Proxy(() => ({}), {
171
+ get: () => () => ({}),
172
+ apply: () => ({}),
173
+ }),
174
+ },
175
+ );
176
+
177
+ /**
178
+ * Noop replacement for CodeMirror's syntaxTree function.
179
+ * Used by link-wrap and markdown-shortcuts for AST traversal.
180
+ * @return {{ cursor: Function }} Minimal tree stub
181
+ */
182
+ export function syntaxTree() {
183
+ return { cursor: () => ({ next: () => false }) };
184
+ }
185
+
186
+ /**
187
+ * Stub for url-utils isURL. Always returns false in the test environment.
188
+ * @return {boolean} Always false
189
+ */
190
+ export function isURL() {
191
+ return false;
192
+ }
193
+
194
+ /**
195
+ * Stub type standing in for CodeMirror's KeyBinding interface.
196
+ */
197
+ export type KeyBinding = unknown;
package/tsconfig.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "strict": true,
4
+ "target": "ESNext",
5
+ "module": "ESNext",
6
+ "moduleResolution": "bundler",
7
+ "esModuleInterop": true,
8
+ "resolveJsonModule": true,
9
+ "isolatedModules": true,
10
+ "declaration": true,
11
+ "declarationMap": true,
12
+ "sourceMap": true,
13
+ "outDir": "dist",
14
+ "rootDir": "src",
15
+ "types": ["svelte", "vitest/globals"]
16
+ },
17
+ "include": ["src/**/*"],
18
+ "exclude": ["node_modules", "dist", "playground", "tests"]
19
+ }
@@ -0,0 +1,178 @@
1
+ import { existsSync } from 'fs';
2
+ import { defineConfig } from 'vitest/config';
3
+ import { svelte } from '@sveltejs/vite-plugin-svelte';
4
+ import { playwright } from '@vitest/browser-playwright';
5
+
6
+ // icons.css is generated by scripts/subset-icons.mjs and gitignored.
7
+ // Stub it to an empty string in tests so they pass without generating the font.
8
+ const iconsPath = new URL('src/client/css/icons.css', import.meta.url).pathname;
9
+
10
+ /**
11
+ * Vite plugin that stubs the generated icons.css import when the file doesn't exist.
12
+ * This allows tests to run in CI or fresh clones without first running the subset script.
13
+ * @return {import('vite').Plugin} The Vite plugin
14
+ */
15
+ function stubIconsCSS(): import('vite').Plugin {
16
+ return {
17
+ name: 'stub-icons-css',
18
+ resolveId(id: string) {
19
+ if (id.endsWith('css/icons.css') && !existsSync(iconsPath)) {
20
+ return '\0stub-icons-css';
21
+ }
22
+ },
23
+ load(id: string) {
24
+ if (id === '\0stub-icons-css') return '';
25
+ },
26
+ };
27
+ }
28
+
29
+ export default defineConfig({
30
+ test: {
31
+ // Inline project definitions replace the separate vitest.workspace.ts file.
32
+ // This keeps the config co-located with package.json so Vite's config
33
+ // search stops here rather than walking up to the parent repo root.
34
+ projects: [
35
+ //////////////////////////////
36
+ // Unit tests — Node.js environment (pure JS/TS, no DOM)
37
+ //////////////////////////////
38
+ {
39
+ plugins: [svelte(), stubIconsCSS()],
40
+ test: {
41
+ name: 'unit',
42
+ include: [
43
+ 'tests/**/*.test.ts',
44
+ '!tests/e2e/**',
45
+ '!tests/**/*components*/**',
46
+ ],
47
+ setupFiles: ['tests/setup.ts'],
48
+ coverage: {
49
+ provider: 'v8',
50
+ reportsDirectory: '.coverage',
51
+ },
52
+ },
53
+ },
54
+
55
+ //////////////////////////////
56
+ // Component tests — jsdom environment (Svelte components via @testing-library/svelte)
57
+ //////////////////////////////
58
+ {
59
+ plugins: [svelte(), stubIconsCSS()],
60
+ // browser condition is required so Vite resolves Svelte's client-side
61
+ // entry point instead of the SSR server entry, which does not have `mount`
62
+ resolve: {
63
+ conditions: ['browser'],
64
+ // @codemirror/* and @lezer/* require a real browser DOM that jsdom cannot
65
+ // provide. Redirect them to a stub so EditorPane (and its transitive
66
+ // imports) can be loaded in the jsdom test environment.
67
+ alias: [
68
+ {
69
+ find: /^@codemirror\/.+/,
70
+ replacement: new URL('tests/stubs/codemirror.ts', import.meta.url)
71
+ .pathname,
72
+ },
73
+ {
74
+ find: /^@lezer\/.+/,
75
+ replacement: new URL('tests/stubs/codemirror.ts', import.meta.url)
76
+ .pathname,
77
+ },
78
+ ],
79
+ },
80
+ test: {
81
+ name: 'components',
82
+ environment: 'jsdom',
83
+ include: ['tests/**/components/**/*.test.ts'],
84
+ setupFiles: ['tests/setup.ts'],
85
+ },
86
+ },
87
+
88
+ //////////////////////////////
89
+ // Browser tests — Playwright via system Chrome
90
+ //////////////////////////////
91
+ {
92
+ plugins: [
93
+ svelte(),
94
+ stubIconsCSS(),
95
+ /*
96
+ * Stub plugin that resolves virtual:nebula/* for the browser test
97
+ * environment. The real virtual:nebula/* plugins are injected by the
98
+ * Astro integration, which is not running during Vitest browser tests.
99
+ * They are mocked in tests, but Vite's dependency scanner runs before
100
+ * mocks are applied and would fail without a resolver.
101
+ */
102
+ {
103
+ name: 'stub-virtual-modules',
104
+ /**
105
+ * Resolves virtual:nebula/* to internal stubs so Vite's dependency scanner doesn't fail before mocks apply.
106
+ * @param {string} id - The module specifier to resolve
107
+ * @return {string | undefined} The resolved internal ID, or undefined to skip
108
+ */
109
+ resolveId(id: string) {
110
+ if (id === 'virtual:nebula/collections')
111
+ return '\0virtual:nebula/collections';
112
+ if (id === 'virtual:nebula/config')
113
+ return '\0virtual:nebula/config';
114
+ },
115
+ /**
116
+ * Provides stub module source for virtual:nebula/* modules.
117
+ * @param {string} id - The internal module ID to load
118
+ * @return {string | undefined} The module source code, or undefined to skip
119
+ */
120
+ load(id: string) {
121
+ if (id === '\0virtual:nebula/collections')
122
+ return 'export default {};';
123
+ if (id === '\0virtual:nebula/config') {
124
+ return 'export default { basePath: "/admin", collectionsPath: "/collections" };';
125
+ }
126
+ },
127
+ },
128
+ ],
129
+ // Exclude @testing-library/svelte-core from pre-bundling so its
130
+ // .svelte.js files go through the Svelte plugin's transform pipeline
131
+ // (Vite pre-bundling skips plugin transforms, which leaves $state
132
+ // runes uncompiled and causes ReferenceError at runtime).
133
+ optimizeDeps: {
134
+ exclude: ['@testing-library/svelte-core'],
135
+ },
136
+ // browser condition ensures Vite resolves Svelte's client-side entry
137
+ // instead of the SSR server entry. @codemirror/* and @lezer/* are
138
+ // stubbed because browser tests mock the editor — the same stub used
139
+ // by component tests is reused here.
140
+ resolve: {
141
+ conditions: ['browser'],
142
+ alias: [
143
+ {
144
+ find: /^@codemirror\/.+/,
145
+ replacement: new URL('tests/stubs/codemirror.ts', import.meta.url)
146
+ .pathname,
147
+ },
148
+ {
149
+ find: /^@lezer\/.+/,
150
+ replacement: new URL('tests/stubs/codemirror.ts', import.meta.url)
151
+ .pathname,
152
+ },
153
+ ],
154
+ },
155
+ test: {
156
+ name: 'browser',
157
+ include: ['tests/e2e/**/*.test.ts'],
158
+ browser: {
159
+ enabled: true,
160
+ headless: true,
161
+ // Uses system Chrome via the 'chrome' channel to avoid downloading
162
+ // a separate Chromium binary. CI adds --no-sandbox because most CI
163
+ // containers lack the kernel namespace support sandboxing requires.
164
+ provider: playwright({
165
+ launchOptions: {
166
+ channel: 'chrome',
167
+ ...(process.env.CI
168
+ ? { args: ['--no-sandbox', '--disable-setuid-sandbox'] }
169
+ : {}),
170
+ },
171
+ }),
172
+ instances: [{ browser: 'chromium' }],
173
+ },
174
+ },
175
+ },
176
+ ],
177
+ },
178
+ });