prettier and build form optimization

This commit is contained in:
André Ferreira 2025-03-28 17:31:53 +00:00
parent e0433e9f7b
commit 0114b371f5
5 changed files with 260 additions and 209 deletions

View File

@ -0,0 +1,9 @@
node_modules
.next
.docker
coverage
.prettierignore
.stylelintignore
.eslintignore
*.log
docs

5
apps/dokploy/.prettierrc Normal file
View File

@ -0,0 +1,5 @@
{
"plugins": ["prettier-plugin-tailwindcss"],
"useTabs": true,
"printWidth": 80
}

View File

@ -20,7 +20,7 @@ import { useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
import { z } from "zod"; import { z } from "zod";
enum BuildType { export enum BuildType {
dockerfile = "dockerfile", dockerfile = "dockerfile",
heroku_buildpacks = "heroku_buildpacks", heroku_buildpacks = "heroku_buildpacks",
paketo_buildpacks = "paketo_buildpacks", paketo_buildpacks = "paketo_buildpacks",
@ -29,9 +29,18 @@ enum BuildType {
railpack = "railpack", railpack = "railpack",
} }
const buildTypeDisplayMap: Record<BuildType, string> = {
[BuildType.dockerfile]: "Dockerfile",
[BuildType.railpack]: "Railpack",
[BuildType.nixpacks]: "Nixpacks",
[BuildType.heroku_buildpacks]: "Heroku Buildpacks",
[BuildType.paketo_buildpacks]: "Paketo Buildpacks",
[BuildType.static]: "Static",
};
const mySchema = z.discriminatedUnion("buildType", [ const mySchema = z.discriminatedUnion("buildType", [
z.object({ z.object({
buildType: z.literal("dockerfile"), buildType: z.literal(BuildType.dockerfile),
dockerfile: z dockerfile: z
.string({ .string({
required_error: "Dockerfile path is required", required_error: "Dockerfile path is required",
@ -42,18 +51,18 @@ const mySchema = z.discriminatedUnion("buildType", [
dockerBuildStage: z.string().nullable().default(""), dockerBuildStage: z.string().nullable().default(""),
}), }),
z.object({ z.object({
buildType: z.literal("heroku_buildpacks"), buildType: z.literal(BuildType.heroku_buildpacks),
herokuVersion: z.string().nullable().default(""), herokuVersion: z.string().nullable().default(""),
}), }),
z.object({ z.object({
buildType: z.literal("paketo_buildpacks"), buildType: z.literal(BuildType.paketo_buildpacks),
}), }),
z.object({ z.object({
buildType: z.literal("nixpacks"), buildType: z.literal(BuildType.nixpacks),
publishDirectory: z.string().optional(), publishDirectory: z.string().optional(),
}), }),
z.object({ z.object({
buildType: z.literal("static"), buildType: z.literal(BuildType.static),
}), }),
z.object({ z.object({
buildType: z.literal("railpack"), buildType: z.literal("railpack"),
@ -61,20 +70,39 @@ const mySchema = z.discriminatedUnion("buildType", [
]); ]);
type AddTemplate = z.infer<typeof mySchema>; type AddTemplate = z.infer<typeof mySchema>;
interface Props { interface Props {
applicationId: string; applicationId: string;
} }
const resetData = (data: any): AddTemplate => {
switch (data.buildType) {
case BuildType.dockerfile:
return {
buildType: BuildType.dockerfile,
dockerfile: data.dockerfile || "",
dockerContextPath: data.dockerContextPath || "",
dockerBuildStage: data.dockerBuildStage || "",
};
case BuildType.heroku_buildpacks:
return {
buildType: BuildType.heroku_buildpacks,
herokuVersion: data.herokuVersion || "",
};
default:
return {
buildType: data.buildType,
publishDirectory: data.publishDirectory || undefined,
};
}
};
export const ShowBuildChooseForm = ({ applicationId }: Props) => { export const ShowBuildChooseForm = ({ applicationId }: Props) => {
const { mutateAsync, isLoading } = const { mutateAsync, isLoading } =
api.application.saveBuildType.useMutation(); api.application.saveBuildType.useMutation();
const { data, refetch } = api.application.one.useQuery( const { data, refetch } = api.application.one.useQuery(
{ { applicationId },
applicationId, { enabled: !!applicationId },
},
{
enabled: !!applicationId,
},
); );
const form = useForm<AddTemplate>({ const form = useForm<AddTemplate>({
@ -85,46 +113,29 @@ export const ShowBuildChooseForm = ({ applicationId }: Props) => {
}); });
const buildType = form.watch("buildType"); const buildType = form.watch("buildType");
useEffect(() => { useEffect(() => {
if (data) { if (data) {
if (data.buildType === "dockerfile") { form.reset(resetData(data));
form.reset({
buildType: data.buildType,
...(data.buildType && {
dockerfile: data.dockerfile || "",
dockerContextPath: data.dockerContextPath || "",
dockerBuildStage: data.dockerBuildStage || "",
}),
});
} else if (data.buildType === "heroku_buildpacks") {
form.reset({
buildType: data.buildType,
...(data.buildType && {
herokuVersion: data.herokuVersion || "",
}),
});
} else {
form.reset({
buildType: data.buildType,
publishDirectory: data.publishDirectory || undefined,
});
} }
} }, [data, form]);
}, [form.formState.isSubmitSuccessful, form.reset, data, form]);
const onSubmit = async (data: AddTemplate) => { const onSubmit = async (data: AddTemplate) => {
await mutateAsync({ await mutateAsync({
applicationId, applicationId,
buildType: data.buildType, buildType: data.buildType,
publishDirectory: publishDirectory:
data.buildType === "nixpacks" ? data.publishDirectory : null, data.buildType === BuildType.nixpacks ? data.publishDirectory : null,
dockerfile: data.buildType === "dockerfile" ? data.dockerfile : null, dockerfile:
data.buildType === BuildType.dockerfile ? data.dockerfile : null,
dockerContextPath: dockerContextPath:
data.buildType === "dockerfile" ? data.dockerContextPath : null, data.buildType === BuildType.dockerfile ? data.dockerContextPath : null,
dockerBuildStage: dockerBuildStage:
data.buildType === "dockerfile" ? data.dockerBuildStage : null, data.buildType === BuildType.dockerfile ? data.dockerBuildStage : null,
herokuVersion: herokuVersion:
data.buildType === "heroku_buildpacks" ? data.herokuVersion : null, data.buildType === BuildType.heroku_buildpacks
? data.herokuVersion
: null,
}) })
.then(async () => { .then(async () => {
toast.success("Build type saved"); toast.success("Build type saved");
@ -160,8 +171,7 @@ export const ShowBuildChooseForm = ({ applicationId }: Props) => {
control={form.control} control={form.control}
name="buildType" name="buildType"
defaultValue={form.control._defaultValues.buildType} defaultValue={form.control._defaultValues.buildType}
render={({ field }) => { render={({ field }) => (
return (
<FormItem className="space-y-3"> <FormItem className="space-y-3">
<FormLabel>Build Type</FormLabel> <FormLabel>Build Type</FormLabel>
<FormControl> <FormControl>
@ -170,183 +180,134 @@ export const ShowBuildChooseForm = ({ applicationId }: Props) => {
value={field.value} value={field.value}
className="flex flex-col space-y-1" className="flex flex-col space-y-1"
> >
<FormItem className="flex items-center space-x-3 space-y-0"> {Object.entries(buildTypeDisplayMap).map(
([value, label]) => (
<FormItem
key={value}
className="flex items-center space-x-3 space-y-0"
>
<FormControl> <FormControl>
<RadioGroupItem value="dockerfile" /> <RadioGroupItem value={value} />
</FormControl> </FormControl>
<FormLabel className="font-normal"> <FormLabel className="font-normal">
Dockerfile {label}
{value === BuildType.railpack && (
<Badge className="ml-2 px-1 text-xs">New</Badge>
)}
</FormLabel> </FormLabel>
</FormItem> </FormItem>
<FormItem className="flex items-center space-x-3 space-y-0"> ),
<FormControl> )}
<RadioGroupItem value="railpack" />
</FormControl>
<FormLabel className="font-normal">
Railpack{" "}
<Badge className="ml-1 text-xs px-1">New</Badge>
</FormLabel>
</FormItem>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="nixpacks" />
</FormControl>
<FormLabel className="font-normal">
Nixpacks
</FormLabel>
</FormItem>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="heroku_buildpacks" />
</FormControl>
<FormLabel className="font-normal">
Heroku Buildpacks
</FormLabel>
</FormItem>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="paketo_buildpacks" />
</FormControl>
<FormLabel className="font-normal">
Paketo Buildpacks
</FormLabel>
</FormItem>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="static" />
</FormControl>
<FormLabel className="font-normal">Static</FormLabel>
</FormItem>
</RadioGroup> </RadioGroup>
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
); )}
}}
/> />
{buildType === "heroku_buildpacks" && ( {buildType === BuildType.heroku_buildpacks && (
<FormField <FormField
control={form.control} control={form.control}
name="herokuVersion" name="herokuVersion"
render={({ field }) => { render={({ field }) => (
return (
<FormItem> <FormItem>
<FormLabel>Heroku Version (Optional)</FormLabel> <FormLabel>Heroku Version (Optional)</FormLabel>
<FormControl> <FormControl>
<Input <Input
placeholder={"Heroku Version (Default: 24)"} placeholder="Heroku Version (Default: 24)"
{...field} {...field}
value={field.value ?? ""} value={field.value ?? ""}
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
); )}
}}
/> />
)} )}
{buildType === "dockerfile" && ( {buildType === BuildType.dockerfile && (
<> <>
<FormField <FormField
control={form.control} control={form.control}
name="dockerfile" name="dockerfile"
render={({ field }) => { render={({ field }) => (
return (
<FormItem> <FormItem>
<FormLabel>Docker File</FormLabel> <FormLabel>Docker File</FormLabel>
<FormControl> <FormControl>
<Input <Input
placeholder={"Path of your docker file"} placeholder="Path of your docker file"
{...field} {...field}
value={field.value ?? ""} value={field.value ?? ""}
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
); )}
}}
/> />
<FormField <FormField
control={form.control} control={form.control}
name="dockerContextPath" name="dockerContextPath"
render={({ field }) => { render={({ field }) => (
return (
<FormItem> <FormItem>
<FormLabel>Docker Context Path</FormLabel> <FormLabel>Docker Context Path</FormLabel>
<FormControl> <FormControl>
<Input <Input
placeholder={ placeholder="Path of your docker context (default: .)"
"Path of your docker context default: ."
}
{...field} {...field}
value={field.value ?? ""} value={field.value ?? ""}
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
); )}
}}
/> />
<FormField <FormField
control={form.control} control={form.control}
name="dockerBuildStage" name="dockerBuildStage"
render={({ field }) => { render={({ field }) => (
return (
<FormItem> <FormItem>
<div className="space-y-0.5"> <div className="space-y-0.5">
<FormLabel>Docker Build Stage</FormLabel> <FormLabel>Docker Build Stage</FormLabel>
<FormDescription> <FormDescription>
Allows you to target a specific stage in a Allows you to target a specific stage in a Multi-stage
Multi-stage Dockerfile. If empty, Docker defaults to Dockerfile. If empty, Docker defaults to build the
build the last defined stage. last defined stage.
</FormDescription> </FormDescription>
</div> </div>
<FormControl> <FormControl>
<Input <Input
placeholder={"E.g. production"} placeholder="E.g. production"
{...field} {...field}
value={field.value ?? ""} value={field.value ?? ""}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>
); )}
}}
/> />
</> </>
)} )}
{buildType === BuildType.nixpacks && (
{buildType === "nixpacks" && (
<FormField <FormField
control={form.control} control={form.control}
name="publishDirectory" name="publishDirectory"
render={({ field }) => { render={({ field }) => (
return (
<FormItem> <FormItem>
<div className="space-y-0.5"> <div className="space-y-0.5">
<FormLabel>Publish Directory</FormLabel> <FormLabel>Publish Directory</FormLabel>
<FormDescription> <FormDescription>
Allows you to serve a single directory via NGINX after Allows you to serve a single directory via NGINX after
the build phase. Useful if the final build assets the build phase. Useful if the final build assets should
should be served as a static site. be served as a static site.
</FormDescription> </FormDescription>
</div> </div>
<FormControl> <FormControl>
<Input <Input
placeholder={"Publish Directory"} placeholder="Publish Directory"
{...field} {...field}
value={field.value ?? ""} value={field.value ?? ""}
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
); )}
}}
/> />
)} )}
<div className="flex w-full justify-end"> <div className="flex w-full justify-end">

View File

@ -176,6 +176,8 @@
"esbuild": "0.20.2", "esbuild": "0.20.2",
"lint-staged": "^15.2.7", "lint-staged": "^15.2.7",
"memfs": "^4.11.0", "memfs": "^4.11.0",
"prettier": "^3.5.3",
"prettier-plugin-tailwindcss": "^0.6.11",
"tailwindcss": "^3.4.1", "tailwindcss": "^3.4.1",
"tsx": "^4.7.0", "tsx": "^4.7.0",
"typescript": "^5.4.2", "typescript": "^5.4.2",
@ -196,6 +198,8 @@
] ]
}, },
"commitlint": { "commitlint": {
"extends": ["@commitlint/config-conventional"] "extends": [
"@commitlint/config-conventional"
]
} }
} }

View File

@ -512,6 +512,12 @@ importers:
memfs: memfs:
specifier: ^4.11.0 specifier: ^4.11.0
version: 4.11.0 version: 4.11.0
prettier:
specifier: ^3.5.3
version: 3.5.3
prettier-plugin-tailwindcss:
specifier: ^0.6.11
version: 0.6.11(prettier@3.5.3)
tailwindcss: tailwindcss:
specifier: ^3.4.1 specifier: ^3.4.1
version: 3.4.7(ts-node@10.9.2(@types/node@18.19.42)(typescript@5.5.3)) version: 3.4.7(ts-node@10.9.2(@types/node@18.19.42)(typescript@5.5.3))
@ -6386,6 +6392,66 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
hasBin: true hasBin: true
prettier-plugin-tailwindcss@0.6.11:
resolution: {integrity: sha512-YxaYSIvZPAqhrrEpRtonnrXdghZg1irNg4qrjboCXrpybLWVs55cW2N3juhspVJiO0JBvYJT8SYsJpc8OQSnsA==}
engines: {node: '>=14.21.3'}
peerDependencies:
'@ianvs/prettier-plugin-sort-imports': '*'
'@prettier/plugin-pug': '*'
'@shopify/prettier-plugin-liquid': '*'
'@trivago/prettier-plugin-sort-imports': '*'
'@zackad/prettier-plugin-twig': '*'
prettier: ^3.0
prettier-plugin-astro: '*'
prettier-plugin-css-order: '*'
prettier-plugin-import-sort: '*'
prettier-plugin-jsdoc: '*'
prettier-plugin-marko: '*'
prettier-plugin-multiline-arrays: '*'
prettier-plugin-organize-attributes: '*'
prettier-plugin-organize-imports: '*'
prettier-plugin-sort-imports: '*'
prettier-plugin-style-order: '*'
prettier-plugin-svelte: '*'
peerDependenciesMeta:
'@ianvs/prettier-plugin-sort-imports':
optional: true
'@prettier/plugin-pug':
optional: true
'@shopify/prettier-plugin-liquid':
optional: true
'@trivago/prettier-plugin-sort-imports':
optional: true
'@zackad/prettier-plugin-twig':
optional: true
prettier-plugin-astro:
optional: true
prettier-plugin-css-order:
optional: true
prettier-plugin-import-sort:
optional: true
prettier-plugin-jsdoc:
optional: true
prettier-plugin-marko:
optional: true
prettier-plugin-multiline-arrays:
optional: true
prettier-plugin-organize-attributes:
optional: true
prettier-plugin-organize-imports:
optional: true
prettier-plugin-sort-imports:
optional: true
prettier-plugin-style-order:
optional: true
prettier-plugin-svelte:
optional: true
prettier@3.5.3:
resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==}
engines: {node: '>=14'}
hasBin: true
pretty-format@29.7.0: pretty-format@29.7.0:
resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@ -13505,6 +13571,12 @@ snapshots:
tunnel-agent: 0.6.0 tunnel-agent: 0.6.0
optional: true optional: true
prettier-plugin-tailwindcss@0.6.11(prettier@3.5.3):
dependencies:
prettier: 3.5.3
prettier@3.5.3: {}
pretty-format@29.7.0: pretty-format@29.7.0:
dependencies: dependencies:
'@jest/schemas': 29.6.3 '@jest/schemas': 29.6.3