sb-mig 5.6.0-beta.2 → 5.6.0-beta.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/api/assets/assets.js +1 -1
- package/dist/api/auth/auth.types.d.ts +1 -1
- package/dist/api-v2/discover/discover.d.ts +14 -3
- package/dist/api-v2/discover/discover.js +53 -6
- package/dist/api-v2/discover/index.d.ts +1 -1
- package/dist/cli/utils/discover.js +49 -49
- package/dist-cjs/api-v2/discover/discover.js +51 -4
- package/package.json +8 -9
|
@@ -38,7 +38,7 @@ export const getAssetByName = async ({ spaceId, fileName }, config) => {
|
|
|
38
38
|
};
|
|
39
39
|
const requestSignedUploadUrl = ({ spaceId, payload }, config) => {
|
|
40
40
|
const { sbApi, debug } = config;
|
|
41
|
-
const { filename: _1, asset_folder_id, ext_id, space_id, ...restPayload } = payload;
|
|
41
|
+
const { filename: _1, asset_folder_id, ext_id, space_id, deleted_at: _2, ...restPayload } = payload;
|
|
42
42
|
const filename = getFileName(payload.filename);
|
|
43
43
|
const size = getSizeFromURL(payload.filename);
|
|
44
44
|
return sbApi
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RequestBaseConfig } from "../utils/request.js";
|
|
2
|
-
import type { ISbResult } from "storyblok-js-client
|
|
2
|
+
import type { ISbResult } from "storyblok-js-client";
|
|
3
3
|
export interface Org {
|
|
4
4
|
}
|
|
5
5
|
export interface CurrentUserResult extends ISbResult {
|
|
@@ -18,14 +18,25 @@ export declare function loadResourceContent(filePath: string): Promise<any>;
|
|
|
18
18
|
* Load multiple resources by file path
|
|
19
19
|
*/
|
|
20
20
|
export declare function loadResources(filePaths: string[]): Promise<LoadedResource[]>;
|
|
21
|
+
/**
|
|
22
|
+
* Options for component discovery
|
|
23
|
+
*/
|
|
24
|
+
export interface DiscoverComponentsOptions {
|
|
25
|
+
/** File extensions to search for (default: [".sb.ts", ".sb.cjs"]) */
|
|
26
|
+
extensions?: string[];
|
|
27
|
+
/** Whether to include external (node_modules) components (default: true) */
|
|
28
|
+
includeExternal?: boolean;
|
|
29
|
+
/** Maximum depth to scan (default: 20, prevents runaway scanning) */
|
|
30
|
+
maxDepth?: number;
|
|
31
|
+
}
|
|
21
32
|
/**
|
|
22
33
|
* Discover components in the working directory
|
|
23
34
|
* Prefers .ts for local files and .cjs for external (node_modules) files
|
|
24
35
|
* to avoid duplicates when both ESM and CJS versions exist
|
|
36
|
+
*
|
|
37
|
+
* Security: Stays within project bounds and doesn't follow symlinks outside
|
|
25
38
|
*/
|
|
26
|
-
export declare function discoverComponents(workingDir: string, options?:
|
|
27
|
-
extensions?: string[];
|
|
28
|
-
}): Promise<DiscoveredResource[]>;
|
|
39
|
+
export declare function discoverComponents(workingDir: string, options?: DiscoverComponentsOptions): Promise<DiscoveredResource[]>;
|
|
29
40
|
/**
|
|
30
41
|
* Discover datasources in the working directory
|
|
31
42
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { readdir, stat, readFile } from "fs/promises";
|
|
2
|
-
import { join } from "path";
|
|
1
|
+
import { readdir, stat, readFile, realpath } from "fs/promises";
|
|
2
|
+
import { join, resolve } from "path";
|
|
3
3
|
import { pathToFileURL } from "url";
|
|
4
4
|
/**
|
|
5
5
|
* Load the content of a resource file (.sb.js, .datasource.js, etc.)
|
|
@@ -72,32 +72,75 @@ async function readComponentDirectories(workingDir) {
|
|
|
72
72
|
}
|
|
73
73
|
return ["src", "components", "storyblok"];
|
|
74
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Check if a path is within the project directory
|
|
77
|
+
* Resolves symlinks and ensures we don't escape the project bounds
|
|
78
|
+
*/
|
|
79
|
+
async function isWithinProject(targetPath, projectRoot) {
|
|
80
|
+
try {
|
|
81
|
+
// Resolve both paths to handle symlinks
|
|
82
|
+
const resolvedTarget = await realpath(targetPath);
|
|
83
|
+
const resolvedRoot = await realpath(projectRoot);
|
|
84
|
+
// Check if target is within project root
|
|
85
|
+
return (resolvedTarget.startsWith(resolvedRoot + "/") ||
|
|
86
|
+
resolvedTarget === resolvedRoot);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// If we can't resolve the path, assume it's not safe
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
75
93
|
/**
|
|
76
94
|
* Discover components in the working directory
|
|
77
95
|
* Prefers .ts for local files and .cjs for external (node_modules) files
|
|
78
96
|
* to avoid duplicates when both ESM and CJS versions exist
|
|
97
|
+
*
|
|
98
|
+
* Security: Stays within project bounds and doesn't follow symlinks outside
|
|
79
99
|
*/
|
|
80
100
|
export async function discoverComponents(workingDir, options) {
|
|
81
101
|
const components = [];
|
|
82
102
|
// Priority order: .ts first (local), then .cjs (for node_modules)
|
|
83
103
|
// Skip .js and .mjs to avoid duplicates
|
|
84
104
|
const extensions = options?.extensions ?? [".sb.ts", ".sb.cjs"];
|
|
105
|
+
const includeExternal = options?.includeExternal ?? true;
|
|
106
|
+
const maxDepth = options?.maxDepth ?? 20;
|
|
107
|
+
// Resolve the project root for security checks
|
|
108
|
+
const projectRoot = resolve(workingDir);
|
|
85
109
|
const componentDirs = await readComponentDirectories(workingDir);
|
|
86
|
-
const scanDir = async (dir, isExternal) => {
|
|
110
|
+
const scanDir = async (dir, isExternal, depth) => {
|
|
111
|
+
// Prevent excessive depth
|
|
112
|
+
if (depth > maxDepth) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// Security: Ensure we're still within project bounds
|
|
116
|
+
if (!(await isWithinProject(dir, projectRoot))) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
87
119
|
try {
|
|
88
120
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
89
121
|
for (const entry of entries) {
|
|
90
122
|
const fullPath = join(dir, entry.name);
|
|
91
123
|
if (entry.isDirectory()) {
|
|
124
|
+
// Skip common non-source directories
|
|
92
125
|
if (entry.name === ".git" ||
|
|
93
126
|
entry.name === ".next" ||
|
|
94
|
-
entry.name === "dist"
|
|
127
|
+
entry.name === "dist" ||
|
|
128
|
+
entry.name === ".cache" ||
|
|
129
|
+
entry.name === "coverage") {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
// Skip node_modules entirely if not including external
|
|
133
|
+
if (entry.name === "node_modules" && !includeExternal) {
|
|
95
134
|
continue;
|
|
96
135
|
}
|
|
97
136
|
const isNowExternal = isExternal || entry.name === "node_modules";
|
|
98
|
-
await scanDir(fullPath, isNowExternal);
|
|
137
|
+
await scanDir(fullPath, isNowExternal, depth + 1);
|
|
99
138
|
}
|
|
100
139
|
else if (entry.isFile()) {
|
|
140
|
+
// Skip external files if not including them
|
|
141
|
+
if (isExternal && !includeExternal) {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
101
144
|
for (const ext of extensions) {
|
|
102
145
|
if (entry.name.endsWith(ext) &&
|
|
103
146
|
!entry.name.startsWith("_")) {
|
|
@@ -119,10 +162,14 @@ export async function discoverComponents(workingDir, options) {
|
|
|
119
162
|
};
|
|
120
163
|
for (const dir of componentDirs) {
|
|
121
164
|
const fullDir = join(workingDir, dir);
|
|
165
|
+
// Skip if the directory path includes node_modules and we're not including external
|
|
166
|
+
if (dir.includes("node_modules") && !includeExternal) {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
122
169
|
try {
|
|
123
170
|
const dirStat = await stat(fullDir);
|
|
124
171
|
if (dirStat.isDirectory()) {
|
|
125
|
-
await scanDir(fullDir, dir.includes("node_modules"));
|
|
172
|
+
await scanDir(fullDir, dir.includes("node_modules"), 0);
|
|
126
173
|
}
|
|
127
174
|
}
|
|
128
175
|
catch {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { discoverComponents, discoverDatasources, discoverRoles, loadResourceContent, loadResources, } from "./discover.js";
|
|
2
|
-
export type { DiscoveredResource, LoadedResource } from "./discover.js";
|
|
2
|
+
export type { DiscoverComponentsOptions, DiscoveredResource, LoadedResource, } from "./discover.js";
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// https://github.com/maoberlehner/storyblok-migrate
|
|
3
3
|
// edit: changed a lot in here, but inspiration still is valid :)
|
|
4
4
|
import path from "path";
|
|
5
|
-
import
|
|
5
|
+
import { globSync } from "glob";
|
|
6
6
|
import storyblokConfig, { SCHEMA } from "../../config/config.js";
|
|
7
7
|
import { buildOnTheFly } from "../../rollup/build-on-the-fly.js";
|
|
8
8
|
import { getFileContentWithRequire } from "../../utils/files.js";
|
|
@@ -34,7 +34,7 @@ export const discoverManyByPackageName = (request) => {
|
|
|
34
34
|
pattern = path.join(`${directory}`, `${normalizeDiscover({
|
|
35
35
|
segments: onlyLocalComponentsDirectories,
|
|
36
36
|
})}`, "**", "package.json");
|
|
37
|
-
listOfPackagesJsonFiles =
|
|
37
|
+
listOfPackagesJsonFiles = globSync(pattern.replace(/\\/g, "/"), {
|
|
38
38
|
follow: true,
|
|
39
39
|
});
|
|
40
40
|
listOfFiles = listOfPackagesJsonFiles
|
|
@@ -46,7 +46,7 @@ export const discoverManyByPackageName = (request) => {
|
|
|
46
46
|
.slice(0, -1)
|
|
47
47
|
.join(path.sep);
|
|
48
48
|
const allStoryblokSchemaFilesWithinFolderPattern = path.join(`${fileFolderPath}`, "**", `[^_]*.${storyblokConfig.schemaFileExt}`);
|
|
49
|
-
return
|
|
49
|
+
return globSync(allStoryblokSchemaFilesWithinFolderPattern.replace(/\\/g, "/"), { follow: true });
|
|
50
50
|
})
|
|
51
51
|
.flat();
|
|
52
52
|
break;
|
|
@@ -56,7 +56,7 @@ export const discoverManyByPackageName = (request) => {
|
|
|
56
56
|
pattern = path.join(`${directory}`, `${normalizeDiscover({
|
|
57
57
|
segments: onlyNodeModulesPackagesComponentsDirectories,
|
|
58
58
|
})}`, "**", "package.json");
|
|
59
|
-
listOfPackagesJsonFiles =
|
|
59
|
+
listOfPackagesJsonFiles = globSync(pattern.replace(/\\/g, "/"), {
|
|
60
60
|
follow: true,
|
|
61
61
|
});
|
|
62
62
|
listOfFiles = listOfPackagesJsonFiles
|
|
@@ -68,7 +68,7 @@ export const discoverManyByPackageName = (request) => {
|
|
|
68
68
|
.slice(0, -1)
|
|
69
69
|
.join(path.sep);
|
|
70
70
|
const allStoryblokSchemaFilesWithinFolderPattern = path.join(`${fileFolderPath}`, "**", `[^_]*.${storyblokConfig.schemaFileExt}`);
|
|
71
|
-
return
|
|
71
|
+
return globSync(allStoryblokSchemaFilesWithinFolderPattern.replace(/\\/g, "/"), { follow: true });
|
|
72
72
|
})
|
|
73
73
|
.flat();
|
|
74
74
|
break;
|
|
@@ -77,7 +77,7 @@ export const discoverManyByPackageName = (request) => {
|
|
|
77
77
|
pattern = path.join(`${directory}`, `${normalizeDiscover({
|
|
78
78
|
segments: storyblokConfig.componentsDirectories,
|
|
79
79
|
})}`, "**", "package.json");
|
|
80
|
-
listOfPackagesJsonFiles =
|
|
80
|
+
listOfPackagesJsonFiles = globSync(pattern.replace(/\\/g, "/"), {
|
|
81
81
|
follow: true,
|
|
82
82
|
});
|
|
83
83
|
listOfFiles = listOfPackagesJsonFiles
|
|
@@ -89,7 +89,7 @@ export const discoverManyByPackageName = (request) => {
|
|
|
89
89
|
.slice(0, -1)
|
|
90
90
|
.join(path.sep);
|
|
91
91
|
const allStoryblokSchemaFilesWithinFolderPattern = path.join(`${fileFolderPath}`, "**", `[^_]*.${storyblokConfig.schemaFileExt}`);
|
|
92
|
-
return
|
|
92
|
+
return globSync(allStoryblokSchemaFilesWithinFolderPattern.replace(/\\/g, "/"), { follow: true });
|
|
93
93
|
})
|
|
94
94
|
.flat();
|
|
95
95
|
break;
|
|
@@ -111,7 +111,7 @@ export const discoverOneByPackageName = (request) => {
|
|
|
111
111
|
pattern = path.join(`${directory}`, `${normalizeDiscover({
|
|
112
112
|
segments: onlyLocalComponentsDirectories,
|
|
113
113
|
})}`, "**", "package.json");
|
|
114
|
-
listOfPackagesJsonFiles =
|
|
114
|
+
listOfPackagesJsonFiles = globSync(pattern.replace(/\\/g, "/"), {
|
|
115
115
|
follow: true,
|
|
116
116
|
});
|
|
117
117
|
listOfFiles = listOfPackagesJsonFiles
|
|
@@ -124,7 +124,7 @@ export const discoverOneByPackageName = (request) => {
|
|
|
124
124
|
.slice(0, -1)
|
|
125
125
|
.join(path.sep);
|
|
126
126
|
const allStoryblokSchemaFilesWithinFolderPattern = path.join(`${fileFolderPath}`, "**", `[^_]*.${storyblokConfig.schemaFileExt}`);
|
|
127
|
-
return
|
|
127
|
+
return globSync(allStoryblokSchemaFilesWithinFolderPattern.replace(/\\/g, "/"), { follow: true });
|
|
128
128
|
})
|
|
129
129
|
.flat();
|
|
130
130
|
break;
|
|
@@ -134,7 +134,7 @@ export const discoverOneByPackageName = (request) => {
|
|
|
134
134
|
pattern = path.join(`${directory}`, `${normalizeDiscover({
|
|
135
135
|
segments: onlyNodeModulesPackagesComponentsDirectories,
|
|
136
136
|
})}`, "**", "package.json");
|
|
137
|
-
listOfPackagesJsonFiles =
|
|
137
|
+
listOfPackagesJsonFiles = globSync(pattern.replace(/\\/g, "/"), {
|
|
138
138
|
follow: true,
|
|
139
139
|
});
|
|
140
140
|
listOfFiles = listOfPackagesJsonFiles
|
|
@@ -147,7 +147,7 @@ export const discoverOneByPackageName = (request) => {
|
|
|
147
147
|
.slice(0, -1)
|
|
148
148
|
.join(path.sep);
|
|
149
149
|
const allStoryblokSchemaFilesWithinFolderPattern = path.join(`${fileFolderPath}`, "**", `[^_]*.${storyblokConfig.schemaFileExt}`);
|
|
150
|
-
return
|
|
150
|
+
return globSync(allStoryblokSchemaFilesWithinFolderPattern.replace(/\\/g, "/"), { follow: true });
|
|
151
151
|
})
|
|
152
152
|
.flat();
|
|
153
153
|
break;
|
|
@@ -156,7 +156,7 @@ export const discoverOneByPackageName = (request) => {
|
|
|
156
156
|
pattern = path.join(`${directory}`, `${normalizeDiscover({
|
|
157
157
|
segments: storyblokConfig.componentsDirectories,
|
|
158
158
|
})}`, "**", "package.json");
|
|
159
|
-
listOfPackagesJsonFiles =
|
|
159
|
+
listOfPackagesJsonFiles = globSync(pattern.replace(/\\/g, "/"), {
|
|
160
160
|
follow: true,
|
|
161
161
|
});
|
|
162
162
|
listOfFiles = listOfPackagesJsonFiles
|
|
@@ -169,7 +169,7 @@ export const discoverOneByPackageName = (request) => {
|
|
|
169
169
|
.slice(0, -1)
|
|
170
170
|
.join(path.sep);
|
|
171
171
|
const allStoryblokSchemaFilesWithinFolderPattern = path.join(`${fileFolderPath}`, "**", `[^_]*.${storyblokConfig.schemaFileExt}`);
|
|
172
|
-
return
|
|
172
|
+
return globSync(allStoryblokSchemaFilesWithinFolderPattern.replace(/\\/g, "/"), { follow: true });
|
|
173
173
|
})
|
|
174
174
|
.flat();
|
|
175
175
|
break;
|
|
@@ -192,19 +192,19 @@ export const discoverMany = async (request) => {
|
|
|
192
192
|
pattern = path.join(`${directory}`, `${normalizeDiscover({
|
|
193
193
|
segments: onlyLocalComponentsDirectories,
|
|
194
194
|
})}`, "**", `${normalizeDiscover({ segments: request.fileNames })}.sb.${storyblokConfig.schemaType}`);
|
|
195
|
-
const listOfFilesToCompile =
|
|
195
|
+
const listOfFilesToCompile = globSync(pattern.replace(/\\/g, "/"), {
|
|
196
196
|
follow: true,
|
|
197
197
|
});
|
|
198
198
|
await buildOnTheFly({ files: listOfFilesToCompile });
|
|
199
199
|
pattern = path.join(directory, ".next", "cache", "sb-mig", "**", `${normalizeDiscover({ segments: request.fileNames })}.${storyblokConfig.schemaFileExt}`);
|
|
200
|
-
listOFSchemaTSFilesCompiled =
|
|
200
|
+
listOFSchemaTSFilesCompiled = globSync(pattern.replace(/\\/g, "/"), {
|
|
201
201
|
follow: true,
|
|
202
202
|
});
|
|
203
203
|
}
|
|
204
204
|
pattern = path.join(`${directory}`, `${normalizeDiscover({
|
|
205
205
|
segments: onlyLocalComponentsDirectories,
|
|
206
206
|
})}`, "**", `${normalizeDiscover({ segments: request.fileNames })}.${storyblokConfig.schemaFileExt}`);
|
|
207
|
-
listOfFiles =
|
|
207
|
+
listOfFiles = globSync(pattern.replace(/\\/g, "/"), {
|
|
208
208
|
follow: true,
|
|
209
209
|
});
|
|
210
210
|
listOfFiles = [...listOfFiles, ...listOFSchemaTSFilesCompiled];
|
|
@@ -215,7 +215,7 @@ export const discoverMany = async (request) => {
|
|
|
215
215
|
pattern = path.join(`${directory}`, `${normalizeDiscover({
|
|
216
216
|
segments: onlyNodeModulesPackagesComponentsDirectories,
|
|
217
217
|
})}`, "**", `${normalizeDiscover({ segments: request.fileNames })}.${storyblokConfig.schemaFileExt}`);
|
|
218
|
-
listOfFiles =
|
|
218
|
+
listOfFiles = globSync(pattern.replace(/\\/g, "/"), {
|
|
219
219
|
follow: true,
|
|
220
220
|
});
|
|
221
221
|
break;
|
|
@@ -226,7 +226,7 @@ export const discoverMany = async (request) => {
|
|
|
226
226
|
pattern = path.join(`${directory}`, `${normalizeDiscover({
|
|
227
227
|
segments: storyblokConfig.componentsDirectories,
|
|
228
228
|
})}`, "**", `${normalizeDiscover({ segments: request.fileNames })}.${storyblokConfig.schemaFileExt}`);
|
|
229
|
-
listOfFiles =
|
|
229
|
+
listOfFiles = globSync(pattern.replace(/\\/g, "/"), {
|
|
230
230
|
follow: true,
|
|
231
231
|
});
|
|
232
232
|
break;
|
|
@@ -251,14 +251,14 @@ export const discoverManyDatasources = async (request) => {
|
|
|
251
251
|
})}`, "**", `${normalizeDiscover({
|
|
252
252
|
segments: request.fileNames,
|
|
253
253
|
})}.sb.datasource.${storyblokConfig.schemaType}`);
|
|
254
|
-
const listOfFilesToCompile =
|
|
254
|
+
const listOfFilesToCompile = globSync(pattern.replace(/\\/g, "/"), {
|
|
255
255
|
follow: true,
|
|
256
256
|
});
|
|
257
257
|
await buildOnTheFly({ files: listOfFilesToCompile });
|
|
258
258
|
pattern = path.join(directory, ".next", "cache", "sb-mig", "**", `${normalizeDiscover({
|
|
259
259
|
segments: request.fileNames,
|
|
260
260
|
})}.${storyblokConfig.datasourceExt}`);
|
|
261
|
-
listOFSchemaTSFilesCompiled =
|
|
261
|
+
listOFSchemaTSFilesCompiled = globSync(pattern.replace(/\\/g, "/"), {
|
|
262
262
|
follow: true,
|
|
263
263
|
});
|
|
264
264
|
}
|
|
@@ -267,7 +267,7 @@ export const discoverManyDatasources = async (request) => {
|
|
|
267
267
|
})}`, "**", `${normalizeDiscover({
|
|
268
268
|
segments: request.fileNames,
|
|
269
269
|
})}.${storyblokConfig.datasourceExt}`);
|
|
270
|
-
listOfFiles =
|
|
270
|
+
listOfFiles = globSync(pattern.replace(/\\/g, "/"), {
|
|
271
271
|
follow: true,
|
|
272
272
|
});
|
|
273
273
|
listOfFiles = [...listOfFiles, ...listOFSchemaTSFilesCompiled];
|
|
@@ -278,7 +278,7 @@ export const discoverManyDatasources = async (request) => {
|
|
|
278
278
|
pattern = path.join(`${directory}`, `${normalizeDiscover({
|
|
279
279
|
segments: onlyNodeModulesPackagesComponentsDirectories,
|
|
280
280
|
})}`, "**", `${normalizeDiscover({ segments: request.fileNames })}.${storyblokConfig.datasourceExt}`);
|
|
281
|
-
listOfFiles =
|
|
281
|
+
listOfFiles = globSync(pattern.replace(/\\/g, "/"), {
|
|
282
282
|
follow: true,
|
|
283
283
|
});
|
|
284
284
|
break;
|
|
@@ -287,7 +287,7 @@ export const discoverManyDatasources = async (request) => {
|
|
|
287
287
|
pattern = path.join(`${directory}`, `${normalizeDiscover({
|
|
288
288
|
segments: storyblokConfig.componentsDirectories,
|
|
289
289
|
})}`, "**", `${normalizeDiscover({ segments: request.fileNames })}.${storyblokConfig.datasourceExt}`);
|
|
290
|
-
listOfFiles =
|
|
290
|
+
listOfFiles = globSync(pattern.replace(/\\/g, "/"), {
|
|
291
291
|
follow: true,
|
|
292
292
|
});
|
|
293
293
|
break;
|
|
@@ -308,7 +308,7 @@ export const discoverStories = (request) => {
|
|
|
308
308
|
const pattern = path.join(`${directory}`, `${normalizeDiscover({
|
|
309
309
|
segments: onlyLocalComponentsDirectories,
|
|
310
310
|
})}`, "**", `${normalizeDiscover({ segments: request.fileNames })}.${storyblokConfig.storiesExt}`);
|
|
311
|
-
listOfFiles =
|
|
311
|
+
listOfFiles = globSync(pattern.replace(/\\/g, "/"), {
|
|
312
312
|
follow: true,
|
|
313
313
|
});
|
|
314
314
|
break;
|
|
@@ -332,7 +332,7 @@ export const discoverMigrationConfig = (request) => {
|
|
|
332
332
|
pattern = path.join(`${directory}`, `${normalizeDiscover({
|
|
333
333
|
segments: storyblokConfig.componentsDirectories,
|
|
334
334
|
})}`, "**", `${normalizeDiscover({ segments: request.fileNames })}.${storyblokConfig.migrationConfigExt}`);
|
|
335
|
-
listOfFiles =
|
|
335
|
+
listOfFiles = globSync(pattern.replace(/\\/g, "/"), {
|
|
336
336
|
follow: true,
|
|
337
337
|
});
|
|
338
338
|
break;
|
|
@@ -353,7 +353,7 @@ export const discoverVersionMapping = (request) => {
|
|
|
353
353
|
})}`, "**", `${normalizeDiscover({
|
|
354
354
|
segments: request.fileNames,
|
|
355
355
|
})}.${"sb.migrations.cjs"}`);
|
|
356
|
-
listOfFiles =
|
|
356
|
+
listOfFiles = globSync(pattern.replace(/\\/g, "/"), {
|
|
357
357
|
follow: true,
|
|
358
358
|
});
|
|
359
359
|
break;
|
|
@@ -376,7 +376,7 @@ export const discoverDatasources = async (request) => {
|
|
|
376
376
|
pattern = path.join(`${directory}`, `${normalizeDiscover({
|
|
377
377
|
segments: onlyLocalComponentsDirectories,
|
|
378
378
|
})}`, "**", `[^_]*.sb.datasource.${storyblokConfig.schemaType}`);
|
|
379
|
-
const listOfFilesToCompile =
|
|
379
|
+
const listOfFilesToCompile = globSync(pattern.replace(/\\/g, "/"), {
|
|
380
380
|
follow: true,
|
|
381
381
|
});
|
|
382
382
|
if (storyblokConfig.debug) {
|
|
@@ -386,14 +386,14 @@ export const discoverDatasources = async (request) => {
|
|
|
386
386
|
}
|
|
387
387
|
await buildOnTheFly({ files: listOfFilesToCompile });
|
|
388
388
|
pattern = path.join(directory, ".next", "cache", "sb-mig", "**", `[^_]*.${storyblokConfig.datasourceExt}`);
|
|
389
|
-
listOFSchemaTSFilesCompiled =
|
|
389
|
+
listOFSchemaTSFilesCompiled = globSync(pattern.replace(/\\/g, "/"), {
|
|
390
390
|
follow: true,
|
|
391
391
|
});
|
|
392
392
|
}
|
|
393
393
|
pattern = path.join(`${directory}`, `${normalizeDiscover({
|
|
394
394
|
segments: onlyLocalComponentsDirectories,
|
|
395
395
|
})}`, "**", `[^_]*.${storyblokConfig.datasourceExt}`);
|
|
396
|
-
listOfFiles =
|
|
396
|
+
listOfFiles = globSync(pattern.replace(/\\/g, "/"), {
|
|
397
397
|
follow: true,
|
|
398
398
|
});
|
|
399
399
|
listOfFiles = [...listOfFiles, ...listOFSchemaTSFilesCompiled];
|
|
@@ -404,7 +404,7 @@ export const discoverDatasources = async (request) => {
|
|
|
404
404
|
pattern = path.join(`${directory}`, `${normalizeDiscover({
|
|
405
405
|
segments: onlyNodeModulesPackagesComponentsDirectories,
|
|
406
406
|
})}`, "**", `[^_]*.${storyblokConfig.datasourceExt}`);
|
|
407
|
-
listOfFiles =
|
|
407
|
+
listOfFiles = globSync(pattern.replace(/\\/g, "/"), {
|
|
408
408
|
follow: true,
|
|
409
409
|
});
|
|
410
410
|
break;
|
|
@@ -413,7 +413,7 @@ export const discoverDatasources = async (request) => {
|
|
|
413
413
|
pattern = path.join(`${directory}`, `${normalizeDiscover({
|
|
414
414
|
segments: storyblokConfig.componentsDirectories,
|
|
415
415
|
})}`, "**", `[^_]*.${storyblokConfig.datasourceExt}`);
|
|
416
|
-
listOfFiles =
|
|
416
|
+
listOfFiles = globSync(pattern.replace(/\\/g, "/"), {
|
|
417
417
|
follow: true,
|
|
418
418
|
});
|
|
419
419
|
break;
|
|
@@ -438,12 +438,12 @@ export const discover = async (request) => {
|
|
|
438
438
|
componentDirectories: onlyLocalComponentsDirectories,
|
|
439
439
|
ext: "sb.ts",
|
|
440
440
|
});
|
|
441
|
-
const listOfFilesToCompile =
|
|
441
|
+
const listOfFilesToCompile = globSync(pattern.replace(/\\/g, "/"), {
|
|
442
442
|
follow: true,
|
|
443
443
|
});
|
|
444
444
|
await buildOnTheFly({ files: listOfFilesToCompile });
|
|
445
445
|
pattern = path.join(directory, ".next", "cache", "sb-mig", "**", `[^_]*.${storyblokConfig.schemaFileExt}`);
|
|
446
|
-
listOFSchemaTSFilesCompiled =
|
|
446
|
+
listOFSchemaTSFilesCompiled = globSync(pattern.replace(/\\/g, "/"), {
|
|
447
447
|
follow: true,
|
|
448
448
|
});
|
|
449
449
|
}
|
|
@@ -452,7 +452,7 @@ export const discover = async (request) => {
|
|
|
452
452
|
componentDirectories: onlyLocalComponentsDirectories,
|
|
453
453
|
ext: storyblokConfig.schemaFileExt,
|
|
454
454
|
});
|
|
455
|
-
listOfFiles =
|
|
455
|
+
listOfFiles = globSync(pattern.replace(/\\/g, "/"), {
|
|
456
456
|
follow: true,
|
|
457
457
|
});
|
|
458
458
|
listOfFiles = [...listOfFiles, ...listOFSchemaTSFilesCompiled];
|
|
@@ -465,7 +465,7 @@ export const discover = async (request) => {
|
|
|
465
465
|
componentDirectories: onlyNodeModulesPackagesComponentsDirectories,
|
|
466
466
|
ext: storyblokConfig.schemaFileExt,
|
|
467
467
|
});
|
|
468
|
-
listOfFiles =
|
|
468
|
+
listOfFiles = globSync(pattern.replace(/\\/g, "/"), {
|
|
469
469
|
follow: true,
|
|
470
470
|
});
|
|
471
471
|
break;
|
|
@@ -476,7 +476,7 @@ export const discover = async (request) => {
|
|
|
476
476
|
componentDirectories: storyblokConfig.componentsDirectories,
|
|
477
477
|
ext: storyblokConfig.schemaFileExt,
|
|
478
478
|
});
|
|
479
|
-
listOfFiles =
|
|
479
|
+
listOfFiles = globSync(pattern.replace(/\\/g, "/"), {
|
|
480
480
|
follow: true,
|
|
481
481
|
});
|
|
482
482
|
break;
|
|
@@ -501,12 +501,12 @@ export const discoverResolvers = async (request) => {
|
|
|
501
501
|
componentDirectories: onlyLocalComponentsDirectories,
|
|
502
502
|
ext: "sb.resolvers.ts",
|
|
503
503
|
});
|
|
504
|
-
const listOfFilesToCompile =
|
|
504
|
+
const listOfFilesToCompile = globSync(pattern.replace(/\\/g, "/"), {
|
|
505
505
|
follow: true,
|
|
506
506
|
});
|
|
507
507
|
await buildOnTheFly({ files: listOfFilesToCompile });
|
|
508
508
|
pattern = path.join(directory, ".next", "cache", "sb-mig", "**", `[^_]*.sb.resolvers.cjs`);
|
|
509
|
-
listOFSchemaTSFilesCompiled =
|
|
509
|
+
listOFSchemaTSFilesCompiled = globSync(pattern.replace(/\\/g, "/"), {
|
|
510
510
|
follow: true,
|
|
511
511
|
});
|
|
512
512
|
}
|
|
@@ -515,7 +515,7 @@ export const discoverResolvers = async (request) => {
|
|
|
515
515
|
componentDirectories: onlyLocalComponentsDirectories,
|
|
516
516
|
ext: "sb.resolvers.cjs",
|
|
517
517
|
});
|
|
518
|
-
listOfFiles =
|
|
518
|
+
listOfFiles = globSync(pattern.replace(/\\/g, "/"), {
|
|
519
519
|
follow: true,
|
|
520
520
|
});
|
|
521
521
|
listOfFiles = [...listOfFiles, ...listOFSchemaTSFilesCompiled];
|
|
@@ -539,19 +539,19 @@ export const discoverRoles = async (request) => {
|
|
|
539
539
|
pattern = path.join(`${directory}`, `${normalizeDiscover({
|
|
540
540
|
segments: onlyLocalComponentsDirectories,
|
|
541
541
|
})}`, "**", `[^_]*.sb.roles.${storyblokConfig.schemaType}`);
|
|
542
|
-
const listOfFilesToCompile =
|
|
542
|
+
const listOfFilesToCompile = globSync(pattern.replace(/\\/g, "/"), {
|
|
543
543
|
follow: true,
|
|
544
544
|
});
|
|
545
545
|
await buildOnTheFly({ files: listOfFilesToCompile });
|
|
546
546
|
pattern = path.join(directory, ".next", "cache", "sb-mig", "**", `[^_]*.${storyblokConfig.rolesExt}`);
|
|
547
|
-
listOFSchemaTSFilesCompiled =
|
|
547
|
+
listOFSchemaTSFilesCompiled = globSync(pattern.replace(/\\/g, "/"), {
|
|
548
548
|
follow: true,
|
|
549
549
|
});
|
|
550
550
|
}
|
|
551
551
|
pattern = path.join(`${directory}`, `${normalizeDiscover({
|
|
552
552
|
segments: onlyLocalComponentsDirectories,
|
|
553
553
|
})}`, "**", `[^_]*.${storyblokConfig.rolesExt}`);
|
|
554
|
-
listOfFiles =
|
|
554
|
+
listOfFiles = globSync(pattern.replace(/\\/g, "/"), {
|
|
555
555
|
follow: true,
|
|
556
556
|
});
|
|
557
557
|
listOfFiles = [...listOfFiles, ...listOFSchemaTSFilesCompiled];
|
|
@@ -562,7 +562,7 @@ export const discoverRoles = async (request) => {
|
|
|
562
562
|
pattern = path.join(`${directory}`, `${normalizeDiscover({
|
|
563
563
|
segments: onlyNodeModulesPackagesComponentsDirectories,
|
|
564
564
|
})}`, "**", `[^_]*.${storyblokConfig.rolesExt}`);
|
|
565
|
-
listOfFiles =
|
|
565
|
+
listOfFiles = globSync(pattern.replace(/\\/g, "/"), {
|
|
566
566
|
follow: true,
|
|
567
567
|
});
|
|
568
568
|
break;
|
|
@@ -571,7 +571,7 @@ export const discoverRoles = async (request) => {
|
|
|
571
571
|
pattern = path.join(`${directory}`, `${normalizeDiscover({
|
|
572
572
|
segments: storyblokConfig.componentsDirectories,
|
|
573
573
|
})}`, "**", `[^_]*.${storyblokConfig.rolesExt}`);
|
|
574
|
-
listOfFiles =
|
|
574
|
+
listOfFiles = globSync(pattern.replace(/\\/g, "/"), {
|
|
575
575
|
follow: true,
|
|
576
576
|
});
|
|
577
577
|
break;
|
|
@@ -596,21 +596,21 @@ export const discoverManyRoles = async (request) => {
|
|
|
596
596
|
})}`, "**", `${normalizeDiscover({
|
|
597
597
|
segments: request.fileNames,
|
|
598
598
|
})}.sb.roles.${storyblokConfig.schemaType}`);
|
|
599
|
-
const listOfFilesToCompile =
|
|
599
|
+
const listOfFilesToCompile = globSync(pattern.replace(/\\/g, "/"), {
|
|
600
600
|
follow: true,
|
|
601
601
|
});
|
|
602
602
|
await buildOnTheFly({ files: listOfFilesToCompile });
|
|
603
603
|
pattern = path.join(directory, ".next", "cache", "sb-mig", "**", `${normalizeDiscover({
|
|
604
604
|
segments: request.fileNames,
|
|
605
605
|
})}.${storyblokConfig.rolesExt}`);
|
|
606
|
-
listOFSchemaTSFilesCompiled =
|
|
606
|
+
listOFSchemaTSFilesCompiled = globSync(pattern.replace(/\\/g, "/"), {
|
|
607
607
|
follow: true,
|
|
608
608
|
});
|
|
609
609
|
}
|
|
610
610
|
pattern = path.join(`${directory}`, `${normalizeDiscover({
|
|
611
611
|
segments: onlyLocalComponentsDirectories,
|
|
612
612
|
})}`, "**", `${normalizeDiscover({ segments: request.fileNames })}.${storyblokConfig.rolesExt}`);
|
|
613
|
-
listOfFiles =
|
|
613
|
+
listOfFiles = globSync(pattern.replace(/\\/g, "/"), {
|
|
614
614
|
follow: true,
|
|
615
615
|
});
|
|
616
616
|
listOfFiles = [...listOfFiles, ...listOFSchemaTSFilesCompiled];
|
|
@@ -621,7 +621,7 @@ export const discoverManyRoles = async (request) => {
|
|
|
621
621
|
pattern = path.join(`${directory}`, `${normalizeDiscover({
|
|
622
622
|
segments: onlyNodeModulesPackagesComponentsDirectories,
|
|
623
623
|
})}`, "**", `${normalizeDiscover({ segments: request.fileNames })}.${storyblokConfig.rolesExt}`);
|
|
624
|
-
listOfFiles =
|
|
624
|
+
listOfFiles = globSync(pattern.replace(/\\/g, "/"), {
|
|
625
625
|
follow: true,
|
|
626
626
|
});
|
|
627
627
|
break;
|
|
@@ -630,7 +630,7 @@ export const discoverManyRoles = async (request) => {
|
|
|
630
630
|
pattern = path.join(`${directory}`, `${normalizeDiscover({
|
|
631
631
|
segments: storyblokConfig.componentsDirectories,
|
|
632
632
|
})}`, "**", `${normalizeDiscover({ segments: request.fileNames })}.${storyblokConfig.rolesExt}`);
|
|
633
|
-
listOfFiles =
|
|
633
|
+
listOfFiles = globSync(pattern.replace(/\\/g, "/"), {
|
|
634
634
|
follow: true,
|
|
635
635
|
});
|
|
636
636
|
break;
|
|
@@ -112,32 +112,75 @@ async function readComponentDirectories(workingDir) {
|
|
|
112
112
|
}
|
|
113
113
|
return ["src", "components", "storyblok"];
|
|
114
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* Check if a path is within the project directory
|
|
117
|
+
* Resolves symlinks and ensures we don't escape the project bounds
|
|
118
|
+
*/
|
|
119
|
+
async function isWithinProject(targetPath, projectRoot) {
|
|
120
|
+
try {
|
|
121
|
+
// Resolve both paths to handle symlinks
|
|
122
|
+
const resolvedTarget = await (0, promises_1.realpath)(targetPath);
|
|
123
|
+
const resolvedRoot = await (0, promises_1.realpath)(projectRoot);
|
|
124
|
+
// Check if target is within project root
|
|
125
|
+
return (resolvedTarget.startsWith(resolvedRoot + "/") ||
|
|
126
|
+
resolvedTarget === resolvedRoot);
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// If we can't resolve the path, assume it's not safe
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
115
133
|
/**
|
|
116
134
|
* Discover components in the working directory
|
|
117
135
|
* Prefers .ts for local files and .cjs for external (node_modules) files
|
|
118
136
|
* to avoid duplicates when both ESM and CJS versions exist
|
|
137
|
+
*
|
|
138
|
+
* Security: Stays within project bounds and doesn't follow symlinks outside
|
|
119
139
|
*/
|
|
120
140
|
async function discoverComponents(workingDir, options) {
|
|
121
141
|
const components = [];
|
|
122
142
|
// Priority order: .ts first (local), then .cjs (for node_modules)
|
|
123
143
|
// Skip .js and .mjs to avoid duplicates
|
|
124
144
|
const extensions = options?.extensions ?? [".sb.ts", ".sb.cjs"];
|
|
145
|
+
const includeExternal = options?.includeExternal ?? true;
|
|
146
|
+
const maxDepth = options?.maxDepth ?? 20;
|
|
147
|
+
// Resolve the project root for security checks
|
|
148
|
+
const projectRoot = (0, path_1.resolve)(workingDir);
|
|
125
149
|
const componentDirs = await readComponentDirectories(workingDir);
|
|
126
|
-
const scanDir = async (dir, isExternal) => {
|
|
150
|
+
const scanDir = async (dir, isExternal, depth) => {
|
|
151
|
+
// Prevent excessive depth
|
|
152
|
+
if (depth > maxDepth) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
// Security: Ensure we're still within project bounds
|
|
156
|
+
if (!(await isWithinProject(dir, projectRoot))) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
127
159
|
try {
|
|
128
160
|
const entries = await (0, promises_1.readdir)(dir, { withFileTypes: true });
|
|
129
161
|
for (const entry of entries) {
|
|
130
162
|
const fullPath = (0, path_1.join)(dir, entry.name);
|
|
131
163
|
if (entry.isDirectory()) {
|
|
164
|
+
// Skip common non-source directories
|
|
132
165
|
if (entry.name === ".git" ||
|
|
133
166
|
entry.name === ".next" ||
|
|
134
|
-
entry.name === "dist"
|
|
167
|
+
entry.name === "dist" ||
|
|
168
|
+
entry.name === ".cache" ||
|
|
169
|
+
entry.name === "coverage") {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
// Skip node_modules entirely if not including external
|
|
173
|
+
if (entry.name === "node_modules" && !includeExternal) {
|
|
135
174
|
continue;
|
|
136
175
|
}
|
|
137
176
|
const isNowExternal = isExternal || entry.name === "node_modules";
|
|
138
|
-
await scanDir(fullPath, isNowExternal);
|
|
177
|
+
await scanDir(fullPath, isNowExternal, depth + 1);
|
|
139
178
|
}
|
|
140
179
|
else if (entry.isFile()) {
|
|
180
|
+
// Skip external files if not including them
|
|
181
|
+
if (isExternal && !includeExternal) {
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
141
184
|
for (const ext of extensions) {
|
|
142
185
|
if (entry.name.endsWith(ext) &&
|
|
143
186
|
!entry.name.startsWith("_")) {
|
|
@@ -159,10 +202,14 @@ async function discoverComponents(workingDir, options) {
|
|
|
159
202
|
};
|
|
160
203
|
for (const dir of componentDirs) {
|
|
161
204
|
const fullDir = (0, path_1.join)(workingDir, dir);
|
|
205
|
+
// Skip if the directory path includes node_modules and we're not including external
|
|
206
|
+
if (dir.includes("node_modules") && !includeExternal) {
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
162
209
|
try {
|
|
163
210
|
const dirStat = await (0, promises_1.stat)(fullDir);
|
|
164
211
|
if (dirStat.isDirectory()) {
|
|
165
|
-
await scanDir(fullDir, dir.includes("node_modules"));
|
|
212
|
+
await scanDir(fullDir, dir.includes("node_modules"), 0);
|
|
166
213
|
}
|
|
167
214
|
}
|
|
168
215
|
catch {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sb-mig",
|
|
3
|
-
"version": "5.6.0-beta.
|
|
3
|
+
"version": "5.6.0-beta.3",
|
|
4
4
|
"description": "CLI to rule the world. (and handle stuff related to Storyblok CMS)",
|
|
5
5
|
"author": "Marcin Krawczyk <marckraw@icloud.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"./dist/*": "./dist/*"
|
|
34
34
|
},
|
|
35
35
|
"engines": {
|
|
36
|
-
"node": "
|
|
36
|
+
"node": ">=18.0.0"
|
|
37
37
|
},
|
|
38
38
|
"files": [
|
|
39
39
|
"/dist",
|
|
@@ -76,19 +76,19 @@
|
|
|
76
76
|
},
|
|
77
77
|
"dependencies": {
|
|
78
78
|
"@swc/core": "1.3.41",
|
|
79
|
-
"@swc/helpers": "0.
|
|
79
|
+
"@swc/helpers": "^0.5.18",
|
|
80
80
|
"chalk": "^4.1.2",
|
|
81
|
-
"dotenv": "^
|
|
81
|
+
"dotenv": "^17.2.3",
|
|
82
82
|
"form-data": "^4.0.0",
|
|
83
83
|
"fs-extra": "^11.2.0",
|
|
84
|
-
"glob": "
|
|
84
|
+
"glob": "^11.0.3",
|
|
85
85
|
"meow": "^11.0.0",
|
|
86
86
|
"ncp": "^2.0.0",
|
|
87
87
|
"node-fetch": "^3.3.2",
|
|
88
88
|
"rollup": "^3.28.0",
|
|
89
89
|
"rollup-plugin-ts": "^3.4.4",
|
|
90
90
|
"semver": "^7.6.2",
|
|
91
|
-
"storyblok-js-client": "
|
|
91
|
+
"storyblok-js-client": "^7.2.1",
|
|
92
92
|
"storyblok-schema-types": "^1.2.4",
|
|
93
93
|
"typescript": "^5.1.6",
|
|
94
94
|
"uuid": "^9.0.0"
|
|
@@ -104,10 +104,9 @@
|
|
|
104
104
|
"@sindresorhus/tsconfig": "^3.0.1",
|
|
105
105
|
"@storyblok/react": "^3.0.10",
|
|
106
106
|
"@types/fs-extra": "^11.0.4",
|
|
107
|
-
"@types/glob": "^8.1.0",
|
|
108
107
|
"@types/ncp": "^2.0.8",
|
|
109
|
-
"@types/node": "
|
|
110
|
-
"@types/uuid": "^
|
|
108
|
+
"@types/node": "^22.15.0",
|
|
109
|
+
"@types/uuid": "^10.0.0",
|
|
111
110
|
"@typescript-eslint/eslint-plugin": "^6.4.1",
|
|
112
111
|
"@typescript-eslint/parser": "^6.4.1",
|
|
113
112
|
"@vitest/coverage-v8": "^2.1.0",
|