style: eslint-plugin-import

This commit is contained in:
Fu Diwei 2025-01-01 20:40:59 +08:00
parent e2d29b8fa2
commit 78d9d5159a
76 changed files with 1996 additions and 164 deletions

View File

@ -8,7 +8,14 @@ module.exports = {
node: true,
es2020: true,
},
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:prettier/recommended", "plugin:react-hooks/recommended"],
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:import/recommended",
"plugin:import/typescript",
"plugin:prettier/recommended",
"plugin:react-hooks/recommended",
],
ignorePatterns: ["dist", ".eslintrc.cjs"],
parser: "@typescript-eslint/parser",
plugins: ["react-refresh"],
@ -28,6 +35,50 @@ module.exports = {
varsIgnorePattern: "^_",
},
],
"import/no-named-as-default-member": "off",
"import/no-unresolved": "off",
"import/order": [
"error",
{
groups: ["builtin", "external", "internal", ["parent", "sibling"], "index"],
pathGroups: [
{
pattern: "react*",
group: "external",
position: "before",
},
{
pattern: "react/**",
group: "external",
position: "before",
},
{
pattern: "react-*",
group: "external",
position: "before",
},
{
pattern: "react-*/**",
group: "external",
position: "before",
},
{
pattern: "~/**",
group: "external",
position: "after",
},
{
pattern: "@/**",
group: "internal",
position: "before",
},
],
pathGroupsExcludedImportTypes: ["builtin"],
alphabetize: {
order: "asc",
},
},
],
"react-refresh/only-export-components": [
"warn",
{
@ -35,4 +86,11 @@ module.exports = {
},
],
},
settings: {
"import/resolver": {
node: {
extensions: [".js", ".jsx", ".ts", ".tsx"],
},
},
},
};

1777
ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -45,6 +45,8 @@
"autoprefixer": "^10.4.20",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-import-resolver-typescript": "^3.7.0",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-react-refresh": "^0.4.16",

View File

