@farm.js/create-app 0.1.0-beta.99 → 0.1.1-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -7
- package/dist/index.js +298 -167
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +298 -167
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/templates/_renderers/preact/package.json +4 -4
- package/templates/_renderers/preact/src/lib/api.ts +1 -1
- package/templates/_renderers/solid/package.json +4 -4
- package/templates/_renderers/solid/src/lib/api.ts +1 -1
- package/templates/_renderers/svelte/package.json +4 -4
- package/templates/_renderers/svelte/src/lib/api.ts +1 -1
- package/templates/_renderers/vue/package.json +4 -4
- package/templates/_renderers/vue/src/lib/api.ts +1 -1
- package/templates/auth/README.md +1 -1
- package/templates/auth/package.json +7 -7
- package/templates/auth/pnpm-workspace.yaml +1 -3
- package/templates/basic/package.json +6 -6
- package/templates/basic/pnpm-workspace.yaml +3 -0
- package/templates/better-auth/README.md +1 -1
- package/templates/better-auth/package.json +7 -7
- package/templates/better-auth/pnpm-workspace.yaml +1 -4
- package/templates/react-compiler/README.md +1 -1
- package/templates/react-compiler/package.json +7 -7
- package/templates/react-compiler/pnpm-workspace.yaml +3 -0
package/dist/index.mjs
CHANGED
|
@@ -10,6 +10,174 @@ import { randomUUID } from "node:crypto";
|
|
|
10
10
|
import { request } from "node:http";
|
|
11
11
|
import { request as request$1 } from "node:https";
|
|
12
12
|
import os from "node:os";
|
|
13
|
+
function shadcnComponentTemplate(component) {
|
|
14
|
+
switch (component) {
|
|
15
|
+
case "badge": return `import * as React from "react";
|
|
16
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
17
|
+
import { cn } from "@/lib/utils";
|
|
18
|
+
|
|
19
|
+
const badgeVariants = cva(
|
|
20
|
+
"inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
21
|
+
{
|
|
22
|
+
variants: {
|
|
23
|
+
variant: {
|
|
24
|
+
default: "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
|
|
25
|
+
secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
26
|
+
outline: "text-foreground",
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
defaultVariants: {
|
|
30
|
+
variant: "default",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
export interface BadgeProps
|
|
36
|
+
extends React.HTMLAttributes<HTMLDivElement>,
|
|
37
|
+
VariantProps<typeof badgeVariants> {}
|
|
38
|
+
|
|
39
|
+
export function Badge({ className, variant, ...props }: BadgeProps) {
|
|
40
|
+
return <div className={cn(badgeVariants({ variant }), className)} {...props} />;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export { badgeVariants };
|
|
44
|
+
`;
|
|
45
|
+
case "button": return `import * as React from "react";
|
|
46
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
47
|
+
import { cn } from "@/lib/utils";
|
|
48
|
+
|
|
49
|
+
const buttonVariants = cva(
|
|
50
|
+
"inline-flex h-9 items-center justify-center whitespace-nowrap rounded-md px-4 py-2 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
|
|
51
|
+
{
|
|
52
|
+
variants: {
|
|
53
|
+
variant: {
|
|
54
|
+
default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
|
|
55
|
+
secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
|
|
56
|
+
outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
|
|
57
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
58
|
+
},
|
|
59
|
+
size: {
|
|
60
|
+
default: "h-9 px-4 py-2",
|
|
61
|
+
sm: "h-8 rounded-md px-3 text-xs",
|
|
62
|
+
lg: "h-10 rounded-md px-8",
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
defaultVariants: {
|
|
66
|
+
variant: "default",
|
|
67
|
+
size: "default",
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
export interface ButtonProps
|
|
73
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
74
|
+
VariantProps<typeof buttonVariants> {}
|
|
75
|
+
|
|
76
|
+
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
77
|
+
({ className, variant, size, ...props }, ref) => {
|
|
78
|
+
return (
|
|
79
|
+
<button
|
|
80
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
81
|
+
ref={ref}
|
|
82
|
+
{...props}
|
|
83
|
+
/>
|
|
84
|
+
);
|
|
85
|
+
},
|
|
86
|
+
);
|
|
87
|
+
Button.displayName = "Button";
|
|
88
|
+
|
|
89
|
+
export { buttonVariants };
|
|
90
|
+
`;
|
|
91
|
+
case "card": return `import * as React from "react";
|
|
92
|
+
import { cn } from "@/lib/utils";
|
|
93
|
+
|
|
94
|
+
export const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
95
|
+
({ className, ...props }, ref) => (
|
|
96
|
+
<div
|
|
97
|
+
ref={ref}
|
|
98
|
+
className={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)}
|
|
99
|
+
{...props}
|
|
100
|
+
/>
|
|
101
|
+
),
|
|
102
|
+
);
|
|
103
|
+
Card.displayName = "Card";
|
|
104
|
+
|
|
105
|
+
export const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
106
|
+
({ className, ...props }, ref) => (
|
|
107
|
+
<div ref={ref} className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />
|
|
108
|
+
),
|
|
109
|
+
);
|
|
110
|
+
CardHeader.displayName = "CardHeader";
|
|
111
|
+
|
|
112
|
+
export const CardTitle = React.forwardRef<HTMLHeadingElement, React.HTMLAttributes<HTMLHeadingElement>>(
|
|
113
|
+
({ className, ...props }, ref) => (
|
|
114
|
+
<h3 ref={ref} className={cn("text-2xl font-semibold leading-none tracking-normal", className)} {...props} />
|
|
115
|
+
),
|
|
116
|
+
);
|
|
117
|
+
CardTitle.displayName = "CardTitle";
|
|
118
|
+
|
|
119
|
+
export const CardDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
|
|
120
|
+
({ className, ...props }, ref) => (
|
|
121
|
+
<p ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />
|
|
122
|
+
),
|
|
123
|
+
);
|
|
124
|
+
CardDescription.displayName = "CardDescription";
|
|
125
|
+
|
|
126
|
+
export const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
127
|
+
({ className, ...props }, ref) => (
|
|
128
|
+
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
|
129
|
+
),
|
|
130
|
+
);
|
|
131
|
+
CardContent.displayName = "CardContent";
|
|
132
|
+
|
|
133
|
+
export const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
134
|
+
({ className, ...props }, ref) => (
|
|
135
|
+
<div ref={ref} className={cn("flex items-center p-6 pt-0", className)} {...props} />
|
|
136
|
+
),
|
|
137
|
+
);
|
|
138
|
+
CardFooter.displayName = "CardFooter";
|
|
139
|
+
`;
|
|
140
|
+
case "input": return `import * as React from "react";
|
|
141
|
+
import { cn } from "@/lib/utils";
|
|
142
|
+
|
|
143
|
+
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {}
|
|
144
|
+
|
|
145
|
+
export const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
146
|
+
({ className, type, ...props }, ref) => (
|
|
147
|
+
<input
|
|
148
|
+
type={type}
|
|
149
|
+
className={cn(
|
|
150
|
+
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
|
|
151
|
+
className,
|
|
152
|
+
)}
|
|
153
|
+
ref={ref}
|
|
154
|
+
{...props}
|
|
155
|
+
/>
|
|
156
|
+
),
|
|
157
|
+
);
|
|
158
|
+
Input.displayName = "Input";
|
|
159
|
+
`;
|
|
160
|
+
case "label": return `import * as React from "react";
|
|
161
|
+
import { cn } from "@/lib/utils";
|
|
162
|
+
|
|
163
|
+
export const Label = React.forwardRef<HTMLLabelElement, React.LabelHTMLAttributes<HTMLLabelElement>>(
|
|
164
|
+
({ className, ...props }, ref) => (
|
|
165
|
+
<label
|
|
166
|
+
ref={ref}
|
|
167
|
+
className={cn("text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70", className)}
|
|
168
|
+
{...props}
|
|
169
|
+
/>
|
|
170
|
+
),
|
|
171
|
+
);
|
|
172
|
+
Label.displayName = "Label";
|
|
173
|
+
`;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
//#endregion
|
|
177
|
+
//#region ../farm-cli/src/ui-registry.ts
|
|
178
|
+
/** Value written into a generated `components.json` as `registries.farm.url`. */
|
|
179
|
+
const UI_REGISTRY_URL_TEMPLATE = "https://farmjs.dev/r/{name}.json";
|
|
180
|
+
//#endregion
|
|
13
181
|
//#region ../farm-cli/src/ui-feature-registry.ts
|
|
14
182
|
async function installUIFeature(input) {
|
|
15
183
|
const feature = input.definition.ui;
|
|
@@ -252,6 +420,16 @@ function unkeyApiKeysUIFeature() {
|
|
|
252
420
|
]
|
|
253
421
|
};
|
|
254
422
|
}
|
|
423
|
+
function sanityContentUIFeature() {
|
|
424
|
+
return {
|
|
425
|
+
name: "sanity-content",
|
|
426
|
+
description: "Sanity content list UI",
|
|
427
|
+
components: ["badge", "card"],
|
|
428
|
+
needsApiClient: false,
|
|
429
|
+
notes: ["Open \"/integrations/sanity\" to check the Sanity connection."],
|
|
430
|
+
files: () => [componentFile("sanity-content-console.tsx", sanityContentTemplate()), integrationPageFile("sanity", "SanityContentConsole")]
|
|
431
|
+
};
|
|
432
|
+
}
|
|
255
433
|
function billingUIFeature(input) {
|
|
256
434
|
return {
|
|
257
435
|
name: `${input.provider}-billing`,
|
|
@@ -554,7 +732,7 @@ function createComponentsJson() {
|
|
|
554
732
|
lib: "@/lib",
|
|
555
733
|
hooks: "@/hooks"
|
|
556
734
|
},
|
|
557
|
-
registries: { farm: { url:
|
|
735
|
+
registries: { farm: { url: UI_REGISTRY_URL_TEMPLATE } }
|
|
558
736
|
};
|
|
559
737
|
}
|
|
560
738
|
function mergeComponentsJson(current, defaults) {
|
|
@@ -597,169 +775,6 @@ import type { AppIntegrations } from "./integrations";
|
|
|
597
775
|
export const { api, apiClient } = createIntegrations<AppIntegrations>();
|
|
598
776
|
`;
|
|
599
777
|
}
|
|
600
|
-
function shadcnComponentTemplate(component) {
|
|
601
|
-
switch (component) {
|
|
602
|
-
case "badge": return `import * as React from "react";
|
|
603
|
-
import { cva, type VariantProps } from "class-variance-authority";
|
|
604
|
-
import { cn } from "@/lib/utils";
|
|
605
|
-
|
|
606
|
-
const badgeVariants = cva(
|
|
607
|
-
"inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
608
|
-
{
|
|
609
|
-
variants: {
|
|
610
|
-
variant: {
|
|
611
|
-
default: "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
|
|
612
|
-
secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
613
|
-
outline: "text-foreground",
|
|
614
|
-
},
|
|
615
|
-
},
|
|
616
|
-
defaultVariants: {
|
|
617
|
-
variant: "default",
|
|
618
|
-
},
|
|
619
|
-
},
|
|
620
|
-
);
|
|
621
|
-
|
|
622
|
-
export interface BadgeProps
|
|
623
|
-
extends React.HTMLAttributes<HTMLDivElement>,
|
|
624
|
-
VariantProps<typeof badgeVariants> {}
|
|
625
|
-
|
|
626
|
-
export function Badge({ className, variant, ...props }: BadgeProps) {
|
|
627
|
-
return <div className={cn(badgeVariants({ variant }), className)} {...props} />;
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
export { badgeVariants };
|
|
631
|
-
`;
|
|
632
|
-
case "button": return `import * as React from "react";
|
|
633
|
-
import { cva, type VariantProps } from "class-variance-authority";
|
|
634
|
-
import { cn } from "@/lib/utils";
|
|
635
|
-
|
|
636
|
-
const buttonVariants = cva(
|
|
637
|
-
"inline-flex h-9 items-center justify-center whitespace-nowrap rounded-md px-4 py-2 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
|
|
638
|
-
{
|
|
639
|
-
variants: {
|
|
640
|
-
variant: {
|
|
641
|
-
default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
|
|
642
|
-
secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
|
|
643
|
-
outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
|
|
644
|
-
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
645
|
-
},
|
|
646
|
-
size: {
|
|
647
|
-
default: "h-9 px-4 py-2",
|
|
648
|
-
sm: "h-8 rounded-md px-3 text-xs",
|
|
649
|
-
lg: "h-10 rounded-md px-8",
|
|
650
|
-
},
|
|
651
|
-
},
|
|
652
|
-
defaultVariants: {
|
|
653
|
-
variant: "default",
|
|
654
|
-
size: "default",
|
|
655
|
-
},
|
|
656
|
-
},
|
|
657
|
-
);
|
|
658
|
-
|
|
659
|
-
export interface ButtonProps
|
|
660
|
-
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
661
|
-
VariantProps<typeof buttonVariants> {}
|
|
662
|
-
|
|
663
|
-
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
664
|
-
({ className, variant, size, ...props }, ref) => {
|
|
665
|
-
return (
|
|
666
|
-
<button
|
|
667
|
-
className={cn(buttonVariants({ variant, size, className }))}
|
|
668
|
-
ref={ref}
|
|
669
|
-
{...props}
|
|
670
|
-
/>
|
|
671
|
-
);
|
|
672
|
-
},
|
|
673
|
-
);
|
|
674
|
-
Button.displayName = "Button";
|
|
675
|
-
|
|
676
|
-
export { buttonVariants };
|
|
677
|
-
`;
|
|
678
|
-
case "card": return `import * as React from "react";
|
|
679
|
-
import { cn } from "@/lib/utils";
|
|
680
|
-
|
|
681
|
-
export const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
682
|
-
({ className, ...props }, ref) => (
|
|
683
|
-
<div
|
|
684
|
-
ref={ref}
|
|
685
|
-
className={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)}
|
|
686
|
-
{...props}
|
|
687
|
-
/>
|
|
688
|
-
),
|
|
689
|
-
);
|
|
690
|
-
Card.displayName = "Card";
|
|
691
|
-
|
|
692
|
-
export const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
693
|
-
({ className, ...props }, ref) => (
|
|
694
|
-
<div ref={ref} className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />
|
|
695
|
-
),
|
|
696
|
-
);
|
|
697
|
-
CardHeader.displayName = "CardHeader";
|
|
698
|
-
|
|
699
|
-
export const CardTitle = React.forwardRef<HTMLHeadingElement, React.HTMLAttributes<HTMLHeadingElement>>(
|
|
700
|
-
({ className, ...props }, ref) => (
|
|
701
|
-
<h3 ref={ref} className={cn("text-2xl font-semibold leading-none tracking-normal", className)} {...props} />
|
|
702
|
-
),
|
|
703
|
-
);
|
|
704
|
-
CardTitle.displayName = "CardTitle";
|
|
705
|
-
|
|
706
|
-
export const CardDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
|
|
707
|
-
({ className, ...props }, ref) => (
|
|
708
|
-
<p ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />
|
|
709
|
-
),
|
|
710
|
-
);
|
|
711
|
-
CardDescription.displayName = "CardDescription";
|
|
712
|
-
|
|
713
|
-
export const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
714
|
-
({ className, ...props }, ref) => (
|
|
715
|
-
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
|
716
|
-
),
|
|
717
|
-
);
|
|
718
|
-
CardContent.displayName = "CardContent";
|
|
719
|
-
|
|
720
|
-
export const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
721
|
-
({ className, ...props }, ref) => (
|
|
722
|
-
<div ref={ref} className={cn("flex items-center p-6 pt-0", className)} {...props} />
|
|
723
|
-
),
|
|
724
|
-
);
|
|
725
|
-
CardFooter.displayName = "CardFooter";
|
|
726
|
-
`;
|
|
727
|
-
case "input": return `import * as React from "react";
|
|
728
|
-
import { cn } from "@/lib/utils";
|
|
729
|
-
|
|
730
|
-
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {}
|
|
731
|
-
|
|
732
|
-
export const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
733
|
-
({ className, type, ...props }, ref) => (
|
|
734
|
-
<input
|
|
735
|
-
type={type}
|
|
736
|
-
className={cn(
|
|
737
|
-
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
|
|
738
|
-
className,
|
|
739
|
-
)}
|
|
740
|
-
ref={ref}
|
|
741
|
-
{...props}
|
|
742
|
-
/>
|
|
743
|
-
),
|
|
744
|
-
);
|
|
745
|
-
Input.displayName = "Input";
|
|
746
|
-
`;
|
|
747
|
-
case "label": return `import * as React from "react";
|
|
748
|
-
import { cn } from "@/lib/utils";
|
|
749
|
-
|
|
750
|
-
export const Label = React.forwardRef<HTMLLabelElement, React.LabelHTMLAttributes<HTMLLabelElement>>(
|
|
751
|
-
({ className, ...props }, ref) => (
|
|
752
|
-
<label
|
|
753
|
-
ref={ref}
|
|
754
|
-
className={cn("text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70", className)}
|
|
755
|
-
{...props}
|
|
756
|
-
/>
|
|
757
|
-
),
|
|
758
|
-
);
|
|
759
|
-
Label.displayName = "Label";
|
|
760
|
-
`;
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
778
|
function billingPricingTemplate(input) {
|
|
764
779
|
return `"use client";
|
|
765
780
|
|
|
@@ -1659,6 +1674,40 @@ export function UnkeyApiKeysConsole() {
|
|
|
1659
1674
|
}
|
|
1660
1675
|
`;
|
|
1661
1676
|
}
|
|
1677
|
+
function sanityContentTemplate() {
|
|
1678
|
+
return `import { Badge } from "@/components/ui/badge";
|
|
1679
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
1680
|
+
|
|
1681
|
+
export function SanityContentConsole() {
|
|
1682
|
+
return (
|
|
1683
|
+
<main className="min-h-screen bg-background px-6 py-12 text-foreground">
|
|
1684
|
+
<section className="mx-auto flex w-full max-w-3xl flex-col gap-6">
|
|
1685
|
+
<div className="space-y-3">
|
|
1686
|
+
<Badge variant="secondary">Sanity</Badge>
|
|
1687
|
+
<h1 className="text-3xl font-semibold tracking-normal">Content</h1>
|
|
1688
|
+
</div>
|
|
1689
|
+
<Card>
|
|
1690
|
+
<CardHeader>
|
|
1691
|
+
<CardTitle className="text-xl">Connected to Sanity</CardTitle>
|
|
1692
|
+
<CardDescription>
|
|
1693
|
+
Query documents with createServerQuery and invalidate them from the Sanity webhook.
|
|
1694
|
+
</CardDescription>
|
|
1695
|
+
</CardHeader>
|
|
1696
|
+
<CardContent className="space-y-3">
|
|
1697
|
+
<code className="block overflow-x-auto rounded-md border bg-muted/30 p-3 text-xs">
|
|
1698
|
+
cms.fetch('*[_type == "post"]{"{"} title {"}"}')
|
|
1699
|
+
</code>
|
|
1700
|
+
<p className="text-sm text-muted-foreground">
|
|
1701
|
+
Point a Sanity webhook at /api/sanity/webhook to keep cached pages fresh.
|
|
1702
|
+
</p>
|
|
1703
|
+
</CardContent>
|
|
1704
|
+
</Card>
|
|
1705
|
+
</section>
|
|
1706
|
+
</main>
|
|
1707
|
+
);
|
|
1708
|
+
}
|
|
1709
|
+
`;
|
|
1710
|
+
}
|
|
1662
1711
|
function unkeyProtectedRouteTemplate() {
|
|
1663
1712
|
return `export function GET() {
|
|
1664
1713
|
return Response.json({
|
|
@@ -1829,7 +1878,7 @@ export const auth0Integration = auth0({
|
|
|
1829
1878
|
exportName: "clerkIntegration",
|
|
1830
1879
|
description: "Clerk auth provider and protected route middleware",
|
|
1831
1880
|
env: ["NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY", "CLERK_SECRET_KEY"],
|
|
1832
|
-
dependencies: { "@clerk/react": "^6.
|
|
1881
|
+
dependencies: { "@clerk/react": "^6.4.3" },
|
|
1833
1882
|
ui: clerkAuthUIFeature(),
|
|
1834
1883
|
template: () => `import { clerk } from "@farm.js/clerk";
|
|
1835
1884
|
|
|
@@ -2201,6 +2250,43 @@ export const unkeyIntegration = unkey({
|
|
|
2201
2250
|
console.log("[unkey]", event.phase, event.route?.path || "none");
|
|
2202
2251
|
},
|
|
2203
2252
|
});
|
|
2253
|
+
`
|
|
2254
|
+
},
|
|
2255
|
+
{
|
|
2256
|
+
provider: "sanity",
|
|
2257
|
+
aliases: ["cms", "sanity-cms"],
|
|
2258
|
+
defaultKey: "cms",
|
|
2259
|
+
packageName: "@farm.js/sanity",
|
|
2260
|
+
fileName: "sanity",
|
|
2261
|
+
exportName: "sanityIntegration",
|
|
2262
|
+
description: "Sanity content with server queries, CDN images, and webhook cache invalidation",
|
|
2263
|
+
env: [
|
|
2264
|
+
"SANITY_PROJECT_ID",
|
|
2265
|
+
"SANITY_DATASET",
|
|
2266
|
+
"SANITY_API_READ_TOKEN",
|
|
2267
|
+
"SANITY_WEBHOOK_SECRET"
|
|
2268
|
+
],
|
|
2269
|
+
dependencies: { "@sanity/client": "^8.4.0" },
|
|
2270
|
+
ui: sanityContentUIFeature(),
|
|
2271
|
+
template: () => `import { sanity } from "@farm.js/sanity";
|
|
2272
|
+
|
|
2273
|
+
export const sanityIntegration = sanity({
|
|
2274
|
+
projectId: process.env.SANITY_PROJECT_ID,
|
|
2275
|
+
dataset: process.env.SANITY_DATASET,
|
|
2276
|
+
token: process.env.SANITY_API_READ_TOKEN,
|
|
2277
|
+
// The webhook fires before Sanity's CDN updates, so reads go through the API.
|
|
2278
|
+
useCdn: false,
|
|
2279
|
+
webhook: {
|
|
2280
|
+
secret: process.env.SANITY_WEBHOOK_SECRET,
|
|
2281
|
+
onChange(payload) {
|
|
2282
|
+
console.log("[sanity]", payload._type, payload._id);
|
|
2283
|
+
return undefined;
|
|
2284
|
+
},
|
|
2285
|
+
},
|
|
2286
|
+
log(event) {
|
|
2287
|
+
console.log("[sanity]", event.phase, event.route?.path || "none");
|
|
2288
|
+
},
|
|
2289
|
+
});
|
|
2204
2290
|
`
|
|
2205
2291
|
}
|
|
2206
2292
|
];
|
|
@@ -2540,6 +2626,7 @@ const FARM_TELEMETRY_TEMPLATES = [
|
|
|
2540
2626
|
"jobs-trigger",
|
|
2541
2627
|
"polar",
|
|
2542
2628
|
"resend",
|
|
2629
|
+
"sanity",
|
|
2543
2630
|
"stripe",
|
|
2544
2631
|
"supabase",
|
|
2545
2632
|
"unkey",
|
|
@@ -3157,6 +3244,7 @@ async function createApp(projectName, options = {}) {
|
|
|
3157
3244
|
await fs.mkdir(projectPath, { recursive: true });
|
|
3158
3245
|
const integrationResult = await copyTemplate(template, projectPath, renderer);
|
|
3159
3246
|
await updatePackageJson(projectPath, projectName, packageManager);
|
|
3247
|
+
await updatePackageManagerInstructions(projectPath, packageManager.name);
|
|
3160
3248
|
logger.success(`🚜 Created ${projectName}`);
|
|
3161
3249
|
if (!options.skipInstall) {
|
|
3162
3250
|
logger.info(`Installing dependencies with ${packageManager.name}...`);
|
|
@@ -3169,7 +3257,7 @@ async function createApp(projectName, options = {}) {
|
|
|
3169
3257
|
if (options.skipInstall) logger.info(` ${packageManager.name} install`);
|
|
3170
3258
|
logger.info(` ${getDevCommand(packageManager.name)}`);
|
|
3171
3259
|
logger.info("");
|
|
3172
|
-
for (const instruction of templateDetails[template]?.instructions ?? []) logger.info(instruction);
|
|
3260
|
+
for (const instruction of templateDetails[template]?.instructions ?? []) logger.info(rewritePackageManagerCommands(instruction, packageManager.name));
|
|
3173
3261
|
if (integrationResult) {
|
|
3174
3262
|
const details = templateDetails[template].integration;
|
|
3175
3263
|
logger.info(`Open ${details.route} for the ${details.label} starter.`);
|
|
@@ -3210,6 +3298,14 @@ async function copyTemplate(template, projectPath, renderer) {
|
|
|
3210
3298
|
}
|
|
3211
3299
|
async function applyRendererTemplate(projectPath, renderer, basePackageJson) {
|
|
3212
3300
|
const rendererTemplatePath = path.join(__dirname, "..", "templates", "_renderers", renderer);
|
|
3301
|
+
await Promise.all([
|
|
3302
|
+
fs.rm(path.join(projectPath, "docs.config.ts"), { force: true }),
|
|
3303
|
+
fs.rm(path.join(projectPath, "docs.json"), { force: true }),
|
|
3304
|
+
fs.rm(path.join(projectPath, "src", "app", "docs"), {
|
|
3305
|
+
recursive: true,
|
|
3306
|
+
force: true
|
|
3307
|
+
})
|
|
3308
|
+
]);
|
|
3213
3309
|
if (renderer === "vue" || renderer === "svelte") await Promise.all([
|
|
3214
3310
|
fs.rm(path.join(projectPath, "src", "app", "page.tsx"), { force: true }),
|
|
3215
3311
|
fs.rm(path.join(projectPath, "src", "app", "layout.tsx"), { force: true }),
|
|
@@ -3227,7 +3323,13 @@ function mergeRendererPackageJson(base, renderer) {
|
|
|
3227
3323
|
...base.devDependencies,
|
|
3228
3324
|
...renderer.devDependencies
|
|
3229
3325
|
};
|
|
3230
|
-
for (const name of [
|
|
3326
|
+
for (const name of [
|
|
3327
|
+
"@farming-labs/docs",
|
|
3328
|
+
"@farming-labs/farmjs",
|
|
3329
|
+
"@farming-labs/theme",
|
|
3330
|
+
"react",
|
|
3331
|
+
"react-dom"
|
|
3332
|
+
]) delete dependencies[name];
|
|
3231
3333
|
for (const name of ["@types/react", "@types/react-dom"]) delete devDependencies[name];
|
|
3232
3334
|
return {
|
|
3233
3335
|
...base,
|
|
@@ -3453,6 +3555,35 @@ function getDevCommand(packageManager) {
|
|
|
3453
3555
|
if (packageManager === "npm" || packageManager === "bun") return `${packageManager} run dev`;
|
|
3454
3556
|
return `${packageManager} dev`;
|
|
3455
3557
|
}
|
|
3558
|
+
function getRunCommand(packageManager, script) {
|
|
3559
|
+
if (packageManager === "npm" || packageManager === "bun") return `${packageManager} run ${script}`;
|
|
3560
|
+
return `${packageManager} ${script}`;
|
|
3561
|
+
}
|
|
3562
|
+
function rewritePackageManagerCommands(content, packageManager) {
|
|
3563
|
+
let output = content.replace(/\bpnpm install\b/g, `${packageManager} install`);
|
|
3564
|
+
output = output.replace(/\bpnpm run (dev|auth:migrate|type-check|build|check|deploy|experiment)\b/g, (match, script) => packageManager === "pnpm" ? match : getRunCommand(packageManager, script));
|
|
3565
|
+
return output.replace(/\bpnpm (dev|auth:migrate|type-check|build|check|deploy|experiment)\b/g, (_match, script) => getRunCommand(packageManager, script));
|
|
3566
|
+
}
|
|
3567
|
+
async function updatePackageManagerInstructions(projectPath, packageManager) {
|
|
3568
|
+
await Promise.all([
|
|
3569
|
+
"package.json",
|
|
3570
|
+
"README.md",
|
|
3571
|
+
"farm.config.ts",
|
|
3572
|
+
"src/app/page.tsx",
|
|
3573
|
+
"src/app/page.jsx",
|
|
3574
|
+
"src/app/page.vue",
|
|
3575
|
+
"src/app/page.svelte"
|
|
3576
|
+
].map(async (file) => {
|
|
3577
|
+
const entryPath = path.join(projectPath, file);
|
|
3578
|
+
try {
|
|
3579
|
+
const content = await fs.readFile(entryPath, "utf8");
|
|
3580
|
+
const nextContent = rewritePackageManagerCommands(content, packageManager);
|
|
3581
|
+
if (nextContent !== content) await fs.writeFile(entryPath, nextContent, "utf8");
|
|
3582
|
+
} catch (error) {
|
|
3583
|
+
if (error.code !== "ENOENT") throw error;
|
|
3584
|
+
}
|
|
3585
|
+
}));
|
|
3586
|
+
}
|
|
3456
3587
|
//#endregion
|
|
3457
3588
|
export { createApp, detectPackageManager, installDependencies };
|
|
3458
3589
|
|