-
-
Notifications
You must be signed in to change notification settings - Fork 196
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: vanilla js (no-framework) support plugin
- Loading branch information
Showing
11 changed files
with
432 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
export * from './codegen/index.js' | ||
export * from './types.js' | ||
export * from './state.js' | ||
export * from './type-utils.js' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export type Awaitable<T> = Promise<T> | T |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
50 changes: 50 additions & 0 deletions
50
packages/histoire/src/node/builtin-plugins/vanilla-support/MountStory.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { | ||
defineComponent as _defineComponent, | ||
PropType as _PropType, | ||
} from '@histoire/vendors/vue' | ||
import type { Story } from '@histoire/shared' | ||
import type { StoryOptions, VariantOptions } from './types' | ||
|
||
export default _defineComponent({ | ||
name: 'MountStory', | ||
|
||
props: { | ||
story: { | ||
type: Object as _PropType<Story>, | ||
required: true, | ||
}, | ||
}, | ||
|
||
setup (props) { | ||
const options = props.story.file.component as StoryOptions | ||
|
||
let rawVariants: VariantOptions[] = [] | ||
|
||
if (options.onMount) { | ||
rawVariants = [{ | ||
id: '_default', | ||
title: 'default', | ||
onMount: options.onMount, | ||
onMountControls: options.onMountControls, | ||
}] | ||
} else { | ||
rawVariants = options.variants | ||
} | ||
|
||
for (const index in props.story.variants) { | ||
const rawVariant = rawVariants[index] | ||
Object.assign(props.story.variants[index], { | ||
slots: () => ({ default: rawVariant.onMount, controls: rawVariant.onMountControls }), | ||
source: rawVariant.source, | ||
responsiveDisabled: rawVariant.responsiveDisabled, | ||
autoPropsDisabled: rawVariant.autoPropsDisabled, | ||
setupApp: rawVariant.setupApp, | ||
configReady: true, | ||
}) | ||
} | ||
}, | ||
|
||
render () { | ||
return null | ||
}, | ||
}) |
166 changes: 166 additions & 0 deletions
166
packages/histoire/src/node/builtin-plugins/vanilla-support/RenderStory.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
/* eslint-disable vue/one-component-per-file */ | ||
|
||
import { | ||
defineComponent as _defineComponent, | ||
onBeforeUnmount as _onBeforeUnmount, | ||
onMounted as _onMounted, | ||
PropType as _PropType, | ||
ref as _ref, | ||
watch as _watch, | ||
h as _h, | ||
} from '@histoire/vendors/vue' | ||
import type { Story, Variant } from '@histoire/shared' | ||
// @ts-expect-error virtual module id | ||
import * as setup from '$histoire-setup' | ||
// @ts-expect-error virtual module id | ||
import * as generatedSetup from '$histoire-generated-global-setup' | ||
import type { App, MountApi, VanillaApi } from './types' | ||
|
||
export default _defineComponent({ | ||
name: 'RenderStory', | ||
|
||
props: { | ||
variant: { | ||
type: Object as _PropType<Variant>, | ||
required: true, | ||
}, | ||
|
||
story: { | ||
type: Object as _PropType<Story>, | ||
required: true, | ||
}, | ||
|
||
slotName: { | ||
type: String, | ||
default: 'default', | ||
}, | ||
}, | ||
|
||
emits: { | ||
ready: () => true, | ||
}, | ||
|
||
setup (props, { emit }) { | ||
const sandbox = _ref<HTMLDivElement>() | ||
let mounting = false | ||
let app: App | ||
let appHooks: Record<'onUpdate' | 'onUnmount', (() => unknown)[]> | ||
|
||
async function unmountVariant () { | ||
if (app) { | ||
await app.onUnmount?.() | ||
if (appHooks) { | ||
for (const hook of appHooks.onUnmount) { | ||
await hook() | ||
} | ||
} | ||
|
||
app.el.parentNode.removeChild(app.el) | ||
app = null | ||
} | ||
} | ||
|
||
async function mountVariant () { | ||
if (mounting) return | ||
mounting = true | ||
|
||
await unmountVariant() | ||
|
||
app = { | ||
el: document.createElement('div'), | ||
} | ||
|
||
if (typeof generatedSetup?.setupVanilla === 'function') { | ||
await generatedSetup.setupVanilla({ | ||
app, | ||
story: props.story, | ||
variant: props.variant, | ||
}) | ||
} | ||
|
||
if (typeof setup?.setupVanilla === 'function') { | ||
await setup.setupVanilla({ | ||
app, | ||
story: props.story, | ||
variant: props.variant, | ||
}) | ||
} | ||
|
||
if (typeof props.variant.setupApp === 'function') { | ||
await props.variant.setupApp({ | ||
app, | ||
story: props.story, | ||
variant: props.variant, | ||
}) | ||
} | ||
|
||
await app.onMount?.() | ||
|
||
appHooks = { | ||
onUpdate: [], | ||
onUnmount: [], | ||
} | ||
|
||
const api: MountApi = { | ||
el: app.el, | ||
state: props.variant.state, | ||
onUpdate: (cb) => { | ||
appHooks.onUpdate.push(cb) | ||
}, | ||
onUnmount: (cb) => { | ||
appHooks.onUnmount.push(cb) | ||
}, | ||
} | ||
|
||
const onMount = props.variant.slots()[props.slotName] as VanillaApi['onMount'] | VanillaApi['onMountControls'] | ||
await onMount(api) | ||
|
||
sandbox.value.appendChild(app.el) | ||
|
||
emit('ready') | ||
} | ||
|
||
_onMounted(async () => { | ||
if (props.variant.configReady) { | ||
await mountVariant() | ||
} | ||
}) | ||
|
||
_watch(() => props.variant, async value => { | ||
if (value.configReady && !mounting) { | ||
if (!app) { | ||
await mountVariant() | ||
} else { | ||
// @TODO check if need to refresh here | ||
} | ||
} | ||
}, { | ||
deep: true, | ||
}) | ||
|
||
_watch(() => props.variant.state, async () => { | ||
if (appHooks) { | ||
for (const hook of appHooks.onUpdate) { | ||
await hook() | ||
} | ||
} | ||
}, { | ||
deep: true, | ||
}) | ||
|
||
_onBeforeUnmount(() => { | ||
unmountVariant() | ||
}) | ||
|
||
return { | ||
sandbox, | ||
} | ||
}, | ||
|
||
render () { | ||
return _h('div', { | ||
ref: 'sandbox', | ||
class: '__histoire-sandbox htw-overflow-auto', | ||
}) | ||
}, | ||
}) |
6 changes: 6 additions & 0 deletions
6
packages/histoire/src/node/builtin-plugins/vanilla-support/client.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export { default as MountStory } from './MountStory' | ||
export { default as RenderStory } from './RenderStory' | ||
|
||
export function generateSourceCode () { | ||
// noop | ||
} |
42 changes: 42 additions & 0 deletions
42
packages/histoire/src/node/builtin-plugins/vanilla-support/collect.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import type { ServerRunPayload, ServerStory, ServerVariant } from '@histoire/shared' | ||
import type { StoryOptions, VariantOptions } from './types' | ||
|
||
export async function run ({ file, storyData }: ServerRunPayload) { | ||
const { default: Comp } = await import(file.moduleId) | ||
|
||
const options = Comp as StoryOptions | ||
|
||
let rawVariants: VariantOptions[] = [] | ||
|
||
if (options.onMount) { | ||
// Implicit variant | ||
rawVariants = [{ | ||
id: '_default', | ||
title: 'default', | ||
}] | ||
} else { | ||
rawVariants = options.variants | ||
} | ||
|
||
const story: ServerStory = { | ||
id: options.id ?? file.id, | ||
title: options.title ?? file.fileName, | ||
group: options.group, | ||
layout: options.layout ?? { type: 'single', iframe: true }, | ||
icon: options.icon, | ||
iconColor: options.iconColor, | ||
docsOnly: options.docsOnly ?? false, | ||
variants: null, | ||
} | ||
|
||
const variants: ServerVariant[] = rawVariants.map((v, index) => ({ | ||
id: v.id ?? `${story.id}-${index}`, | ||
title: v.title ?? 'untitled', | ||
icon: v.icon, | ||
iconColor: v.iconColor, | ||
})) | ||
|
||
story.variants = variants | ||
|
||
storyData.push(story) | ||
} |
35 changes: 35 additions & 0 deletions
35
packages/histoire/src/node/builtin-plugins/vanilla-support/plugin.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { dirname } from 'pathe' | ||
import { fileURLToPath } from 'url' | ||
import { Plugin } from '../../plugin.js' | ||
|
||
const __dirname = dirname(fileURLToPath(import.meta.url)) | ||
|
||
export function vanillaSupport (): Plugin { | ||
return { | ||
name: 'builtin:vanilla-support', | ||
|
||
config () { | ||
return { | ||
supportMatch: [ | ||
{ | ||
id: 'vanilla', | ||
patterns: ['**/*.js'], | ||
pluginIds: ['vanilla'], | ||
}, | ||
], | ||
} | ||
}, | ||
|
||
supportPlugin: { | ||
id: 'vanilla', | ||
moduleName: __dirname, | ||
setupFn: 'setupVanilla', | ||
importStoryComponent: (file, index) => `import Comp${index} from '${file.path}'`, | ||
}, | ||
|
||
// onDev (api) { | ||
// // Test vanilla story | ||
// api.addStoryFile(resolve(__dirname, './vanilla.story.js')) | ||
// }, | ||
} | ||
} |
Oops, something went wrong.