mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-11-07 12:33:36 +09:00
Fix styles uploads (#789)
Forgot to move uploads logic to normalized styles
This commit is contained in:
1 parent
b40c967ee2
commit
44b96edf93
2 files changed
+106
-106
No files matched your search
@@ -1,6 +1,99 @@
|
||||
import { z } from "zod";
|
||||
import warnOnce from "warn-once";
|
||||
import DataLoader from "dataloader";
|
||||
import { type Patch, applyPatches } from "immer";
|
||||
import { prisma } from "@webstudio-is/prisma-client";
|
||||
import { Styles } from "@webstudio-is/react-sdk";
|
||||
import { type Asset, prisma } from "@webstudio-is/prisma-client";
|
||||
import { formatAsset } from "@webstudio-is/asset-uploader/server";
|
||||
import {
|
||||
type StyleProperty,
|
||||
ImageValue,
|
||||
SharedStyleValue,
|
||||
} from "@webstudio-is/css-data";
|
||||
|
||||
const assetsLoader = new DataLoader<string, Asset | undefined>(
|
||||
async (assetIds) => {
|
||||
const assets = await prisma.asset.findMany({
|
||||
where: {
|
||||
id: {
|
||||
// Spread to remove readonly from assetIds, otherwise ts error.
|
||||
in: [...assetIds],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Dataloader docs:
|
||||
* The Array of values must be the same length as the Array of keys.
|
||||
* Each index in the Array of values must correspond to the same index in the Array of keys.
|
||||
* (assets returned from DB can have a different order, some could not exist)
|
||||
*/
|
||||
return assetIds.map((assetId) =>
|
||||
assets.find((asset) => asset.id === assetId)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Use zod + DataLoader to load/format assets from the Assets table.
|
||||
*/
|
||||
const ImageAssetDbOut = z.object({
|
||||
type: z.literal("asset"),
|
||||
value: z
|
||||
.string()
|
||||
.uuid()
|
||||
.transform(async (assetId) => {
|
||||
const asset = await assetsLoader.load(assetId);
|
||||
if (asset === undefined) {
|
||||
warnOnce(true, `Asset with assetId "${assetId}" not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
return formatAsset(asset);
|
||||
}),
|
||||
});
|
||||
|
||||
const ImageValueDbOut = z.object({
|
||||
type: z.literal("image"),
|
||||
value: z
|
||||
.array(ImageAssetDbOut)
|
||||
// an Asset can be not present in DB, skip it.
|
||||
.transform((assets) => assets.filter((asset) => asset.value !== undefined)),
|
||||
});
|
||||
|
||||
const StyleValueDbOut = z.union([SharedStyleValue, ImageValueDbOut]);
|
||||
|
||||
const StylesItemDbOut = z.object({
|
||||
breakpointId: z.string(),
|
||||
instanceId: z.string(),
|
||||
// @todo can't figure out how to make property to be enum
|
||||
property: z.string() as z.ZodType<StyleProperty>,
|
||||
value: StyleValueDbOut,
|
||||
});
|
||||
|
||||
export const StylesDbOut = z.array(StylesItemDbOut);
|
||||
|
||||
/**
|
||||
* In the DB we hold only assetId
|
||||
**/
|
||||
const ImageValueDbIn = ImageValue.transform((imageStyle) => ({
|
||||
type: imageStyle.type,
|
||||
value: imageStyle.value.map((value) =>
|
||||
/* Now value.type is always equal to the "asset", but in the future, it will have additional types */
|
||||
value.type === "asset" ? { type: "asset", value: value.value.id } : value
|
||||
),
|
||||
}));
|
||||
|
||||
const StyleValueDbIn = z.union([SharedStyleValue, ImageValueDbIn]);
|
||||
|
||||
const StylesItemDbIn = z.object({
|
||||
breakpointId: z.string(),
|
||||
instanceId: z.string(),
|
||||
// @todo can't figure out how to make property to be enum
|
||||
property: z.string() as z.ZodType<StyleProperty>,
|
||||
value: StyleValueDbIn,
|
||||
});
|
||||
|
||||
export const StylesDbIn = z.array(StylesItemDbIn);
|
||||
|
||||
const loadStylesByTreeId = async (treeId: string) => {
|
||||
const tree = await prisma.tree.findUnique({
|
||||
@@ -11,7 +104,7 @@ const loadStylesByTreeId = async (treeId: string) => {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Styles.parse(JSON.parse(tree.styles));
|
||||
return StylesDbOut.parse(JSON.parse(tree.styles));
|
||||
};
|
||||
|
||||
export const patch = async (
|
||||
@@ -19,7 +112,7 @@ export const patch = async (
|
||||
patches: Array<Patch>
|
||||
) => {
|
||||
const styles = await loadStylesByTreeId(treeId);
|
||||
const patchedStyles = Styles.parse(applyPatches(styles, patches));
|
||||
const patchedStyles = StylesDbIn.parse(applyPatches(styles, patches));
|
||||
|
||||
await prisma.tree.update({
|
||||
data: {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { formatAsset } from "@webstudio-is/asset-uploader/server";
|
||||
import {
|
||||
type Tree,
|
||||
type ComponentName,
|
||||
@@ -10,75 +9,13 @@ import {
|
||||
} from "@webstudio-is/react-sdk";
|
||||
import { applyPatches, type Patch } from "immer";
|
||||
import {
|
||||
Asset,
|
||||
prisma,
|
||||
type Prisma,
|
||||
type Tree as DbTree,
|
||||
} from "@webstudio-is/prisma-client";
|
||||
import { utils } from "../index";
|
||||
import { SharedStyleValue, ImageValue } from "@webstudio-is/css-data";
|
||||
import { z } from "zod";
|
||||
import DataLoader from "dataloader";
|
||||
import warnOnce from "warn-once";
|
||||
|
||||
const assetsLoader = new DataLoader<string, Asset | undefined>(
|
||||
async (assetIds) => {
|
||||
const assets = await prisma.asset.findMany({
|
||||
where: {
|
||||
id: {
|
||||
// Spread to remove readonly from assetIds, otherwise ts error.
|
||||
in: [...assetIds],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Dataloader docs:
|
||||
* The Array of values must be the same length as the Array of keys.
|
||||
* Each index in the Array of values must correspond to the same index in the Array of keys.
|
||||
* (assets returned from DB can have a different order, some could not exist)
|
||||
*/
|
||||
return assetIds.map((assetId) =>
|
||||
assets.find((asset) => asset.id === assetId)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Use zod + DataLoader to load/format assets from the Assets table.
|
||||
*/
|
||||
const ImageAssetDbOut = z.object({
|
||||
type: z.literal("asset"),
|
||||
value: z
|
||||
.string()
|
||||
.uuid()
|
||||
.transform(async (assetId) => {
|
||||
const asset = await assetsLoader.load(assetId);
|
||||
if (asset === undefined) {
|
||||
warnOnce(true, `Asset with assetId "${assetId}" not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
return formatAsset(asset);
|
||||
}),
|
||||
});
|
||||
|
||||
const ImageValueDbOut = z.object({
|
||||
type: z.literal("image"),
|
||||
value: z
|
||||
.array(ImageAssetDbOut)
|
||||
// an Asset can be not present in DB, skip it.
|
||||
.transform((assets) => assets.filter((asset) => asset.value !== undefined)),
|
||||
});
|
||||
|
||||
const StyleValueDbOut = z.union([SharedStyleValue, ImageValueDbOut]);
|
||||
|
||||
const StyleDbOut = z.record(z.string(), StyleValueDbOut);
|
||||
|
||||
export const CssRuleDbOut = z.object({
|
||||
style: StyleDbOut,
|
||||
breakpoint: z.optional(z.string()),
|
||||
});
|
||||
import { StylesDbIn, StylesDbOut } from "./styles";
|
||||
|
||||
/**
|
||||
* validate/transform DB data schema to the client schema.
|
||||
@@ -89,42 +26,10 @@ const InstanceDbOut = z.lazy(() =>
|
||||
id: z.string(),
|
||||
component: z.string(),
|
||||
children: z.array(z.union([InstanceDbOut, Text])),
|
||||
cssRules: z.optional(z.array(CssRuleDbOut)),
|
||||
cssRules: z.optional(z.unknown()),
|
||||
})
|
||||
) as z.ZodType<Instance>;
|
||||
|
||||
/**
|
||||
* In the DB we hold only assetId
|
||||
**/
|
||||
const ImageValueDbIn = ImageValue.transform((imageStyle) => ({
|
||||
type: imageStyle.type,
|
||||
value: imageStyle.value.map((value) =>
|
||||
/* Now value.type is always equal to the "asset", but in the future, it will have additional types */
|
||||
value.type === "asset" ? { type: "asset", value: value.value.id } : value
|
||||
),
|
||||
}));
|
||||
|
||||
const StyleValueDbIn = z.union([SharedStyleValue, ImageValueDbIn]);
|
||||
|
||||
const StyleDbIn = z.record(z.string(), StyleValueDbIn);
|
||||
|
||||
export const CssRuleDbIn = z.object({
|
||||
style: StyleDbIn,
|
||||
breakpoint: z.optional(z.string()),
|
||||
});
|
||||
|
||||
/**
|
||||
* validate/transform client schema into DB data schema.
|
||||
*/
|
||||
const InstanceDbIn = z.lazy(() =>
|
||||
z.object({
|
||||
type: z.literal("instance"),
|
||||
id: z.string(),
|
||||
component: z.string(),
|
||||
children: z.array(z.union([InstanceDbIn, Text])),
|
||||
})
|
||||
) as /* Instance is wrong type here, ImageValue is different after transform. We don't use it anyway */ z.ZodType<Instance>;
|
||||
|
||||
type TreeData = Omit<Tree, "id">;
|
||||
|
||||
export const createTree = (): TreeData => {
|
||||
@@ -143,13 +48,13 @@ export const create = async (
|
||||
treeData: TreeData,
|
||||
client: Prisma.TransactionClient = prisma
|
||||
): Promise<DbTree> => {
|
||||
const root = InstanceDbIn.parse(treeData.root);
|
||||
const root = Instance.parse(treeData.root);
|
||||
|
||||
return await client.tree.create({
|
||||
data: {
|
||||
root: JSON.stringify(root),
|
||||
presetStyles: JSON.stringify(treeData.presetStyles),
|
||||
styles: JSON.stringify(treeData.styles),
|
||||
styles: JSON.stringify(await StylesDbIn.parseAsync(treeData.styles)),
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -184,14 +89,16 @@ export const loadById = async (
|
||||
|
||||
const dbRoot = JSON.parse(tree.root);
|
||||
|
||||
const root = await InstanceDbOut.parseAsync(dbRoot);
|
||||
const root = InstanceDbOut.parse(dbRoot);
|
||||
|
||||
deleteCssRulesFromInstancesMutable(root);
|
||||
|
||||
Instance.parse(root);
|
||||
|
||||
const presetStyles = PresetStyles.parse(JSON.parse(tree.presetStyles));
|
||||
const styles = Styles.parse(JSON.parse(tree.styles));
|
||||
const styles = Styles.parse(
|
||||
await StylesDbOut.parseAsync(JSON.parse(tree.styles))
|
||||
);
|
||||
|
||||
return {
|
||||
...tree,
|
||||
@@ -238,7 +145,7 @@ export const patch = async (
|
||||
);
|
||||
const presetStyles = [...tree.presetStyles, ...missingPresetStyles];
|
||||
|
||||
const root = InstanceDbIn.parse(clientRoot);
|
||||
const root = Instance.parse(clientRoot);
|
||||
|
||||
await prisma.tree.update({
|
||||
data: {
|
||||
|
||||
Reference in new issue
Block a user