mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-11-06 20:13:35 +09:00
fix: imports/exports
This commit is contained in:
1 parent
f35a3dbcd4
commit
6bd52dc72f
29 files changed
+513
-1740
No files matched your search
@@ -1,30 +1,30 @@
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import store from "immerhin";
|
||||
import * as db from "@webstudio-is/project";
|
||||
import type { Project } from "@webstudio-is/project";
|
||||
import {
|
||||
type OnChangeChildren,
|
||||
createElementsTree,
|
||||
type Data,
|
||||
globalStyles,
|
||||
type OnChangeChildren,
|
||||
type Tree,
|
||||
useAllUserProps,
|
||||
globalStyles,
|
||||
createElementsTree,
|
||||
} from "@webstudio-is/react-sdk";
|
||||
import { useSubscribe } from "~/shared/pubsub";
|
||||
import { useShortcuts } from "./shared/use-shortcuts";
|
||||
import {
|
||||
usePopulateRootInstance,
|
||||
useInsertInstance,
|
||||
useDeleteInstance,
|
||||
useReparentInstance,
|
||||
usePublishSelectedInstanceData,
|
||||
usePublishRootInstance,
|
||||
useUpdateSelectedInstance,
|
||||
usePublishSelectedInstanceDataRect,
|
||||
usePublishHoveredInstanceRect,
|
||||
useInsertInstance,
|
||||
usePopulateRootInstance,
|
||||
usePublishHoveredInstanceData,
|
||||
usePublishHoveredInstanceRect,
|
||||
usePublishRootInstance,
|
||||
usePublishSelectedInstanceData,
|
||||
usePublishSelectedInstanceDataRect,
|
||||
usePublishTextEditingInstanceId,
|
||||
useReparentInstance,
|
||||
useSetHoveredInstance,
|
||||
useUnselectInstance,
|
||||
usePublishTextEditingInstanceId,
|
||||
useUpdateSelectedInstance,
|
||||
} from "./shared/instance";
|
||||
import { useUpdateStyle } from "./shared/style";
|
||||
import { useTrackSelectedElement } from "./shared/use-track-selected-element";
|
||||
@@ -83,7 +83,7 @@ const useSubscribePreviewMode = () => {
|
||||
|
||||
type DesignModeProps = {
|
||||
treeId: Tree["id"];
|
||||
project: db.project.Project;
|
||||
project: Project;
|
||||
};
|
||||
|
||||
const DesignMode = ({ treeId, project }: DesignModeProps) => {
|
||||
@@ -112,7 +112,7 @@ const DesignMode = ({ treeId, project }: DesignModeProps) => {
|
||||
};
|
||||
|
||||
type CanvasProps = {
|
||||
data: Data & { project: db.project.Project };
|
||||
data: Data & { project: Project };
|
||||
};
|
||||
|
||||
export const Canvas = ({ data }: CanvasProps): JSX.Element | null => {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import useInterval from "react-use/lib/useInterval";
|
||||
import { sync } from "immerhin";
|
||||
import { enqueue } from "./queue";
|
||||
import { project } from "@webstudio-is/project";
|
||||
import type { Project } from "@webstudio-is/project";
|
||||
|
||||
export const useSync = ({ project }: { project: project.Project }) => {
|
||||
export const useSync = ({ project }: { project: Project }) => {
|
||||
useInterval(() => {
|
||||
const entries = sync();
|
||||
if (entries.length === 0) return;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useCallback } from "react";
|
||||
import { useSubscribe, usePublish, type Publish } from "~/shared/pubsub";
|
||||
import * as db from "@webstudio-is/project";
|
||||
import { type Publish, usePublish, useSubscribe } from "~/shared/pubsub";
|
||||
import type { Project } from "@webstudio-is/project";
|
||||
import type { Config } from "~/config";
|
||||
import { Box, Flex, Grid, type CSS } from "@webstudio-is/design-system";
|
||||
import { Box, type CSS, Flex, Grid } from "@webstudio-is/design-system";
|
||||
import interStyles from "~/shared/font-faces/inter.css";
|
||||
import { SidebarLeft } from "./features/sidebar-left";
|
||||
import { Inspector } from "./features/inspector";
|
||||
@@ -16,19 +16,19 @@ import designerStyles from "./designer.css";
|
||||
import { Footer } from "./features/footer";
|
||||
import { TreePrevew } from "./features/tree-preview";
|
||||
import {
|
||||
useUpdateCanvasWidth,
|
||||
useSubscribeBreakpoints,
|
||||
useUpdateCanvasWidth,
|
||||
} from "./features/breakpoints";
|
||||
import {
|
||||
CanvasIframe,
|
||||
useReadCanvasRect,
|
||||
Workspace,
|
||||
CanvasIframe,
|
||||
} from "./features/workspace";
|
||||
import { usePublishShortcuts } from "./shared/shortcuts";
|
||||
import {
|
||||
useDragAndDropState,
|
||||
useIsPreviewMode,
|
||||
useRootInstance,
|
||||
useDragAndDropState,
|
||||
} from "~/shared/nano-states";
|
||||
import { useClientSettings } from "./shared/client-settings";
|
||||
import { Navigator } from "./features/sidebar-left";
|
||||
@@ -211,7 +211,7 @@ const NavigatorPanel = ({ publish, isPreviewMode }: NavigatorPanelProps) => {
|
||||
|
||||
type DesignerProps = {
|
||||
config: Config;
|
||||
project: db.project.Project;
|
||||
project: Project;
|
||||
};
|
||||
|
||||
export const Designer = ({ config, project }: DesignerProps) => {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { type Breakpoint } from "@webstudio-is/react-sdk";
|
||||
import { useSubscribe, type Publish } from "~/shared/pubsub";
|
||||
import { type Publish, useSubscribe } from "~/shared/pubsub";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuPortal,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuArrow,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuCheckboxItem,
|
||||
__DEPRECATED__Text,
|
||||
DropdownMenu,
|
||||
DropdownMenuArrow,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuPortal,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
Flex,
|
||||
} from "@webstudio-is/design-system";
|
||||
import { useSelectedBreakpoint } from "../../shared/nano-states";
|
||||
@@ -20,12 +20,12 @@ import { ZoomSetting } from "./zoom-setting";
|
||||
import { TriggerButton } from "./trigger-button";
|
||||
import { WidthSetting } from "./width-setting";
|
||||
import {
|
||||
useSubscribeZoomFromShortcut,
|
||||
useSubscribeSelectBreakpointFromShortcut,
|
||||
useSubscribeZoomFromShortcut,
|
||||
} from "./use-subscribe-shortcuts";
|
||||
import { ConfirmationDialog } from "./confirmation-dialog";
|
||||
import { useBreakpoints } from "~/shared/nano-states";
|
||||
import { sort } from "@webstudio-is/project";
|
||||
import { utils } from "@webstudio-is/project";
|
||||
|
||||
declare module "~/shared/pubsub" {
|
||||
export interface PubsubMap {
|
||||
@@ -105,7 +105,7 @@ export const Breakpoints = ({ publish }: BreakpointsProps) => {
|
||||
nextBreakpoints.splice(index, 1);
|
||||
setBreakpoints(nextBreakpoints);
|
||||
if (breakpointToDelete === selectedBreakpoint) {
|
||||
setSelectedBreakpoint(sort(nextBreakpoints)[0]);
|
||||
setSelectedBreakpoint(utils.breakpoints.sort(nextBreakpoints)[0]);
|
||||
}
|
||||
publish({
|
||||
type: "breakpointDelete",
|
||||
@@ -163,7 +163,7 @@ export const Breakpoints = ({ publish }: BreakpointsProps) => {
|
||||
)}
|
||||
{view === "selector" && (
|
||||
<>
|
||||
{sort(breakpoints).map((breakpoint) => {
|
||||
{utils.breakpoints.sort(breakpoints).map((breakpoint) => {
|
||||
return (
|
||||
<DropdownMenuCheckboxItem
|
||||
checked={breakpoint === selectedBreakpoint}
|
||||
|
||||
@@ -2,8 +2,7 @@ import { useEffect } from "react";
|
||||
import { useSubscribe } from "~/shared/pubsub";
|
||||
import { useSelectedBreakpoint } from "~/designer/shared/nano-states";
|
||||
import { useBreakpoints } from "~/shared/nano-states";
|
||||
import { sort } from "@webstudio-is/project";
|
||||
|
||||
import { utils } from "@webstudio-is/project";
|
||||
export const useSubscribeBreakpoints = () => {
|
||||
const [breakpoints, setBreakpoints] = useBreakpoints();
|
||||
const [selectedBreakpoint, setSelectedBreakpoint] = useSelectedBreakpoint();
|
||||
@@ -13,7 +12,7 @@ export const useSubscribeBreakpoints = () => {
|
||||
useEffect(() => {
|
||||
// Set the initial selected breakpoint
|
||||
if (selectedBreakpoint === undefined && breakpoints.length !== 0) {
|
||||
setSelectedBreakpoint(sort(breakpoints)[0]);
|
||||
setSelectedBreakpoint(utils.breakpoints.sort(breakpoints)[0]);
|
||||
}
|
||||
|
||||
// Breakpoints must have changed, lets update the selected breakpoint
|
||||
|
||||
@@ -2,13 +2,13 @@ import { useSubscribe } from "~/shared/pubsub";
|
||||
import { useBreakpoints } from "~/shared/nano-states";
|
||||
import { useZoom, useSelectedBreakpoint } from "../../shared/nano-states";
|
||||
import { minZoom } from "./zoom-setting";
|
||||
import { sort } from "@webstudio-is/project";
|
||||
import { utils } from "@webstudio-is/project";
|
||||
|
||||
export const useSubscribeSelectBreakpointFromShortcut = () => {
|
||||
const [breakpoints] = useBreakpoints();
|
||||
const [, setSelectedBreakpoint] = useSelectedBreakpoint();
|
||||
useSubscribe("selectBreakpointFromShortcut", (index) => {
|
||||
const breakpoint = sort(breakpoints)[index - 1];
|
||||
const breakpoint = utils.breakpoints.sort(breakpoints)[index - 1];
|
||||
if (breakpoint) setSelectedBreakpoint(breakpoint);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -3,9 +3,9 @@ import {
|
||||
useCanvasWidth,
|
||||
useSelectedBreakpoint,
|
||||
} from "~/designer/shared/nano-states";
|
||||
import { Slider, __DEPRECATED__Text, Flex } from "@webstudio-is/design-system";
|
||||
import { __DEPRECATED__Text, Flex, Slider } from "@webstudio-is/design-system";
|
||||
import { useBreakpoints, useIsPreviewMode } from "~/shared/nano-states";
|
||||
import { sort } from "@webstudio-is/project";
|
||||
import { utils } from "@webstudio-is/project";
|
||||
|
||||
// Doesn't make sense to allow resizing the canvas lower/higher than this.
|
||||
export const minWidth = 50;
|
||||
@@ -18,7 +18,10 @@ const useNextBreakpoint = () => {
|
||||
const [selectedBreakpoint] = useSelectedBreakpoint();
|
||||
const [breakpoints] = useBreakpoints();
|
||||
|
||||
const sortedBreakpoints = useMemo(() => sort(breakpoints), [breakpoints]);
|
||||
const sortedBreakpoints = useMemo(
|
||||
() => utils.breakpoints.sort(breakpoints),
|
||||
[breakpoints]
|
||||
);
|
||||
|
||||
return useMemo(() => {
|
||||
if (selectedBreakpoint === undefined) return;
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useFetcher } from "@remix-run/react";
|
||||
import { RocketIcon, ExternalLinkIcon } from "@webstudio-is/icons";
|
||||
import { ExternalLinkIcon, RocketIcon } from "@webstudio-is/icons";
|
||||
import {
|
||||
__DEPRECATED__Text,
|
||||
Button,
|
||||
Flex,
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
PopoverPortal,
|
||||
TextField,
|
||||
Text,
|
||||
__DEPRECATED__Text,
|
||||
Label,
|
||||
Link,
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverPortal,
|
||||
PopoverTrigger,
|
||||
Text,
|
||||
TextField,
|
||||
useId,
|
||||
} from "@webstudio-is/design-system";
|
||||
import { useIsPublishDialogOpen } from "../../shared/nano-states";
|
||||
import env from "~/shared/env";
|
||||
import * as db from "@webstudio-is/project";
|
||||
type PublishButtonProps = { project: db.project.Project };
|
||||
import type { Project } from "@webstudio-is/project";
|
||||
type PublishButtonProps = { project: Project };
|
||||
|
||||
const getHost = () => {
|
||||
if (env.PUBLISHER_ENDPOINT && env.PUBLISHER_HOST) {
|
||||
@@ -60,7 +60,7 @@ const Content = ({ project }: PublishButtonProps) => {
|
||||
gap: "$0",
|
||||
}}
|
||||
>
|
||||
<Text truncate>{`${domain}.${getHost()}`} </Text>
|
||||
<Text truncate>{`${domain}.${getHost()}`}</Text>
|
||||
<ExternalLinkIcon />
|
||||
</Link>
|
||||
)}
|
||||
|
||||
@@ -4,15 +4,15 @@ import {
|
||||
IconButton,
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
PopoverPortal,
|
||||
PopoverTrigger,
|
||||
TextField,
|
||||
} from "@webstudio-is/design-system";
|
||||
import { Share1Icon } from "@webstudio-is/icons";
|
||||
import * as db from "@webstudio-is/project";
|
||||
import type { Project } from "@webstudio-is/project";
|
||||
import { useIsShareDialogOpen } from "../../shared/nano-states";
|
||||
|
||||
type ShareButtonProps = { path: string; project: db.project.Project };
|
||||
type ShareButtonProps = { path: string; project: Project };
|
||||
|
||||
const Content = ({ path, project }: ShareButtonProps) => {
|
||||
if (typeof location === "undefined") {
|
||||
|
||||
@@ -7,12 +7,12 @@ import { PublishButton } from "./publish";
|
||||
import { SyncStatus } from "./sync-status";
|
||||
import { Menu } from "./menu";
|
||||
import { Breakpoints } from "../breakpoints";
|
||||
import * as db from "@webstudio-is/project";
|
||||
import type { Project } from "@webstudio-is/project";
|
||||
|
||||
type TopbarProps = {
|
||||
config: Config;
|
||||
css: CSS;
|
||||
project: db.project.Project;
|
||||
project: Project;
|
||||
publish: Publish;
|
||||
};
|
||||
export const Topbar = ({ config, css, project, publish }: TopbarProps) => {
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { useLoaderData } from "@remix-run/react";
|
||||
import {
|
||||
redirect,
|
||||
type LoaderFunction,
|
||||
type ActionFunction,
|
||||
json,
|
||||
type LoaderFunction,
|
||||
redirect,
|
||||
} from "@remix-run/node";
|
||||
import { Dashboard, links } from "~/dashboard";
|
||||
import * as db from "@webstudio-is/project";
|
||||
import { db } from "@webstudio-is/project";
|
||||
import config, { type Config } from "~/config";
|
||||
import { ensureUserCookie } from "~/shared/session";
|
||||
import { authenticator } from "~/services/auth.server";
|
||||
import { zfd } from "zod-form-data";
|
||||
import { User, type Project } from "@webstudio-is/prisma-client";
|
||||
import { type Project, User } from "@webstudio-is/prisma-client";
|
||||
|
||||
export { links };
|
||||
const schema = zfd.formData({
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { useLoaderData } from "@remix-run/react";
|
||||
import { LoaderFunction } from "@remix-run/node";
|
||||
import { Designer, links } from "~/designer";
|
||||
import * as db from "@webstudio-is/project";
|
||||
import { db } from "@webstudio-is/project";
|
||||
import type { Project } from "@webstudio-is/project";
|
||||
import config from "~/config";
|
||||
import env from "~/env.server";
|
||||
import { ErrorMessage } from "~/shared/error";
|
||||
import { action, useAction } from "./_assets";
|
||||
|
||||
export { links, action };
|
||||
export { action, links };
|
||||
|
||||
export const loader: LoaderFunction = async ({ params }) => {
|
||||
if (params.id === undefined) throw new Error("Project id undefined");
|
||||
@@ -20,7 +21,7 @@ export const loader: LoaderFunction = async ({ params }) => {
|
||||
|
||||
type Data = {
|
||||
config: typeof config;
|
||||
project: db.project.Project;
|
||||
project: Project;
|
||||
};
|
||||
|
||||
type Error = {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { redirect, type LoaderFunction } from "@remix-run/node";
|
||||
import { type LoaderFunction, redirect } from "@remix-run/node";
|
||||
import { useLoaderData } from "@remix-run/react";
|
||||
import { Root, type Data } from "@webstudio-is/react-sdk";
|
||||
import { type Data, Root } from "@webstudio-is/react-sdk";
|
||||
import config from "~/config";
|
||||
import * as db from "@webstudio-is/project";
|
||||
import { db } from "@webstudio-is/project";
|
||||
import { Canvas as CanvasDocument } from "~/shared/documents/canvas";
|
||||
import env, { Env } from "~/env.server";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { LoaderFunction } from "@remix-run/node";
|
||||
import { Breakpoint } from "@webstudio-is/react-sdk";
|
||||
import * as projectdomain from "@webstudio-is/project";
|
||||
import { db } from "@webstudio-is/project";
|
||||
|
||||
export type ErrorData = {
|
||||
errors: string;
|
||||
@@ -10,7 +10,7 @@ export const loader: LoaderFunction = async ({
|
||||
params,
|
||||
}): Promise<Array<Breakpoint> | ErrorData> => {
|
||||
try {
|
||||
const project = await projectdomain.project.loadById(params.projectId);
|
||||
const project = await db.project.loadById(params.projectId);
|
||||
if (project === null) {
|
||||
throw new Error(`Project ${params.projectId} not found`);
|
||||
}
|
||||
@@ -19,7 +19,7 @@ export const loader: LoaderFunction = async ({
|
||||
`Project ${params.projectId} needs to be published first`
|
||||
);
|
||||
}
|
||||
const data = await projectdomain.breakpoints.load(project.prodTreeId);
|
||||
const data = await db.breakpoints.load(project.prodTreeId);
|
||||
if (data === null) {
|
||||
throw new Error(
|
||||
`Breakpoints not found for project ${params.projectId} and tree ID ${project.prodTreeId}`
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { LoaderFunction } from "@remix-run/node";
|
||||
import type { Breakpoint } from "@webstudio-is/react-sdk";
|
||||
import * as projectdomain from "@webstudio-is/project";
|
||||
import { db } from "@webstudio-is/project";
|
||||
|
||||
export type ErrorData = {
|
||||
errors: string;
|
||||
@@ -10,7 +10,7 @@ export const loader: LoaderFunction = async ({
|
||||
params,
|
||||
}): Promise<Array<Breakpoint> | ErrorData> => {
|
||||
try {
|
||||
const project = await projectdomain.project.loadById(params.projectId);
|
||||
const project = await db.project.loadById(params.projectId);
|
||||
if (project === null) {
|
||||
throw new Error(`Project ${params.projectId} not found`);
|
||||
}
|
||||
@@ -19,7 +19,7 @@ export const loader: LoaderFunction = async ({
|
||||
`Project ${params.projectId} needs to be published first`
|
||||
);
|
||||
}
|
||||
const data = await projectdomain.breakpoints.load(project.prodTreeId);
|
||||
const data = await db.breakpoints.load(project.prodTreeId);
|
||||
if (data === null) {
|
||||
throw new Error(
|
||||
`Breakpoints not found for project ${params.projectId} and tree ID ${project.prodTreeId}`
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { type ActionFunction } from "@remix-run/node";
|
||||
import * as projectdomain from "@webstudio-is/project";
|
||||
import { db } from "@webstudio-is/project";
|
||||
import { type SyncItem } from "immerhin";
|
||||
import { type Tree } from "@webstudio-is/react-sdk";
|
||||
import { type Project } from "@webstudio-is/prisma-client";
|
||||
|
||||
const updaters = {
|
||||
root: projectdomain.tree.patchRoot,
|
||||
props: projectdomain.props.patch,
|
||||
breakpoints: projectdomain.breakpoints.patch,
|
||||
root: db.tree.patchRoot,
|
||||
props: db.props.patch,
|
||||
breakpoints: db.breakpoints.patch,
|
||||
};
|
||||
|
||||
type UpdaterKey = keyof typeof updaters;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { type LoaderFunction, redirect } from "@remix-run/node";
|
||||
import type { User } from "@webstudio-is/prisma-client";
|
||||
import * as projectdomain from "@webstudio-is/project";
|
||||
import { db as database } from "@webstudio-is/project";
|
||||
import type { Project } from "@webstudio-is/project";
|
||||
import * as db from "~/shared/db";
|
||||
import { ensureUserCookie } from "~/shared/session";
|
||||
import config from "~/config";
|
||||
@@ -12,11 +13,11 @@ const ensureProject = async ({
|
||||
}: {
|
||||
userId: User["id"];
|
||||
domain: string;
|
||||
}): Promise<projectdomain.project.Project> => {
|
||||
const projects = await projectdomain.project.loadManyByUserId(userId);
|
||||
}): Promise<Project> => {
|
||||
const projects = await database.project.loadManyByUserId(userId);
|
||||
if (projects.length !== 0) return projects[0];
|
||||
|
||||
return await projectdomain.project.clone(domain, userId);
|
||||
return await database.project.clone(domain, userId);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { type LoaderFunction } from "@remix-run/node";
|
||||
import { type InstanceProps } from "@webstudio-is/react-sdk";
|
||||
import * as projectdomain from "@webstudio-is/project";
|
||||
import { db } from "@webstudio-is/project";
|
||||
type ErrorData = {
|
||||
errors: string;
|
||||
};
|
||||
@@ -9,8 +9,8 @@ export const loader: LoaderFunction = async ({
|
||||
params,
|
||||
}): Promise<Array<InstanceProps> | ErrorData> => {
|
||||
try {
|
||||
const project = await projectdomain.project.loadById(params.projectId);
|
||||
return await projectdomain.props.loadByProject(project, "production");
|
||||
const project = await db.project.loadById(params.projectId);
|
||||
return await db.props.loadByProject(project, "production");
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
return {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { type LoaderFunction } from "@remix-run/node";
|
||||
import type { Tree } from "@webstudio-is/react-sdk";
|
||||
import * as projectdomain from "@webstudio-is/project";
|
||||
import { db } from "@webstudio-is/project";
|
||||
export type ErrorData = {
|
||||
errors: string;
|
||||
};
|
||||
@@ -9,8 +9,8 @@ export const loader: LoaderFunction = async ({
|
||||
params,
|
||||
}): Promise<Tree | null | ErrorData> => {
|
||||
try {
|
||||
const project = await projectdomain.project.loadById(params.projectId);
|
||||
return await projectdomain.tree.loadByProject(project, "production");
|
||||
const project = await db.project.loadById(params.projectId);
|
||||
return await db.tree.loadByProject(project, "production");
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
return {
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import { type Data } from "@webstudio-is/react-sdk";
|
||||
import * as projectdomain from "@webstudio-is/project";
|
||||
|
||||
export type CanvasData = Data & { project: projectdomain.project.Project };
|
||||
import { db } from "@webstudio-is/project";
|
||||
import type { Project } from "@webstudio-is/project";
|
||||
export type CanvasData = Data & { project: Project };
|
||||
|
||||
export type ErrorData = {
|
||||
errors: string;
|
||||
};
|
||||
|
||||
const loadData = async (projectId: projectdomain.project.Project["id"]) => {
|
||||
const project = await projectdomain.project.loadById(projectId);
|
||||
const loadData = async (projectId: Project["id"]) => {
|
||||
const project = await db.project.loadById(projectId);
|
||||
|
||||
if (project === null) throw new Error(`Project "${projectId}" not found`);
|
||||
|
||||
const [tree, props, breakpoints] = await Promise.all([
|
||||
projectdomain.tree.loadByProject(project, "development"),
|
||||
projectdomain.props.loadByProject(project, "development"),
|
||||
projectdomain.breakpoints.load(project.devTreeId),
|
||||
db.tree.loadByProject(project, "development"),
|
||||
db.props.loadByProject(project, "development"),
|
||||
db.breakpoints.load(project.devTreeId),
|
||||
]);
|
||||
|
||||
if (tree === null) {
|
||||
@@ -39,7 +39,7 @@ const loadData = async (projectId: projectdomain.project.Project["id"]) => {
|
||||
export const loadCanvasData = async ({
|
||||
projectId,
|
||||
}: {
|
||||
projectId: projectdomain.project.Project["id"];
|
||||
projectId: Project["id"];
|
||||
}): Promise<CanvasData | ErrorData> => {
|
||||
return await loadData(projectId);
|
||||
};
|
||||
@@ -49,7 +49,7 @@ export type PreviewData = Data;
|
||||
export const loadPreviewData = async ({
|
||||
projectId,
|
||||
}: {
|
||||
projectId: projectdomain.project.Project["id"];
|
||||
projectId: Project["id"];
|
||||
}): Promise<PreviewData | ErrorData> => {
|
||||
const { tree, props, breakpoints } = await loadData(projectId);
|
||||
return { tree, props, breakpoints };
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as projectdomain from "@webstudio-is/project";
|
||||
import { db } from "@webstudio-is/project";
|
||||
|
||||
/**
|
||||
* Conceptually publishing is cloning all data that affects user site
|
||||
@@ -18,17 +18,17 @@ export const publish = async ({
|
||||
throw new Error("Domain required");
|
||||
}
|
||||
|
||||
const project = await projectdomain.project.loadById(projectId);
|
||||
const project = await db.project.loadById(projectId);
|
||||
if (project === null) {
|
||||
throw new Error(`Project "${projectId}" not found`);
|
||||
}
|
||||
|
||||
const tree = await projectdomain.tree.clone(project.devTreeId);
|
||||
await projectdomain.props.clone({
|
||||
const tree = await db.tree.clone(project.devTreeId);
|
||||
await db.props.clone({
|
||||
previousTreeId: project.devTreeId,
|
||||
nextTreeId: tree.id,
|
||||
});
|
||||
await projectdomain.breakpoints.clone({
|
||||
await db.breakpoints.clone({
|
||||
previousTreeId: project.devTreeId,
|
||||
nextTreeId: tree.id,
|
||||
});
|
||||
@@ -36,7 +36,7 @@ export const publish = async ({
|
||||
if (project.prodTreeId) {
|
||||
prodTreeIdHistory.push(project.prodTreeId);
|
||||
}
|
||||
const updatedProject = await projectdomain.project.update({
|
||||
const updatedProject = await db.project.update({
|
||||
id: projectId,
|
||||
domain,
|
||||
prodTreeId: tree.id,
|
||||
|
||||
@@ -28,4 +28,3 @@ export const loadProject = async ({
|
||||
breakpoints,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
import {
|
||||
initialBreakpoints,
|
||||
type Tree,
|
||||
type Breakpoint,
|
||||
BreakpointsSchema,
|
||||
} from "@webstudio-is/react-sdk";
|
||||
import ObjectId from "bson-objectid";
|
||||
import { applyPatches, type Patch } from "immer";
|
||||
import { prisma, Project } from "@webstudio-is/prisma-client";
|
||||
|
||||
export const load = async (treeId?: Tree["id"]) => {
|
||||
if (typeof treeId !== "string") {
|
||||
throw new Error("Tree ID required");
|
||||
}
|
||||
|
||||
const breakpoint = await prisma.breakpoints.findUnique({
|
||||
where: { treeId },
|
||||
});
|
||||
|
||||
if (breakpoint === null) {
|
||||
throw new Error("Breakpoint not found");
|
||||
}
|
||||
const values: Array<Breakpoint> = JSON.parse(breakpoint.values);
|
||||
BreakpointsSchema.parse(values);
|
||||
return {
|
||||
...breakpoint,
|
||||
values,
|
||||
};
|
||||
};
|
||||
|
||||
export const getBreakpointsWithId = () =>
|
||||
initialBreakpoints.map((breakpoint) => ({
|
||||
...breakpoint,
|
||||
id: ObjectId().toString(),
|
||||
}));
|
||||
|
||||
export const create = async (
|
||||
treeId: Project["id"],
|
||||
values: Array<Breakpoint>
|
||||
) => {
|
||||
BreakpointsSchema.parse(values);
|
||||
|
||||
const data = {
|
||||
treeId,
|
||||
values: JSON.stringify(values),
|
||||
};
|
||||
await prisma.breakpoints.create({ data });
|
||||
return {
|
||||
...data,
|
||||
breakpoints: values,
|
||||
};
|
||||
};
|
||||
|
||||
export const clone = async ({
|
||||
previousTreeId,
|
||||
nextTreeId,
|
||||
}: {
|
||||
previousTreeId: Tree["id"];
|
||||
nextTreeId: Tree["id"];
|
||||
}) => {
|
||||
const breakpoints = await load(previousTreeId);
|
||||
if (breakpoints === null) {
|
||||
throw new Error(`Didn't find breakpoints with tree id "${previousTreeId}"`);
|
||||
}
|
||||
await create(nextTreeId, breakpoints.values);
|
||||
};
|
||||
|
||||
export const patch = async (
|
||||
{ treeId }: { treeId: Tree["id"]; projectId: Project["id"] },
|
||||
patches: Array<Patch>
|
||||
) => {
|
||||
const breakpoints = await load(treeId);
|
||||
if (breakpoints === null) return;
|
||||
const nextValues = applyPatches(breakpoints.values, patches);
|
||||
|
||||
BreakpointsSchema.parse(nextValues);
|
||||
|
||||
await prisma.breakpoints.update({
|
||||
where: { treeId },
|
||||
data: { values: JSON.stringify(nextValues) },
|
||||
});
|
||||
};
|
||||
@@ -1,9 +1,8 @@
|
||||
import * as project from "./project";
|
||||
import * as breakpoints from "./breakpoints";
|
||||
import * as tree from "./tree";
|
||||
import * as props from "./props";
|
||||
import * as dbFunctions from "./db";
|
||||
import * as breakpointsUtils from "./shared/breakpoints";
|
||||
import * as treeUtils from "./shared/tree-utils";
|
||||
|
||||
export const db = {project, breakpoints, tree, props}
|
||||
export const utils = { breakpoints: breakpointsUtils, tree: treeUtils };
|
||||
export const db = dbFunctions;
|
||||
|
||||
export breakpoints from "./shared/breakpoints";
|
||||
export tree from "./shared/tree-utils";
|
||||
export * from "./db/types";
|
||||
@@ -1,210 +0,0 @@
|
||||
import slugify from "slugify";
|
||||
import { nanoid } from "nanoid";
|
||||
import { z } from "zod";
|
||||
import {
|
||||
User,
|
||||
prisma,
|
||||
Prisma,
|
||||
Project as BaseProject,
|
||||
} from "@webstudio-is/prisma-client";
|
||||
import { formatAsset } from "@webstudio-is/asset-uploader";
|
||||
import type { Asset } from "@webstudio-is/asset-uploader";
|
||||
import * as db from "./index";
|
||||
const TreeHistorySchema = z.array(z.string());
|
||||
|
||||
export type Project = Omit<BaseProject, "prodTreeIdHistory" | "assets"> & {
|
||||
prodTreeIdHistory: z.infer<typeof TreeHistorySchema>;
|
||||
assets?: Array<Asset>;
|
||||
};
|
||||
|
||||
const parseProject = (project: BaseProject): Project => {
|
||||
const prodTreeIdHistory = JSON.parse(project.prodTreeIdHistory);
|
||||
return {
|
||||
...project,
|
||||
prodTreeIdHistory: TreeHistorySchema.parse(prodTreeIdHistory),
|
||||
assets: project?.assets?.map(formatAsset),
|
||||
};
|
||||
};
|
||||
|
||||
export const loadById = async (projectId?: Project["id"]) => {
|
||||
if (typeof projectId !== "string") {
|
||||
throw new Error("Project ID required");
|
||||
}
|
||||
|
||||
const project = await prisma.project.findUnique({
|
||||
where: { id: projectId },
|
||||
include: {
|
||||
assets: {
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!project) return null;
|
||||
return parseProject(project);
|
||||
};
|
||||
|
||||
export const loadByDomain = async (domain: string): Promise<Project | null> => {
|
||||
const project = await prisma.project.findUnique({
|
||||
where: { domain },
|
||||
include: {
|
||||
assets: {
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!project) return null;
|
||||
|
||||
return parseProject(project);
|
||||
};
|
||||
|
||||
export const loadManyByUserId = async (
|
||||
userId: User["id"]
|
||||
): Promise<Array<Project>> => {
|
||||
const projects = await prisma.project.findMany({
|
||||
where: {
|
||||
user: {
|
||||
id: userId,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return projects.map(parseProject) as Project[];
|
||||
};
|
||||
|
||||
const slugifyOptions = { lower: true, strict: true };
|
||||
|
||||
const MIN_DOMAIN_LENGTH = 10;
|
||||
const MIN_TITLE_LENGTH = 2;
|
||||
|
||||
const generateDomain = (title: string) => {
|
||||
const slugifiedTitle = slugify(title, slugifyOptions);
|
||||
const domain = `${slugifiedTitle}-${nanoid(
|
||||
// If user entered a long title already, we just add 5 chars generated id
|
||||
// Otherwise we add the amount of chars to satisfy min length
|
||||
Math.max(MIN_DOMAIN_LENGTH - slugifiedTitle.length - 1, 5)
|
||||
)}`;
|
||||
return domain;
|
||||
};
|
||||
|
||||
export const create = async ({
|
||||
userId,
|
||||
title,
|
||||
}: {
|
||||
userId: string;
|
||||
title: string;
|
||||
}) => {
|
||||
if (title.length < MIN_TITLE_LENGTH) {
|
||||
throw new Error(`Minimum ${MIN_TITLE_LENGTH} characters required`);
|
||||
}
|
||||
|
||||
const domain = generateDomain(title);
|
||||
const breakpoints = db.breakpoints.getBreakpointsWithId();
|
||||
const tree = await db.tree.create(db.tree.createRootInstance(breakpoints));
|
||||
const project = await prisma.project.create({
|
||||
data: {
|
||||
userId,
|
||||
title,
|
||||
domain,
|
||||
devTreeId: tree.id,
|
||||
},
|
||||
});
|
||||
|
||||
await db.breakpoints.create(tree.id, breakpoints);
|
||||
|
||||
return parseProject(project);
|
||||
};
|
||||
|
||||
export const clone = async (clonableDomain: string, userId: string) => {
|
||||
const clonableProject = await loadByDomain(clonableDomain);
|
||||
if (clonableProject === null) {
|
||||
throw new Error(`Not found project "${clonableDomain}"`);
|
||||
}
|
||||
if (clonableProject.prodTreeId === null) {
|
||||
throw new Error("Expected project to be published first");
|
||||
}
|
||||
|
||||
const tree = await db.tree.clone(clonableProject.prodTreeId);
|
||||
const domain = generateDomain(clonableProject.title);
|
||||
const [project] = await Promise.all([
|
||||
prisma.project.create({
|
||||
data: {
|
||||
userId: userId,
|
||||
title: clonableProject.title,
|
||||
domain,
|
||||
devTreeId: tree.id,
|
||||
},
|
||||
include: {
|
||||
assets: {
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
db.props.clone({
|
||||
previousTreeId: clonableProject.prodTreeId,
|
||||
nextTreeId: tree.id,
|
||||
}),
|
||||
db.breakpoints.clone({
|
||||
previousTreeId: clonableProject.prodTreeId,
|
||||
nextTreeId: tree.id,
|
||||
}),
|
||||
]);
|
||||
|
||||
const parsedProject = parseProject(project);
|
||||
if (parsedProject === null) {
|
||||
throw new Error(`Not found project "${clonableDomain}"`);
|
||||
}
|
||||
return parsedProject;
|
||||
};
|
||||
|
||||
export const update = async ({
|
||||
id,
|
||||
...data
|
||||
}: {
|
||||
id: string;
|
||||
domain?: string;
|
||||
prodTreeId?: string;
|
||||
devTreeId?: string;
|
||||
prodTreeIdHistory: Array<string>;
|
||||
}) => {
|
||||
if (data.domain) {
|
||||
data.domain = slugify(data.domain, slugifyOptions);
|
||||
if (data.domain.length < MIN_DOMAIN_LENGTH) {
|
||||
throw new Error(`Minimum ${MIN_DOMAIN_LENGTH} characters required`);
|
||||
}
|
||||
}
|
||||
|
||||
TreeHistorySchema.parse(data.prodTreeIdHistory);
|
||||
|
||||
try {
|
||||
const project = await prisma.project.update({
|
||||
data: {
|
||||
...data,
|
||||
prodTreeIdHistory: JSON.stringify(data.prodTreeIdHistory),
|
||||
},
|
||||
where: { id },
|
||||
include: {
|
||||
assets: {
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
return project;
|
||||
} catch (error) {
|
||||
if (
|
||||
error instanceof Prisma.PrismaClientKnownRequestError &&
|
||||
error.code === "P2002"
|
||||
) {
|
||||
throw new Error(`Domain "${data.domain}" is already used`);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
@@ -1,98 +0,0 @@
|
||||
import {
|
||||
type Tree,
|
||||
type AllUserProps,
|
||||
UserPropsSchema,
|
||||
} from "@webstudio-is/react-sdk";
|
||||
import { applyPatches, type Patch } from "immer";
|
||||
import { prisma } from "@webstudio-is/prisma-client";
|
||||
import { Project } from "./project";
|
||||
|
||||
export const loadByProject = async (
|
||||
project: Project | null,
|
||||
env: "production" | "development" = "development"
|
||||
) => {
|
||||
if (project === null) {
|
||||
throw new Error("Project required");
|
||||
}
|
||||
|
||||
const treeId = env === "production" ? project.prodTreeId : project.devTreeId;
|
||||
|
||||
if (treeId === null) {
|
||||
throw new Error("Site needs to be published, production tree ID is null.");
|
||||
}
|
||||
|
||||
return loadByTreeId(treeId);
|
||||
};
|
||||
|
||||
export const loadByTreeId = async (treeId: Tree["id"]) => {
|
||||
const instancePropsEntries = await prisma.instanceProps.findMany({
|
||||
where: { treeId },
|
||||
});
|
||||
|
||||
return instancePropsEntries.map((instanceProps) => {
|
||||
const props = JSON.parse(instanceProps.props);
|
||||
UserPropsSchema.parse(props);
|
||||
return {
|
||||
...instanceProps,
|
||||
props,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export const clone = async ({
|
||||
previousTreeId,
|
||||
nextTreeId,
|
||||
}: {
|
||||
previousTreeId: string;
|
||||
nextTreeId: string;
|
||||
}) => {
|
||||
const props = await prisma.instanceProps.findMany({
|
||||
where: { treeId: previousTreeId },
|
||||
});
|
||||
if (props.length === 0) return;
|
||||
const data = props.map(({ id: _id, treeId: _treeId, ...rest }) => ({
|
||||
...rest,
|
||||
treeId: nextTreeId,
|
||||
}));
|
||||
|
||||
await prisma.$transaction(
|
||||
data.map((prop) =>
|
||||
prisma.instanceProps.create({
|
||||
data: prop,
|
||||
})
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
export const patch = async (
|
||||
{ treeId }: { treeId: Tree["id"]; projectId: Project["id"] },
|
||||
patches: Array<Patch>
|
||||
) => {
|
||||
const allProps = await loadByTreeId(treeId);
|
||||
|
||||
// We should get rid of this by applying patches on the client to the original
|
||||
// model instead of the instanceId map.
|
||||
// The map is handy for accessing props, but probably should be just cached interface, not the main data structure.
|
||||
const allPropsMapByInstanceId = allProps.reduce((acc, prop) => {
|
||||
acc[prop.instanceId] = prop;
|
||||
return acc;
|
||||
}, {} as AllUserProps);
|
||||
const nextProps = applyPatches<AllUserProps>(
|
||||
allPropsMapByInstanceId,
|
||||
patches
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
Object.values(nextProps).map(({ id, instanceId, treeId, props }) => {
|
||||
UserPropsSchema.parse(props);
|
||||
const propsString = JSON.stringify(props);
|
||||
return prisma.instanceProps.upsert({
|
||||
where: { id: id },
|
||||
create: { id: id, instanceId, treeId, props: propsString },
|
||||
update: {
|
||||
props: propsString,
|
||||
},
|
||||
});
|
||||
})
|
||||
);
|
||||
};
|
||||
@@ -1,86 +0,0 @@
|
||||
import {
|
||||
type Instance,
|
||||
type Breakpoint,
|
||||
type Tree,
|
||||
InstanceSchema,
|
||||
} from "@webstudio-is/react-sdk";
|
||||
import { applyPatches, type Patch } from "immer";
|
||||
import { prisma } from "@webstudio-is/prisma-client";
|
||||
import { Tree as DbTree } from "@prisma/client";
|
||||
import { Project } from "./project";
|
||||
import { sort } from "./shared/breakpoints";
|
||||
import { createInstance, populateInstance } from "./shared/tree-utils";
|
||||
|
||||
export const createRootInstance = (breakpoints: Array<Breakpoint>) => {
|
||||
// Take the smallest breakpoint as default
|
||||
const defaultBreakpoint = sort(breakpoints)[0];
|
||||
if (defaultBreakpoint === undefined) {
|
||||
throw new Error("A breakpoint with minWidth 0 is required");
|
||||
}
|
||||
const instance = createInstance({ component: "Body" });
|
||||
return populateInstance(instance, defaultBreakpoint.id);
|
||||
};
|
||||
|
||||
export const create = async (root: Instance): Promise<DbTree> => {
|
||||
InstanceSchema.parse(root);
|
||||
const rootString = JSON.stringify(root);
|
||||
return await prisma.tree.create({
|
||||
data: { root: rootString },
|
||||
});
|
||||
};
|
||||
|
||||
export const loadById = async (treeId: string): Promise<Tree | null> => {
|
||||
const tree = await prisma.tree.findUnique({
|
||||
where: { id: treeId },
|
||||
});
|
||||
|
||||
if (tree === null) return null;
|
||||
|
||||
const root = JSON.parse(tree.root);
|
||||
InstanceSchema.parse(root);
|
||||
return {
|
||||
...tree,
|
||||
root,
|
||||
};
|
||||
};
|
||||
|
||||
export const loadByProject = async (
|
||||
project: Project | null,
|
||||
env: "production" | "development" = "development"
|
||||
) => {
|
||||
if (project === null) {
|
||||
throw new Error("Project required");
|
||||
}
|
||||
|
||||
const treeId = env === "production" ? project.prodTreeId : project.devTreeId;
|
||||
|
||||
if (treeId === null) {
|
||||
throw new Error("Site needs to be published, production tree ID is null.");
|
||||
}
|
||||
|
||||
return await loadById(treeId);
|
||||
};
|
||||
|
||||
export const clone = async (treeId: string) => {
|
||||
const tree = await loadById(treeId);
|
||||
if (tree === null) {
|
||||
throw new Error(`Tree ${treeId} not found`);
|
||||
}
|
||||
return await create(tree.root);
|
||||
};
|
||||
|
||||
export const patchRoot = async (
|
||||
{ treeId }: { treeId: Tree["id"] },
|
||||
patches: Array<Patch>
|
||||
) => {
|
||||
const tree = await loadById(treeId);
|
||||
if (tree === null) {
|
||||
throw new Error(`Tree ${treeId} not found`);
|
||||
}
|
||||
const root = applyPatches(tree.root, patches);
|
||||
InstanceSchema.parse(root);
|
||||
await prisma.tree.update({
|
||||
data: { root: JSON.stringify(root) },
|
||||
where: { id: treeId },
|
||||
});
|
||||
};
|
||||
File diff suppressed because it is too large.
Load diff
@@ -28,5 +28,5 @@ export const units = [
|
||||
"vmin",
|
||||
"vw",
|
||||
"x",
|
||||
"%"
|
||||
] as const;
|
||||
"%",
|
||||
] as const;
|
||||
Reference in new issue
Block a user