Skip to content

Commit

Permalink
fix: create nuxt instance more reliable (#619)
Browse files Browse the repository at this point in the history
Fixes storybook-vue/storybook-nuxt#97
```
Error: [nuxt] instance unavailable
```
  • Loading branch information
tobiasdiez authored Jun 27, 2024
1 parent d1bd15a commit 4f97749
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 101 deletions.
27 changes: 27 additions & 0 deletions examples/showcase/components/UseComposable.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { Meta, StoryObj } from '@storybook/vue3'

import UseComposable from './UseComposable.vue'

// More on how to set up stories at: https://storybook.js.org/docs/vue/writing-stories/introduction

/**
* Shows how to use a composable in a component
*/
const meta = {
title: 'Features/Use Composable',
component: UseComposable,
// This component will have an automatically generated docsPage entry: https://storybook.js.org/docs/vue/writing-docs/autodocs
tags: ['autodocs'],
} satisfies Meta<typeof UseComposable>

export default meta
type Story = StoryObj<typeof meta>
/*
*👇 Render functions are a framework specific feature to allow you control on how the component renders.
* See https://storybook.js.org/docs/vue/api/csf
* to learn how to use render functions.
*/

export const UseComposableStory: Story = {
args: {},
}
9 changes: 9 additions & 0 deletions examples/showcase/components/UseComposable.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<script setup lang="ts">
const { config } = useMyComposable()
</script>

<template>
<h1>Using composable</h1>
<h5>runtime config :</h5>
<pre>{{ config }}</pre>
</template>
7 changes: 7 additions & 0 deletions examples/showcase/composables/useMyComposable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export function useMyComposable() {
// Because your composable is called in the right place in the lifecycle,
// useRuntimeConfig will also work
const config = useRuntimeConfig()
// console.log('useMyComposable config', config)
return { config }
}
10 changes: 10 additions & 0 deletions examples/showcase/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@
export default defineNuxtConfig({
devtools: { enabled: true },
modules: ['@nuxtjs/storybook'],
runtimeConfig: {
// For testing runtimeConfig in useMyComposable
app: {
name: 'Nuxt',
version: '1.0.0',
baseURL: '/',
host: 'localhost',
port: 3000,
},
},
vue: {
runtimeCompiler: true,
},
Expand Down
14 changes: 5 additions & 9 deletions packages/storybook-addon/src/preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@ async function extendComposables(nuxt: Nuxt) {
}

async function loadNuxtViteConfig(root: string | undefined) {
const { loadNuxt, tryUseNuxt, buildNuxt, addPlugin, extendPages } =
await import('@nuxt/kit')
const { loadNuxt, tryUseNuxt, buildNuxt, extendPages } = await import(
'@nuxt/kit'
)

let nuxt = tryUseNuxt()
if (nuxt) {
Expand Down Expand Up @@ -97,10 +98,6 @@ async function loadNuxtViteConfig(root: string | undefined) {
// Override nuxt-link component to use storybook router
extendComponents(nuxt)
// nuxt.options.build.transpile.push('@storybook-vue/nuxt')
addPlugin({
src: join(pluginsDir, 'storybook'),
mode: 'client',
})
// Add iframe page
extendPages((pages) => {
pages.push({
Expand Down Expand Up @@ -207,9 +204,8 @@ export const core: PresetProperty<'core', StorybookConfig> = async (
}
}
/**
*
* @param entry preview entries
* @returns preview entries with nuxt runtime
* This is needed to correctly load the `preview.js` file,
* see https://github.com/storybookjs/storybook/blob/main/docs/contribute/framework.md#4-author-the-framework-itself
*/
export const previewAnnotations: StorybookConfig['previewAnnotations'] = async (
entry = [],
Expand Down
82 changes: 60 additions & 22 deletions packages/storybook-addon/src/preview.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,63 @@
const vueAppRootContainer = document.createElement('div')
vueAppRootContainer.id = '__nuxt'
vueAppRootContainer.setAttribute('hidden', 'true')
document.body.appendChild(vueAppRootContainer)
/**
* This is loaded by the Storybook canvas preview iframe and applies to all stories.
* https://github.com/storybookjs/storybook/blob/main/docs/contribute/framework.md#4-author-the-framework-itself
* https://github.com/storybookjs/storybook/blob/main/docs/configure/index.md#configure-story-rendering
*
* We use it to load the Nuxt app in the preview iframe.
* This should contain the same setup as the what Nuxt does in the background.
* https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/entry.ts
*/

// entry()
const logger = console
async function nuxtAppEntry() {
const nuxtApp = () => import('#app/entry').then((m) => m.default)
return nuxtApp()
}
import { setup } from '@storybook/vue3'
import type { ObjectPlugin, Plugin } from 'nuxt/app'
import { applyPlugins, createNuxtApp } from 'nuxt/app'
import { getContext } from 'unctx'

nuxtAppEntry().then((app) => {
logger.log('nuxtAppEntry done', app)
app()
.then(() => {
logger.log('nuxtAppEntry app done')
})
.catch(() => {
logger.log('nuxtAppEntry app error')
})
// app()
})
// This is used to overwrite the fetch function, not sure if it's necessary for Storybook
// It doesn't work with the current setup
// import '#build/fetch.mjs'
import '#build/css'
// @ts-expect-error virtual file
import plugins from '#build/plugins'
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const pluginsTyped: Array<Plugin & ObjectPlugin<any>> = plugins

setup(async (vueApp, storyContext) => {
// We key the Nuxt apps to the id of the story
// This is not totally correct, since the storybook vue renderer actually uses the canvas element
// Also this doesn't allow to "forceRemount"
// TODO: Improve this (needs PR to storybook to pass the necessary infos to this function)
const key = storyContext?.id
if (!key) {
throw new Error('StoryContext is not provided')
}
const nuxtAppName = `nuxt-app-${key}`
const nuxtCtx = getContext(nuxtAppName)
if (nuxtCtx.tryUse()) {
// Nothing to do, the Nuxt app is already created
return
}

export default nuxtAppEntry
const nuxt = createNuxtApp({
vueApp,
globalName: nuxtAppName,
})
await applyPlugins(
nuxt,
// The revive plugin deletes the nuxt context (window.__NUXT__)
// which leads to problems down the line
// So we don't apply it here, although that's probably not the best solution
// https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/plugins/revive-payload.client.ts
pluginsTyped.filter(
(plugin) => plugin.name === 'nuxt:revive-payload:client',
),
)
await nuxt.hooks.callHook('app:created', vueApp)
await nuxt.hooks.callHook('app:beforeMount', vueApp)
nuxtCtx.set(nuxt, true)

// TODO: The following are usually called after the app is mounted
// but currently storybook doesn't provide a hook to do that
// await nuxt.hooks.callHook('app:mounted', vueApp)
// await nextTick()
})
70 changes: 0 additions & 70 deletions packages/storybook-addon/src/runtime/plugins/storybook.ts

This file was deleted.

0 comments on commit 4f97749

Please sign in to comment.