@storyblok/astro 6.0.0-next.1 → 6.0.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/README.md CHANGED
@@ -46,13 +46,13 @@ npm install @storyblok/astro
46
46
  ```
47
47
 
48
48
  > [!NOTE]
49
- > With pnpm, hoist Storyblok dependencies publicly with `.npmrc`. For more information, check pnpm documentation on [here](https://pnpm.io/npmrc).
49
+ > With pnpm, hoist Storyblok dependencies publicly with `.npmrc`. For more information, please refer to the [pnpm documentation](https://pnpm.io/npmrc).
50
50
 
51
51
  Add the following code to `astro.config.mjs` and replace the `accessToken` with the preview API token of your Storyblok space.
52
52
 
53
53
  ```js
54
54
  import { defineConfig } from "astro/config";
55
- import storyblok from "@storyblok/astro";
55
+ import { storyblok } from "@storyblok/astro";
56
56
 
57
57
  export default defineConfig({
58
58
  integrations: [
@@ -75,6 +75,7 @@ When you initialize the integration, you can pass all [_@storyblok/js_ options](
75
75
  storyblok({
76
76
  accessToken: "<your-access-token>",
77
77
  bridge: true,
78
+ livePreview: false,
78
79
  apiOptions: {}, // storyblok-js-client options
79
80
  components: {},
80
81
  componentsDir: "src",
@@ -109,15 +110,15 @@ storyblok({
109
110
  ```
110
111
 
111
112
  > [!WARNING]
112
- > For spaces created in the United States or China, the `region` parameter **must** be specified.
113
+ > The `region` parameter **must** be specified unless the space was created in the EU.
113
114
 
114
115
  ## Getting started
115
116
 
116
117
  ### 1. Creating and linking your components to the Storyblok Visual Editor
117
118
 
118
- In order to link your Astro components to their equivalents you created in Storyblok:
119
+ Link your Astro components to their equivalents created in Storyblok with the following steps.
119
120
 
120
- First, you need to load them globally by specifying their name and their path in `astro.config.mjs`:
121
+ First, load the components globally by specifying their name and their path in `astro.config.mjs`:
121
122
 
122
123
  ```js
123
124
  components: {
@@ -163,7 +164,7 @@ const { blok } = Astro.props
163
164
  </div>
164
165
  ```
165
166
 
166
- Finally, you can use the provided `<StoryblokComponent>` for nested components; it will automatically render them (if they have been registered globally beforehand):
167
+ Finally, you can use the provided `<StoryblokComponent>` for nested components; it will automatically render them (if they are registered globally):
167
168
 
168
169
  ```jsx
169
170
  ---
@@ -174,7 +175,7 @@ const { blok } = Astro.props
174
175
  ---
175
176
 
176
177
  <main {...storyblokEditable(blok)}>
177
- {blok.body?.map(blok => {return <StoryblokComponent blok="{blok}" />})}
178
+ {blok.body?.map(blok => {return <StoryblokComponent blok={blok} />})}
178
179
  </main>
179
180
  ```
180
181
 
@@ -214,11 +215,11 @@ const { data } = await storyblokApi.get("cdn/stories/home", {
214
215
  const story = data.story;
215
216
  ---
216
217
 
217
- <StoryblokComponent blok="{story.content}" />
218
+ <StoryblokComponent blok={story.content} />
218
219
  ```
219
220
 
220
221
  > [!NOTE]
221
- > The available methods are described in the [storyblok-js-client] repository(https://github.com/storyblok/storyblok-js-client#method-storyblokget)
222
+ > The available methods are described in the [storyblok-js-client](https://github.com/storyblok/storyblok-js-client#method-storyblokget) repository.
222
223
 
223
224
  #### Dynamic Routing
224
225
 
@@ -256,16 +257,13 @@ const { data } = await storyblokApi.get(`cdn/stories/${slug}`, {
256
257
  const story = data.story;
257
258
  ---
258
259
 
259
- <StoryblokComponent blok="{story.content}" />
260
+ <StoryblokComponent blok={story.content} />
260
261
  ```
261
262
 
262
263
  ### Using the Storyblok Bridge
263
264
 
264
265
  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`:
265
266
 
266
- > [!NOTE]
267
- > 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.
268
-
269
267
  You can also provide a `StoryblokBridgeConfigV2` configuration object to the `bridge` parameter.
270
268
 
271
269
  ```ts
@@ -283,13 +281,77 @@ bridge: {
283
281
  - `resolveLinks` may be needed to resolve link fields.
284
282
 
285
283
  > [!NOTE]
286
- > `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.
284
+ > `resolveRelations` and `resolveLinks` will only become effective if the live preview feature is used (`getLiveStory()`).
287
285
 
288
286
  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).
289
287
 
290
288
  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/).
291
289
 
292
- ### Rendering Rich Text
290
+ ## Enabling Live Preview for Storyblok's Visual Editor
291
+
292
+ 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.
293
+
294
+ > [!NOTE]
295
+ > To utilize the Astro Storyblok Live feature, Astro must be configured to run in SSR mode.
296
+
297
+ To activate the live preview feature, follow these steps:
298
+
299
+ 1. Set `livePreview` to `true` in your `astro.config.mjs` file.
300
+
301
+ ```js
302
+ //astro.config.mjs
303
+ export default defineConfig({
304
+ integrations: [
305
+ storyblok({
306
+ accessToken: "OsvN..",
307
+ livePreview: true,
308
+ }),
309
+ ],
310
+ output: "server", // Astro must be configured to run in SSR mode
311
+ });
312
+ ```
313
+
314
+ 2. Additionally, use `getLiveStory` on your Astro pages.
315
+
316
+ ```jsx
317
+ //pages/[...slug].astro
318
+ ---
319
+ import { getLiveStory, useStoryblokApi } from '@storyblok/astro';
320
+ import StoryblokComponent from "@storyblok/astro/StoryblokComponent.astro";
321
+
322
+ const { slug } = Astro.params;
323
+ let story = null;
324
+
325
+ const liveStory = await getLiveStory(Astro);
326
+ if (liveStory) {
327
+ story = liveStory;
328
+ } else {
329
+ const sbApi = useStoryblokApi();
330
+ const { data } = await sbApi.get(`cdn/stories/${slug || 'home'}`, {
331
+ version: 'draft',
332
+ resolve_relations: ['featured-articles.posts'],
333
+ });
334
+ story = data?.story;
335
+ }
336
+ // If you are using `resolve_relations` or `resolve_links`, you must also pass them to the Bridge configuration in `astro.config.mjs`.
337
+ ---
338
+
339
+ <StoryblokComponent blok={story.content} />
340
+ ```
341
+ ## Dom update event
342
+
343
+ ```js
344
+ //page.astro
345
+
346
+ <script>
347
+ document.addEventListener('storyblok-live-preview-updated', () => {
348
+ // Here is the callback we could run code every time the body is updated via live preview
349
+ console.log('Live preview: body updated');
350
+ // Example regenerated all your css
351
+ });
352
+ </script>
353
+ ```
354
+ ## Rendering Rich Text
293
355
 
294
356
  > [!NOTE]
295
357
  > 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 +368,7 @@ const { blok } = Astro.props
306
368
  const renderedRichText = renderRichText(blok.text)
307
369
  ---
308
370
 
309
- <div set:html="{renderedRichText}"></div>
371
+ <div set:html={renderedRichText}></div>
310
372
  ```
311
373
 
312
374
  You can also set a **custom Schema and component resolver** by passing the options as the second parameter of the `renderRichText` function:
@@ -348,60 +410,6 @@ const renderedRichText = renderRichText(blok.text, {
348
410
 
349
411
  Returns the instance of the `storyblok-js-client`.
350
412
 
351
- ## Enabling Live Preview for Storyblok's Visual Editor
352
-
353
- > [!WARNING]
354
- > This feature is currently experimental and optional. You may encounters bugs or performance issues.
355
-
356
- 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.
357
-
358
- > [!NOTE]
359
- > To utilize the Astro Storyblok Live feature, Astro must be configured to run in SSR mode.
360
-
361
- To activate the experimental live preview feature, follow these steps:
362
-
363
- 1. Set `livePreview` to `true` within your `astro.config.mjs` file.
364
-
365
- ```js
366
- //astro.config.mjs
367
- export default defineConfig({
368
- integrations: [
369
- storyblok({
370
- accessToken: "OsvN..",
371
- livePreview: true,
372
- }),
373
- ],
374
- output: "server", // Astro must be configured to run in SSR mode
375
- });
376
- ```
377
-
378
- 2. Additionally, please use `useStoryblok` on your Astro pages for story fetching. This replaces the previously used `useStoryblokApi` method.
379
-
380
- ```jsx
381
- //pages/[...slug].astro
382
- ---
383
- import { useStoryblok } from "@storyblok/astro";
384
- import StoryblokComponent from "@storyblok/astro/StoryblokComponent.astro";
385
-
386
- const { slug } = Astro.params;
387
-
388
- const story = await useStoryblok(
389
- // The slug to fetch
390
- `cdn/stories/${slug === undefined ? "home" : slug}`,
391
- // The API options
392
- {
393
- version: "draft",
394
- },
395
- // The Bridge options (optional, if an empty object, null, or false are set, the API options will be considered automatically as far as applicable)
396
- {},
397
- // The Astro object (essential for the live preview functionality)
398
- Astro
399
- );
400
- ---
401
-
402
- <StoryblokComponent blok={story.content} />
403
- ```
404
-
405
413
  ## The Storyblok JavaScript SDK Ecosystem
406
414
 
407
415
  ![A visual representation of the Storyblok JavaScript SDK Ecosystem](https://a.storyblok.com/f/88751/2400x1350/be4a4a4180/sdk-ecosystem.png/m/1200x0)
@@ -436,4 +444,4 @@ Please review our [Contributing Guidelines](https://github.com/storyblok/.github
436
444
 
437
445
  This project employs [semantic-release](https://semantic-release.gitbook.io/semantic-release/) to generate new versions based on commit messages, following the Angular Commit Message Convention.
438
446
 
439
- When using playgrounds during development, ensure the build is running in watch mode for an efficient workflow.
447
+ When using playgrounds during development, ensure the build is running in watch mode for an efficient workflow.
@@ -1,7 +1,7 @@
1
1
  import { ISbRichtext, ISbStoryData, SbRichTextOptions, StoryblokBridgeConfigV2, StoryblokClient } from '../types';
2
2
  import { AstroGlobal } from 'astro';
3
3
  export declare function useStoryblokApi(): StoryblokClient;
4
- export declare function getLiveStory(Astro: AstroGlobal): Promise<ISbStoryData<import('@storyblok/js').StoryblokComponentType<string> & {
4
+ export declare function getLiveStory(Astro: Readonly<AstroGlobal>): Promise<ISbStoryData<import('@storyblok/js').StoryblokComponentType<string> & {
5
5
  [index: string]: any;
6
6
  }> | null>;
7
7
  export declare function renderRichText(data?: ISbRichtext, options?: SbRichTextOptions): string;
@@ -6,7 +6,7 @@ export const onRequest = defineMiddleware(async ({ locals, request }, next) => {
6
6
  // First do a basic check if its coming from within storyblok
7
7
  const isStoryblokRequest
8
8
  = url.searchParams.has('_storyblok')
9
- && url.searchParams.has('_storyblok_c');
9
+ && url.searchParams.has('_storyblok_c');
10
10
 
11
11
  if (isStoryblokRequest) {
12
12
  try {
@@ -609,22 +609,20 @@ function je(r) {
609
609
  `
610
610
  ), e.bridge && !e.livePreview && o(
611
611
  "page",
612
- `
613
- import { loadStoryblokBridge } from "@storyblok/astro";
614
- loadStoryblokBridge().then(() => {
615
- const { StoryblokBridge, location } = window;
616
- ${t}
617
- storyblokInstance.on(["published", "change"], (event) => {
618
- if (!event.slugChanged) {
619
- location.reload(true);
620
- }
621
- });
612
+ `import { loadStoryblokBridge } from "@storyblok/astro";
613
+ loadStoryblokBridge().then(() => {
614
+ const { StoryblokBridge, location } = window;
615
+ ${t}
616
+ storyblokInstance.on(["published", "change"], (event) => {
617
+ if (!event.slugChanged) {
618
+ location.reload(true);
619
+ }
622
620
  });
621
+ });
623
622
  `
624
623
  ), e.livePreview && (o(
625
624
  "page",
626
- `
627
- import { loadStoryblokBridge, handleStoryblokMessage } from "@storyblok/astro";
625
+ `import { loadStoryblokBridge, handleStoryblokMessage } from "@storyblok/astro";
628
626
  loadStoryblokBridge().then(() => {
629
627
  const { StoryblokBridge, location } = window;
630
628
  ${t}
@@ -43,19 +43,17 @@ Detailed information can be found here: https://github.com/storyblok/storyblok-a
43
43
  }`),""}optimizeImages(e,t){let o=0,s=0,n="",a="";typeof t!="boolean"&&(typeof t.width=="number"&&t.width>0&&(n+=`width="${t.width}" `,o=t.width),typeof t.height=="number"&&t.height>0&&(n+=`height="${t.height}" `,s=t.height),(t.loading==="lazy"||t.loading==="eager")&&(n+=`loading="${t.loading}" `),typeof t.class=="string"&&t.class.length>0&&(n+=`class="${t.class}" `),t.filters&&(typeof t.filters.blur=="number"&&t.filters.blur>=0&&t.filters.blur<=100&&(a+=`:blur(${t.filters.blur})`),typeof t.filters.brightness=="number"&&t.filters.brightness>=-100&&t.filters.brightness<=100&&(a+=`:brightness(${t.filters.brightness})`),t.filters.fill&&(t.filters.fill.match(/[0-9A-F]{6}/gi)||t.filters.fill==="transparent")&&(a+=`:fill(${t.filters.fill})`),t.filters.format&&["webp","png","jpeg"].includes(t.filters.format)&&(a+=`:format(${t.filters.format})`),typeof t.filters.grayscale=="boolean"&&t.filters.grayscale&&(a+=":grayscale()"),typeof t.filters.quality=="number"&&t.filters.quality>=0&&t.filters.quality<=100&&(a+=`:quality(${t.filters.quality})`),t.filters.rotate&&[90,180,270].includes(t.filters.rotate)&&(a+=`:rotate(${t.filters.rotate})`),a.length>0&&(a=`/filters${a}`))),n.length>0&&(e=e.replace(/<img/g,`<img ${n.trim()}`));const l=o>0||s>0||a.length>0?`${o}x${s}${a}`:"";return e=e.replace(/a.storyblok.com\/f\/(\d+)\/([^.]+)\.(gif|jpg|jpeg|png|tif|bmp)/g,`a.storyblok.com/f/$1/$2.$3/m/${l}`),typeof t!="boolean"&&(t.sizes||t.srcset)&&(e=e.replace(/<img.*?src=["|'](.*?)["|']/g,i=>{var c,d;const f=i.match(/a.storyblok.com\/f\/(\d+)\/([^.]+)\.(gif|jpg|jpeg|png|tif|bmp)/g);if(f&&f.length>0){const h={srcset:(c=t.srcset)==null?void 0:c.map(g=>{if(typeof g=="number")return`//${f}/m/${g}x0${a} ${g}w`;if(typeof g=="object"&&g.length===2){let w=0,R=0;return typeof g[0]=="number"&&(w=g[0]),typeof g[1]=="number"&&(R=g[1]),`//${f}/m/${w}x${R}${a} ${w}w`}return""}).join(", "),sizes:(d=t.sizes)==null?void 0:d.map(g=>g).join(", ")};let k="";return h.srcset&&(k+=`srcset="${h.srcset}" `),h.sizes&&(k+=`sizes="${h.sizes}" `),i.replace(/<img/g,`<img ${k.trim()}`)}return i})),e}renderNode(e){const t=[];e.marks&&e.marks.forEach(s=>{const n=this.getMatchingMark(s);n&&n.tag!==""&&t.push(this.renderOpeningTag(n.tag))});const o=this.getMatchingNode(e);return o&&o.tag&&t.push(this.renderOpeningTag(o.tag)),e.content?e.content.forEach(s=>{t.push(this.renderNode(s))}):e.text?t.push(me(e.text)):o&&o.singleTag?t.push(this.renderTag(o.singleTag," /")):o&&o.html?t.push(o.html):e.type==="emoji"&&t.push(this.renderEmoji(e)),o&&o.tag&&t.push(this.renderClosingTag(o.tag)),e.marks&&e.marks.slice(0).reverse().forEach(s=>{const n=this.getMatchingMark(s);n&&n.tag!==""&&t.push(this.renderClosingTag(n.tag))}),t.join("")}renderTag(e,t){return e.constructor===String?`<${e}${t}>`:e.map(o=>{if(o.constructor===String)return`<${o}${t}>`;{let s=`<${o.tag}`;if(o.attrs){for(const n in o.attrs)if(Object.prototype.hasOwnProperty.call(o.attrs,n)){const a=o.attrs[n];a!==null&&(s+=` ${n}="${a}"`)}}return`${s}${t}>`}}).join("")}renderOpeningTag(e){return this.renderTag(e,"")}renderClosingTag(e){return e.constructor===String?`</${e}>`:e.slice(0).reverse().map(t=>t.constructor===String?`</${t}>`:`</${t.tag}>`).join("")}getMatchingNode(e){const t=this.nodes[e.type];if(typeof t=="function")return t(e)}getMatchingMark(e){const t=this.marks[e.type];if(typeof t=="function")return t(e)}renderEmoji(e){if(e.attrs.emoji)return e.attrs.emoji;const t=[{tag:"img",attrs:{src:e.attrs.fallbackImage,draggable:"false",loading:"lazy",align:"absmiddle"}}];return this.renderTag(t," /")}}const L=ke,we=r=>{if(typeof r!="object"||typeof r._editable>"u")return{};try{const e=JSON.parse(r._editable.replace(/^<!--#storyblok#/,"").replace(/-->$/,""));return e?{"data-blok-c":JSON.stringify(e),"data-blok-uid":e.id+"-"+e.uid}:{}}catch{return{}}};let $e,ve="https://app.storyblok.com/f/storyblok-v2-latest.js";const Ce=(r,e)=>{r.addNode("blok",t=>{let o="";return t.attrs.body.forEach(s=>{o+=e(s.component,s)}),{html:o}})},Se=r=>!r||!(r!=null&&r.content.some(e=>e.content||e.type==="blok"||e.type==="horizontal_rule")),Ie=(r,e,t)=>{let o=t||$e;if(!o){console.error("Please initialize the Storyblok SDK before calling the renderRichText function");return}return Se(r)?"":(e&&(o=new L(e.schema),e.resolver&&Ce(o,e.resolver)),o.render(r,{},!1))},Te=()=>U(ve);function Ae(){if(!(globalThis!=null&&globalThis.storyblokApiInstance))throw new Error("storyblokApiInstance has not been initialized correctly");return globalThis.storyblokApiInstance}async function Ee(r){let e=null;return r&&r.locals._storyblok_preview_data&&(e=r.locals._storyblok_preview_data),e}function Le(r,e){var o;const t=(o=globalThis==null?void 0:globalThis.storyblokApiInstance)==null?void 0:o.richTextResolver;if(!t)throw new Error("Please initialize the Storyblok SDK before calling the renderRichText function");return Ie(r,e,t)}function xe(r){return typeof r=="object"?`const storyblokInstance = new StoryblokBridge(${JSON.stringify(r)});`:"const storyblokInstance = new StoryblokBridge();"}function Re(r){const e={useCustomApi:!1,bridge:!0,componentsDir:"src",enableFallbackComponent:!1,livePreview:!1,...r},t=xe(e.bridge);return{name:"@storyblok/astro",hooks:{"astro:config:setup":({injectScript:o,updateConfig:s,addDevToolbarApp:n,addMiddleware:a,config:l})=>{if(s({vite:{plugins:[F(e.accessToken,e.useCustomApi,e.apiOptions),B(e.componentsDir,e.components,e.enableFallbackComponent,e.customFallbackComponent),D(e)]}}),e.livePreview&&(l==null?void 0:l.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.");o("page-ssr",`
44
44
  import { storyblokApiInstance } from "virtual:storyblok-init";
45
45
  globalThis.storyblokApiInstance = storyblokApiInstance;
46
- `),e.bridge&&!e.livePreview&&o("page",`
47
- import { loadStoryblokBridge } from "@storyblok/astro";
48
- loadStoryblokBridge().then(() => {
49
- const { StoryblokBridge, location } = window;
50
- ${t}
51
- storyblokInstance.on(["published", "change"], (event) => {
52
- if (!event.slugChanged) {
53
- location.reload(true);
54
- }
55
- });
46
+ `),e.bridge&&!e.livePreview&&o("page",`import { loadStoryblokBridge } from "@storyblok/astro";
47
+ loadStoryblokBridge().then(() => {
48
+ const { StoryblokBridge, location } = window;
49
+ ${t}
50
+ storyblokInstance.on(["published", "change"], (event) => {
51
+ if (!event.slugChanged) {
52
+ location.reload(true);
53
+ }
56
54
  });
57
- `),e.livePreview&&(o("page",`
58
- import { loadStoryblokBridge, handleStoryblokMessage } from "@storyblok/astro";
55
+ });
56
+ `),e.livePreview&&(o("page",`import { loadStoryblokBridge, handleStoryblokMessage } from "@storyblok/astro";
59
57
  loadStoryblokBridge().then(() => {
60
58
  const { StoryblokBridge, location } = window;
61
59
  ${t}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@storyblok/astro",
3
3
  "type": "module",
4
- "version": "6.0.0-next.1",
4
+ "version": "6.0.0",
5
5
  "private": false,
6
6
  "packageManager": "pnpm@9.13.2",
7
7
  "description": "Official Astro integration for the Storyblok Headless CMS",
@@ -71,7 +71,7 @@
71
71
  "astro": "^3.0.0 || ^4.0.0 || ^5.0.0"
72
72
  },
73
73
  "dependencies": {
74
- "@storyblok/js": "^3.1.9",
74
+ "@storyblok/js": "3.1.9",
75
75
  "camelcase": "^8.0.0"
76
76
  },
77
77
  "devDependencies": {
@@ -80,6 +80,7 @@
80
80
  "@cypress/vite-dev-server": "^5.2.0",
81
81
  "@rollup/plugin-dynamic-import-vars": "^2.1.5",
82
82
  "@storyblok/eslint-config": "^0.3.0",
83
+ "@types/lodash.mergewith": "^4.6.9",
83
84
  "@types/node": "22.10.2",
84
85
  "astro": "^5.0.9",
85
86
  "cypress": "^13.15.2",