poveste 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 (251) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +11 -0
  3. package/bin.mjs +4 -0
  4. package/client.d.ts +19 -0
  5. package/client.js +15 -0
  6. package/dist/controls.d.ts +2 -0
  7. package/dist/controls.d.ts.map +1 -0
  8. package/dist/controls.js +1 -0
  9. package/dist/node/alias.d.ts +3 -0
  10. package/dist/node/alias.d.ts.map +1 -0
  11. package/dist/node/alias.js +5 -0
  12. package/dist/node/bin.d.ts +2 -0
  13. package/dist/node/bin.d.ts.map +1 -0
  14. package/dist/node/bin.js +35 -0
  15. package/dist/node/build-serialize.d.ts +22 -0
  16. package/dist/node/build-serialize.d.ts.map +1 -0
  17. package/dist/node/build-serialize.js +36 -0
  18. package/dist/node/build.d.ts +3 -0
  19. package/dist/node/build.d.ts.map +1 -0
  20. package/dist/node/build.js +228 -0
  21. package/dist/node/builtin-plugins/pinceau-tokens.d.ts +7 -0
  22. package/dist/node/builtin-plugins/pinceau-tokens.d.ts.map +1 -0
  23. package/dist/node/builtin-plugins/pinceau-tokens.js +505 -0
  24. package/dist/node/builtin-plugins/tailwind-tokens.d.ts +6 -0
  25. package/dist/node/builtin-plugins/tailwind-tokens.d.ts.map +1 -0
  26. package/dist/node/builtin-plugins/tailwind-tokens.js +523 -0
  27. package/dist/node/builtin-plugins/vanilla-support/MountStory.d.ts +15 -0
  28. package/dist/node/builtin-plugins/vanilla-support/MountStory.d.ts.map +1 -0
  29. package/dist/node/builtin-plugins/vanilla-support/MountStory.js +39 -0
  30. package/dist/node/builtin-plugins/vanilla-support/RenderStory.d.ts +39 -0
  31. package/dist/node/builtin-plugins/vanilla-support/RenderStory.d.ts.map +1 -0
  32. package/dist/node/builtin-plugins/vanilla-support/RenderStory.js +129 -0
  33. package/dist/node/builtin-plugins/vanilla-support/client.d.ts +4 -0
  34. package/dist/node/builtin-plugins/vanilla-support/client.d.ts.map +1 -0
  35. package/dist/node/builtin-plugins/vanilla-support/client.js +5 -0
  36. package/dist/node/builtin-plugins/vanilla-support/collect.d.ts +3 -0
  37. package/dist/node/builtin-plugins/vanilla-support/collect.d.ts.map +1 -0
  38. package/dist/node/builtin-plugins/vanilla-support/collect.js +33 -0
  39. package/dist/node/builtin-plugins/vanilla-support/plugin.d.ts +3 -0
  40. package/dist/node/builtin-plugins/vanilla-support/plugin.d.ts.map +1 -0
  41. package/dist/node/builtin-plugins/vanilla-support/plugin.js +29 -0
  42. package/dist/node/builtin-plugins/vanilla-support/types.d.ts +36 -0
  43. package/dist/node/builtin-plugins/vanilla-support/types.d.ts.map +1 -0
  44. package/dist/node/builtin-plugins/vanilla-support/types.js +3 -0
  45. package/dist/node/builtin-plugins/vanilla-support/vanilla.story.d.ts +4 -0
  46. package/dist/node/builtin-plugins/vanilla-support/vanilla.story.d.ts.map +1 -0
  47. package/dist/node/builtin-plugins/vanilla-support/vanilla.story.js +55 -0
  48. package/dist/node/collect/index.d.ts +14 -0
  49. package/dist/node/collect/index.d.ts.map +1 -0
  50. package/dist/node/collect/index.js +129 -0
  51. package/dist/node/collect/run.d.ts +3 -0
  52. package/dist/node/collect/run.d.ts.map +1 -0
  53. package/dist/node/collect/run.js +7 -0
  54. package/dist/node/collect/worker.d.ts +14 -0
  55. package/dist/node/collect/worker.d.ts.map +1 -0
  56. package/dist/node/collect/worker.js +64 -0
  57. package/dist/node/colors.d.ts +287 -0
  58. package/dist/node/colors.d.ts.map +1 -0
  59. package/dist/node/colors.js +324 -0
  60. package/dist/node/commands/build.d.ts +5 -0
  61. package/dist/node/commands/build.d.ts.map +1 -0
  62. package/dist/node/commands/build.js +9 -0
  63. package/dist/node/commands/dev.d.ts +8 -0
  64. package/dist/node/commands/dev.d.ts.map +1 -0
  65. package/dist/node/commands/dev.js +52 -0
  66. package/dist/node/commands/preview.d.ts +5 -0
  67. package/dist/node/commands/preview.d.ts.map +1 -0
  68. package/dist/node/commands/preview.js +15 -0
  69. package/dist/node/config.d.ts +40 -0
  70. package/dist/node/config.d.ts.map +1 -0
  71. package/dist/node/config.js +330 -0
  72. package/dist/node/context.d.ts +18 -0
  73. package/dist/node/context.d.ts.map +1 -0
  74. package/dist/node/context.js +28 -0
  75. package/dist/node/dom/dom-keys.d.ts +5 -0
  76. package/dist/node/dom/dom-keys.d.ts.map +1 -0
  77. package/dist/node/dom/dom-keys.js +233 -0
  78. package/dist/node/dom/env.d.ts +5 -0
  79. package/dist/node/dom/env.d.ts.map +1 -0
  80. package/dist/node/dom/env.js +47 -0
  81. package/dist/node/dom/util.d.ts +11 -0
  82. package/dist/node/dom/util.d.ts.map +1 -0
  83. package/dist/node/dom/util.js +65 -0
  84. package/dist/node/index.d.ts +12 -0
  85. package/dist/node/index.d.ts.map +1 -0
  86. package/dist/node/index.js +4 -0
  87. package/dist/node/load.d.ts +9 -0
  88. package/dist/node/load.d.ts.map +1 -0
  89. package/dist/node/load.js +42 -0
  90. package/dist/node/markdown.d.ts +11 -0
  91. package/dist/node/markdown.d.ts.map +1 -0
  92. package/dist/node/markdown.js +228 -0
  93. package/dist/node/plugin.d.ts +42 -0
  94. package/dist/node/plugin.d.ts.map +1 -0
  95. package/dist/node/plugin.js +67 -0
  96. package/dist/node/preview.d.ts +8 -0
  97. package/dist/node/preview.d.ts.map +1 -0
  98. package/dist/node/preview.js +41 -0
  99. package/dist/node/search.d.ts +17 -0
  100. package/dist/node/search.d.ts.map +1 -0
  101. package/dist/node/search.js +72 -0
  102. package/dist/node/server.d.ts +12 -0
  103. package/dist/node/server.d.ts.map +1 -0
  104. package/dist/node/server.js +189 -0
  105. package/dist/node/stories.d.ts +22 -0
  106. package/dist/node/stories.d.ts.map +1 -0
  107. package/dist/node/stories.js +125 -0
  108. package/dist/node/tree.d.ts +4 -0
  109. package/dist/node/tree.d.ts.map +1 -0
  110. package/dist/node/tree.js +117 -0
  111. package/dist/node/util/find-up.d.ts +2 -0
  112. package/dist/node/util/find-up.d.ts.map +1 -0
  113. package/dist/node/util/find-up.js +20 -0
  114. package/dist/node/util/fs.d.ts +2 -0
  115. package/dist/node/util/fs.d.ts.map +1 -0
  116. package/dist/node/util/fs.js +1 -0
  117. package/dist/node/util/log.d.ts +2 -0
  118. package/dist/node/util/log.d.ts.map +1 -0
  119. package/dist/node/util/log.js +9 -0
  120. package/dist/node/util/slugify.d.ts +2 -0
  121. package/dist/node/util/slugify.d.ts.map +1 -0
  122. package/dist/node/util/slugify.js +22 -0
  123. package/dist/node/util/vendors.d.ts +3 -0
  124. package/dist/node/util/vendors.d.ts.map +1 -0
  125. package/dist/node/util/vendors.js +22 -0
  126. package/dist/node/vendors/controls.d.ts +2 -0
  127. package/dist/node/vendors/controls.d.ts.map +1 -0
  128. package/dist/node/vendors/controls.js +1 -0
  129. package/dist/node/vendors/vue.d.ts +2 -0
  130. package/dist/node/vendors/vue.d.ts.map +1 -0
  131. package/dist/node/vendors/vue.js +1 -0
  132. package/dist/node/virtual/index.d.ts +38 -0
  133. package/dist/node/virtual/index.d.ts.map +1 -0
  134. package/dist/node/virtual/index.js +49 -0
  135. package/dist/node/virtual/markdown.d.ts +3 -0
  136. package/dist/node/virtual/markdown.d.ts.map +1 -0
  137. package/dist/node/virtual/markdown.js +19 -0
  138. package/dist/node/virtual/noop.d.ts +2 -0
  139. package/dist/node/virtual/noop.d.ts.map +1 -0
  140. package/dist/node/virtual/noop.js +1 -0
  141. package/dist/node/virtual/resolved-commands.d.ts +3 -0
  142. package/dist/node/virtual/resolved-commands.d.ts.map +1 -0
  143. package/dist/node/virtual/resolved-commands.js +32 -0
  144. package/dist/node/virtual/resolved-config.d.ts +3 -0
  145. package/dist/node/virtual/resolved-config.d.ts.map +1 -0
  146. package/dist/node/virtual/resolved-config.js +10 -0
  147. package/dist/node/virtual/resolved-generated-global-setup.d.ts +3 -0
  148. package/dist/node/virtual/resolved-generated-global-setup.d.ts.map +1 -0
  149. package/dist/node/virtual/resolved-generated-global-setup.js +23 -0
  150. package/dist/node/virtual/resolved-generated-setup-code.d.ts +3 -0
  151. package/dist/node/virtual/resolved-generated-setup-code.d.ts.map +1 -0
  152. package/dist/node/virtual/resolved-generated-setup-code.js +5 -0
  153. package/dist/node/virtual/resolved-markdown-files.d.ts +3 -0
  154. package/dist/node/virtual/resolved-markdown-files.d.ts.map +1 -0
  155. package/dist/node/virtual/resolved-markdown-files.js +17 -0
  156. package/dist/node/virtual/resolved-stories.d.ts +3 -0
  157. package/dist/node/virtual/resolved-stories.d.ts.map +1 -0
  158. package/dist/node/virtual/resolved-stories.js +42 -0
  159. package/dist/node/virtual/resolved-support-plugins-client.d.ts +3 -0
  160. package/dist/node/virtual/resolved-support-plugins-client.d.ts.map +1 -0
  161. package/dist/node/virtual/resolved-support-plugins-client.js +11 -0
  162. package/dist/node/virtual/resolved-support-plugins-collect.d.ts +3 -0
  163. package/dist/node/virtual/resolved-support-plugins-collect.d.ts.map +1 -0
  164. package/dist/node/virtual/resolved-support-plugins-collect.js +11 -0
  165. package/dist/node/virtual/resolved-theme.d.ts +3 -0
  166. package/dist/node/virtual/resolved-theme.d.ts.map +1 -0
  167. package/dist/node/virtual/resolved-theme.js +12 -0
  168. package/dist/node/virtual/story-source.d.ts +3 -0
  169. package/dist/node/virtual/story-source.d.ts.map +1 -0
  170. package/dist/node/virtual/story-source.js +16 -0
  171. package/dist/node/virtual/story.d.ts +3 -0
  172. package/dist/node/virtual/story.d.ts.map +1 -0
  173. package/dist/node/virtual/story.js +7 -0
  174. package/dist/node/virtual/util.d.ts +3 -0
  175. package/dist/node/virtual/util.d.ts.map +1 -0
  176. package/dist/node/virtual/util.js +4 -0
  177. package/dist/node/virtual/vite-plugin.d.ts +4 -0
  178. package/dist/node/virtual/vite-plugin.d.ts.map +1 -0
  179. package/dist/node/virtual/vite-plugin.js +126 -0
  180. package/dist/node/vite.d.ts +9 -0
  181. package/dist/node/vite.d.ts.map +1 -0
  182. package/dist/node/vite.js +337 -0
  183. package/index.d.ts +1 -0
  184. package/package.json +89 -0
  185. package/plugin.d.ts +3 -0
  186. package/plugin.js +11 -0
  187. package/src/controls.ts +1 -0
  188. package/src/node/__tests__/markdown/test1.story.md +7 -0
  189. package/src/node/__tests__/markdown/test2.story.md +5 -0
  190. package/src/node/__tests__/markdown.spec.ts +61 -0
  191. package/src/node/__tests__/tree.spec.ts +109 -0
  192. package/src/node/alias.ts +8 -0
  193. package/src/node/bin.ts +43 -0
  194. package/src/node/build-serialize.ts +62 -0
  195. package/src/node/build.ts +283 -0
  196. package/src/node/builtin-plugins/pinceau-tokens.ts +521 -0
  197. package/src/node/builtin-plugins/tailwind-tokens.ts +536 -0
  198. package/src/node/builtin-plugins/vanilla-support/MountStory.ts +49 -0
  199. package/src/node/builtin-plugins/vanilla-support/RenderStory.ts +164 -0
  200. package/src/node/builtin-plugins/vanilla-support/client.ts +6 -0
  201. package/src/node/builtin-plugins/vanilla-support/collect.ts +43 -0
  202. package/src/node/builtin-plugins/vanilla-support/plugin.ts +35 -0
  203. package/src/node/builtin-plugins/vanilla-support/types.ts +40 -0
  204. package/src/node/builtin-plugins/vanilla-support/vanilla.story.ts +63 -0
  205. package/src/node/collect/index.ts +160 -0
  206. package/src/node/collect/run.ts +9 -0
  207. package/src/node/collect/worker.ts +97 -0
  208. package/src/node/colors.ts +340 -0
  209. package/src/node/commands/build.ts +14 -0
  210. package/src/node/commands/dev.ts +67 -0
  211. package/src/node/commands/preview.ts +22 -0
  212. package/src/node/config.ts +385 -0
  213. package/src/node/context.ts +60 -0
  214. package/src/node/dom/dom-keys.ts +238 -0
  215. package/src/node/dom/env.ts +60 -0
  216. package/src/node/dom/util.ts +83 -0
  217. package/src/node/index.ts +12 -0
  218. package/src/node/load.ts +59 -0
  219. package/src/node/markdown.ts +267 -0
  220. package/src/node/plugin.ts +95 -0
  221. package/src/node/preview.ts +57 -0
  222. package/src/node/search.ts +84 -0
  223. package/src/node/server.ts +235 -0
  224. package/src/node/stories.ts +154 -0
  225. package/src/node/tree.ts +158 -0
  226. package/src/node/util/find-up.ts +24 -0
  227. package/src/node/util/fs.ts +1 -0
  228. package/src/node/util/log.ts +10 -0
  229. package/src/node/util/slugify.ts +26 -0
  230. package/src/node/util/vendors.ts +27 -0
  231. package/src/node/vendors/controls.ts +1 -0
  232. package/src/node/vendors/vue.ts +1 -0
  233. package/src/node/virtual/index.ts +50 -0
  234. package/src/node/virtual/markdown.ts +21 -0
  235. package/src/node/virtual/noop.ts +1 -0
  236. package/src/node/virtual/resolved-commands.ts +42 -0
  237. package/src/node/virtual/resolved-config.ts +12 -0
  238. package/src/node/virtual/resolved-generated-global-setup.ts +25 -0
  239. package/src/node/virtual/resolved-generated-setup-code.ts +7 -0
  240. package/src/node/virtual/resolved-markdown-files.ts +19 -0
  241. package/src/node/virtual/resolved-stories.ts +44 -0
  242. package/src/node/virtual/resolved-support-plugins-client.ts +14 -0
  243. package/src/node/virtual/resolved-support-plugins-collect.ts +14 -0
  244. package/src/node/virtual/resolved-theme.ts +14 -0
  245. package/src/node/virtual/story-source.ts +18 -0
  246. package/src/node/virtual/story.ts +9 -0
  247. package/src/node/virtual/util.ts +5 -0
  248. package/src/node/virtual/vite-plugin.ts +146 -0
  249. package/src/node/vite.ts +379 -0
  250. package/tsconfig.json +44 -0
  251. package/vitest.config.ts +10 -0
