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.
- package/LICENSE +22 -0
- package/README.md +11 -0
- package/bin.mjs +4 -0
- package/client.d.ts +19 -0
- package/client.js +15 -0
- package/dist/controls.d.ts +2 -0
- package/dist/controls.d.ts.map +1 -0
- package/dist/controls.js +1 -0
- package/dist/node/alias.d.ts +3 -0
- package/dist/node/alias.d.ts.map +1 -0
- package/dist/node/alias.js +5 -0
- package/dist/node/bin.d.ts +2 -0
- package/dist/node/bin.d.ts.map +1 -0
- package/dist/node/bin.js +35 -0
- package/dist/node/build-serialize.d.ts +22 -0
- package/dist/node/build-serialize.d.ts.map +1 -0
- package/dist/node/build-serialize.js +36 -0
- package/dist/node/build.d.ts +3 -0
- package/dist/node/build.d.ts.map +1 -0
- package/dist/node/build.js +228 -0
- package/dist/node/builtin-plugins/pinceau-tokens.d.ts +7 -0
- package/dist/node/builtin-plugins/pinceau-tokens.d.ts.map +1 -0
- package/dist/node/builtin-plugins/pinceau-tokens.js +505 -0
- package/dist/node/builtin-plugins/tailwind-tokens.d.ts +6 -0
- package/dist/node/builtin-plugins/tailwind-tokens.d.ts.map +1 -0
- package/dist/node/builtin-plugins/tailwind-tokens.js +523 -0
- package/dist/node/builtin-plugins/vanilla-support/MountStory.d.ts +15 -0
- package/dist/node/builtin-plugins/vanilla-support/MountStory.d.ts.map +1 -0
- package/dist/node/builtin-plugins/vanilla-support/MountStory.js +39 -0
- package/dist/node/builtin-plugins/vanilla-support/RenderStory.d.ts +39 -0
- package/dist/node/builtin-plugins/vanilla-support/RenderStory.d.ts.map +1 -0
- package/dist/node/builtin-plugins/vanilla-support/RenderStory.js +129 -0
- package/dist/node/builtin-plugins/vanilla-support/client.d.ts +4 -0
- package/dist/node/builtin-plugins/vanilla-support/client.d.ts.map +1 -0
- package/dist/node/builtin-plugins/vanilla-support/client.js +5 -0
- package/dist/node/builtin-plugins/vanilla-support/collect.d.ts +3 -0
- package/dist/node/builtin-plugins/vanilla-support/collect.d.ts.map +1 -0
- package/dist/node/builtin-plugins/vanilla-support/collect.js +33 -0
- package/dist/node/builtin-plugins/vanilla-support/plugin.d.ts +3 -0
- package/dist/node/builtin-plugins/vanilla-support/plugin.d.ts.map +1 -0
- package/dist/node/builtin-plugins/vanilla-support/plugin.js +29 -0
- package/dist/node/builtin-plugins/vanilla-support/types.d.ts +36 -0
- package/dist/node/builtin-plugins/vanilla-support/types.d.ts.map +1 -0
- package/dist/node/builtin-plugins/vanilla-support/types.js +3 -0
- package/dist/node/builtin-plugins/vanilla-support/vanilla.story.d.ts +4 -0
- package/dist/node/builtin-plugins/vanilla-support/vanilla.story.d.ts.map +1 -0
- package/dist/node/builtin-plugins/vanilla-support/vanilla.story.js +55 -0
- package/dist/node/collect/index.d.ts +14 -0
- package/dist/node/collect/index.d.ts.map +1 -0
- package/dist/node/collect/index.js +129 -0
- package/dist/node/collect/run.d.ts +3 -0
- package/dist/node/collect/run.d.ts.map +1 -0
- package/dist/node/collect/run.js +7 -0
- package/dist/node/collect/worker.d.ts +14 -0
- package/dist/node/collect/worker.d.ts.map +1 -0
- package/dist/node/collect/worker.js +64 -0
- package/dist/node/colors.d.ts +287 -0
- package/dist/node/colors.d.ts.map +1 -0
- package/dist/node/colors.js +324 -0
- package/dist/node/commands/build.d.ts +5 -0
- package/dist/node/commands/build.d.ts.map +1 -0
- package/dist/node/commands/build.js +9 -0
- package/dist/node/commands/dev.d.ts +8 -0
- package/dist/node/commands/dev.d.ts.map +1 -0
- package/dist/node/commands/dev.js +52 -0
- package/dist/node/commands/preview.d.ts +5 -0
- package/dist/node/commands/preview.d.ts.map +1 -0
- package/dist/node/commands/preview.js +15 -0
- package/dist/node/config.d.ts +40 -0
- package/dist/node/config.d.ts.map +1 -0
- package/dist/node/config.js +330 -0
- package/dist/node/context.d.ts +18 -0
- package/dist/node/context.d.ts.map +1 -0
- package/dist/node/context.js +28 -0
- package/dist/node/dom/dom-keys.d.ts +5 -0
- package/dist/node/dom/dom-keys.d.ts.map +1 -0
- package/dist/node/dom/dom-keys.js +233 -0
- package/dist/node/dom/env.d.ts +5 -0
- package/dist/node/dom/env.d.ts.map +1 -0
- package/dist/node/dom/env.js +47 -0
- package/dist/node/dom/util.d.ts +11 -0
- package/dist/node/dom/util.d.ts.map +1 -0
- package/dist/node/dom/util.js +65 -0
- package/dist/node/index.d.ts +12 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +4 -0
- package/dist/node/load.d.ts +9 -0
- package/dist/node/load.d.ts.map +1 -0
- package/dist/node/load.js +42 -0
- package/dist/node/markdown.d.ts +11 -0
- package/dist/node/markdown.d.ts.map +1 -0
- package/dist/node/markdown.js +228 -0
- package/dist/node/plugin.d.ts +42 -0
- package/dist/node/plugin.d.ts.map +1 -0
- package/dist/node/plugin.js +67 -0
- package/dist/node/preview.d.ts +8 -0
- package/dist/node/preview.d.ts.map +1 -0
- package/dist/node/preview.js +41 -0
- package/dist/node/search.d.ts +17 -0
- package/dist/node/search.d.ts.map +1 -0
- package/dist/node/search.js +72 -0
- package/dist/node/server.d.ts +12 -0
- package/dist/node/server.d.ts.map +1 -0
- package/dist/node/server.js +189 -0
- package/dist/node/stories.d.ts +22 -0
- package/dist/node/stories.d.ts.map +1 -0
- package/dist/node/stories.js +125 -0
- package/dist/node/tree.d.ts +4 -0
- package/dist/node/tree.d.ts.map +1 -0
- package/dist/node/tree.js +117 -0
- package/dist/node/util/find-up.d.ts +2 -0
- package/dist/node/util/find-up.d.ts.map +1 -0
- package/dist/node/util/find-up.js +20 -0
- package/dist/node/util/fs.d.ts +2 -0
- package/dist/node/util/fs.d.ts.map +1 -0
- package/dist/node/util/fs.js +1 -0
- package/dist/node/util/log.d.ts +2 -0
- package/dist/node/util/log.d.ts.map +1 -0
- package/dist/node/util/log.js +9 -0
- package/dist/node/util/slugify.d.ts +2 -0
- package/dist/node/util/slugify.d.ts.map +1 -0
- package/dist/node/util/slugify.js +22 -0
- package/dist/node/util/vendors.d.ts +3 -0
- package/dist/node/util/vendors.d.ts.map +1 -0
- package/dist/node/util/vendors.js +22 -0
- package/dist/node/vendors/controls.d.ts +2 -0
- package/dist/node/vendors/controls.d.ts.map +1 -0
- package/dist/node/vendors/controls.js +1 -0
- package/dist/node/vendors/vue.d.ts +2 -0
- package/dist/node/vendors/vue.d.ts.map +1 -0
- package/dist/node/vendors/vue.js +1 -0
- package/dist/node/virtual/index.d.ts +38 -0
- package/dist/node/virtual/index.d.ts.map +1 -0
- package/dist/node/virtual/index.js +49 -0
- package/dist/node/virtual/markdown.d.ts +3 -0
- package/dist/node/virtual/markdown.d.ts.map +1 -0
- package/dist/node/virtual/markdown.js +19 -0
- package/dist/node/virtual/noop.d.ts +2 -0
- package/dist/node/virtual/noop.d.ts.map +1 -0
- package/dist/node/virtual/noop.js +1 -0
- package/dist/node/virtual/resolved-commands.d.ts +3 -0
- package/dist/node/virtual/resolved-commands.d.ts.map +1 -0
- package/dist/node/virtual/resolved-commands.js +32 -0
- package/dist/node/virtual/resolved-config.d.ts +3 -0
- package/dist/node/virtual/resolved-config.d.ts.map +1 -0
- package/dist/node/virtual/resolved-config.js +10 -0
- package/dist/node/virtual/resolved-generated-global-setup.d.ts +3 -0
- package/dist/node/virtual/resolved-generated-global-setup.d.ts.map +1 -0
- package/dist/node/virtual/resolved-generated-global-setup.js +23 -0
- package/dist/node/virtual/resolved-generated-setup-code.d.ts +3 -0
- package/dist/node/virtual/resolved-generated-setup-code.d.ts.map +1 -0
- package/dist/node/virtual/resolved-generated-setup-code.js +5 -0
- package/dist/node/virtual/resolved-markdown-files.d.ts +3 -0
- package/dist/node/virtual/resolved-markdown-files.d.ts.map +1 -0
- package/dist/node/virtual/resolved-markdown-files.js +17 -0
- package/dist/node/virtual/resolved-stories.d.ts +3 -0
- package/dist/node/virtual/resolved-stories.d.ts.map +1 -0
- package/dist/node/virtual/resolved-stories.js +42 -0
- package/dist/node/virtual/resolved-support-plugins-client.d.ts +3 -0
- package/dist/node/virtual/resolved-support-plugins-client.d.ts.map +1 -0
- package/dist/node/virtual/resolved-support-plugins-client.js +11 -0
- package/dist/node/virtual/resolved-support-plugins-collect.d.ts +3 -0
- package/dist/node/virtual/resolved-support-plugins-collect.d.ts.map +1 -0
- package/dist/node/virtual/resolved-support-plugins-collect.js +11 -0
- package/dist/node/virtual/resolved-theme.d.ts +3 -0
- package/dist/node/virtual/resolved-theme.d.ts.map +1 -0
- package/dist/node/virtual/resolved-theme.js +12 -0
- package/dist/node/virtual/story-source.d.ts +3 -0
- package/dist/node/virtual/story-source.d.ts.map +1 -0
- package/dist/node/virtual/story-source.js +16 -0
- package/dist/node/virtual/story.d.ts +3 -0
- package/dist/node/virtual/story.d.ts.map +1 -0
- package/dist/node/virtual/story.js +7 -0
- package/dist/node/virtual/util.d.ts +3 -0
- package/dist/node/virtual/util.d.ts.map +1 -0
- package/dist/node/virtual/util.js +4 -0
- package/dist/node/virtual/vite-plugin.d.ts +4 -0
- package/dist/node/virtual/vite-plugin.d.ts.map +1 -0
- package/dist/node/virtual/vite-plugin.js +126 -0
- package/dist/node/vite.d.ts +9 -0
- package/dist/node/vite.d.ts.map +1 -0
- package/dist/node/vite.js +337 -0
- package/index.d.ts +1 -0
- package/package.json +89 -0
- package/plugin.d.ts +3 -0
- package/plugin.js +11 -0
- package/src/controls.ts +1 -0
- package/src/node/__tests__/markdown/test1.story.md +7 -0
- package/src/node/__tests__/markdown/test2.story.md +5 -0
- package/src/node/__tests__/markdown.spec.ts +61 -0
- package/src/node/__tests__/tree.spec.ts +109 -0
- package/src/node/alias.ts +8 -0
- package/src/node/bin.ts +43 -0
- package/src/node/build-serialize.ts +62 -0
- package/src/node/build.ts +283 -0
- package/src/node/builtin-plugins/pinceau-tokens.ts +521 -0
- package/src/node/builtin-plugins/tailwind-tokens.ts +536 -0
- package/src/node/builtin-plugins/vanilla-support/MountStory.ts +49 -0
- package/src/node/builtin-plugins/vanilla-support/RenderStory.ts +164 -0
- package/src/node/builtin-plugins/vanilla-support/client.ts +6 -0
- package/src/node/builtin-plugins/vanilla-support/collect.ts +43 -0
- package/src/node/builtin-plugins/vanilla-support/plugin.ts +35 -0
- package/src/node/builtin-plugins/vanilla-support/types.ts +40 -0
- package/src/node/builtin-plugins/vanilla-support/vanilla.story.ts +63 -0
- package/src/node/collect/index.ts +160 -0
- package/src/node/collect/run.ts +9 -0
- package/src/node/collect/worker.ts +97 -0
- package/src/node/colors.ts +340 -0
- package/src/node/commands/build.ts +14 -0
- package/src/node/commands/dev.ts +67 -0
- package/src/node/commands/preview.ts +22 -0
- package/src/node/config.ts +385 -0
- package/src/node/context.ts +60 -0
- package/src/node/dom/dom-keys.ts +238 -0
- package/src/node/dom/env.ts +60 -0
- package/src/node/dom/util.ts +83 -0
- package/src/node/index.ts +12 -0
- package/src/node/load.ts +59 -0
- package/src/node/markdown.ts +267 -0
- package/src/node/plugin.ts +95 -0
- package/src/node/preview.ts +57 -0
- package/src/node/search.ts +84 -0
- package/src/node/server.ts +235 -0
- package/src/node/stories.ts +154 -0
- package/src/node/tree.ts +158 -0
- package/src/node/util/find-up.ts +24 -0
- package/src/node/util/fs.ts +1 -0
- package/src/node/util/log.ts +10 -0
- package/src/node/util/slugify.ts +26 -0
- package/src/node/util/vendors.ts +27 -0
- package/src/node/vendors/controls.ts +1 -0
- package/src/node/vendors/vue.ts +1 -0
- package/src/node/virtual/index.ts +50 -0
- package/src/node/virtual/markdown.ts +21 -0
- package/src/node/virtual/noop.ts +1 -0
- package/src/node/virtual/resolved-commands.ts +42 -0
- package/src/node/virtual/resolved-config.ts +12 -0
- package/src/node/virtual/resolved-generated-global-setup.ts +25 -0
- package/src/node/virtual/resolved-generated-setup-code.ts +7 -0
- package/src/node/virtual/resolved-markdown-files.ts +19 -0
- package/src/node/virtual/resolved-stories.ts +44 -0
- package/src/node/virtual/resolved-support-plugins-client.ts +14 -0
- package/src/node/virtual/resolved-support-plugins-collect.ts +14 -0
- package/src/node/virtual/resolved-theme.ts +14 -0
- package/src/node/virtual/story-source.ts +18 -0
- package/src/node/virtual/story.ts +9 -0
- package/src/node/virtual/util.ts +5 -0
- package/src/node/virtual/vite-plugin.ts +146 -0
- package/src/node/vite.ts +379 -0
- package/tsconfig.json +44 -0
- package/vitest.config.ts +10 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { Context } from './context.js'
|
|
2
|
+
import { noCase } from 'change-case'
|
|
3
|
+
|
|
4
|
+
export async function generateTitleSearchData(ctx: Context) {
|
|
5
|
+
const searchIndex = []
|
|
6
|
+
const { idMap, addToIdMap } = createIdMap()
|
|
7
|
+
|
|
8
|
+
for (const storyFile of ctx.storyFiles) {
|
|
9
|
+
if (storyFile.story) {
|
|
10
|
+
searchIndex.push({
|
|
11
|
+
id: addToIdMap(storyFile.story.id, 'story'),
|
|
12
|
+
text: convertTitleToSentence(storyFile.story.title),
|
|
13
|
+
})
|
|
14
|
+
for (const variant of storyFile.story.variants) {
|
|
15
|
+
searchIndex.push({
|
|
16
|
+
id: addToIdMap(`${storyFile.story.id}:${variant.id}`, 'variant'),
|
|
17
|
+
text: convertTitleToSentence(`${storyFile.story.title} ${variant.title}`),
|
|
18
|
+
})
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
index: searchIndex,
|
|
25
|
+
idMap,
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function generateDocSearchData(ctx: Context) {
|
|
30
|
+
const searchIndex = []
|
|
31
|
+
const { idMap, addToIdMap } = createIdMap()
|
|
32
|
+
|
|
33
|
+
for (const storyFile of ctx.storyFiles) {
|
|
34
|
+
if (storyFile.story && storyFile.story.docsText) {
|
|
35
|
+
searchIndex.push({
|
|
36
|
+
id: addToIdMap(storyFile.story.id, 'story'),
|
|
37
|
+
text: storyFile.story.docsText,
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
index: searchIndex,
|
|
44
|
+
idMap,
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function createIdMap() {
|
|
49
|
+
let uid = 0
|
|
50
|
+
const idMap: Record<number, { id: string, kind: string }> = {}
|
|
51
|
+
|
|
52
|
+
function addToIdMap(id: string, kind: string) {
|
|
53
|
+
const n = uid++
|
|
54
|
+
idMap[n] = { id, kind }
|
|
55
|
+
return n
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
idMap,
|
|
60
|
+
addToIdMap,
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function convertTitleToSentence(text: string) {
|
|
65
|
+
return text.split(' ').map(str => noCase(str)).join(' ')
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// @TODO clear handlers when SearchPane unmounts
|
|
69
|
+
export function getSearchDataJS(data: any) {
|
|
70
|
+
return `export let searchData = ${JSON.stringify(data)}
|
|
71
|
+
const handlers = []
|
|
72
|
+
export function onUpdate (cb) {
|
|
73
|
+
handlers.push(cb)
|
|
74
|
+
}
|
|
75
|
+
if (import.meta.hot) {
|
|
76
|
+
import.meta.hot.accept(newModule => {
|
|
77
|
+
searchData = newModule.searchData
|
|
78
|
+
handlers.forEach(h => {
|
|
79
|
+
h(newModule.searchData)
|
|
80
|
+
newModule.onUpdate(h)
|
|
81
|
+
})
|
|
82
|
+
})
|
|
83
|
+
}`
|
|
84
|
+
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import type { ServerStoryFile } from '@poveste/shared'
|
|
2
|
+
import type { Context } from './context.js'
|
|
3
|
+
import { performance } from 'node:perf_hooks'
|
|
4
|
+
import pc from 'picocolors'
|
|
5
|
+
import { createServer as createViteServer, mergeConfig as mergeViteConfig } from 'vite'
|
|
6
|
+
import { useCollectStories } from './collect/index.js'
|
|
7
|
+
import { useModuleLoader } from './load.js'
|
|
8
|
+
import { createMarkdownFilesWatcher, onMarkdownListChange } from './markdown.js'
|
|
9
|
+
import { DevEventPluginApi, DevPluginApi } from './plugin.js'
|
|
10
|
+
import { onStoryChange, onStoryListChange, watchStories } from './stories.js'
|
|
11
|
+
import { wrapLogError } from './util/log.js'
|
|
12
|
+
import * as VirtualFiles from './virtual/index.js'
|
|
13
|
+
import { getViteConfigWithPlugins } from './vite.js'
|
|
14
|
+
|
|
15
|
+
export interface CreateServerOptions {
|
|
16
|
+
port?: number
|
|
17
|
+
open?: boolean
|
|
18
|
+
host?: string | boolean
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function createServer(ctx: Context, options: CreateServerOptions = {}) {
|
|
22
|
+
const getViteServer = async (collecting: boolean) => {
|
|
23
|
+
const { viteConfig, viteConfigFile } = await getViteConfigWithPlugins(collecting, ctx)
|
|
24
|
+
|
|
25
|
+
if (!collecting) {
|
|
26
|
+
if (options.open) {
|
|
27
|
+
viteConfig.server.open = true
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (options.host) {
|
|
31
|
+
viteConfig.server.host = options.host
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const server = await createViteServer(
|
|
36
|
+
mergeViteConfig(viteConfig, {
|
|
37
|
+
optimizeDeps: { include: viteConfig.optimizeDeps?.include ?? [], noDiscovery: collecting },
|
|
38
|
+
}),
|
|
39
|
+
)
|
|
40
|
+
await server.pluginContainer.buildStart({})
|
|
41
|
+
return {
|
|
42
|
+
server,
|
|
43
|
+
viteConfigFile,
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Should be run sequentially to get a fresh vite.config.js each time
|
|
48
|
+
const { server: nodeServer } = await getViteServer(true) // Run before normal vite to prevent breaking HMR in Nuxt
|
|
49
|
+
const { server, viteConfigFile } = await getViteServer(false)
|
|
50
|
+
await watchStories(ctx)
|
|
51
|
+
const { stop: stopMdFileWatcher } = await createMarkdownFilesWatcher(ctx)
|
|
52
|
+
|
|
53
|
+
const moduleLoader = useModuleLoader({
|
|
54
|
+
server: nodeServer,
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
const pluginOnCleanups: (() => void | Promise<void>)[] = []
|
|
58
|
+
for (const plugin of ctx.config.plugins) {
|
|
59
|
+
if (plugin.onDev) {
|
|
60
|
+
const api = new DevPluginApi(ctx, plugin, moduleLoader)
|
|
61
|
+
const onCleanup = (cb: () => void | Promise<void>) => {
|
|
62
|
+
pluginOnCleanups.push(cb)
|
|
63
|
+
}
|
|
64
|
+
await plugin.onDev(api, onCleanup)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Custom dev events
|
|
69
|
+
server.ws.on(`histoire:dev-event`, async ({ event, payload }) => {
|
|
70
|
+
for (const plugin of ctx.config.plugins) {
|
|
71
|
+
if (plugin.onDevEvent) {
|
|
72
|
+
const api = new DevEventPluginApi(ctx, plugin, moduleLoader, event, payload)
|
|
73
|
+
const result = await plugin.onDevEvent(api)
|
|
74
|
+
if (!event.startsWith('on') && result !== undefined) {
|
|
75
|
+
server.ws.send(`histoire:dev-event-result`, { event, result })
|
|
76
|
+
break
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
// Wait for pre-bundling (in `listen()`)
|
|
83
|
+
await server.listen(options.port ?? server.config.server?.port)
|
|
84
|
+
|
|
85
|
+
const {
|
|
86
|
+
clearCache,
|
|
87
|
+
executeStoryFile,
|
|
88
|
+
destroy: destroyCollectStories,
|
|
89
|
+
} = useCollectStories({
|
|
90
|
+
server: nodeServer,
|
|
91
|
+
mainServer: server,
|
|
92
|
+
}, ctx)
|
|
93
|
+
|
|
94
|
+
// onStoryChange debouncing
|
|
95
|
+
let queued = false
|
|
96
|
+
let queuedFiles: ServerStoryFile[] = []
|
|
97
|
+
let currentFiles: ServerStoryFile[] = []
|
|
98
|
+
let queueTimer
|
|
99
|
+
let collecting = false
|
|
100
|
+
let didAllStoriesYet = false
|
|
101
|
+
|
|
102
|
+
// Invalidate modules
|
|
103
|
+
const invalidateModule = (id: string) => {
|
|
104
|
+
const mod = server.moduleGraph.getModuleById(id)
|
|
105
|
+
if (!mod) {
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
server.moduleGraph.invalidateModule(mod)
|
|
109
|
+
|
|
110
|
+
// Send HMR update
|
|
111
|
+
const timestamp = Date.now()
|
|
112
|
+
mod.lastHMRTimestamp = timestamp
|
|
113
|
+
server.ws.send({
|
|
114
|
+
type: 'update',
|
|
115
|
+
updates: [
|
|
116
|
+
{
|
|
117
|
+
type: 'js-update',
|
|
118
|
+
acceptedPath: mod.url,
|
|
119
|
+
path: mod.url,
|
|
120
|
+
timestamp,
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
onStoryChange(async (changedFile) => {
|
|
127
|
+
if (changedFile && !didAllStoriesYet) {
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (changedFile) {
|
|
132
|
+
if (!queuedFiles.includes(changedFile)) {
|
|
133
|
+
queuedFiles.push(changedFile)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
queuedFiles = []
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (!queued) {
|
|
141
|
+
queued = true
|
|
142
|
+
if (!collecting) {
|
|
143
|
+
clearTimeout(queueTimer)
|
|
144
|
+
// Debounce
|
|
145
|
+
queueTimer = setTimeout(collect, 100)
|
|
146
|
+
}
|
|
147
|
+
else if (!changedFile && !currentFiles.length) {
|
|
148
|
+
// Full collect in progress
|
|
149
|
+
queued = false
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
async function collect() {
|
|
155
|
+
collecting = true
|
|
156
|
+
|
|
157
|
+
clearCache()
|
|
158
|
+
|
|
159
|
+
currentFiles = queuedFiles.slice()
|
|
160
|
+
queuedFiles = []
|
|
161
|
+
queued = false
|
|
162
|
+
|
|
163
|
+
console.log('Collect stories start', currentFiles.length ? currentFiles.map(f => f.fileName).join(', ') : 'all')
|
|
164
|
+
const time = performance.now()
|
|
165
|
+
if (currentFiles.length) {
|
|
166
|
+
await Promise.all(currentFiles.map(async (storyFile) => {
|
|
167
|
+
await executeStoryFile(storyFile)
|
|
168
|
+
if (storyFile.story) {
|
|
169
|
+
await invalidateModule(`/__resolved__virtual:story-source:${storyFile.story.id}`)
|
|
170
|
+
}
|
|
171
|
+
}))
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
// Full update
|
|
175
|
+
|
|
176
|
+
// Progress tracking
|
|
177
|
+
const fileCount = ctx.storyFiles.length
|
|
178
|
+
let loadedFilesCount = 0
|
|
179
|
+
const sendProgress = () => {
|
|
180
|
+
server.ws.send('histoire:stories-loading-progress', {
|
|
181
|
+
loadedFileCount: loadedFilesCount,
|
|
182
|
+
totalFileCount: fileCount,
|
|
183
|
+
})
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
sendProgress()
|
|
187
|
+
|
|
188
|
+
await Promise.all(ctx.storyFiles.map(async (storyFile) => {
|
|
189
|
+
await executeStoryFile(storyFile)
|
|
190
|
+
loadedFilesCount++
|
|
191
|
+
sendProgress()
|
|
192
|
+
}))
|
|
193
|
+
|
|
194
|
+
didAllStoriesYet = true
|
|
195
|
+
server.ws.send('histoire:all-stories-loaded', {})
|
|
196
|
+
}
|
|
197
|
+
console.log(`Collect stories end ${pc.bold(pc.blue(Math.round(performance.now() - time)))}ms`)
|
|
198
|
+
|
|
199
|
+
invalidateModule(VirtualFiles.RESOLVED_STORIES_ID)
|
|
200
|
+
invalidateModule(VirtualFiles.RESOLVED_SEARCH_TITLE_DATA_ID)
|
|
201
|
+
|
|
202
|
+
collecting = false
|
|
203
|
+
|
|
204
|
+
if (queued) {
|
|
205
|
+
await collect()
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
onStoryListChange(() => {
|
|
210
|
+
invalidateModule(VirtualFiles.RESOLVED_STORIES_ID)
|
|
211
|
+
invalidateModule(VirtualFiles.RESOLVED_SEARCH_TITLE_DATA_ID)
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
onMarkdownListChange(() => {
|
|
215
|
+
invalidateModule(VirtualFiles.RESOLVED_MARKDOWN_FILES)
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
async function close() {
|
|
219
|
+
for (const cb of pluginOnCleanups) {
|
|
220
|
+
await wrapLogError('plugin.onDev.onCleanup', () => cb())
|
|
221
|
+
}
|
|
222
|
+
await wrapLogError('server.close', () => server.close())
|
|
223
|
+
await wrapLogError('nodeServer', () => nodeServer.close())
|
|
224
|
+
await wrapLogError('destroyCollectStories', () => destroyCollectStories())
|
|
225
|
+
await wrapLogError('stopMdFileWatcher', () => stopMdFileWatcher())
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
collect()
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
server,
|
|
232
|
+
viteConfigFile,
|
|
233
|
+
close,
|
|
234
|
+
}
|
|
235
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import type { ServerStoryFile } from '@poveste/shared'
|
|
2
|
+
import type { Context } from './context.js'
|
|
3
|
+
import { kebabCase } from 'change-case'
|
|
4
|
+
import chokidar from 'chokidar'
|
|
5
|
+
import { globby } from 'globby'
|
|
6
|
+
import micromatch from 'micromatch'
|
|
7
|
+
import { basename, resolve } from 'pathe'
|
|
8
|
+
|
|
9
|
+
type StoryChangeHandler = (file?: ServerStoryFile) => unknown
|
|
10
|
+
const storyChangeHandlers: StoryChangeHandler[] = []
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Called when a new story is added or modified. Collecting should be done.
|
|
14
|
+
* @param handler
|
|
15
|
+
*/
|
|
16
|
+
export function onStoryChange(handler: StoryChangeHandler) {
|
|
17
|
+
storyChangeHandlers.push(handler)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function notifyStoryChange(file?: ServerStoryFile) {
|
|
21
|
+
for (const handler of storyChangeHandlers) {
|
|
22
|
+
handler(file)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type StoryListChangeHandler = () => unknown
|
|
27
|
+
const storyListChangeHandlers: StoryListChangeHandler[] = []
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Called when the story list has changed (ex: removed a story). No collecting should be needed.
|
|
31
|
+
* @param handler
|
|
32
|
+
*/
|
|
33
|
+
export function onStoryListChange(handler: StoryListChangeHandler) {
|
|
34
|
+
storyListChangeHandlers.push(handler)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function notifyStoryListChange() {
|
|
38
|
+
for (const handler of storyListChangeHandlers) {
|
|
39
|
+
handler()
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let context: Context
|
|
44
|
+
|
|
45
|
+
export async function watchStories(newContext: Context) {
|
|
46
|
+
context = newContext
|
|
47
|
+
|
|
48
|
+
const baseWatchPaths = Array.from(new Set(context.config.storyMatch.map((pattern) => {
|
|
49
|
+
const segments = pattern.split('/')
|
|
50
|
+
return segments.slice(0, segments.findIndex(segment => segment.includes('*'))).join('/') || '.'
|
|
51
|
+
})))
|
|
52
|
+
|
|
53
|
+
const resolvedStoryIgnored = context.config.storyIgnored.map((pattern) => {
|
|
54
|
+
return resolve(context.root, pattern)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
const resolveStoryMatch = context.config.storyMatch.map((pattern) => {
|
|
58
|
+
return resolve(context.root, pattern)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
const watcher = chokidar.watch(baseWatchPaths, {
|
|
62
|
+
cwd: context.root,
|
|
63
|
+
ignored: (path, stats) => {
|
|
64
|
+
if (resolvedStoryIgnored.some(pattern => micromatch.isMatch(path, pattern))) {
|
|
65
|
+
return true
|
|
66
|
+
}
|
|
67
|
+
if (resolveStoryMatch.some(pattern => micromatch.isMatch(path, pattern))) {
|
|
68
|
+
return false
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Allow directories to be traversed, only ignore files that don't match
|
|
72
|
+
return stats?.isFile() ?? false
|
|
73
|
+
},
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
watcher
|
|
77
|
+
.on('add', (file) => {
|
|
78
|
+
const storyFile = addStory(file)
|
|
79
|
+
setTimeout(() => notifyStoryChange(storyFile), 100) // Delay in case file renaming fired Add event before Unlink event
|
|
80
|
+
})
|
|
81
|
+
.on('unlink', (file) => {
|
|
82
|
+
removeStory(file)
|
|
83
|
+
notifyStoryListChange()
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
return watcher
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function getAbsoluteFilePath(relativeFilePath: string) {
|
|
90
|
+
return resolve(context.root, relativeFilePath)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function addStory(relativeFilePath: string, virtualModuleCode?: string) {
|
|
94
|
+
const absoluteFilePath = getAbsoluteFilePath(relativeFilePath)
|
|
95
|
+
|
|
96
|
+
for (const file of context.storyFiles) {
|
|
97
|
+
if (file.path === absoluteFilePath) {
|
|
98
|
+
return file
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const fileId = kebabCase(relativeFilePath.toLowerCase())
|
|
103
|
+
let fileName = basename(relativeFilePath)
|
|
104
|
+
if (fileName.includes('.')) {
|
|
105
|
+
fileName = fileName.substring(0, fileName.indexOf('.'))
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let supportPluginId: string
|
|
109
|
+
|
|
110
|
+
for (const p of context.config.supportMatch) {
|
|
111
|
+
if (micromatch.isMatch(absoluteFilePath, p.patterns, {
|
|
112
|
+
dot: true,
|
|
113
|
+
})) {
|
|
114
|
+
supportPluginId = p.pluginIds[0]
|
|
115
|
+
break
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (!supportPluginId) {
|
|
120
|
+
throw new Error(`No support plugin found for file ${absoluteFilePath}`)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const file: ServerStoryFile = {
|
|
124
|
+
id: fileId, // The file id will be changed by the story id after it is collected
|
|
125
|
+
path: absoluteFilePath,
|
|
126
|
+
relativePath: relativeFilePath,
|
|
127
|
+
fileName,
|
|
128
|
+
moduleId: virtualModuleCode ? `virtual:story:${absoluteFilePath}` : absoluteFilePath,
|
|
129
|
+
supportPluginId,
|
|
130
|
+
virtual: !!virtualModuleCode,
|
|
131
|
+
moduleCode: virtualModuleCode,
|
|
132
|
+
}
|
|
133
|
+
context.storyFiles.push(file)
|
|
134
|
+
return file
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function removeStory(relativeFilePath: string) {
|
|
138
|
+
const absoluteFilePath = getAbsoluteFilePath(relativeFilePath)
|
|
139
|
+
const index = context.storyFiles.findIndex(file => file.path === absoluteFilePath)
|
|
140
|
+
if (index !== -1) context.storyFiles.splice(index, 1)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export async function findAllStories(newContext: Context) {
|
|
144
|
+
context = newContext
|
|
145
|
+
|
|
146
|
+
const files = await globby(context.config.storyMatch, {
|
|
147
|
+
cwd: context.root,
|
|
148
|
+
ignore: context.config.storyIgnored,
|
|
149
|
+
})
|
|
150
|
+
context.storyFiles.length = 0
|
|
151
|
+
for (const file of files) {
|
|
152
|
+
addStory(file)
|
|
153
|
+
}
|
|
154
|
+
}
|
package/src/node/tree.ts
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
PovesteConfig,
|
|
3
|
+
ServerStoryFile,
|
|
4
|
+
ServerTree,
|
|
5
|
+
ServerTreeFile,
|
|
6
|
+
TreeGroupConfig,
|
|
7
|
+
} from '@poveste/shared'
|
|
8
|
+
import pc from 'picocolors'
|
|
9
|
+
|
|
10
|
+
export function createPath(config: PovesteConfig, file: ServerTreeFile) {
|
|
11
|
+
if (config.tree.file === 'title') {
|
|
12
|
+
return file.title.split('/')
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (config.tree.file === 'path') {
|
|
16
|
+
const paths = file.path.split('/').slice(0, -1)
|
|
17
|
+
|
|
18
|
+
// check if tree file path is a plugin
|
|
19
|
+
const index = paths.findIndex(p => p.includes('.histoire'))
|
|
20
|
+
|
|
21
|
+
if (index !== -1) {
|
|
22
|
+
return ['plugins', file.title]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return [...paths, file.title]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return config.tree.file(file)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function makeTree(config: PovesteConfig, files: ServerStoryFile[]) {
|
|
32
|
+
interface ITreeObject {
|
|
33
|
+
[index: string]: number | ITreeObject
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface ITreeGroup {
|
|
37
|
+
groupConfig?: TreeGroupConfig
|
|
38
|
+
treeObject: ITreeObject
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const groups: ITreeGroup[] = config.tree?.groups?.map(g => ({
|
|
42
|
+
groupConfig: g,
|
|
43
|
+
treeObject: {},
|
|
44
|
+
})) || []
|
|
45
|
+
const defaultGroup = {
|
|
46
|
+
treeObject: {},
|
|
47
|
+
}
|
|
48
|
+
groups.push(defaultGroup)
|
|
49
|
+
|
|
50
|
+
files.forEach((file, index) => {
|
|
51
|
+
const group = getGroup(file)
|
|
52
|
+
setPath(file.treePath, index, group.treeObject)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
let sortingFunction = (a: string, b: string) => a.localeCompare(b)
|
|
56
|
+
|
|
57
|
+
if (config.tree.order !== 'asc') {
|
|
58
|
+
sortingFunction = config.tree.order
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const result: ServerTree = []
|
|
62
|
+
|
|
63
|
+
for (const group of groups) {
|
|
64
|
+
if (group === defaultGroup) {
|
|
65
|
+
result.push(...buildTree(group.treeObject))
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
result.push({
|
|
69
|
+
group: true,
|
|
70
|
+
id: group.groupConfig.id,
|
|
71
|
+
title: group.groupConfig.title,
|
|
72
|
+
children: buildTree(group.treeObject),
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return result
|
|
78
|
+
|
|
79
|
+
function getGroup(file: ServerStoryFile): ITreeGroup {
|
|
80
|
+
if (file.story?.group) {
|
|
81
|
+
const group = groups.find(g => g.groupConfig?.id === file.story.group)
|
|
82
|
+
if (group) {
|
|
83
|
+
return group
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
console.error(pc.red(`Group ${file.story.group} not found for story ${file.path}`))
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
for (const group of groups) {
|
|
90
|
+
if (group.groupConfig?.include && group.groupConfig.include(file.treeFile)) {
|
|
91
|
+
return group
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return defaultGroup
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function setPath(path: string[], value: unknown, tree: unknown) {
|
|
98
|
+
path.reduce((subtree, key, i) => {
|
|
99
|
+
if (i === path.length - 1) {
|
|
100
|
+
setKey(subtree, key, value)
|
|
101
|
+
}
|
|
102
|
+
else if (isLeaf(subtree[key])) {
|
|
103
|
+
setKey(subtree, key, subtree[key])
|
|
104
|
+
subtree[key] = {}
|
|
105
|
+
}
|
|
106
|
+
else if (!subtree[key]) {
|
|
107
|
+
subtree[key] = {}
|
|
108
|
+
}
|
|
109
|
+
return subtree[key]
|
|
110
|
+
}, tree)
|
|
111
|
+
|
|
112
|
+
function isLeaf(element) {
|
|
113
|
+
return !Number.isNaN(Number(element))
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function setKey(tree, key, value) {
|
|
118
|
+
if (isUndefined(tree[key])) {
|
|
119
|
+
tree[key] = value
|
|
120
|
+
return
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
let copyNumber = 1
|
|
124
|
+
|
|
125
|
+
while (!isUndefined(tree[`${key}-${copyNumber}`])) {
|
|
126
|
+
copyNumber++
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
tree[`${key}-${copyNumber}`] = value
|
|
130
|
+
|
|
131
|
+
function isUndefined(element) {
|
|
132
|
+
return element === undefined
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function buildTree(treeObject: ITreeObject): ServerTree {
|
|
137
|
+
const tree: ServerTree = []
|
|
138
|
+
|
|
139
|
+
for (const [key, element] of Object.entries(treeObject)) {
|
|
140
|
+
if (Number.isInteger(element)) {
|
|
141
|
+
tree.push({
|
|
142
|
+
title: key,
|
|
143
|
+
index: element as number,
|
|
144
|
+
})
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
tree.push({
|
|
148
|
+
title: key,
|
|
149
|
+
children: buildTree(element as ITreeObject),
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
tree.sort((a, b) => sortingFunction(a.title, b.title))
|
|
155
|
+
|
|
156
|
+
return tree
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'pathe'
|
|
3
|
+
|
|
4
|
+
export function findUp(cwd: string = process.cwd(), fileNames: string[]): string {
|
|
5
|
+
let { root } = path.parse(cwd)
|
|
6
|
+
let dir = cwd
|
|
7
|
+
|
|
8
|
+
// On Windows, it will for example return `C:`, we need to add the trailing `/`
|
|
9
|
+
if (root[1] === ':' && root[2] === undefined) {
|
|
10
|
+
root += '/'
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
while (dir !== root) {
|
|
14
|
+
for (const fileName of fileNames) {
|
|
15
|
+
const searchPath = path.join(dir, fileName)
|
|
16
|
+
if (fs.existsSync(searchPath)) {
|
|
17
|
+
return searchPath
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
dir = path.dirname(dir)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return null
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const slash = (str: string) => str.replace(/\\/g, '/')
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// From vitepress
|
|
2
|
+
|
|
3
|
+
// string.js slugify drops non ascii chars so we have to
|
|
4
|
+
// use a custom implementation here
|
|
5
|
+
import { remove as removeDiacritics } from 'diacritics'
|
|
6
|
+
// eslint-disable-next-line no-control-regex
|
|
7
|
+
const rControl = /[\u0000-\u001F]/g
|
|
8
|
+
const rSpecial = /[\s~`!@#$%^&*()\-_+=[\]{}|\\;:"'<>,.?/]+/g
|
|
9
|
+
|
|
10
|
+
export function slugify(str: string): string {
|
|
11
|
+
return (
|
|
12
|
+
removeDiacritics(str)
|
|
13
|
+
// Remove control characters
|
|
14
|
+
.replace(rControl, '')
|
|
15
|
+
// Replace special characters
|
|
16
|
+
.replace(rSpecial, '-')
|
|
17
|
+
// Remove continuous separators
|
|
18
|
+
.replace(/-{2,}/g, '-')
|
|
19
|
+
// Remove prefixing and trailing separators
|
|
20
|
+
.replace(/^-+|-+$/g, '')
|
|
21
|
+
// ensure it doesn't start with a number (#121)
|
|
22
|
+
.replace(/^(\d)/, '_$1')
|
|
23
|
+
// lowercase
|
|
24
|
+
.toLowerCase()
|
|
25
|
+
)
|
|
26
|
+
}
|