@storyblok/nuxt 2.0.4 → 3.0.0-beta.1

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
@@ -27,37 +27,45 @@
27
27
  </a>
28
28
  </p>
29
29
 
30
- **Note**: This plugin is for Nuxt 2. [Check out the @next version for Nuxt 3](https://github.com/storyblok/storyblok-nuxt/tree/next)
30
+ **Note**: This plugin is for Nuxt 3. [Check out the docs for Nuxt 2 version](https://github.com/storyblok/storyblok-nuxt/tree/master)
31
31
 
32
32
  ## 🚀 Usage
33
33
 
34
- > If you are first-time user of the Storyblok, read the [Getting Started](https://www.storyblok.com/docs/guide/getting-started?utm_source=github.com&utm_medium=readme&utm_campaign=storyblok-nuxt) guide to get a project ready in less than 5 minutes.
34
+ > If you are first-time user of the Storyblok, read the [Getting Started](https://www.storyblok.com/docs/guide/getting-started?utm_source=github.com&utm_medium=readme&utm_campaign=storyblok-vue) guide to get a project ready in less than 5 minutes.
35
35
 
36
36
  ### Installation
37
37
 
38
- Install `@storyblok/nuxt` and `axios` as its peer dependency:
38
+ Install `@storyblok/nuxt@next` and `axios` as its peer dependency:
39
39
 
40
40
  ```bash
41
- npm install --save-dev @storyblok/nuxt axios
42
- # yarn add -D @storyblok/nuxt axios
41
+ npm install --save-dev @storyblok/nuxt@next axios
42
+ # yarn add -D @storyblok/nuxt@next axios
43
43
  ```
44
44
 
45
- > _Hint: You don't have to install Axios if you already installed Axios module of Nuxt._
46
-
47
45
  Add following code to modules section of `nuxt.config.js` and replace the accessToken with API token from Storyblok space.
48
46
 
49
47
  ```js
50
- {
48
+ import { defineNuxtConfig } from "nuxt3";
49
+
50
+ export default defineNuxtConfig({
51
51
  modules: [
52
- [
53
- "@storyblok/nuxt",
54
- {
55
- accessToken: "YOUR_PREVIEW_TOKEN",
56
- cacheProvider: "memory",
57
- },
58
- ],
59
- ];
60
- }
52
+ ["@storyblok/nuxt", { accessToken: "YOUR_ACCESS_TOKEN" }]
53
+ // ...
54
+ ]
55
+ });
56
+ ```
57
+
58
+ You can also use the `storyblok` config if you prefer:
59
+
60
+ ```js
61
+ import { defineNuxtConfig } from "nuxt3";
62
+
63
+ export default defineNuxtConfig({
64
+ modules: ["@storyblok/nuxt"],
65
+ storyblok: {
66
+ accessToken: "YOUR_ACCESS_TOKEN"
67
+ }
68
+ });
61
69
  ```
62
70
 
63
71
  ### Getting started
@@ -65,15 +73,66 @@ Add following code to modules section of `nuxt.config.js` and replace the access
65
73
  This module adds two objects to the the Nuxt.js context.
66
74
 
67
75
  1. $storyapi: The [Storyblok API client](https://github.com/storyblok/storyblok-nuxt).
68
- 2. $storybridge: A loader for the [Storyblok JS bridge](https://www.storyblok.com/docs/Guides/storyblok-latest-js?utm_source=github.com&utm_medium=readme&utm_campaign=storyblok-nuxt) that is responsible for adding the editing interface to your website.
76
+ 2. $storybridge: A loader for the [Storyblok JS bridge](https://www.storyblok.com/docs/Guides/storyblok-latest-js) that is responsible for adding the editing interface to your website.
77
+
78
+ ### Examples
79
+
80
+ #### Fetching data
81
+
82
+ Use `useStoryApi` composable, auto-imported when using `<script setup>`:
83
+
84
+ ```html
85
+ <template>
86
+ <div>
87
+ <p v-for="story in stories" :key="story.id">{{ story.name }}</p>
88
+ </div>
89
+ </template>
90
+
91
+ <script setup>
92
+ const storyapi = useStoryApi();
93
+ const { data } = await storyapi.get("cdn/stories", { version: "draft" });
94
+ </script>
95
+ ```
96
+
97
+ If you need to import them manually, do it from `composables`:
98
+
99
+ ```js
100
+ import { useStoryApi, useStoryBridge } from "@storyblok/nuxt/composables";
101
+ ```
102
+
103
+ #### Listen to Storyblok editor events
69
104
 
70
- Example of fetching data of the homepage and listening to the change events of the JS bridge:
105
+ Use `useStoryBridge`. You need to pass the story id as first param, and a callback function as second param to update the new story:
106
+
107
+ ```html
108
+ <script setup>
109
+ const storyapi = useStoryApi();
110
+ const { data } = await storyapi.get("cdn/stories/home", { version: "draft" });
111
+ const state = reactive({ stories: data.story });
112
+
113
+ onMounted(() => {
114
+ useStoryBridge(state.story.id, story => (state.story = story));
115
+ });
116
+ </script>
117
+ ```
118
+
119
+ You can pass [Bridge options](https://www.storyblok.com/docs/Guides/storyblok-latest-js?utm_source=github.com&utm_medium=readme&utm_campaign=storyblok-nuxt) as a third parameter as well:
120
+
121
+ ```js
122
+ useStoryBridge(state.story.id, (story) => (state.story = story), {
123
+ resolveRelations: ["Article.author"]
124
+ });
125
+ ```
126
+
127
+ ### Options API
128
+
129
+ Traditional Option API is used just like in [version 2](/../../):
71
130
 
72
131
  ```js
73
132
  export default {
74
133
  data() {
75
134
  return {
76
- story: { content: {} },
135
+ story: { content: {} }
77
136
  };
78
137
  },
79
138
  mounted() {
@@ -99,7 +158,7 @@ export default {
99
158
  asyncData(context) {
100
159
  return context.app.$storyapi
101
160
  .get("cdn/stories/home", {
102
- version: "draft",
161
+ version: "draft"
103
162
  })
104
163
  .then((res) => {
105
164
  return res.data;
@@ -109,17 +168,17 @@ export default {
109
168
  console.error(res);
110
169
  context.error({
111
170
  statusCode: 404,
112
- message: "Failed to receive content form api",
171
+ message: "Failed to receive content form api"
113
172
  });
114
173
  } else {
115
174
  console.error(res.response.data);
116
175
  context.error({
117
176
  statusCode: res.response.status,
118
- message: res.response.data,
177
+ message: res.response.data
119
178
  });
120
179
  }
121
180
  });
122
- },
181
+ }
123
182
  };
124
183
  ```
125
184
 
@@ -127,73 +186,23 @@ export default {
127
186
 
128
187
  ### API
129
188
 
130
- Like described above, this package includes two objects into Nuxt.js context:
189
+ Like described above, this package includes two composablestwo objects into Nuxt.js context:
131
190
 
132
- #### $storyapi
191
+ #### useStoryApi()
133
192
 
134
- This object is a instance of StoryblokClient. You can check the documentation about StoryblokClient in the repository: https://github.com/storyblok/storyblok-nuxt
193
+ It's basically a convenient way to access `$storyapi`
135
194
 
136
- #### $storybridge(successCallback, errorCallback)
195
+ #### useStoryBridge(storyId, callback, bridgeOptions)
137
196
 
138
- You can use this object to load the [Storyblok JS Bridge](https://www.storyblok.com/docs/Guides/storyblok-latest-js?utm_source=github.com&utm_medium=readme&utm_campaign=storyblok-nuxt). In the success callback you will it have available in the window variable StoryblokBridge.
197
+ Use this one-line composable to cover the most common use case: updating the story when any kind of change happens on Storyblok side. It's the equivalent to the [Options API code you've seen above](/../../tree/next#options-api).
139
198
 
140
- ### Migrate from 1.x to 2.x
141
-
142
- #### Listening to Visual Editor events in 1.x
143
-
144
- Most of our tutorials and recordings still using following deprecated approach for real-time editing and listening to Storyblok's Visual Editor events. **This approach can be used only with 1.x version of the storyblok-nuxt.**
145
-
146
- ```js
147
- export default {
148
- mounted() {
149
- // Use the input event for instant update of content
150
- this.$storybridge.on("input", (event) => {
151
- if (event.story.id === this.story.id) {
152
- this.story.content = event.story.content;
153
- }
154
- });
155
- // Use the bridge to listen the events
156
- this.$storybridge.on(["published", "change"], (event) => {
157
- this.$nuxt.$router.go({
158
- path: this.$nuxt.$router.currentRoute,
159
- force: true,
160
- });
161
- });
162
- },
163
- };
164
- ```
165
-
166
- #### Listening to Visual Editor events in 2.x
199
+ #### $storyapi
167
200
 
168
- The recommended approach for 2.x storyblok-nuxt plugin.
201
+ This object is a instance of StoryblokClient. You can check the documentation about StoryblokClient in the repository: https://github.com/storyblok/storyblok-nuxt
169
202
 
170
- ```js
171
- export default {
172
- mounted() {
173
- this.$storybridge(
174
- () => {
175
- const storyblokInstance = new StoryblokBridge();
203
+ #### $storybridge(successCallback, errorCallback)
176
204
 
177
- storyblokInstance.on(["input", "published", "change"], (event) => {
178
- if (event.action == "input") {
179
- if (event.story.id === this.story.id) {
180
- this.story.content = event.story.content;
181
- }
182
- } else {
183
- this.$nuxt.$router.go({
184
- path: this.$nuxt.$router.currentRoute,
185
- force: true,
186
- });
187
- }
188
- });
189
- },
190
- (error) => {
191
- console.error(error);
192
- }
193
- );
194
- },
195
- };
196
- ```
205
+ You can use this object to load the [Storyblok JS Bridge](https://www.storyblok.com/docs/Guides/storyblok-latest-js?utm_source=github.com&utm_medium=readme&utm_campaign=storyblok-nuxt). In the success callback you will it have available in the window variable StoryblokBridge.
197
206
 
198
207
  ## 🔗 Related Links
199
208
 
@@ -206,6 +215,7 @@ export default {
206
215
  ### Support
207
216
 
208
217
  - Bugs or Feature Requests? [Submit an issue](/../../issues/new);
218
+
209
219
  - Do you have questions about Storyblok or you need help? [Join our Discord Community](https://discord.gg/jKrbAMz).
210
220
 
211
221
  ### Contributing
@@ -0,0 +1,24 @@
1
+ import { useNuxtApp } from "#app";
2
+ import { useRouter } from "vue-router";
3
+ var useStoryBridge = (id, cb, options = {}) => {
4
+ if (typeof window === "undefined")
5
+ return;
6
+ const app = useNuxtApp();
7
+ const router = useRouter();
8
+ app.$storybridge(() => {
9
+ const sbBridge = new window.StoryblokBridge(options);
10
+ sbBridge.on(["input", "published", "change"], (event) => {
11
+ if (event.action == "input" && event.story.id === id)
12
+ cb(event.story);
13
+ else
14
+ router.go({ path: router.currentRoute, force: true });
15
+ });
16
+ }, (error) => {
17
+ console.error(error);
18
+ });
19
+ };
20
+ var useStoryApi = () => {
21
+ const app = useNuxtApp();
22
+ return app.$storyapi;
23
+ };
24
+ export { useStoryApi, useStoryBridge };
@@ -0,0 +1 @@
1
+ (function(e,r){typeof exports=="object"&&typeof module!="undefined"?r(exports,require("#app"),require("vue-router")):typeof define=="function"&&define.amd?define(["exports","#app","vue-router"],r):(e=typeof globalThis!="undefined"?globalThis:e||self,r(e.composables={},e._app,e.VueRouter))})(this,function(e,r,n){"use strict";var s=(t,d,f={})=>{if(typeof window=="undefined")return;const c=r.useNuxtApp(),u=n.useRouter();c.$storybridge(()=>{new window.StoryblokBridge(f).on(["input","published","change"],o=>{o.action=="input"&&o.story.id===t?d(o.story):u.go({path:u.currentRoute,force:!0})})},i=>{console.error(i)})},p=()=>r.useNuxtApp().$storyapi;e.useStoryApi=p,e.useStoryBridge=s,Object.defineProperty(e,"__esModule",{value:!0}),e[Symbol.toStringTag]="Module"});
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "@storyblok/nuxt-composables",
3
+ "version": "0.0.0",
4
+ "main": "./dist/composables.umd.js",
5
+ "module": "./dist/composables.es.js",
6
+ "exports": {
7
+ ".": {
8
+ "import": "./dist/composables.es.js",
9
+ "require": "./dist/composables.umd.js"
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,2 @@
1
+ export { default as useStoryBridge } from "./useStoryBridge";
2
+ export { default as useStoryApi } from "./useStoryApi";
@@ -0,0 +1,6 @@
1
+ import { useNuxtApp } from "#app";
2
+
3
+ export default () => {
4
+ const app = useNuxtApp();
5
+ return app.$storyapi;
6
+ };
@@ -0,0 +1,22 @@
1
+ import { useNuxtApp } from "#app";
2
+ import { useRouter } from "vue-router";
3
+
4
+ export default (id, cb, options = {}) => {
5
+ if (typeof window === "undefined") return;
6
+ const app = useNuxtApp();
7
+ const router = useRouter();
8
+
9
+ app.$storybridge(
10
+ () => {
11
+ const sbBridge = new window.StoryblokBridge(options);
12
+
13
+ sbBridge.on(["input", "published", "change"], (event) => {
14
+ if (event.action == "input" && event.story.id === id) cb(event.story);
15
+ else router.go({ path: router.currentRoute, force: true });
16
+ });
17
+ },
18
+ (error) => {
19
+ console.error(error);
20
+ }
21
+ );
22
+ };
package/module.js CHANGED
@@ -1,12 +1,53 @@
1
- const path = require("path");
1
+ import path from "path";
2
+ import { defineNuxtModule, addPluginTemplate } from "@nuxt/kit";
2
3
 
3
- module.exports = function nuxtStoryblok(moduleOptions) {
4
- let options = Object.assign({}, this.options.storyblok, moduleOptions);
5
-
6
- this.addPlugin({
7
- src: path.resolve(__dirname, "templates/plugin.js"),
8
- options,
9
- });
4
+ export const noopTransform = () => {
5
+ return {
6
+ props: [],
7
+ needRuntime: true,
8
+ };
10
9
  };
11
10
 
12
- module.exports.meta = require("./package.json");
11
+ export default defineNuxtModule({
12
+ name: "@storyblok/nuxt",
13
+ configKey: "storyblok",
14
+ defaults: {},
15
+ setup(options) {
16
+ addPluginTemplate({
17
+ src: path.resolve(__dirname, "templates/plugin.js"),
18
+ options,
19
+ });
20
+ },
21
+ // We need to add the v-editable SSR impl or it will crash
22
+ // info here: https://github.com/vuejs/vue-next/issues/3298
23
+ hooks: {
24
+ "build:before": ({ nuxt }, config) => {
25
+ // If it's using Webpack
26
+ const isWebpack = nuxt.options.vite === false;
27
+ const isProduction = nuxt.options.dev === false;
28
+ if (isWebpack || (!isWebpack && isProduction)) {
29
+ config.transpile = [
30
+ ...(config.transpile || []),
31
+ "@storyblok/vue",
32
+ "storyblok-js-client",
33
+ ];
34
+ }
35
+
36
+ const opts = config.loaders.vue.compilerOptions;
37
+ const transforms = opts.directiveTransforms || {};
38
+ opts.directiveTransforms = { ...transforms, editable: noopTransform };
39
+ },
40
+ "autoImports:extend": (autoimports) => {
41
+ autoimports.push({
42
+ from: "@storyblok/nuxt/composables",
43
+ name: "useStoryBridge",
44
+ as: "useStoryBridge",
45
+ });
46
+ autoimports.push({
47
+ from: "@storyblok/nuxt/composables",
48
+ name: "useStoryApi",
49
+ as: "useStoryApi",
50
+ });
51
+ },
52
+ },
53
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@storyblok/nuxt",
3
- "version": "2.0.4",
3
+ "version": "3.0.0-beta.1",
4
4
  "description": "Storyblok Nuxt.js module",
5
5
  "license": "MIT",
6
6
  "contributors": [
@@ -11,20 +11,24 @@
11
11
  "main": "./module.js",
12
12
  "repository": "https://github.com/storyblok/storyblok-nuxt",
13
13
  "scripts": {
14
+ "build": "vite build",
15
+ "test": "npm run test:unit && npm run test:e2e",
16
+ "test:unit": "jest __tests__",
14
17
  "cy:open": "cypress open",
15
18
  "cy:run": "cypress run",
16
19
  "playground:run": "cd ../playground && npm run start",
17
20
  "pretest:e2e": "cd ../playground && npm run build",
18
21
  "test:e2e": "start-server-and-test playground:run http://localhost:3000 cy:run",
19
22
  "test:e2e-watch": "start-server-and-test playground:run http://localhost:3000 cy:open",
20
- "prepublishOnly": "cp ../README.md ./"
23
+ "prepublishOnly": "npm run build && cp ../README.md ./"
21
24
  },
22
25
  "files": [
23
26
  "module.js",
24
- "templates"
27
+ "templates",
28
+ "composables"
25
29
  ],
26
30
  "dependencies": {
27
- "@storyblok/vue": "^2.0.0",
31
+ "@storyblok/vue": "^3.0.0",
28
32
  "storyblok-js-client": "^4.1.5"
29
33
  },
30
34
  "devDependencies": {
@@ -33,7 +37,8 @@
33
37
  "cypress": "^8.3.0",
34
38
  "eslint-plugin-cypress": "^2.12.1",
35
39
  "jest": "^26.6.3",
36
- "start-server-and-test": "^1.14.0"
40
+ "start-server-and-test": "^1.14.0",
41
+ "vite": "^2.6.14"
37
42
  },
38
43
  "eslintConfig": {
39
44
  "env": {
@@ -1,82 +1,67 @@
1
- import Vue from 'vue'
2
- import StoryblokClient from 'storyblok-js-client'
3
- import StoryblokVue from '@storyblok/vue'
1
+ import StoryblokVue from "@storyblok/vue";
2
+ import StoryblokClient from 'storyblok-js-client/source'
3
+ import { defineNuxtPlugin } from '#app'
4
4
 
5
- const loadScript = function(src, cb) {
6
- if (document.getElementById('storyblok-javascript-bridge')) {
7
- return cb()
5
+ const loadScript = (src, cb) => {
6
+ if (document.getElementById("storyblok-javascript-bridge")) {
7
+ return cb();
8
8
  }
9
9
 
10
- var script = document.createElement('script')
11
- script.async = true
12
- script.src = src
13
- script.id = 'storyblok-javascript-bridge'
10
+ const script = document.createElement("script");
11
+ script.async = true;
12
+ script.src = src;
13
+ script.id = "storyblok-javascript-bridge";
14
14
 
15
- script.onerror = function() {
16
- cb(new Error('Failed to load' + src))
17
- }
18
-
19
- script.onload = function() {
20
- cb()
21
- }
15
+ script.onerror = function () {
16
+ cb(new Error("Failed to load" + src));
17
+ };
22
18
 
23
- document.getElementsByTagName('head')[0].appendChild(script)
24
- }
19
+ script.onload = function () {
20
+ cb();
21
+ };
25
22
 
26
- let doLoadScript = true
23
+ document.getElementsByTagName("head")[0].appendChild(script);
24
+ };
27
25
 
28
- const Client = {
29
- install () {
30
- if (!Vue.prototype.$storyapi) {
31
- Vue.prototype.$storyapi = new StoryblokClient({
32
- accessToken: '<%= options.accessToken %>',
33
- cache: {
34
- clear: 'auto',
35
- type: '<%= options.cacheProvider || 'memory' %>'
36
- },
37
- timeout: <%= options.timeout || 0 %><% if (options.region) { %>,
38
- region: '<%= options.region %>'<% } %><% if (typeof options.https !== 'undefined') { %>,
39
- https: <%= options.https %><% } %>
40
- }<% if (typeof options.endpoint !== 'undefined') { %>, '<%= options.endpoint %>'<% } %>)
26
+ let doLoadScript = true;
41
27
 
42
- Vue.prototype.$storybridge = function(cb, errorCb) {
43
- if (typeof errorCb !== 'function') {
44
- errorCb = function() {}
45
- }
46
-
47
- if (window.location == window.parent.location) {
48
- errorCb('You are not in the edit mode.')
49
- return
50
- }
51
-
52
- if (!doLoadScript) {
53
- if (!window.StoryblokBridge) {
54
- errorCb('The Storyblok bridge script is already loading.')
55
- return
56
- }
57
- cb()
58
- return
59
- }
28
+ const initStoryapi = () => {
29
+ return new StoryblokClient({
30
+ accessToken: '<%= options.accessToken %>',
31
+ cache: {
32
+ clear: 'auto',
33
+ type: '<%= options.cacheProvider || 'memory' %>'
34
+ },
35
+ timeout: <%= options.timeout || 0 %><% if (options.region) { %>,
36
+ region: '<%= options.region %>'<% } %><% if (typeof options.https !== 'undefined') { %>,
37
+ https: <%= options.https %><% } %>
38
+ }<% if (typeof options.endpoint !== 'undefined') { %>, '<%= options.endpoint %>'<% } %>)
39
+ }
60
40
 
61
- doLoadScript = false
62
- loadScript('https://app.storyblok.com/f/storyblok-v2-latest.js', cb)
63
- }
41
+ const storybridge = (cb, errorCb) => {
42
+ if (typeof errorCb !== "function") {
43
+ errorCb = function () {};
44
+ }
45
+ if (window.location == window.parent.location) {
46
+ errorCb("You are not in the edit mode.");
47
+ return;
48
+ }
49
+ if (!doLoadScript) {
50
+ if (!window.StoryblokBridge) {
51
+ errorCb("The Storyblok bridge script is already loading.");
52
+ return;
64
53
  }
54
+ cb();
55
+ return;
65
56
  }
66
- }
67
-
68
- Vue.use(Client)
69
- Vue.use(StoryblokVue)
57
+ doLoadScript = false;
58
+ loadScript("https://app.storyblok.com/f/storyblok-v2-latest.js", cb);
59
+ };
70
60
 
71
- export default (ctx) => {
72
- const { app, store } = ctx
73
61
 
74
- app.$storyapi = Vue.prototype.$storyapi
75
- ctx.$storyapi = Vue.prototype.$storyapi
76
- app.$storybridge = Vue.prototype.$storybridge
77
- ctx.$storybridge = Vue.prototype.$storybridge
78
-
79
- if (store) {
80
- store.$storyapi = Vue.prototype.$storyapi
81
- }
82
- }
62
+ export default defineNuxtPlugin(({vueApp, provide}) => {
63
+ vueApp.use(StoryblokVue);
64
+
65
+ provide("storyapi", initStoryapi());
66
+ provide("storybridge", storybridge);
67
+ })