@@ -0,0 +1,60 @@
1
+ import {
2
+ JSDOM,
3
+ VirtualConsole,
4
+ } from 'jsdom'
5
+ import { populateGlobal } from './util.js'
6
+
7
+ export function createDomEnv() {
8
+ const dom = new JSDOM(
9
+ '<!DOCTYPE html>',
10
+ {
11
+ pretendToBeVisual: true,
12
+ runScripts: 'dangerously',
13
+ url: 'http://localhost:3000',
14
+ virtualConsole: console && globalThis.console ? new VirtualConsole().forwardTo(globalThis.console) : undefined,
15
+ includeNodeLocations: false,
16
+ contentType: 'text/html',
17
+ },
18
+ )
19
+
20
+ const { keys, originals } = populateGlobal(globalThis, dom.window, { bindFunctions: true })
21
+
22
+ function destroy() {
23
+ keys.forEach(key => delete globalThis[key])
24
+ originals.forEach((v, k) => {
25
+ globalThis[k] = v
26
+ })
27
+ }
28
+
29
+ window.ResizeObserver = window.ResizeObserver || class ResizeObserver {
30
+ disconnect(): void { /* noop */ }
31
+ observe(_target: Element, _options?: ResizeObserverOptions): void { /* noop */ }
32
+ unobserve(_target: Element): void { /* noop */ }
33
+ }
34
+
35
+ window.IntersectionObserver = window.IntersectionObserver || class IntersectionObserver {
36
+ root: Element
37
+ rootMargin: string
38
+ thresholds: number[]
39
+ disconnect(): void { /* noop */ }
40
+ observe = (_target: Element) => void { /* noop */ }
41
+ unobserve(_target: Element): void { /* noop */ }
42
+ takeRecords(): IntersectionObserverEntry[] { return [] }
43
+ }
44
+
45
+ window.matchMedia = window.matchMedia || ((query: string) => ({
46
+ matches: false,
47
+ media: query,
48
+ onchange: null,
49
+ addListener: () => {},
50
+ removeListener: () => {},
51
+ addEventListener: () => {},
52
+ removeEventListener: () => {},
53
+ dispatchEvent: () => false,
54
+ }))
55
+
56
+ return {
57
+ window,
58
+ destroy,
59
+ }
60
+ }
@@ -0,0 +1,83 @@
1
+ /*
2
+ * Code from vitest https://github.com/vitest-dev/vitest/blob/98974ba4a5b95a57a98bc2077b74bc5609d6bb3a/packages/vitest/src/integrations/env/utils.ts#L1 by @antfu and @patak
3
+ */
4
+
5
+ import { KEYS } from './dom-keys.js'
6
+
7
+ const skipKeys = [
8
+ 'window',
9
+ 'self',
10
+ 'top',
11
+ 'parent',
12
+ ]
13
+
14
+ export function getWindowKeys(global: any, win: any) {
15
+ const keys = new Set(KEYS.concat(Object.getOwnPropertyNames(win))
16
+ .filter((k) => {
17
+ if (skipKeys.includes(k)) {
18
+ return false
19
+ }
20
+ if (k in global) {
21
+ return KEYS.includes(k)
22
+ }
23
+ return true
24
+ }))
25
+
26
+ return keys
27
+ }
28
+
29
+ function isClassLikeName(name: string) {
30
+ return name[0] === name[0].toUpperCase()
31
+ }
32
+
33
+ interface PopulateOptions {
34
+ // we bind functions such as addEventListener and others
35
+ // because they rely on `this` in happy-dom, and in jsdom it
36
+ // has a priority for getting implementation from symbols
37
+ // (global doesn't have these symbols, but window - does)
38
+ bindFunctions?: boolean
39
+ }
40
+
41
+ export function populateGlobal(global: any, win: any, options: PopulateOptions = {}) {
42
+ const { bindFunctions = false } = options
43
+ const keys = getWindowKeys(global, win)
44
+
45
+ const originals = new Map<string | symbol, any>()
46
+
47
+ const overrideObject = new Map<string | symbol, any>()
48
+ for (const key of keys) {
49
+ const boundFunction = bindFunctions
50
+ && typeof win[key] === 'function'
51
+ && !isClassLikeName(key)
52
+ && win[key].bind(win)
53
+
54
+ if (KEYS.includes(key) && key in global) originals.set(key, global[key])
55
+
56
+ Object.defineProperty(global, key, {
57
+ get() {
58
+ if (overrideObject.has(key)) return overrideObject.get(key)
59
+ if (boundFunction) return boundFunction
60
+ return win[key]
61
+ },
62
+ set(v) {
63
+ overrideObject.set(key, v)
64
+ },
65
+ configurable: true,
66
+ })
67
+ }
68
+
69
+ global.window = global
70
+ global.self = global
71
+ global.top = global
72
+ global.parent = global
73
+
74
+ if (global.global) global.global = global
75
+
76
+ skipKeys.forEach(k => keys.add(k))
77
+
78
+ return {
79
+ keys,
80
+ skipKeys,
81
+ originals,
82
+ }
83
+ }
@@ -0,0 +1,12 @@
1
+ export { defaultColors } from './colors.js'
2
+ export * from './config.js'
3
+ export * from './plugin.js'
4
+ export * from '@poveste/shared'
5
+
6
+ declare module 'rollup' {
7
+ interface PluginContextMeta {
8
+ histoire: {
9
+ isCollecting: boolean
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,59 @@
1
+ import type { ModuleLoader } from '@poveste/shared'
2
+ import type { ViteDevServer } from 'vite'
3
+ import { resolve } from 'pathe'
4
+ import pc from 'picocolors'
5
+ import { ViteNodeRunner } from 'vite-node/client'
6
+ import { ViteNodeServer } from 'vite-node/server'
7
+
8
+ export interface UseModuleLoaderOptions {
9
+ server: ViteDevServer
10
+ throws?: boolean
11
+ }
12
+
13
+ let _load: ModuleLoader['loadModule']
14
+
15
+ export function useModuleLoader(options: UseModuleLoaderOptions): ModuleLoader {
16
+ const { server } = options
17
+
18
+ const node = new ViteNodeServer(server as any)
19
+
20
+ const runner = new ViteNodeRunner({
21
+ root: server.config.root,
22
+ base: server.config.base,
23
+ fetchModule: async id => node.fetchModule(id),
24
+ resolveId: (id, importer) => node.resolveId(id, importer),
25
+ })
26
+
27
+ function clearCache() {
28
+ server.moduleGraph.invalidateAll()
29
+ node.fetchCache.clear()
30
+ runner.moduleCache.clear()
31
+ }
32
+
33
+ async function loadModule(file: string) {
34
+ try {
35
+ const result = await runner.executeFile(resolve(file))
36
+ return result
37
+ }
38
+ catch (e) {
39
+ console.error(pc.red(`Error while loading module ${file}:\n${e.frame ? `${pc.bold(e.message)}\n${e.frame}` : e.stack}`))
40
+ if (options.throws) {
41
+ throw e
42
+ }
43
+ }
44
+ }
45
+
46
+ _load = loadModule
47
+
48
+ async function destroy() {
49
+ // Noop
50
+ }
51
+
52
+ return {
53
+ clearCache,
54
+ loadModule,
55
+ destroy,
56
+ }
57
+ }
58
+
59
+ export const loadModule: ModuleLoader['loadModule'] = (...args) => _load?.(...args)
@@ -0,0 +1,267 @@
1
+ import type { ServerMarkdownFile } from '@poveste/shared'
2
+ import type { Plugin as VitePlugin } from 'vite'
3
+ import type { Context } from './context.js'
4
+ import { kebabCase } from 'change-case'
5
+ import chokidar from 'chokidar'
6
+ import fs from 'fs-extra'
7
+ import matter from 'gray-matter'
8
+ import MarkdownIt from 'markdown-it'
9
+ import anchor from 'markdown-it-anchor'
10
+ import attrs from 'markdown-it-attrs'
11
+ import { full as emoji } from 'markdown-it-emoji'
12
+ import micromatch from 'micromatch'
13
+ import path from 'pathe'
14
+ import pc from 'picocolors'
15
+ import { bundledLanguages, createHighlighter } from 'shiki'
16
+ import { addStory, notifyStoryChange, removeStory } from './stories.js'
17
+ import { slugify } from './util/slugify.js'
18
+
19
+ const onMarkdownListChangeHandlers: (() => unknown)[] = []
20
+
21
+ export function onMarkdownListChange(handler: () => unknown) {
22
+ onMarkdownListChangeHandlers.push(handler)
23
+ }
24
+
25
+ function notifyMarkdownListChange() {
26
+ for (const handler of onMarkdownListChangeHandlers) {
27
+ handler()
28
+ }
29
+ }
30
+
31
+ export async function createMarkdownRenderer(ctx: Context) {
32
+ const highlighter = await createHighlighter({
33
+ themes: ['github-dark'],
34
+ langs: Object.keys(bundledLanguages), // not ideal but markdown-it does not provide async highlight
35
+ })
36
+
37
+ const md = new MarkdownIt({
38
+ highlight: (code, lang) => `<div class="htw-relative htw-not-prose __histoire-code"><div class="htw-absolute htw-top-0 htw-right-0 htw-text-xs htw-text-white/40">${lang}</div>${highlighter.codeToHtml(code, { theme: 'github-dark', lang })}</div>`,
39
+ linkify: true,
40
+ html: true,
41
+ breaks: false,
42
+ })
43
+
44
+ md.use(anchor, {
45
+ slugify,
46
+ permalink: anchor.permalink.ariaHidden({}),
47
+ })
48
+ .use(attrs)
49
+ .use(emoji)
50
+
51
+ // External links
52
+ {
53
+ const defaultRender = md.renderer.rules.link_open || function (tokens, idx, options, env, self) {
54
+ return self.renderToken(tokens, idx, options)
55
+ }
56
+
57
+ md.renderer.rules.link_open = function (tokens, idx, options, env, self) {
58
+ const token = tokens[idx]
59
+ const hrefIndex = token.attrIndex('href')
60
+ const classIndex = token.attrIndex('class')
61
+
62
+ if (hrefIndex >= 0) {
63
+ const href = token.attrs[hrefIndex][1]
64
+ if (href.startsWith('.')) {
65
+ const queryIndex = href.indexOf('?')
66
+ const pathname = queryIndex >= 0 ? href.slice(0, queryIndex) : href
67
+ const query = queryIndex >= 0 ? href.slice(queryIndex) : ''
68
+
69
+ // File lookup
70
+ const file = path.resolve(path.dirname(env.file), pathname)
71
+ const storyFile = ctx.storyFiles.find(f => f.path === file)
72
+ const mdFile = ctx.markdownFiles.find(f => f.absolutePath === file)
73
+ if (!storyFile && !mdFile?.storyFile) {
74
+ throw new Error(pc.red(`[md] Cannot find story file: ${pathname} from ${env.file}`))
75
+ }
76
+
77
+ // Add attributes
78
+ const newHref = `${ctx.resolvedViteConfig.base}story/${encodeURIComponent(storyFile?.id ?? mdFile.storyFile.id)}${query}`
79
+ token.attrSet('href', newHref)
80
+ token.attrSet('data-route', 'true')
81
+ }
82
+ else if (!href.startsWith('/') && !href.startsWith('#') && (classIndex < 0 || !token.attrs[classIndex][1].includes('header-anchor'))) {
83
+ // Add target="_blank" to external links
84
+ const aIndex = token.attrIndex('target')
85
+
86
+ if (aIndex < 0) {
87
+ token.attrPush(['target', '_blank']) // add new attribute
88
+ }
89
+ else {
90
+ token.attrs[aIndex][1] = '_blank' // replace value of existing attr
91
+ }
92
+ }
93
+ }
94
+
95
+ // pass token to default renderer.
96
+ return defaultRender(tokens, idx, options, env, self)
97
+ }
98
+ }
99
+
100
+ return md
101
+ }
102
+
103
+ async function createMarkdownRendererWithPlugins(ctx: Context) {
104
+ let md = await createMarkdownRenderer(ctx)
105
+ if (ctx.config.markdown) {
106
+ const result = await ctx.config.markdown(md)
107
+ if (result) {
108
+ md = result
109
+ }
110
+ }
111
+ return md
112
+ }
113
+
114
+ export async function createMarkdownPlugins(ctx: Context) {
115
+ const plugins: VitePlugin[] = []
116
+ const md = await createMarkdownRendererWithPlugins(ctx)
117
+
118
+ // @TODO extract
119
+ plugins.push({
120
+ name: 'histoire-vue-docs-block',
121
+ transform(code, id) {
122
+ if (!id.includes('?vue&type=docs')) return
123
+ if (!id.includes('lang.md')) return
124
+ const file = id.substring(0, id.indexOf('?vue'))
125
+ const html = md.render(code, {
126
+ file,
127
+ })
128
+ return `export default Comp => {
129
+ Comp.doc = ${JSON.stringify(html)}
130
+ }`
131
+ },
132
+ })
133
+
134
+ return plugins
135
+ }
136
+
137
+ export async function createMarkdownFilesWatcher(ctx: Context) {
138
+ const md = await createMarkdownRendererWithPlugins(ctx)
139
+
140
+ const watcher = chokidar.watch('.', {
141
+ cwd: ctx.root,
142
+ ignored: (path, stats) => {
143
+ if (ctx.config.storyIgnored.some(pattern => micromatch.isMatch(path, pattern))) {
144
+ return true
145
+ }
146
+ if (micromatch.isMatch(path, '**/*.story.md')) {
147
+ return false
148
+ }
149
+
150
+ return stats?.isFile()
151
+ },
152
+ })
153
+
154
+ /**
155
+ * Initial scan is complete.
156
+ */
157
+ let watcherIsReady = false
158
+
159
+ function addFile(relativePath: string) {
160
+ const absolutePath = path.resolve(ctx.root, relativePath)
161
+ const dirFiles = fs.readdirSync(path.dirname(absolutePath))
162
+ const truncatedName = path.basename(absolutePath, '.md')
163
+ const isRelatedToStory = dirFiles.some(file => !file.endsWith('.md') && file.startsWith(truncatedName))
164
+
165
+ const { data: frontmatter, content } = matter(fs.readFileSync(absolutePath, 'utf8'))
166
+
167
+ let html: string | undefined
168
+
169
+ // We don't immediately render markdown during initial scanning in case
170
+ // markdown references other files in links (otherwise they might not
171
+ // be scanned yet and will throw 'not found' errors).
172
+ if (watcherIsReady) {
173
+ html = md.render(content, {
174
+ file: absolutePath,
175
+ })
176
+ }
177
+
178
+ const file: ServerMarkdownFile = {
179
+ id: kebabCase(relativePath.toLowerCase()),
180
+ relativePath,
181
+ absolutePath,
182
+ isRelatedToStory,
183
+ frontmatter,
184
+ content,
185
+ html,
186
+ }
187
+ ctx.markdownFiles.push(file)
188
+
189
+ if (!isRelatedToStory) {
190
+ const storyRelativePath = relativePath.replace(/\.md$/, '.js')
191
+ const storyFile = addStory(storyRelativePath, `export default ${JSON.stringify({
192
+ id: frontmatter.id,
193
+ title: frontmatter.title,
194
+ icon: frontmatter.icon ?? 'carbon:document-blank',
195
+ iconColor: frontmatter.iconColor,
196
+ group: frontmatter.group,
197
+ docsOnly: true,
198
+ variants: [],
199
+ })}`)
200
+ file.storyFile = storyFile
201
+ storyFile.markdownFile = file
202
+ notifyStoryChange(storyFile)
203
+ }
204
+ else {
205
+ const searchPath = path.join(path.dirname(relativePath), truncatedName)
206
+ const storyFile = ctx.storyFiles.find(f => f.relativePath.startsWith(searchPath))
207
+ if (storyFile) {
208
+ file.storyFile = storyFile
209
+ storyFile.markdownFile = file
210
+ notifyStoryChange(storyFile)
211
+ }
212
+ }
213
+
214
+ notifyMarkdownListChange()
215
+
216
+ return file
217
+ }
218
+
219
+ function removeFile(relativePath: string) {
220
+ const index = ctx.markdownFiles.findIndex(file => file.relativePath === relativePath)
221
+ if (index !== -1) {
222
+ const file = ctx.markdownFiles[index]
223
+ if (!file.isRelatedToStory) {
224
+ removeStory(file.storyFile.relativePath)
225
+ notifyStoryChange()
226
+ }
227
+ ctx.markdownFiles.splice(index, 1)
228
+ notifyMarkdownListChange()
229
+ }
230
+ }
231
+
232
+ async function stop() {
233
+ await watcher.close()
234
+ }
235
+
236
+ watcher
237
+ .on('add', (relativePath) => {
238
+ addFile(relativePath)
239
+ })
240
+ .on('unlink', (relativePath) => {
241
+ removeFile(relativePath)
242
+ })
243
+
244
+ await new Promise((resolve) => {
245
+ watcher.once('ready', resolve as () => void)
246
+ })
247
+
248
+ try {
249
+ // Render markdown after initial scan is complete.
250
+ for (const mdFile of ctx.markdownFiles) {
251
+ mdFile.html = md.render(mdFile.content, {
252
+ file: mdFile.absolutePath,
253
+ })
254
+ }
255
+ watcherIsReady = true
256
+
257
+ return {
258
+ stop,
259
+ }
260
+ }
261
+ catch (e) {
262
+ await stop()
263
+ throw e
264
+ }
265
+ }
266
+
267
+ export type MarkdownFilesWatcher = ReturnType<typeof createMarkdownFilesWatcher>
@@ -0,0 +1,95 @@
1
+ import type {
2
+ BuildEndCallback,
3
+ ChangeViteConfigCallback,
4
+ ModuleLoader,
5
+ Plugin,
6
+ PluginApiBase,
7
+ PluginApiBuild,
8
+ PluginApiDev,
9
+ PluginApiDevEvent,
10
+ PovesteConfig,
11
+ PreviewStoryCallback,
12
+ ServerStory,
13
+ } from '@poveste/shared'
14
+ import type { Context } from './context.js'
15
+ import chokidar from 'chokidar'
16
+ import fs from 'fs-extra'
17
+ import path from 'pathe'
18
+ import pc from 'picocolors'
19
+ import { TEMP_PATH } from './alias.js'
20
+ import { addStory, removeStory } from './stories.js'
21
+
22
+ export class BasePluginApi implements PluginApiBase {
23
+ colors = pc
24
+ path = path
25
+ fs = fs
26
+
27
+ constructor(
28
+ protected ctx: Context,
29
+ protected plugin: Plugin,
30
+ public moduleLoader: ModuleLoader,
31
+ ) { }
32
+
33
+ get pluginTempDir() {
34
+ return path.resolve(TEMP_PATH, 'plugins', this.plugin.name.replace(/:/g, '_'))
35
+ }
36
+
37
+ log(...msg) {
38
+ console.log(this.colors.gray(`[Plugin:${this.plugin.name}]`), ...msg)
39
+ }
40
+
41
+ warn(...msg) {
42
+ console.warn(this.colors.yellow(`[Plugin:${this.plugin.name}]`), ...msg)
43
+ }
44
+
45
+ error(...msg) {
46
+ console.error(this.colors.red(`[Plugin:${this.plugin.name}]`), ...msg)
47
+ }
48
+
49
+ addStoryFile(file: string) {
50
+ removeStory(file)
51
+ addStory(file)
52
+ }
53
+
54
+ getStories(): ServerStory[] {
55
+ return this.ctx.storyFiles.map(f => f.story).filter(Boolean)
56
+ }
57
+
58
+ getConfig(): PovesteConfig {
59
+ return this.ctx.config
60
+ }
61
+ }
62
+
63
+ export class DevPluginApi extends BasePluginApi implements PluginApiDev {
64
+ watcher = chokidar
65
+ }
66
+
67
+ export class BuildPluginApi extends BasePluginApi implements PluginApiBuild {
68
+ changeViteConfigCallbacks: ChangeViteConfigCallback[] = []
69
+ buildEndCallbacks: BuildEndCallback[] = []
70
+ previewStoryCallbacks: PreviewStoryCallback[] = []
71
+
72
+ changeViteConfig(cb: ChangeViteConfigCallback) {
73
+ this.changeViteConfigCallbacks.push(cb)
74
+ }
75
+
76
+ onBuildEnd(cb: BuildEndCallback) {
77
+ this.buildEndCallbacks.push(cb)
78
+ }
79
+
80
+ onPreviewStory(cb: PreviewStoryCallback) {
81
+ this.previewStoryCallbacks.push(cb)
82
+ }
83
+ }
84
+
85
+ export class DevEventPluginApi extends BasePluginApi implements PluginApiDevEvent {
86
+ constructor(
87
+ ctx: Context,
88
+ plugin: Plugin,
89
+ moduleLoader: ModuleLoader,
90
+ public event: string,
91
+ public payload: any,
92
+ ) {
93
+ super(ctx, plugin, moduleLoader)
94
+ }
95
+ }
@@ -0,0 +1,57 @@
1
+ import type { Context } from './context.js'
2
+ import http from 'node:http'
3
+ import connect from 'connect'
4
+ import sirv from 'sirv'
5
+
6
+ interface ReturnPayload {
7
+ baseUrl: string
8
+ close: () => Promise<void>
9
+ }
10
+
11
+ export async function startPreview(port: number | null, ctx: Context): Promise<ReturnPayload> {
12
+ const app = connect()
13
+
14
+ app.use(
15
+ ctx.resolvedViteConfig.base,
16
+ sirv(ctx.config.outDir, {
17
+ dev: true,
18
+ etag: true,
19
+ single: true,
20
+ }),
21
+ )
22
+
23
+ let finalPort = port ?? 6006
24
+
25
+ const httpServer = http.createServer(app)
26
+
27
+ return new Promise((resolve, reject) => {
28
+ function onError(e: Error & { code?: string }) {
29
+ if (e.code === 'EADDRINUSE') {
30
+ httpServer.listen(++finalPort)
31
+ }
32
+ else {
33
+ reject(e)
34
+ }
35
+ }
36
+
37
+ httpServer.on('error', onError)
38
+
39
+ httpServer.listen(finalPort, () => {
40
+ httpServer.off('error', onError)
41
+ const baseUrl = `http://localhost:${finalPort}${ctx.resolvedViteConfig.base}`
42
+ resolve({
43
+ baseUrl,
44
+ close: () => new Promise((resolve, reject) => {
45
+ httpServer.close((err) => {
46
+ if (err) {
47
+ reject(err)
48
+ }
49
+ else {
50
+ resolve()
51
+ }
52
+ })
53
+ }),
54
+ })
55
+ })
56
+ })
57
+ }