storyblok 4.3.1 → 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 +233 -261
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -3
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`);
|
|
@@ -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();
|
|
@@ -2724,63 +2538,6 @@ async function pushWithDependencyGraph(space, spaceState, maxConcurrency = 5) {
|
|
|
2724
2538
|
return results;
|
|
2725
2539
|
}
|
|
2726
2540
|
|
|
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
2541
|
const program$c = getProgram();
|
|
2785
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) => {
|
|
2786
2543
|
konsola.title(`${commands.COMPONENTS}`, colorPalette.COMPONENTS, componentName ? `Pushing component ${componentName}...` : "Pushing components...");
|
|
@@ -2834,10 +2591,9 @@ componentsCommand.command("push [componentName]").description(`Push your space's
|
|
|
2834
2591
|
fetchComponents(space),
|
|
2835
2592
|
fetchComponentGroups(space),
|
|
2836
2593
|
fetchComponentPresets(space),
|
|
2837
|
-
fetchComponentInternalTags(space)
|
|
2838
|
-
fetchDatasources(space)
|
|
2594
|
+
fetchComponentInternalTags(space)
|
|
2839
2595
|
];
|
|
2840
|
-
const [components, groups, presets, internalTags
|
|
2596
|
+
const [components, groups, presets, internalTags] = await Promise.all(promises);
|
|
2841
2597
|
if (components) {
|
|
2842
2598
|
components.forEach((component) => {
|
|
2843
2599
|
spaceState.target.components.set(component.name, component);
|
|
@@ -2862,11 +2618,6 @@ componentsCommand.command("push [componentName]").description(`Push your space's
|
|
|
2862
2618
|
spaceState.target.tags.set(tag.name, tag);
|
|
2863
2619
|
});
|
|
2864
2620
|
}
|
|
2865
|
-
if (datasources) {
|
|
2866
|
-
datasources.forEach((datasource) => {
|
|
2867
|
-
spaceState.target.datasources.set(datasource.name, datasource);
|
|
2868
|
-
});
|
|
2869
|
-
}
|
|
2870
2621
|
if (componentName) {
|
|
2871
2622
|
spaceState.local = filterSpaceDataByComponent(spaceState.local, componentName);
|
|
2872
2623
|
if (!spaceState.local.components.length) {
|
|
@@ -2904,6 +2655,26 @@ componentsCommand.command("push [componentName]").description(`Push your space's
|
|
|
2904
2655
|
}
|
|
2905
2656
|
}
|
|
2906
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
|
+
}
|
|
2907
2678
|
} catch (error) {
|
|
2908
2679
|
handleError(error, verbose);
|
|
2909
2680
|
}
|
|
@@ -4176,6 +3947,15 @@ const getComponentPropertiesTypeAnnotations = async (component, options, spaceDa
|
|
|
4176
3947
|
);
|
|
4177
3948
|
propertyTypeAnnotation[key].tsType = componentsInGroupWhitelist.length > 0 ? `(${componentsInGroupWhitelist.join(" | ")})[]` : `never[]`;
|
|
4178
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
|
+
}
|
|
4179
3959
|
} else {
|
|
4180
3960
|
if (Array.isArray(value.component_whitelist) && value.component_whitelist.length > 0) {
|
|
4181
3961
|
propertyTypeAnnotation[key].tsType = `(${value.component_whitelist.map((name) => getComponentType(name, options)).join(" | ")})[]`;
|
|
@@ -4366,6 +4146,64 @@ typesCommand.command("generate").description("Generate types d.ts for your compo
|
|
|
4366
4146
|
const program$4 = getProgram();
|
|
4367
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);
|
|
4368
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
|
+
};
|
|
4206
|
+
|
|
4369
4207
|
const program$3 = getProgram();
|
|
4370
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) => {
|
|
4371
4209
|
konsola.title(`${commands.DATASOURCES}`, colorPalette.DATASOURCES, datasourceName ? `Pulling datasource ${datasourceName}...` : "Pulling datasources...");
|
|
@@ -4436,6 +4274,140 @@ datasourcesCommand.command("pull [datasourceName]").option("-f, --filename <file
|
|
|
4436
4274
|
}
|
|
4437
4275
|
});
|
|
4438
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
|
+
|
|
4439
4411
|
const program$2 = getProgram();
|
|
4440
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) => {
|
|
4441
4413
|
konsola.title(`${commands.DATASOURCES}`, colorPalette.DATASOURCES, datasourceName ? `Pushing datasource ${datasourceName}...` : "Pushing datasources...");
|
|
@@ -4885,7 +4857,7 @@ program$1.command(`${commands.CREATE} [project-path]`).alias("c").description(`S
|
|
|
4885
4857
|
konsola.br();
|
|
4886
4858
|
});
|
|
4887
4859
|
|
|
4888
|
-
const version = "4.3.
|
|
4860
|
+
const version = "4.3.2";
|
|
4889
4861
|
const pkg = {
|
|
4890
4862
|
version: version};
|
|
4891
4863
|
|