storyblok 4.15.0 → 4.15.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/dist/index.mjs +154 -140
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -6
package/dist/index.mjs
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import 'dotenv/config';
|
|
3
3
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
4
|
-
import { resolve, dirname,
|
|
4
|
+
import { resolve, dirname, join, parse, extname, relative, isAbsolute, basename } from 'pathe';
|
|
5
5
|
import { existsSync, mkdirSync, appendFileSync, writeFileSync, readdirSync, unlinkSync, readFileSync } from 'node:fs';
|
|
6
6
|
import { homedir } from 'node:os';
|
|
7
7
|
import { loadConfig as loadConfig$1, SUPPORTED_EXTENSIONS } from 'c12';
|
|
8
8
|
import chalk from 'chalk';
|
|
9
9
|
import { readPackageUp } from 'read-package-up';
|
|
10
10
|
import { Command } from 'commander';
|
|
11
|
-
import path, { join, resolve as resolve$1, parse, dirname as dirname$1, extname, relative, basename } from 'node:path';
|
|
12
11
|
import { MultiBar, Presets } from 'cli-progress';
|
|
13
12
|
import { Spinner } from '@topcli/spinner';
|
|
14
|
-
import fs, { mkdir, writeFile, readFile as readFile$1, appendFile, access, constants, readdir, unlink
|
|
13
|
+
import fs, { mkdir, writeFile, readFile as readFile$1, appendFile, access, constants, readdir, unlink } from 'node:fs/promises';
|
|
15
14
|
import filenamify from 'filenamify';
|
|
16
15
|
import { ManagementApiClient } from '@storyblok/management-api-client';
|
|
17
16
|
import { RateLimit, Sema } from 'async-sema';
|
|
@@ -103,6 +102,44 @@ const regionNames = {
|
|
|
103
102
|
({
|
|
104
103
|
SB_Agent_Version: process.env.npm_package_version || "4.x"
|
|
105
104
|
});
|
|
105
|
+
const SUPPORTED_ASSET_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
106
|
+
// Images: image/png, image/x-png, image/gif, image/jpeg, image/avif, image/svg+xml, image/webp
|
|
107
|
+
".jpg",
|
|
108
|
+
".jpeg",
|
|
109
|
+
".png",
|
|
110
|
+
".gif",
|
|
111
|
+
".webp",
|
|
112
|
+
".avif",
|
|
113
|
+
".svg",
|
|
114
|
+
// Video: video/*, application/mp4, application/x-mpegurl, application/vnd.apple.mpegurl
|
|
115
|
+
".mp4",
|
|
116
|
+
".mov",
|
|
117
|
+
".avi",
|
|
118
|
+
".webm",
|
|
119
|
+
".wmv",
|
|
120
|
+
".mkv",
|
|
121
|
+
".flv",
|
|
122
|
+
".ogv",
|
|
123
|
+
".3gp",
|
|
124
|
+
".m4v",
|
|
125
|
+
".mpg",
|
|
126
|
+
".mpeg",
|
|
127
|
+
".m3u8",
|
|
128
|
+
// Audio: audio/*
|
|
129
|
+
".mp3",
|
|
130
|
+
".wav",
|
|
131
|
+
".ogg",
|
|
132
|
+
".aac",
|
|
133
|
+
".flac",
|
|
134
|
+
".wma",
|
|
135
|
+
".m4a",
|
|
136
|
+
".opus",
|
|
137
|
+
// Documents: application/msword, text/plain, application/pdf, application/vnd.openxmlformats-officedocument.wordprocessingml.document
|
|
138
|
+
".pdf",
|
|
139
|
+
".doc",
|
|
140
|
+
".docx",
|
|
141
|
+
".txt"
|
|
142
|
+
]);
|
|
106
143
|
const directories = {
|
|
107
144
|
assets: "assets",
|
|
108
145
|
components: "components",
|
|
@@ -651,25 +688,38 @@ const API_ACTIONS = {
|
|
|
651
688
|
const API_ERRORS = {
|
|
652
689
|
unauthorized: "The user is not authorized to access the API",
|
|
653
690
|
network_error: "No response from server, please check if you are correctly connected to internet",
|
|
691
|
+
server_error: "The server returned an error",
|
|
654
692
|
invalid_credentials: "The provided credentials are invalid",
|
|
655
693
|
timeout: "The API request timed out",
|
|
656
694
|
generic: "Error fetching data from the API",
|
|
657
695
|
not_found: "The requested resource was not found",
|
|
658
696
|
unprocessable_entity: "The request was well-formed but was unable to be followed due to semantic errors"
|
|
659
697
|
};
|
|
698
|
+
function getErrorId(status) {
|
|
699
|
+
switch (status) {
|
|
700
|
+
case 401:
|
|
701
|
+
return "unauthorized";
|
|
702
|
+
case 404:
|
|
703
|
+
return "not_found";
|
|
704
|
+
case 422:
|
|
705
|
+
return "unprocessable_entity";
|
|
706
|
+
default:
|
|
707
|
+
return status >= 500 ? "server_error" : "generic";
|
|
708
|
+
}
|
|
709
|
+
}
|
|
660
710
|
function handleAPIError(action, error, customMessage) {
|
|
661
711
|
if (error instanceof FetchError) {
|
|
662
|
-
const
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
712
|
+
const errorId = getErrorId(error.response.status);
|
|
713
|
+
throw new APIError(errorId, action, error, customMessage);
|
|
714
|
+
}
|
|
715
|
+
const response = error?.response;
|
|
716
|
+
if (response?.status) {
|
|
717
|
+
const wrappedError = new FetchError(
|
|
718
|
+
response.statusText ?? error.message,
|
|
719
|
+
{ status: response.status, statusText: response.statusText ?? "", data: response.data }
|
|
720
|
+
);
|
|
721
|
+
const errorId = getErrorId(response.status);
|
|
722
|
+
throw new APIError(errorId, action, wrappedError, customMessage);
|
|
673
723
|
}
|
|
674
724
|
throw new APIError("generic", action, error, customMessage);
|
|
675
725
|
}
|
|
@@ -1258,7 +1308,7 @@ const deduplicateManifest = async (manifestFile) => {
|
|
|
1258
1308
|
};
|
|
1259
1309
|
const resolvePath = (path, folder) => {
|
|
1260
1310
|
const basePath = path ?? DEFAULT_STORAGE_DIR;
|
|
1261
|
-
return resolve
|
|
1311
|
+
return resolve(process.cwd(), basePath, folder);
|
|
1262
1312
|
};
|
|
1263
1313
|
function resolveCommandPath(commandPath, space, baseDir) {
|
|
1264
1314
|
if (space) {
|
|
@@ -1370,7 +1420,7 @@ class Reporter {
|
|
|
1370
1420
|
if (this.maxFiles === void 0) {
|
|
1371
1421
|
return;
|
|
1372
1422
|
}
|
|
1373
|
-
const dir = dirname
|
|
1423
|
+
const dir = dirname(this.filePath);
|
|
1374
1424
|
const ext = extname(this.filePath);
|
|
1375
1425
|
Reporter.pruneReportFiles(dir, this.maxFiles, ext);
|
|
1376
1426
|
}
|
|
@@ -1428,7 +1478,7 @@ class FileTransport {
|
|
|
1428
1478
|
if (this.maxFiles === void 0) {
|
|
1429
1479
|
return;
|
|
1430
1480
|
}
|
|
1431
|
-
const dir = dirname
|
|
1481
|
+
const dir = dirname(this.filePath);
|
|
1432
1482
|
const ext = extname(this.filePath);
|
|
1433
1483
|
FileTransport.pruneLogFiles(dir, this.maxFiles, ext);
|
|
1434
1484
|
}
|
|
@@ -1827,7 +1877,7 @@ function getProgram() {
|
|
|
1827
1877
|
options.path
|
|
1828
1878
|
);
|
|
1829
1879
|
const logFilename = `${commandPieces.join("-")}-${runId}.jsonl`;
|
|
1830
|
-
logFilePath =
|
|
1880
|
+
logFilePath = join(logsPath, logFilename);
|
|
1831
1881
|
transports.push(
|
|
1832
1882
|
new FileTransport({
|
|
1833
1883
|
filePath: logFilePath,
|
|
@@ -1851,7 +1901,7 @@ function getProgram() {
|
|
|
1851
1901
|
options.path
|
|
1852
1902
|
);
|
|
1853
1903
|
const reportFilename = `${commandPieces.join("-")}-${runId}.json`;
|
|
1854
|
-
const reportFilePath =
|
|
1904
|
+
const reportFilePath = join(reportPath, reportFilename);
|
|
1855
1905
|
const reporter = getReporter({
|
|
1856
1906
|
enabled: true,
|
|
1857
1907
|
filePath: reportFilePath,
|
|
@@ -1888,34 +1938,10 @@ const getUser = async (token, region) => {
|
|
|
1888
1938
|
});
|
|
1889
1939
|
return data?.user;
|
|
1890
1940
|
} catch (error) {
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
throw new APIError("unauthorized", "get_user", error, `The token provided ${chalk.bold(maskToken(token))} is invalid.
|
|
1896
|
-
Please make sure you are using the correct token and try again.`);
|
|
1897
|
-
default:
|
|
1898
|
-
throw new APIError("network_error", "get_user", error);
|
|
1899
|
-
}
|
|
1900
|
-
}
|
|
1901
|
-
if (typeof error === "string" && error === "Unauthorized") {
|
|
1902
|
-
const mockFetchError = new FetchError("Non-JSON response", {
|
|
1903
|
-
status: 401,
|
|
1904
|
-
statusText: "Unauthorized",
|
|
1905
|
-
data: null
|
|
1906
|
-
});
|
|
1907
|
-
throw new APIError("unauthorized", "get_user", mockFetchError, `The token provided ${chalk.bold(maskToken(token))} is invalid.
|
|
1908
|
-
Please make sure you are using the correct token and try again.`);
|
|
1909
|
-
}
|
|
1910
|
-
if (typeof error === "object" && error !== null && Object.keys(error).length === 0) {
|
|
1911
|
-
const mockFetchError = new FetchError("Network Error", {
|
|
1912
|
-
status: 500,
|
|
1913
|
-
statusText: "Internal Server Error",
|
|
1914
|
-
data: null
|
|
1915
|
-
});
|
|
1916
|
-
throw new APIError("network_error", "get_user", mockFetchError);
|
|
1917
|
-
}
|
|
1918
|
-
throw new APIError("generic", "get_user", error);
|
|
1941
|
+
const status = error?.response?.status;
|
|
1942
|
+
const customMessage = status === 401 ? `The token provided ${chalk.bold(maskToken(token))} is invalid.
|
|
1943
|
+
Please make sure you are using the correct token and try again.` : void 0;
|
|
1944
|
+
handleAPIError("get_user", error, customMessage);
|
|
1919
1945
|
}
|
|
1920
1946
|
};
|
|
1921
1947
|
|
|
@@ -1926,17 +1952,7 @@ const loginWithToken = async (token, region) => {
|
|
|
1926
1952
|
if (error instanceof APIError) {
|
|
1927
1953
|
throw error;
|
|
1928
1954
|
}
|
|
1929
|
-
|
|
1930
|
-
const status = error.response.status;
|
|
1931
|
-
switch (status) {
|
|
1932
|
-
case 401:
|
|
1933
|
-
throw new APIError("unauthorized", "login_with_token", error, `The token provided ${chalk.bold(maskToken(token))} is invalid.
|
|
1934
|
-
Please make sure you are using the correct token and try again.`);
|
|
1935
|
-
default:
|
|
1936
|
-
throw new APIError("network_error", "login_with_token", error);
|
|
1937
|
-
}
|
|
1938
|
-
}
|
|
1939
|
-
throw new APIError("generic", "login_with_token", error, "The provided credentials are invalid");
|
|
1955
|
+
handleAPIError("login_with_token", error);
|
|
1940
1956
|
}
|
|
1941
1957
|
};
|
|
1942
1958
|
const loginWithEmailAndPassword = async (email, password, region) => {
|
|
@@ -2322,7 +2338,7 @@ const fetchComponentInternalTags = async (spaceId) => {
|
|
|
2322
2338
|
const saveComponentsToFiles = async (space, spaceData, options) => {
|
|
2323
2339
|
const { components = [], groups = [], presets = [], internalTags = [] } = spaceData;
|
|
2324
2340
|
const { filename = DEFAULT_COMPONENTS_FILENAME, suffix, path, separateFiles } = options;
|
|
2325
|
-
const resolvedPath = path ? resolve
|
|
2341
|
+
const resolvedPath = path ? resolve(process.cwd(), path, "components", space) : resolvePath(path, `components/${space}`);
|
|
2326
2342
|
try {
|
|
2327
2343
|
if (separateFiles) {
|
|
2328
2344
|
for (const component of components) {
|
|
@@ -2696,17 +2712,17 @@ pullCmd$4.action(async (componentName, options, command) => {
|
|
|
2696
2712
|
konsola.warn(`The --filename option is ignored when using --separate-files`);
|
|
2697
2713
|
}
|
|
2698
2714
|
const filePath = `${componentsOutputDir}/`;
|
|
2699
|
-
const displayPath = path && isAbsolute(path) ? filePath : `${relative
|
|
2715
|
+
const displayPath = path && isAbsolute(path) ? filePath : `${relative(process.cwd(), componentsOutputDir)}/`;
|
|
2700
2716
|
konsola.ok(`Components downloaded successfully to ${chalk.hex(colorPalette.PRIMARY)(displayPath)}`);
|
|
2701
2717
|
} else if (componentName) {
|
|
2702
2718
|
const fileName = suffix ? `${actualFilename}.${suffix}.json` : `${componentName}.json`;
|
|
2703
|
-
const filePath = join
|
|
2704
|
-
const displayPath = path && isAbsolute(path) ? filePath : relative
|
|
2719
|
+
const filePath = join(componentsOutputDir, fileName);
|
|
2720
|
+
const displayPath = path && isAbsolute(path) ? filePath : relative(process.cwd(), filePath);
|
|
2705
2721
|
konsola.ok(`Component ${chalk.hex(colorPalette.PRIMARY)(componentName)} downloaded successfully in ${chalk.hex(colorPalette.PRIMARY)(displayPath)}`);
|
|
2706
2722
|
} else {
|
|
2707
2723
|
const fileName = suffix ? `${actualFilename}.${suffix}.json` : `${actualFilename}.json`;
|
|
2708
|
-
const filePath = join
|
|
2709
|
-
const displayPath = path && isAbsolute(path) ? filePath : relative
|
|
2724
|
+
const filePath = join(componentsOutputDir, fileName);
|
|
2725
|
+
const displayPath = path && isAbsolute(path) ? filePath : relative(process.cwd(), filePath);
|
|
2710
2726
|
konsola.ok(`Components downloaded successfully to ${chalk.hex(colorPalette.PRIMARY)(displayPath)}`);
|
|
2711
2727
|
}
|
|
2712
2728
|
konsola.br();
|
|
@@ -3889,8 +3905,8 @@ pullCmd$3.action(async (options, command) => {
|
|
|
3889
3905
|
});
|
|
3890
3906
|
const languagesOutputDir = resolveCommandPath("languages", space, path);
|
|
3891
3907
|
const fileName = suffix ? `${filename}.${suffix}.json` : `${filename}.json`;
|
|
3892
|
-
const filePath = join
|
|
3893
|
-
const displayPath = path && isAbsolute(path) ? filePath : relative
|
|
3908
|
+
const filePath = join(languagesOutputDir, fileName);
|
|
3909
|
+
const displayPath = path && isAbsolute(path) ? filePath : relative(process.cwd(), filePath);
|
|
3894
3910
|
spinner.succeed();
|
|
3895
3911
|
konsola.ok(`Languages schema downloaded successfully at ${chalk.hex(colorPalette.PRIMARY)(displayPath)}`, true);
|
|
3896
3912
|
} catch (error) {
|
|
@@ -3920,7 +3936,7 @@ const getMigrationTemplate = () => {
|
|
|
3920
3936
|
`;
|
|
3921
3937
|
};
|
|
3922
3938
|
const generateMigration = async (space, path, component, suffix) => {
|
|
3923
|
-
const resolvedPath = path ? resolve
|
|
3939
|
+
const resolvedPath = path ? resolve(process.cwd(), path, "migrations", space) : resolvePath(path, `migrations/${space}`);
|
|
3924
3940
|
const fileName = suffix ? `${component.name}.${suffix}.js` : `${component.name}.js`;
|
|
3925
3941
|
const migrationPath = join(resolvedPath, fileName);
|
|
3926
3942
|
try {
|
|
@@ -4025,13 +4041,13 @@ const createStory = async (spaceId, payload) => {
|
|
|
4025
4041
|
},
|
|
4026
4042
|
body: {
|
|
4027
4043
|
story: payload.story,
|
|
4028
|
-
publish: payload.publish
|
|
4044
|
+
...payload.publish ? { publish: payload.publish } : {}
|
|
4029
4045
|
},
|
|
4030
4046
|
throwOnError: true
|
|
4031
4047
|
});
|
|
4032
4048
|
return data?.story;
|
|
4033
|
-
} catch (
|
|
4034
|
-
handleAPIError("create_story",
|
|
4049
|
+
} catch (error) {
|
|
4050
|
+
handleAPIError("create_story", error);
|
|
4035
4051
|
}
|
|
4036
4052
|
};
|
|
4037
4053
|
const updateStory = async (spaceId, storyId, payload) => {
|
|
@@ -4045,18 +4061,20 @@ const updateStory = async (spaceId, storyId, payload) => {
|
|
|
4045
4061
|
body: {
|
|
4046
4062
|
story: payload.story,
|
|
4047
4063
|
force_update: payload.force_update === "1" ? "1" : "0",
|
|
4048
|
-
publish: payload.publish
|
|
4064
|
+
...payload.publish ? { publish: payload.publish } : {}
|
|
4049
4065
|
},
|
|
4050
4066
|
throwOnError: true
|
|
4051
4067
|
});
|
|
4052
|
-
const
|
|
4068
|
+
const story = data?.story;
|
|
4053
4069
|
if (!story) {
|
|
4054
4070
|
throw new Error("Failed to update story");
|
|
4055
4071
|
}
|
|
4056
4072
|
return story;
|
|
4057
|
-
} catch (
|
|
4058
|
-
|
|
4059
|
-
|
|
4073
|
+
} catch (error) {
|
|
4074
|
+
if (error instanceof Error && error.message === "Failed to update story") {
|
|
4075
|
+
throw error;
|
|
4076
|
+
}
|
|
4077
|
+
handleAPIError("update_story", error);
|
|
4060
4078
|
}
|
|
4061
4079
|
};
|
|
4062
4080
|
|
|
@@ -4526,8 +4544,8 @@ const findComponentSchemas = async (directoryPath) => {
|
|
|
4526
4544
|
}
|
|
4527
4545
|
throw error;
|
|
4528
4546
|
});
|
|
4529
|
-
const fileContents = files.filter((f) =>
|
|
4530
|
-
const filePath =
|
|
4547
|
+
const fileContents = files.filter((f) => extname(f) === ".json").map((f) => {
|
|
4548
|
+
const filePath = join(directoryPath, f);
|
|
4531
4549
|
const fileContent = readFileSync(filePath, "utf-8");
|
|
4532
4550
|
return JSON.parse(fileContent);
|
|
4533
4551
|
});
|
|
@@ -5592,7 +5610,7 @@ const getComponentPropertiesTypeAnnotations = async (component, options, spaceDa
|
|
|
5592
5610
|
};
|
|
5593
5611
|
const loadCustomFieldsParser = async (path) => {
|
|
5594
5612
|
try {
|
|
5595
|
-
const customFieldsParser = await import(resolve
|
|
5613
|
+
const customFieldsParser = await import(pathToFileURL(resolve(path)).href);
|
|
5596
5614
|
return customFieldsParser.default;
|
|
5597
5615
|
} catch (error) {
|
|
5598
5616
|
handleError(error);
|
|
@@ -5601,7 +5619,7 @@ const loadCustomFieldsParser = async (path) => {
|
|
|
5601
5619
|
};
|
|
5602
5620
|
async function loadCompilerOptions(path) {
|
|
5603
5621
|
if (path) {
|
|
5604
|
-
const compilerOptions = await import(resolve
|
|
5622
|
+
const compilerOptions = await import(pathToFileURL(resolve(path)).href);
|
|
5605
5623
|
return compilerOptions.default;
|
|
5606
5624
|
}
|
|
5607
5625
|
return {};
|
|
@@ -5751,7 +5769,7 @@ const generateTypes = async (spaceData, options = {
|
|
|
5751
5769
|
};
|
|
5752
5770
|
const saveTypesToComponentsFile = async (space, typedefData, options) => {
|
|
5753
5771
|
const { filename = DEFAULT_COMPONENT_FILENAME, path, separateFiles } = options;
|
|
5754
|
-
const resolvedPath = path ? resolve
|
|
5772
|
+
const resolvedPath = path ? resolve(process.cwd(), path, "types", space) : resolvePath(path, `types/${space}`);
|
|
5755
5773
|
try {
|
|
5756
5774
|
if (separateFiles && Array.isArray(typedefData)) {
|
|
5757
5775
|
for (const { name, content } of typedefData) {
|
|
@@ -5767,7 +5785,7 @@ const saveTypesToComponentsFile = async (space, typedefData, options) => {
|
|
|
5767
5785
|
const generateStoryblokTypes = async (options = {}) => {
|
|
5768
5786
|
const { path } = options;
|
|
5769
5787
|
try {
|
|
5770
|
-
const storyblokTypesPath = resolve
|
|
5788
|
+
const storyblokTypesPath = resolve(__dirname, "./index.d.ts");
|
|
5771
5789
|
const storyblokTypesContent = readFileSync(storyblokTypesPath, "utf-8");
|
|
5772
5790
|
const typeDefs = [
|
|
5773
5791
|
"// This file was generated by the Storyblok CLI.",
|
|
@@ -5775,7 +5793,7 @@ const generateStoryblokTypes = async (options = {}) => {
|
|
|
5775
5793
|
`import type { ${STORY_TYPE} } from '@storyblok/js';`,
|
|
5776
5794
|
storyblokTypesContent
|
|
5777
5795
|
].join("\n");
|
|
5778
|
-
const resolvedPath = path ? resolve
|
|
5796
|
+
const resolvedPath = path ? resolve(process.cwd(), path, "types") : resolvePath(path, "types");
|
|
5779
5797
|
await saveToFile(join(resolvedPath, `storyblok.d.ts`), typeDefs);
|
|
5780
5798
|
return true;
|
|
5781
5799
|
} catch (error) {
|
|
@@ -6094,7 +6112,7 @@ const fetchDatasource = async (spaceId, datasourceName) => {
|
|
|
6094
6112
|
};
|
|
6095
6113
|
const saveDatasourcesToFiles = async (space, datasources, options) => {
|
|
6096
6114
|
const { filename = DEFAULT_DATASOURCES_FILENAME, suffix, path, separateFiles } = options;
|
|
6097
|
-
const resolvedPath = path ? resolve
|
|
6115
|
+
const resolvedPath = path ? resolve(process.cwd(), path, "datasources", space) : resolvePath(path, `datasources/${space}`);
|
|
6098
6116
|
try {
|
|
6099
6117
|
if (separateFiles) {
|
|
6100
6118
|
for (const datasource of datasources) {
|
|
@@ -6162,17 +6180,17 @@ pullCmd$2.action(async (datasourceName, options, command) => {
|
|
|
6162
6180
|
konsola.warn(`The --filename option is ignored when using --separate-files`);
|
|
6163
6181
|
}
|
|
6164
6182
|
const filePath = `${datasourcesOutputDir}/`;
|
|
6165
|
-
const displayPath = path && isAbsolute(path) ? filePath : `${relative
|
|
6183
|
+
const displayPath = path && isAbsolute(path) ? filePath : `${relative(process.cwd(), datasourcesOutputDir)}/`;
|
|
6166
6184
|
konsola.ok(`Datasources downloaded successfully to ${chalk.hex(colorPalette.PRIMARY)(displayPath)}`);
|
|
6167
6185
|
} else if (datasourceName) {
|
|
6168
6186
|
const fileName = suffix ? `${actualFilename}.${suffix}.json` : `${datasourceName}.json`;
|
|
6169
|
-
const filePath = join
|
|
6170
|
-
const displayPath = path && isAbsolute(path) ? filePath : relative
|
|
6187
|
+
const filePath = join(datasourcesOutputDir, fileName);
|
|
6188
|
+
const displayPath = path && isAbsolute(path) ? filePath : relative(process.cwd(), filePath);
|
|
6171
6189
|
konsola.ok(`Datasource ${chalk.hex(colorPalette.PRIMARY)(datasourceName)} downloaded successfully in ${chalk.hex(colorPalette.PRIMARY)(displayPath)}`);
|
|
6172
6190
|
} else {
|
|
6173
6191
|
const fileName = suffix ? `${actualFilename}.${suffix}.json` : `${actualFilename}.json`;
|
|
6174
|
-
const filePath = join
|
|
6175
|
-
const displayPath = path && isAbsolute(path) ? filePath : relative
|
|
6192
|
+
const filePath = join(datasourcesOutputDir, fileName);
|
|
6193
|
+
const displayPath = path && isAbsolute(path) ? filePath : relative(process.cwd(), filePath);
|
|
6176
6194
|
konsola.ok(`Datasources downloaded successfully to ${chalk.hex(colorPalette.PRIMARY)(displayPath)}`);
|
|
6177
6195
|
}
|
|
6178
6196
|
konsola.br();
|
|
@@ -6381,7 +6399,7 @@ const createOctokit = (token) => {
|
|
|
6381
6399
|
|
|
6382
6400
|
const generateProject = async (blueprint, projectName, targetPath = process.cwd()) => {
|
|
6383
6401
|
try {
|
|
6384
|
-
const projectPath =
|
|
6402
|
+
const projectPath = join(targetPath, projectName);
|
|
6385
6403
|
const templateRepo = `storyblok/blueprint-core-${blueprint}`;
|
|
6386
6404
|
try {
|
|
6387
6405
|
await fs.access(projectPath);
|
|
@@ -6418,7 +6436,7 @@ const generateProject = async (blueprint, projectName, targetPath = process.cwd(
|
|
|
6418
6436
|
};
|
|
6419
6437
|
const createEnvFile = async (projectPath, storyblokVars, additionalVars) => {
|
|
6420
6438
|
try {
|
|
6421
|
-
const envPath =
|
|
6439
|
+
const envPath = join(projectPath, ".env");
|
|
6422
6440
|
let envContent = `# Storyblok Configuration
|
|
6423
6441
|
${Object.entries(storyblokVars).map(([key, value]) => `${key}=${value}`).join("\n")}
|
|
6424
6442
|
`;
|
|
@@ -6636,7 +6654,7 @@ program$5.command(`${commands.CREATE} [project-path]`).alias("c").description(`S
|
|
|
6636
6654
|
if (!value.trim()) {
|
|
6637
6655
|
return "Project path is required";
|
|
6638
6656
|
}
|
|
6639
|
-
const projectName2 =
|
|
6657
|
+
const projectName2 = basename(value);
|
|
6640
6658
|
if (!/^[\w-]+$/.test(projectName2)) {
|
|
6641
6659
|
return "Project name (last part of the path) can only contain letters, numbers, hyphens, and underscores";
|
|
6642
6660
|
}
|
|
@@ -6644,9 +6662,9 @@ program$5.command(`${commands.CREATE} [project-path]`).alias("c").description(`S
|
|
|
6644
6662
|
}
|
|
6645
6663
|
});
|
|
6646
6664
|
}
|
|
6647
|
-
const resolvedPath =
|
|
6648
|
-
const targetDirectory =
|
|
6649
|
-
const projectName =
|
|
6665
|
+
const resolvedPath = resolve(finalProjectPath);
|
|
6666
|
+
const targetDirectory = dirname(resolvedPath);
|
|
6667
|
+
const projectName = basename(resolvedPath);
|
|
6650
6668
|
konsola.br();
|
|
6651
6669
|
konsola.info(`Scaffolding your project using the ${chalk.hex(colorPalette.CREATE)(technologyTemplate)} template...`);
|
|
6652
6670
|
await generateProject(technologyTemplate, projectName, targetDirectory);
|
|
@@ -7106,7 +7124,7 @@ const parseAssetData = (raw) => {
|
|
|
7106
7124
|
}
|
|
7107
7125
|
};
|
|
7108
7126
|
const getSidecarFilename = (assetBinaryPath) => {
|
|
7109
|
-
return join(dirname
|
|
7127
|
+
return join(dirname(assetBinaryPath), `${basename(assetBinaryPath, extname(assetBinaryPath))}.json`);
|
|
7110
7128
|
};
|
|
7111
7129
|
const loadSidecarAssetData = async (assetBinaryPath) => {
|
|
7112
7130
|
const sidecarPath = getSidecarFilename(assetBinaryPath);
|
|
@@ -7460,31 +7478,25 @@ const readLocalAssetsStream = ({
|
|
|
7460
7478
|
const iterator = async function* readAssets() {
|
|
7461
7479
|
try {
|
|
7462
7480
|
const files = await readdir(directoryPath);
|
|
7463
|
-
const
|
|
7464
|
-
setTotalAssets?.(
|
|
7465
|
-
for (const file of
|
|
7466
|
-
const
|
|
7481
|
+
const binaryFiles = files.filter((f) => SUPPORTED_ASSET_EXTENSIONS.has(extname(f).toLowerCase()));
|
|
7482
|
+
setTotalAssets?.(binaryFiles.length);
|
|
7483
|
+
for (const file of binaryFiles) {
|
|
7484
|
+
const binaryFilePath = join(directoryPath, file);
|
|
7467
7485
|
try {
|
|
7468
|
-
const
|
|
7469
|
-
|
|
7470
|
-
continue;
|
|
7471
|
-
}
|
|
7472
|
-
const metadataContent = await readFile$1(filePath, "utf8");
|
|
7473
|
-
const assetRaw = JSON.parse(metadataContent);
|
|
7486
|
+
const sidecar = await loadSidecarAssetData(binaryFilePath);
|
|
7487
|
+
const shortFilename = sidecar.short_filename || (sidecar.filename ? basename(sidecar.filename) : void 0) || file;
|
|
7474
7488
|
const asset = {
|
|
7475
|
-
...
|
|
7476
|
-
short_filename:
|
|
7489
|
+
...sidecar,
|
|
7490
|
+
short_filename: shortFilename
|
|
7477
7491
|
};
|
|
7478
|
-
const
|
|
7479
|
-
const
|
|
7480
|
-
const assetBinaryPath = join(directoryPath, `${baseName}${extFromMetadata}`);
|
|
7481
|
-
const fileBuffer = await readFile$1(assetBinaryPath);
|
|
7492
|
+
const fileBuffer = await readFile$1(binaryFilePath);
|
|
7493
|
+
const sidecarPath = getSidecarFilename(binaryFilePath);
|
|
7482
7494
|
yield {
|
|
7483
7495
|
asset,
|
|
7484
7496
|
context: {
|
|
7485
7497
|
fileBuffer,
|
|
7486
|
-
assetBinaryPath,
|
|
7487
|
-
assetPath:
|
|
7498
|
+
assetBinaryPath: binaryFilePath,
|
|
7499
|
+
assetPath: sidecarPath
|
|
7488
7500
|
}
|
|
7489
7501
|
};
|
|
7490
7502
|
} catch (maybeError) {
|
|
@@ -7868,6 +7880,9 @@ const traverseAndMapBySchema = (data, {
|
|
|
7868
7880
|
processedFields,
|
|
7869
7881
|
missingSchemas
|
|
7870
7882
|
}) => {
|
|
7883
|
+
if (!data?.component) {
|
|
7884
|
+
return data ?? {};
|
|
7885
|
+
}
|
|
7871
7886
|
const schema = schemas[data.component];
|
|
7872
7887
|
if (!schema) {
|
|
7873
7888
|
missingSchemas.add(data.component);
|
|
@@ -7911,7 +7926,7 @@ const traverseAndMapRichtextDoc = (data, {
|
|
|
7911
7926
|
}));
|
|
7912
7927
|
}
|
|
7913
7928
|
if (data && typeof data === "object") {
|
|
7914
|
-
if (data.type === "link" && data.attrs
|
|
7929
|
+
if (data.type === "link" && data.attrs?.linktype === "story") {
|
|
7915
7930
|
return {
|
|
7916
7931
|
...data,
|
|
7917
7932
|
attrs: {
|
|
@@ -7925,7 +7940,7 @@ const traverseAndMapRichtextDoc = (data, {
|
|
|
7925
7940
|
...data,
|
|
7926
7941
|
attrs: {
|
|
7927
7942
|
...data.attrs,
|
|
7928
|
-
body: data.attrs
|
|
7943
|
+
body: (data.attrs?.body ?? []).map((d) => traverseAndMapBySchema(d, {
|
|
7929
7944
|
schemas,
|
|
7930
7945
|
maps,
|
|
7931
7946
|
fieldRefMappers: fieldRefMappers2,
|
|
@@ -7967,7 +7982,7 @@ const multilinkFieldRefMapper = (data, { maps }) => {
|
|
|
7967
7982
|
};
|
|
7968
7983
|
const bloksFieldRefMapper = (data, { schemas, maps, fieldRefMappers: fieldRefMappers2, processedFields, missingSchemas }) => {
|
|
7969
7984
|
if (!Array.isArray(data)) {
|
|
7970
|
-
throw new TypeError(
|
|
7985
|
+
throw new TypeError(`Invalid bloks field: expected an array, but received ${JSON.stringify(data)}. Please make sure your bloks field value is an array of components (e.g. [{ component: "my_blok", ... }]).`);
|
|
7971
7986
|
}
|
|
7972
7987
|
return data.map((d) => traverseAndMapBySchema(d, {
|
|
7973
7988
|
schemas,
|
|
@@ -7986,7 +8001,7 @@ const assetFieldRefMapper = (data, { maps }) => {
|
|
|
7986
8001
|
};
|
|
7987
8002
|
const multiassetFieldRefMapper = (data, options) => {
|
|
7988
8003
|
if (!Array.isArray(data)) {
|
|
7989
|
-
throw new TypeError(
|
|
8004
|
+
throw new TypeError(`Invalid multiasset field: expected an array, but received ${JSON.stringify(data)}. Please make sure your multiasset field value is an array of asset objects (e.g. [{ filename: "...", id: 123 }]).`);
|
|
7990
8005
|
}
|
|
7991
8006
|
return data.map((d) => assetFieldRefMapper(d, options));
|
|
7992
8007
|
};
|
|
@@ -8009,23 +8024,23 @@ const storyRefMapper = (story, { schemas, maps }) => {
|
|
|
8009
8024
|
const missingSchemas = /* @__PURE__ */ new Set();
|
|
8010
8025
|
const alternates = story.alternates ? story.alternates.map((a) => ({
|
|
8011
8026
|
...a,
|
|
8012
|
-
id: maps.stories?.get(a.id)
|
|
8013
|
-
parent_id: maps.stories?.get(a.parent_id)
|
|
8027
|
+
id: maps.stories?.get(a.id) ?? a.id,
|
|
8028
|
+
parent_id: maps.stories?.get(a.parent_id) ?? a.parent_id
|
|
8014
8029
|
})) : story.alternates;
|
|
8015
|
-
const parentId = maps.stories?.get(story.parent_id)
|
|
8030
|
+
const parentId = maps.stories?.get(story.parent_id) ?? story.parent_id;
|
|
8016
8031
|
const mappedStory = {
|
|
8017
8032
|
...story,
|
|
8018
|
-
content: traverseAndMapBySchema(story.content, {
|
|
8033
|
+
content: story.content?.component ? traverseAndMapBySchema(story.content, {
|
|
8019
8034
|
schemas,
|
|
8020
8035
|
maps,
|
|
8021
8036
|
fieldRefMappers,
|
|
8022
8037
|
processedFields,
|
|
8023
8038
|
missingSchemas
|
|
8024
|
-
}),
|
|
8025
|
-
id: Number(maps.stories?.get(story.id)
|
|
8026
|
-
uuid: String(maps.stories?.get(story.uuid)
|
|
8039
|
+
}) : story.content,
|
|
8040
|
+
id: Number(maps.stories?.get(story.id) ?? story.id),
|
|
8041
|
+
uuid: String(maps.stories?.get(story.uuid) ?? story.uuid),
|
|
8027
8042
|
// @ts-expect-error Our types are wrong.
|
|
8028
|
-
parent_id: parentId ? Number(parentId) : null,
|
|
8043
|
+
parent_id: parentId != null ? Number(parentId) : null,
|
|
8029
8044
|
alternates
|
|
8030
8045
|
};
|
|
8031
8046
|
return {
|
|
@@ -8187,14 +8202,14 @@ const getRemoteStory = async ({ spaceId, storyId }) => {
|
|
|
8187
8202
|
return data?.story;
|
|
8188
8203
|
};
|
|
8189
8204
|
const makeCreateStoryAPITransport = ({ spaceId }) => async (localStory) => {
|
|
8190
|
-
const { id: _id, uuid: _uuid,
|
|
8205
|
+
const { id: _id, uuid: _uuid, parent_id: _parentId, content, ...newStoryData } = localStory;
|
|
8206
|
+
if (!localStory.is_folder && !content?.component) {
|
|
8207
|
+
throw new Error(`Story "${localStory.slug}" is missing a content type (content.component). Every story must define a content field with a valid component.`);
|
|
8208
|
+
}
|
|
8191
8209
|
const remoteStory = await createStory(spaceId, {
|
|
8192
8210
|
story: {
|
|
8193
8211
|
...newStoryData,
|
|
8194
|
-
content: {
|
|
8195
|
-
_uid: "",
|
|
8196
|
-
component: "__tmp__"
|
|
8197
|
-
}
|
|
8212
|
+
...content?.component ? { content: { _uid: "", component: "__migration_artifact__" } } : {}
|
|
8198
8213
|
},
|
|
8199
8214
|
publish: 0
|
|
8200
8215
|
});
|
|
@@ -8265,12 +8280,12 @@ const createStoryPlaceholderStream = ({
|
|
|
8265
8280
|
});
|
|
8266
8281
|
};
|
|
8267
8282
|
const makeWriteStoryFSTransport = ({ directoryPath }) => async (story) => {
|
|
8268
|
-
await saveToFile(resolve
|
|
8283
|
+
await saveToFile(resolve(directoryPath, getStoryFilename(story)), JSON.stringify(story, null, 2));
|
|
8269
8284
|
return story;
|
|
8270
8285
|
};
|
|
8271
8286
|
const makeWriteStoryAPITransport = ({ spaceId, publish }) => (mappedLocalStory) => updateStory(spaceId, mappedLocalStory.id, {
|
|
8272
8287
|
story: mappedLocalStory,
|
|
8273
|
-
publish: publish ?? (mappedLocalStory
|
|
8288
|
+
publish: publish ?? (isStoryPublishedWithoutChanges(mappedLocalStory) ? 1 : 0)
|
|
8274
8289
|
});
|
|
8275
8290
|
const makeCleanupStoryFSTransport = ({ directoryPath, maps }) => async (mappedStory) => {
|
|
8276
8291
|
const mapEntry = maps.stories?.entries().find(([_, v]) => v === mappedStory.uuid);
|
|
@@ -8279,7 +8294,7 @@ const makeCleanupStoryFSTransport = ({ directoryPath, maps }) => async (mappedSt
|
|
|
8279
8294
|
slug: mappedStory.slug,
|
|
8280
8295
|
uuid: originalUuid
|
|
8281
8296
|
});
|
|
8282
|
-
const storyFilePath = resolve
|
|
8297
|
+
const storyFilePath = resolve(directoryPath, storyFilename);
|
|
8283
8298
|
await unlink(storyFilePath);
|
|
8284
8299
|
};
|
|
8285
8300
|
const writeStoryStream = ({
|
|
@@ -8896,14 +8911,14 @@ pushCmd.action(async (options, command) => {
|
|
|
8896
8911
|
processProgress.setTotal(total);
|
|
8897
8912
|
updateProgress.setTotal(total);
|
|
8898
8913
|
},
|
|
8899
|
-
onStoryError(error) {
|
|
8914
|
+
onStoryError(error, filename) {
|
|
8900
8915
|
summary.creationResults.failed += 1;
|
|
8901
8916
|
summary.processResults.total -= 1;
|
|
8902
8917
|
summary.updateResults.total -= 1;
|
|
8903
8918
|
processProgress.setTotal(summary.processResults.total);
|
|
8904
8919
|
updateProgress.setTotal(summary.updateResults.total);
|
|
8905
8920
|
creationProgress.increment();
|
|
8906
|
-
handleError(error, verbose);
|
|
8921
|
+
handleError(error, verbose, { storyFile: filename });
|
|
8907
8922
|
}
|
|
8908
8923
|
}),
|
|
8909
8924
|
// Create remote stories.
|
|
@@ -8912,7 +8927,6 @@ pushCmd.action(async (options, command) => {
|
|
|
8912
8927
|
spaceId: space,
|
|
8913
8928
|
transports: {
|
|
8914
8929
|
createStory: options.dryRun ? async (story) => story : makeCreateStoryAPITransport({
|
|
8915
|
-
maps,
|
|
8916
8930
|
spaceId: space
|
|
8917
8931
|
}),
|
|
8918
8932
|
appendStoryManifest: options.dryRun ? () => Promise.resolve() : makeAppendToManifestFSTransport({
|
|
@@ -8937,13 +8951,13 @@ pushCmd.action(async (options, command) => {
|
|
|
8937
8951
|
logger.info("Skipped creating story", { storyId: localStory.uuid });
|
|
8938
8952
|
summary.creationResults.skipped += 1;
|
|
8939
8953
|
},
|
|
8940
|
-
onStoryError(error) {
|
|
8954
|
+
onStoryError(error, localStory) {
|
|
8941
8955
|
summary.creationResults.failed += 1;
|
|
8942
8956
|
summary.processResults.total -= 1;
|
|
8943
8957
|
summary.updateResults.total -= 1;
|
|
8944
8958
|
processProgress.setTotal(summary.processResults.total);
|
|
8945
8959
|
updateProgress.setTotal(summary.updateResults.total);
|
|
8946
|
-
handleError(error, verbose);
|
|
8960
|
+
handleError(error, verbose, { storyId: localStory?.uuid });
|
|
8947
8961
|
},
|
|
8948
8962
|
onIncrement() {
|
|
8949
8963
|
creationProgress.increment();
|
|
@@ -8963,13 +8977,13 @@ pushCmd.action(async (options, command) => {
|
|
|
8963
8977
|
processProgress.setTotal(total);
|
|
8964
8978
|
updateProgress.setTotal(total);
|
|
8965
8979
|
},
|
|
8966
|
-
onStoryError(error) {
|
|
8980
|
+
onStoryError(error, filename) {
|
|
8967
8981
|
summary.creationResults.failed += 1;
|
|
8968
8982
|
summary.processResults.total -= 1;
|
|
8969
8983
|
summary.updateResults.total -= 1;
|
|
8970
8984
|
processProgress.setTotal(summary.processResults.total);
|
|
8971
8985
|
updateProgress.setTotal(summary.updateResults.total);
|
|
8972
|
-
handleError(error, verbose);
|
|
8986
|
+
handleError(error, verbose, { storyFile: filename });
|
|
8973
8987
|
}
|
|
8974
8988
|
}),
|
|
8975
8989
|
// Map all references to numeric ids and uuids.
|