storyblok 4.2.2 → 4.3.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 +297 -311
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -3
package/dist/index.mjs
CHANGED
|
@@ -9,8 +9,10 @@ import { Spinner } from '@topcli/spinner';
|
|
|
9
9
|
import { select, password, input, confirm } from '@inquirer/prompts';
|
|
10
10
|
import fs, { mkdir, writeFile, readFile as readFile$1, access, readdir } from 'node:fs/promises';
|
|
11
11
|
import path, { join, parse, resolve } from 'node:path';
|
|
12
|
+
import filenamify from 'filenamify';
|
|
12
13
|
import { exec, spawn } from 'node:child_process';
|
|
13
14
|
import { promisify } from 'node:util';
|
|
15
|
+
import { getRegion } from '@storyblok/region-helper';
|
|
14
16
|
import { minimatch } from 'minimatch';
|
|
15
17
|
import { hash } from 'ohash';
|
|
16
18
|
import { compile } from 'json-schema-to-typescript';
|
|
@@ -613,6 +615,11 @@ const resolvePath = (path, folder) => {
|
|
|
613
615
|
const getComponentNameFromFilename = (filename) => {
|
|
614
616
|
return filename.replace(/\.js$/, "");
|
|
615
617
|
};
|
|
618
|
+
const sanitizeFilename = (filename) => {
|
|
619
|
+
return filenamify(filename, {
|
|
620
|
+
replacement: "_"
|
|
621
|
+
});
|
|
622
|
+
};
|
|
616
623
|
async function readJsonFile(filePath) {
|
|
617
624
|
try {
|
|
618
625
|
const content = (await readFile(filePath)).toString();
|
|
@@ -1019,8 +1026,30 @@ program$f.command(commands.USER).description("Get the current user").action(asyn
|
|
|
1019
1026
|
konsola.br();
|
|
1020
1027
|
});
|
|
1021
1028
|
|
|
1029
|
+
function getRegionFromSpaceId(spaceId) {
|
|
1030
|
+
try {
|
|
1031
|
+
const region = getRegion(spaceId);
|
|
1032
|
+
return region;
|
|
1033
|
+
} catch (error) {
|
|
1034
|
+
console.warn(`Failed to determine region from space ID: ${error}`);
|
|
1035
|
+
return void 0;
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
const resolveRegion = async (thisCommand) => {
|
|
1039
|
+
const options = thisCommand.opts();
|
|
1040
|
+
const spaceId = options.space;
|
|
1041
|
+
if (spaceId) {
|
|
1042
|
+
const { state, initializeSession } = session();
|
|
1043
|
+
await initializeSession();
|
|
1044
|
+
const detectedRegion = getRegionFromSpaceId(spaceId);
|
|
1045
|
+
if (detectedRegion) {
|
|
1046
|
+
state.region = detectedRegion;
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
};
|
|
1050
|
+
|
|
1022
1051
|
const program$e = getProgram();
|
|
1023
|
-
const componentsCommand = program$e.command(commands.COMPONENTS).alias("comp").description(`Manage your space's block schema`).option("-s, --space <space>", "space ID").option("-p, --path <path>", "path to save the file. Default is .storyblok/components");
|
|
1052
|
+
const componentsCommand = program$e.command(commands.COMPONENTS).alias("comp").description(`Manage your space's block schema`).option("-s, --space <space>", "space ID").option("-p, --path <path>", "path to save the file. Default is .storyblok/components").hook("preAction", resolveRegion);
|
|
1024
1053
|
|
|
1025
1054
|
let instance = null;
|
|
1026
1055
|
const createMapiClient = (options) => {
|
|
@@ -1223,11 +1252,12 @@ const saveComponentsToFiles = async (space, spaceData, options) => {
|
|
|
1223
1252
|
try {
|
|
1224
1253
|
if (separateFiles) {
|
|
1225
1254
|
for (const component of components) {
|
|
1226
|
-
const
|
|
1255
|
+
const sanitizedName = sanitizeFilename(component.name);
|
|
1256
|
+
const componentFilePath = join(resolvedPath, suffix ? `${sanitizedName}.${suffix}.json` : `${sanitizedName}.json`);
|
|
1227
1257
|
await saveToFile(componentFilePath, JSON.stringify(component, null, 2));
|
|
1228
1258
|
const componentPresets = presets.filter((preset) => preset.component_id === component.id);
|
|
1229
1259
|
if (componentPresets.length > 0) {
|
|
1230
|
-
const presetsFilePath = join(resolvedPath, suffix ? `${
|
|
1260
|
+
const presetsFilePath = join(resolvedPath, suffix ? `${sanitizedName}.presets.${suffix}.json` : `${sanitizedName}.presets.json`);
|
|
1231
1261
|
await saveToFile(presetsFilePath, JSON.stringify(componentPresets, null, 2));
|
|
1232
1262
|
}
|
|
1233
1263
|
const groupsFilePath = join(resolvedPath, suffix ? `groups.${suffix}.json` : `groups.json`);
|
|
@@ -1569,153 +1599,6 @@ componentsCommand.command("pull [componentName]").option("-f, --filename <filena
|
|
|
1569
1599
|
}
|
|
1570
1600
|
});
|
|
1571
1601
|
|
|
1572
|
-
const pushDatasource = async (space, datasource) => {
|
|
1573
|
-
try {
|
|
1574
|
-
const client = mapiClient();
|
|
1575
|
-
const { data } = await client.post(`spaces/${space}/datasources`, {
|
|
1576
|
-
body: JSON.stringify(datasource)
|
|
1577
|
-
});
|
|
1578
|
-
return data.datasource;
|
|
1579
|
-
} catch (error) {
|
|
1580
|
-
handleAPIError("push_datasource", error, `Failed to push datasource ${datasource.name}`);
|
|
1581
|
-
}
|
|
1582
|
-
};
|
|
1583
|
-
const updateDatasource = async (space, datasourceId, datasource) => {
|
|
1584
|
-
try {
|
|
1585
|
-
const client = mapiClient();
|
|
1586
|
-
const { data } = await client.put(`spaces/${space}/datasources/${datasourceId}`, {
|
|
1587
|
-
body: JSON.stringify(datasource)
|
|
1588
|
-
});
|
|
1589
|
-
return data.datasource;
|
|
1590
|
-
} catch (error) {
|
|
1591
|
-
handleAPIError("update_datasource", error, `Failed to update datasource ${datasource.name}`);
|
|
1592
|
-
}
|
|
1593
|
-
};
|
|
1594
|
-
const upsertDatasource = async (space, datasource, existingId) => {
|
|
1595
|
-
if (existingId) {
|
|
1596
|
-
return await updateDatasource(space, existingId, datasource);
|
|
1597
|
-
} else {
|
|
1598
|
-
return await pushDatasource(space, datasource);
|
|
1599
|
-
}
|
|
1600
|
-
};
|
|
1601
|
-
const pushDatasourceEntry = async (space, datasourceId, entry) => {
|
|
1602
|
-
try {
|
|
1603
|
-
const client = mapiClient();
|
|
1604
|
-
const { data } = await client.post(`spaces/${space}/datasource_entries`, {
|
|
1605
|
-
body: JSON.stringify({
|
|
1606
|
-
datasource_entry: {
|
|
1607
|
-
...entry,
|
|
1608
|
-
datasource_id: datasourceId
|
|
1609
|
-
}
|
|
1610
|
-
})
|
|
1611
|
-
});
|
|
1612
|
-
return data.datasource_entry;
|
|
1613
|
-
} catch (error) {
|
|
1614
|
-
handleAPIError("push_datasource", error, `Failed to push datasource entry ${entry.name}`);
|
|
1615
|
-
}
|
|
1616
|
-
};
|
|
1617
|
-
const updateDatasourceEntry = async (space, entryId, entry) => {
|
|
1618
|
-
try {
|
|
1619
|
-
const client = mapiClient();
|
|
1620
|
-
await client.put(`spaces/${space}/datasource_entries/${entryId}`, {
|
|
1621
|
-
body: JSON.stringify({
|
|
1622
|
-
datasource_entry: entry
|
|
1623
|
-
})
|
|
1624
|
-
});
|
|
1625
|
-
} catch (error) {
|
|
1626
|
-
handleAPIError("update_datasource", error, `Failed to update datasource entry ${entry.name}`);
|
|
1627
|
-
}
|
|
1628
|
-
};
|
|
1629
|
-
const upsertDatasourceEntry = async (space, datasourceId, entry, existingId) => {
|
|
1630
|
-
if (existingId) {
|
|
1631
|
-
await updateDatasourceEntry(space, existingId, entry);
|
|
1632
|
-
return void 0;
|
|
1633
|
-
} else {
|
|
1634
|
-
return await pushDatasourceEntry(space, datasourceId, entry);
|
|
1635
|
-
}
|
|
1636
|
-
};
|
|
1637
|
-
const readDatasourcesFiles = async (options) => {
|
|
1638
|
-
const { from, path, separateFiles = false, suffix, space } = options;
|
|
1639
|
-
const resolvedPath = resolvePath(path, `datasources/${from}`);
|
|
1640
|
-
try {
|
|
1641
|
-
await readdir(resolvedPath);
|
|
1642
|
-
} catch (error) {
|
|
1643
|
-
const message = `No local datasources found for space ${chalk.bold(from)}. To push datasources, you need to pull them first:
|
|
1644
|
-
|
|
1645
|
-
1. Pull the datasources from your source space:
|
|
1646
|
-
${chalk.cyan(`storyblok datasources pull --space ${from}`)}
|
|
1647
|
-
|
|
1648
|
-
2. Then try pushing again:
|
|
1649
|
-
${chalk.cyan(`storyblok datasources push --space ${space} --from ${from}`)}`;
|
|
1650
|
-
throw new FileSystemError(
|
|
1651
|
-
"file_not_found",
|
|
1652
|
-
"read",
|
|
1653
|
-
error,
|
|
1654
|
-
message
|
|
1655
|
-
);
|
|
1656
|
-
}
|
|
1657
|
-
if (separateFiles) {
|
|
1658
|
-
return await readSeparateFiles(resolvedPath, suffix);
|
|
1659
|
-
}
|
|
1660
|
-
return await readConsolidatedFiles(resolvedPath, suffix);
|
|
1661
|
-
};
|
|
1662
|
-
async function readSeparateFiles(resolvedPath, suffix) {
|
|
1663
|
-
const files = await readdir(resolvedPath);
|
|
1664
|
-
const datasources = [];
|
|
1665
|
-
const filteredFiles = files.filter((file) => {
|
|
1666
|
-
if (suffix) {
|
|
1667
|
-
return file.endsWith(`.${suffix}.json`);
|
|
1668
|
-
} else {
|
|
1669
|
-
return !/\.\w+\.json$/.test(file);
|
|
1670
|
-
}
|
|
1671
|
-
});
|
|
1672
|
-
for (const file of filteredFiles) {
|
|
1673
|
-
const filePath = join(resolvedPath, file);
|
|
1674
|
-
if (file.endsWith(".json") || file.endsWith(`${suffix}.json`)) {
|
|
1675
|
-
if (file === "datasources.json" || /^datasources\.\w+\.json$/.test(file)) {
|
|
1676
|
-
continue;
|
|
1677
|
-
}
|
|
1678
|
-
const result = await readJsonFile(filePath);
|
|
1679
|
-
if (result.error) {
|
|
1680
|
-
handleFileSystemError("read", result.error);
|
|
1681
|
-
continue;
|
|
1682
|
-
}
|
|
1683
|
-
datasources.push(...result.data);
|
|
1684
|
-
}
|
|
1685
|
-
}
|
|
1686
|
-
return {
|
|
1687
|
-
datasources
|
|
1688
|
-
};
|
|
1689
|
-
}
|
|
1690
|
-
async function readConsolidatedFiles(resolvedPath, suffix) {
|
|
1691
|
-
const datasourcesPath = join(resolvedPath, suffix ? `datasources.${suffix}.json` : "datasources.json");
|
|
1692
|
-
const datasourcesResult = await readJsonFile(datasourcesPath);
|
|
1693
|
-
if (datasourcesResult.error || !datasourcesResult.data.length) {
|
|
1694
|
-
throw new FileSystemError(
|
|
1695
|
-
"file_not_found",
|
|
1696
|
-
"read",
|
|
1697
|
-
datasourcesResult.error || new Error("Datasources file is empty"),
|
|
1698
|
-
`No datasources found in ${datasourcesPath}. Please make sure you have pulled the datasources first.`
|
|
1699
|
-
);
|
|
1700
|
-
}
|
|
1701
|
-
return {
|
|
1702
|
-
datasources: datasourcesResult.data
|
|
1703
|
-
};
|
|
1704
|
-
}
|
|
1705
|
-
|
|
1706
|
-
function createStubDatasource(name) {
|
|
1707
|
-
return {
|
|
1708
|
-
id: 0,
|
|
1709
|
-
// Will be set by API
|
|
1710
|
-
name,
|
|
1711
|
-
slug: name,
|
|
1712
|
-
dimensions: [],
|
|
1713
|
-
entries: [],
|
|
1714
|
-
// Empty entries for stub
|
|
1715
|
-
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1716
|
-
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1717
|
-
};
|
|
1718
|
-
}
|
|
1719
1602
|
function buildDependencyGraph(context) {
|
|
1720
1603
|
const { spaceState } = context;
|
|
1721
1604
|
const graph = { nodes: /* @__PURE__ */ new Map() };
|
|
@@ -1727,22 +1610,6 @@ function buildDependencyGraph(context) {
|
|
|
1727
1610
|
dependency.dependents.add(dependentId);
|
|
1728
1611
|
}
|
|
1729
1612
|
}
|
|
1730
|
-
const referencedDatasources = /* @__PURE__ */ new Set();
|
|
1731
|
-
spaceState.local.components.forEach((component) => {
|
|
1732
|
-
if (component.schema) {
|
|
1733
|
-
const dependencies = collectWhitelistDependencies(component.schema);
|
|
1734
|
-
dependencies.datasourceNames.forEach((datasourceName) => {
|
|
1735
|
-
referencedDatasources.add(datasourceName);
|
|
1736
|
-
});
|
|
1737
|
-
}
|
|
1738
|
-
});
|
|
1739
|
-
referencedDatasources.forEach((datasourceName) => {
|
|
1740
|
-
const nodeId = `datasource:${datasourceName}`;
|
|
1741
|
-
const targetDatasource = spaceState.target.datasources?.get(datasourceName);
|
|
1742
|
-
const stubDatasource = createStubDatasource(datasourceName);
|
|
1743
|
-
const node = new DatasourceNode(nodeId, stubDatasource, targetDatasource);
|
|
1744
|
-
graph.nodes.set(nodeId, node);
|
|
1745
|
-
});
|
|
1746
1613
|
spaceState.local.internalTags.forEach((tag) => {
|
|
1747
1614
|
const nodeId = `tag:${tag.id}`;
|
|
1748
1615
|
const targetTag = spaceState.target.tags.get(tag.name);
|
|
@@ -1818,10 +1685,6 @@ function buildDependencyGraph(context) {
|
|
|
1818
1685
|
const dependencyId = `component:${componentName}`;
|
|
1819
1686
|
addDependency(componentId, dependencyId);
|
|
1820
1687
|
});
|
|
1821
|
-
dependencies.datasourceNames.forEach((datasourceName) => {
|
|
1822
|
-
const datasourceId = `datasource:${datasourceName}`;
|
|
1823
|
-
addDependency(componentId, datasourceId);
|
|
1824
|
-
});
|
|
1825
1688
|
}
|
|
1826
1689
|
});
|
|
1827
1690
|
spaceState.local.presets.forEach((preset) => {
|
|
@@ -2204,15 +2067,6 @@ class ComponentNode extends GraphNode {
|
|
|
2204
2067
|
});
|
|
2205
2068
|
}
|
|
2206
2069
|
}
|
|
2207
|
-
if ((resolvedField.type === "option" || resolvedField.type === "options") && resolvedField.source === "internal") {
|
|
2208
|
-
if (resolvedField.datasource_slug && typeof resolvedField.datasource_slug === "string") {
|
|
2209
|
-
const datasourceNodeId = `datasource:${resolvedField.datasource_slug}`;
|
|
2210
|
-
const datasourceNode = graph.nodes.get(datasourceNodeId);
|
|
2211
|
-
if (datasourceNode?.targetData) {
|
|
2212
|
-
resolvedField.datasource_slug = datasourceNode.targetData.resource.slug;
|
|
2213
|
-
}
|
|
2214
|
-
}
|
|
2215
|
-
}
|
|
2216
2070
|
Object.keys(resolvedField).forEach((key) => {
|
|
2217
2071
|
if (typeof resolvedField[key] === "object" && resolvedField[key] !== null) {
|
|
2218
2072
|
resolvedField[key] = resolveField(resolvedField[key]);
|
|
@@ -2284,23 +2138,6 @@ class PresetNode {
|
|
|
2284
2138
|
};
|
|
2285
2139
|
}
|
|
2286
2140
|
}
|
|
2287
|
-
class DatasourceNode extends GraphNode {
|
|
2288
|
-
constructor(id, data, targetDatasource) {
|
|
2289
|
-
super(id, "datasource", data.name, data, targetDatasource);
|
|
2290
|
-
}
|
|
2291
|
-
resolveReferences(_graph) {
|
|
2292
|
-
}
|
|
2293
|
-
async upsert(space) {
|
|
2294
|
-
const existingDatasource = this.targetData?.resource;
|
|
2295
|
-
const existingId = existingDatasource?.id;
|
|
2296
|
-
const { entries, ...datasourceDefinition } = this.sourceData;
|
|
2297
|
-
const result = await upsertDatasource(space, datasourceDefinition, existingId);
|
|
2298
|
-
if (!result) {
|
|
2299
|
-
throw new Error(`Failed to upsert datasource ${this.name}`);
|
|
2300
|
-
}
|
|
2301
|
-
return result;
|
|
2302
|
-
}
|
|
2303
|
-
}
|
|
2304
2141
|
|
|
2305
2142
|
function collectAllDependencies(components, allComponents, allGroups, allTags) {
|
|
2306
2143
|
const requiredComponents = /* @__PURE__ */ new Set();
|
|
@@ -2701,63 +2538,6 @@ async function pushWithDependencyGraph(space, spaceState, maxConcurrency = 5) {
|
|
|
2701
2538
|
return results;
|
|
2702
2539
|
}
|
|
2703
2540
|
|
|
2704
|
-
const fetchDatasourceEntries = async (space, datasourceId) => {
|
|
2705
|
-
try {
|
|
2706
|
-
const client = mapiClient();
|
|
2707
|
-
const { data } = await client.get(`spaces/${space}/datasource_entries?datasource_id=${datasourceId}`);
|
|
2708
|
-
return data.datasource_entries;
|
|
2709
|
-
} catch (error) {
|
|
2710
|
-
handleAPIError("pull_datasources", error);
|
|
2711
|
-
}
|
|
2712
|
-
};
|
|
2713
|
-
const fetchDatasources = async (space) => {
|
|
2714
|
-
try {
|
|
2715
|
-
const client = mapiClient();
|
|
2716
|
-
const { data } = await client.get(`spaces/${space}/datasources`);
|
|
2717
|
-
const datasources = data.datasources;
|
|
2718
|
-
const datasourcesWithEntries = await Promise.all(
|
|
2719
|
-
datasources.map(async (ds) => {
|
|
2720
|
-
const entries = await fetchDatasourceEntries(space, ds.id);
|
|
2721
|
-
return { ...ds, entries };
|
|
2722
|
-
})
|
|
2723
|
-
);
|
|
2724
|
-
return datasourcesWithEntries;
|
|
2725
|
-
} catch (error) {
|
|
2726
|
-
handleAPIError("pull_datasources", error);
|
|
2727
|
-
}
|
|
2728
|
-
};
|
|
2729
|
-
const fetchDatasource = async (space, datasourceName) => {
|
|
2730
|
-
try {
|
|
2731
|
-
const client = mapiClient();
|
|
2732
|
-
const { data } = await client.get(`spaces/${space}/datasources?search=${encodeURIComponent(datasourceName)}`);
|
|
2733
|
-
const found = data.datasources?.find((d) => d.name === datasourceName);
|
|
2734
|
-
if (!found) {
|
|
2735
|
-
return void 0;
|
|
2736
|
-
}
|
|
2737
|
-
const entries = await fetchDatasourceEntries(space, found.id);
|
|
2738
|
-
return { ...found, entries };
|
|
2739
|
-
} catch (error) {
|
|
2740
|
-
handleAPIError("pull_datasources", error, `Failed to fetch datasource ${datasourceName}`);
|
|
2741
|
-
}
|
|
2742
|
-
};
|
|
2743
|
-
const saveDatasourcesToFiles = async (space, datasources, options) => {
|
|
2744
|
-
const { filename = "datasources", suffix, path, separateFiles } = options;
|
|
2745
|
-
const resolvedPath = path ? resolve(process.cwd(), path, "datasources", space) : resolvePath(path, `datasources/${space}`);
|
|
2746
|
-
try {
|
|
2747
|
-
if (separateFiles) {
|
|
2748
|
-
for (const datasource of datasources) {
|
|
2749
|
-
const datasourceFilePath = join(resolvedPath, suffix ? `${datasource.name}.${suffix}.json` : `${datasource.name}.json`);
|
|
2750
|
-
await saveToFile(datasourceFilePath, JSON.stringify(datasource, null, 2));
|
|
2751
|
-
}
|
|
2752
|
-
return;
|
|
2753
|
-
}
|
|
2754
|
-
const datasourcesFilePath = join(resolvedPath, suffix ? `${filename}.${suffix}.json` : `${filename}.json`);
|
|
2755
|
-
await saveToFile(datasourcesFilePath, JSON.stringify(datasources, null, 2));
|
|
2756
|
-
} catch (error) {
|
|
2757
|
-
handleFileSystemError("write", error);
|
|
2758
|
-
}
|
|
2759
|
-
};
|
|
2760
|
-
|
|
2761
2541
|
const program$c = getProgram();
|
|
2762
2542
|
componentsCommand.command("push [componentName]").description(`Push your space's components schema as json`).option("-f, --from <from>", "source space id").option("--fi, --filter <filter>", "glob filter to apply to the components before pushing").option("--sf, --separate-files", "Read from separate files instead of consolidated files").option("--su, --suffix <suffix>", "Suffix to add to the component name").action(async (componentName, options) => {
|
|
2763
2543
|
konsola.title(`${commands.COMPONENTS}`, colorPalette.COMPONENTS, componentName ? `Pushing component ${componentName}...` : "Pushing components...");
|
|
@@ -2811,10 +2591,9 @@ componentsCommand.command("push [componentName]").description(`Push your space's
|
|
|
2811
2591
|
fetchComponents(space),
|
|
2812
2592
|
fetchComponentGroups(space),
|
|
2813
2593
|
fetchComponentPresets(space),
|
|
2814
|
-
fetchComponentInternalTags(space)
|
|
2815
|
-
fetchDatasources(space)
|
|
2594
|
+
fetchComponentInternalTags(space)
|
|
2816
2595
|
];
|
|
2817
|
-
const [components, groups, presets, internalTags
|
|
2596
|
+
const [components, groups, presets, internalTags] = await Promise.all(promises);
|
|
2818
2597
|
if (components) {
|
|
2819
2598
|
components.forEach((component) => {
|
|
2820
2599
|
spaceState.target.components.set(component.name, component);
|
|
@@ -2839,11 +2618,6 @@ componentsCommand.command("push [componentName]").description(`Push your space's
|
|
|
2839
2618
|
spaceState.target.tags.set(tag.name, tag);
|
|
2840
2619
|
});
|
|
2841
2620
|
}
|
|
2842
|
-
if (datasources) {
|
|
2843
|
-
datasources.forEach((datasource) => {
|
|
2844
|
-
spaceState.target.datasources.set(datasource.name, datasource);
|
|
2845
|
-
});
|
|
2846
|
-
}
|
|
2847
2621
|
if (componentName) {
|
|
2848
2622
|
spaceState.local = filterSpaceDataByComponent(spaceState.local, componentName);
|
|
2849
2623
|
if (!spaceState.local.components.length) {
|
|
@@ -2881,6 +2655,26 @@ componentsCommand.command("push [componentName]").description(`Push your space's
|
|
|
2881
2655
|
}
|
|
2882
2656
|
}
|
|
2883
2657
|
console.log(`${requestCount} requests made`);
|
|
2658
|
+
const referencedDatasources = /* @__PURE__ */ new Set();
|
|
2659
|
+
spaceState.local.components.forEach((component) => {
|
|
2660
|
+
if (component.schema) {
|
|
2661
|
+
const fields = JSON.stringify(component.schema);
|
|
2662
|
+
const datasourceMatches = fields.match(/"datasource_slug"\s*:\s*"([^"]+)"/g);
|
|
2663
|
+
if (datasourceMatches) {
|
|
2664
|
+
datasourceMatches.forEach((match) => {
|
|
2665
|
+
const slug = match.match(/"([^"]+)"$/)?.[1];
|
|
2666
|
+
if (slug) {
|
|
2667
|
+
referencedDatasources.add(slug);
|
|
2668
|
+
}
|
|
2669
|
+
});
|
|
2670
|
+
}
|
|
2671
|
+
}
|
|
2672
|
+
});
|
|
2673
|
+
if (referencedDatasources.size > 0) {
|
|
2674
|
+
konsola.br();
|
|
2675
|
+
konsola.info(`Components reference datasources: ${chalk.yellow(Array.from(referencedDatasources).join(", "))}`);
|
|
2676
|
+
konsola.info(`To manage datasources, use: ${chalk.cyan("storyblok datasources push")}`);
|
|
2677
|
+
}
|
|
2884
2678
|
} catch (error) {
|
|
2885
2679
|
handleError(error, verbose);
|
|
2886
2680
|
}
|
|
@@ -2916,7 +2710,7 @@ const saveLanguagesToFile = async (space, internationalizationOptions, options)
|
|
|
2916
2710
|
};
|
|
2917
2711
|
|
|
2918
2712
|
const program$b = getProgram();
|
|
2919
|
-
const languagesCommand = program$b.command(commands.LANGUAGES).alias("lang").description(`Manage your space's languages`).option("-s, --space <space>", "space ID").option("-p, --path <path>", "path to save the file. Default is .storyblok/languages");
|
|
2713
|
+
const languagesCommand = program$b.command(commands.LANGUAGES).alias("lang").description(`Manage your space's languages`).option("-s, --space <space>", "space ID").option("-p, --path <path>", "path to save the file. Default is .storyblok/languages").hook("preAction", resolveRegion);
|
|
2920
2714
|
languagesCommand.command("pull").description(`Download your space's languages schema as json`).option("-f, --filename <filename>", "filename to save the file as <filename>.<suffix>.json").option("--su, --suffix <suffix>", "suffix to add to the file name (e.g. languages.<suffix>.json). By default, the space ID is used.").action(async (options) => {
|
|
2921
2715
|
konsola.title(`${commands.LANGUAGES}`, colorPalette.LANGUAGES);
|
|
2922
2716
|
const verbose = program$b.opts().verbose;
|
|
@@ -2961,7 +2755,7 @@ languagesCommand.command("pull").description(`Download your space's languages sc
|
|
|
2961
2755
|
});
|
|
2962
2756
|
|
|
2963
2757
|
const program$a = getProgram();
|
|
2964
|
-
const migrationsCommand = program$a.command(commands.MIGRATIONS).alias("mig").description(`Manage your space's migrations`).option("-s, --space <space>", "space ID").option("-p, --path <path>", "path to save the file. Default is .storyblok/migrations");
|
|
2758
|
+
const migrationsCommand = program$a.command(commands.MIGRATIONS).alias("mig").description(`Manage your space's migrations`).option("-s, --space <space>", "space ID").option("-p, --path <path>", "path to save the file. Default is .storyblok/migrations").hook("preAction", resolveRegion);
|
|
2965
2759
|
|
|
2966
2760
|
const getMigrationTemplate = () => {
|
|
2967
2761
|
return `export default function (block) {
|
|
@@ -3034,9 +2828,9 @@ migrationsCommand.command("generate [componentName]").description("Generate a mi
|
|
|
3034
2828
|
}
|
|
3035
2829
|
});
|
|
3036
2830
|
|
|
3037
|
-
const fetchStories = async (space,
|
|
2831
|
+
const fetchStories = async (space, params) => {
|
|
3038
2832
|
try {
|
|
3039
|
-
const
|
|
2833
|
+
const client = mapiClient();
|
|
3040
2834
|
const allStories = [];
|
|
3041
2835
|
let currentPage = 1;
|
|
3042
2836
|
let hasMorePages = true;
|
|
@@ -3047,14 +2841,10 @@ const fetchStories = async (space, token, region, params) => {
|
|
|
3047
2841
|
...currentPage > 1 && { page: currentPage.toString() }
|
|
3048
2842
|
}).toString();
|
|
3049
2843
|
const queryString = filter_query ? `${regularParams ? `${regularParams}&` : ""}${filter_query}` : regularParams;
|
|
3050
|
-
const endpoint =
|
|
3051
|
-
const
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
}
|
|
3055
|
-
});
|
|
3056
|
-
allStories.push(...response.stories);
|
|
3057
|
-
const totalPages = Math.ceil(response.total / response.perPage);
|
|
2844
|
+
const endpoint = `spaces/${space}/stories${queryString ? `?${queryString}` : ""}`;
|
|
2845
|
+
const { data } = await client.get(endpoint, {});
|
|
2846
|
+
allStories.push(...data.stories);
|
|
2847
|
+
const totalPages = Math.ceil(data.total / data.per_page);
|
|
3058
2848
|
hasMorePages = currentPage < totalPages;
|
|
3059
2849
|
currentPage++;
|
|
3060
2850
|
}
|
|
@@ -3064,7 +2854,7 @@ const fetchStories = async (space, token, region, params) => {
|
|
|
3064
2854
|
}
|
|
3065
2855
|
};
|
|
3066
2856
|
async function fetchStoriesByComponent(spaceOptions, filterOptions) {
|
|
3067
|
-
const { spaceId
|
|
2857
|
+
const { spaceId } = spaceOptions;
|
|
3068
2858
|
const { componentName = "", query, starts_with } = filterOptions || {};
|
|
3069
2859
|
const params = {
|
|
3070
2860
|
...starts_with && { starts_with }
|
|
@@ -3076,39 +2866,30 @@ async function fetchStoriesByComponent(spaceOptions, filterOptions) {
|
|
|
3076
2866
|
params.filter_query = query.startsWith("filter_query") ? query : `filter_query${query}`;
|
|
3077
2867
|
}
|
|
3078
2868
|
try {
|
|
3079
|
-
const stories = await fetchStories(spaceId,
|
|
2869
|
+
const stories = await fetchStories(spaceId, params);
|
|
3080
2870
|
return stories ?? [];
|
|
3081
2871
|
} catch (error) {
|
|
3082
2872
|
handleAPIError("pull_stories", error);
|
|
3083
2873
|
}
|
|
3084
2874
|
}
|
|
3085
|
-
const fetchStory = async (space,
|
|
2875
|
+
const fetchStory = async (space, storyId) => {
|
|
3086
2876
|
try {
|
|
3087
|
-
const
|
|
3088
|
-
const endpoint =
|
|
3089
|
-
const
|
|
3090
|
-
|
|
3091
|
-
Authorization: token
|
|
3092
|
-
}
|
|
3093
|
-
});
|
|
3094
|
-
return response.story;
|
|
2877
|
+
const client = mapiClient();
|
|
2878
|
+
const endpoint = `spaces/${space}/stories/${storyId}`;
|
|
2879
|
+
const { data } = await client.get(endpoint, {});
|
|
2880
|
+
return data.story;
|
|
3095
2881
|
} catch (error) {
|
|
3096
2882
|
handleAPIError("pull_story", error);
|
|
3097
2883
|
}
|
|
3098
2884
|
};
|
|
3099
|
-
const updateStory = async (space,
|
|
2885
|
+
const updateStory = async (space, storyId, payload) => {
|
|
3100
2886
|
try {
|
|
3101
|
-
const
|
|
3102
|
-
const endpoint =
|
|
3103
|
-
const
|
|
3104
|
-
method: "PUT",
|
|
3105
|
-
headers: {
|
|
3106
|
-
"Authorization": token,
|
|
3107
|
-
"Content-Type": "application/json"
|
|
3108
|
-
},
|
|
2887
|
+
const client = mapiClient();
|
|
2888
|
+
const endpoint = `spaces/${space}/stories/${storyId}`;
|
|
2889
|
+
const { data } = await client.put(endpoint, {
|
|
3109
2890
|
body: JSON.stringify(payload)
|
|
3110
2891
|
});
|
|
3111
|
-
return
|
|
2892
|
+
return data.story;
|
|
3112
2893
|
} catch (error) {
|
|
3113
2894
|
handleAPIError("update_story", error);
|
|
3114
2895
|
}
|
|
@@ -3424,6 +3205,10 @@ migrationsCommand.command("run [componentName]").description("Run migrations").o
|
|
|
3424
3205
|
return;
|
|
3425
3206
|
}
|
|
3426
3207
|
const { password, region } = state;
|
|
3208
|
+
mapiClient({
|
|
3209
|
+
token: password,
|
|
3210
|
+
region
|
|
3211
|
+
});
|
|
3427
3212
|
try {
|
|
3428
3213
|
const spinner = new Spinner({
|
|
3429
3214
|
verbose: !isVitest
|
|
@@ -3448,9 +3233,7 @@ migrationsCommand.command("run [componentName]").description("Run migrations").o
|
|
|
3448
3233
|
const storiesSpinner = new Spinner({ verbose: !isVitest }).start(`Fetching stories...`);
|
|
3449
3234
|
const stories = await fetchStoriesByComponent(
|
|
3450
3235
|
{
|
|
3451
|
-
spaceId: space
|
|
3452
|
-
token: password,
|
|
3453
|
-
region
|
|
3236
|
+
spaceId: space
|
|
3454
3237
|
},
|
|
3455
3238
|
// Filter options
|
|
3456
3239
|
{
|
|
@@ -3464,7 +3247,7 @@ migrationsCommand.command("run [componentName]").description("Run migrations").o
|
|
|
3464
3247
|
return;
|
|
3465
3248
|
}
|
|
3466
3249
|
const storiesWithContent = await Promise.all(stories.map(async (story) => {
|
|
3467
|
-
const fullStory = await fetchStory(space,
|
|
3250
|
+
const fullStory = await fetchStory(space, story.id.toString());
|
|
3468
3251
|
return {
|
|
3469
3252
|
...story,
|
|
3470
3253
|
content: fullStory?.content
|
|
@@ -3536,7 +3319,7 @@ migrationsCommand.command("run [componentName]").description("Run migrations").o
|
|
|
3536
3319
|
payload.publish = 1;
|
|
3537
3320
|
}
|
|
3538
3321
|
try {
|
|
3539
|
-
const updatedStory = await updateStory(space,
|
|
3322
|
+
const updatedStory = await updateStory(space, story.id, payload);
|
|
3540
3323
|
if (updatedStory) {
|
|
3541
3324
|
successCount++;
|
|
3542
3325
|
storySpinner.succeed(`Updated story ${chalk.hex(colorPalette.PRIMARY)(story.name || story.id.toString())} - Completed in ${storySpinner.elapsedTime.toFixed(2)}ms`);
|
|
@@ -3608,7 +3391,7 @@ migrationsCommand.command("rollback [migrationFile]").description("Rollback a mi
|
|
|
3608
3391
|
});
|
|
3609
3392
|
|
|
3610
3393
|
const program$6 = getProgram();
|
|
3611
|
-
const typesCommand = program$6.command(commands.TYPES).alias("ts").description(`Generate types d.ts for your component schemas`).option("-s, --space <space>", "space ID").option("-p, --path <path>", "path to save the file. Default is .storyblok/types");
|
|
3394
|
+
const typesCommand = program$6.command(commands.TYPES).alias("ts").description(`Generate types d.ts for your component schemas`).option("-s, --space <space>", "space ID").option("-p, --path <path>", "path to save the file. Default is .storyblok/types").hook("preAction", resolveRegion);
|
|
3612
3395
|
|
|
3613
3396
|
const getAssetJSONSchema = (title) => ({
|
|
3614
3397
|
$id: "#/asset",
|
|
@@ -4034,7 +3817,7 @@ const DEFAULT_TYPEDEFS_HEADER = [
|
|
|
4034
3817
|
"// This file was generated by the storyblok CLI.",
|
|
4035
3818
|
"// DO NOT MODIFY THIS FILE BY HAND."
|
|
4036
3819
|
];
|
|
4037
|
-
const getPropertyTypeAnnotation = (property, prefix) => {
|
|
3820
|
+
const getPropertyTypeAnnotation = (property, prefix, suffix) => {
|
|
4038
3821
|
if (Array.from(storyblokSchemas.keys()).includes(property.type)) {
|
|
4039
3822
|
return { type: property.type };
|
|
4040
3823
|
}
|
|
@@ -4047,11 +3830,11 @@ const getPropertyTypeAnnotation = (property, prefix) => {
|
|
|
4047
3830
|
if (property.filter_content_type) {
|
|
4048
3831
|
if (typeof property.filter_content_type === "string") {
|
|
4049
3832
|
return {
|
|
4050
|
-
tsType: `(${getStoryType(property.filter_content_type, prefix)} | string )${property.type === "options" ? "[]" : ""}`
|
|
3833
|
+
tsType: `(${getStoryType(property.filter_content_type, prefix, suffix)} | string )${property.type === "options" ? "[]" : ""}`
|
|
4051
3834
|
};
|
|
4052
3835
|
}
|
|
4053
3836
|
return {
|
|
4054
|
-
tsType: `(${property.filter_content_type.map((type2) => getStoryType(type2, prefix)).join(" | ")} | string )${property.type === "options" ? "[]" : ""}`
|
|
3837
|
+
tsType: `(${property.filter_content_type.map((type2) => getStoryType(type2, prefix, suffix)).join(" | ")} | string )${property.type === "options" ? "[]" : ""}`
|
|
4055
3838
|
};
|
|
4056
3839
|
}
|
|
4057
3840
|
}
|
|
@@ -4106,13 +3889,14 @@ const getPropertyTypeAnnotation = (property, prefix) => {
|
|
|
4106
3889
|
return { type: "any" };
|
|
4107
3890
|
}
|
|
4108
3891
|
};
|
|
4109
|
-
function getStoryType(property, prefix) {
|
|
4110
|
-
return `${STORY_TYPE}<${prefix ?? ""}${capitalize(toCamelCase(property))}>`;
|
|
3892
|
+
function getStoryType(property, prefix, suffix) {
|
|
3893
|
+
return `${STORY_TYPE}<${prefix ?? ""}${capitalize(toCamelCase(property))}${suffix ?? ""}>`;
|
|
4111
3894
|
}
|
|
4112
3895
|
const getComponentType = (componentName, options) => {
|
|
4113
3896
|
const prefix = options.typePrefix ?? "";
|
|
3897
|
+
const suffix = options.typeSuffix ?? "";
|
|
4114
3898
|
const sanitizedName = componentName.replace(/[^a-z0-9]/gi, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
|
|
4115
|
-
const componentType = toPascalCase(toCamelCase(`${prefix}_${sanitizedName}`));
|
|
3899
|
+
const componentType = toPascalCase(toCamelCase(`${prefix}_${sanitizedName}_${suffix}`));
|
|
4116
3900
|
const isFirstCharacterNumber = !Number.isNaN(Number.parseInt(componentType.charAt(0)));
|
|
4117
3901
|
return isFirstCharacterNumber ? `_${componentType}` : componentType;
|
|
4118
3902
|
};
|
|
@@ -4124,7 +3908,7 @@ const getComponentPropertiesTypeAnnotations = async (component, options, spaceDa
|
|
|
4124
3908
|
}
|
|
4125
3909
|
const propertyType = value.type;
|
|
4126
3910
|
const propertyTypeAnnotation = {
|
|
4127
|
-
[key]: getPropertyTypeAnnotation(value, options.typePrefix)
|
|
3911
|
+
[key]: getPropertyTypeAnnotation(value, options.typePrefix, options.typeSuffix)
|
|
4128
3912
|
};
|
|
4129
3913
|
if (propertyType === "custom" && customFieldsParser) {
|
|
4130
3914
|
const customField = typeof customFieldsParser === "function" ? customFieldsParser(key, value) : {};
|
|
@@ -4163,6 +3947,15 @@ const getComponentPropertiesTypeAnnotations = async (component, options, spaceDa
|
|
|
4163
3947
|
);
|
|
4164
3948
|
propertyTypeAnnotation[key].tsType = componentsInGroupWhitelist.length > 0 ? `(${componentsInGroupWhitelist.join(" | ")})[]` : `never[]`;
|
|
4165
3949
|
}
|
|
3950
|
+
} else if (value.restrict_type === "tags") {
|
|
3951
|
+
if (Array.isArray(value.component_tag_whitelist) && value.component_tag_whitelist.length > 0) {
|
|
3952
|
+
const componentsWithTags = spaceData.components.filter(
|
|
3953
|
+
(component2) => component2.internal_tag_ids && component2.internal_tag_ids.some(
|
|
3954
|
+
(tagId) => value.component_tag_whitelist.includes(Number(tagId))
|
|
3955
|
+
)
|
|
3956
|
+
);
|
|
3957
|
+
propertyTypeAnnotation[key].tsType = componentsWithTags.length > 0 ? `(${componentsWithTags.map((component2) => getComponentType(component2.name, options)).join(" | ")})[]` : `never[]`;
|
|
3958
|
+
}
|
|
4166
3959
|
} else {
|
|
4167
3960
|
if (Array.isArray(value.component_whitelist) && value.component_whitelist.length > 0) {
|
|
4168
3961
|
propertyTypeAnnotation[key].tsType = `(${value.component_whitelist.map((name) => getComponentType(name, options)).join(" | ")})[]`;
|
|
@@ -4308,7 +4101,7 @@ const generateStoryblokTypes = async (options = {}) => {
|
|
|
4308
4101
|
};
|
|
4309
4102
|
|
|
4310
4103
|
const program$5 = getProgram();
|
|
4311
|
-
typesCommand.command("generate").description("Generate types d.ts for your component schemas").option("--sf, --separate-files", "").option("--strict", "strict mode, no loose typing").option("--type-prefix <prefix>", "prefix to be prepended to all generated component type names").option("--suffix <suffix>", "Components suffix").option("--custom-fields-parser <path>", "Path to the parser file for Custom Field Types").option("--compiler-options <options>", "path to the compiler options from json-schema-to-typescript").action(async (options) => {
|
|
4104
|
+
typesCommand.command("generate").description("Generate types d.ts for your component schemas").option("--sf, --separate-files", "").option("--strict", "strict mode, no loose typing").option("--type-prefix <prefix>", "prefix to be prepended to all generated component type names").option("--type-suffix <suffix>", "suffix to be appended to all generated component type names").option("--suffix <suffix>", "Components suffix").option("--custom-fields-parser <path>", "Path to the parser file for Custom Field Types").option("--compiler-options <options>", "path to the compiler options from json-schema-to-typescript").action(async (options) => {
|
|
4312
4105
|
konsola.title(`${commands.TYPES}`, colorPalette.TYPES, "Generating types...");
|
|
4313
4106
|
const verbose = program$5.opts().verbose;
|
|
4314
4107
|
const { space, path } = typesCommand.opts();
|
|
@@ -4351,7 +4144,65 @@ typesCommand.command("generate").description("Generate types d.ts for your compo
|
|
|
4351
4144
|
});
|
|
4352
4145
|
|
|
4353
4146
|
const program$4 = getProgram();
|
|
4354
|
-
const datasourcesCommand = program$4.command(commands.DATASOURCES).alias("ds").description(`Manage your space's datasources`).option("-s, --space <space>", "space ID").option("-p, --path <path>", "path to save the file. Default is .storyblok/datasources");
|
|
4147
|
+
const datasourcesCommand = program$4.command(commands.DATASOURCES).alias("ds").description(`Manage your space's datasources`).option("-s, --space <space>", "space ID").option("-p, --path <path>", "path to save the file. Default is .storyblok/datasources").hook("preAction", resolveRegion);
|
|
4148
|
+
|
|
4149
|
+
const fetchDatasourceEntries = async (space, datasourceId) => {
|
|
4150
|
+
try {
|
|
4151
|
+
const client = mapiClient();
|
|
4152
|
+
const { data } = await client.get(`spaces/${space}/datasource_entries?datasource_id=${datasourceId}`);
|
|
4153
|
+
return data.datasource_entries;
|
|
4154
|
+
} catch (error) {
|
|
4155
|
+
handleAPIError("pull_datasources", error);
|
|
4156
|
+
}
|
|
4157
|
+
};
|
|
4158
|
+
const fetchDatasources = async (space) => {
|
|
4159
|
+
try {
|
|
4160
|
+
const client = mapiClient();
|
|
4161
|
+
const { data } = await client.get(`spaces/${space}/datasources`);
|
|
4162
|
+
const datasources = data.datasources;
|
|
4163
|
+
const datasourcesWithEntries = await Promise.all(
|
|
4164
|
+
datasources.map(async (ds) => {
|
|
4165
|
+
const entries = await fetchDatasourceEntries(space, ds.id);
|
|
4166
|
+
return { ...ds, entries };
|
|
4167
|
+
})
|
|
4168
|
+
);
|
|
4169
|
+
return datasourcesWithEntries;
|
|
4170
|
+
} catch (error) {
|
|
4171
|
+
handleAPIError("pull_datasources", error);
|
|
4172
|
+
}
|
|
4173
|
+
};
|
|
4174
|
+
const fetchDatasource = async (space, datasourceName) => {
|
|
4175
|
+
try {
|
|
4176
|
+
const client = mapiClient();
|
|
4177
|
+
const { data } = await client.get(`spaces/${space}/datasources?search=${encodeURIComponent(datasourceName)}`);
|
|
4178
|
+
const found = data.datasources?.find((d) => d.name === datasourceName);
|
|
4179
|
+
if (!found) {
|
|
4180
|
+
return void 0;
|
|
4181
|
+
}
|
|
4182
|
+
const entries = await fetchDatasourceEntries(space, found.id);
|
|
4183
|
+
return { ...found, entries };
|
|
4184
|
+
} catch (error) {
|
|
4185
|
+
handleAPIError("pull_datasources", error, `Failed to fetch datasource ${datasourceName}`);
|
|
4186
|
+
}
|
|
4187
|
+
};
|
|
4188
|
+
const saveDatasourcesToFiles = async (space, datasources, options) => {
|
|
4189
|
+
const { filename = "datasources", suffix, path, separateFiles } = options;
|
|
4190
|
+
const resolvedPath = path ? resolve(process.cwd(), path, "datasources", space) : resolvePath(path, `datasources/${space}`);
|
|
4191
|
+
try {
|
|
4192
|
+
if (separateFiles) {
|
|
4193
|
+
for (const datasource of datasources) {
|
|
4194
|
+
const sanitizedName = sanitizeFilename(datasource.name);
|
|
4195
|
+
const datasourceFilePath = join(resolvedPath, suffix ? `${sanitizedName}.${suffix}.json` : `${sanitizedName}.json`);
|
|
4196
|
+
await saveToFile(datasourceFilePath, JSON.stringify(datasource, null, 2));
|
|
4197
|
+
}
|
|
4198
|
+
return;
|
|
4199
|
+
}
|
|
4200
|
+
const datasourcesFilePath = join(resolvedPath, suffix ? `${filename}.${suffix}.json` : `${filename}.json`);
|
|
4201
|
+
await saveToFile(datasourcesFilePath, JSON.stringify(datasources, null, 2));
|
|
4202
|
+
} catch (error) {
|
|
4203
|
+
handleFileSystemError("write", error);
|
|
4204
|
+
}
|
|
4205
|
+
};
|
|
4355
4206
|
|
|
4356
4207
|
const program$3 = getProgram();
|
|
4357
4208
|
datasourcesCommand.command("pull [datasourceName]").option("-f, --filename <filename>", "custom name to be used in file(s) name instead of space id").option("--sf, --separate-files", "Argument to create a single file for each datasource").option("--su, --suffix <suffix>", "suffix to add to the file name (e.g. datasources.<suffix>.json)").description("Pull datasources from your space").action(async (datasourceName, options) => {
|
|
@@ -4423,6 +4274,140 @@ datasourcesCommand.command("pull [datasourceName]").option("-f, --filename <file
|
|
|
4423
4274
|
}
|
|
4424
4275
|
});
|
|
4425
4276
|
|
|
4277
|
+
const pushDatasource = async (space, datasource) => {
|
|
4278
|
+
try {
|
|
4279
|
+
const client = mapiClient();
|
|
4280
|
+
const { data } = await client.post(`spaces/${space}/datasources`, {
|
|
4281
|
+
body: JSON.stringify(datasource)
|
|
4282
|
+
});
|
|
4283
|
+
return data.datasource;
|
|
4284
|
+
} catch (error) {
|
|
4285
|
+
handleAPIError("push_datasource", error, `Failed to push datasource ${datasource.name}`);
|
|
4286
|
+
}
|
|
4287
|
+
};
|
|
4288
|
+
const updateDatasource = async (space, datasourceId, datasource) => {
|
|
4289
|
+
try {
|
|
4290
|
+
const client = mapiClient();
|
|
4291
|
+
const { data } = await client.put(`spaces/${space}/datasources/${datasourceId}`, {
|
|
4292
|
+
body: JSON.stringify(datasource)
|
|
4293
|
+
});
|
|
4294
|
+
return data.datasource;
|
|
4295
|
+
} catch (error) {
|
|
4296
|
+
handleAPIError("update_datasource", error, `Failed to update datasource ${datasource.name}`);
|
|
4297
|
+
}
|
|
4298
|
+
};
|
|
4299
|
+
const upsertDatasource = async (space, datasource, existingId) => {
|
|
4300
|
+
if (existingId) {
|
|
4301
|
+
return await updateDatasource(space, existingId, datasource);
|
|
4302
|
+
} else {
|
|
4303
|
+
return await pushDatasource(space, datasource);
|
|
4304
|
+
}
|
|
4305
|
+
};
|
|
4306
|
+
const pushDatasourceEntry = async (space, datasourceId, entry) => {
|
|
4307
|
+
try {
|
|
4308
|
+
const client = mapiClient();
|
|
4309
|
+
const { data } = await client.post(`spaces/${space}/datasource_entries`, {
|
|
4310
|
+
body: JSON.stringify({
|
|
4311
|
+
datasource_entry: {
|
|
4312
|
+
...entry,
|
|
4313
|
+
datasource_id: datasourceId
|
|
4314
|
+
}
|
|
4315
|
+
})
|
|
4316
|
+
});
|
|
4317
|
+
return data.datasource_entry;
|
|
4318
|
+
} catch (error) {
|
|
4319
|
+
handleAPIError("push_datasource", error, `Failed to push datasource entry ${entry.name}`);
|
|
4320
|
+
}
|
|
4321
|
+
};
|
|
4322
|
+
const updateDatasourceEntry = async (space, entryId, entry) => {
|
|
4323
|
+
try {
|
|
4324
|
+
const client = mapiClient();
|
|
4325
|
+
await client.put(`spaces/${space}/datasource_entries/${entryId}`, {
|
|
4326
|
+
body: JSON.stringify({
|
|
4327
|
+
datasource_entry: entry
|
|
4328
|
+
})
|
|
4329
|
+
});
|
|
4330
|
+
} catch (error) {
|
|
4331
|
+
handleAPIError("update_datasource", error, `Failed to update datasource entry ${entry.name}`);
|
|
4332
|
+
}
|
|
4333
|
+
};
|
|
4334
|
+
const upsertDatasourceEntry = async (space, datasourceId, entry, existingId) => {
|
|
4335
|
+
if (existingId) {
|
|
4336
|
+
await updateDatasourceEntry(space, existingId, entry);
|
|
4337
|
+
return void 0;
|
|
4338
|
+
} else {
|
|
4339
|
+
return await pushDatasourceEntry(space, datasourceId, entry);
|
|
4340
|
+
}
|
|
4341
|
+
};
|
|
4342
|
+
const readDatasourcesFiles = async (options) => {
|
|
4343
|
+
const { from, path, separateFiles = false, suffix, space } = options;
|
|
4344
|
+
const resolvedPath = resolvePath(path, `datasources/${from}`);
|
|
4345
|
+
try {
|
|
4346
|
+
await readdir(resolvedPath);
|
|
4347
|
+
} catch (error) {
|
|
4348
|
+
const message = `No local datasources found for space ${chalk.bold(from)}. To push datasources, you need to pull them first:
|
|
4349
|
+
|
|
4350
|
+
1. Pull the datasources from your source space:
|
|
4351
|
+
${chalk.cyan(`storyblok datasources pull --space ${from}`)}
|
|
4352
|
+
|
|
4353
|
+
2. Then try pushing again:
|
|
4354
|
+
${chalk.cyan(`storyblok datasources push --space ${space} --from ${from}`)}`;
|
|
4355
|
+
throw new FileSystemError(
|
|
4356
|
+
"file_not_found",
|
|
4357
|
+
"read",
|
|
4358
|
+
error,
|
|
4359
|
+
message
|
|
4360
|
+
);
|
|
4361
|
+
}
|
|
4362
|
+
if (separateFiles) {
|
|
4363
|
+
return await readSeparateFiles(resolvedPath, suffix);
|
|
4364
|
+
}
|
|
4365
|
+
return await readConsolidatedFiles(resolvedPath, suffix);
|
|
4366
|
+
};
|
|
4367
|
+
async function readSeparateFiles(resolvedPath, suffix) {
|
|
4368
|
+
const files = await readdir(resolvedPath);
|
|
4369
|
+
const datasources = [];
|
|
4370
|
+
const filteredFiles = files.filter((file) => {
|
|
4371
|
+
if (suffix) {
|
|
4372
|
+
return file.endsWith(`.${suffix}.json`);
|
|
4373
|
+
} else {
|
|
4374
|
+
return !/\.\w+\.json$/.test(file);
|
|
4375
|
+
}
|
|
4376
|
+
});
|
|
4377
|
+
for (const file of filteredFiles) {
|
|
4378
|
+
const filePath = join(resolvedPath, file);
|
|
4379
|
+
if (file.endsWith(".json") || file.endsWith(`${suffix}.json`)) {
|
|
4380
|
+
if (file === "datasources.json" || /^datasources\.\w+\.json$/.test(file)) {
|
|
4381
|
+
continue;
|
|
4382
|
+
}
|
|
4383
|
+
const result = await readJsonFile(filePath);
|
|
4384
|
+
if (result.error) {
|
|
4385
|
+
handleFileSystemError("read", result.error);
|
|
4386
|
+
continue;
|
|
4387
|
+
}
|
|
4388
|
+
datasources.push(...result.data);
|
|
4389
|
+
}
|
|
4390
|
+
}
|
|
4391
|
+
return {
|
|
4392
|
+
datasources
|
|
4393
|
+
};
|
|
4394
|
+
}
|
|
4395
|
+
async function readConsolidatedFiles(resolvedPath, suffix) {
|
|
4396
|
+
const datasourcesPath = join(resolvedPath, suffix ? `datasources.${suffix}.json` : "datasources.json");
|
|
4397
|
+
const datasourcesResult = await readJsonFile(datasourcesPath);
|
|
4398
|
+
if (datasourcesResult.error || !datasourcesResult.data.length) {
|
|
4399
|
+
throw new FileSystemError(
|
|
4400
|
+
"file_not_found",
|
|
4401
|
+
"read",
|
|
4402
|
+
datasourcesResult.error || new Error("Datasources file is empty"),
|
|
4403
|
+
`No datasources found in ${datasourcesPath}. Please make sure you have pulled the datasources first.`
|
|
4404
|
+
);
|
|
4405
|
+
}
|
|
4406
|
+
return {
|
|
4407
|
+
datasources: datasourcesResult.data
|
|
4408
|
+
};
|
|
4409
|
+
}
|
|
4410
|
+
|
|
4426
4411
|
const program$2 = getProgram();
|
|
4427
4412
|
datasourcesCommand.command("push [datasourceName]").description(`Push your space's datasources schema as json`).option("-f, --from <from>", "source space id").option("--fi, --filter <filter>", "glob filter to apply to the datasources before pushing").option("--sf, --separate-files", "Read from separate files instead of consolidated files").option("--su, --suffix <suffix>", "Suffix to add to the datasource name").action(async (datasourceName, options) => {
|
|
4428
4413
|
konsola.title(`${commands.DATASOURCES}`, colorPalette.DATASOURCES, datasourceName ? `Pushing datasource ${datasourceName}...` : "Pushing datasources...");
|
|
@@ -4872,7 +4857,7 @@ program$1.command(`${commands.CREATE} [project-path]`).alias("c").description(`S
|
|
|
4872
4857
|
konsola.br();
|
|
4873
4858
|
});
|
|
4874
4859
|
|
|
4875
|
-
const version = "4.
|
|
4860
|
+
const version = "4.3.2";
|
|
4876
4861
|
const pkg = {
|
|
4877
4862
|
version: version};
|
|
4878
4863
|
|
|
@@ -4887,7 +4872,8 @@ program.helpOption("-h, --help", "Display help for command");
|
|
|
4887
4872
|
program.on("command:*", () => {
|
|
4888
4873
|
console.error(`Invalid command: ${program.args.join(" ")}`);
|
|
4889
4874
|
konsola.br();
|
|
4890
|
-
program.
|
|
4875
|
+
program.outputHelp();
|
|
4876
|
+
process.exit(1);
|
|
4891
4877
|
});
|
|
4892
4878
|
try {
|
|
4893
4879
|
program.parse(process.argv);
|