@mattgrill/storyblok-11ty 2.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/dist/data.d.ts +79 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +653 -0
- package/dist/plugin/index.d.ts +17 -0
- package/dist/plugin/liquid.d.ts +12 -0
- package/dist/plugin/nunjuks.d.ts +11 -0
- package/dist/types.d.ts +139 -0
- package/dist/utils.d.ts +11 -0
- package/package.json +86 -0
- package/readme.md +419 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,653 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
"use strict";
|
|
3
|
+
// The require scope
|
|
4
|
+
var __webpack_require__ = {};
|
|
5
|
+
|
|
6
|
+
// webpack/runtime/compat_get_default_export
|
|
7
|
+
(() => {
|
|
8
|
+
// getDefaultExport function for compatibility with non-ESM modules
|
|
9
|
+
__webpack_require__.n = (module) => {
|
|
10
|
+
var getter = module && module.__esModule ?
|
|
11
|
+
() => (module['default']) :
|
|
12
|
+
() => (module);
|
|
13
|
+
__webpack_require__.d(getter, { a: getter });
|
|
14
|
+
return getter;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
})();
|
|
18
|
+
// webpack/runtime/define_property_getters
|
|
19
|
+
(() => {
|
|
20
|
+
__webpack_require__.d = (exports, definition) => {
|
|
21
|
+
for(var key in definition) {
|
|
22
|
+
if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
|
|
23
|
+
Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
})();
|
|
28
|
+
// webpack/runtime/has_own_property
|
|
29
|
+
(() => {
|
|
30
|
+
__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
|
|
31
|
+
})();
|
|
32
|
+
// webpack/runtime/make_namespace_object
|
|
33
|
+
(() => {
|
|
34
|
+
// define __esModule on exports
|
|
35
|
+
__webpack_require__.r = (exports) => {
|
|
36
|
+
if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
|
|
37
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
38
|
+
}
|
|
39
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
var __webpack_exports__ = {};
|
|
43
|
+
// ESM COMPAT FLAG
|
|
44
|
+
__webpack_require__.r(__webpack_exports__);
|
|
45
|
+
|
|
46
|
+
// EXPORTS
|
|
47
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
48
|
+
StoryblokTo11tyPlugin: () => (/* reexport */ StoryblokTo11tyPlugin),
|
|
49
|
+
StoryblokTo11tyData: () => (/* reexport */ StoryblokTo11tyData),
|
|
50
|
+
"default": () => (/* binding */ src)
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
;// CONCATENATED MODULE: ./src/utils.ts
|
|
54
|
+
/**
|
|
55
|
+
* Utility class for common operations
|
|
56
|
+
*/ class Utils {
|
|
57
|
+
/**
|
|
58
|
+
* Convert text to URL-friendly slug
|
|
59
|
+
* @param text - The text to slugify
|
|
60
|
+
* @returns Slugified string
|
|
61
|
+
*/ static slugify(text) {
|
|
62
|
+
return (text || '').toString().toLowerCase().trim().replace(/\s+/g, '-') // Replace spaces with -
|
|
63
|
+
.replace(/&/g, '-and-') // Replace & with 'and'
|
|
64
|
+
.replace(/[^\w-]+/g, '') // Remove all non-word chars
|
|
65
|
+
.replace(/-+/g, '-'); // Replace multiple - with single -
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
;// CONCATENATED MODULE: external "@storyblok/richtext"
|
|
70
|
+
const richtext_namespaceObject = require("@storyblok/richtext");
|
|
71
|
+
;// CONCATENATED MODULE: ./src/plugin/liquid.ts
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
// Create a default rich text renderer
|
|
75
|
+
const renderRichText = (0,richtext_namespaceObject.richTextResolver)().render;
|
|
76
|
+
/**
|
|
77
|
+
* LiquidPlugin is the class to add custom tags for liquid templates
|
|
78
|
+
*/ class LiquidPlugin {
|
|
79
|
+
blocks_folder;
|
|
80
|
+
config;
|
|
81
|
+
blocks;
|
|
82
|
+
data;
|
|
83
|
+
constructor(config, params = {}){
|
|
84
|
+
this.blocks_folder = params.blocks_folder ? `${params.blocks_folder.replace(/^\//g, '')}` : 'blocks/';
|
|
85
|
+
this.config = config;
|
|
86
|
+
}
|
|
87
|
+
addTags() {
|
|
88
|
+
this.config.addLiquidTag('sb_blocks', (liquidEngine)=>{
|
|
89
|
+
const engine = liquidEngine;
|
|
90
|
+
return {
|
|
91
|
+
parse: (tagToken)=>{
|
|
92
|
+
this.blocks = tagToken.args;
|
|
93
|
+
},
|
|
94
|
+
render: async (scope)=>{
|
|
95
|
+
const blocksValue = this.blocks || '';
|
|
96
|
+
let blocks = engine.evalValue(blocksValue, scope);
|
|
97
|
+
if (blocks && typeof blocks === 'object' && !Array.isArray(blocks)) {
|
|
98
|
+
blocks = [
|
|
99
|
+
blocks
|
|
100
|
+
];
|
|
101
|
+
}
|
|
102
|
+
if (!blocks || !Array.isArray(blocks)) {
|
|
103
|
+
return '';
|
|
104
|
+
}
|
|
105
|
+
let html_output = '';
|
|
106
|
+
for(let index = 0; index < blocks.length; index++){
|
|
107
|
+
const block = blocks[index];
|
|
108
|
+
block.component = Utils.slugify(block.component);
|
|
109
|
+
const code = `{% include ${this.blocks_folder + block.component} %}`;
|
|
110
|
+
const tpl = engine.parse(code);
|
|
111
|
+
const html = await engine.render(tpl, {
|
|
112
|
+
block: block
|
|
113
|
+
});
|
|
114
|
+
html_output += html;
|
|
115
|
+
}
|
|
116
|
+
return Promise.resolve(html_output);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
});
|
|
120
|
+
this.config.addLiquidTag('sb_richtext', (liquidEngine)=>{
|
|
121
|
+
const engine = liquidEngine;
|
|
122
|
+
return {
|
|
123
|
+
parse: (tagToken)=>{
|
|
124
|
+
this.data = tagToken.args;
|
|
125
|
+
},
|
|
126
|
+
render: async (scope)=>{
|
|
127
|
+
const dataValue = this.data || '';
|
|
128
|
+
const data = engine.evalValue(dataValue, scope);
|
|
129
|
+
if (typeof data === 'string') {
|
|
130
|
+
return data;
|
|
131
|
+
}
|
|
132
|
+
if (typeof data === 'undefined' || data === null) {
|
|
133
|
+
return '';
|
|
134
|
+
}
|
|
135
|
+
let output = '';
|
|
136
|
+
if (typeof data === 'object' && data.content && Array.isArray(data.content)) {
|
|
137
|
+
try {
|
|
138
|
+
output = renderRichText(data);
|
|
139
|
+
} catch {
|
|
140
|
+
output = '';
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return Promise.resolve(output);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
;// CONCATENATED MODULE: ./src/plugin/nunjuks.ts
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* NunjucksPlugin is the class to add custom tags for nunjucks templates
|
|
155
|
+
*/ class NunjucksPlugin {
|
|
156
|
+
blocks_folder;
|
|
157
|
+
config;
|
|
158
|
+
constructor(config, params = {}){
|
|
159
|
+
this.blocks_folder = params.blocks_folder ? `${params.blocks_folder.replace(/^\//g, '')}` : 'blocks/';
|
|
160
|
+
this.config = config;
|
|
161
|
+
}
|
|
162
|
+
outputBlocks(blocks, engine) {
|
|
163
|
+
let blockArray = null;
|
|
164
|
+
if (blocks && typeof blocks === 'object' && !Array.isArray(blocks)) {
|
|
165
|
+
blockArray = [
|
|
166
|
+
blocks
|
|
167
|
+
];
|
|
168
|
+
} else if (Array.isArray(blocks)) {
|
|
169
|
+
blockArray = blocks;
|
|
170
|
+
}
|
|
171
|
+
if (!blockArray || !Array.isArray(blockArray)) {
|
|
172
|
+
return '';
|
|
173
|
+
}
|
|
174
|
+
let html_output = '';
|
|
175
|
+
blockArray.forEach((block)=>{
|
|
176
|
+
block.component = Utils.slugify(block.component);
|
|
177
|
+
const html = engine.render(`${this.blocks_folder + block.component}.njk`, {
|
|
178
|
+
block: block
|
|
179
|
+
});
|
|
180
|
+
html_output += html;
|
|
181
|
+
});
|
|
182
|
+
return html_output;
|
|
183
|
+
}
|
|
184
|
+
addTags() {
|
|
185
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
186
|
+
const self = this;
|
|
187
|
+
this.config.addNunjucksTag('sb_blocks', (_nunjucksEngine, nunjucksEnv)=>{
|
|
188
|
+
const engine = nunjucksEnv;
|
|
189
|
+
return new function() {
|
|
190
|
+
this.tags = [
|
|
191
|
+
'sb_blocks'
|
|
192
|
+
];
|
|
193
|
+
this.parse = function(parser, nodes) {
|
|
194
|
+
const tok = parser.nextToken();
|
|
195
|
+
const args = parser.parseSignature(null, true);
|
|
196
|
+
parser.advanceAfterBlockEnd(tok.value);
|
|
197
|
+
return new nodes.CallExtensionAsync(this, 'run', args);
|
|
198
|
+
};
|
|
199
|
+
this.run = function(_context, blocks, callback) {
|
|
200
|
+
const html_output = self.outputBlocks(blocks, engine);
|
|
201
|
+
return callback(null, html_output);
|
|
202
|
+
};
|
|
203
|
+
}();
|
|
204
|
+
});
|
|
205
|
+
this.config.addNunjucksTag('sb_richtext', (_nunjucksEngine, nunjucksEnv)=>{
|
|
206
|
+
const engine = nunjucksEnv;
|
|
207
|
+
return new function() {
|
|
208
|
+
this.tags = [
|
|
209
|
+
'sb_richtext'
|
|
210
|
+
];
|
|
211
|
+
this.parse = function(parser, nodes) {
|
|
212
|
+
const tok = parser.nextToken();
|
|
213
|
+
const args = parser.parseSignature(null, true);
|
|
214
|
+
parser.advanceAfterBlockEnd(tok.value);
|
|
215
|
+
return new nodes.CallExtensionAsync(this, 'run', args);
|
|
216
|
+
};
|
|
217
|
+
this.run = async function(_context, data, callback) {
|
|
218
|
+
if (typeof data === 'string') {
|
|
219
|
+
return callback(null, data);
|
|
220
|
+
}
|
|
221
|
+
if (typeof data === 'undefined' || data === null) {
|
|
222
|
+
return callback(null, '');
|
|
223
|
+
}
|
|
224
|
+
let output = '';
|
|
225
|
+
if (typeof data === 'object' && data.content && Array.isArray(data.content)) {
|
|
226
|
+
try {
|
|
227
|
+
const resolver = (0,richtext_namespaceObject.richTextResolver)({
|
|
228
|
+
resolvers: {
|
|
229
|
+
blok: (node)=>{
|
|
230
|
+
const blocks = node.attrs?.body || [];
|
|
231
|
+
return self.outputBlocks(blocks, engine);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
output = resolver.render(data);
|
|
236
|
+
} catch {
|
|
237
|
+
output = '';
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return callback(null, output);
|
|
241
|
+
};
|
|
242
|
+
}();
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
;// CONCATENATED MODULE: ./src/plugin/index.ts
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* StoryblokTo11tyPlugin is the main plugin class for Eleventy
|
|
252
|
+
*/ class StoryblokTo11tyPlugin {
|
|
253
|
+
params;
|
|
254
|
+
/**
|
|
255
|
+
* Constructor
|
|
256
|
+
* @param params - The params for initialising the class
|
|
257
|
+
*/ constructor(params = {}){
|
|
258
|
+
this.params = params;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Install the plugin into 11ty config
|
|
262
|
+
* @param config - Eleventy configuration object
|
|
263
|
+
*/ configFunction(config) {
|
|
264
|
+
const nunjucks = new NunjucksPlugin(config, this.params);
|
|
265
|
+
nunjucks.addTags();
|
|
266
|
+
const liquid = new LiquidPlugin(config, this.params);
|
|
267
|
+
liquid.addTags();
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
;// CONCATENATED MODULE: external "storyblok-js-client"
|
|
272
|
+
const external_storyblok_js_client_namespaceObject = require("storyblok-js-client");
|
|
273
|
+
var external_storyblok_js_client_default = /*#__PURE__*/__webpack_require__.n(external_storyblok_js_client_namespaceObject);
|
|
274
|
+
;// CONCATENATED MODULE: external "fs"
|
|
275
|
+
const external_fs_namespaceObject = require("fs");
|
|
276
|
+
var external_fs_default = /*#__PURE__*/__webpack_require__.n(external_fs_namespaceObject);
|
|
277
|
+
;// CONCATENATED MODULE: ./src/data.ts
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* StoryblokTo11tyData is the main class that fetches the data from Storyblok
|
|
282
|
+
*/ class StoryblokTo11tyData {
|
|
283
|
+
api_version;
|
|
284
|
+
storyblok_api_token;
|
|
285
|
+
stories_path;
|
|
286
|
+
datasources_path;
|
|
287
|
+
layouts_path;
|
|
288
|
+
components_layouts_map;
|
|
289
|
+
per_page;
|
|
290
|
+
storyblok_client_config;
|
|
291
|
+
client;
|
|
292
|
+
/**
|
|
293
|
+
* Constructor
|
|
294
|
+
* @param params - The params for initialising the class
|
|
295
|
+
*/ constructor(params = {}){
|
|
296
|
+
this.api_version = params.version || 'draft';
|
|
297
|
+
this.storyblok_api_token = params.token;
|
|
298
|
+
this.stories_path = this.cleanPath(params.stories_path || 'storyblok');
|
|
299
|
+
this.datasources_path = this.cleanPath(params.datasources_path || '_data');
|
|
300
|
+
this.layouts_path = params.layouts_path || '';
|
|
301
|
+
this.components_layouts_map = params.components_layouts_map || {};
|
|
302
|
+
this.per_page = 100;
|
|
303
|
+
this.storyblok_client_config = params.storyblok_client_config || {};
|
|
304
|
+
// Init the Storyblok client
|
|
305
|
+
if (this.storyblok_api_token || this.storyblok_client_config && this.storyblok_client_config.accessToken) {
|
|
306
|
+
if (!this.storyblok_client_config.accessToken) {
|
|
307
|
+
this.storyblok_client_config.accessToken = this.storyblok_api_token;
|
|
308
|
+
}
|
|
309
|
+
// Setting up cache settings if not specified
|
|
310
|
+
if (!('cache' in this.storyblok_client_config)) {
|
|
311
|
+
this.storyblok_client_config.cache = {
|
|
312
|
+
clear: 'auto',
|
|
313
|
+
type: 'memory'
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
this.client = new (external_storyblok_js_client_default())(this.storyblok_client_config);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Takes care of cleaning a path set by the user removing
|
|
321
|
+
* leading and trailing slashes and add the process cwd
|
|
322
|
+
* @param path - The path string
|
|
323
|
+
* @returns The cleaned path
|
|
324
|
+
*/ cleanPath(path) {
|
|
325
|
+
const cleanedPath = path ? `/${path.replace(/^\/|\/$/g, '')}` : '';
|
|
326
|
+
return `${process.cwd()}${cleanedPath}/`;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Get data of a single datasource retrieving a specific dimension or all of them
|
|
330
|
+
* @param slug - Name of the datasource
|
|
331
|
+
* @param dimension_name - The name of the dimension
|
|
332
|
+
* @returns An array with the datasource entries
|
|
333
|
+
*/ async getDatasource(slug, dimension_name) {
|
|
334
|
+
const request_options = {
|
|
335
|
+
query: {
|
|
336
|
+
datasource: slug
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
if (typeof dimension_name === 'undefined') {
|
|
340
|
+
// Get all the dimensions names of a datasource, then we'll request each
|
|
341
|
+
// individual dimension data.
|
|
342
|
+
let data = [];
|
|
343
|
+
let dimensions = [
|
|
344
|
+
''
|
|
345
|
+
];
|
|
346
|
+
let datasource_info = null;
|
|
347
|
+
// Getting data of this datasource
|
|
348
|
+
datasource_info = await this.getData(`datasources/${slug}`, 'datasource');
|
|
349
|
+
if (!datasource_info.error && !datasource_info.data) {
|
|
350
|
+
console.error(`Datasource with slug "${slug}" not found`);
|
|
351
|
+
}
|
|
352
|
+
if (datasource_info.error || !datasource_info.data) {
|
|
353
|
+
return {};
|
|
354
|
+
}
|
|
355
|
+
// Getting the list of dimensions
|
|
356
|
+
if (datasource_info.data[0]?.dimensions) {
|
|
357
|
+
dimensions = dimensions.concat(datasource_info.data[0].dimensions.map((dimension)=>dimension.entry_value));
|
|
358
|
+
}
|
|
359
|
+
// Requesting the data of each individual datasource
|
|
360
|
+
await Promise.all(dimensions.map(async (dimension)=>{
|
|
361
|
+
const dimension_entries = await this.getDatasource(slug, dimension);
|
|
362
|
+
if (dimension_entries && Array.isArray(dimension_entries)) {
|
|
363
|
+
data = data.concat(dimension_entries);
|
|
364
|
+
}
|
|
365
|
+
}));
|
|
366
|
+
// Returning the data
|
|
367
|
+
return data;
|
|
368
|
+
} else {
|
|
369
|
+
// If the dimension is not undefined, set the dimension parameter in the query
|
|
370
|
+
// The dimension can be empty in case it's the default dimension that you are
|
|
371
|
+
// trying to retrieve.
|
|
372
|
+
if (request_options.query) {
|
|
373
|
+
request_options.query.dimension = dimension_name;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
// Getting the entries of a datasource
|
|
377
|
+
const datasource = await this.getData('datasource_entries', 'datasource_entries', request_options);
|
|
378
|
+
if (datasource.error) {
|
|
379
|
+
return [];
|
|
380
|
+
} else {
|
|
381
|
+
return datasource.data || [];
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Get data of datasources. It can be single or multiple
|
|
386
|
+
* @param slug - Name of the datasource
|
|
387
|
+
* @returns An object with the data of the datasource/s requested
|
|
388
|
+
*/ async getDatasources(slug) {
|
|
389
|
+
const datasources = {};
|
|
390
|
+
// If the slug is set, request a single datasource
|
|
391
|
+
// otherwise get the index of datasources first
|
|
392
|
+
if (slug) {
|
|
393
|
+
return this.getDatasource(slug);
|
|
394
|
+
} else {
|
|
395
|
+
const request_options = {
|
|
396
|
+
query: {
|
|
397
|
+
per_page: this.per_page
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
// Get the index of the datasources of the space
|
|
401
|
+
const datasources_index = await this.getData('datasources', 'datasources', request_options);
|
|
402
|
+
if (!datasources_index.data || datasources_index.error) {
|
|
403
|
+
return {};
|
|
404
|
+
}
|
|
405
|
+
// Get the entries of each individual datasource
|
|
406
|
+
await Promise.all(datasources_index.data.map(async (datasource)=>{
|
|
407
|
+
datasources[datasource.slug] = await this.getDatasource(datasource.slug);
|
|
408
|
+
}));
|
|
409
|
+
return datasources;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Store a datasource to a json file
|
|
414
|
+
* @param slug - Name of the datasource
|
|
415
|
+
* @returns True or false depending if the script was able to store the data
|
|
416
|
+
*/ async storeDatasources(slug) {
|
|
417
|
+
const data = await this.getDatasources(slug);
|
|
418
|
+
// If the data is empty, it won't save the file
|
|
419
|
+
if (Array.isArray(data) && !data.length || !Array.isArray(data) && !Object.keys(data).length) {
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
// Creating the cache path if it doesn't exist
|
|
423
|
+
if (!external_fs_default().existsSync(this.datasources_path)) {
|
|
424
|
+
external_fs_default().mkdirSync(this.datasources_path, {
|
|
425
|
+
recursive: true
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
// If it's not a specific datasource, the filename will be "datasources"
|
|
429
|
+
const filename = slug || 'datasources';
|
|
430
|
+
// Storing entries as json front matter
|
|
431
|
+
try {
|
|
432
|
+
external_fs_default().writeFileSync(`${this.datasources_path}${filename}.json`, JSON.stringify(data, null, 4));
|
|
433
|
+
console.log(`Datasources saved in ${this.datasources_path}`);
|
|
434
|
+
return true;
|
|
435
|
+
} catch (_error) {
|
|
436
|
+
return false;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Transforms a story based on the params provided
|
|
441
|
+
* @param story - The story that has to be transformed
|
|
442
|
+
* @returns The transformed story
|
|
443
|
+
*/ transformStories(story) {
|
|
444
|
+
// Setting the path
|
|
445
|
+
const layout_base = `${this.layouts_path.replace(/^\/|\/$/g, '')}/`;
|
|
446
|
+
// Setting the collection
|
|
447
|
+
const tags = story.content.component;
|
|
448
|
+
const data = Object.assign({}, story.content);
|
|
449
|
+
// Creating transformed story by omitting content
|
|
450
|
+
const { content: _content, ...storyWithoutContent } = story;
|
|
451
|
+
const transformed = {
|
|
452
|
+
...storyWithoutContent,
|
|
453
|
+
layout: '',
|
|
454
|
+
tags,
|
|
455
|
+
data,
|
|
456
|
+
permalink: ''
|
|
457
|
+
};
|
|
458
|
+
// Adding template name
|
|
459
|
+
transformed.layout = layout_base + (this.components_layouts_map[data.component] || data.component);
|
|
460
|
+
// Creating the permalink using the story path override field (real path in Storyblok)
|
|
461
|
+
// or the full slug
|
|
462
|
+
transformed.permalink = `${(story.path || story.full_slug).replace(/\/$/, '')}/`;
|
|
463
|
+
return transformed;
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Get all the stories from Storyblok
|
|
467
|
+
* @param params - Filters for the stories request
|
|
468
|
+
* @returns Array of transformed stories
|
|
469
|
+
*/ async getStories(params) {
|
|
470
|
+
const request_options = {
|
|
471
|
+
query: {
|
|
472
|
+
version: this.api_version,
|
|
473
|
+
per_page: this.per_page
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
// Filtering by component
|
|
477
|
+
if (params?.component && request_options.query) {
|
|
478
|
+
request_options.query['filter_query[component][in]'] = params.component;
|
|
479
|
+
}
|
|
480
|
+
// Whether to resolve relations
|
|
481
|
+
if (params?.resolve_relations && request_options.query) {
|
|
482
|
+
request_options.query['resolve_relations'] = params.resolve_relations;
|
|
483
|
+
}
|
|
484
|
+
// Whether to resolve links
|
|
485
|
+
if (params?.resolve_links && request_options.query) {
|
|
486
|
+
request_options.query['resolve_links'] = params.resolve_links;
|
|
487
|
+
}
|
|
488
|
+
// Language
|
|
489
|
+
if (params?.language && request_options.query) {
|
|
490
|
+
request_options.query['language'] = params.language;
|
|
491
|
+
}
|
|
492
|
+
// Fallback language
|
|
493
|
+
if (params?.fallback_lang && request_options.query) {
|
|
494
|
+
request_options.query['fallback_lang'] = params.fallback_lang;
|
|
495
|
+
}
|
|
496
|
+
// Getting the data
|
|
497
|
+
const pages = await this.getData('stories', 'stories', request_options);
|
|
498
|
+
if (!pages.data || pages.error) {
|
|
499
|
+
return [];
|
|
500
|
+
}
|
|
501
|
+
// Returning the transformed stories
|
|
502
|
+
return pages.data.map((story)=>this.transformStories(story));
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Cache stories in a folder as json files
|
|
506
|
+
* @param params - Filters for the stories request
|
|
507
|
+
* @returns True or false depending if the script was able to store the data
|
|
508
|
+
*/ async storeStories(params) {
|
|
509
|
+
const stories = await this.getStories(params);
|
|
510
|
+
// Creating the cache path if it doesn't exist
|
|
511
|
+
if (!external_fs_default().existsSync(this.stories_path)) {
|
|
512
|
+
external_fs_default().mkdirSync(this.stories_path, {
|
|
513
|
+
recursive: true
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
// Storing entries as json front matter
|
|
517
|
+
try {
|
|
518
|
+
stories.forEach((story)=>{
|
|
519
|
+
external_fs_default().writeFileSync(`${this.stories_path}${story.uuid}.md`, `---json\n${JSON.stringify(story, null, 4)}\n---`);
|
|
520
|
+
});
|
|
521
|
+
console.log(`${stories.length} stories saved in ${this.stories_path}`);
|
|
522
|
+
return true;
|
|
523
|
+
} catch (err) {
|
|
524
|
+
console.error(err);
|
|
525
|
+
return false;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Get a page of data from Storyblok API
|
|
530
|
+
* @param endpoint - The endpoint to query
|
|
531
|
+
* @param entity_name - The name of the entity to be retrieved from the api response
|
|
532
|
+
* @param params - Parameters to add to the API request
|
|
533
|
+
* @returns The data fetched from the API
|
|
534
|
+
*/ getData(endpoint, entity_name, params) {
|
|
535
|
+
return new Promise((resolve)=>{
|
|
536
|
+
let data = [];
|
|
537
|
+
const data_requests = [];
|
|
538
|
+
const fetchData = async ()=>{
|
|
539
|
+
// Paginated request vs single request
|
|
540
|
+
if (params?.query?.per_page) {
|
|
541
|
+
// Paginated request
|
|
542
|
+
params.query.page = 1;
|
|
543
|
+
// Get the first page to retrieve the total number of entries
|
|
544
|
+
let first_page = null;
|
|
545
|
+
try {
|
|
546
|
+
first_page = await this.apiRequest(endpoint, params);
|
|
547
|
+
} catch (err) {
|
|
548
|
+
return resolve({
|
|
549
|
+
error: true,
|
|
550
|
+
message: err
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
if (!first_page?.data) {
|
|
554
|
+
return resolve({
|
|
555
|
+
data: []
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
data = data.concat(first_page.data[entity_name]);
|
|
559
|
+
// Getting the stories
|
|
560
|
+
const total_entries = first_page.headers.total;
|
|
561
|
+
const total_pages = Math.ceil(total_entries / this.per_page);
|
|
562
|
+
// The script will request all the pages of entries at the same time
|
|
563
|
+
for(let page_index = 2; page_index <= total_pages; page_index++){
|
|
564
|
+
params.query.page = page_index;
|
|
565
|
+
data_requests.push(this.apiRequest(endpoint, params));
|
|
566
|
+
}
|
|
567
|
+
} else {
|
|
568
|
+
// Single request
|
|
569
|
+
data_requests.push(this.apiRequest(endpoint, params));
|
|
570
|
+
}
|
|
571
|
+
// When all the pages of entries are retrieved
|
|
572
|
+
Promise.all(data_requests).then((values)=>{
|
|
573
|
+
// Concatenating the data of each page
|
|
574
|
+
values.forEach((response)=>{
|
|
575
|
+
if (response.data) {
|
|
576
|
+
data = data.concat(response.data[entity_name]);
|
|
577
|
+
}
|
|
578
|
+
});
|
|
579
|
+
resolve({
|
|
580
|
+
data: data
|
|
581
|
+
});
|
|
582
|
+
}).catch((err)=>{
|
|
583
|
+
// Returning an object with an error property to let
|
|
584
|
+
// any method calling this one know that something went
|
|
585
|
+
// wrong with the api request
|
|
586
|
+
resolve({
|
|
587
|
+
error: true,
|
|
588
|
+
message: err
|
|
589
|
+
});
|
|
590
|
+
});
|
|
591
|
+
};
|
|
592
|
+
fetchData();
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Get a page of stories from Storyblok
|
|
597
|
+
* @param endpoint - The endpoint to query
|
|
598
|
+
* @param params - Parameters to add to the API request
|
|
599
|
+
* @returns The data fetched from the API
|
|
600
|
+
*/ apiRequest(endpoint, params) {
|
|
601
|
+
// Storyblok query options
|
|
602
|
+
const request_options = {};
|
|
603
|
+
// Adding the optional query filters
|
|
604
|
+
if (params?.query) {
|
|
605
|
+
Object.assign(request_options, params.query);
|
|
606
|
+
}
|
|
607
|
+
// API request
|
|
608
|
+
return new Promise((resolve, reject)=>{
|
|
609
|
+
if (!this.client) {
|
|
610
|
+
reject(new Error('Storyblok client not initialized'));
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
this.client.get(`cdn/${endpoint}`, request_options).then((response)=>{
|
|
614
|
+
// Returning the response from the endpoint
|
|
615
|
+
resolve(response);
|
|
616
|
+
}).catch((err)=>{
|
|
617
|
+
// Error handling
|
|
618
|
+
// Returning custom errors for 401 and 404 because they might
|
|
619
|
+
// be the most common
|
|
620
|
+
if (err.response) {
|
|
621
|
+
switch(err.response.status){
|
|
622
|
+
case 401:
|
|
623
|
+
console.error('\x1b[31mStoryblokTo11ty - Error 401: Unauthorized. Probably the API token is wrong.\x1b[0m');
|
|
624
|
+
break;
|
|
625
|
+
case 404:
|
|
626
|
+
console.error("\x1b[31mStoryblokTo11ty - Error 404: The item you are trying to get doesn't exist.\x1b[0m");
|
|
627
|
+
break;
|
|
628
|
+
default:
|
|
629
|
+
console.error(`\x1b[31mStoryblokTo11ty - Error ${err.response.status}: ${err.response.statusText}\x1b[0m`);
|
|
630
|
+
break;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
reject(err);
|
|
634
|
+
});
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
;// CONCATENATED MODULE: ./src/index.ts
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Main exports for StoryblokTo11ty
|
|
644
|
+
*/
|
|
645
|
+
|
|
646
|
+
/* export default */ const src = ({
|
|
647
|
+
importer: StoryblokTo11tyData,
|
|
648
|
+
plugin: StoryblokTo11tyPlugin
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
module.exports = __webpack_exports__;
|
|
652
|
+
})()
|
|
653
|
+
;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { EleventyConfig, PluginConfig } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* StoryblokTo11tyPlugin is the main plugin class for Eleventy
|
|
4
|
+
*/
|
|
5
|
+
export declare class StoryblokTo11tyPlugin {
|
|
6
|
+
private params;
|
|
7
|
+
/**
|
|
8
|
+
* Constructor
|
|
9
|
+
* @param params - The params for initialising the class
|
|
10
|
+
*/
|
|
11
|
+
constructor(params?: PluginConfig);
|
|
12
|
+
/**
|
|
13
|
+
* Install the plugin into 11ty config
|
|
14
|
+
* @param config - Eleventy configuration object
|
|
15
|
+
*/
|
|
16
|
+
configFunction(config: EleventyConfig): void;
|
|
17
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { EleventyConfig, PluginConfig } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* LiquidPlugin is the class to add custom tags for liquid templates
|
|
4
|
+
*/
|
|
5
|
+
export declare class LiquidPlugin {
|
|
6
|
+
private blocks_folder;
|
|
7
|
+
private config;
|
|
8
|
+
private blocks?;
|
|
9
|
+
private data?;
|
|
10
|
+
constructor(config: EleventyConfig, params?: PluginConfig);
|
|
11
|
+
addTags(): void;
|
|
12
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { EleventyConfig, PluginConfig } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* NunjucksPlugin is the class to add custom tags for nunjucks templates
|
|
4
|
+
*/
|
|
5
|
+
export declare class NunjucksPlugin {
|
|
6
|
+
private blocks_folder;
|
|
7
|
+
private config;
|
|
8
|
+
constructor(config: EleventyConfig, params?: PluginConfig);
|
|
9
|
+
private outputBlocks;
|
|
10
|
+
addTags(): void;
|
|
11
|
+
}
|