@storyblok/astro 4.0.5 → 4.1.0-alpha.2
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 +50 -0
- package/dist/storyblok-astro.js +21 -10
- package/dist/storyblok-astro.mjs +979 -318
- package/dist/types/components/FallbackComponent.d.ts +1 -2
- package/dist/types/components/RichTextRenderer.d.ts +1 -2
- package/dist/types/components/StoryblokComponent.d.ts +1 -2
- package/dist/types/index.d.ts +12 -5
- package/dist/types/live-preview/handleStoryblokMessage.d.ts +11 -0
- package/dist/types/live-preview/middleware.d.ts +1 -0
- package/dist/types/types.d.ts +1 -1
- package/dist/types/utils/generateFinalBridgeObject.d.ts +8 -0
- package/dist/types/utils/parseAstCode.d.ts +1 -0
- package/dist/types/vite-plugins/vite-plugin-storyblok-bridge.d.ts +2 -0
- package/dist/types/vite-plugins/vite-plugin-storyblok-components.d.ts +1 -2
- package/dist/types/vite-plugins/vite-plugin-storyblok-init.d.ts +2 -3
- package/dist/types/vite-plugins/vite-plugin-storyblok-options.d.ts +1 -2
- package/live-preview/handleStoryblokMessage.ts +75 -0
- package/live-preview/middleware.ts +11 -0
- package/package.json +19 -7
- package/utils/generateFinalBridgeObject.ts +42 -0
- package/utils/parseAstCode.ts +43 -0
package/dist/types/index.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
export { storyblokEditable, loadStoryblokBridge, RichTextResolver, RichTextSchema, } from
|
|
1
|
+
import type { AstroGlobal, AstroIntegration } from "astro";
|
|
2
|
+
import type { ISbConfig, ISbRichtext, ISbStoriesParams, ISbStoryData, SbRichTextOptions, StoryblokBridgeConfigV2, StoryblokClient } from "./types";
|
|
3
|
+
export { handleStoryblokMessage } from "./live-preview/handleStoryblokMessage";
|
|
4
|
+
export { storyblokEditable, loadStoryblokBridge, RichTextResolver, RichTextSchema, } from "@storyblok/js";
|
|
5
5
|
export declare function useStoryblokApi(): StoryblokClient;
|
|
6
|
+
export declare function useStoryblok(slug: string, apiOptions: ISbStoriesParams, bridgeOptions: StoryblokBridgeConfigV2, Astro: AstroGlobal): Promise<ISbStoryData<import("@storyblok/js").StoryblokComponentType<string> & {
|
|
7
|
+
[index: string]: any;
|
|
8
|
+
}>>;
|
|
6
9
|
export declare function renderRichText(data?: ISbRichtext, options?: SbRichTextOptions): string;
|
|
7
10
|
export type IntegrationOptions = {
|
|
8
11
|
/**
|
|
@@ -47,6 +50,10 @@ export type IntegrationOptions = {
|
|
|
47
50
|
* Please note: the path takes into account the `componentsDir` option.
|
|
48
51
|
*/
|
|
49
52
|
customFallbackComponent?: string;
|
|
53
|
+
/**
|
|
54
|
+
* A boolean to enable/disable the Experimental Live Preview feature. Disabled by default.
|
|
55
|
+
*/
|
|
56
|
+
livePreview?: boolean;
|
|
50
57
|
};
|
|
51
58
|
export default function storyblokIntegration(options: IntegrationOptions): AstroIntegration;
|
|
52
|
-
export * from
|
|
59
|
+
export * from "./types";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ISbStoryData } from "@storyblok/js";
|
|
2
|
+
/**
|
|
3
|
+
* Our current tests indicate that the post request section
|
|
4
|
+
* is the bottleneck in terms of time consumption.
|
|
5
|
+
* We should explore alternative methods to optimize
|
|
6
|
+
* this process and improve efficiency.
|
|
7
|
+
*/
|
|
8
|
+
export declare function handleStoryblokMessage(event: {
|
|
9
|
+
action: string;
|
|
10
|
+
story: ISbStoryData;
|
|
11
|
+
}): Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const onRequest: import("astro").MiddlewareHandler;
|
package/dist/types/types.d.ts
CHANGED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ISbStoriesParams, StoryblokBridgeConfigV2 } from "@storyblok/js";
|
|
2
|
+
export interface RawCodeItem {
|
|
3
|
+
options?: {
|
|
4
|
+
apiOptions?: ISbStoriesParams;
|
|
5
|
+
bridgeOptions?: StoryblokBridgeConfigV2;
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
export declare function generateFinalBridgeObject(rawCode: RawCodeItem[]): StoryblokBridgeConfigV2;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function parseAstRawCode(astCode: any): any;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import type { ISbConfig } from "../types";
|
|
2
|
+
import type { Plugin } from "vite";
|
|
4
3
|
export declare function vitePluginStoryblokInit(accessToken: string, useCustomApi: boolean, apiOptions: ISbConfig): Plugin;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { ISbStoryData } from "@storyblok/js";
|
|
2
|
+
|
|
3
|
+
let timeout: NodeJS.Timeout;
|
|
4
|
+
/**
|
|
5
|
+
* Our current tests indicate that the post request section
|
|
6
|
+
* is the bottleneck in terms of time consumption.
|
|
7
|
+
* We should explore alternative methods to optimize
|
|
8
|
+
* this process and improve efficiency.
|
|
9
|
+
*/
|
|
10
|
+
export async function handleStoryblokMessage(event: {
|
|
11
|
+
action: string;
|
|
12
|
+
story: ISbStoryData;
|
|
13
|
+
}) {
|
|
14
|
+
const { action, story } = event || {};
|
|
15
|
+
|
|
16
|
+
if (action === "input" && story) {
|
|
17
|
+
// Debounce the getNewHTMLBody function
|
|
18
|
+
const debouncedGetNewHTMLBody = async () => {
|
|
19
|
+
const newBody = await getNewHTMLBody(story);
|
|
20
|
+
const currentBody = document.body as HTMLElement;
|
|
21
|
+
if (newBody.outerHTML === currentBody.outerHTML) return;
|
|
22
|
+
// Get current focused element in Storyblok
|
|
23
|
+
const focusedElem = document.querySelector('[data-blok-focused="true"]');
|
|
24
|
+
updateDOMWithNewBody(currentBody, newBody, focusedElem);
|
|
25
|
+
};
|
|
26
|
+
const debounceDelay = 500; // Adjust the delay as needed
|
|
27
|
+
clearTimeout(timeout);
|
|
28
|
+
timeout = setTimeout(debouncedGetNewHTMLBody, debounceDelay);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (["published", "change"].includes(event?.action)) {
|
|
32
|
+
location.reload();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function updateDOMWithNewBody(
|
|
37
|
+
currentBody: HTMLElement,
|
|
38
|
+
newBody: HTMLElement,
|
|
39
|
+
focusedElem: Element | null
|
|
40
|
+
) {
|
|
41
|
+
if (focusedElem) {
|
|
42
|
+
//Get the [data-blok-uid] of the focused element in storyblok
|
|
43
|
+
const focusedElementID = focusedElem.getAttribute("data-blok-uid");
|
|
44
|
+
//Now find the same element by above [data-blok-uid] in our new virtual HTML page
|
|
45
|
+
const newDomFocusElem = newBody.querySelector(
|
|
46
|
+
`[data-blok-uid="${focusedElementID}"]`
|
|
47
|
+
);
|
|
48
|
+
if (newDomFocusElem) {
|
|
49
|
+
// Add the [data-blok-focused] attribute to the above element
|
|
50
|
+
newDomFocusElem.setAttribute("data-blok-focused", "true");
|
|
51
|
+
// console.log("Doing partial replace");
|
|
52
|
+
focusedElem.replaceWith(newDomFocusElem);
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
// console.log("Doing full replace");
|
|
56
|
+
currentBody.replaceWith(newBody);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function getNewHTMLBody(story: ISbStoryData) {
|
|
61
|
+
const result = await fetch(location.href, {
|
|
62
|
+
method: "POST",
|
|
63
|
+
body: JSON.stringify({
|
|
64
|
+
...story,
|
|
65
|
+
is_storyblok_preview: true,
|
|
66
|
+
}),
|
|
67
|
+
headers: {
|
|
68
|
+
"Content-Type": "application/json",
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
const html = await result.text();
|
|
72
|
+
const parser = new DOMParser();
|
|
73
|
+
const doc = parser.parseFromString(html, "text/html");
|
|
74
|
+
return doc.body;
|
|
75
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { defineMiddleware } from "astro/middleware";
|
|
2
|
+
|
|
3
|
+
export const onRequest = defineMiddleware(async ({ locals, request }, next) => {
|
|
4
|
+
if (request["method"] === "POST") {
|
|
5
|
+
const requestBody = await request.json();
|
|
6
|
+
if (requestBody && requestBody["is_storyblok_preview"]) {
|
|
7
|
+
locals["_storyblok_preview_data"] = requestBody;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
return next();
|
|
11
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@storyblok/astro",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.1.0-alpha.2",
|
|
4
4
|
"description": "Official Astro integration for the Storyblok Headless CMS",
|
|
5
5
|
"main": "./dist/storyblok-astro.js",
|
|
6
6
|
"module": "./dist/storyblok-astro.mjs",
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"dist",
|
|
9
9
|
"components",
|
|
10
10
|
"dev-toolbar",
|
|
11
|
-
"live-preview"
|
|
11
|
+
"live-preview",
|
|
12
|
+
"utils"
|
|
12
13
|
],
|
|
13
14
|
"exports": {
|
|
14
15
|
".": {
|
|
@@ -35,6 +36,11 @@
|
|
|
35
36
|
"types": "./dev-toolbar/toolbarApp.ts",
|
|
36
37
|
"import": "./dev-toolbar/toolbarApp.ts",
|
|
37
38
|
"require": "./dev-toolbar/toolbarApp.ts"
|
|
39
|
+
},
|
|
40
|
+
"./middleware.ts": {
|
|
41
|
+
"types": "./live-preview/middleware.ts",
|
|
42
|
+
"import": "./live-preview/middleware.ts",
|
|
43
|
+
"require": "./live-preview/middleware.ts"
|
|
38
44
|
}
|
|
39
45
|
},
|
|
40
46
|
"types": "./dist/types/index.d.ts",
|
|
@@ -51,19 +57,21 @@
|
|
|
51
57
|
},
|
|
52
58
|
"dependencies": {
|
|
53
59
|
"@storyblok/js": "^3.0.8",
|
|
54
|
-
"camelcase": "^8.0.0"
|
|
60
|
+
"camelcase": "^8.0.0",
|
|
61
|
+
"lodash.mergewith": "^4.6.2"
|
|
55
62
|
},
|
|
56
63
|
"devDependencies": {
|
|
57
64
|
"@cypress/vite-dev-server": "^5.0.7",
|
|
58
65
|
"@rollup/plugin-dynamic-import-vars": "^2.1.2",
|
|
59
|
-
"@types/
|
|
66
|
+
"@types/lodash.mergewith": "^4.6.9",
|
|
67
|
+
"@types/node": "20.12.11",
|
|
60
68
|
"astro": "^4.6.3",
|
|
61
|
-
"cypress": "^13.8.
|
|
62
|
-
"eslint-plugin-cypress": "^2.
|
|
69
|
+
"cypress": "^13.8.1",
|
|
70
|
+
"eslint-plugin-cypress": "^3.2.0",
|
|
63
71
|
"start-server-and-test": "^2.0.3",
|
|
64
72
|
"typescript": "5.4.5",
|
|
65
73
|
"vite": "^5.2.10",
|
|
66
|
-
"vite-plugin-dts": "^3.
|
|
74
|
+
"vite-plugin-dts": "^3.7.3"
|
|
67
75
|
},
|
|
68
76
|
"peerDependencies": {
|
|
69
77
|
"astro": "^3.0.0 || ^4.0.0"
|
|
@@ -88,6 +96,10 @@
|
|
|
88
96
|
{
|
|
89
97
|
"name": "next",
|
|
90
98
|
"prerelease": true
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"name": "alpha",
|
|
102
|
+
"prerelease": true
|
|
91
103
|
}
|
|
92
104
|
]
|
|
93
105
|
},
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ISbStoriesParams, StoryblokBridgeConfigV2 } from "@storyblok/js";
|
|
2
|
+
|
|
3
|
+
export interface RawCodeItem {
|
|
4
|
+
options?: {
|
|
5
|
+
apiOptions?: ISbStoriesParams;
|
|
6
|
+
bridgeOptions?: StoryblokBridgeConfigV2;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
export function generateFinalBridgeObject(
|
|
10
|
+
rawCode: RawCodeItem[]
|
|
11
|
+
): StoryblokBridgeConfigV2 {
|
|
12
|
+
let mergedOptions = {
|
|
13
|
+
resolveRelations: [],
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function addToResolveRelations(
|
|
17
|
+
resolveRelations: string[] | string | undefined
|
|
18
|
+
) {
|
|
19
|
+
if (resolveRelations) {
|
|
20
|
+
if (Array.isArray(resolveRelations)) {
|
|
21
|
+
mergedOptions.resolveRelations.push(...resolveRelations);
|
|
22
|
+
} else {
|
|
23
|
+
mergedOptions.resolveRelations.push(resolveRelations);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
for (const item of rawCode) {
|
|
29
|
+
if (item.options) {
|
|
30
|
+
const { apiOptions, bridgeOptions } = item.options;
|
|
31
|
+
addToResolveRelations(apiOptions?.resolve_relations);
|
|
32
|
+
if (bridgeOptions) {
|
|
33
|
+
const { resolveRelations, ...rest } = bridgeOptions;
|
|
34
|
+
addToResolveRelations(resolveRelations);
|
|
35
|
+
Object.assign(mergedOptions, rest);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
mergedOptions.resolveRelations = [...new Set(mergedOptions.resolveRelations)];
|
|
40
|
+
|
|
41
|
+
return mergedOptions;
|
|
42
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import mergeWith from "lodash.mergewith";
|
|
2
|
+
|
|
3
|
+
export function parseAstRawCode(astCode) {
|
|
4
|
+
let obj;
|
|
5
|
+
function customizer(_, srcValue) {
|
|
6
|
+
if (
|
|
7
|
+
srcValue?.type === "AwaitExpression" &&
|
|
8
|
+
srcValue?.argument?.callee?.name === "useStoryblok"
|
|
9
|
+
) {
|
|
10
|
+
const props = srcValue?.argument?.arguments;
|
|
11
|
+
if (props[1]?.type === "ObjectExpression") {
|
|
12
|
+
const apiOptions = getAstPropToObj(props[1]?.properties);
|
|
13
|
+
obj = {
|
|
14
|
+
...obj,
|
|
15
|
+
apiOptions,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
if (props[2]?.type === "ObjectExpression") {
|
|
19
|
+
const bridgeOptions = getAstPropToObj(props[2]?.properties);
|
|
20
|
+
obj = {
|
|
21
|
+
...obj,
|
|
22
|
+
bridgeOptions,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
mergeWith({}, astCode, customizer);
|
|
29
|
+
return obj;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function getAstPropToObj(properties) {
|
|
33
|
+
return properties.reduce((options, { key, value }) => {
|
|
34
|
+
const { type } = value;
|
|
35
|
+
options[key.name] =
|
|
36
|
+
type === "Literal"
|
|
37
|
+
? value.value
|
|
38
|
+
: type === "ArrayExpression"
|
|
39
|
+
? value.elements.map((v) => v.value)
|
|
40
|
+
: value.value;
|
|
41
|
+
return options;
|
|
42
|
+
}, {});
|
|
43
|
+
}
|