@ -1,6 +1,6 @@
import { useEffect, useLayoutEffect, useMemo, useState } from "react";
import { RouterProvider } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { RouterProvider } from "react-router-dom";
import { App, ConfigProvider, theme, type ThemeConfig } from "antd";
import { type Locale } from "antd/es/locale";
import AntdLocaleEnUs from "antd/locale/en_US";
@ -8,9 +8,9 @@ import AntdLocaleZhCN from "antd/locale/zh_CN";
import dayjs from "dayjs";
import "dayjs/locale/zh-cn";
import { localeNames } from "./i18n";
import { useBrowserTheme } from "./hooks";
import { router } from "./router.tsx";
import { useBrowserTheme } from "@/hooks";
import { localeNames } from "@/i18n";
import { router } from "@/router.tsx";
const RootApp = () => {
const { i18n } = useTranslation();

View File

@ -5,12 +5,12 @@ import { Form, Input, type FormInstance } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { useAntdForm } from "@/hooks";
import { ACCESS_PROVIDERS, type AccessModel } from "@/domain/access";
import AccessTypeSelect from "./AccessTypeSelect";
import { useAntdForm } from "@/hooks";
import AccessEditFormACMEHttpReqConfig from "./AccessEditFormACMEHttpReqConfig";
import AccessEditFormAliyunConfig from "./AccessEditFormAliyunConfig";
import AccessEditFormAWSConfig from "./AccessEditFormAWSConfig";
import AccessEditFormAliyunConfig from "./AccessEditFormAliyunConfig";
import AccessEditFormBaiduCloudConfig from "./AccessEditFormBaiduCloudConfig";
import AccessEditFormBytePlusConfig from "./AccessEditFormBytePlusConfig";
import AccessEditFormCloudflareConfig from "./AccessEditFormCloudflareConfig";
@ -27,6 +27,7 @@ import AccessEditFormSSHConfig from "./AccessEditFormSSHConfig";
import AccessEditFormTencentCloudConfig from "./AccessEditFormTencentCloudConfig";
import AccessEditFormVolcEngineConfig from "./AccessEditFormVolcEngineConfig";
import AccessEditFormWebhookConfig from "./AccessEditFormWebhookConfig";
import AccessTypeSelect from "./AccessTypeSelect";
type AccessEditFormFieldValues = Partial<MaybeModelRecord<AccessModel>>;
type AccessEditFormPresets = "add" | "edit";

View File

@ -3,8 +3,8 @@ import { Form, Input, Select, type FormInstance } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { useAntdForm } from "@/hooks";
import { type ACMEHttpReqAccessConfig } from "@/domain/access";
import { useAntdForm } from "@/hooks";
type AccessEditFormACMEHttpReqConfigFieldValues = Partial<ACMEHttpReqAccessConfig>;

View File

@ -3,8 +3,8 @@ import { Form, Input, type FormInstance } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { useAntdForm } from "@/hooks";
import { type AWSAccessConfig } from "@/domain/access";
import { useAntdForm } from "@/hooks";
type AccessEditFormAWSConfigFieldValues = Partial<AWSAccessConfig>;

View File

@ -3,8 +3,8 @@ import { Form, Input, type FormInstance } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { useAntdForm } from "@/hooks";
import { type AliyunAccessConfig } from "@/domain/access";
import { useAntdForm } from "@/hooks";
type AccessEditFormAliyunConfigFieldValues = Partial<AliyunAccessConfig>;

View File

@ -3,8 +3,8 @@ import { Form, Input, type FormInstance } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { useAntdForm } from "@/hooks";
import { type BaiduCloudAccessConfig } from "@/domain/access";
import { useAntdForm } from "@/hooks";
type AccessEditFormBaiduCloudConfigFieldValues = Partial<BaiduCloudAccessConfig>;

View File

@ -3,8 +3,8 @@ import { Form, Input, type FormInstance } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { useAntdForm } from "@/hooks";
import { type BytePlusAccessConfig } from "@/domain/access";
import { useAntdForm } from "@/hooks";
type AccessEditFormBytePlusConfigFieldValues = Partial<BytePlusAccessConfig>;

View File

@ -3,8 +3,8 @@ import { Form, Input, type FormInstance } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { useAntdForm } from "@/hooks";
import { type CloudflareAccessConfig } from "@/domain/access";
import { useAntdForm } from "@/hooks";
type AccessEditFormCloudflareConfigFieldValues = Partial<CloudflareAccessConfig>;

View File

@ -3,8 +3,8 @@ import { Form, Input, type FormInstance } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { useAntdForm } from "@/hooks";
import { type DogeCloudAccessConfig } from "@/domain/access";
import { useAntdForm } from "@/hooks";
type AccessEditFormDogeCloudConfigFieldValues = Partial<DogeCloudAccessConfig>;

View File

@ -3,8 +3,8 @@ import { Form, Input, type FormInstance } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { useAntdForm } from "@/hooks";
import { type GoDaddyAccessConfig } from "@/domain/access";
import { useAntdForm } from "@/hooks";
type AccessEditFormGoDaddyConfigFieldValues = Partial<GoDaddyAccessConfig>;

View File

@ -3,8 +3,8 @@ import { Form, Input, type FormInstance } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { useAntdForm } from "@/hooks";
import { type HuaweiCloudAccessConfig } from "@/domain/access";
import { useAntdForm } from "@/hooks";
type AccessEditFormHuaweiCloudConfigFieldValues = Partial<HuaweiCloudAccessConfig>;

View File

@ -1,14 +1,14 @@
import { useState } from "react";
import { flushSync } from "react-dom";
import { useTranslation } from "react-i18next";
import { UploadOutlined as UploadOutlinedIcon } from "@ant-design/icons";
import { useDeepCompareEffect } from "ahooks";
import { Button, Form, Input, Upload, type FormInstance, type UploadFile, type UploadProps } from "antd";
import { UploadOutlined as UploadOutlinedIcon } from "@ant-design/icons";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { useAntdForm } from "@/hooks";
import { type KubernetesAccessConfig } from "@/domain/access";
import { useAntdForm } from "@/hooks";
import { readFileContent } from "@/utils/file";
type AccessEditFormKubernetesConfigFieldValues = Partial<KubernetesAccessConfig>;

View File

@ -1,7 +1,7 @@
import { Form, type FormInstance } from "antd";
import { useAntdForm } from "@/hooks";
import { type LocalAccessConfig } from "@/domain/access";
import { useAntdForm } from "@/hooks";
type AccessEditFormLocalConfigFieldValues = Partial<LocalAccessConfig>;

View File

@ -3,8 +3,8 @@ import { Form, Input, type FormInstance } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { useAntdForm } from "@/hooks";
import { type NameDotComAccessConfig } from "@/domain/access";
import { useAntdForm } from "@/hooks";
type AccessEditFormNameDotComConfigFieldValues = Partial<NameDotComAccessConfig>;

View File

@ -3,8 +3,8 @@ import { Form, Input, type FormInstance } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { useAntdForm } from "@/hooks";
import { type NameSiloAccessConfig } from "@/domain/access";
import { useAntdForm } from "@/hooks";
type AccessEditFormNameSiloConfigFieldValues = Partial<NameSiloAccessConfig>;

View File

@ -3,8 +3,8 @@ import { Form, Input, type FormInstance } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { useAntdForm } from "@/hooks";
import { type PowerDNSAccessConfig } from "@/domain/access";
import { useAntdForm } from "@/hooks";
type AccessEditFormPowerDNSConfigFieldValues = Partial<PowerDNSAccessConfig>;

View File

@ -3,8 +3,8 @@ import { Form, Input, type FormInstance } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { useAntdForm } from "@/hooks";
import { type QiniuAccessConfig } from "@/domain/access";
import { useAntdForm } from "@/hooks";
type AccessEditFormQiniuConfigFieldValues = Partial<QiniuAccessConfig>;

View File

@ -1,14 +1,14 @@
import { useState } from "react";
import { flushSync } from "react-dom";
import { useTranslation } from "react-i18next";
import { UploadOutlined as UploadOutlinedIcon } from "@ant-design/icons";
import { useDeepCompareEffect } from "ahooks";
import { Button, Form, Input, InputNumber, Upload, type FormInstance, type UploadFile, type UploadProps } from "antd";
import { UploadOutlined as UploadOutlinedIcon } from "@ant-design/icons";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { useAntdForm } from "@/hooks";
import { type SSHAccessConfig } from "@/domain/access";
import { useAntdForm } from "@/hooks";
import { readFileContent } from "@/utils/file";
import { validDomainName, validIPv4Address, validIPv6Address } from "@/utils/validators";

View File

@ -3,8 +3,8 @@ import { Form, Input, type FormInstance } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { useAntdForm } from "@/hooks";
import { type TencentCloudAccessConfig } from "@/domain/access";
import { useAntdForm } from "@/hooks";
type AccessEditFormTencentCloudConfigFieldValues = Partial<TencentCloudAccessConfig>;

View File

@ -3,8 +3,8 @@ import { Form, Input, type FormInstance } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { useAntdForm } from "@/hooks";
import { type VolcEngineAccessConfig } from "@/domain/access";
import { useAntdForm } from "@/hooks";
type AccessEditFormVolcEngineConfigFieldValues = Partial<VolcEngineAccessConfig>;

View File

@ -3,8 +3,8 @@ import { Form, Input, type FormInstance } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { useAntdForm } from "@/hooks";
import { type WebhookAccessConfig } from "@/domain/access";
import { useAntdForm } from "@/hooks";
type AccessEditFormWebhookConfigFieldValues = Partial<WebhookAccessConfig>;

View File

@ -3,10 +3,11 @@ import { useTranslation } from "react-i18next";
import { useControllableValue } from "ahooks";
import { Modal, notification } from "antd";
import { useTriggerElement } from "@/hooks";
import { type AccessModel } from "@/domain/access";
import { useTriggerElement } from "@/hooks";
import { useAccessStore } from "@/stores/access";
import { getErrMsg } from "@/utils/error";
import AccessEditForm, { type AccessEditFormInstance, type AccessEditFormProps } from "./AccessEditForm";
export type AccessEditModalProps = {

View File

@ -1,7 +1,7 @@
import { useTranslation } from "react-i18next";
import { Button, Dropdown, Form, Input, message, Space, Tooltip } from "antd";
import { CopyOutlined as CopyOutlinedIcon, DownOutlined as DownOutlinedIcon, LikeOutlined as LikeOutlinedIcon } from "@ant-design/icons";
import { CopyToClipboard } from "react-copy-to-clipboard";
import { useTranslation } from "react-i18next";
import { CopyOutlined as CopyOutlinedIcon, DownOutlined as DownOutlinedIcon, LikeOutlined as LikeOutlinedIcon } from "@ant-design/icons";
import { Button, Dropdown, Form, Input, message, Space, Tooltip } from "antd";
import dayjs from "dayjs";
import { type CertificateModel } from "@/domain/certificate";

View File

@ -2,9 +2,9 @@ import { useControllableValue } from "ahooks";
import { Drawer } from "antd";
import Show from "@/components/Show";
import CertificateDetail from "./CertificateDetail";
import { useTriggerElement } from "@/hooks";
import { type CertificateModel } from "@/domain/certificate";
import { useTriggerElement } from "@/hooks";
import CertificateDetail from "./CertificateDetail";
export type CertificateDetailDrawerProps = {
data?: CertificateModel;

View File

@ -1,13 +1,13 @@
import { forwardRef, useImperativeHandle, useMemo, useRef, type ChangeEvent } from "react";
import { useTranslation } from "react-i18next";
import { useControllableValue } from "ahooks";
import { Button, Input, Space, type InputRef, type InputProps } from "antd";
import {
ArrowDownOutlined as ArrowDownOutlinedIcon,
ArrowUpOutlined as ArrowUpOutlinedIcon,
MinusOutlined as MinusOutlinedIcon,
PlusOutlined as PlusOutlinedIcon,
} from "@ant-design/icons";
import { useControllableValue } from "ahooks";
import { Button, Input, Space, type InputRef, type InputProps } from "antd";
import { produce } from "immer";
export type MultipleInputProps = Omit<InputProps, "count" | "defaultValue" | "showCount" | "value" | "onChange" | "onPressEnter" | "onClear"> & {

View File

@ -1,6 +1,6 @@
import { useTranslation } from "react-i18next";
import { Divider, Space, Typography } from "antd";
import { BookOutlined as BookOutlinedIcon } from "@ant-design/icons";
import { Divider, Space, Typography } from "antd";
import { version } from "@/domain/version";

View File

@ -2,16 +2,17 @@ import { forwardRef, useImperativeHandle, useMemo } from "react";
import { useCreation } from "ahooks";
import { Form, type FormInstance } from "antd";
import { useAntdForm } from "@/hooks";
import { NOTIFY_CHANNELS, type NotifyChannelsSettingsContent } from "@/domain/settings";
import { useAntdForm } from "@/hooks";
import NotifyChannelEditFormBarkFields from "./NotifyChannelEditFormBarkFields";
import NotifyChannelEditFormDingTalkFields from "./NotifyChannelEditFormDingTalkFields";
import NotifyChannelEditFormEmailFields from "./NotifyChannelEditFormEmailFields";
import NotifyChannelEditFormLarkFields from "./NotifyChannelEditFormLarkFields";
import NotifyChannelEditFormServerChanFields from "./NotifyChannelEditFormServerChanFields";
import NotifyChannelEditFormTelegramFields from "./NotifyChannelEditFormTelegramFields";
import NotifyChannelEditFormWebhookFields from "./NotifyChannelEditFormWebhookFields";
import NotifyChannelEditFormWeComFields from "./NotifyChannelEditFormWeComFields";
import NotifyChannelEditFormWebhookFields from "./NotifyChannelEditFormWebhookFields";
type NotifyChannelEditFormFieldValues = NotifyChannelsSettingsContent[keyof NotifyChannelsSettingsContent];

View File

@ -4,12 +4,13 @@ import { useDeepCompareMemo } from "@ant-design/pro-components";
import { Button, Collapse, message, notification, Skeleton, Space, Switch, type CollapseProps } from "antd";
import Show from "@/components/Show";
import NotifyChannelEditForm, { type NotifyChannelEditFormInstance } from "./NotifyChannelEditForm";
import NotifyTestButton from "./NotifyTestButton";
import { notifyChannelsMap } from "@/domain/settings";
import { useNotifyChannelStore } from "@/stores/notify";
import { getErrMsg } from "@/utils/error";
import NotifyChannelEditForm, { type NotifyChannelEditFormInstance } from "./NotifyChannelEditForm";
import NotifyTestButton from "./NotifyTestButton";
type NotifyChannelProps = {
className?: string;
style?: React.CSSProperties;

View File

@ -7,8 +7,8 @@ import { ClientResponseError } from "pocketbase";
import { z } from "zod";
import Show from "@/components/Show";
import { useAntdForm } from "@/hooks";
import { defaultNotifyTemplate, SETTINGS_NAMES, type NotifyTemplatesSettingsContent } from "@/domain/settings";
import { useAntdForm } from "@/hooks";
import { get as getSettings, save as saveSettings } from "@/repository/settings";
import { getErrMsg } from "@/utils/error";

View File

@ -1,5 +1,5 @@
import { useRequest } from "ahooks";
import { useTranslation } from "react-i18next";
import { useRequest } from "ahooks";
import { Button, message, notification, type ButtonProps } from "antd";
import { notifyTest } from "@/api/notify";

View File

@ -1,11 +1,12 @@
import { Dropdown } from "antd";
import { PlusOutlined as PlusOutlinedIcon } from "@ant-design/icons";
import { Dropdown } from "antd";
import { useZustandShallowSelector } from "@/hooks";
import { newWorkflowNode, workflowNodeDropdownList, WorkflowNodeType } from "@/domain/workflow";
import { useZustandShallowSelector } from "@/hooks";
import { useWorkflowStore } from "@/stores/workflow";
import { type BrandNodeProps, type NodeProps } from "./types";
import DropdownMenuItemIcon from "./DropdownMenuItemIcon";
import { type BrandNodeProps, type NodeProps } from "./types";
const AddNode = ({ data }: NodeProps | BrandNodeProps) => {
const { addNode } = useWorkflowStore(useZustandShallowSelector(["addNode"]));

View File

@ -2,11 +2,12 @@ import { memo } from "react";
import { useTranslation } from "react-i18next";
import { Button } from "antd";
import { type WorkflowBranchNode, type WorkflowNode } from "@/domain/workflow";
import { useZustandShallowSelector } from "@/hooks";
import { useWorkflowStore } from "@/stores/workflow";
import AddNode from "./AddNode";
import NodeRender from "./NodeRender";
import { useZustandShallowSelector } from "@/hooks";
import { type WorkflowBranchNode, type WorkflowNode } from "@/domain/workflow";
import { useWorkflowStore } from "@/stores/workflow";
import { type BrandNodeProps } from "./types";
const BranchNode = memo(({ data }: BrandNodeProps) => {

View File

@ -1,9 +1,10 @@
import { Dropdown } from "antd";
import { DeleteOutlined as DeleteOutlinedIcon, EllipsisOutlined as EllipsisOutlinedIcon } from "@ant-design/icons";
import { Dropdown } from "antd";
import AddNode from "./AddNode";
import { useZustandShallowSelector } from "@/hooks";
import { useWorkflowStore } from "@/stores/workflow";
import AddNode from "./AddNode";
import { type NodeProps } from "./types";
const ConditionNode = ({ data, branchId, branchIndex }: NodeProps) => {

View File

@ -2,10 +2,11 @@ import { memo, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import Show from "@/components/Show";
import DeployNodeForm from "./node/DeployNodeForm";
import { deployProvidersMap } from "@/domain/provider";
import { type WorkflowNode } from "@/domain/workflow";
import DeployNodeForm from "./node/DeployNodeForm";
type DeployPanelBodyProps = {
data: WorkflowNode;
};

View File

@ -1,5 +1,5 @@
import { type WorkflowNodeDropdwonItemIcon, WorkflowNodeDropdwonItemIconType } from "@/domain/workflow";
import { CloudUpload, GitFork, Megaphone, NotebookPen } from "lucide-react";
import { type WorkflowNodeDropdwonItemIcon, WorkflowNodeDropdwonItemIconType } from "@/domain/workflow";
const icons = new Map([
["NotebookPen", <NotebookPen size={16} />],

View File

@ -1,17 +1,18 @@
import { useTranslation } from "react-i18next";
import { Dropdown } from "antd";
import { DeleteOutlined as DeleteOutlinedIcon, EllipsisOutlined as EllipsisOutlinedIcon } from "@ant-design/icons";
import { Dropdown } from "antd";
import Show from "@/components/Show";
import AddNode from "./AddNode";
import { usePanel } from "./PanelProvider";
import PanelBody from "./PanelBody";
import { useZustandShallowSelector } from "@/hooks";
import { deployProvidersMap } from "@/domain/provider";
import { notifyChannelsMap } from "@/domain/settings";
import { type WorkflowNode, WorkflowNodeType } from "@/domain/workflow";
import { useZustandShallowSelector } from "@/hooks";
import { useWorkflowStore } from "@/stores/workflow";
import AddNode from "./AddNode";
import PanelBody from "./PanelBody";
import { usePanel } from "./PanelProvider";
type NodeProps = {
data: WorkflowNode;
};

View File

@ -1,9 +1,11 @@
import { memo } from "react";
import { WorkflowBranchNode, WorkflowNode, WorkflowNodeType } from "@/domain/workflow";
import Node from "./Node";
import End from "./End";
import BranchNode from "./BranchNode";
import ConditionNode from "./ConditionNode";
import End from "./End";
import Node from "./Node";
import { NodeProps } from "./types";
const NodeRender = memo(({ data, branchId, branchIndex }: NodeProps) => {

View File

@ -1,6 +1,7 @@
import { WorkflowNodeType } from "@/domain/workflow";
import { CloudUpload, GitFork, Megaphone, NotebookPen } from "lucide-react";
import { WorkflowNodeType } from "@/domain/workflow";
type NodeTypesPanelProps = {
onTypeSelected: (type: WorkflowNodeType) => void;
};

View File

@ -1,8 +1,9 @@
import { WorkflowNode, WorkflowNodeType } from "@/domain/workflow";
import StartNodeForm from "./node/StartNodeForm";
import DeployPanelBody from "./DeployPanelBody";
import ApplyNodeForm from "./node/ApplyNodeForm";
import NotifyNodeForm from "./node/NotifyNodeForm";
import StartNodeForm from "./node/StartNodeForm";
type PanelBodyProps = {
data: WorkflowNode;

View File

@ -1,5 +1,5 @@
// contexts/DialogContext.tsx
import { createContext, useContext, useState } from "react";
import Panel from "./Panel";
type PanelContentProps = { name: string; children: React.ReactNode };

View File

@ -1,24 +1,24 @@
import { memo, useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { FormOutlined as FormOutlinedIcon, PlusOutlined as PlusOutlinedIcon, QuestionCircleOutlined as QuestionCircleOutlinedIcon } from "@ant-design/icons";
import { useControllableValue } from "ahooks";
import { AutoComplete, Button, Divider, Form, Input, Select, Space, Switch, Tooltip, Typography, type AutoCompleteProps } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { FormOutlined as FormOutlinedIcon, PlusOutlined as PlusOutlinedIcon, QuestionCircleOutlined as QuestionCircleOutlinedIcon } from "@ant-design/icons";
import { produce } from "immer";
import z from "zod";
import { z } from "zod";
import AccessEditModal from "@/components/access/AccessEditModal";
import AccessSelect from "@/components/access/AccessSelect";
import ModalForm from "@/components/core/ModalForm";
import MultipleInput from "@/components/core/MultipleInput";
import { usePanel } from "../PanelProvider";
import { useAntdForm, useZustandShallowSelector } from "@/hooks";
import { ACCESS_USAGES } from "@/domain/access";
import { accessProvidersMap } from "@/domain/provider";
import { type WorkflowNode, type WorkflowNodeConfig } from "@/domain/workflow";
import { useAntdForm, useZustandShallowSelector } from "@/hooks";
import { useContactStore } from "@/stores/contact";
import { useWorkflowStore } from "@/stores/workflow";
import { validDomainName, validIPv4Address, validIPv6Address } from "@/utils/validators";
import { usePanel } from "../PanelProvider";
export type ApplyNodeFormProps = {
data: WorkflowNode;

View File

@ -1,17 +1,22 @@
import { memo, useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { PlusOutlined as PlusOutlinedIcon, QuestionCircleOutlined as QuestionCircleOutlinedIcon } from "@ant-design/icons";
import { Avatar, Button, Divider, Form, Select, Space, Tooltip, Typography } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { PlusOutlined as PlusOutlinedIcon, QuestionCircleOutlined as QuestionCircleOutlinedIcon } from "@ant-design/icons";
import { produce } from "immer";
import z from "zod";
import { z } from "zod";
import AccessEditModal from "@/components/access/AccessEditModal";
import AccessSelect from "@/components/access/AccessSelect";
import { ACCESS_USAGES } from "@/domain/access";
import { accessProvidersMap, deployProvidersMap } from "@/domain/provider";
import { type WorkflowNode, type WorkflowNodeConfig } from "@/domain/workflow";
import { useAntdForm, useZustandShallowSelector } from "@/hooks";
import { useWorkflowStore } from "@/stores/workflow";
import { usePanel } from "../PanelProvider";
import DeployNodeFormAliyunALBFields from "./DeployNodeFormAliyunALBFields";
import DeployNodeFormAliyunCLBFields from "./DeployNodeFormAliyunCLBFields";
import DeployNodeFormAliyunCDNFields from "./DeployNodeFormAliyunCDNFields";
import DeployNodeFormAliyunCLBFields from "./DeployNodeFormAliyunCLBFields";
import DeployNodeFormAliyunDCDNFields from "./DeployNodeFormAliyunDCDNFields";
import DeployNodeFormAliyunNLBFields from "./DeployNodeFormAliyunNLBFields";
import DeployNodeFormAliyunOSSFields from "./DeployNodeFormAliyunOSSFields";
@ -32,11 +37,6 @@ import DeployNodeFormTencentCloudEOFields from "./DeployNodeFormTencentCloudEOFi
import DeployNodeFormVolcEngineCDNFields from "./DeployNodeFormVolcEngineCDNFields";
import DeployNodeFormVolcEngineLiveFields from "./DeployNodeFormVolcEngineLiveFields";
import DeployNodeFormWebhookFields from "./DeployNodeFormWebhookFields";
import { useAntdForm, useZustandShallowSelector } from "@/hooks";
import { ACCESS_USAGES } from "@/domain/access";
import { accessProvidersMap, deployProvidersMap } from "@/domain/provider";
import { type WorkflowNode, type WorkflowNodeConfig } from "@/domain/workflow";
import { useWorkflowStore } from "@/stores/workflow";
export type DeployFormProps = {
data: WorkflowNode;

View File

@ -1,7 +1,7 @@
import { useTranslation } from "react-i18next";
import { DownOutlined as DownOutlinedIcon } from "@ant-design/icons";
import { Button, Dropdown, Form, Input, Select } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { DownOutlined as DownOutlinedIcon } from "@ant-design/icons";
import { z } from "zod";
import Show from "@/components/Show";

View File

@ -1,7 +1,7 @@
import { useTranslation } from "react-i18next";
import { DownOutlined as DownOutlinedIcon } from "@ant-design/icons";
import { Button, Dropdown, Form, Input, Select } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { DownOutlined as DownOutlinedIcon } from "@ant-design/icons";
import { z } from "zod";
import Show from "@/components/Show";

View File

@ -1,18 +1,18 @@
import { memo, useEffect } from "react";
import { Link } from "react-router";
import { useTranslation } from "react-i18next";
import { Link } from "react-router";
import { RightOutlined as RightOutlinedIcon } from "@ant-design/icons";
import { Button, Form, Input, Select } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { RightOutlined as RightOutlinedIcon } from "@ant-design/icons";
import { produce } from "immer";
import { z } from "zod";
import { usePanel } from "../PanelProvider";
import { useAntdForm, useZustandShallowSelector } from "@/hooks";
import { notifyChannelsMap } from "@/domain/settings";
import { type WorkflowNode, type WorkflowNodeConfig } from "@/domain/workflow";
import { useAntdForm, useZustandShallowSelector } from "@/hooks";
import { useNotifyChannelStore } from "@/stores/notify";
import { useWorkflowStore } from "@/stores/workflow";
import { usePanel } from "../PanelProvider";
export type NotifyNodeFormProps = {
data: WorkflowNode;

View File

@ -7,11 +7,11 @@ import { produce } from "immer";
import { z } from "zod";
import Show from "@/components/Show";
import { usePanel } from "../PanelProvider";
import { useAntdForm, useZustandShallowSelector } from "@/hooks";
import { type WorkflowNode, type WorkflowNodeConfig } from "@/domain/workflow";
import { useAntdForm, useZustandShallowSelector } from "@/hooks";
import { useWorkflowStore } from "@/stores/workflow";
import { validCronExpression, getNextCronExecutions } from "@/utils/cron";
import { usePanel } from "../PanelProvider";
export type StartNodeFormProps = {
data: WorkflowNode;

View File

@ -3,8 +3,8 @@ import { useControllableValue } from "ahooks";
import { Alert, Drawer, Typography } from "antd";
import Show from "@/components/Show";
import { useTriggerElement } from "@/hooks";
import { type WorkflowRunModel } from "@/domain/workflowRun";
import { useTriggerElement } from "@/hooks";
export type WorkflowRunDetailDrawerProps = {
data?: WorkflowRunModel;

View File

@ -1,18 +1,18 @@
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { useRequest } from "ahooks";
import { Button, Empty, notification, Space, Table, theme, Typography, type TableProps } from "antd";
import {
CheckCircleOutlined as CheckCircleOutlinedIcon,
CloseCircleOutlined as CloseCircleOutlinedIcon,
SelectOutlined as SelectOutlinedIcon,
} from "@ant-design/icons";
import { useRequest } from "ahooks";
import { Button, Empty, notification, Space, Table, theme, Typography, type TableProps } from "antd";
import { ClientResponseError } from "pocketbase";
import WorkflowRunDetailDrawer from "./WorkflowRunDetailDrawer";
import { type WorkflowRunModel } from "@/domain/workflowRun";
import { list as listWorkflowRuns } from "@/repository/workflowRun";
import { getErrMsg } from "@/utils/error";
import WorkflowRunDetailDrawer from "./WorkflowRunDetailDrawer";
export type WorkflowRunsProps = {
className?: string;

View File

@ -1,4 +1,4 @@
import { WorkflowBranchNode, WorkflowNode } from "@/domain/workflow";
import { type WorkflowBranchNode, type WorkflowNode } from "@/domain/workflow";
export type NodeProps = {
data: WorkflowNode | WorkflowBranchNode;

View File

@ -1,6 +1,6 @@
import { useState } from "react";
import { Form, type FormInstance, type FormProps } from "antd";
import { useDeepCompareEffect } from "ahooks";
import { Form, type FormInstance, type FormProps } from "antd";
export interface UseAntdFormOptions<T extends NonNullable<unknown> = any> {
form?: FormInstance<T>;

View File

@ -1,6 +1,5 @@
import { pick, isArray } from "radash";
import { useRef } from "react";
import { useRef } from "react";
import { pick, isArray } from "radash";
import { shallow } from "zustand/shallow";
type MaybeMany<T> = T | readonly T[];

View File

@ -1,5 +1,6 @@
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import i18n from "i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import resources, { LOCALE_ZH_NAME, LOCALE_EN_NAME } from "./locales";

View File

@ -1,12 +1,12 @@
import nlsCommon from "./nls.common.json";
import nlsLogin from "./nls.login.json";
import nlsAccess from "./nls.access.json";
import nlsCertificate from "./nls.certificate.json";
import nlsCommon from "./nls.common.json";
import nlsDashboard from "./nls.dashboard.json";
import nlsLogin from "./nls.login.json";
import nlsSettings from "./nls.settings.json";
import nlsAccess from "./nls.access.json";
import nlsWorkflow from "./nls.workflow.json";
import nlsWorkflowNodes from "./nls.workflow.nodes.json";
import nlsWorkflowRuns from "./nls.workflow.runs.json";
import nlsCertificate from "./nls.certificate.json";
export default Object.freeze({
...nlsCommon,

View File

@ -1,7 +1,7 @@
import { Resource } from "i18next";
import zh from "./zh";
import en from "./en";
import zh from "./zh";
export const LOCALE_ZH_NAME = "zh" as const;
export const LOCALE_EN_NAME = "en" as const;

View File

@ -1,12 +1,12 @@
import nlsCommon from "./nls.common.json";
import nlsLogin from "./nls.login.json";
import nlsAccess from "./nls.access.json";
import nlsCertificate from "./nls.certificate.json";
import nlsCommon from "./nls.common.json";
import nlsDashboard from "./nls.dashboard.json";
import nlsLogin from "./nls.login.json";
import nlsSettings from "./nls.settings.json";
import nlsAccess from "./nls.access.json";
import nlsWorkflow from "./nls.workflow.json";
import nlsWorkflowNodes from "./nls.workflow.nodes.json";
import nlsWorkflowRuns from "./nls.workflow.runs.json";
import nlsCertificate from "./nls.certificate.json";
export default Object.freeze({
...nlsCommon,

View File

@ -1,7 +1,6 @@
import { memo, useEffect, useState } from "react";
import { Link, Navigate, Outlet, useLocation, useNavigate } from "react-router-dom";
import { memo, useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { Button, Drawer, Dropdown, Layout, Menu, Tooltip, theme, type ButtonProps, type MenuProps } from "antd";
import { Link, Navigate, Outlet, useLocation, useNavigate } from "react-router-dom";
import {
CloudServerOutlined as CloudServerOutlinedIcon,
GlobalOutlined as GlobalOutlinedIcon,
@ -14,6 +13,7 @@ import {
SettingOutlined as SettingOutlinedIcon,
SunOutlined as SunOutlinedIcon,
} from "@ant-design/icons";
import { Button, Drawer, Dropdown, Layout, Menu, Tooltip, theme, type ButtonProps, type MenuProps } from "antd";
import Version from "@/components/core/Version";
import { useBrowserTheme } from "@/hooks";
@ -135,12 +135,12 @@ const SiderMenu = memo(({ onSelect }: { onSelect?: (key: string) => void }) => {
});
const [menuSelectedKey, setMenuSelectedKey] = useState<string>();
const getActiveMenuItem = () => {
const getActiveMenuItem = useCallback(() => {
const item =
menuItems.find((item) => item!.key === location.pathname) ??
menuItems.find((item) => item!.key !== MENU_KEY_HOME && location.pathname.startsWith(item!.key as string));
return item;
};
}, [location.pathname, menuItems]);
useEffect(() => {
const item = getActiveMenuItem();
@ -149,13 +149,13 @@ const SiderMenu = memo(({ onSelect }: { onSelect?: (key: string) => void }) => {
} else {
setMenuSelectedKey(undefined);
}
}, [location.pathname]);
}, [location.pathname, getActiveMenuItem]);
useEffect(() => {
if (menuSelectedKey && menuSelectedKey !== getActiveMenuItem()?.key) {
navigate(menuSelectedKey);
}
}, [menuSelectedKey]);
}, [menuSelectedKey, navigate, getActiveMenuItem]);
return (
<>

View File

@ -1,14 +1,14 @@
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { useRequest } from "ahooks";
import { Avatar, Button, Empty, Modal, notification, Space, Table, Tooltip, Typography, type TableProps } from "antd";
import { PageHeader } from "@ant-design/pro-components";
import {
DeleteOutlined as DeleteOutlinedIcon,
EditOutlined as EditOutlinedIcon,
PlusOutlined as PlusOutlinedIcon,
SnippetsOutlined as SnippetsOutlinedIcon,
} from "@ant-design/icons";
import { PageHeader } from "@ant-design/pro-components";
import { useRequest } from "ahooks";
import { Avatar, Button, Empty, Modal, notification, Space, Table, Tooltip, Typography, type TableProps } from "antd";
import dayjs from "dayjs";
import { ClientResponseError } from "pocketbase";
@ -125,7 +125,7 @@ const AccessList = () => {
console.error(err);
notificationApi.error({ message: t("common.text.request_error"), description: getErrMsg(err) });
});
}, []);
}, [fetchAccesses]);
const { loading } = useRequest(
() => {

View File

@ -1,10 +1,10 @@
import { useState } from "react";
import { useNavigate, useSearchParams } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { useNavigate, useSearchParams } from "react-router-dom";
import { DeleteOutlined as DeleteOutlinedIcon, SelectOutlined as SelectOutlinedIcon } from "@ant-design/icons";
import { PageHeader } from "@ant-design/pro-components";
import { useRequest } from "ahooks";
import { Button, Divider, Empty, Menu, notification, Radio, Space, Table, theme, Tooltip, Typography, type MenuProps, type TableProps } from "antd";
import { PageHeader } from "@ant-design/pro-components";
import { DeleteOutlined as DeleteOutlinedIcon, SelectOutlined as SelectOutlinedIcon } from "@ant-design/icons";
import dayjs from "dayjs";
import { ClientResponseError } from "pocketbase";

View File

@ -1,9 +1,9 @@
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import { PageHeader } from "@ant-design/pro-components";
import { useRequest } from "ahooks";
import { Card, Col, Divider, notification, Row, Space, Statistic, theme, Typography } from "antd";
import { PageHeader } from "@ant-design/pro-components";
import {
CalendarClock as CalendarClockIcon,
CalendarX2 as CalendarX2Icon,
@ -13,8 +13,8 @@ import {
} from "lucide-react";
import { ClientResponseError } from "pocketbase";
import { type Statistics } from "@/domain/statistics";
import { get as getStatistics } from "@/api/statistics";
import { type Statistics } from "@/domain/statistics";
import { getErrMsg } from "@/utils/error";
const Dashboard = () => {

View File

@ -1,5 +1,5 @@
import { useNavigate } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import { Button, Card, Form, Input, notification } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";

View File

@ -1,14 +1,14 @@
import { useEffect, useState } from "react";
import { Outlet, useLocation, useNavigate } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { Card, Space } from "antd";
import { PageHeader } from "@ant-design/pro-components";
import { Outlet, useLocation, useNavigate } from "react-router-dom";
import {
ApiOutlined as ApiOutlinedIcon,
LockOutlined as LockOutlinedIcon,
SendOutlined as SendOutlinedIcon,
UserOutlined as UserOutlinedIcon,
} from "@ant-design/icons";
import { PageHeader } from "@ant-design/pro-components";
import { Card, Space } from "antd";
const Settings = () => {
const location = useLocation();
@ -25,7 +25,7 @@ const Settings = () => {
}
setTabValue(path);
}, [location]);
}, [location, navigate]);
return (
<div className="p-4">

View File

@ -1,6 +1,6 @@
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import { Button, Form, Input, message, notification } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";

View File

@ -1,6 +1,6 @@
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import { Button, Form, Input, message, notification } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";

View File

@ -1,15 +1,15 @@
import { createContext, useContext, useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { CheckCard } from "@ant-design/pro-components";
import { useDeepCompareEffect } from "ahooks";
import { Button, Form, Input, message, notification, Skeleton } from "antd";
import { CheckCard } from "@ant-design/pro-components";
import { createSchemaFieldRule } from "antd-zod";
import { produce } from "immer";
import { z } from "zod";
import Show from "@/components/Show";
import { useAntdForm } from "@/hooks";
import { SETTINGS_NAMES, SSLPROVIDERS, type SettingsModel, type SSLProviderSettingsContent, type SSLProviders } from "@/domain/settings";
import { useAntdForm } from "@/hooks";
import { get as getSettings, save as saveSettings } from "@/repository/settings";
import { getErrMsg } from "@/utils/error";

View File

@ -1,31 +1,31 @@
import { useEffect, useMemo, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { useDeepCompareEffect } from "ahooks";
import { Button, Card, Dropdown, Form, Input, message, Modal, notification, Space, Tabs, Typography } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { PageHeader } from "@ant-design/pro-components";
import { useNavigate, useParams } from "react-router-dom";
import {
CaretRightOutlined as CaretRightOutlinedIcon,
DeleteOutlined as DeleteOutlinedIcon,
EllipsisOutlined as EllipsisOutlinedIcon,
UndoOutlined as UndoOutlinedIcon,
} from "@ant-design/icons";
import { PageHeader } from "@ant-design/pro-components";
import { useDeepCompareEffect } from "ahooks";
import { Button, Card, Dropdown, Form, Input, message, Modal, notification, Space, Tabs, Typography } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { ClientResponseError } from "pocketbase";
import { isEqual } from "radash";
import { z } from "zod";
import { run as runWorkflow } from "@/api/workflow";
import Show from "@/components/Show";
import ModalForm from "@/components/core/ModalForm";
import End from "@/components/workflow/End";
import NodeRender from "@/components/workflow/NodeRender";
import WorkflowRuns from "@/components/workflow/run/WorkflowRuns";
import WorkflowProvider from "@/components/workflow/WorkflowProvider";
import { useAntdForm, useZustandShallowSelector } from "@/hooks";
import WorkflowRuns from "@/components/workflow/run/WorkflowRuns";
import { isAllNodesValidated, type WorkflowModel, type WorkflowNode } from "@/domain/workflow";
import { useWorkflowStore } from "@/stores/workflow";
import { useAntdForm, useZustandShallowSelector } from "@/hooks";
import { remove as removeWorkflow } from "@/repository/workflow";
import { run as runWorkflow } from "@/api/workflow";
import { useWorkflowStore } from "@/stores/workflow";
import { getErrMsg } from "@/utils/error";
const WorkflowDetail = () => {

View File

@ -1,6 +1,8 @@
import { useState } from "react";
import { useNavigate, useSearchParams } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { useNavigate, useSearchParams } from "react-router-dom";
import { DeleteOutlined as DeleteOutlinedIcon, EditOutlined as EditOutlinedIcon, PlusOutlined as PlusOutlinedIcon } from "@ant-design/icons";
import { PageHeader } from "@ant-design/pro-components";
import { useRequest } from "ahooks";
import {
Button,
@ -20,8 +22,6 @@ import {
type MenuProps,
type TableProps,
} from "antd";
import { PageHeader } from "@ant-design/pro-components";
import { DeleteOutlined as DeleteOutlinedIcon, EditOutlined as EditOutlinedIcon, PlusOutlined as PlusOutlinedIcon } from "@ant-design/icons";
import dayjs from "dayjs";
import { ClientResponseError } from "pocketbase";

View File

@ -1,4 +1,5 @@
import { type WorkflowRunModel } from "@/domain/workflowRun";
import { getPocketBase } from "./pocketbase";
const COLLECTION_NAME = "workflow_run_log";

View File

@ -2,17 +2,17 @@ import { createHashRouter } from "react-router-dom";
import AuthLayout from "./pages/AuthLayout";
import ConsoleLayout from "./pages/ConsoleLayout";
import Login from "./pages/login/Login";
import Dashboard from "./pages/dashboard/Dashboard";
import AccessList from "./pages/accesses/AccessList";
import WorkflowList from "./pages/workflows/WorkflowList";
import WorkflowDetail from "./pages/workflows/WorkflowDetail";
import CertificateList from "./pages/certificates/CertificateList";
import Dashboard from "./pages/dashboard/Dashboard";
import Login from "./pages/login/Login";
import Settings from "./pages/settings/Settings";
import SettingsAccount from "./pages/settings/SettingsAccount";
import SettingsPassword from "./pages/settings/SettingsPassword";
import SettingsNotification from "./pages/settings/SettingsNotification";
import SettingsPassword from "./pages/settings/SettingsPassword";
import SettingsSSLProvider from "./pages/settings/SettingsSSLProvider";
import WorkflowDetail from "./pages/workflows/WorkflowDetail";
import WorkflowList from "./pages/workflows/WorkflowList";
export const router = createHashRouter([
{

View File

@ -1,5 +1,5 @@
import { create } from "zustand";
import { produce } from "immer";
import { produce } from "immer";
import { create } from "zustand";
import { type AccessModel } from "@/domain/access";
import { list as listAccess, save as saveAccess, remove as removeAccess } from "@/repository/access";

View File

@ -1,5 +1,5 @@
import { create } from "zustand";
import { produce } from "immer";
import { produce } from "immer";
import { create } from "zustand";
import { SETTINGS_NAMES, type EmailsSettingsContent, type SettingsModel } from "@/domain/settings";
import { get as getSettings, save as saveSettings } from "@/repository/settings";

View File

@ -1,5 +1,5 @@
import { create } from "zustand";
import { produce } from "immer";
import { produce } from "immer";
import { create } from "zustand";
import { SETTINGS_NAMES, type NotifyChannelsSettingsContent, type SettingsModel } from "@/domain/settings";
import { get as getSettings, save as saveSettings } from "@/repository/settings";

View File

@ -12,7 +12,7 @@
"baseUrl": ".",
"paths": {
"@/*": [
"./src/*"
"src/*"
]
}
}

View File

@ -1,7 +1,8 @@
import path from "node:path";
import fs from "fs-extra";
import legacyPlugin from "@vitejs/plugin-legacy";
import reactPlugin from "@vitejs/plugin-react";
import fs from "fs-extra";
import { defineConfig, type Plugin } from "vite";
const preserveFilesPlugin = (filesToPreserve: string[]): Plugin => {