storyblok 4.0.0-beta.2 → 4.0.0-beta.4
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/index.mjs +149 -82
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
package/dist/index.mjs
CHANGED
|
@@ -107,7 +107,11 @@ async function customFetch(url, options = {}) {
|
|
|
107
107
|
data
|
|
108
108
|
});
|
|
109
109
|
}
|
|
110
|
-
return
|
|
110
|
+
return {
|
|
111
|
+
...data,
|
|
112
|
+
perPage: Number(response.headers.get("Per-Page")),
|
|
113
|
+
total: Number(response.headers.get("Total"))
|
|
114
|
+
};
|
|
111
115
|
} catch (error) {
|
|
112
116
|
if (error instanceof FetchError) {
|
|
113
117
|
throw error;
|
|
@@ -315,6 +319,22 @@ class FileSystemError extends Error {
|
|
|
315
319
|
}
|
|
316
320
|
}
|
|
317
321
|
|
|
322
|
+
function handleVerboseError(error) {
|
|
323
|
+
if (error instanceof CommandError || error instanceof APIError || error instanceof FileSystemError) {
|
|
324
|
+
const errorDetails = "getInfo" in error ? error.getInfo() : {};
|
|
325
|
+
if (error instanceof CommandError) {
|
|
326
|
+
konsola.error(`Command Error: ${error.getInfo().message}`, errorDetails);
|
|
327
|
+
} else if (error instanceof APIError) {
|
|
328
|
+
konsola.error(`API Error: ${error.getInfo().cause}`, errorDetails);
|
|
329
|
+
} else if (error instanceof FileSystemError) {
|
|
330
|
+
konsola.error(`File System Error: ${error.getInfo().cause}`, errorDetails);
|
|
331
|
+
} else {
|
|
332
|
+
konsola.error(`Unexpected Error: ${error}`, errorDetails);
|
|
333
|
+
}
|
|
334
|
+
} else {
|
|
335
|
+
konsola.error(`Unexpected Error`, error);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
318
338
|
function handleError(error, verbose = false) {
|
|
319
339
|
if (error instanceof APIError || error instanceof FileSystemError) {
|
|
320
340
|
const messageStack = error.messageStack;
|
|
@@ -329,17 +349,8 @@ function handleError(error, verbose = false) {
|
|
|
329
349
|
header: true
|
|
330
350
|
});
|
|
331
351
|
}
|
|
332
|
-
if (verbose
|
|
333
|
-
|
|
334
|
-
if (error instanceof CommandError) {
|
|
335
|
-
konsola.error(`Command Error: ${error.getInfo().message}`, errorDetails);
|
|
336
|
-
} else if (error instanceof APIError) {
|
|
337
|
-
konsola.error(`API Error: ${error.getInfo().cause}`, errorDetails);
|
|
338
|
-
} else if (error instanceof FileSystemError) {
|
|
339
|
-
konsola.error(`File System Error: ${error.getInfo().cause}`, errorDetails);
|
|
340
|
-
} else {
|
|
341
|
-
konsola.error(`Unexpected Error: ${error}`, errorDetails);
|
|
342
|
-
}
|
|
352
|
+
if (verbose) {
|
|
353
|
+
handleVerboseError(error);
|
|
343
354
|
} else {
|
|
344
355
|
konsola.br();
|
|
345
356
|
konsola.info("For more information about the error, run the command with the `--verbose` flag");
|
|
@@ -353,7 +364,10 @@ const toPascalCase = (str) => {
|
|
|
353
364
|
return str.replace(/(?:^|_)(\w)/g, (_, char) => char.toUpperCase());
|
|
354
365
|
};
|
|
355
366
|
const toCamelCase = (str) => {
|
|
356
|
-
return str.replace(/
|
|
367
|
+
return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()).replace(/_/g, "").replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()).replace(/[^a-z0-9]([a-z])/gi, (_, letter) => letter.toUpperCase()).replace(/[^a-z0-9]/gi, "");
|
|
368
|
+
};
|
|
369
|
+
const capitalize = (str) => {
|
|
370
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
357
371
|
};
|
|
358
372
|
function maskToken(token) {
|
|
359
373
|
if (token.length <= 4) {
|
|
@@ -563,14 +577,18 @@ const getCredentials = async (filePath = join(getStoryblokGlobalPath(), "credent
|
|
|
563
577
|
try {
|
|
564
578
|
await access(filePath);
|
|
565
579
|
const content = await readFile(filePath);
|
|
566
|
-
|
|
580
|
+
const parsedContent = JSON.parse(content);
|
|
581
|
+
if (Object.keys(parsedContent).length === 0) {
|
|
582
|
+
return null;
|
|
583
|
+
}
|
|
584
|
+
return parsedContent;
|
|
567
585
|
} catch (error) {
|
|
568
586
|
if (error.code === "ENOENT") {
|
|
569
587
|
await saveToFile(filePath, JSON.stringify({}, null, 2), { mode: 384 });
|
|
570
|
-
return
|
|
588
|
+
return null;
|
|
571
589
|
}
|
|
572
590
|
handleFileSystemError("read", error);
|
|
573
|
-
return
|
|
591
|
+
return null;
|
|
574
592
|
}
|
|
575
593
|
};
|
|
576
594
|
const addCredentials = async ({
|
|
@@ -604,7 +622,7 @@ function createSession() {
|
|
|
604
622
|
const state = {
|
|
605
623
|
isLoggedIn: false
|
|
606
624
|
};
|
|
607
|
-
async function initializeSession(
|
|
625
|
+
async function initializeSession() {
|
|
608
626
|
const envCredentials = getEnvCredentials();
|
|
609
627
|
if (envCredentials) {
|
|
610
628
|
state.isLoggedIn = true;
|
|
@@ -614,9 +632,9 @@ function createSession() {
|
|
|
614
632
|
state.envLogin = true;
|
|
615
633
|
return;
|
|
616
634
|
}
|
|
617
|
-
const
|
|
618
|
-
|
|
619
|
-
|
|
635
|
+
const credentials = await getCredentials();
|
|
636
|
+
if (credentials) {
|
|
637
|
+
const creds = Object.values(credentials)[0];
|
|
620
638
|
state.isLoggedIn = true;
|
|
621
639
|
state.login = creds.login;
|
|
622
640
|
state.password = creds.password;
|
|
@@ -1191,16 +1209,18 @@ async function readJsonFile(filePath) {
|
|
|
1191
1209
|
}
|
|
1192
1210
|
}
|
|
1193
1211
|
const readComponentsFiles = async (options) => {
|
|
1194
|
-
const { from, path, separateFiles = false, suffix } = options;
|
|
1212
|
+
const { from, path, separateFiles = false, suffix, space } = options;
|
|
1195
1213
|
const resolvedPath = resolvePath(path, `components/${from}`);
|
|
1196
1214
|
try {
|
|
1197
1215
|
await readdir(resolvedPath);
|
|
1198
1216
|
} catch (error) {
|
|
1199
|
-
const message = `No
|
|
1217
|
+
const message = `No local components found for space ${chalk.bold(from)}. To push components, you need to pull them first:
|
|
1200
1218
|
|
|
1201
|
-
|
|
1219
|
+
1. Pull the components from your source space:
|
|
1220
|
+
${chalk.cyan(`storyblok components pull --space ${from}`)}
|
|
1202
1221
|
|
|
1203
|
-
|
|
1222
|
+
2. Then try pushing again:
|
|
1223
|
+
${chalk.cyan(`storyblok components push --space ${space} --from ${from}`)}`;
|
|
1204
1224
|
throw new FileSystemError(
|
|
1205
1225
|
"file_not_found",
|
|
1206
1226
|
"read",
|
|
@@ -1380,7 +1400,7 @@ componentsCommand.command("pull [componentName]").option("-f, --filename <filena
|
|
|
1380
1400
|
}
|
|
1381
1401
|
});
|
|
1382
1402
|
|
|
1383
|
-
function findRelatedResources(components, spaceData) {
|
|
1403
|
+
function findRelatedResources(components, spaceData, visitedComponents = /* @__PURE__ */ new Set()) {
|
|
1384
1404
|
const componentIds = new Set(components.map((c) => c.id));
|
|
1385
1405
|
const whitelistedComponentNames = /* @__PURE__ */ new Set();
|
|
1386
1406
|
const componentGroupUuids = /* @__PURE__ */ new Set();
|
|
@@ -1465,7 +1485,11 @@ function findRelatedResources(components, spaceData) {
|
|
|
1465
1485
|
whitelistedComponents: []
|
|
1466
1486
|
};
|
|
1467
1487
|
if (whitelistedComponents.length > 0) {
|
|
1468
|
-
|
|
1488
|
+
const newComponents = whitelistedComponents.filter((component) => !visitedComponents.has(component.name));
|
|
1489
|
+
if (newComponents.length > 0) {
|
|
1490
|
+
components.forEach((component) => visitedComponents.add(component.name));
|
|
1491
|
+
additionalResources = findRelatedResources(newComponents, spaceData, visitedComponents);
|
|
1492
|
+
}
|
|
1469
1493
|
}
|
|
1470
1494
|
const result = {
|
|
1471
1495
|
groups: Array.from(/* @__PURE__ */ new Set([...Array.from(relatedGroups), ...additionalResources.groups])),
|
|
@@ -1759,11 +1783,6 @@ async function handleWhitelists(space, password, region, spaceData) {
|
|
|
1759
1783
|
return;
|
|
1760
1784
|
}
|
|
1761
1785
|
if (visited.has(componentName)) {
|
|
1762
|
-
failedComponents.add(componentName);
|
|
1763
|
-
results.failed.push({
|
|
1764
|
-
name: componentName,
|
|
1765
|
-
error: new Error(`Circular dependency detected for component ${componentName}`)
|
|
1766
|
-
});
|
|
1767
1786
|
return;
|
|
1768
1787
|
}
|
|
1769
1788
|
visited.add(componentName);
|
|
@@ -2022,11 +2041,14 @@ componentsCommand.command("push [componentName]").description(`Push your space's
|
|
|
2022
2041
|
if (!from) {
|
|
2023
2042
|
options.from = space;
|
|
2024
2043
|
}
|
|
2044
|
+
konsola.info(`Attempting to push components ${chalk.bold("from")} space ${chalk.hex(colorPalette.COMPONENTS)(options.from)} ${chalk.bold("to")} ${chalk.hex(colorPalette.COMPONENTS)(space)}`);
|
|
2045
|
+
konsola.br();
|
|
2025
2046
|
const { password, region } = state;
|
|
2026
2047
|
try {
|
|
2027
2048
|
let spaceData = await readComponentsFiles({
|
|
2028
2049
|
...options,
|
|
2029
|
-
path
|
|
2050
|
+
path,
|
|
2051
|
+
space
|
|
2030
2052
|
});
|
|
2031
2053
|
if (componentName) {
|
|
2032
2054
|
spaceData = filterSpaceDataByComponent(spaceData, componentName);
|
|
@@ -2241,16 +2263,28 @@ migrationsCommand.command("generate [componentName]").description("Generate a mi
|
|
|
2241
2263
|
const fetchStories = async (space, token, region, params) => {
|
|
2242
2264
|
try {
|
|
2243
2265
|
const url = getStoryblokUrl(region);
|
|
2244
|
-
const
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2266
|
+
const allStories = [];
|
|
2267
|
+
let currentPage = 1;
|
|
2268
|
+
let hasMorePages = true;
|
|
2269
|
+
while (hasMorePages) {
|
|
2270
|
+
const { filter_query, ...restParams } = params || {};
|
|
2271
|
+
const regularParams = new URLSearchParams({
|
|
2272
|
+
...objectToStringParams({ ...restParams, per_page: 100 }),
|
|
2273
|
+
...currentPage > 1 && { page: currentPage.toString() }
|
|
2274
|
+
}).toString();
|
|
2275
|
+
const queryString = filter_query ? `${regularParams ? `${regularParams}&` : ""}${filter_query}` : regularParams;
|
|
2276
|
+
const endpoint = `${url}/spaces/${space}/stories${queryString ? `?${queryString}` : ""}`;
|
|
2277
|
+
const response = await customFetch(endpoint, {
|
|
2278
|
+
headers: {
|
|
2279
|
+
Authorization: token
|
|
2280
|
+
}
|
|
2281
|
+
});
|
|
2282
|
+
allStories.push(...response.stories);
|
|
2283
|
+
const totalPages = Math.ceil(response.total / response.perPage);
|
|
2284
|
+
hasMorePages = currentPage < totalPages;
|
|
2285
|
+
currentPage++;
|
|
2286
|
+
}
|
|
2287
|
+
return allStories;
|
|
2254
2288
|
} catch (error) {
|
|
2255
2289
|
handleAPIError("pull_stories", error);
|
|
2256
2290
|
}
|
|
@@ -3223,18 +3257,67 @@ const storyblokSchemas = /* @__PURE__ */ new Map([
|
|
|
3223
3257
|
["richtext", getRichtextJSONSchema]
|
|
3224
3258
|
]);
|
|
3225
3259
|
|
|
3260
|
+
const STORY_TYPE = "ISbStoryData";
|
|
3226
3261
|
const DEFAULT_TYPEDEFS_HEADER = [
|
|
3227
3262
|
"// This file was generated by the storyblok CLI.",
|
|
3228
3263
|
"// DO NOT MODIFY THIS FILE BY HAND."
|
|
3229
3264
|
];
|
|
3230
|
-
const getPropertyTypeAnnotation = (property) => {
|
|
3265
|
+
const getPropertyTypeAnnotation = (property, prefix) => {
|
|
3231
3266
|
if (Array.from(storyblokSchemas.keys()).includes(property.type)) {
|
|
3232
3267
|
return { type: property.type };
|
|
3233
3268
|
}
|
|
3269
|
+
let type = "unknown";
|
|
3234
3270
|
const options = property.options && property.options.length > 0 ? property.options.map((item) => item.value) : [];
|
|
3235
3271
|
if (options.length > 0 && property.exclude_empty_option !== true) {
|
|
3236
3272
|
options.unshift("");
|
|
3237
3273
|
}
|
|
3274
|
+
if (property.source === "internal_stories") {
|
|
3275
|
+
if (property.filter_content_type) {
|
|
3276
|
+
if (typeof property.filter_content_type === "string") {
|
|
3277
|
+
return {
|
|
3278
|
+
tsType: `(${getStoryType(property.filter_content_type, prefix)} | string )${property.type === "options" ? "[]" : ""}`
|
|
3279
|
+
};
|
|
3280
|
+
}
|
|
3281
|
+
return {
|
|
3282
|
+
tsType: `(${property.filter_content_type.map((type2) => getStoryType(type2, prefix)).join(" | ")} | string )${property.type === "options" ? "[]" : ""}`
|
|
3283
|
+
};
|
|
3284
|
+
}
|
|
3285
|
+
}
|
|
3286
|
+
if (
|
|
3287
|
+
// If there is no `source` and there are options, the data source is the component itself
|
|
3288
|
+
// TODO: check if this is an old behaviour (shouldn't this be handled as an "internal" source?)
|
|
3289
|
+
options.length > 0 && !property.source || property.source === "internal_languages" || property.source === "external"
|
|
3290
|
+
) {
|
|
3291
|
+
type = "string";
|
|
3292
|
+
}
|
|
3293
|
+
if (property.source === "internal") {
|
|
3294
|
+
type = ["number", "string"];
|
|
3295
|
+
}
|
|
3296
|
+
if (property.type === "option") {
|
|
3297
|
+
if (options.length > 0) {
|
|
3298
|
+
return {
|
|
3299
|
+
type,
|
|
3300
|
+
enum: options
|
|
3301
|
+
};
|
|
3302
|
+
}
|
|
3303
|
+
return {
|
|
3304
|
+
type
|
|
3305
|
+
};
|
|
3306
|
+
}
|
|
3307
|
+
if (property.type === "options") {
|
|
3308
|
+
if (options.length > 0) {
|
|
3309
|
+
return {
|
|
3310
|
+
type: "array",
|
|
3311
|
+
items: {
|
|
3312
|
+
enum: options
|
|
3313
|
+
}
|
|
3314
|
+
};
|
|
3315
|
+
}
|
|
3316
|
+
return {
|
|
3317
|
+
type: "array",
|
|
3318
|
+
items: { type }
|
|
3319
|
+
};
|
|
3320
|
+
}
|
|
3238
3321
|
switch (property.type) {
|
|
3239
3322
|
case "bloks":
|
|
3240
3323
|
return { type: "array" };
|
|
@@ -3251,6 +3334,9 @@ const getPropertyTypeAnnotation = (property) => {
|
|
|
3251
3334
|
return { type: "any" };
|
|
3252
3335
|
}
|
|
3253
3336
|
};
|
|
3337
|
+
function getStoryType(property, prefix) {
|
|
3338
|
+
return `${STORY_TYPE}<${prefix ?? ""}${capitalize(toCamelCase(property))}>`;
|
|
3339
|
+
}
|
|
3254
3340
|
const getComponentType = (componentName, options) => {
|
|
3255
3341
|
const prefix = options.typePrefix ?? "";
|
|
3256
3342
|
const sanitizedName = componentName.replace(/[^a-z0-9]/gi, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
|
|
@@ -3266,7 +3352,7 @@ const getComponentPropertiesTypeAnnotations = async (component, options, spaceDa
|
|
|
3266
3352
|
}
|
|
3267
3353
|
const propertyType = value.type;
|
|
3268
3354
|
const propertyTypeAnnotation = {
|
|
3269
|
-
[key]: getPropertyTypeAnnotation(value)
|
|
3355
|
+
[key]: getPropertyTypeAnnotation(value, options.typePrefix)
|
|
3270
3356
|
};
|
|
3271
3357
|
if (propertyType === "custom" && customFieldsParser) {
|
|
3272
3358
|
const customField = typeof customFieldsParser === "function" ? customFieldsParser(key, value) : {};
|
|
@@ -3354,7 +3440,7 @@ const generateTypes = async (spaceData, options = {
|
|
|
3354
3440
|
const schemas = await Promise.all(spaceData.components.map(async (component) => {
|
|
3355
3441
|
const type = getComponentType(component.name, options);
|
|
3356
3442
|
const componentPropertiesTypeAnnotations = await getComponentPropertiesTypeAnnotations(component, options, spaceData, customFieldsParser);
|
|
3357
|
-
const requiredFields = Object.entries(component
|
|
3443
|
+
const requiredFields = Object.entries(component?.schema || {}).reduce(
|
|
3358
3444
|
(acc, [key, value]) => {
|
|
3359
3445
|
if (value.required) {
|
|
3360
3446
|
return [...acc, key];
|
|
@@ -3424,45 +3510,13 @@ const saveTypesToFile = async (space, typedefString, options) => {
|
|
|
3424
3510
|
const generateStoryblokTypes = async (options = {}) => {
|
|
3425
3511
|
const { filename = "storyblok", path } = options;
|
|
3426
3512
|
try {
|
|
3427
|
-
const storyblokTypesPath = resolve(
|
|
3513
|
+
const storyblokTypesPath = resolve(__dirname, "./index.d.ts");
|
|
3428
3514
|
const storyblokTypesContent = readFileSync(storyblokTypesPath, "utf-8");
|
|
3429
|
-
const lines = storyblokTypesContent.split("\n");
|
|
3430
|
-
const typeDefinitions = [];
|
|
3431
|
-
let isCollecting = false;
|
|
3432
|
-
let bracketCount = 0;
|
|
3433
|
-
let currentType = "";
|
|
3434
|
-
for (let i = 0; i < lines.length; i++) {
|
|
3435
|
-
const line = lines[i];
|
|
3436
|
-
if (line.includes("export type StoryblokPropertyType") || line.includes("export interface Storyblok")) {
|
|
3437
|
-
if (isCollecting) {
|
|
3438
|
-
typeDefinitions.push("");
|
|
3439
|
-
}
|
|
3440
|
-
isCollecting = true;
|
|
3441
|
-
typeDefinitions.push(line);
|
|
3442
|
-
currentType = line.includes("type") ? "type" : "interface";
|
|
3443
|
-
bracketCount += (line.match(/\{/g) || []).length;
|
|
3444
|
-
bracketCount -= (line.match(/\}/g) || []).length;
|
|
3445
|
-
if (currentType === "type") {
|
|
3446
|
-
isCollecting = false;
|
|
3447
|
-
continue;
|
|
3448
|
-
}
|
|
3449
|
-
let j = i + 1;
|
|
3450
|
-
while (j < lines.length && bracketCount > 0) {
|
|
3451
|
-
const nextLine = lines[j];
|
|
3452
|
-
bracketCount += (nextLine.match(/\{/g) || []).length;
|
|
3453
|
-
bracketCount -= (nextLine.match(/\}/g) || []).length;
|
|
3454
|
-
typeDefinitions.push(nextLine);
|
|
3455
|
-
j++;
|
|
3456
|
-
}
|
|
3457
|
-
i = j - 1;
|
|
3458
|
-
isCollecting = false;
|
|
3459
|
-
}
|
|
3460
|
-
}
|
|
3461
3515
|
const typeDefs = [
|
|
3462
|
-
"// This file was generated by the
|
|
3516
|
+
"// This file was generated by the Storyblok CLI.",
|
|
3463
3517
|
"// DO NOT MODIFY THIS FILE BY HAND.",
|
|
3464
|
-
|
|
3465
|
-
|
|
3518
|
+
`import type { ${STORY_TYPE} } from '@storyblok/js';`,
|
|
3519
|
+
storyblokTypesContent
|
|
3466
3520
|
].join("\n");
|
|
3467
3521
|
const resolvedPath = path ? resolve(process.cwd(), path, "types") : resolvePath(path, "types");
|
|
3468
3522
|
await saveToFile(join(resolvedPath, `${filename}.d.ts`), typeDefs);
|
|
@@ -3516,7 +3570,7 @@ typesCommand.command("generate").description("Generate types d.ts for your compo
|
|
|
3516
3570
|
}
|
|
3517
3571
|
});
|
|
3518
3572
|
|
|
3519
|
-
const version = "4.0.0-beta.
|
|
3573
|
+
const version = "4.0.0-beta.4";
|
|
3520
3574
|
const pkg = {
|
|
3521
3575
|
version: version};
|
|
3522
3576
|
|
|
@@ -3533,6 +3587,19 @@ program.on("command:*", () => {
|
|
|
3533
3587
|
konsola.br();
|
|
3534
3588
|
program.help();
|
|
3535
3589
|
});
|
|
3590
|
+
program.command("test").description("Test the CLI").action(async () => {
|
|
3591
|
+
const { state, initializeSession } = session();
|
|
3592
|
+
await initializeSession();
|
|
3593
|
+
const { password, region } = state;
|
|
3594
|
+
try {
|
|
3595
|
+
const result = await fetchStories("85047", password, region, {
|
|
3596
|
+
per_page: 100
|
|
3597
|
+
});
|
|
3598
|
+
console.log(result?.length);
|
|
3599
|
+
} catch (error) {
|
|
3600
|
+
console.error(error);
|
|
3601
|
+
}
|
|
3602
|
+
});
|
|
3536
3603
|
try {
|
|
3537
3604
|
program.parse(process.argv);
|
|
3538
3605
|
} catch (error) {
|