From dc1883ce75d0df095c9e0e02f88bc7aa21cfaa44 Mon Sep 17 00:00:00 2001 From: Dipankar Maikap <45673791+dipankarmaikap@users.noreply.github.com> Date: Mon, 30 Dec 2024 15:56:07 +0530 Subject: [PATCH 01/11] chore: cleanup index.ts file --- src/index.ts | 236 +------------------------------ src/lib/healpers.ts | 54 +++++++ src/lib/storyblok-integration.ts | 177 +++++++++++++++++++++++ src/types.ts | 1 + 4 files changed, 235 insertions(+), 233 deletions(-) create mode 100644 src/lib/healpers.ts create mode 100644 src/lib/storyblok-integration.ts diff --git a/src/index.ts b/src/index.ts index a13aa874..e7f18b4d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,22 +1,3 @@ -import { vitePluginStoryblokInit } from './vite-plugins/vite-plugin-storyblok-init'; -import { vitePluginStoryblokComponents } from './vite-plugins/vite-plugin-storyblok-components'; -import { vitePluginStoryblokOptions } from './vite-plugins/vite-plugin-storyblok-options'; -import type { RichTextResolver } from '@storyblok/js'; -import { renderRichText as origRenderRichText } from '@storyblok/js'; - -import type { AstroGlobal, AstroIntegration } from 'astro'; -import type { - ISbConfig, - ISbRichtext, - ISbStoriesParams, - ISbStoryData, - SbRichTextOptions, - StoryblokBridgeConfigV2, - StoryblokClient, -} from './types'; -import { vitePluginStoryblokBridge } from './vite-plugins/vite-plugin-storyblok-bridge'; -import { storyblokLogo } from './dev-toolbar/toolbarApp'; - export { handleStoryblokMessage } from './live-preview/handleStoryblokMessage'; export * from './types'; export { toCamelCase } from './utils/toCamelCase'; @@ -26,218 +7,7 @@ export { RichTextSchema, storyblokEditable, } from '@storyblok/js'; +import storyblokIntegration from './lib/storyblok-integration'; +export { renderRichText, useStoryblok, useStoryblokApi } from './lib/healpers'; -export function useStoryblokApi(): StoryblokClient { - if (!globalThis?.storyblokApiInstance) { - throw new Error('storyblokApiInstance has not been initialized correctly'); - } - return globalThis.storyblokApiInstance; -} - -export async function useStoryblok( - slug: string, - apiOptions: ISbStoriesParams = {}, - _: StoryblokBridgeConfigV2 = {}, - Astro: AstroGlobal, -) { - if (!globalThis.storyblokApiInstance) { - throw new Error('storyblokApiInstance has not been initialized correctly'); - } - let story: ISbStoryData | null = null; - if (Astro && Astro.locals._storyblok_preview_data) { - story = Astro.locals._storyblok_preview_data; - } - else { - const { data } = await globalThis?.storyblokApiInstance?.get( - slug, - apiOptions, - ); - story = data.story; - } - return story; -} - -export function renderRichText( - data?: ISbRichtext, - options?: SbRichTextOptions, -) { - const resolverInstance: RichTextResolver | undefined - = globalThis?.storyblokApiInstance?.richTextResolver; - if (!resolverInstance) { - throw new Error( - 'Please initialize the Storyblok SDK before calling the renderRichText function', - ); - } - return origRenderRichText(data, options, resolverInstance); -} - -export interface IntegrationOptions { - /** - * The access token from your space. - */ - accessToken: string; - /** - * If you want to use your own method to fetch data from Storyblok, you can disable this behavior by setting `useCustomApi` to `true`, resulting in an optimized final bundle. - */ - useCustomApi?: boolean; - /** - * Set custom API options here (cache, region, and more). All options are documented [here](https://github.com/storyblok/storyblok-js-client#class-storyblok). - */ - apiOptions?: ISbConfig; - /** - * A boolean to enable/disable the Storyblok JavaScript Bridge or provide a StoryblokBridgeConfigV2 configuration object. Enabled by default. - */ - bridge?: boolean | StoryblokBridgeConfigV2; - /** - * An object containing your Astro components to their Storyblok equivalents. - * Example: - * ```js - * components: { - * page: "storyblok/Page", - * feature: "storyblok/Feature", - * grid: "storyblok/Grid", - * teaser: "storyblok/Teaser", - * }, - * ``` - */ - components?: Record; - /** - * The directory containing your Astro components are. Defaults to "src". - */ - componentsDir?: string; - /** - * Show a fallback component in your frontend if a component is not registered properly. - */ - enableFallbackComponent?: boolean; - /** - * Provide a path to a custom fallback component, e.g. "storyblok/customFallback". - * Please note: the path takes into account the `componentsDir` option. - */ - customFallbackComponent?: string; - /** - * A boolean to enable/disable the Experimental Live Preview feature. Disabled by default. - */ - livePreview?: boolean; -} - -export default function storyblokIntegration( - options: IntegrationOptions, -): AstroIntegration { - const resolvedOptions = { - useCustomApi: false, - bridge: true, - componentsDir: 'src', - enableFallbackComponent: false, - livePreview: false, - ...options, - }; - return { - name: '@storyblok/astro', - hooks: { - 'astro:config:setup': ({ - injectScript, - updateConfig, - addDevToolbarApp, - addMiddleware, - config, - }) => { - updateConfig({ - vite: { - plugins: [ - vitePluginStoryblokInit( - resolvedOptions.accessToken, - resolvedOptions.useCustomApi, - resolvedOptions.apiOptions, - ), - vitePluginStoryblokComponents( - resolvedOptions.componentsDir, - resolvedOptions.components, - resolvedOptions.enableFallbackComponent, - resolvedOptions.customFallbackComponent, - ), - vitePluginStoryblokOptions(resolvedOptions), - vitePluginStoryblokBridge( - resolvedOptions.livePreview, - config.output, - ), - ], - }, - }); - if (resolvedOptions.livePreview && config?.output !== 'server') { - throw new Error( - 'To utilize the Astro Storyblok Live feature, Astro must be configured to run in SSR mode. Please disable this feature or switch Astro to SSR mode.', - ); - } - injectScript( - 'page-ssr', - ` - import { storyblokApiInstance } from "virtual:storyblok-init"; - globalThis.storyblokApiInstance = storyblokApiInstance; - `, - ); - - // This is only enabled if experimentalLivePreview is disabled and bridge is enabled. - - if (resolvedOptions.bridge && !resolvedOptions.livePreview) { - let initBridge: string = ''; - - if (typeof resolvedOptions.bridge === 'object') { - const bridgeConfigurationOptions = { ...resolvedOptions.bridge }; - initBridge = `const storyblokInstance = new StoryblokBridge(${JSON.stringify( - bridgeConfigurationOptions, - )});`; - } - else { - initBridge = 'const storyblokInstance = new StoryblokBridge()'; - } - - injectScript( - 'page', - ` - import { loadStoryblokBridge } from "@storyblok/astro"; - loadStoryblokBridge().then(() => { - const { StoryblokBridge, location } = window; - ${initBridge} - - storyblokInstance.on(["published", "change"], (event) => { - if (!event.slugChanged) { - location.reload(true); - } - }); - }); - `, - ); - } - - // This is only enabled if experimentalLivePreview feature is on - if (resolvedOptions.livePreview) { - injectScript( - 'page', - ` - import { loadStoryblokBridge, handleStoryblokMessage } from "@storyblok/astro"; - import { bridgeOptions } from "virtual:storyblok-bridge"; - console.info("The Storyblok Astro live preview feature is currently in an experimental phase, and its API is subject to change in the future.") - loadStoryblokBridge().then(() => { - const { StoryblokBridge, location } = window; - if(bridgeOptions){ - const storyblokInstance = new StoryblokBridge(bridgeOptions); - storyblokInstance.on(["published", "change", "input"], handleStoryblokMessage); - }; - }); - `, - ); - addMiddleware({ - entrypoint: '@storyblok/astro/middleware.ts', - order: 'pre', - }); - } - addDevToolbarApp({ - id: 'storyblok', - name: 'Storyblok', - icon: storyblokLogo, - entrypoint: '@storyblok/astro/toolbarApp.ts', - }); - }, - }, - }; -} +export default storyblokIntegration; diff --git a/src/lib/healpers.ts b/src/lib/healpers.ts new file mode 100644 index 00000000..bcd5984b --- /dev/null +++ b/src/lib/healpers.ts @@ -0,0 +1,54 @@ +import type { + ISbRichtext, + ISbStoriesParams, + ISbStoryData, + RichTextResolver, + SbRichTextOptions, + StoryblokBridgeConfigV2, + StoryblokClient, +} from '../types'; +import type { AstroGlobal } from 'astro'; +import { renderRichText as origRenderRichText } from '@storyblok/js'; + +export function useStoryblokApi(): StoryblokClient { + if (!globalThis?.storyblokApiInstance) { + throw new Error('storyblokApiInstance has not been initialized correctly'); + } + return globalThis.storyblokApiInstance; +} + +export async function useStoryblok( + slug: string, + apiOptions: ISbStoriesParams = {}, + _: StoryblokBridgeConfigV2 = {}, + Astro: AstroGlobal +) { + if (!globalThis.storyblokApiInstance) { + throw new Error('storyblokApiInstance has not been initialized correctly'); + } + let story: ISbStoryData | null = null; + if (Astro && Astro.locals._storyblok_preview_data) { + story = Astro.locals._storyblok_preview_data; + } else { + const { data } = await globalThis?.storyblokApiInstance?.get( + slug, + apiOptions + ); + story = data.story; + } + return story; +} + +export function renderRichText( + data?: ISbRichtext, + options?: SbRichTextOptions +) { + const resolverInstance: RichTextResolver | undefined = + globalThis?.storyblokApiInstance?.richTextResolver; + if (!resolverInstance) { + throw new Error( + 'Please initialize the Storyblok SDK before calling the renderRichText function' + ); + } + return origRenderRichText(data, options, resolverInstance); +} diff --git a/src/lib/storyblok-integration.ts b/src/lib/storyblok-integration.ts new file mode 100644 index 00000000..29fd77ee --- /dev/null +++ b/src/lib/storyblok-integration.ts @@ -0,0 +1,177 @@ +import type { ISbConfig, StoryblokBridgeConfigV2 } from '@storyblok/js'; +import type { AstroIntegration } from 'astro'; +import { storyblokLogo } from '../dev-toolbar/toolbarApp'; +import { vitePluginStoryblokBridge } from '../vite-plugins/vite-plugin-storyblok-bridge'; +import { vitePluginStoryblokComponents } from '../vite-plugins/vite-plugin-storyblok-components'; +import { vitePluginStoryblokInit } from '../vite-plugins/vite-plugin-storyblok-init'; +import { vitePluginStoryblokOptions } from '../vite-plugins/vite-plugin-storyblok-options'; + +export interface IntegrationOptions { + /** + * The access token from your space. + */ + accessToken: string; + /** + * If you want to use your own method to fetch data from Storyblok, you can disable this behavior by setting `useCustomApi` to `true`, resulting in an optimized final bundle. + */ + useCustomApi?: boolean; + /** + * Set custom API options here (cache, region, and more). All options are documented [here](https://github.com/storyblok/storyblok-js-client#class-storyblok). + */ + apiOptions?: ISbConfig; + /** + * A boolean to enable/disable the Storyblok JavaScript Bridge or provide a StoryblokBridgeConfigV2 configuration object. Enabled by default. + */ + bridge?: boolean | StoryblokBridgeConfigV2; + /** + * An object containing your Astro components to their Storyblok equivalents. + * Example: + * ```js + * components: { + * page: "storyblok/Page", + * feature: "storyblok/Feature", + * grid: "storyblok/Grid", + * teaser: "storyblok/Teaser", + * }, + * ``` + */ + components?: Record; + /** + * The directory containing your Astro components are. Defaults to "src". + */ + componentsDir?: string; + /** + * Show a fallback component in your frontend if a component is not registered properly. + */ + enableFallbackComponent?: boolean; + /** + * Provide a path to a custom fallback component, e.g. "storyblok/customFallback". + * Please note: the path takes into account the `componentsDir` option. + */ + customFallbackComponent?: string; + /** + * A boolean to enable/disable the Experimental Live Preview feature. Disabled by default. + */ + livePreview?: boolean; +} + +export default function storyblokIntegration( + options: IntegrationOptions +): AstroIntegration { + const resolvedOptions = { + useCustomApi: false, + bridge: true, + componentsDir: 'src', + enableFallbackComponent: false, + livePreview: false, + ...options, + }; + return { + name: '@storyblok/astro', + hooks: { + 'astro:config:setup': ({ + injectScript, + updateConfig, + addDevToolbarApp, + addMiddleware, + config, + }) => { + updateConfig({ + vite: { + plugins: [ + vitePluginStoryblokInit( + resolvedOptions.accessToken, + resolvedOptions.useCustomApi, + resolvedOptions.apiOptions + ), + vitePluginStoryblokComponents( + resolvedOptions.componentsDir, + resolvedOptions.components, + resolvedOptions.enableFallbackComponent, + resolvedOptions.customFallbackComponent + ), + vitePluginStoryblokOptions(resolvedOptions), + vitePluginStoryblokBridge( + resolvedOptions.livePreview, + config.output + ), + ], + }, + }); + if (resolvedOptions.livePreview && config?.output !== 'server') { + throw new Error( + 'To utilize the Astro Storyblok Live feature, Astro must be configured to run in SSR mode. Please disable this feature or switch Astro to SSR mode.' + ); + } + injectScript( + 'page-ssr', + ` + import { storyblokApiInstance } from "virtual:storyblok-init"; + globalThis.storyblokApiInstance = storyblokApiInstance; + ` + ); + + // This is only enabled if experimentalLivePreview is disabled and bridge is enabled. + + if (resolvedOptions.bridge && !resolvedOptions.livePreview) { + let initBridge: string = ''; + + if (typeof resolvedOptions.bridge === 'object') { + const bridgeConfigurationOptions = { ...resolvedOptions.bridge }; + initBridge = `const storyblokInstance = new StoryblokBridge(${JSON.stringify( + bridgeConfigurationOptions + )});`; + } else { + initBridge = 'const storyblokInstance = new StoryblokBridge()'; + } + + injectScript( + 'page', + ` + import { loadStoryblokBridge } from "@storyblok/astro"; + loadStoryblokBridge().then(() => { + const { StoryblokBridge, location } = window; + ${initBridge} + + storyblokInstance.on(["published", "change"], (event) => { + if (!event.slugChanged) { + location.reload(true); + } + }); + }); + ` + ); + } + + // This is only enabled if experimentalLivePreview feature is on + if (resolvedOptions.livePreview) { + injectScript( + 'page', + ` + import { loadStoryblokBridge, handleStoryblokMessage } from "@storyblok/astro"; + import { bridgeOptions } from "virtual:storyblok-bridge"; + console.info("The Storyblok Astro live preview feature is currently in an experimental phase, and its API is subject to change in the future.") + loadStoryblokBridge().then(() => { + const { StoryblokBridge, location } = window; + if(bridgeOptions){ + const storyblokInstance = new StoryblokBridge(bridgeOptions); + storyblokInstance.on(["published", "change", "input"], handleStoryblokMessage); + }; + }); + ` + ); + addMiddleware({ + entrypoint: '@storyblok/astro/middleware.ts', + order: 'pre', + }); + } + addDevToolbarApp({ + id: 'storyblok', + name: 'Storyblok', + icon: storyblokLogo, + entrypoint: '@storyblok/astro/toolbarApp.ts', + }); + }, + }, + }; +} diff --git a/src/types.ts b/src/types.ts index 59c71d23..ba08ea58 100644 --- a/src/types.ts +++ b/src/types.ts @@ -28,4 +28,5 @@ export type { StoryblokBridgeV2, StoryblokClient, StoryblokComponentType, + RichTextResolver, } from '@storyblok/js'; From 0238c1d1576a5922b95d6572c55dc3dd89ad7ee1 Mon Sep 17 00:00:00 2001 From: Dipankar Maikap <45673791+dipankarmaikap@users.noreply.github.com> Date: Mon, 30 Dec 2024 15:59:10 +0530 Subject: [PATCH 02/11] chore: created a helper to init bridge config --- src/lib/healpers.ts | 10 ++++++++++ src/lib/storyblok-integration.ts | 14 ++------------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/lib/healpers.ts b/src/lib/healpers.ts index bcd5984b..752c242c 100644 --- a/src/lib/healpers.ts +++ b/src/lib/healpers.ts @@ -52,3 +52,13 @@ export function renderRichText( } return origRenderRichText(data, options, resolverInstance); } + +export function initStoryblokBridge( + config: boolean | StoryblokBridgeConfigV2 +): string { + if (typeof config === 'object') { + const bridgeConfig = JSON.stringify(config); + return `const storyblokInstance = new StoryblokBridge(${bridgeConfig});`; + } + return 'const storyblokInstance = new StoryblokBridge();'; +} diff --git a/src/lib/storyblok-integration.ts b/src/lib/storyblok-integration.ts index 29fd77ee..afcf9b47 100644 --- a/src/lib/storyblok-integration.ts +++ b/src/lib/storyblok-integration.ts @@ -5,6 +5,7 @@ import { vitePluginStoryblokBridge } from '../vite-plugins/vite-plugin-storyblok import { vitePluginStoryblokComponents } from '../vite-plugins/vite-plugin-storyblok-components'; import { vitePluginStoryblokInit } from '../vite-plugins/vite-plugin-storyblok-init'; import { vitePluginStoryblokOptions } from '../vite-plugins/vite-plugin-storyblok-options'; +import { initStoryblokBridge } from './healpers'; export interface IntegrationOptions { /** @@ -66,6 +67,7 @@ export default function storyblokIntegration( livePreview: false, ...options, }; + const initBridge = initStoryblokBridge(resolvedOptions.bridge); return { name: '@storyblok/astro', hooks: { @@ -114,17 +116,6 @@ export default function storyblokIntegration( // This is only enabled if experimentalLivePreview is disabled and bridge is enabled. if (resolvedOptions.bridge && !resolvedOptions.livePreview) { - let initBridge: string = ''; - - if (typeof resolvedOptions.bridge === 'object') { - const bridgeConfigurationOptions = { ...resolvedOptions.bridge }; - initBridge = `const storyblokInstance = new StoryblokBridge(${JSON.stringify( - bridgeConfigurationOptions - )});`; - } else { - initBridge = 'const storyblokInstance = new StoryblokBridge()'; - } - injectScript( 'page', ` @@ -132,7 +123,6 @@ export default function storyblokIntegration( loadStoryblokBridge().then(() => { const { StoryblokBridge, location } = window; ${initBridge} - storyblokInstance.on(["published", "change"], (event) => { if (!event.slugChanged) { location.reload(true); From 08a6fa1e6fd33550650ecc81dfe7e93ac5df36a0 Mon Sep 17 00:00:00 2001 From: Dipankar Maikap <45673791+dipankarmaikap@users.noreply.github.com> Date: Mon, 30 Dec 2024 16:08:24 +0530 Subject: [PATCH 03/11] feat!: remove `useStoryblok` function `useStoryblok` function has been deprecated and replaced with a new function, `getLiveStory`. For details on how to use getLiveStory and its capabilities, please refer to the updated documentation. --- package.json | 7 +- src/index.ts | 2 +- src/lib/healpers.ts | 17 +-- src/lib/storyblok-integration.ts | 23 ++-- src/utils/generateFinalBridgeObject.ts | 33 ------ src/utils/parseAstCode.ts | 74 ------------ .../vite-plugin-storyblok-bridge.ts | 112 ------------------ 7 files changed, 12 insertions(+), 256 deletions(-) delete mode 100644 src/utils/generateFinalBridgeObject.ts delete mode 100644 src/utils/parseAstCode.ts delete mode 100644 src/vite-plugins/vite-plugin-storyblok-bridge.ts diff --git a/package.json b/package.json index cba92558..3f65c061 100644 --- a/package.json +++ b/package.json @@ -71,9 +71,8 @@ "astro": "^3.0.0 || ^4.0.0 || ^5.0.0" }, "dependencies": { - "@storyblok/js": "3.1.9", - "camelcase": "^8.0.0", - "lodash.mergewith": "^4.6.2" + "@storyblok/js": "^3.1.9", + "camelcase": "^8.0.0" }, "devDependencies": { "@commitlint/cli": "^19.5.0", @@ -162,4 +161,4 @@ "publishConfig": { "access": "public" } -} +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index e7f18b4d..b2d020d8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,6 +8,6 @@ export { storyblokEditable, } from '@storyblok/js'; import storyblokIntegration from './lib/storyblok-integration'; -export { renderRichText, useStoryblok, useStoryblokApi } from './lib/healpers'; +export { renderRichText, getLiveStory, useStoryblokApi } from './lib/healpers'; export default storyblokIntegration; diff --git a/src/lib/healpers.ts b/src/lib/healpers.ts index 752c242c..62eae1f7 100644 --- a/src/lib/healpers.ts +++ b/src/lib/healpers.ts @@ -1,6 +1,5 @@ import type { ISbRichtext, - ISbStoriesParams, ISbStoryData, RichTextResolver, SbRichTextOptions, @@ -17,24 +16,10 @@ export function useStoryblokApi(): StoryblokClient { return globalThis.storyblokApiInstance; } -export async function useStoryblok( - slug: string, - apiOptions: ISbStoriesParams = {}, - _: StoryblokBridgeConfigV2 = {}, - Astro: AstroGlobal -) { - if (!globalThis.storyblokApiInstance) { - throw new Error('storyblokApiInstance has not been initialized correctly'); - } +export async function getLiveStory(Astro: AstroGlobal) { let story: ISbStoryData | null = null; if (Astro && Astro.locals._storyblok_preview_data) { story = Astro.locals._storyblok_preview_data; - } else { - const { data } = await globalThis?.storyblokApiInstance?.get( - slug, - apiOptions - ); - story = data.story; } return story; } diff --git a/src/lib/storyblok-integration.ts b/src/lib/storyblok-integration.ts index afcf9b47..89972cd4 100644 --- a/src/lib/storyblok-integration.ts +++ b/src/lib/storyblok-integration.ts @@ -1,7 +1,6 @@ import type { ISbConfig, StoryblokBridgeConfigV2 } from '@storyblok/js'; import type { AstroIntegration } from 'astro'; import { storyblokLogo } from '../dev-toolbar/toolbarApp'; -import { vitePluginStoryblokBridge } from '../vite-plugins/vite-plugin-storyblok-bridge'; import { vitePluginStoryblokComponents } from '../vite-plugins/vite-plugin-storyblok-components'; import { vitePluginStoryblokInit } from '../vite-plugins/vite-plugin-storyblok-init'; import { vitePluginStoryblokOptions } from '../vite-plugins/vite-plugin-storyblok-options'; @@ -93,10 +92,6 @@ export default function storyblokIntegration( resolvedOptions.customFallbackComponent ), vitePluginStoryblokOptions(resolvedOptions), - vitePluginStoryblokBridge( - resolvedOptions.livePreview, - config.output - ), ], }, }); @@ -133,21 +128,17 @@ export default function storyblokIntegration( ); } - // This is only enabled if experimentalLivePreview feature is on + // This is only enabled if LivePreview feature is on if (resolvedOptions.livePreview) { injectScript( 'page', ` - import { loadStoryblokBridge, handleStoryblokMessage } from "@storyblok/astro"; - import { bridgeOptions } from "virtual:storyblok-bridge"; - console.info("The Storyblok Astro live preview feature is currently in an experimental phase, and its API is subject to change in the future.") - loadStoryblokBridge().then(() => { - const { StoryblokBridge, location } = window; - if(bridgeOptions){ - const storyblokInstance = new StoryblokBridge(bridgeOptions); - storyblokInstance.on(["published", "change", "input"], handleStoryblokMessage); - }; - }); + import { loadStoryblokBridge, handleStoryblokMessage } from "@storyblok/astro"; + loadStoryblokBridge().then(() => { + const { StoryblokBridge, location } = window; + ${initBridge} + storyblokInstance.on(["published", "change", "input"], handleStoryblokMessage); + }); ` ); addMiddleware({ diff --git a/src/utils/generateFinalBridgeObject.ts b/src/utils/generateFinalBridgeObject.ts deleted file mode 100644 index ccad2bbd..00000000 --- a/src/utils/generateFinalBridgeObject.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { StoryblokBridgeConfigV2 } from '@storyblok/js'; -import type { RawCode } from '../vite-plugins/vite-plugin-storyblok-bridge'; - -export function generateFinalBridgeObject(rawCode: RawCode) { - const mergedOptions: StoryblokBridgeConfigV2 = { - resolveRelations: [], - }; - - function addToResolveRelations(resolveRelations?: string[] | string) { - if (resolveRelations && Array.isArray(mergedOptions.resolveRelations)) { - mergedOptions.resolveRelations.push( - ...(Array.isArray(resolveRelations) - ? resolveRelations - : [resolveRelations]), - ); - } - } - - for (const item of rawCode) { - if (item.options) { - const { apiOptions, bridgeOptions } = item.options; - addToResolveRelations(apiOptions?.resolve_relations); - if (bridgeOptions) { - const { resolveRelations, ...rest } = bridgeOptions; - addToResolveRelations(resolveRelations); - Object.assign(mergedOptions, rest); - } - } - } - mergedOptions.resolveRelations = [...new Set(mergedOptions.resolveRelations)]; - - return mergedOptions; -} diff --git a/src/utils/parseAstCode.ts b/src/utils/parseAstCode.ts deleted file mode 100644 index b0fcea5b..00000000 --- a/src/utils/parseAstCode.ts +++ /dev/null @@ -1,74 +0,0 @@ -import mergeWith from 'lodash.mergewith'; -import type { ISbStoriesParams, StoryblokBridgeConfigV2 } from '@storyblok/js'; -import type { RawCodeItemOptions } from '../vite-plugins/vite-plugin-storyblok-bridge'; -import type { Rollup } from 'vite'; -import type { SpreadElement } from 'typescript'; - -/** - * Parses through the Abstract Syntax Tree (AST) code to locate the 'useStoryblok' function and its properties. - * This functionality is crucial for generating a virtual module that can be utilized during the initialization of the Storyblok bridge. - */ - -export function parseAstRawCode(astCode: Rollup.ProgramNode) { - let obj: RawCodeItemOptions = {}; - - function customizer(_: any, srcValue: any) { - if ( - srcValue?.type === 'AwaitExpression' - && srcValue.argument.type === 'CallExpression' - && srcValue.argument.callee.type === 'Identifier' - && srcValue.argument.callee.name === 'useStoryblok' - ) { - const props = srcValue.argument.arguments; - if (props && props[1].type === 'ObjectExpression') { - const apiOptions = getAstPropToObj(props[1].properties); - obj = { - ...obj, - apiOptions, - }; - } - if (props && props[2].type === 'ObjectExpression') { - const bridgeOptions = getAstPropToObj(props[2].properties); - obj = { - ...obj, - bridgeOptions, - }; - } - } - } - - mergeWith({}, astCode, customizer); - return obj; -} - -function getAstPropToObj(properties: (SpreadElement | any)[]) { - const option: ISbStoriesParams | StoryblokBridgeConfigV2 = {}; - - return properties.reduce((options, property) => { - if (property.type !== 'Property') { - return options; - } - const { key, value } = property; - const { type } = value; - if (key.type !== 'Identifier') { - return options; - } - - if (type === 'Literal') { - options[key.name] = value.value; - } - else if (type === 'ArrayExpression') { - const arrayValues = value.elements.reduce( - (acc: any, element: { type: string; value: any }) => { - if (element.type === 'Literal' && element.value) { - return [...acc, element.value]; - } - return acc; - }, - [], - ); - options[key.name] = arrayValues; - } - return options; - }, option); -} diff --git a/src/vite-plugins/vite-plugin-storyblok-bridge.ts b/src/vite-plugins/vite-plugin-storyblok-bridge.ts deleted file mode 100644 index eb595011..00000000 --- a/src/vite-plugins/vite-plugin-storyblok-bridge.ts +++ /dev/null @@ -1,112 +0,0 @@ -import type { ISbStoriesParams, StoryblokBridgeConfigV2 } from '@storyblok/js'; -import { generateFinalBridgeObject } from '../utils/generateFinalBridgeObject'; -import { parseAstRawCode } from '../utils/parseAstCode'; -import type { Plugin, ViteDevServer } from 'vite'; - -let previousRawCode: RawCode = []; - -export interface RawCodeItem { - url: string; - options?: RawCodeItemOptions; -} - -export type RawCode = RawCodeItem[]; - -export interface RawCodeItemOptions { - apiOptions?: ISbStoriesParams; - bridgeOptions?: StoryblokBridgeConfigV2; -} -export function vitePluginStoryblokBridge( - experimentalLivePreview: boolean, - output: string, -): Plugin { - const virtualModuleId = 'virtual:storyblok-bridge'; - const resolvedVirtualModuleId = `\0${virtualModuleId}`; - if (!experimentalLivePreview || output !== 'server') { - return { - name: 'vite-plugin-storyblok-bridge', - resolveId(id: string) { - if (id === virtualModuleId) { - return resolvedVirtualModuleId; - } - }, - load(id: string) { - if (id === resolvedVirtualModuleId) { - return `export const bridgeOptions = null`; - } - }, - }; - } - let rawCode: RawCode = []; - let _server: ViteDevServer | null = null; - let restartTimeout: NodeJS.Timeout; - - return { - name: 'vite-plugin-storyblok-bridge', - async resolveId(id: string) { - if (id === virtualModuleId) { - return resolvedVirtualModuleId; - } - }, - async transform(code, id) { - const notPagesFolder - = id.includes('node_modules') && !id.includes('/pages/'); - if (notPagesFolder) { - return; - } - if (!code.includes('useStoryblok')) { - return; - } - const moduleInfo = this.getModuleInfo(id); - if (!moduleInfo?.meta?.astro) { - return; - } - const [, ...routeArray] = id.split('src/pages/'); - const url = routeArray.join('/').replace('.astro', ''); - const options = parseAstRawCode(this.parse(code)); - if (previousRawCode.length) { - rawCode = previousRawCode.filter(i => i.url !== url); - } - rawCode.push({ - url, - options, - }); - if (!_server) { - return; - } - if (restartTimeout) { - clearTimeout(restartTimeout); - } - restartTimeout = setTimeout(() => { - if (alreadyHaveThisUrl(previousRawCode, rawCode)) { - return; - } - if (previousRawCode.length !== 0) { - _server?.restart(); - console.info('Bridge options updated. Restarting...'); - } - previousRawCode = [...rawCode]; - }, 1000); - }, - async load(id: string) { - if (id === resolvedVirtualModuleId) { - /** - * Crrently we are merging all the bridge options to be one - * in the future we need to find a way to make it work per - * route basis - */ - return `export const bridgeOptions = ${JSON.stringify(generateFinalBridgeObject(rawCode))}`; - } - }, - configureServer(server) { - _server = server; - }, - }; -} - -function alreadyHaveThisUrl(a: RawCode = [], b: RawCode = []) { - return b.every(({ url, options }) => { - const aCopy = a.find(e => e?.url === url); - return aCopy && JSON.stringify(options) === JSON.stringify(aCopy.options); - }); -} From d3f4fcfaf3691f1846bca82f3a3b480fdbc50ce1 Mon Sep 17 00:00:00 2001 From: Dipankar Maikap <45673791+dipankarmaikap@users.noreply.github.com> Date: Mon, 30 Dec 2024 16:10:57 +0530 Subject: [PATCH 04/11] chore: ssr playground updated to work with getLiveStroy --- package.json | 2 +- playground/ssr/astro.config.mjs | 4 ++- playground/ssr/src/pages/[...slug].astro | 31 +++++++++++++++--------- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 3f65c061..636debbb 100644 --- a/package.json +++ b/package.json @@ -161,4 +161,4 @@ "publishConfig": { "access": "public" } -} \ No newline at end of file +} diff --git a/playground/ssr/astro.config.mjs b/playground/ssr/astro.config.mjs index 35433b91..2227a9a9 100644 --- a/playground/ssr/astro.config.mjs +++ b/playground/ssr/astro.config.mjs @@ -22,7 +22,9 @@ export default defineConfig({ }, }, useCustomApi: false, - bridge: true, + bridge: { + resolveRelations: ['featured-articles.posts'], + }, enableFallbackComponent: true, livePreview: true, components: { diff --git a/playground/ssr/src/pages/[...slug].astro b/playground/ssr/src/pages/[...slug].astro index 1d6eb43f..8973fc5b 100644 --- a/playground/ssr/src/pages/[...slug].astro +++ b/playground/ssr/src/pages/[...slug].astro @@ -1,19 +1,26 @@ --- -import { useStoryblok } from "@storyblok/astro"; -import StoryblokComponent from "@storyblok/astro/StoryblokComponent.astro"; -import BaseLayout from "../layouts/BaseLayout.astro"; +import { + getLiveStory, + useStoryblokApi, + type ISbStoryData, +} from '@storyblok/astro'; +import StoryblokComponent from '@storyblok/astro/StoryblokComponent.astro'; +import BaseLayout from '../layouts/BaseLayout.astro'; const { slug } = Astro.params; +let story: ISbStoryData | null = null; -const story = await useStoryblok( - `cdn/stories/${slug === undefined ? "home" : slug}`, - { - version: "draft", - resolve_relations: ["featured-articles.posts"], - }, - {}, - Astro -); +const liveStory = await getLiveStory(Astro); +if (liveStory) { + story = liveStory; +} else { + const sbApi = useStoryblokApi(); + const { data } = await sbApi.get(`cdn/stories/${slug || 'home'}`, { + version: 'draft', + resolve_relations: ['featured-articles.posts'], + }); + story = data?.story; +} --- From bd2c0423f7b55e701c49051d7d08c36261c762ce Mon Sep 17 00:00:00 2001 From: Dipankar Maikap <45673791+dipankarmaikap@users.noreply.github.com> Date: Mon, 30 Dec 2024 16:11:21 +0530 Subject: [PATCH 05/11] chore: lint fix --- src/index.ts | 5 +++-- src/lib/healpers.ts | 10 +++++----- src/lib/storyblok-integration.ts | 14 +++++++------- src/types.ts | 2 +- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/index.ts b/src/index.ts index b2d020d8..d5ddbbe9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,6 @@ +import storyblokIntegration from './lib/storyblok-integration'; + +export { getLiveStory, renderRichText, useStoryblokApi } from './lib/healpers'; export { handleStoryblokMessage } from './live-preview/handleStoryblokMessage'; export * from './types'; export { toCamelCase } from './utils/toCamelCase'; @@ -7,7 +10,5 @@ export { RichTextSchema, storyblokEditable, } from '@storyblok/js'; -import storyblokIntegration from './lib/storyblok-integration'; -export { renderRichText, getLiveStory, useStoryblokApi } from './lib/healpers'; export default storyblokIntegration; diff --git a/src/lib/healpers.ts b/src/lib/healpers.ts index 62eae1f7..7c5d5629 100644 --- a/src/lib/healpers.ts +++ b/src/lib/healpers.ts @@ -26,20 +26,20 @@ export async function getLiveStory(Astro: AstroGlobal) { export function renderRichText( data?: ISbRichtext, - options?: SbRichTextOptions + options?: SbRichTextOptions, ) { - const resolverInstance: RichTextResolver | undefined = - globalThis?.storyblokApiInstance?.richTextResolver; + const resolverInstance: RichTextResolver | undefined + = globalThis?.storyblokApiInstance?.richTextResolver; if (!resolverInstance) { throw new Error( - 'Please initialize the Storyblok SDK before calling the renderRichText function' + 'Please initialize the Storyblok SDK before calling the renderRichText function', ); } return origRenderRichText(data, options, resolverInstance); } export function initStoryblokBridge( - config: boolean | StoryblokBridgeConfigV2 + config: boolean | StoryblokBridgeConfigV2, ): string { if (typeof config === 'object') { const bridgeConfig = JSON.stringify(config); diff --git a/src/lib/storyblok-integration.ts b/src/lib/storyblok-integration.ts index 89972cd4..3b157572 100644 --- a/src/lib/storyblok-integration.ts +++ b/src/lib/storyblok-integration.ts @@ -56,7 +56,7 @@ export interface IntegrationOptions { } export default function storyblokIntegration( - options: IntegrationOptions + options: IntegrationOptions, ): AstroIntegration { const resolvedOptions = { useCustomApi: false, @@ -83,13 +83,13 @@ export default function storyblokIntegration( vitePluginStoryblokInit( resolvedOptions.accessToken, resolvedOptions.useCustomApi, - resolvedOptions.apiOptions + resolvedOptions.apiOptions, ), vitePluginStoryblokComponents( resolvedOptions.componentsDir, resolvedOptions.components, resolvedOptions.enableFallbackComponent, - resolvedOptions.customFallbackComponent + resolvedOptions.customFallbackComponent, ), vitePluginStoryblokOptions(resolvedOptions), ], @@ -97,7 +97,7 @@ export default function storyblokIntegration( }); if (resolvedOptions.livePreview && config?.output !== 'server') { throw new Error( - 'To utilize the Astro Storyblok Live feature, Astro must be configured to run in SSR mode. Please disable this feature or switch Astro to SSR mode.' + 'To utilize the Astro Storyblok Live feature, Astro must be configured to run in SSR mode. Please disable this feature or switch Astro to SSR mode.', ); } injectScript( @@ -105,7 +105,7 @@ export default function storyblokIntegration( ` import { storyblokApiInstance } from "virtual:storyblok-init"; globalThis.storyblokApiInstance = storyblokApiInstance; - ` + `, ); // This is only enabled if experimentalLivePreview is disabled and bridge is enabled. @@ -124,7 +124,7 @@ export default function storyblokIntegration( } }); }); - ` + `, ); } @@ -139,7 +139,7 @@ export default function storyblokIntegration( ${initBridge} storyblokInstance.on(["published", "change", "input"], handleStoryblokMessage); }); - ` + `, ); addMiddleware({ entrypoint: '@storyblok/astro/middleware.ts', diff --git a/src/types.ts b/src/types.ts index ba08ea58..696de6e6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -19,6 +19,7 @@ export type { ISbStory, // previously Story ISbStoryData, // previously StoryData ISbStoryParams, // previously StoryParams + RichTextResolver, SbBlokData, SbBlokKeyDataTypes, SbPluginFactory, @@ -28,5 +29,4 @@ export type { StoryblokBridgeV2, StoryblokClient, StoryblokComponentType, - RichTextResolver, } from '@storyblok/js'; From 3596a1535314ba4156e33f356771a70de071f7f4 Mon Sep 17 00:00:00 2001 From: Dipankar Maikap <45673791+dipankarmaikap@users.noreply.github.com> Date: Mon, 30 Dec 2024 16:12:56 +0530 Subject: [PATCH 06/11] feat!: replace default export with named export BREAKING CHANGE: The default export has been replaced with a named export when initializing the Storyblok SDK in your `astro.config.js` file. You should now use: `import { storyblok } from '@storyblok/astro';` Update your imports accordingly to ensure compatibility. --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index d5ddbbe9..f78dbb7b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,4 +11,4 @@ export { storyblokEditable, } from '@storyblok/js'; -export default storyblokIntegration; +export { storyblokIntegration as storyblok }; From 02e0fe17b105e098a175759f2a6bfe4f665400c3 Mon Sep 17 00:00:00 2001 From: Dipankar Maikap <45673791+dipankarmaikap@users.noreply.github.com> Date: Mon, 30 Dec 2024 16:14:22 +0530 Subject: [PATCH 07/11] chore: playground updated to work with named export --- playground/ssg/astro.config.mjs | 2 +- playground/ssr/astro.config.mjs | 2 +- playground/test/astro.config.mjs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/playground/ssg/astro.config.mjs b/playground/ssg/astro.config.mjs index 83cd2072..9a9b5b03 100644 --- a/playground/ssg/astro.config.mjs +++ b/playground/ssg/astro.config.mjs @@ -2,7 +2,7 @@ import { defineConfig } from 'astro/config'; import svelte from '@astrojs/svelte'; import vue from '@astrojs/vue'; import react from '@astrojs/react'; -import storyblok from '@storyblok/astro'; +import { storyblok } from '@storyblok/astro'; import tailwind from '@astrojs/tailwind'; import mkcert from 'vite-plugin-mkcert'; diff --git a/playground/ssr/astro.config.mjs b/playground/ssr/astro.config.mjs index 2227a9a9..791ad8b4 100644 --- a/playground/ssr/astro.config.mjs +++ b/playground/ssr/astro.config.mjs @@ -1,5 +1,5 @@ import { defineConfig } from 'astro/config'; -import storyblok from '@storyblok/astro'; +import { storyblok } from '@storyblok/astro'; import tailwind from '@astrojs/tailwind'; import mkcert from 'vite-plugin-mkcert'; import vercel from '@astrojs/vercel/serverless'; diff --git a/playground/test/astro.config.mjs b/playground/test/astro.config.mjs index 08735937..efd78979 100644 --- a/playground/test/astro.config.mjs +++ b/playground/test/astro.config.mjs @@ -2,7 +2,7 @@ import { defineConfig } from 'astro/config'; import svelte from '@astrojs/svelte'; import vue from '@astrojs/vue'; import react from '@astrojs/react'; -import storyblok from '@storyblok/astro'; +import { storyblok } from '@storyblok/astro'; import tailwind from '@astrojs/tailwind'; export default defineConfig({ From bde6fa70c3a97bbbc26337c5252f31cd9c94964b Mon Sep 17 00:00:00 2001 From: Dipankar Maikap <45673791+dipankarmaikap@users.noreply.github.com> Date: Mon, 30 Dec 2024 16:16:50 +0530 Subject: [PATCH 08/11] feat: add new event triggered on dom updates A new event is introduced that triggers when changes are made in the Storyblok Visual Editor. This is particularly helpful for generating CSS dynamically, as outlined in issue #864. --- src/live-preview/handleStoryblokMessage.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/live-preview/handleStoryblokMessage.ts b/src/live-preview/handleStoryblokMessage.ts index 5599273a..6e92f31b 100644 --- a/src/live-preview/handleStoryblokMessage.ts +++ b/src/live-preview/handleStoryblokMessage.ts @@ -24,6 +24,8 @@ export async function handleStoryblokMessage(event: { // Get current focused element in Storyblok const focusedElem = document.querySelector('[data-blok-focused="true"]'); updateDOMWithNewBody(currentBody, newBody, focusedElem); + // Dispatch a custom event after the body update + document.dispatchEvent(new Event('storyblok-live-preview-updated')); }; const debounceDelay = 500; // Adjust the delay as needed clearTimeout(timeout); From 465d068754bb798172bbe4d520db78ea78444efc Mon Sep 17 00:00:00 2001 From: Dipankar Maikap <45673791+dipankarmaikap@users.noreply.github.com> Date: Mon, 30 Dec 2024 16:21:31 +0530 Subject: [PATCH 09/11] chore: docs updated --- README.md | 136 ++++++++++++++++++++++++++---------------------------- 1 file changed, 66 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index 08e7bf4d..373acd1b 100644 --- a/README.md +++ b/README.md @@ -46,13 +46,13 @@ npm install @storyblok/astro ``` > [!NOTE] -> With pnpm, hoist Storyblok dependencies publicly with `.npmrc`. For more information, check pnpm documentation on [here](https://pnpm.io/npmrc). +> With pnpm, hoist Storyblok dependencies publicly with `.npmrc`. For more information, please refer to the [pnpm documentation](https://pnpm.io/npmrc). Add the following code to `astro.config.mjs` and replace the `accessToken` with the preview API token of your Storyblok space. ```js import { defineConfig } from "astro/config"; -import storyblok from "@storyblok/astro"; +import { storyblok } from "@storyblok/astro"; export default defineConfig({ integrations: [ @@ -75,6 +75,7 @@ When you initialize the integration, you can pass all [_@storyblok/js_ options]( storyblok({ accessToken: "", bridge: true, + livePreview: false, apiOptions: {}, // storyblok-js-client options components: {}, componentsDir: "src", @@ -109,15 +110,15 @@ storyblok({ ``` > [!WARNING] -> For spaces created in the United States or China, the `region` parameter **must** be specified. +> The `region` parameter **must** be specified unless the space was created in the EU. ## Getting started ### 1. Creating and linking your components to the Storyblok Visual Editor -In order to link your Astro components to their equivalents you created in Storyblok: +Link your Astro components to their equivalents created in Storyblok with the following steps. -First, you need to load them globally by specifying their name and their path in `astro.config.mjs`: +First, load the components globally by specifying their name and their path in `astro.config.mjs`: ```js components: { @@ -163,7 +164,7 @@ const { blok } = Astro.props ``` -Finally, you can use the provided `` for nested components; it will automatically render them (if they have been registered globally beforehand): +Finally, you can use the provided `` for nested components; it will automatically render them (if they are registered globally): ```jsx --- @@ -174,7 +175,7 @@ const { blok } = Astro.props ---
- {blok.body?.map(blok => {return })} + {blok.body?.map(blok => {return })}
``` @@ -214,11 +215,11 @@ const { data } = await storyblokApi.get("cdn/stories/home", { const story = data.story; --- - + ``` > [!NOTE] -> The available methods are described in the [storyblok-js-client] repository(https://github.com/storyblok/storyblok-js-client#method-storyblokget) +> The available methods are described in the [storyblok-js-client](https://github.com/storyblok/storyblok-js-client#method-storyblokget) repository. #### Dynamic Routing @@ -256,16 +257,13 @@ const { data } = await storyblokApi.get(`cdn/stories/${slug}`, { const story = data.story; --- - + ``` ### Using the Storyblok Bridge The Storyblok Bridge is enabled by default. If you would like to disable it or enable it conditionally (e.g. depending on the environment) you can set the `bridge` parameter to `true` or `false` in `astro.config.mjs`: -> [!NOTE] -> Since Astro is not a reactive JavaScript framework and renders everything as HTML, the Storyblok Bridge will not provide real-time editing as you may know it from other frameworks. However, it automatically refreshes the site for you whenever you save or publish a story. - You can also provide a `StoryblokBridgeConfigV2` configuration object to the `bridge` parameter. ```ts @@ -283,13 +281,65 @@ bridge: { - `resolveLinks` may be needed to resolve link fields. > [!NOTE] -> `resolveRelations` and `resolveLinks` will not have any effect in Astro, since the Storyblok Bridge is configured to reload the page. Thus, all the requests needed will be performed after the reload. +> `resolveRelations` and `resolveLinks` will only become effective if the live preview feature is used (`getLiveStory()`). The provided options will be used when initializing the Storyblok Bridge. You can find more information about the Storyblok Bridge and its configuration options on the [In Depth Storyblok Bridge guide](https://www.storyblok.com/docs/guide/in-depth/storyblok-latest-js-v2?utm_source=github.com&utm_medium=readme&utm_campaign=storyblok-astro). If you want to deploy a dedicated preview environment with the Bridge enabled, allowing users of the Storyblok CMS to see their changes being reflected on the frontend directly without having to rebuild the static site, you can enable Server Side Rendering for that particular use case. More information can be found in the [Astro Docs](https://docs.astro.build/en/guides/server-side-rendering/). -### Rendering Rich Text +## Enabling Live Preview for Storyblok's Visual Editor + +The Astro SDK provides a live preview feature, designed to offer real-time editing capabilities for an enhanced user experience in Storyblok's Visual Editor. + +> [!NOTE] +> To utilize the Astro Storyblok Live feature, Astro must be configured to run in SSR mode. + +To activate the live preview feature, follow these steps: + +1. Set `livePreview` to `true` in your `astro.config.mjs` file. + +```js +//astro.config.mjs +export default defineConfig({ + integrations: [ + storyblok({ + accessToken: "OsvN..", + livePreview: true, + }), + ], + output: "server", // Astro must be configured to run in SSR mode +}); +``` + +2. Additionally, use `getLiveStory` on your Astro pages. + +```jsx +//pages/[...slug].astro +--- +import { getLiveStory, useStoryblokApi } from '@storyblok/astro'; +import StoryblokComponent from "@storyblok/astro/StoryblokComponent.astro"; + +const { slug } = Astro.params; +let story = null; + +const liveStory = await getLiveStory(Astro); +if (liveStory) { + story = liveStory; +} else { + const sbApi = useStoryblokApi(); + const { data } = await sbApi.get(`cdn/stories/${slug || 'home'}`, { + version: 'draft', + resolve_relations: ['featured-articles.posts'], + }); + story = data?.story; +} +// If you are using `resolve_relations` or `resolve_links`, you must also pass them to the Bridge configuration in `astro.config.mjs`. +--- + + +``` + +## Rendering Rich Text > [!NOTE] > While @storyblok/astro provides basic richtext rendering capabilities, for advanced use cases, it is highly recommended to use [storyblok-rich-text-astro-renderer](https://github.com/NordSecurity/storyblok-rich-text-astro-renderer). @@ -306,7 +356,7 @@ const { blok } = Astro.props const renderedRichText = renderRichText(blok.text) --- -
+
``` You can also set a **custom Schema and component resolver** by passing the options as the second parameter of the `renderRichText` function: @@ -348,60 +398,6 @@ const renderedRichText = renderRichText(blok.text, { Returns the instance of the `storyblok-js-client`. -## Enabling Live Preview for Storyblok's Visual Editor - -> [!WARNING] -> This feature is currently experimental and optional. You may encounters bugs or performance issues. - -The Astro SDK now provides a live preview feature, designed to offer real-time editing capabilities for an enhanced user experience in Storyblok's Visual Editor. - -> [!NOTE] -> To utilize the Astro Storyblok Live feature, Astro must be configured to run in SSR mode. - -To activate the experimental live preview feature, follow these steps: - -1. Set `livePreview` to `true` within your `astro.config.mjs` file. - -```js -//astro.config.mjs -export default defineConfig({ - integrations: [ - storyblok({ - accessToken: "OsvN..", - livePreview: true, - }), - ], - output: "server", // Astro must be configured to run in SSR mode -}); -``` - -2. Additionally, please use `useStoryblok` on your Astro pages for story fetching. This replaces the previously used `useStoryblokApi` method. - -```jsx -//pages/[...slug].astro ---- -import { useStoryblok } from "@storyblok/astro"; -import StoryblokComponent from "@storyblok/astro/StoryblokComponent.astro"; - -const { slug } = Astro.params; - -const story = await useStoryblok( - // The slug to fetch - `cdn/stories/${slug === undefined ? "home" : slug}`, - // The API options - { - version: "draft", - }, - // The Bridge options (optional, if an empty object, null, or false are set, the API options will be considered automatically as far as applicable) - {}, - // The Astro object (essential for the live preview functionality) - Astro -); ---- - - -``` - ## The Storyblok JavaScript SDK Ecosystem ![A visual representation of the Storyblok JavaScript SDK Ecosystem](https://a.storyblok.com/f/88751/2400x1350/be4a4a4180/sdk-ecosystem.png/m/1200x0) From 3ac80f8db0f8da266d7f85c31a5238470b8273de Mon Sep 17 00:00:00 2001 From: Dipankar Maikap <45673791+dipankarmaikap@users.noreply.github.com> Date: Mon, 30 Dec 2024 16:22:34 +0530 Subject: [PATCH 10/11] chore: sdk version updated in package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 636debbb..07b6f621 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@storyblok/astro", "type": "module", - "version": "1.0.0", + "version": "6.0.0", "private": false, "packageManager": "pnpm@9.13.2", "description": "Official Astro integration for the Storyblok Headless CMS", From 9803b85a15e4149f42d2c0308a7457883f0df6e3 Mon Sep 17 00:00:00 2001 From: Dipankar Maikap <45673791+dipankarmaikap@users.noreply.github.com> Date: Mon, 30 Dec 2024 16:29:10 +0530 Subject: [PATCH 11/11] chore: updated local pnpm lock file --- pnpm-lock.yaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d08cba01..39ea525c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,14 +9,11 @@ importers: .: dependencies: '@storyblok/js': - specifier: 3.1.9 + specifier: ^3.1.9 version: 3.1.9 camelcase: specifier: ^8.0.0 version: 8.0.0 - lodash.mergewith: - specifier: ^4.6.2 - version: 4.6.2 devDependencies: '@commitlint/cli': specifier: ^19.5.0