storyblok 4.3.1 → 4.3.3
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 +243 -268
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -5
package/dist/index.mjs
CHANGED
|
@@ -9,6 +9,7 @@ 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';
|
|
14
15
|
import { getRegion } from '@storyblok/region-helper';
|
|
@@ -614,6 +615,11 @@ const resolvePath = (path, folder) => {
|
|
|
614
615
|
const getComponentNameFromFilename = (filename) => {
|
|
615
616
|
return filename.replace(/\.js$/, "");
|
|
616
617
|
};
|
|
618
|
+
const sanitizeFilename = (filename) => {
|
|
619
|
+
return filenamify(filename, {
|
|
620
|
+
replacement: "_"
|
|
621
|
+
});
|
|
622
|
+
};
|
|
617
623
|
async function readJsonFile(filePath) {
|
|
618
624
|
try {
|
|
619
625
|
const content = (await readFile(filePath)).toString();
|
|
@@ -1246,11 +1252,12 @@ const saveComponentsToFiles = async (space, spaceData, options) => {
|
|
|
1246
1252
|
try {
|
|
1247
1253
|
if (separateFiles) {
|
|
1248
1254
|
for (const component of components) {
|
|
1249
|
-
const
|
|
1255
|
+
const sanitizedName = sanitizeFilename(component.name);
|
|
1256
|
+
const componentFilePath = join(resolvedPath, suffix ? `${sanitizedName}.${suffix}.json` : `${sanitizedName}.json`);
|
|
1250
1257
|
await saveToFile(componentFilePath, JSON.stringify(component, null, 2));
|
|
1251
1258
|
const componentPresets = presets.filter((preset) => preset.component_id === component.id);
|
|
1252
1259
|
if (componentPresets.length > 0) {
|
|
1253
|
-
const presetsFilePath = join(resolvedPath, suffix ? `${
|
|
1260
|
+
const presetsFilePath = join(resolvedPath, suffix ? `${sanitizedName}.presets.${suffix}.json` : `${sanitizedName}.presets.json`);
|
|
1254
1261
|
await saveToFile(presetsFilePath, JSON.stringify(componentPresets, null, 2));
|
|
1255
1262
|
}
|
|
1256
1263
|
const groupsFilePath = join(resolvedPath, suffix ? `groups.${suffix}.json` : `groups.json`);
|
|
@@ -1562,7 +1569,7 @@ componentsCommand.command("pull [componentName]").option("-f, --filename <filena
|
|
|
1562
1569
|
spinnerComponents.succeed(`${chalk.hex(colorPalette.COMPONENTS)("Components")} - Completed in ${spinnerComponents.elapsedTime.toFixed(2)}ms`);
|
|
1563
1570
|
await saveComponentsToFiles(
|
|
1564
1571
|
space,
|
|
1565
|
-
{ components, groups: groups || [], presets: presets || [], internalTags: internalTags || [] },
|
|
1572
|
+
{ components, groups: groups || [], presets: presets || [], internalTags: internalTags || [], datasources: [] },
|
|
1566
1573
|
{ ...options, path, separateFiles: separateFiles || !!componentName }
|
|
1567
1574
|
);
|
|
1568
1575
|
konsola.br();
|
|
@@ -1592,153 +1599,6 @@ componentsCommand.command("pull [componentName]").option("-f, --filename <filena
|
|
|
1592
1599
|
}
|
|
1593
1600
|
});
|
|
1594
1601
|
|
|
1595
|
-
const pushDatasource = async (space, datasource) => {
|
|
1596
|
-
try {
|
|
1597
|
-
const client = mapiClient();
|
|
1598
|
-
const { data } = await client.post(`spaces/${space}/datasources`, {
|
|
1599
|
-
body: JSON.stringify(datasource)
|
|
1600
|
-
});
|
|
1601
|
-
return data.datasource;
|
|
1602
|
-
} catch (error) {
|
|
1603
|
-
handleAPIError("push_datasource", error, `Failed to push datasource ${datasource.name}`);
|
|
1604
|
-
}
|
|
1605
|
-
};
|
|
1606
|
-
const updateDatasource = async (space, datasourceId, datasource) => {
|
|
1607
|
-
try {
|
|
1608
|
-
const client = mapiClient();
|
|
1609
|
-
const { data } = await client.put(`spaces/${space}/datasources/${datasourceId}`, {
|
|
1610
|
-
body: JSON.stringify(datasource)
|
|
1611
|
-
});
|
|
1612
|
-
return data.datasource;
|
|
1613
|
-
} catch (error) {
|
|
1614
|
-
handleAPIError("update_datasource", error, `Failed to update datasource ${datasource.name}`);
|
|
1615
|
-
}
|
|
1616
|
-
};
|
|
1617
|
-
const upsertDatasource = async (space, datasource, existingId) => {
|
|
1618
|
-
if (existingId) {
|
|
1619
|
-
return await updateDatasource(space, existingId, datasource);
|
|
1620
|
-
} else {
|
|
1621
|
-
return await pushDatasource(space, datasource);
|
|
1622
|
-
}
|
|
1623
|
-
};
|
|
1624
|
-
const pushDatasourceEntry = async (space, datasourceId, entry) => {
|
|
1625
|
-
try {
|
|
1626
|
-
const client = mapiClient();
|
|
1627
|
-
const { data } = await client.post(`spaces/${space}/datasource_entries`, {
|
|
1628
|
-
body: JSON.stringify({
|
|
1629
|
-
datasource_entry: {
|
|
1630
|
-
...entry,
|
|
1631
|
-
datasource_id: datasourceId
|
|
1632
|
-
}
|
|
1633
|
-
})
|
|
1634
|
-
});
|
|
1635
|
-
return data.datasource_entry;
|
|
1636
|
-
} catch (error) {
|
|
1637
|
-
handleAPIError("push_datasource", error, `Failed to push datasource entry ${entry.name}`);
|
|
1638
|
-
}
|
|
1639
|
-
};
|
|
1640
|
-
const updateDatasourceEntry = async (space, entryId, entry) => {
|
|
1641
|
-
try {
|
|
1642
|
-
const client = mapiClient();
|
|
1643
|
-
await client.put(`spaces/${space}/datasource_entries/${entryId}`, {
|
|
1644
|
-
body: JSON.stringify({
|
|
1645
|
-
datasource_entry: entry
|
|
1646
|
-
})
|
|
1647
|
-
});
|
|
1648
|
-
} catch (error) {
|
|
1649
|
-
handleAPIError("update_datasource", error, `Failed to update datasource entry ${entry.name}`);
|
|
1650
|
-
}
|
|
1651
|
-
};
|
|
1652
|
-
const upsertDatasourceEntry = async (space, datasourceId, entry, existingId) => {
|
|
1653
|
-
if (existingId) {
|
|
1654
|
-
await updateDatasourceEntry(space, existingId, entry);
|
|
1655
|
-
return void 0;
|
|
1656
|
-
} else {
|
|
1657
|
-
return await pushDatasourceEntry(space, datasourceId, entry);
|
|
1658
|
-
}
|
|
1659
|
-
};
|
|
1660
|
-
const readDatasourcesFiles = async (options) => {
|
|
1661
|
-
const { from, path, separateFiles = false, suffix, space } = options;
|
|
1662
|
-
const resolvedPath = resolvePath(path, `datasources/${from}`);
|
|
1663
|
-
try {
|
|
1664
|
-
await readdir(resolvedPath);
|
|
1665
|
-
} catch (error) {
|
|
1666
|
-
const message = `No local datasources found for space ${chalk.bold(from)}. To push datasources, you need to pull them first:
|
|
1667
|
-
|
|
1668
|
-
1. Pull the datasources from your source space:
|
|
1669
|
-
${chalk.cyan(`storyblok datasources pull --space ${from}`)}
|
|
1670
|
-
|
|
1671
|
-
2. Then try pushing again:
|
|
1672
|
-
${chalk.cyan(`storyblok datasources push --space ${space} --from ${from}`)}`;
|
|
1673
|
-
throw new FileSystemError(
|
|
1674
|
-
"file_not_found",
|
|
1675
|
-
"read",
|
|
1676
|
-
error,
|
|
1677
|
-
message
|
|
1678
|
-
);
|
|
1679
|
-
}
|
|
1680
|
-
if (separateFiles) {
|
|
1681
|
-
return await readSeparateFiles(resolvedPath, suffix);
|
|
1682
|
-
}
|
|
1683
|
-
return await readConsolidatedFiles(resolvedPath, suffix);
|
|
1684
|
-
};
|
|
1685
|
-
async function readSeparateFiles(resolvedPath, suffix) {
|
|
1686
|
-
const files = await readdir(resolvedPath);
|
|
1687
|
-
const datasources = [];
|
|
1688
|
-
const filteredFiles = files.filter((file) => {
|
|
1689
|
-
if (suffix) {
|
|
1690
|
-
return file.endsWith(`.${suffix}.json`);
|
|
1691
|
-
} else {
|
|
1692
|
-
return !/\.\w+\.json$/.test(file);
|
|
1693
|
-
}
|
|
1694
|
-
});
|
|
1695
|
-
for (const file of filteredFiles) {
|
|
1696
|
-
const filePath = join(resolvedPath, file);
|
|
1697
|
-
if (file.endsWith(".json") || file.endsWith(`${suffix}.json`)) {
|
|
1698
|
-
if (file === "datasources.json" || /^datasources\.\w+\.json$/.test(file)) {
|
|
1699
|
-
continue;
|
|
1700
|
-
}
|
|
1701
|
-
const result = await readJsonFile(filePath);
|
|
1702
|
-
if (result.error) {
|
|
1703
|
-
handleFileSystemError("read", result.error);
|
|
1704
|
-
continue;
|
|
1705
|
-
}
|
|
1706
|
-
datasources.push(...result.data);
|
|
1707
|
-
}
|
|
1708
|
-
}
|
|
1709
|
-
return {
|
|
1710
|
-
datasources
|
|
1711
|
-
};
|
|
1712
|
-
}
|
|
1713
|
-
async function readConsolidatedFiles(resolvedPath, suffix) {
|
|
1714
|
-
const datasourcesPath = join(resolvedPath, suffix ? `datasources.${suffix}.json` : "datasources.json");
|
|
1715
|
-
const datasourcesResult = await readJsonFile(datasourcesPath);
|
|
1716
|
-
if (datasourcesResult.error || !datasourcesResult.data.length) {
|
|
1717
|
-
throw new FileSystemError(
|
|
1718
|
-
"file_not_found",
|
|
1719
|
-
"read",
|
|
1720
|
-
datasourcesResult.error || new Error("Datasources file is empty"),
|
|
1721
|
-
`No datasources found in ${datasourcesPath}. Please make sure you have pulled the datasources first.`
|
|
1722
|
-
);
|
|
1723
|
-
}
|
|
1724
|
-
return {
|
|
1725
|
-
datasources: datasourcesResult.data
|
|
1726
|
-
};
|
|
1727
|
-
}
|
|
1728
|
-
|
|
1729
|
-
function createStubDatasource(name) {
|
|
1730
|
-
return {
|
|
1731
|
-
id: 0,
|
|
1732
|
-
// Will be set by API
|
|
1733
|
-
name,
|
|
1734
|
-
slug: name,
|
|
1735
|
-
dimensions: [],
|
|
1736
|
-
entries: [],
|
|
1737
|
-
// Empty entries for stub
|
|
1738
|
-
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1739
|
-
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1740
|
-
};
|
|
1741
|
-
}
|
|
1742
1602
|
function buildDependencyGraph(context) {
|
|
1743
1603
|
const { spaceState } = context;
|
|
1744
1604
|
const graph = { nodes: /* @__PURE__ */ new Map() };
|
|
@@ -1750,22 +1610,6 @@ function buildDependencyGraph(context) {
|
|
|
1750
1610
|
dependency.dependents.add(dependentId);
|
|
1751
1611
|
}
|
|
1752
1612
|
}
|
|
1753
|
-
const referencedDatasources = /* @__PURE__ */ new Set();
|
|
1754
|
-
spaceState.local.components.forEach((component) => {
|
|
1755
|
-
if (component.schema) {
|
|
1756
|
-
const dependencies = collectWhitelistDependencies(component.schema);
|
|
1757
|
-
dependencies.datasourceNames.forEach((datasourceName) => {
|
|
1758
|
-
referencedDatasources.add(datasourceName);
|
|
1759
|
-
});
|
|
1760
|
-
}
|
|
1761
|
-
});
|
|
1762
|
-
referencedDatasources.forEach((datasourceName) => {
|
|
1763
|
-
const nodeId = `datasource:${datasourceName}`;
|
|
1764
|
-
const targetDatasource = spaceState.target.datasources?.get(datasourceName);
|
|
1765
|
-
const stubDatasource = createStubDatasource(datasourceName);
|
|
1766
|
-
const node = new DatasourceNode(nodeId, stubDatasource, targetDatasource);
|
|
1767
|
-
graph.nodes.set(nodeId, node);
|
|
1768
|
-
});
|
|
1769
1613
|
spaceState.local.internalTags.forEach((tag) => {
|
|
1770
1614
|
const nodeId = `tag:${tag.id}`;
|
|
1771
1615
|
const targetTag = spaceState.target.tags.get(tag.name);
|
|
@@ -1841,10 +1685,6 @@ function buildDependencyGraph(context) {
|
|
|
1841
1685
|
const dependencyId = `component:${componentName}`;
|
|
1842
1686
|
addDependency(componentId, dependencyId);
|
|
1843
1687
|
});
|
|
1844
|
-
dependencies.datasourceNames.forEach((datasourceName) => {
|
|
1845
|
-
const datasourceId = `datasource:${datasourceName}`;
|
|
1846
|
-
addDependency(componentId, datasourceId);
|
|
1847
|
-
});
|
|
1848
1688
|
}
|
|
1849
1689
|
});
|
|
1850
1690
|
spaceState.local.presets.forEach((preset) => {
|
|
@@ -2227,15 +2067,6 @@ class ComponentNode extends GraphNode {
|
|
|
2227
2067
|
});
|
|
2228
2068
|
}
|
|
2229
2069
|
}
|
|
2230
|
-
if ((resolvedField.type === "option" || resolvedField.type === "options") && resolvedField.source === "internal") {
|
|
2231
|
-
if (resolvedField.datasource_slug && typeof resolvedField.datasource_slug === "string") {
|
|
2232
|
-
const datasourceNodeId = `datasource:${resolvedField.datasource_slug}`;
|
|
2233
|
-
const datasourceNode = graph.nodes.get(datasourceNodeId);
|
|
2234
|
-
if (datasourceNode?.targetData) {
|
|
2235
|
-
resolvedField.datasource_slug = datasourceNode.targetData.resource.slug;
|
|
2236
|
-
}
|
|
2237
|
-
}
|
|
2238
|
-
}
|
|
2239
2070
|
Object.keys(resolvedField).forEach((key) => {
|
|
2240
2071
|
if (typeof resolvedField[key] === "object" && resolvedField[key] !== null) {
|
|
2241
2072
|
resolvedField[key] = resolveField(resolvedField[key]);
|
|
@@ -2307,23 +2138,6 @@ class PresetNode {
|
|
|
2307
2138
|
};
|
|
2308
2139
|
}
|
|
2309
2140
|
}
|
|
2310
|
-
class DatasourceNode extends GraphNode {
|
|
2311
|
-
constructor(id, data, targetDatasource) {
|
|
2312
|
-
super(id, "datasource", data.name, data, targetDatasource);
|
|
2313
|
-
}
|
|
2314
|
-
resolveReferences(_graph) {
|
|
2315
|
-
}
|
|
2316
|
-
async upsert(space) {
|
|
2317
|
-
const existingDatasource = this.targetData?.resource;
|
|
2318
|
-
const existingId = existingDatasource?.id;
|
|
2319
|
-
const { entries, ...datasourceDefinition } = this.sourceData;
|
|
2320
|
-
const result = await upsertDatasource(space, datasourceDefinition, existingId);
|
|
2321
|
-
if (!result) {
|
|
2322
|
-
throw new Error(`Failed to upsert datasource ${this.name}`);
|
|
2323
|
-
}
|
|
2324
|
-
return result;
|
|
2325
|
-
}
|
|
2326
|
-
}
|
|
2327
2141
|
|
|
2328
2142
|
function collectAllDependencies(components, allComponents, allGroups, allTags) {
|
|
2329
2143
|
const requiredComponents = /* @__PURE__ */ new Set();
|
|
@@ -2392,7 +2206,8 @@ function filterSpaceDataByComponent(spaceData, componentName) {
|
|
|
2392
2206
|
components: [],
|
|
2393
2207
|
groups: [],
|
|
2394
2208
|
internalTags: [],
|
|
2395
|
-
presets: []
|
|
2209
|
+
presets: [],
|
|
2210
|
+
datasources: []
|
|
2396
2211
|
};
|
|
2397
2212
|
}
|
|
2398
2213
|
const { filteredComponents, filteredGroups, filteredTags } = collectAllDependencies(
|
|
@@ -2409,7 +2224,8 @@ function filterSpaceDataByComponent(spaceData, componentName) {
|
|
|
2409
2224
|
components: filteredComponents,
|
|
2410
2225
|
groups: filteredGroups,
|
|
2411
2226
|
internalTags: filteredTags,
|
|
2412
|
-
presets: filteredPresets
|
|
2227
|
+
presets: filteredPresets,
|
|
2228
|
+
datasources: []
|
|
2413
2229
|
};
|
|
2414
2230
|
}
|
|
2415
2231
|
function filterSpaceDataByPattern(spaceData, pattern) {
|
|
@@ -2421,7 +2237,8 @@ function filterSpaceDataByPattern(spaceData, pattern) {
|
|
|
2421
2237
|
components: [],
|
|
2422
2238
|
groups: [],
|
|
2423
2239
|
internalTags: [],
|
|
2424
|
-
presets: []
|
|
2240
|
+
presets: [],
|
|
2241
|
+
datasources: []
|
|
2425
2242
|
};
|
|
2426
2243
|
}
|
|
2427
2244
|
const { filteredComponents, filteredGroups, filteredTags } = collectAllDependencies(
|
|
@@ -2438,7 +2255,8 @@ function filterSpaceDataByPattern(spaceData, pattern) {
|
|
|
2438
2255
|
components: filteredComponents,
|
|
2439
2256
|
groups: filteredGroups,
|
|
2440
2257
|
internalTags: filteredTags,
|
|
2441
|
-
presets: filteredPresets
|
|
2258
|
+
presets: filteredPresets,
|
|
2259
|
+
datasources: []
|
|
2442
2260
|
};
|
|
2443
2261
|
}
|
|
2444
2262
|
|
|
@@ -2724,63 +2542,6 @@ async function pushWithDependencyGraph(space, spaceState, maxConcurrency = 5) {
|
|
|
2724
2542
|
return results;
|
|
2725
2543
|
}
|
|
2726
2544
|
|
|
2727
|
-
const fetchDatasourceEntries = async (space, datasourceId) => {
|
|
2728
|
-
try {
|
|
2729
|
-
const client = mapiClient();
|
|
2730
|
-
const { data } = await client.get(`spaces/${space}/datasource_entries?datasource_id=${datasourceId}`);
|
|
2731
|
-
return data.datasource_entries;
|
|
2732
|
-
} catch (error) {
|
|
2733
|
-
handleAPIError("pull_datasources", error);
|
|
2734
|
-
}
|
|
2735
|
-
};
|
|
2736
|
-
const fetchDatasources = async (space) => {
|
|
2737
|
-
try {
|
|
2738
|
-
const client = mapiClient();
|
|
2739
|
-
const { data } = await client.get(`spaces/${space}/datasources`);
|
|
2740
|
-
const datasources = data.datasources;
|
|
2741
|
-
const datasourcesWithEntries = await Promise.all(
|
|
2742
|
-
datasources.map(async (ds) => {
|
|
2743
|
-
const entries = await fetchDatasourceEntries(space, ds.id);
|
|
2744
|
-
return { ...ds, entries };
|
|
2745
|
-
})
|
|
2746
|
-
);
|
|
2747
|
-
return datasourcesWithEntries;
|
|
2748
|
-
} catch (error) {
|
|
2749
|
-
handleAPIError("pull_datasources", error);
|
|
2750
|
-
}
|
|
2751
|
-
};
|
|
2752
|
-
const fetchDatasource = async (space, datasourceName) => {
|
|
2753
|
-
try {
|
|
2754
|
-
const client = mapiClient();
|
|
2755
|
-
const { data } = await client.get(`spaces/${space}/datasources?search=${encodeURIComponent(datasourceName)}`);
|
|
2756
|
-
const found = data.datasources?.find((d) => d.name === datasourceName);
|
|
2757
|
-
if (!found) {
|
|
2758
|
-
return void 0;
|
|
2759
|
-
}
|
|
2760
|
-
const entries = await fetchDatasourceEntries(space, found.id);
|
|
2761
|
-
return { ...found, entries };
|
|
2762
|
-
} catch (error) {
|
|
2763
|
-
handleAPIError("pull_datasources", error, `Failed to fetch datasource ${datasourceName}`);
|
|
2764
|
-
}
|
|
2765
|
-
};
|
|
2766
|
-
const saveDatasourcesToFiles = async (space, datasources, options) => {
|
|
2767
|
-
const { filename = "datasources", suffix, path, separateFiles } = options;
|
|
2768
|
-
const resolvedPath = path ? resolve(process.cwd(), path, "datasources", space) : resolvePath(path, `datasources/${space}`);
|
|
2769
|
-
try {
|
|
2770
|
-
if (separateFiles) {
|
|
2771
|
-
for (const datasource of datasources) {
|
|
2772
|
-
const datasourceFilePath = join(resolvedPath, suffix ? `${datasource.name}.${suffix}.json` : `${datasource.name}.json`);
|
|
2773
|
-
await saveToFile(datasourceFilePath, JSON.stringify(datasource, null, 2));
|
|
2774
|
-
}
|
|
2775
|
-
return;
|
|
2776
|
-
}
|
|
2777
|
-
const datasourcesFilePath = join(resolvedPath, suffix ? `${filename}.${suffix}.json` : `${filename}.json`);
|
|
2778
|
-
await saveToFile(datasourcesFilePath, JSON.stringify(datasources, null, 2));
|
|
2779
|
-
} catch (error) {
|
|
2780
|
-
handleFileSystemError("write", error);
|
|
2781
|
-
}
|
|
2782
|
-
};
|
|
2783
|
-
|
|
2784
2545
|
const program$c = getProgram();
|
|
2785
2546
|
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) => {
|
|
2786
2547
|
konsola.title(`${commands.COMPONENTS}`, colorPalette.COMPONENTS, componentName ? `Pushing component ${componentName}...` : "Pushing components...");
|
|
@@ -2834,10 +2595,9 @@ componentsCommand.command("push [componentName]").description(`Push your space's
|
|
|
2834
2595
|
fetchComponents(space),
|
|
2835
2596
|
fetchComponentGroups(space),
|
|
2836
2597
|
fetchComponentPresets(space),
|
|
2837
|
-
fetchComponentInternalTags(space)
|
|
2838
|
-
fetchDatasources(space)
|
|
2598
|
+
fetchComponentInternalTags(space)
|
|
2839
2599
|
];
|
|
2840
|
-
const [components, groups, presets, internalTags
|
|
2600
|
+
const [components, groups, presets, internalTags] = await Promise.all(promises);
|
|
2841
2601
|
if (components) {
|
|
2842
2602
|
components.forEach((component) => {
|
|
2843
2603
|
spaceState.target.components.set(component.name, component);
|
|
@@ -2862,11 +2622,6 @@ componentsCommand.command("push [componentName]").description(`Push your space's
|
|
|
2862
2622
|
spaceState.target.tags.set(tag.name, tag);
|
|
2863
2623
|
});
|
|
2864
2624
|
}
|
|
2865
|
-
if (datasources) {
|
|
2866
|
-
datasources.forEach((datasource) => {
|
|
2867
|
-
spaceState.target.datasources.set(datasource.name, datasource);
|
|
2868
|
-
});
|
|
2869
|
-
}
|
|
2870
2625
|
if (componentName) {
|
|
2871
2626
|
spaceState.local = filterSpaceDataByComponent(spaceState.local, componentName);
|
|
2872
2627
|
if (!spaceState.local.components.length) {
|
|
@@ -2904,6 +2659,26 @@ componentsCommand.command("push [componentName]").description(`Push your space's
|
|
|
2904
2659
|
}
|
|
2905
2660
|
}
|
|
2906
2661
|
console.log(`${requestCount} requests made`);
|
|
2662
|
+
const referencedDatasources = /* @__PURE__ */ new Set();
|
|
2663
|
+
spaceState.local.components.forEach((component) => {
|
|
2664
|
+
if (component.schema) {
|
|
2665
|
+
const fields = JSON.stringify(component.schema);
|
|
2666
|
+
const datasourceMatches = fields.match(/"datasource_slug"\s*:\s*"([^"]+)"/g);
|
|
2667
|
+
if (datasourceMatches) {
|
|
2668
|
+
datasourceMatches.forEach((match) => {
|
|
2669
|
+
const slug = match.match(/"([^"]+)"$/)?.[1];
|
|
2670
|
+
if (slug) {
|
|
2671
|
+
referencedDatasources.add(slug);
|
|
2672
|
+
}
|
|
2673
|
+
});
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
});
|
|
2677
|
+
if (referencedDatasources.size > 0) {
|
|
2678
|
+
konsola.br();
|
|
2679
|
+
konsola.info(`Components reference datasources: ${chalk.yellow(Array.from(referencedDatasources).join(", "))}`);
|
|
2680
|
+
konsola.info(`To manage datasources, use: ${chalk.cyan("storyblok datasources push")}`);
|
|
2681
|
+
}
|
|
2907
2682
|
} catch (error) {
|
|
2908
2683
|
handleError(error, verbose);
|
|
2909
2684
|
}
|
|
@@ -3591,7 +3366,6 @@ migrationsCommand.command("rollback [migrationFile]").description("Rollback a mi
|
|
|
3591
3366
|
handleError(new CommandError(`Please provide the space as argument --space YOUR_SPACE_ID.`), verbose);
|
|
3592
3367
|
return;
|
|
3593
3368
|
}
|
|
3594
|
-
const { password, region } = state;
|
|
3595
3369
|
try {
|
|
3596
3370
|
const rollbackData = await readRollbackFile({
|
|
3597
3371
|
space,
|
|
@@ -3601,7 +3375,7 @@ migrationsCommand.command("rollback [migrationFile]").description("Rollback a mi
|
|
|
3601
3375
|
for (const story of rollbackData.stories) {
|
|
3602
3376
|
const spinner = new Spinner({ verbose }).start(`Restoring story ${chalk.hex(colorPalette.PRIMARY)(story.name || story.storyId)}...`);
|
|
3603
3377
|
try {
|
|
3604
|
-
await updateStory(space,
|
|
3378
|
+
await updateStory(space, story.storyId, {
|
|
3605
3379
|
story: {
|
|
3606
3380
|
content: story.content,
|
|
3607
3381
|
id: story.storyId,
|
|
@@ -4176,6 +3950,15 @@ const getComponentPropertiesTypeAnnotations = async (component, options, spaceDa
|
|
|
4176
3950
|
);
|
|
4177
3951
|
propertyTypeAnnotation[key].tsType = componentsInGroupWhitelist.length > 0 ? `(${componentsInGroupWhitelist.join(" | ")})[]` : `never[]`;
|
|
4178
3952
|
}
|
|
3953
|
+
} else if (value.restrict_type === "tags") {
|
|
3954
|
+
if (Array.isArray(value.component_tag_whitelist) && value.component_tag_whitelist.length > 0) {
|
|
3955
|
+
const componentsWithTags = spaceData.components.filter(
|
|
3956
|
+
(component2) => component2.internal_tag_ids && component2.internal_tag_ids.some(
|
|
3957
|
+
(tagId) => value.component_tag_whitelist.includes(Number(tagId))
|
|
3958
|
+
)
|
|
3959
|
+
);
|
|
3960
|
+
propertyTypeAnnotation[key].tsType = componentsWithTags.length > 0 ? `(${componentsWithTags.map((component2) => getComponentType(component2.name, options)).join(" | ")})[]` : `never[]`;
|
|
3961
|
+
}
|
|
4179
3962
|
} else {
|
|
4180
3963
|
if (Array.isArray(value.component_whitelist) && value.component_whitelist.length > 0) {
|
|
4181
3964
|
propertyTypeAnnotation[key].tsType = `(${value.component_whitelist.map((name) => getComponentType(name, options)).join(" | ")})[]`;
|
|
@@ -4366,6 +4149,64 @@ typesCommand.command("generate").description("Generate types d.ts for your compo
|
|
|
4366
4149
|
const program$4 = getProgram();
|
|
4367
4150
|
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);
|
|
4368
4151
|
|
|
4152
|
+
const fetchDatasourceEntries = async (space, datasourceId) => {
|
|
4153
|
+
try {
|
|
4154
|
+
const client = mapiClient();
|
|
4155
|
+
const { data } = await client.get(`spaces/${space}/datasource_entries?datasource_id=${datasourceId}`);
|
|
4156
|
+
return data.datasource_entries;
|
|
4157
|
+
} catch (error) {
|
|
4158
|
+
handleAPIError("pull_datasources", error);
|
|
4159
|
+
}
|
|
4160
|
+
};
|
|
4161
|
+
const fetchDatasources = async (space) => {
|
|
4162
|
+
try {
|
|
4163
|
+
const client = mapiClient();
|
|
4164
|
+
const { data } = await client.get(`spaces/${space}/datasources`);
|
|
4165
|
+
const datasources = data.datasources;
|
|
4166
|
+
const datasourcesWithEntries = await Promise.all(
|
|
4167
|
+
datasources.map(async (ds) => {
|
|
4168
|
+
const entries = await fetchDatasourceEntries(space, ds.id);
|
|
4169
|
+
return { ...ds, entries };
|
|
4170
|
+
})
|
|
4171
|
+
);
|
|
4172
|
+
return datasourcesWithEntries;
|
|
4173
|
+
} catch (error) {
|
|
4174
|
+
handleAPIError("pull_datasources", error);
|
|
4175
|
+
}
|
|
4176
|
+
};
|
|
4177
|
+
const fetchDatasource = async (space, datasourceName) => {
|
|
4178
|
+
try {
|
|
4179
|
+
const client = mapiClient();
|
|
4180
|
+
const { data } = await client.get(`spaces/${space}/datasources?search=${encodeURIComponent(datasourceName)}`);
|
|
4181
|
+
const found = data.datasources?.find((d) => d.name === datasourceName);
|
|
4182
|
+
if (!found) {
|
|
4183
|
+
return void 0;
|
|
4184
|
+
}
|
|
4185
|
+
const entries = await fetchDatasourceEntries(space, found.id);
|
|
4186
|
+
return { ...found, entries };
|
|
4187
|
+
} catch (error) {
|
|
4188
|
+
handleAPIError("pull_datasources", error, `Failed to fetch datasource ${datasourceName}`);
|
|
4189
|
+
}
|
|
4190
|
+
};
|
|
4191
|
+
const saveDatasourcesToFiles = async (space, datasources, options) => {
|
|
4192
|
+
const { filename = "datasources", suffix, path, separateFiles } = options;
|
|
4193
|
+
const resolvedPath = path ? resolve(process.cwd(), path, "datasources", space) : resolvePath(path, `datasources/${space}`);
|
|
4194
|
+
try {
|
|
4195
|
+
if (separateFiles) {
|
|
4196
|
+
for (const datasource of datasources) {
|
|
4197
|
+
const sanitizedName = sanitizeFilename(datasource.name);
|
|
4198
|
+
const datasourceFilePath = join(resolvedPath, suffix ? `${sanitizedName}.${suffix}.json` : `${sanitizedName}.json`);
|
|
4199
|
+
await saveToFile(datasourceFilePath, JSON.stringify(datasource, null, 2));
|
|
4200
|
+
}
|
|
4201
|
+
return;
|
|
4202
|
+
}
|
|
4203
|
+
const datasourcesFilePath = join(resolvedPath, suffix ? `${filename}.${suffix}.json` : `${filename}.json`);
|
|
4204
|
+
await saveToFile(datasourcesFilePath, JSON.stringify(datasources, null, 2));
|
|
4205
|
+
} catch (error) {
|
|
4206
|
+
handleFileSystemError("write", error);
|
|
4207
|
+
}
|
|
4208
|
+
};
|
|
4209
|
+
|
|
4369
4210
|
const program$3 = getProgram();
|
|
4370
4211
|
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) => {
|
|
4371
4212
|
konsola.title(`${commands.DATASOURCES}`, colorPalette.DATASOURCES, datasourceName ? `Pulling datasource ${datasourceName}...` : "Pulling datasources...");
|
|
@@ -4436,6 +4277,140 @@ datasourcesCommand.command("pull [datasourceName]").option("-f, --filename <file
|
|
|
4436
4277
|
}
|
|
4437
4278
|
});
|
|
4438
4279
|
|
|
4280
|
+
const pushDatasource = async (space, datasource) => {
|
|
4281
|
+
try {
|
|
4282
|
+
const client = mapiClient();
|
|
4283
|
+
const { data } = await client.post(`spaces/${space}/datasources`, {
|
|
4284
|
+
body: JSON.stringify(datasource)
|
|
4285
|
+
});
|
|
4286
|
+
return data.datasource;
|
|
4287
|
+
} catch (error) {
|
|
4288
|
+
handleAPIError("push_datasource", error, `Failed to push datasource ${datasource.name}`);
|
|
4289
|
+
}
|
|
4290
|
+
};
|
|
4291
|
+
const updateDatasource = async (space, datasourceId, datasource) => {
|
|
4292
|
+
try {
|
|
4293
|
+
const client = mapiClient();
|
|
4294
|
+
const { data } = await client.put(`spaces/${space}/datasources/${datasourceId}`, {
|
|
4295
|
+
body: JSON.stringify(datasource)
|
|
4296
|
+
});
|
|
4297
|
+
return data.datasource;
|
|
4298
|
+
} catch (error) {
|
|
4299
|
+
handleAPIError("update_datasource", error, `Failed to update datasource ${datasource.name}`);
|
|
4300
|
+
}
|
|
4301
|
+
};
|
|
4302
|
+
const upsertDatasource = async (space, datasource, existingId) => {
|
|
4303
|
+
if (existingId) {
|
|
4304
|
+
return await updateDatasource(space, existingId, datasource);
|
|
4305
|
+
} else {
|
|
4306
|
+
return await pushDatasource(space, datasource);
|
|
4307
|
+
}
|
|
4308
|
+
};
|
|
4309
|
+
const pushDatasourceEntry = async (space, datasourceId, entry) => {
|
|
4310
|
+
try {
|
|
4311
|
+
const client = mapiClient();
|
|
4312
|
+
const { data } = await client.post(`spaces/${space}/datasource_entries`, {
|
|
4313
|
+
body: JSON.stringify({
|
|
4314
|
+
datasource_entry: {
|
|
4315
|
+
...entry,
|
|
4316
|
+
datasource_id: datasourceId
|
|
4317
|
+
}
|
|
4318
|
+
})
|
|
4319
|
+
});
|
|
4320
|
+
return data.datasource_entry;
|
|
4321
|
+
} catch (error) {
|
|
4322
|
+
handleAPIError("push_datasource", error, `Failed to push datasource entry ${entry.name}`);
|
|
4323
|
+
}
|
|
4324
|
+
};
|
|
4325
|
+
const updateDatasourceEntry = async (space, entryId, entry) => {
|
|
4326
|
+
try {
|
|
4327
|
+
const client = mapiClient();
|
|
4328
|
+
await client.put(`spaces/${space}/datasource_entries/${entryId}`, {
|
|
4329
|
+
body: JSON.stringify({
|
|
4330
|
+
datasource_entry: entry
|
|
4331
|
+
})
|
|
4332
|
+
});
|
|
4333
|
+
} catch (error) {
|
|
4334
|
+
handleAPIError("update_datasource", error, `Failed to update datasource entry ${entry.name}`);
|
|
4335
|
+
}
|
|
4336
|
+
};
|
|
4337
|
+
const upsertDatasourceEntry = async (space, datasourceId, entry, existingId) => {
|
|
4338
|
+
if (existingId) {
|
|
4339
|
+
await updateDatasourceEntry(space, existingId, entry);
|
|
4340
|
+
return void 0;
|
|
4341
|
+
} else {
|
|
4342
|
+
return await pushDatasourceEntry(space, datasourceId, entry);
|
|
4343
|
+
}
|
|
4344
|
+
};
|
|
4345
|
+
const readDatasourcesFiles = async (options) => {
|
|
4346
|
+
const { from, path, separateFiles = false, suffix, space } = options;
|
|
4347
|
+
const resolvedPath = resolvePath(path, `datasources/${from}`);
|
|
4348
|
+
try {
|
|
4349
|
+
await readdir(resolvedPath);
|
|
4350
|
+
} catch (error) {
|
|
4351
|
+
const message = `No local datasources found for space ${chalk.bold(from)}. To push datasources, you need to pull them first:
|
|
4352
|
+
|
|
4353
|
+
1. Pull the datasources from your source space:
|
|
4354
|
+
${chalk.cyan(`storyblok datasources pull --space ${from}`)}
|
|
4355
|
+
|
|
4356
|
+
2. Then try pushing again:
|
|
4357
|
+
${chalk.cyan(`storyblok datasources push --space ${space} --from ${from}`)}`;
|
|
4358
|
+
throw new FileSystemError(
|
|
4359
|
+
"file_not_found",
|
|
4360
|
+
"read",
|
|
4361
|
+
error,
|
|
4362
|
+
message
|
|
4363
|
+
);
|
|
4364
|
+
}
|
|
4365
|
+
if (separateFiles) {
|
|
4366
|
+
return await readSeparateFiles(resolvedPath, suffix);
|
|
4367
|
+
}
|
|
4368
|
+
return await readConsolidatedFiles(resolvedPath, suffix);
|
|
4369
|
+
};
|
|
4370
|
+
async function readSeparateFiles(resolvedPath, suffix) {
|
|
4371
|
+
const files = await readdir(resolvedPath);
|
|
4372
|
+
const datasources = [];
|
|
4373
|
+
const filteredFiles = files.filter((file) => {
|
|
4374
|
+
if (suffix) {
|
|
4375
|
+
return file.endsWith(`.${suffix}.json`);
|
|
4376
|
+
} else {
|
|
4377
|
+
return !/\.\w+\.json$/.test(file);
|
|
4378
|
+
}
|
|
4379
|
+
});
|
|
4380
|
+
for (const file of filteredFiles) {
|
|
4381
|
+
const filePath = join(resolvedPath, file);
|
|
4382
|
+
if (file.endsWith(".json") || file.endsWith(`${suffix}.json`)) {
|
|
4383
|
+
if (file === "datasources.json" || /^datasources\.\w+\.json$/.test(file)) {
|
|
4384
|
+
continue;
|
|
4385
|
+
}
|
|
4386
|
+
const result = await readJsonFile(filePath);
|
|
4387
|
+
if (result.error) {
|
|
4388
|
+
handleFileSystemError("read", result.error);
|
|
4389
|
+
continue;
|
|
4390
|
+
}
|
|
4391
|
+
datasources.push(...result.data);
|
|
4392
|
+
}
|
|
4393
|
+
}
|
|
4394
|
+
return {
|
|
4395
|
+
datasources
|
|
4396
|
+
};
|
|
4397
|
+
}
|
|
4398
|
+
async function readConsolidatedFiles(resolvedPath, suffix) {
|
|
4399
|
+
const datasourcesPath = join(resolvedPath, suffix ? `datasources.${suffix}.json` : "datasources.json");
|
|
4400
|
+
const datasourcesResult = await readJsonFile(datasourcesPath);
|
|
4401
|
+
if (datasourcesResult.error || !datasourcesResult.data.length) {
|
|
4402
|
+
throw new FileSystemError(
|
|
4403
|
+
"file_not_found",
|
|
4404
|
+
"read",
|
|
4405
|
+
datasourcesResult.error || new Error("Datasources file is empty"),
|
|
4406
|
+
`No datasources found in ${datasourcesPath}. Please make sure you have pulled the datasources first.`
|
|
4407
|
+
);
|
|
4408
|
+
}
|
|
4409
|
+
return {
|
|
4410
|
+
datasources: datasourcesResult.data
|
|
4411
|
+
};
|
|
4412
|
+
}
|
|
4413
|
+
|
|
4439
4414
|
const program$2 = getProgram();
|
|
4440
4415
|
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) => {
|
|
4441
4416
|
konsola.title(`${commands.DATASOURCES}`, colorPalette.DATASOURCES, datasourceName ? `Pushing datasource ${datasourceName}...` : "Pushing datasources...");
|
|
@@ -4885,7 +4860,7 @@ program$1.command(`${commands.CREATE} [project-path]`).alias("c").description(`S
|
|
|
4885
4860
|
konsola.br();
|
|
4886
4861
|
});
|
|
4887
4862
|
|
|
4888
|
-
const version = "4.3.
|
|
4863
|
+
const version = "4.3.3";
|
|
4889
4864
|
const pkg = {
|
|
4890
4865
|
version: version};
|
|
4891
4866
|
|