Merge pull request #277 from webstudio-is/project-package

Project package
This commit is contained in:
Andras Bacsai authored and GitHub committed 2022-09-13 21:05:35 +02:00
commit 30bf0f1850
71 files changed
+1781 -1257

No files matched your search

+15 -15
View File
@@ -1,30 +1,30 @@
import { useCallback, useMemo, useState } from "react";
import store from "immerhin";
import * as db from "~/shared/db";
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 * as db from "~/shared/db";
import type { Project } from "@webstudio-is/project";
export const useSync = ({ project }: { project: db.project.Project }) => {
export const useSync = ({ project }: { project: Project }) => {
useInterval(() => {
const entries = sync();
if (entries.length === 0) return;
+7 -7
View File
@@ -1,8 +1,8 @@
import { useCallback } from "react";
import { useSubscribe, usePublish, type Publish } from "~/shared/pubsub";
import * as db from "~/shared/db";
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,19 +1,18 @@
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 { sort } from "~/shared/breakpoints";
import { useSelectedBreakpoint } from "../../shared/nano-states";
import { BreakpointsEditor } from "./breakpoints-editor";
import { Preview } from "./preview";
@@ -21,11 +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 { 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}
@@ -1,9 +1,8 @@
import { useEffect } from "react";
import { useSubscribe } from "~/shared/pubsub";
import { useSelectedBreakpoint } from "~/designer/shared/nano-states";
import { sort } from "~/shared/breakpoints";
import { useBreakpoints } from "~/shared/nano-states";
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
@@ -1,14 +1,14 @@
import { useSubscribe } from "~/shared/pubsub";
import { sort } from "~/shared/breakpoints";
import { useBreakpoints } from "~/shared/nano-states";
import { useZoom, useSelectedBreakpoint } from "../../shared/nano-states";
import { minZoom } from "./zoom-setting";
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 { sort } from "~/shared/breakpoints";
import { __DEPRECATED__Text, Flex, Slider } from "@webstudio-is/design-system";
import { useBreakpoints, useIsPreviewMode } from "~/shared/nano-states";
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 "~/shared/db";
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 "~/shared/db";
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 "~/shared/db";
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) => {
+4 -4
View File
@@ -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 "~/shared/db";
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({
+4 -3
View File
@@ -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 "~/shared/db";
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 = {
+3 -3
View File
@@ -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 "~/shared/db";
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 type { Breakpoint } from "@webstudio-is/react-sdk";
import * as db from "~/shared/db";
import { Breakpoint } from "@webstudio-is/react-sdk";
import { db } from "@webstudio-is/project";
export type ErrorData = {
errors: string;
@@ -1,6 +1,6 @@
import type { LoaderFunction } from "@remix-run/node";
import type { Breakpoint } from "@webstudio-is/react-sdk";
import * as db from "~/shared/db";
import { db } from "@webstudio-is/project";
export type ErrorData = {
errors: string;
+1 -1
View File
@@ -1,5 +1,5 @@
import { type ActionFunction } from "@remix-run/node";
import * as db from "~/shared/db";
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";
@@ -1,6 +1,7 @@
import { type LoaderFunction, redirect } from "@remix-run/node";
import type { User } from "@webstudio-is/prisma-client";
import * as db from "~/shared/db";
import { db as projectDb, type Project } from "@webstudio-is/project";
import * as userDb from "~/shared/db";
import { ensureUserCookie } from "~/shared/session";
import config from "~/config";
import { authenticator } from "~/services/auth.server";
@@ -11,11 +12,11 @@ const ensureProject = async ({
}: {
userId: User["id"];
domain: string;
}): Promise<db.project.Project> => {
const projects = await db.project.loadManyByUserId(userId);
}): Promise<Project> => {
const projects = await projectDb.project.loadManyByUserId(userId);
if (projects.length !== 0) return projects[0];
return await db.project.clone(domain, userId);
return await projectDb.project.clone(domain, userId);
};
/**
@@ -31,7 +32,7 @@ export const loader: LoaderFunction = async ({ request, params }) => {
const user = await authenticator.isAuthenticated(request);
const { headers, userId: generatedUserId } = await ensureUserCookie(request);
try {
const userId = await db.user.ensureUser({
const userId = await userDb.user.ensureUser({
userId: user ? user.id : generatedUserId,
});
const project = await ensureProject({
@@ -1,7 +1,6 @@
import { type LoaderFunction } from "@remix-run/node";
import { type InstanceProps } from "@webstudio-is/react-sdk";
import * as db from "~/shared/db";
import { db } from "@webstudio-is/project";
type ErrorData = {
errors: string;
};
@@ -1,7 +1,6 @@
import { type LoaderFunction } from "@remix-run/node";
import type { Tree } from "@webstudio-is/react-sdk";
import * as db from "~/shared/db";
import { db } from "@webstudio-is/project";
export type ErrorData = {
errors: string;
};
+2 -3
View File
@@ -1,7 +1,6 @@
import { type Data } from "@webstudio-is/react-sdk";
import * as db from ".";
import { Project } from "./project.server";
import { db } from "@webstudio-is/project";
import type { Project } from "@webstudio-is/project";
export type CanvasData = Data & { project: Project };
export type ErrorData = {
-4
View File
@@ -1,7 +1,3 @@
export * as tree from "./tree.server";
export * as project from "./project.server";
export * as props from "./props.server";
export * as misc from "./misc.server";
export * as user from "./user.server";
export * from "./canvas.server";
export * as breakpoints from "./breakpoints.server";
+2 -1
View File
@@ -1,4 +1,5 @@
import * as db from "./";
import { db } from "@webstudio-is/project";
/**
* Conceptually publishing is cloning all data that affects user site
* and referencing it under a new tree id.
+1
View File
@@ -30,6 +30,7 @@
"@webstudio-is/prisma-client": "*",
"@webstudio-is/react-sdk": "*",
"@webstudio-is/css-vars": "*",
"@webstudio-is/project": "*",
"bson-objectid": "^2.0.2",
"camelcase": "^6.3.0",
"debug": "^4.3.4",
-1
View File
@@ -28,4 +28,3 @@ export const loadProject = async ({
breakpoints,
};
};
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Webstudio
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+3
View File
@@ -0,0 +1,3 @@
# Webstudio Project
Project related functionalities.
+1
View File
@@ -0,0 +1 @@
export * from "./src/index";
+1
View File
@@ -0,0 +1 @@
module.exports = require("@webstudio-is/jest-config");
+35
View File
@@ -0,0 +1,35 @@
{
"name": "@webstudio-is/project",
"version": "0.0.1",
"description": "Webstudio Project",
"author": "Webstudio <github@webstudio.is>",
"homepage": "https://webstudio.is",
"scripts": {
"typecheck": "tsc --noEmit",
"test": "jest",
"checks": "yarn typecheck && yarn lint && yarn test",
"dev": "tsup src/index.ts --format esm,cjs --watch --out-dir=lib",
"build": "rm -fr lib && tsc",
"lint": "eslint ./src --ext .ts,.tsx --max-warnings 0"
},
"dependencies": {},
"devDependencies": {
"@webstudio-is/asset-uploader": "*",
"@webstudio-is/prisma-client": "*",
"@webstudio-is/react-sdk": "*",
"nanoid": "^4.0.0",
"slugify": "^1.6.5",
"zod": "^3.19.1",
"tsup": "^6.1.3",
"typescript": "4.7.4"
},
"main": "lib/index.js",
"types": "lib/index.d.ts",
"files": [
"lib/*",
"README.md",
"!*.test.*"
],
"license": "MIT",
"private": false
}
+5
View File
@@ -0,0 +1,5 @@
import * as project from "./project";
import * as tree from "./tree";
import * as props from "./props";
import * as breakpoints from "./breakpoints";
export { project, tree, breakpoints, props };
@@ -7,17 +7,11 @@ import {
Prisma,
Project as BaseProject,
} from "@webstudio-is/prisma-client";
import * as db from ".";
import { formatAsset } from "@webstudio-is/asset-uploader";
import type { Asset } from "@webstudio-is/asset-uploader";
import * as db from "./index";
import type { Project } 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 {
@@ -5,7 +5,7 @@ import {
} from "@webstudio-is/react-sdk";
import { applyPatches, type Patch } from "immer";
import { prisma } from "@webstudio-is/prisma-client";
import { Project } from "./project.server";
import type { Project } from "./types";
export const loadByProject = async (
project: Project | null,
@@ -6,19 +6,18 @@ import {
} from "@webstudio-is/react-sdk";
import { applyPatches, type Patch } from "immer";
import { prisma } from "@webstudio-is/prisma-client";
import { createInstance, populateInstance } from "~/shared/tree-utils";
import { sort } from "~/shared/breakpoints";
import { Tree as DbTree } from "@prisma/client";
import { Project } from "./project.server";
import type { Project } from "../index";
import { utils } from "../index";
export const createRootInstance = (breakpoints: Array<Breakpoint>) => {
// Take the smallest breakpoint as default
const defaultBreakpoint = sort(breakpoints)[0];
const defaultBreakpoint = utils.breakpoints.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);
const instance = utils.tree.createInstance({ component: "Body" });
return utils.tree.populateInstance(instance, defaultBreakpoint.id);
};
export const create = async (root: Instance): Promise<DbTree> => {
+9
View File
@@ -0,0 +1,9 @@
import { z } from "zod";
import { Project as BaseProject } from "@webstudio-is/prisma-client";
import type { Asset } from "@webstudio-is/asset-uploader";
const TreeHistorySchema = z.array(z.string());
export type Project = Omit<BaseProject, "prodTreeIdHistory" | "assets"> & {
prodTreeIdHistory: z.infer<typeof TreeHistorySchema>;
assets?: Array<Asset>;
};
+8
View File
@@ -0,0 +1,8 @@
import * as dbFunctions from "./db";
import * as breakpointsUtils from "./shared/breakpoints";
import * as treeUtils from "./shared/tree-utils";
export const utils = { breakpoints: breakpointsUtils, tree: treeUtils };
export const db = dbFunctions;
export * from "./db/types";
@@ -0,0 +1 @@
export * from "./instance-data";
@@ -0,0 +1,33 @@
import {
type Instance,
type InstanceProps,
type CssRule,
type Style,
type StyleProperty,
type StyleValue,
type Breakpoint,
} from "@webstudio-is/react-sdk";
export type SelectedInstanceData = {
id: Instance["id"];
component: Instance["component"];
cssRules: Array<CssRule>;
browserStyle: Style;
props: InstanceProps;
};
export type HoveredInstanceData = {
id: Instance["id"];
component: Instance["component"];
};
type StyleUpdate = {
property: StyleProperty;
value: StyleValue;
};
export type StyleUpdates = {
id: Instance["id"];
updates: Array<StyleUpdate>;
breakpoint: Breakpoint;
};
@@ -0,0 +1,33 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Delete instance div 1 > div 2 > [div 3] 1`] = `
Object {
"children": Array [
Object {
"children": Array [],
"component": "Box",
"cssRules": Array [],
"id": "2",
},
],
"component": "Box",
"cssRules": Array [],
"id": "1",
}
`;
exports[`Delete instance div 1 > div 2, [div 3] 1`] = `
Object {
"children": Array [
Object {
"children": Array [],
"component": "Box",
"cssRules": Array [],
"id": "2",
},
],
"component": "Box",
"cssRules": Array [],
"id": "1",
}
`;
@@ -0,0 +1,90 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Insert instance after a nested div using "end": div 1 > div 2, [div 3] 1`] = `
Object {
"children": Array [
Object {
"children": Array [],
"component": "Box",
"cssRules": Array [],
"id": "2",
},
Object {
"children": Array [],
"component": "Box",
"cssRules": Array [],
"id": "3",
},
],
"component": "Box",
"cssRules": Array [],
"id": "1",
}
`;
exports[`Insert instance after a nested div using index: div 1 > div 2, [div 3] 1`] = `
Object {
"children": Array [
Object {
"children": Array [],
"component": "Box",
"cssRules": Array [],
"id": "2",
},
Object {
"children": Array [],
"component": "Box",
"cssRules": Array [],
"id": "3",
},
],
"component": "Box",
"cssRules": Array [],
"id": "1",
}
`;
exports[`Insert instance before a nested div using index: div 1 > [div 3], div 2 1`] = `
Object {
"children": Array [
Object {
"children": Array [],
"component": "Box",
"cssRules": Array [],
"id": "3",
},
Object {
"children": Array [],
"component": "Box",
"cssRules": Array [],
"id": "2",
},
],
"component": "Box",
"cssRules": Array [],
"id": "1",
}
`;
exports[`Insert instance inside a nested div using "end": div 1 > div 2 > [div 3] 1`] = `
Object {
"children": Array [
Object {
"children": Array [
Object {
"children": Array [],
"component": "Box",
"cssRules": Array [],
"id": "3",
},
],
"component": "Box",
"cssRules": Array [],
"id": "2",
},
],
"component": "Box",
"cssRules": Array [],
"id": "1",
}
`;
@@ -0,0 +1,58 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Reparent instance new parent 1`] = `
Object {
"children": Array [
Object {
"children": Array [
Object {
"children": Array [],
"component": "Box",
"cssRules": Array [],
"id": "4",
},
],
"component": "Box",
"cssRules": Array [],
"id": "2",
},
Object {
"children": Array [],
"component": "Box",
"cssRules": Array [],
"id": "3",
},
],
"component": "Box",
"cssRules": Array [],
"id": "1",
}
`;
exports[`Reparent instance same parent different position 1`] = `
Object {
"children": Array [
Object {
"children": Array [],
"component": "Box",
"cssRules": Array [],
"id": "3",
},
Object {
"children": Array [],
"component": "Box",
"cssRules": Array [],
"id": "2",
},
Object {
"children": Array [],
"component": "Box",
"cssRules": Array [],
"id": "4",
},
],
"component": "Box",
"cssRules": Array [],
"id": "1",
}
`;
@@ -0,0 +1,42 @@
import { type Instance } from "@webstudio-is/react-sdk";
import { cloneInstance } from "./clone-instance";
const getIds = (instance: Instance, ids: Array<Instance["id"]> = []) => {
ids.push(instance.id);
for (const child of instance.children) {
if (typeof child !== "string") getIds(child);
}
return ids;
};
describe("Clone instance", () => {
test("ensure new ids", () => {
const instance: Instance = {
component: "Box",
id: "1",
cssRules: [],
children: [
{
component: "Box",
id: "2",
cssRules: [],
children: [
{
component: "Box",
id: "3",
cssRules: [],
children: [],
},
],
},
],
};
const ids = getIds(instance);
const clone = cloneInstance(instance);
const clonedIds = getIds(clone);
ids.forEach((id, index) => {
expect(id).not.toBe(clonedIds[index]);
});
});
});
@@ -0,0 +1,14 @@
import ObjectId from "bson-objectid";
import produce from "immer";
import { type Instance } from "@webstudio-is/react-sdk";
const updateIds = (instance: Instance) => {
instance.id = ObjectId().toString();
for (const child of instance.children) {
if (typeof child === "string") continue;
updateIds(child);
}
};
export const cloneInstance = (instance: Instance): Instance =>
produce(updateIds)(instance);
@@ -0,0 +1,48 @@
import { createInstance } from "./create-instance";
describe("create instance", () => {
test("instance without id should be auto generated", () => {
const createdInstance = createInstance({
component: "Box",
});
expect(typeof createdInstance.id).toBe("string");
});
test("instance without children should be empty by default", () => {
const createdInstance = createInstance({
component: "Box",
});
expect(createdInstance.children.length).toBe(0);
});
test("instance without style should be an object after creation", () => {
const createdInstance = createInstance({
component: "Box",
});
expect(Array.isArray(createdInstance.cssRules)).toBeTruthy();
});
test("instance with children ", () => {
const createdInstance = createInstance({
component: "Box",
children: [
{
id: "1",
component: "Box",
cssRules: [],
children: [],
},
],
cssRules: [],
});
expect(createdInstance.children).toEqual([
{
id: "1",
component: "Box",
cssRules: [],
children: [],
},
]);
});
});
@@ -0,0 +1,26 @@
import ObjectId from "bson-objectid";
import {
type CssRule,
type Instance,
components,
} from "@webstudio-is/react-sdk";
export const createInstance = ({
component,
id,
children,
cssRules,
}: {
component: Instance["component"];
id?: Instance["id"];
children?: Instance["children"];
cssRules?: Array<CssRule>;
}): Instance => {
const componentMeta = components[component];
return {
component,
id: id === undefined ? ObjectId().toString() : id,
cssRules: cssRules ?? [],
children: children === undefined ? componentMeta.children ?? [] : children,
};
};
@@ -0,0 +1,55 @@
import { type Instance } from "@webstudio-is/react-sdk";
import { deleteInstanceMutable } from "./delete-instance";
describe("Delete instance", () => {
test("div 1 > div 2 > [div 3]", () => {
const instance: Instance = {
component: "Box",
id: "1",
cssRules: [],
children: [
{
component: "Box",
id: "2",
cssRules: [],
children: [
{
component: "Box",
id: "3",
cssRules: [],
children: [],
},
],
},
],
};
deleteInstanceMutable(instance, "3");
expect(instance).toMatchSnapshot();
});
test("div 1 > div 2, [div 3]", () => {
const instance: Instance = {
component: "Box",
id: "1",
cssRules: [],
children: [
{
component: "Box",
id: "2",
cssRules: [],
children: [],
},
{
component: "Box",
id: "3",
cssRules: [],
children: [],
},
],
};
deleteInstanceMutable(instance, "3");
expect(instance).toMatchSnapshot();
});
});
@@ -0,0 +1,19 @@
import { type Instance } from "@webstudio-is/react-sdk";
export const deleteInstanceMutable = (
instance: Instance,
instanceId: Instance["id"]
): boolean => {
if (instance.id === instanceId) return true;
for (const child of instance.children) {
if (typeof child === "string") continue;
const shouldDelete = deleteInstanceMutable(child, instanceId);
if (shouldDelete === true) {
const index = instance.children.indexOf(child);
instance.children.splice(index, 1);
return false;
}
}
return false;
};
@@ -0,0 +1,51 @@
import { createInstance } from "./create-instance";
import { findClosestNonInlineParent } from "./find-closest-non-inline-parent";
describe("findClosestNonInlineParent", () => {
const italic = createInstance({ component: "Italic" });
const boldWithItalic = createInstance({
component: "Bold",
children: [italic],
});
test("when there's no non-inline parent, returns undefined", () => {
expect(
findClosestNonInlineParent(boldWithItalic, italic.id)
).toBeUndefined();
expect(findClosestNonInlineParent(italic, italic.id)).toBeUndefined();
});
test("when the given instance isn't in the tree, returns undefined", () => {
const root = createInstance({ component: "Body" });
expect(findClosestNonInlineParent(root, italic.id)).toBeUndefined();
});
test("retrunrns the first non-inline parent", () => {
const root = createInstance({
component: "Body",
children: [
createInstance({
component: "Heading",
id: "the-heading",
children: [italic],
}),
],
});
expect(findClosestNonInlineParent(root, italic.id)?.id).toBe("the-heading");
});
test("retrunrns the first non-inline parent (nested)", () => {
const root = createInstance({
component: "Body",
children: [
createInstance({
component: "Heading",
id: "the-heading",
children: [boldWithItalic],
}),
],
});
expect(findClosestNonInlineParent(root, italic.id)?.id).toBe("the-heading");
});
});
@@ -0,0 +1,11 @@
import { Instance, components } from "@webstudio-is/react-sdk";
import { getInstancePath } from "./get-instance-path";
export const findClosestNonInlineParent = (
rootInstance: Instance,
instanceId: Instance["id"]
): Instance | undefined => {
const path = getInstancePath(rootInstance, instanceId);
path.reverse();
return path.find((item) => components[item.component].isInlineOnly === false);
};
@@ -0,0 +1,42 @@
import { type Instance } from "@webstudio-is/react-sdk";
import { findClosestSiblingInstance } from "./find-closest-sibling-instance";
describe("Find closest sibling instance", () => {
test("find", () => {
const rootInstance: Instance = {
component: "Box",
id: "1",
cssRules: [],
children: [
{
component: "Box",
id: "3",
cssRules: [],
children: [],
},
"a",
{
component: "Box",
id: "4",
cssRules: [],
children: [],
},
"b",
{
component: "Box",
id: "5",
cssRules: [],
children: [],
},
],
};
expect(
findClosestSiblingInstance(rootInstance, "notFound")
).toBeUndefined();
expect(findClosestSiblingInstance(rootInstance, "1")).toBeUndefined();
expect(findClosestSiblingInstance(rootInstance, "3")?.id).toBe("4");
expect(findClosestSiblingInstance(rootInstance, "4")?.id).toBe("5");
expect(findClosestSiblingInstance(rootInstance, "5")?.id).toBe("4");
});
});
@@ -0,0 +1,17 @@
import { type Instance } from "@webstudio-is/react-sdk";
export const findClosestSiblingInstance = (
instance: Instance,
instanceId: Instance["id"]
): Instance | undefined => {
if (instance.children.length === 0) return;
const children = instance.children.filter(
(instance) => typeof instance === "object"
) as Array<Instance>;
const index = children.findIndex((instance) => instance.id === instanceId);
if (index === -1) return;
const nextInstance = children[index + 1];
if (nextInstance !== undefined) return nextInstance;
const previousInstance = children[index - 1];
return previousInstance;
};
@@ -0,0 +1,19 @@
import { type Instance } from "@webstudio-is/react-sdk";
// @todo should be optimized because it will be a frequent operation on a potentially large tree.
export const findInstanceById = (
instance: Instance,
id: Instance["id"]
): Instance | undefined => {
if (instance.id === id) {
return instance;
}
if (instance.children !== undefined) {
for (const child of instance.children) {
if (typeof child === "string") continue;
const foundInstance = findInstanceById(child, id);
if (foundInstance !== undefined) return foundInstance;
}
}
};
@@ -0,0 +1,31 @@
import { type Instance } from "@webstudio-is/react-sdk";
import { findParentInstance } from "./find-parent-instance";
describe("Find parent instance", () => {
test("find", () => {
const rootInstance: Instance = {
component: "Box",
id: "1",
cssRules: [],
children: [
{
component: "Box",
id: "2",
cssRules: [],
children: [
{
component: "Box",
id: "3",
cssRules: [],
children: [],
},
],
},
],
};
expect(findParentInstance(rootInstance, "3")?.id).toBe("2");
expect(findParentInstance(rootInstance, "2")?.id).toBe("1");
expect(findParentInstance(rootInstance, "1")).toBe(undefined);
});
});
@@ -0,0 +1,19 @@
import { type Instance } from "@webstudio-is/react-sdk";
export const findParentInstance = (
instance: Instance,
instanceId: Instance["id"]
): Instance | undefined => {
const find = (
childInstance: Instance,
parentInstance?: Instance
): Instance | undefined => {
if (childInstance.id === instanceId) return parentInstance;
for (const child of childInstance.children) {
if (typeof child === "string") continue;
const foundInstance = find(child, childInstance);
if (foundInstance !== undefined) return foundInstance;
}
};
return find(instance);
};
@@ -0,0 +1,158 @@
import { type Instance } from "@webstudio-is/react-sdk";
import {
getInstancePath,
getInstancePathWithPositions,
} from "./get-instance-path";
const getIds = (array: Array<{ id: string }>) => array.map((item) => item.id);
describe("Get instance path", () => {
test("single possible path", () => {
const instance: Instance = {
component: "Box",
id: "1",
cssRules: [],
children: [
{
component: "Box",
id: "2",
cssRules: [],
children: [
{
component: "Box",
id: "3",
cssRules: [],
children: [],
},
],
},
],
};
const path = getInstancePath(instance, "3");
expect(getIds(path)).toEqual(["1", "2", "3"]);
});
test("two possible paths", () => {
const instance: Instance = {
component: "Box",
id: "1",
cssRules: [],
children: [
{
component: "Box",
id: "2a",
cssRules: [],
children: [
{
component: "Box",
id: "3a",
cssRules: [],
children: [],
},
],
},
{
component: "Box",
id: "2b",
cssRules: [],
children: [
{
component: "Box",
id: "3b",
cssRules: [],
children: [],
},
],
},
],
};
const path1 = getInstancePath(instance, "3b");
expect(getIds(path1)).toEqual(["1", "2b", "3b"]);
const path2 = getInstancePath(instance, "3a");
expect(getIds(path2)).toEqual(["1", "2a", "3a"]);
});
});
const getIdAndPosition = (
array: Array<{ item: { id: string }; position: number }>
) => array.map((item) => [item.item.id, item.position]);
describe("Get instance path with positions", () => {
test("single possible path", () => {
const instance: Instance = {
component: "Box",
id: "1",
cssRules: [],
children: [
{
component: "Box",
id: "2",
cssRules: [],
children: [
{
component: "Box",
id: "3",
cssRules: [],
children: [],
},
],
},
],
};
const path = getInstancePathWithPositions(instance, "3");
expect(getIdAndPosition(path)).toEqual([
["1", 0],
["2", 0],
["3", 0],
]);
});
test("two possible paths", () => {
const instance: Instance = {
component: "Box",
id: "1",
cssRules: [],
children: [
{
component: "Box",
id: "2a",
cssRules: [],
children: [
{
component: "Box",
id: "3a",
cssRules: [],
children: [],
},
],
},
{
component: "Box",
id: "2b",
cssRules: [],
children: [
{
component: "Box",
id: "3b",
cssRules: [],
children: [],
},
],
},
],
};
const path1 = getInstancePathWithPositions(instance, "3b");
expect(getIdAndPosition(path1)).toEqual([
["1", 0],
["2b", 1],
["3b", 0],
]);
const path2 = getInstancePathWithPositions(instance, "3a");
expect(getIdAndPosition(path2)).toEqual([
["1", 0],
["2a", 0],
["3a", 0],
]);
});
});
@@ -0,0 +1,52 @@
import { Instance } from "@webstudio-is/react-sdk";
export const getInstancePath = (
instance: Instance,
instanceId: Instance["id"]
) => {
const path = [];
const find = (instance: Instance) => {
if (instance.id === instanceId) return true;
for (const child of instance.children) {
if (typeof child === "string") continue;
const found = find(child);
if (found) {
path.push(child);
return true;
}
}
};
if (find(instance)) {
path.push(instance);
}
return path.reverse();
};
export const getInstancePathWithPositions = (
instance: Instance,
instanceId: Instance["id"]
) => {
const path = [];
const find = (instance: Instance) => {
if (instance.id === instanceId) return true;
for (let index = 0; index < instance.children.length; index++) {
const child = instance.children[index];
if (typeof child === "string") continue;
const found = find(child);
if (found) {
path.push({ item: child, position: index });
return true;
}
}
};
if (find(instance)) {
path.push({ item: instance, position: 0 });
}
return path.reverse();
};
@@ -0,0 +1,13 @@
export * from "./create-instance";
export * from "./delete-instance";
export * from "./insert-instance";
export * from "./populate";
export * from "./find-instance";
export * from "./set-instance-style";
export * from "./set-instance-children";
export * from "./get-instance-path";
export * from "./find-closest-sibling-instance";
export * from "./find-parent-instance";
export * from "./clone-instance";
export * from "./reparent-instance";
export * from "./find-closest-non-inline-parent";
@@ -0,0 +1,98 @@
import { type Instance } from "@webstudio-is/react-sdk";
import { insertInstanceMutable } from "./insert-instance";
import { createInstance } from "./create-instance";
const instance = createInstance({ id: "3", component: "Box" });
describe("Insert instance", () => {
test(`inside a nested div using "end": div 1 > div 2 > [div 3]`, () => {
const rootInstance: Instance = {
component: "Box",
id: "1",
cssRules: [],
children: [
{
component: "Box",
id: "2",
cssRules: [],
children: [],
},
],
};
const hasInserted = insertInstanceMutable(rootInstance, instance, {
parentId: "2",
position: "end",
});
expect(hasInserted).toBeTruthy();
expect(rootInstance).toMatchSnapshot();
});
test("after a nested div using index: div 1 > div 2, [div 3]", () => {
const rootInstance: Instance = {
component: "Box",
id: "1",
cssRules: [],
children: [
{
component: "Box",
id: "2",
cssRules: [],
children: [],
},
],
};
const hasInserted = insertInstanceMutable(rootInstance, instance, {
parentId: "1",
position: 1,
});
expect(hasInserted).toBeTruthy();
expect(rootInstance).toMatchSnapshot();
});
test("before a nested div using index: div 1 > [div 3], div 2", () => {
const rootInstance: Instance = {
component: "Box",
id: "1",
cssRules: [],
children: [
{
component: "Box",
id: "2",
cssRules: [],
children: [],
},
],
};
const hasInserted = insertInstanceMutable(rootInstance, instance, {
parentId: "1",
position: 0,
});
expect(hasInserted).toBeTruthy();
expect(rootInstance).toMatchSnapshot();
});
test(`after a nested div using "end": div 1 > div 2, [div 3]`, () => {
const rootInstance: Instance = {
component: "Box",
id: "1",
cssRules: [],
children: [
{
component: "Box",
id: "2",
cssRules: [],
children: [],
},
],
};
const hasInserted = insertInstanceMutable(rootInstance, instance, {
parentId: "1",
position: "end",
});
expect(hasInserted).toBeTruthy();
expect(rootInstance).toMatchSnapshot();
});
});
@@ -0,0 +1,35 @@
import { type Instance } from "@webstudio-is/react-sdk";
export type InstanceInsertionSpec = {
parentId: Instance["id"];
position: number | "end";
};
export const insertInstanceMutable = (
rootInstance: Instance,
instance: Instance,
spec: InstanceInsertionSpec
): boolean => {
if (spec.parentId !== rootInstance.id) {
for (const child of rootInstance.children) {
if (typeof child === "string") continue;
const hasInserted = insertInstanceMutable(child, instance, spec);
if (hasInserted === true) return true;
}
return false;
}
// Inserting inside the selected/hovered instance.
if (typeof spec.position === "number") {
rootInstance.children.splice(spec.position, 0, instance);
return true;
}
// Inserting after all children
if (spec.position === "end") {
rootInstance.children.push(instance);
return true;
}
return true;
};
@@ -0,0 +1,26 @@
import {
type Instance,
type Breakpoint,
components,
} from "@webstudio-is/react-sdk";
/**
* Populate instance with:
* - defaultStyles
* @todo rewrite using immer
*/
export const populateInstance = (
instance: Instance,
breakpoint: Breakpoint["id"] = ""
): Instance => {
const populatedInstance = { ...instance };
const componentMeta = components[instance.component];
if (componentMeta !== undefined && "defaultStyle" in componentMeta) {
const cssRule = {
breakpoint,
style: componentMeta.defaultStyle ?? {},
};
populatedInstance.cssRules.push(cssRule);
}
return populatedInstance;
};
@@ -0,0 +1,67 @@
import { type Instance } from "@webstudio-is/react-sdk";
import { reparentInstanceMutable } from "./reparent-instance";
describe("Reparent instance", () => {
test(`new parent`, () => {
const rootInstance: Instance = {
component: "Box",
id: "1",
cssRules: [],
children: [
{
component: "Box",
id: "2",
cssRules: [],
children: [],
},
{
component: "Box",
id: "3",
cssRules: [],
children: [
{
component: "Box",
id: "4",
cssRules: [],
children: [],
},
],
},
],
};
reparentInstanceMutable(rootInstance, "4", "2", 0);
expect(rootInstance).toMatchSnapshot();
});
test(`same parent different position`, () => {
const rootInstance: Instance = {
component: "Box",
id: "1",
cssRules: [],
children: [
{
component: "Box",
id: "2",
cssRules: [],
children: [],
},
{
component: "Box",
id: "3",
cssRules: [],
children: [],
},
{
component: "Box",
id: "4",
cssRules: [],
children: [],
},
],
};
reparentInstanceMutable(rootInstance, "2", "1", 2);
expect(rootInstance).toMatchSnapshot();
});
});
@@ -0,0 +1,45 @@
import { type Instance } from "@webstudio-is/react-sdk";
import { findInstanceById } from "./find-instance";
import { findParentInstance } from "./find-parent-instance";
import { deleteInstanceMutable } from "./delete-instance";
import { insertInstanceMutable } from "./insert-instance";
export const reparentInstanceMutable = (
rootInstance: Instance,
instanceId: Instance["id"],
newParentId: Instance["id"],
newPosition: number | "end"
) => {
const instance = findInstanceById(rootInstance, instanceId);
if (instance === undefined) {
return;
}
if (newPosition === "end") {
deleteInstanceMutable(rootInstance, instance.id);
insertInstanceMutable(rootInstance, instance, {
parentId: newParentId,
position: "end",
});
return;
}
let newPositionAdjusted = newPosition;
// If parent is the same, we need to adjust the position to account for the removal of the instance.
const currentParent = findParentInstance(rootInstance, instance.id);
if (currentParent !== undefined && currentParent.id === newParentId) {
const currentPosition = currentParent.children.findIndex(
(child) => typeof child !== "string" && child.id === instance.id
);
if (currentPosition < newPosition) {
newPositionAdjusted--;
}
}
deleteInstanceMutable(rootInstance, instance.id);
insertInstanceMutable(rootInstance, instance, {
parentId: newParentId,
position: newPositionAdjusted,
});
};
@@ -0,0 +1,53 @@
import { type ChildrenUpdates } from "@webstudio-is/react-sdk";
import { type Instance } from "@webstudio-is/react-sdk";
import { createInstance } from "./create-instance";
import { findInstanceById } from "./find-instance";
/**
* Update children of an instance with new text.
* When child is an instance - we update it's children with a single text entry.
*/
export const setInstanceChildrenMutable = (
id: Instance["id"],
// Not a consistent format now, maybe better:
// [{set: 'string'}, {set: instance}, {update: {id,text}}]
updates: ChildrenUpdates,
rootInstance: Instance
) => {
const instance = findInstanceById(rootInstance, id);
if (instance === undefined) return false;
const children = [];
for (const update of updates) {
// Set a string as a child
if (typeof update === "string") {
children.push(update);
continue;
}
// We need to create an instance
if ("createInstance" in update) {
const childInstance = createInstance({
id: update.id,
component: update.component,
children: [update.text],
});
children.push(childInstance);
continue;
}
// Set text as a single child of a child instance
if ("text" in update) {
const childInstance = findInstanceById(instance, update.id);
// It should be impossible to have not found that instance
if (childInstance === undefined) continue;
children.push({ ...childInstance, children: [update.text] });
continue;
}
// It's a new instance.
children.push(update);
}
instance.children = children;
return true;
};
@@ -0,0 +1,25 @@
import { Breakpoint, type Instance } from "@webstudio-is/react-sdk";
import { StyleUpdates } from "../canvas-components";
import { findInstanceById } from "./find-instance";
export const setInstanceStyleMutable = (
rootInstance: Instance,
id: Instance["id"],
updates: StyleUpdates["updates"],
breakpoint: Breakpoint
) => {
const instance = findInstanceById(rootInstance, id);
if (instance === undefined) return false;
let cssRule = instance.cssRules.find(
(cssRule) => cssRule.breakpoint === breakpoint.id
);
if (cssRule === undefined) {
cssRule = { style: {}, breakpoint: breakpoint.id };
instance.cssRules.push(cssRule);
}
for (const update of updates) {
cssRule.style[update.property] = update.value;
}
return true;
};
+12
View File
@@ -0,0 +1,12 @@
// Extends can't use node's resolution rn https://github.com/dividab/tsconfig-paths/issues/209#issuecomment-1177261091
{
"extends": "../../node_modules/@webstudio-is/tsconfig/base.json",
"include": ["./src/**/*.ts"],
"compilerOptions": {
"module": "commonjs",
"target": "ES2020",
"strict": true,
"baseUrl": ".",
"outDir": "./lib"
}
}
File diff suppressed because it is too large. Load diff
+2 -2
View File
@@ -28,5 +28,5 @@ export const units = [
"vmin",
"vw",
"x",
"%"
] as const;
"%",
] as const;
+10
View File
@@ -13262,6 +13262,11 @@ nanoid@^3.2.0, nanoid@^3.3.1, nanoid@^3.3.4:
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
nanoid@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-4.0.0.tgz#6e144dee117609232c3f415c34b0e550e64999a5"
integrity sha512-IgBP8piMxe/gf73RTQx7hmnhwz0aaEXYakvqZyE302IXW3HyVNhdNGC+O2MwMAVhLEnvXlvKtGbtJf6wvHihCg==
nanomatch@^1.2.9:
version "1.2.13"
resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
@@ -18113,6 +18118,11 @@ zod@^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"
integrity sha512-LYjZsEDhCdYET9ikFu6dVPGp2YH9DegXjdJToSzD9rO6fy4qiRYFoyEYwps88OseJlPyl2NOe2iJuhEhL7IpEA==
zwitch@^1.0.0:
version "1.0.5"
resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920"