@webstudio-is/sdk 0.263.0 → 0.265.0
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/lib/index.js +267 -16
- package/lib/migrations/webstudio-data.js +163 -0
- package/lib/types/index.d.ts +1 -0
- package/lib/types/migrations/index.d.ts +1 -0
- package/lib/types/migrations/pages.d.ts +7 -0
- package/lib/types/migrations/styles.d.ts +2 -0
- package/lib/types/migrations/webstudio-data.d.ts +8 -0
- package/lib/types/page-utils.d.ts +6 -1
- package/lib/types/schema/component-meta.d.ts +110 -0
- package/lib/types/schema/pages.d.ts +77 -150
- package/lib/types/schema/prop-meta.d.ts +66 -0
- package/lib/types/schema/props.d.ts +8 -8
- package/lib/types/schema/webstudio.d.ts +6 -6
- package/package.json +16 -10
package/lib/index.js
CHANGED
|
@@ -97,10 +97,6 @@ var commonPageFields = {
|
|
|
97
97
|
)
|
|
98
98
|
};
|
|
99
99
|
var HomePagePath = z2.string().refine((path) => path === "", "Home page path must be empty");
|
|
100
|
-
var HomePage = z2.object({
|
|
101
|
-
...commonPageFields,
|
|
102
|
-
path: HomePagePath
|
|
103
|
-
});
|
|
104
100
|
var DefaultPagePage = z2.string().refine((path) => path !== "", "Can't be empty").refine((path) => path !== "/", "Can't be just a /").refine((path) => path.endsWith("/") === false, "Can't end with a /").refine((path) => path.includes("//") === false, "Can't contain repeating /").refine(
|
|
105
101
|
(path) => /^[-_a-z0-9*:?\\/.]*$/.test(path),
|
|
106
102
|
"Only a-z, 0-9, -, _, /, :, ?, . and * are allowed"
|
|
@@ -133,7 +129,7 @@ var PagePath = DefaultPagePage.refine(
|
|
|
133
129
|
);
|
|
134
130
|
var Page = z2.object({
|
|
135
131
|
...commonPageFields,
|
|
136
|
-
path: PagePath
|
|
132
|
+
path: z2.union([HomePagePath, PagePath])
|
|
137
133
|
});
|
|
138
134
|
var ProjectMeta = z2.object({
|
|
139
135
|
// All fields are optional to ensure consistency and allow for the addition of new fields without requiring migration
|
|
@@ -163,9 +159,123 @@ var Pages = z2.object({
|
|
|
163
159
|
meta: ProjectMeta.optional(),
|
|
164
160
|
compiler: CompilerSettings.optional(),
|
|
165
161
|
redirects: z2.array(PageRedirect).optional(),
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
162
|
+
homePageId: PageId,
|
|
163
|
+
rootFolderId: FolderId,
|
|
164
|
+
pages: z2.map(PageId, Page),
|
|
165
|
+
folders: z2.map(FolderId, Folder).refine((folders) => folders.size > 0, "Folders can't be empty")
|
|
166
|
+
}).superRefine((pages, context) => {
|
|
167
|
+
const homePage = pages.pages.get(pages.homePageId);
|
|
168
|
+
const rootFolder = pages.folders.get(pages.rootFolderId);
|
|
169
|
+
if (homePage === void 0) {
|
|
170
|
+
context.addIssue({
|
|
171
|
+
code: z2.ZodIssueCode.custom,
|
|
172
|
+
path: ["homePageId"],
|
|
173
|
+
message: "Home page must reference an existing page"
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
if (rootFolder === void 0) {
|
|
177
|
+
context.addIssue({
|
|
178
|
+
code: z2.ZodIssueCode.custom,
|
|
179
|
+
path: ["rootFolderId"],
|
|
180
|
+
message: "Root folder must reference an existing folder"
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
if (homePage !== void 0 && homePage.path !== "") {
|
|
184
|
+
context.addIssue({
|
|
185
|
+
code: z2.ZodIssueCode.custom,
|
|
186
|
+
path: ["pages", pages.homePageId, "path"],
|
|
187
|
+
message: "Home page path must be empty"
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
for (const [pageId, page] of pages.pages) {
|
|
191
|
+
if (page.id !== pageId) {
|
|
192
|
+
context.addIssue({
|
|
193
|
+
code: z2.ZodIssueCode.custom,
|
|
194
|
+
path: ["pages", pageId, "id"],
|
|
195
|
+
message: "Page id must match its record key"
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
if (pageId !== pages.homePageId && page.path === "") {
|
|
199
|
+
context.addIssue({
|
|
200
|
+
code: z2.ZodIssueCode.custom,
|
|
201
|
+
path: ["pages", pageId, "path"],
|
|
202
|
+
message: "Page path can't be empty"
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
for (const [folderId, folder] of pages.folders) {
|
|
207
|
+
if (folder.id !== folderId) {
|
|
208
|
+
context.addIssue({
|
|
209
|
+
code: z2.ZodIssueCode.custom,
|
|
210
|
+
path: ["folders", folderId, "id"],
|
|
211
|
+
message: "Folder id must match its record key"
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
for (const [index, childId] of folder.children.entries()) {
|
|
215
|
+
if (pages.pages.has(childId) === false && pages.folders.has(childId) === false) {
|
|
216
|
+
context.addIssue({
|
|
217
|
+
code: z2.ZodIssueCode.custom,
|
|
218
|
+
path: ["folders", folderId, "children", index],
|
|
219
|
+
message: "Folder child must reference an existing page or folder"
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
if (childId === pages.rootFolderId) {
|
|
223
|
+
context.addIssue({
|
|
224
|
+
code: z2.ZodIssueCode.custom,
|
|
225
|
+
path: ["folders", folderId, "children", index],
|
|
226
|
+
message: "Root folder can't be nested"
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (rootFolder !== void 0 && rootFolder.children[0] !== pages.homePageId) {
|
|
232
|
+
context.addIssue({
|
|
233
|
+
code: z2.ZodIssueCode.custom,
|
|
234
|
+
path: ["folders", pages.rootFolderId, "children"],
|
|
235
|
+
message: "Root folder must start with the home page"
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
const childParents = /* @__PURE__ */ new Map();
|
|
239
|
+
for (const [folderId, folder] of pages.folders) {
|
|
240
|
+
for (const [index, childId] of folder.children.entries()) {
|
|
241
|
+
const parentId = childParents.get(childId);
|
|
242
|
+
if (parentId !== void 0) {
|
|
243
|
+
context.addIssue({
|
|
244
|
+
code: z2.ZodIssueCode.custom,
|
|
245
|
+
path: ["folders", folderId, "children", index],
|
|
246
|
+
message: `Child is already registered in folder "${parentId}"`
|
|
247
|
+
});
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
childParents.set(childId, folderId);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
const hasFolderCycle = (folderId, path = /* @__PURE__ */ new Set()) => {
|
|
254
|
+
if (path.has(folderId)) {
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
const folder = pages.folders.get(folderId);
|
|
258
|
+
if (folder === void 0) {
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
path.add(folderId);
|
|
262
|
+
for (const childId of folder.children) {
|
|
263
|
+
if (pages.folders.has(childId) && hasFolderCycle(childId, path)) {
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
path.delete(folderId);
|
|
268
|
+
return false;
|
|
269
|
+
};
|
|
270
|
+
for (const folderId of pages.folders.keys()) {
|
|
271
|
+
if (hasFolderCycle(folderId)) {
|
|
272
|
+
context.addIssue({
|
|
273
|
+
code: z2.ZodIssueCode.custom,
|
|
274
|
+
path: ["folders", folderId, "children"],
|
|
275
|
+
message: "Folders can't contain cycles"
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
}
|
|
169
279
|
});
|
|
170
280
|
|
|
171
281
|
// src/schema/instances.ts
|
|
@@ -700,7 +810,8 @@ import { z as z14 } from "zod";
|
|
|
700
810
|
var common = {
|
|
701
811
|
label: z14.string().optional(),
|
|
702
812
|
description: z14.string().optional(),
|
|
703
|
-
required: z14.boolean()
|
|
813
|
+
required: z14.boolean(),
|
|
814
|
+
contentMode: z14.boolean().optional()
|
|
704
815
|
};
|
|
705
816
|
var Tag = z14.object({
|
|
706
817
|
...common,
|
|
@@ -2364,16 +2475,36 @@ var isAbsoluteUrl = (href) => {
|
|
|
2364
2475
|
// src/page-utils.ts
|
|
2365
2476
|
var ROOT_FOLDER_ID = "root";
|
|
2366
2477
|
var isRootFolder = ({ id }) => id === ROOT_FOLDER_ID;
|
|
2478
|
+
var getPageById = (pages, pageId) => {
|
|
2479
|
+
return pages.pages.get(pageId);
|
|
2480
|
+
};
|
|
2481
|
+
var getFolderById = (pages, folderId) => {
|
|
2482
|
+
return pages.folders.get(folderId);
|
|
2483
|
+
};
|
|
2484
|
+
var getAllPages = (pages) => {
|
|
2485
|
+
return Array.from(pages.pages.values());
|
|
2486
|
+
};
|
|
2487
|
+
var getAllFolders = (pages) => {
|
|
2488
|
+
return Array.from(pages.folders.values());
|
|
2489
|
+
};
|
|
2490
|
+
var getHomePage = (pages) => {
|
|
2491
|
+
const homePage = getPageById(pages, pages.homePageId);
|
|
2492
|
+
if (homePage === void 0) {
|
|
2493
|
+
throw new Error(`Home page "${pages.homePageId}" was not found.`);
|
|
2494
|
+
}
|
|
2495
|
+
return homePage;
|
|
2496
|
+
};
|
|
2367
2497
|
var findPageByIdOrPath = (idOrPath, pages) => {
|
|
2368
|
-
if (idOrPath === "" || idOrPath === "/" || idOrPath === pages.
|
|
2369
|
-
return pages
|
|
2498
|
+
if (idOrPath === "" || idOrPath === "/" || idOrPath === pages.homePageId) {
|
|
2499
|
+
return getHomePage(pages);
|
|
2370
2500
|
}
|
|
2371
|
-
return pages.
|
|
2501
|
+
return getAllPages(pages).find(
|
|
2372
2502
|
(page) => page.id === idOrPath || getPagePath(page.id, pages) === idOrPath
|
|
2373
2503
|
);
|
|
2374
2504
|
};
|
|
2375
2505
|
var findParentFolderByChildId = (id, folders) => {
|
|
2376
|
-
|
|
2506
|
+
const folderList = folders instanceof Map ? folders.values() : folders;
|
|
2507
|
+
for (const folder of folderList) {
|
|
2377
2508
|
if (folder.children.includes(id)) {
|
|
2378
2509
|
return folder;
|
|
2379
2510
|
}
|
|
@@ -2382,7 +2513,7 @@ var findParentFolderByChildId = (id, folders) => {
|
|
|
2382
2513
|
var getPagePath = (id, pages) => {
|
|
2383
2514
|
const foldersMap = /* @__PURE__ */ new Map();
|
|
2384
2515
|
const childParentMap = /* @__PURE__ */ new Map();
|
|
2385
|
-
for (const folder of pages
|
|
2516
|
+
for (const folder of getAllFolders(pages)) {
|
|
2386
2517
|
foldersMap.set(folder.id, folder);
|
|
2387
2518
|
for (const childId of folder.children) {
|
|
2388
2519
|
childParentMap.set(childId, folder.id);
|
|
@@ -2390,7 +2521,7 @@ var getPagePath = (id, pages) => {
|
|
|
2390
2521
|
}
|
|
2391
2522
|
const paths = [];
|
|
2392
2523
|
let currentId = id;
|
|
2393
|
-
const allPages =
|
|
2524
|
+
const allPages = getAllPages(pages);
|
|
2394
2525
|
for (const page of allPages) {
|
|
2395
2526
|
if (page.id === id) {
|
|
2396
2527
|
paths.push(page.path);
|
|
@@ -2409,7 +2540,7 @@ var getPagePath = (id, pages) => {
|
|
|
2409
2540
|
return paths.reverse().join("/").replace(/\/+/g, "/");
|
|
2410
2541
|
};
|
|
2411
2542
|
var getStaticSiteMapXml = (pages, updatedAt) => {
|
|
2412
|
-
const allPages =
|
|
2543
|
+
const allPages = getAllPages(pages);
|
|
2413
2544
|
return allPages.filter((page) => (page.meta.documentType ?? "html") === "html").filter(
|
|
2414
2545
|
(page) => executeExpression(page.meta.excludePageFromSearch) !== true
|
|
2415
2546
|
).filter((page) => false === isPathnamePattern(page.path)).map((page) => ({
|
|
@@ -2418,6 +2549,117 @@ var getStaticSiteMapXml = (pages, updatedAt) => {
|
|
|
2418
2549
|
}));
|
|
2419
2550
|
};
|
|
2420
2551
|
|
|
2552
|
+
// src/migrations/pages.ts
|
|
2553
|
+
var toMap = (items, normalizeItem = (item) => item) => {
|
|
2554
|
+
if (items instanceof Map) {
|
|
2555
|
+
return new Map(
|
|
2556
|
+
Array.from(items, ([id, item]) => [id, normalizeItem(item)])
|
|
2557
|
+
);
|
|
2558
|
+
}
|
|
2559
|
+
const list = Array.isArray(items) ? items : Object.values(items);
|
|
2560
|
+
return new Map(list.map((item) => [item.id, normalizeItem(item)]));
|
|
2561
|
+
};
|
|
2562
|
+
var normalizePage = (page) => ({
|
|
2563
|
+
...page,
|
|
2564
|
+
meta: page.meta ?? {}
|
|
2565
|
+
});
|
|
2566
|
+
var isLegacyPages = (pages) => {
|
|
2567
|
+
if (typeof pages !== "object" || pages === null) {
|
|
2568
|
+
return false;
|
|
2569
|
+
}
|
|
2570
|
+
return "homePage" in pages && Array.isArray(pages.pages);
|
|
2571
|
+
};
|
|
2572
|
+
var isSerializedPages = (pages) => {
|
|
2573
|
+
if (typeof pages !== "object" || pages === null) {
|
|
2574
|
+
return false;
|
|
2575
|
+
}
|
|
2576
|
+
const candidate = pages;
|
|
2577
|
+
return typeof candidate.homePageId === "string" && typeof candidate.rootFolderId === "string" && candidate.pages !== void 0 && candidate.folders !== void 0;
|
|
2578
|
+
};
|
|
2579
|
+
var serializePages = (pages) => {
|
|
2580
|
+
const parsedPages = Pages.parse(pages);
|
|
2581
|
+
return {
|
|
2582
|
+
meta: parsedPages.meta,
|
|
2583
|
+
compiler: parsedPages.compiler,
|
|
2584
|
+
redirects: parsedPages.redirects,
|
|
2585
|
+
homePageId: parsedPages.homePageId,
|
|
2586
|
+
rootFolderId: parsedPages.rootFolderId,
|
|
2587
|
+
pages: Array.from(parsedPages.pages.values()),
|
|
2588
|
+
folders: Array.from(parsedPages.folders.values())
|
|
2589
|
+
};
|
|
2590
|
+
};
|
|
2591
|
+
var migratePages = (pages) => {
|
|
2592
|
+
if (isSerializedPages(pages) && pages.pages instanceof Map && pages.folders instanceof Map) {
|
|
2593
|
+
return pages;
|
|
2594
|
+
}
|
|
2595
|
+
if (isSerializedPages(pages)) {
|
|
2596
|
+
return {
|
|
2597
|
+
meta: pages.meta,
|
|
2598
|
+
compiler: pages.compiler,
|
|
2599
|
+
redirects: pages.redirects,
|
|
2600
|
+
homePageId: pages.homePageId,
|
|
2601
|
+
rootFolderId: pages.rootFolderId,
|
|
2602
|
+
pages: toMap(pages.pages, normalizePage),
|
|
2603
|
+
folders: toMap(pages.folders)
|
|
2604
|
+
};
|
|
2605
|
+
}
|
|
2606
|
+
if (isLegacyPages(pages) === false) {
|
|
2607
|
+
throw new Error("Pages data has unsupported shape.");
|
|
2608
|
+
}
|
|
2609
|
+
const homePage = {
|
|
2610
|
+
...normalizePage(pages.homePage),
|
|
2611
|
+
path: ""
|
|
2612
|
+
};
|
|
2613
|
+
const nextPages = /* @__PURE__ */ new Map([[homePage.id, homePage]]);
|
|
2614
|
+
for (const page of pages.pages) {
|
|
2615
|
+
if (page.id === homePage.id) {
|
|
2616
|
+
continue;
|
|
2617
|
+
}
|
|
2618
|
+
nextPages.set(page.id, normalizePage(page));
|
|
2619
|
+
}
|
|
2620
|
+
const nextFolders = /* @__PURE__ */ new Map();
|
|
2621
|
+
for (const folder of pages.folders ?? []) {
|
|
2622
|
+
nextFolders.set(folder.id, { ...folder, children: [...folder.children] });
|
|
2623
|
+
}
|
|
2624
|
+
const rootFolder = Array.from(nextFolders.values()).find(isRootFolder) ?? pages.folders?.[0] ?? {
|
|
2625
|
+
id: ROOT_FOLDER_ID,
|
|
2626
|
+
name: "Root",
|
|
2627
|
+
slug: "",
|
|
2628
|
+
children: []
|
|
2629
|
+
};
|
|
2630
|
+
if (nextFolders.has(rootFolder.id) === false) {
|
|
2631
|
+
nextFolders.set(rootFolder.id, { ...rootFolder, children: [] });
|
|
2632
|
+
}
|
|
2633
|
+
const nextRootFolder = nextFolders.get(rootFolder.id);
|
|
2634
|
+
if (nextRootFolder === void 0) {
|
|
2635
|
+
throw new Error("Pages must include a root folder.");
|
|
2636
|
+
}
|
|
2637
|
+
for (const folder of nextFolders.values()) {
|
|
2638
|
+
folder.children = folder.children.filter(
|
|
2639
|
+
(childId) => childId !== homePage.id
|
|
2640
|
+
);
|
|
2641
|
+
}
|
|
2642
|
+
nextRootFolder.children.unshift(homePage.id);
|
|
2643
|
+
const referencedIds = new Set(
|
|
2644
|
+
Array.from(nextFolders.values()).flatMap((folder) => folder.children)
|
|
2645
|
+
);
|
|
2646
|
+
for (const page of pages.pages) {
|
|
2647
|
+
if (page.id !== homePage.id && referencedIds.has(page.id) === false) {
|
|
2648
|
+
nextRootFolder.children.push(page.id);
|
|
2649
|
+
referencedIds.add(page.id);
|
|
2650
|
+
}
|
|
2651
|
+
}
|
|
2652
|
+
return {
|
|
2653
|
+
meta: pages.meta,
|
|
2654
|
+
compiler: pages.compiler,
|
|
2655
|
+
redirects: pages.redirects,
|
|
2656
|
+
homePageId: homePage.id,
|
|
2657
|
+
rootFolderId: rootFolder.id,
|
|
2658
|
+
pages: nextPages,
|
|
2659
|
+
folders: nextFolders
|
|
2660
|
+
};
|
|
2661
|
+
};
|
|
2662
|
+
|
|
2421
2663
|
// src/scope.ts
|
|
2422
2664
|
import reservedIdentifiers from "reserved-identifiers";
|
|
2423
2665
|
var identifiers = reservedIdentifiers({ includeGlobalProperties: true });
|
|
@@ -3008,6 +3250,7 @@ export {
|
|
|
3008
3250
|
FONT_EXTENSIONS,
|
|
3009
3251
|
FileAsset,
|
|
3010
3252
|
Folder,
|
|
3253
|
+
FolderId,
|
|
3011
3254
|
FolderName,
|
|
3012
3255
|
FontAsset,
|
|
3013
3256
|
HomePagePath,
|
|
@@ -3021,6 +3264,7 @@ export {
|
|
|
3021
3264
|
Instances,
|
|
3022
3265
|
MIME_CATEGORIES,
|
|
3023
3266
|
OldPagePath,
|
|
3267
|
+
PageId,
|
|
3024
3268
|
PageName,
|
|
3025
3269
|
PagePath,
|
|
3026
3270
|
PageRedirect,
|
|
@@ -3087,12 +3331,17 @@ export {
|
|
|
3087
3331
|
generateObjectExpression,
|
|
3088
3332
|
generatePageMeta,
|
|
3089
3333
|
generateResources,
|
|
3334
|
+
getAllFolders,
|
|
3335
|
+
getAllPages,
|
|
3090
3336
|
getAssetMime,
|
|
3091
3337
|
getAssetUrl,
|
|
3092
3338
|
getExpressionIdentifiers,
|
|
3339
|
+
getFolderById,
|
|
3340
|
+
getHomePage,
|
|
3093
3341
|
getIndexesWithinAncestors,
|
|
3094
3342
|
getMimeTypeByExtension,
|
|
3095
3343
|
getMimeTypeByFilename,
|
|
3344
|
+
getPageById,
|
|
3096
3345
|
getPagePath,
|
|
3097
3346
|
getStaticSiteMapXml,
|
|
3098
3347
|
getStyleDeclKey,
|
|
@@ -3108,6 +3357,7 @@ export {
|
|
|
3108
3357
|
isRootFolder,
|
|
3109
3358
|
lintExpression,
|
|
3110
3359
|
matchPathnameParams,
|
|
3360
|
+
migratePages,
|
|
3111
3361
|
parseComponentName,
|
|
3112
3362
|
parseObjectExpression,
|
|
3113
3363
|
portalComponent,
|
|
@@ -3115,6 +3365,7 @@ export {
|
|
|
3115
3365
|
replaceFormActionsWithResources,
|
|
3116
3366
|
rootComponent,
|
|
3117
3367
|
scrollAnimationSchema,
|
|
3368
|
+
serializePages,
|
|
3118
3369
|
systemParameter,
|
|
3119
3370
|
tags,
|
|
3120
3371
|
toRuntimeAsset,
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// src/page-utils.ts
|
|
2
|
+
var ROOT_FOLDER_ID = "root";
|
|
3
|
+
var isRootFolder = ({ id }) => id === ROOT_FOLDER_ID;
|
|
4
|
+
|
|
5
|
+
// src/migrations/pages.ts
|
|
6
|
+
var toMap = (items, normalizeItem = (item) => item) => {
|
|
7
|
+
if (items instanceof Map) {
|
|
8
|
+
return new Map(
|
|
9
|
+
Array.from(items, ([id, item]) => [id, normalizeItem(item)])
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
const list = Array.isArray(items) ? items : Object.values(items);
|
|
13
|
+
return new Map(list.map((item) => [item.id, normalizeItem(item)]));
|
|
14
|
+
};
|
|
15
|
+
var normalizePage = (page) => ({
|
|
16
|
+
...page,
|
|
17
|
+
meta: page.meta ?? {}
|
|
18
|
+
});
|
|
19
|
+
var isLegacyPages = (pages) => {
|
|
20
|
+
if (typeof pages !== "object" || pages === null) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
return "homePage" in pages && Array.isArray(pages.pages);
|
|
24
|
+
};
|
|
25
|
+
var isSerializedPages = (pages) => {
|
|
26
|
+
if (typeof pages !== "object" || pages === null) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
const candidate = pages;
|
|
30
|
+
return typeof candidate.homePageId === "string" && typeof candidate.rootFolderId === "string" && candidate.pages !== void 0 && candidate.folders !== void 0;
|
|
31
|
+
};
|
|
32
|
+
var migratePages = (pages) => {
|
|
33
|
+
if (isSerializedPages(pages) && pages.pages instanceof Map && pages.folders instanceof Map) {
|
|
34
|
+
return pages;
|
|
35
|
+
}
|
|
36
|
+
if (isSerializedPages(pages)) {
|
|
37
|
+
return {
|
|
38
|
+
meta: pages.meta,
|
|
39
|
+
compiler: pages.compiler,
|
|
40
|
+
redirects: pages.redirects,
|
|
41
|
+
homePageId: pages.homePageId,
|
|
42
|
+
rootFolderId: pages.rootFolderId,
|
|
43
|
+
pages: toMap(pages.pages, normalizePage),
|
|
44
|
+
folders: toMap(pages.folders)
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
if (isLegacyPages(pages) === false) {
|
|
48
|
+
throw new Error("Pages data has unsupported shape.");
|
|
49
|
+
}
|
|
50
|
+
const homePage = {
|
|
51
|
+
...normalizePage(pages.homePage),
|
|
52
|
+
path: ""
|
|
53
|
+
};
|
|
54
|
+
const nextPages = /* @__PURE__ */ new Map([[homePage.id, homePage]]);
|
|
55
|
+
for (const page of pages.pages) {
|
|
56
|
+
if (page.id === homePage.id) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
nextPages.set(page.id, normalizePage(page));
|
|
60
|
+
}
|
|
61
|
+
const nextFolders = /* @__PURE__ */ new Map();
|
|
62
|
+
for (const folder of pages.folders ?? []) {
|
|
63
|
+
nextFolders.set(folder.id, { ...folder, children: [...folder.children] });
|
|
64
|
+
}
|
|
65
|
+
const rootFolder = Array.from(nextFolders.values()).find(isRootFolder) ?? pages.folders?.[0] ?? {
|
|
66
|
+
id: ROOT_FOLDER_ID,
|
|
67
|
+
name: "Root",
|
|
68
|
+
slug: "",
|
|
69
|
+
children: []
|
|
70
|
+
};
|
|
71
|
+
if (nextFolders.has(rootFolder.id) === false) {
|
|
72
|
+
nextFolders.set(rootFolder.id, { ...rootFolder, children: [] });
|
|
73
|
+
}
|
|
74
|
+
const nextRootFolder = nextFolders.get(rootFolder.id);
|
|
75
|
+
if (nextRootFolder === void 0) {
|
|
76
|
+
throw new Error("Pages must include a root folder.");
|
|
77
|
+
}
|
|
78
|
+
for (const folder of nextFolders.values()) {
|
|
79
|
+
folder.children = folder.children.filter(
|
|
80
|
+
(childId) => childId !== homePage.id
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
nextRootFolder.children.unshift(homePage.id);
|
|
84
|
+
const referencedIds = new Set(
|
|
85
|
+
Array.from(nextFolders.values()).flatMap((folder) => folder.children)
|
|
86
|
+
);
|
|
87
|
+
for (const page of pages.pages) {
|
|
88
|
+
if (page.id !== homePage.id && referencedIds.has(page.id) === false) {
|
|
89
|
+
nextRootFolder.children.push(page.id);
|
|
90
|
+
referencedIds.add(page.id);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
meta: pages.meta,
|
|
95
|
+
compiler: pages.compiler,
|
|
96
|
+
redirects: pages.redirects,
|
|
97
|
+
homePageId: homePage.id,
|
|
98
|
+
rootFolderId: rootFolder.id,
|
|
99
|
+
pages: nextPages,
|
|
100
|
+
folders: nextFolders
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// src/migrations/styles.ts
|
|
105
|
+
import { hyphenateProperty, toValue } from "@webstudio-is/css-engine";
|
|
106
|
+
import {
|
|
107
|
+
camelCaseProperty,
|
|
108
|
+
expandShorthands,
|
|
109
|
+
parseCssValue
|
|
110
|
+
} from "@webstudio-is/css-data";
|
|
111
|
+
|
|
112
|
+
// src/schema/styles.ts
|
|
113
|
+
import { z } from "zod";
|
|
114
|
+
import { StyleValue } from "@webstudio-is/css-engine";
|
|
115
|
+
var StyleDeclRaw = z.object({
|
|
116
|
+
styleSourceId: z.string(),
|
|
117
|
+
breakpointId: z.string(),
|
|
118
|
+
state: z.optional(z.string()),
|
|
119
|
+
// @todo can't figure out how to make property to be enum
|
|
120
|
+
property: z.string(),
|
|
121
|
+
value: StyleValue,
|
|
122
|
+
listed: z.boolean().optional().describe("Whether the style is from the Advanced panel")
|
|
123
|
+
});
|
|
124
|
+
var StyleDecl = StyleDeclRaw;
|
|
125
|
+
var getStyleDeclKey = (styleDecl) => {
|
|
126
|
+
return `${styleDecl.styleSourceId}:${styleDecl.breakpointId}:${styleDecl.property}:${styleDecl.state ?? ""}`;
|
|
127
|
+
};
|
|
128
|
+
var Styles = z.map(z.string(), StyleDecl);
|
|
129
|
+
|
|
130
|
+
// src/migrations/styles.ts
|
|
131
|
+
var migratedShorthands = /* @__PURE__ */ new Set([
|
|
132
|
+
"overflow",
|
|
133
|
+
"transition",
|
|
134
|
+
"white-space",
|
|
135
|
+
"background-position"
|
|
136
|
+
]);
|
|
137
|
+
var migrateStylesMutable = (styles) => {
|
|
138
|
+
for (const [styleDeclKey, styleDecl] of styles) {
|
|
139
|
+
const property = hyphenateProperty(styleDecl.property);
|
|
140
|
+
if (migratedShorthands.has(property) === false) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
styles.delete(styleDeclKey);
|
|
144
|
+
const longhands = expandShorthands([[property, toValue(styleDecl.value)]]);
|
|
145
|
+
for (const [hyphenedProperty, value] of longhands) {
|
|
146
|
+
const longhandStyleDecl = {
|
|
147
|
+
...styleDecl,
|
|
148
|
+
property: camelCaseProperty(hyphenedProperty),
|
|
149
|
+
value: parseCssValue(hyphenedProperty, value)
|
|
150
|
+
};
|
|
151
|
+
styles.set(getStyleDeclKey(longhandStyleDecl), longhandStyleDecl);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// src/migrations/webstudio-data.ts
|
|
157
|
+
var migrateWebstudioDataMutable = (data) => {
|
|
158
|
+
data.pages = migratePages(data.pages);
|
|
159
|
+
migrateStylesMutable(data.styles);
|
|
160
|
+
};
|
|
161
|
+
export {
|
|
162
|
+
migrateWebstudioDataMutable
|
|
163
|
+
};
|
package/lib/types/index.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export * from "./assets";
|
|
|
16
16
|
export * from "./core-metas";
|
|
17
17
|
export * from "./instances-utils";
|
|
18
18
|
export * from "./page-utils";
|
|
19
|
+
export * from "./migrations";
|
|
19
20
|
export * from "./scope";
|
|
20
21
|
export * from "./expression";
|
|
21
22
|
export * from "./resources-generator";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./pages";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type Folder, type Page, type Pages } from "../schema/pages";
|
|
2
|
+
export type SerializedPages = Omit<Pages, "pages" | "folders"> & {
|
|
3
|
+
pages: Page[];
|
|
4
|
+
folders: Folder[];
|
|
5
|
+
};
|
|
6
|
+
export declare const serializePages: (pages: Pages) => SerializedPages;
|
|
7
|
+
export declare const migratePages: (pages: unknown) => Pages;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { WebstudioData } from "../schema/webstudio";
|
|
2
|
+
/**
|
|
3
|
+
* Normalizes persisted project data after loading.
|
|
4
|
+
*
|
|
5
|
+
* This is intentionally idempotent because data can pass through multiple
|
|
6
|
+
* load boundaries before all callers stop seeing older persisted shapes.
|
|
7
|
+
*/
|
|
8
|
+
export declare const migrateWebstudioDataMutable: (data: WebstudioData) => void;
|
|
@@ -6,6 +6,11 @@ export declare const ROOT_FOLDER_ID = "root";
|
|
|
6
6
|
export declare const isRootFolder: ({ id }: {
|
|
7
7
|
id: Folder["id"];
|
|
8
8
|
}) => boolean;
|
|
9
|
+
export declare const getPageById: (pages: Pages, pageId: Page["id"]) => Page | undefined;
|
|
10
|
+
export declare const getFolderById: (pages: Pages, folderId: Folder["id"]) => Folder | undefined;
|
|
11
|
+
export declare const getAllPages: (pages: Pages) => Page[];
|
|
12
|
+
export declare const getAllFolders: (pages: Pages) => Folder[];
|
|
13
|
+
export declare const getHomePage: (pages: Pages) => Page;
|
|
9
14
|
/**
|
|
10
15
|
* Find a page by id or path.
|
|
11
16
|
*/
|
|
@@ -13,7 +18,7 @@ export declare const findPageByIdOrPath: (idOrPath: string, pages: Pages) => Pag
|
|
|
13
18
|
/**
|
|
14
19
|
* Find a folder that has has that id in the children.
|
|
15
20
|
*/
|
|
16
|
-
export declare const findParentFolderByChildId: (id: Folder["id"] | Page["id"], folders:
|
|
21
|
+
export declare const findParentFolderByChildId: (id: Folder["id"] | Page["id"], folders: Iterable<Folder> | Map<Folder["id"], Folder>) => Folder | undefined;
|
|
17
22
|
/**
|
|
18
23
|
* Get a path from all folder slugs from root to the current folder or page.
|
|
19
24
|
*/
|