diff --git a/apps/designer/app/designer/features/props-panel/use-props-logic.test.ts b/apps/designer/app/designer/features/props-panel/use-props-logic.test.ts index 059ecd93af..5c1fd6e705 100644 --- a/apps/designer/app/designer/features/props-panel/use-props-logic.test.ts +++ b/apps/designer/app/designer/features/props-panel/use-props-logic.test.ts @@ -1,5 +1,5 @@ import { renderHook, act } from "@testing-library/react-hooks"; -import { type ComponentName, UserProp } from "@webstudio-is/react-sdk"; +import type { ComponentName, UserProp } from "@webstudio-is/react-sdk"; import { nanoid } from "nanoid"; import type { SelectedInstanceData } from "@webstudio-is/project"; import { usePropsLogic } from "./use-props-logic"; diff --git a/apps/designer/app/shared/copy-paste/serialize.ts b/apps/designer/app/shared/copy-paste/serialize.ts index f018e46566..50c1a19038 100644 --- a/apps/designer/app/shared/copy-paste/serialize.ts +++ b/apps/designer/app/shared/copy-paste/serialize.ts @@ -1,11 +1,11 @@ -import { Instance, UserUserProp } from "@webstudio-is/react-sdk"; +import { Instance, UserProps } from "@webstudio-is/react-sdk"; import { z } from "zod"; const TYPE = "@webstudio/instance/v0.1" as const; const InstanceCopyData = z.object({ instance: Instance, - props: z.array(UserUserProp).optional(), + props: UserProps.optional(), }); export type InstanceCopyData = z.infer; diff --git a/apps/designer/jest.config.js b/apps/designer/jest.config.js index 1a2a0f591f..f63069a28f 100644 --- a/apps/designer/jest.config.js +++ b/apps/designer/jest.config.js @@ -21,4 +21,5 @@ module.exports = { moduleNameMapper: { "^~/(.*)$": "/app/$1", }, + // extensionsToTreatAsEsm: [".ts", ".tsx"], }; diff --git a/packages/asset-uploader/package.json b/packages/asset-uploader/package.json index d4304c4b01..e7baba653b 100644 --- a/packages/asset-uploader/package.json +++ b/packages/asset-uploader/package.json @@ -29,7 +29,7 @@ "@webstudio-is/jest-config": "*", "tsup": "^6.1.3", "typescript": "4.7.4", - "zod": "^3.17.3" + "zod": "^3.19.1" }, "exports": { ".": "./lib/index.js", diff --git a/packages/asset-uploader/src/db/create.ts b/packages/asset-uploader/src/db/create.ts index 74d6f23123..f8e8502348 100644 --- a/packages/asset-uploader/src/db/create.ts +++ b/packages/asset-uploader/src/db/create.ts @@ -1,10 +1,11 @@ -import { type FontMeta } from "@webstudio-is/fonts/server"; +import { type FontMeta } from "@webstudio-is/fonts"; import { prisma, type Location, type Project, } from "@webstudio-is/prisma-client"; -import { formatAsset, type ImageMeta } from "../utils/format-asset"; +import { type ImageMeta } from "../schema"; +import { formatAsset } from "../utils/format-asset"; type BaseOptions = { name: string; diff --git a/packages/asset-uploader/src/db/delete.ts b/packages/asset-uploader/src/db/delete.ts index 4b40fa60c9..b3172cd450 100644 --- a/packages/asset-uploader/src/db/delete.ts +++ b/packages/asset-uploader/src/db/delete.ts @@ -1,5 +1,5 @@ import { prisma } from "@webstudio-is/prisma-client"; -import { Asset } from "../types"; +import { Asset } from "../schema"; export const deleteFromDb = async (ids: Array) => { if (ids.length === 0) { diff --git a/packages/asset-uploader/src/db/load.ts b/packages/asset-uploader/src/db/load.ts index 9678560cc5..7d3ce500db 100644 --- a/packages/asset-uploader/src/db/load.ts +++ b/packages/asset-uploader/src/db/load.ts @@ -1,5 +1,5 @@ import { prisma, Project } from "@webstudio-is/prisma-client"; -import { Asset } from "../types"; +import { Asset } from "../schema"; import { formatAsset } from "../utils/format-asset"; export const loadByProject = async ( diff --git a/packages/asset-uploader/src/delete.ts b/packages/asset-uploader/src/delete.ts index 1083408e62..db49da8025 100644 --- a/packages/asset-uploader/src/delete.ts +++ b/packages/asset-uploader/src/delete.ts @@ -1,6 +1,6 @@ import { prisma } from "@webstudio-is/prisma-client"; import { deleteFromDb } from "./db"; -import { Asset } from "./types"; +import { Asset } from "./schema"; import { deleteFromFs } from "./targets/fs/delete"; import { deleteFromS3 } from "./targets/s3/delete"; import { formatAsset } from "./utils/format-asset"; diff --git a/packages/asset-uploader/src/index.ts b/packages/asset-uploader/src/index.ts index 5772be5362..699250178c 100644 --- a/packages/asset-uploader/src/index.ts +++ b/packages/asset-uploader/src/index.ts @@ -1,4 +1,5 @@ export * from "./types"; +export * from "./schema"; export * from "./constants"; export * from "./utils/filter-by-type"; export * from "./utils/to-bytes"; diff --git a/packages/asset-uploader/src/schema.ts b/packages/asset-uploader/src/schema.ts index a723602c71..65a49d5572 100644 --- a/packages/asset-uploader/src/schema.ts +++ b/packages/asset-uploader/src/schema.ts @@ -1,7 +1,14 @@ import { z } from "zod"; +import { FontFormat, FontMeta } from "@webstudio-is/fonts"; import { DEFAULT_UPLOAD_PATH, MAX_UPLOAD_SIZE } from "./constants"; import { toBytes } from "./utils/to-bytes"; +export const ImageMeta = z.object({ + width: z.number(), + height: z.number(), +}); +export type ImageMeta = z.infer; + const maxSize = z .string() // user inputs the max value in mb and we transform it to bytes @@ -23,3 +30,32 @@ export const FsEnv = z.object({ MAX_UPLOAD_SIZE: maxSize, FILE_UPLOAD_PATH: z.string().default(DEFAULT_UPLOAD_PATH), }); + +const Location = z.union([z.literal("FS"), z.literal("REMOTE")]); + +const BaseAsset = z.object({ + id: z.string(), + projectId: z.string(), + format: z.string(), + size: z.number(), + name: z.string(), + description: z.union([z.string(), z.null()]), + location: Location, + createdAt: z.string(), + path: z.string(), + status: z.literal("uploaded").optional(), +}); + +export const FontAsset = BaseAsset.omit({ format: true }).extend({ + format: FontFormat, + meta: FontMeta, +}); +export type FontAsset = z.infer; + +export const ImageAsset = BaseAsset.extend({ + meta: ImageMeta, +}); +export type ImageAsset = z.infer; + +export const Asset = z.union([FontAsset, ImageAsset]); +export type Asset = z.infer; diff --git a/packages/asset-uploader/src/targets/fs/upload.ts b/packages/asset-uploader/src/targets/fs/upload.ts index dfcd794e7d..d32edf543a 100644 --- a/packages/asset-uploader/src/targets/fs/upload.ts +++ b/packages/asset-uploader/src/targets/fs/upload.ts @@ -8,7 +8,7 @@ import { Location } from "@webstudio-is/prisma-client"; import { getAssetData } from "../../utils/get-asset-data"; import { createMany } from "../../db"; import { FILE_DIRECTORY } from "./file-path"; -import { Asset } from "../../types"; +import { Asset } from "../../schema"; import { getUniqueFilename } from "../../utils/get-unique-filename"; import { sanitizeS3Key } from "../../utils/sanitize-s3-key"; diff --git a/packages/asset-uploader/src/targets/s3/upload.ts b/packages/asset-uploader/src/targets/s3/upload.ts index 75815f2791..b6d9b3edf7 100644 --- a/packages/asset-uploader/src/targets/s3/upload.ts +++ b/packages/asset-uploader/src/targets/s3/upload.ts @@ -11,7 +11,7 @@ import { S3Env } from "../../schema"; import { toUint8Array } from "../../utils/to-uint8-array"; import { getAssetData } from "../../utils/get-asset-data"; import { createMany } from "../../db"; -import type { Asset } from "../../types"; +import type { Asset } from "../../schema"; import { getUniqueFilename } from "../../utils/get-unique-filename"; import { getS3Client } from "./client"; import { sanitizeS3Key } from "../../utils/sanitize-s3-key"; diff --git a/packages/asset-uploader/src/types.ts b/packages/asset-uploader/src/types.ts index 79d01291f9..389629be98 100644 --- a/packages/asset-uploader/src/types.ts +++ b/packages/asset-uploader/src/types.ts @@ -1,23 +1 @@ -import type { FontFormat } from "@webstudio-is/fonts"; -import type { FontMeta } from "@webstudio-is/fonts/server"; -import { Asset as DbAsset } from "@webstudio-is/prisma-client"; -import type { ImageMeta } from "./utils/format-asset"; - -type BaseAsset = Omit & { - path: string; - status?: "uploaded"; - createdAt: string; -}; - -export type FontAsset = Omit & { - format: FontFormat; - meta: FontMeta; -}; - -export type ImageAsset = BaseAsset & { - meta: ImageMeta; -}; - -export type Asset = FontAsset | ImageAsset; - export type AssetType = "image" | "font"; diff --git a/packages/asset-uploader/src/utils/format-asset.ts b/packages/asset-uploader/src/utils/format-asset.ts index b89a938ee5..bcb68b1b2b 100644 --- a/packages/asset-uploader/src/utils/format-asset.ts +++ b/packages/asset-uploader/src/utils/format-asset.ts @@ -1,15 +1,8 @@ -import { z } from "zod"; import type { Asset as DbAsset } from "@webstudio-is/prisma-client"; import { type FontFormat, FONT_FORMATS } from "@webstudio-is/fonts"; -import { FontMeta } from "@webstudio-is/fonts/server"; -import type { Asset } from "../types"; +import { FontMeta } from "@webstudio-is/fonts"; import { getAssetPath } from "./get-asset-path"; - -const ImageMeta = z.object({ - width: z.number(), - height: z.number(), -}); -export type ImageMeta = z.infer; +import { type Asset, ImageMeta } from "../schema"; export const formatAsset = (asset: DbAsset): Asset => { const base = { ...asset, path: getAssetPath(asset) }; diff --git a/packages/asset-uploader/src/utils/get-asset-data.ts b/packages/asset-uploader/src/utils/get-asset-data.ts index f329eeea75..9c4fc77b56 100644 --- a/packages/asset-uploader/src/utils/get-asset-data.ts +++ b/packages/asset-uploader/src/utils/get-asset-data.ts @@ -1,7 +1,8 @@ import { Location } from "@webstudio-is/prisma-client"; import sharp from "sharp"; -import { ImageMeta } from "./format-asset"; -import { type FontMeta, getFontData } from "@webstudio-is/fonts/server"; +import { type ImageMeta } from "../schema"; +import { type FontMeta } from "@webstudio-is/fonts"; +import { getFontData } from "@webstudio-is/fonts/server"; type BaseData = { name: string; diff --git a/packages/fonts/src/constants.ts b/packages/fonts/src/constants.ts index 03df1b85b3..595a6be30a 100644 --- a/packages/fonts/src/constants.ts +++ b/packages/fonts/src/constants.ts @@ -1,4 +1,4 @@ -import type { FontFormat } from "./types"; +import type { FontFormat } from "./schema"; export const SYSTEM_FONTS = new Map([ ["Arial", ["sans-serif"]], diff --git a/packages/fonts/src/font-data.ts b/packages/fonts/src/font-data.ts index ec786df716..0b7345a91a 100644 --- a/packages/fonts/src/font-data.ts +++ b/packages/fonts/src/font-data.ts @@ -1,4 +1,4 @@ -import type { FontFormat } from "./types"; +import type { FontFormat } from "./schema"; import { create as createFontKit } from "fontkit"; import { FontWeight, fontWeights } from "./font-weights"; diff --git a/packages/fonts/src/get-font-faces.ts b/packages/fonts/src/get-font-faces.ts index 15311910fc..9948239ce3 100644 --- a/packages/fonts/src/get-font-faces.ts +++ b/packages/fonts/src/get-font-faces.ts @@ -1,6 +1,5 @@ import { FONT_FORMATS } from "./constants"; -import type { FontMeta } from "./schema"; -import type { FontFormat } from "./types"; +import type { FontMeta, FontFormat } from "./schema"; export type PartialFontAsset = { format: FontFormat; diff --git a/packages/fonts/src/index.server.ts b/packages/fonts/src/index.server.ts index 348b128747..7393f18191 100644 --- a/packages/fonts/src/index.server.ts +++ b/packages/fonts/src/index.server.ts @@ -1,2 +1 @@ export { getFontData } from "./font-data"; -export * from "./schema"; diff --git a/packages/fonts/src/index.ts b/packages/fonts/src/index.ts index 0686031b85..6ed0d0780a 100644 --- a/packages/fonts/src/index.ts +++ b/packages/fonts/src/index.ts @@ -1,4 +1,4 @@ export * from "./constants"; export * from "./get-font-faces"; -export * from "./types"; +export * from "./schema"; export * from "./font-weights"; diff --git a/packages/fonts/src/schema.ts b/packages/fonts/src/schema.ts index a5e7e6ec90..00cb4e98c6 100644 --- a/packages/fonts/src/schema.ts +++ b/packages/fonts/src/schema.ts @@ -1,6 +1,14 @@ import { z } from "zod"; import { styles } from "./font-data"; +export const FontFormat = z.union([ + z.literal("ttf"), + z.literal("woff"), + z.literal("woff2"), + z.literal("otf"), +]); +export type FontFormat = z.infer; + export const FontMeta = z.object({ family: z.string(), style: z.enum(styles), diff --git a/packages/fonts/src/types.ts b/packages/fonts/src/types.ts deleted file mode 100644 index f7da9f208d..0000000000 --- a/packages/fonts/src/types.ts +++ /dev/null @@ -1 +0,0 @@ -export type FontFormat = "ttf" | "woff" | "woff2" | "otf"; diff --git a/packages/project/src/db/props.ts b/packages/project/src/db/props.ts index 1a68a1c55e..6359c10bfa 100644 --- a/packages/project/src/db/props.ts +++ b/packages/project/src/db/props.ts @@ -4,7 +4,7 @@ import { type AllUserProps, type UserProp, // @todo move zod schema into project package - UserUserProps, + UserProps, UserDbProps, } from "@webstudio-is/react-sdk"; import { applyPatches, type Patch } from "immer"; @@ -124,7 +124,10 @@ export const patch = async ( await Promise.all( Object.values(nextProps).map(({ id, instanceId, treeId, props }) => { - const propsDb = UserUserProps.parse(props); + const propsDb: UserDbProps = UserProps.parse(props).map((prop) => { + const { asset, ...rest } = prop; + return { ...rest, assetId: asset?.id }; + }); const propsString = JSON.stringify(propsDb); diff --git a/packages/react-sdk/src/user-props/schema.ts b/packages/react-sdk/src/user-props/schema.ts index a76f3def2e..f51c38cb49 100644 --- a/packages/react-sdk/src/user-props/schema.ts +++ b/packages/react-sdk/src/user-props/schema.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import type { Asset } from "@webstudio-is/asset-uploader"; +import { Asset } from "@webstudio-is/asset-uploader"; const UserPropDb = z.object({ id: z.string(), @@ -8,36 +8,19 @@ const UserPropDb = z.object({ assetId: z.optional(z.string()), required: z.optional(z.boolean()), }); +export type UserPropDb = z.infer; export const UserDbProps = z.array(UserPropDb); +export type UserDbProps = z.infer; -export const UserUserProp = z - .object({ - id: z.string(), - prop: z.string(), - value: z.union([z.string(), z.boolean()]), - // We are not interested in Props data does any other props on asset exists - asset: z.optional(z.object({ id: z.string() })), - required: z.optional(z.boolean()), - }) - .transform(({ id, prop, value, asset, required }) => { - // Check with infer that we are transforming to UserPropDb - const res: z.infer = { - id, - prop, - value, - assetId: asset?.id, - required, - }; - return res; - }); +export const UserProp = z.object({ + id: z.string(), + prop: z.string(), + value: z.union([z.string(), z.boolean()]), + asset: Asset.optional(), + required: z.optional(z.boolean()), +}); +export type UserProp = z.infer; -export const UserUserProps = z.array(UserUserProp); - -export type UserProp = { - id: string; - prop: string; - value: string | boolean; - asset?: Asset; - required?: boolean | undefined; -}; +export const UserProps = z.array(UserProp); +export type UserProps = z.infer; diff --git a/yarn.lock b/yarn.lock index 81784cdbb5..3e1dccef61 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18600,11 +18600,6 @@ zod-form-data@^1.2.1: resolved "https://registry.yarnpkg.com/zod-form-data/-/zod-form-data-1.2.1.tgz#6da07d7964765b464e959a75f0d797690ee477f7" integrity sha512-L4zocmlzikYB9A2uVVj3OUKX/t3spOZTzQLOhoCXECn9w+eN3bw0D/KYGX4J72iLdCIwDP6YJyYwVXkx3WTu7A== -zod@^3.17.3: - version "3.17.3" - resolved "https://registry.yarnpkg.com/zod/-/zod-3.17.3.tgz#86abbc670ff0063a4588d85a4dcc917d6e4af2ba" - integrity sha512-4oKP5zvG6GGbMlqBkI5FESOAweldEhSOZ6LI6cG+JzUT7ofj1ZOC0PJudpQOpT1iqOFpYYtX5Pw0+o403y4bcg== - zod@^3.19.1: version "3.19.1" resolved "https://registry.yarnpkg.com/zod/-/zod-3.19.1.tgz#112f074a97b50bfc4772d4ad1576814bd8ac4473"