create-goodfellow 0.0.0-stage → 0.2.0

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.
Files changed (150) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +15 -2
  3. package/bin/create-goodfellow.js +2 -0
  4. package/dist/cli.d.ts +1 -0
  5. package/dist/cli.js +220 -0
  6. package/dist/index.d.ts +34 -0
  7. package/dist/index.js +2 -0
  8. package/dist/scaffold-BGxzPdN1.js +150 -0
  9. package/package.json +55 -3
  10. package/templates/next/.github/workflows/deploy.yml +86 -0
  11. package/templates/next/.gitlab-ci.yml +28 -0
  12. package/templates/next/README.md +127 -0
  13. package/templates/next/_gitignore +5 -0
  14. package/templates/next/app/(site)/[[...path]]/page.tsx +8 -0
  15. package/templates/next/app/(site)/layout.tsx +5 -0
  16. package/templates/next/app/admin/demo-content.json/route.ts +5 -0
  17. package/templates/next/app/admin/layout.tsx +8 -0
  18. package/templates/next/app/admin/page.tsx +8 -0
  19. package/templates/next/app/layout.tsx +11 -0
  20. package/templates/next/app/not-found.tsx +4 -0
  21. package/templates/next/app/robots.ts +4 -0
  22. package/templates/next/app/site.css +11 -0
  23. package/templates/next/app/sitemap.ts +4 -0
  24. package/templates/next/blocks/installed/index.ts +7 -0
  25. package/templates/next/components.json +24 -0
  26. package/templates/next/content/collections/news/_collection.json +113 -0
  27. package/templates/next/content/collections/news/open-house.json +9 -0
  28. package/templates/next/content/collections/news/welcome.json +10 -0
  29. package/templates/next/content/layout/footer.json +55 -0
  30. package/templates/next/content/layout/header.json +54 -0
  31. package/templates/next/content/menus.json +25 -0
  32. package/templates/next/content/pages/404.json +73 -0
  33. package/templates/next/content/pages/about.json +59 -0
  34. package/templates/next/content/pages/index.json +215 -0
  35. package/templates/next/content/pages/news.json +55 -0
  36. package/templates/next/content/site.json +25 -0
  37. package/templates/next/content/styles/custom.css +4 -0
  38. package/templates/next/goodfellow.config.tsx +17 -0
  39. package/templates/next/lib/site.ts +5 -0
  40. package/templates/next/next.config.ts +7 -0
  41. package/templates/next/package.json +40 -0
  42. package/templates/next/postcss.config.mjs +3 -0
  43. package/templates/next/public/media/favicon.svg +1 -0
  44. package/templates/next/public/media/logo.svg +1 -0
  45. package/templates/next/public/media/placeholder.svg +1 -0
  46. package/templates/next/tsconfig.json +27 -0
  47. package/templates/parish/.github/workflows/deploy.yml +84 -0
  48. package/templates/parish/.gitlab-ci.yml +28 -0
  49. package/templates/parish/README.md +128 -0
  50. package/templates/parish/_gitignore +2 -0
  51. package/templates/parish/blocks/installed/index.ts +7 -0
  52. package/templates/parish/blocks/mass-times.tsx +59 -0
  53. package/templates/parish/blocks/notice.tsx +46 -0
  54. package/templates/parish/blocks/scripture.tsx +40 -0
  55. package/templates/parish/components.json +24 -0
  56. package/templates/parish/content/collections/bulletins/2026-09-27.json +8 -0
  57. package/templates/parish/content/collections/bulletins/2026-10-04.json +8 -0
  58. package/templates/parish/content/collections/bulletins/_collection.json +113 -0
  59. package/templates/parish/content/collections/events/_collection.json +156 -0
  60. package/templates/parish/content/collections/events/advent-penance-service.json +14 -0
  61. package/templates/parish/content/collections/events/all-souls.json +14 -0
  62. package/templates/parish/content/collections/events/christmas-concert.json +14 -0
  63. package/templates/parish/content/collections/events/fall-festival.json +14 -0
  64. package/templates/parish/content/collections/events/youth-group.json +23 -0
  65. package/templates/parish/content/collections/news/_collection.json +137 -0
  66. package/templates/parish/content/collections/news/bell-tower-repairs.json +13 -0
  67. package/templates/parish/content/collections/news/choir-new-voices.json +14 -0
  68. package/templates/parish/content/collections/news/food-drive.json +13 -0
  69. package/templates/parish/content/collections/news/new-website.json +13 -0
  70. package/templates/parish/content/collections/news/winter-coats.json +13 -0
  71. package/templates/parish/content/collections/news/youth-group.json +13 -0
  72. package/templates/parish/content/collections/news/youth-service-day.json +14 -0
  73. package/templates/parish/content/collections/staff/_collection.json +34 -0
  74. package/templates/parish/content/collections/staff/david-okafor.json +9 -0
  75. package/templates/parish/content/collections/staff/fr-thomas-reed.json +9 -0
  76. package/templates/parish/content/collections/staff/maria-chen.json +9 -0
  77. package/templates/parish/content/collections/staff/sarah-lindqvist.json +9 -0
  78. package/templates/parish/content/layout/footer.json +142 -0
  79. package/templates/parish/content/layout/header.json +54 -0
  80. package/templates/parish/content/menus.json +67 -0
  81. package/templates/parish/content/pages/404.json +85 -0
  82. package/templates/parish/content/pages/about.json +153 -0
  83. package/templates/parish/content/pages/bulletins.json +64 -0
  84. package/templates/parish/content/pages/calendar.json +59 -0
  85. package/templates/parish/content/pages/contact.json +180 -0
  86. package/templates/parish/content/pages/events.json +107 -0
  87. package/templates/parish/content/pages/index.json +390 -0
  88. package/templates/parish/content/pages/mass-times.json +168 -0
  89. package/templates/parish/content/pages/news.json +68 -0
  90. package/templates/parish/content/pages/sacraments.json +259 -0
  91. package/templates/parish/content/site.json +36 -0
  92. package/templates/parish/content/styles/custom.css +4 -0
  93. package/templates/parish/goodfellow.config.tsx +24 -0
  94. package/templates/parish/package.json +36 -0
  95. package/templates/parish/public/media/bulletin-example.pdf +32 -0
  96. package/templates/parish/public/media/candles.svg +22 -0
  97. package/templates/parish/public/media/church.svg +33 -0
  98. package/templates/parish/public/media/favicon.svg +5 -0
  99. package/templates/parish/public/media/gathering.svg +18 -0
  100. package/templates/parish/public/media/logo.svg +5 -0
  101. package/templates/parish/public/media/person.svg +5 -0
  102. package/templates/parish/public/media/placeholder.svg +5 -0
  103. package/templates/parish/src/styles.css +8 -0
  104. package/templates/parish/tsconfig.json +14 -0
  105. package/templates/parish/vercel.ts +65 -0
  106. package/templates/registry/accordion.json +17 -0
  107. package/templates/registry/alert.json +16 -0
  108. package/templates/registry/badge.json +17 -0
  109. package/templates/registry/button.json +17 -0
  110. package/templates/registry/card.json +16 -0
  111. package/templates/registry/carousel.json +19 -0
  112. package/templates/registry/custom-html.json +31 -0
  113. package/templates/registry/registry.json +364 -0
  114. package/templates/registry/shadcn-call-to-action.json +26 -0
  115. package/templates/registry/shadcn-cards.json +26 -0
  116. package/templates/registry/shadcn-carousel.json +29 -0
  117. package/templates/registry/shadcn-faq.json +30 -0
  118. package/templates/registry/shadcn-hero.json +25 -0
  119. package/templates/registry/shadcn-notice.json +27 -0
  120. package/templates/registry/shadcn-pricing.json +28 -0
  121. package/templates/registry/shadcn-tabs.json +30 -0
  122. package/templates/registry/shadcn-testimonials.json +25 -0
  123. package/templates/registry/tabs.json +17 -0
  124. package/templates/starter/.github/workflows/deploy.yml +84 -0
  125. package/templates/starter/.gitlab-ci.yml +28 -0
  126. package/templates/starter/README.md +108 -0
  127. package/templates/starter/_gitignore +2 -0
  128. package/templates/starter/blocks/installed/index.ts +7 -0
  129. package/templates/starter/components.json +24 -0
  130. package/templates/starter/content/collections/news/_collection.json +113 -0
  131. package/templates/starter/content/collections/news/open-house.json +9 -0
  132. package/templates/starter/content/collections/news/welcome.json +10 -0
  133. package/templates/starter/content/layout/footer.json +55 -0
  134. package/templates/starter/content/layout/header.json +54 -0
  135. package/templates/starter/content/menus.json +25 -0
  136. package/templates/starter/content/pages/404.json +73 -0
  137. package/templates/starter/content/pages/about.json +59 -0
  138. package/templates/starter/content/pages/index.json +215 -0
  139. package/templates/starter/content/pages/news.json +55 -0
  140. package/templates/starter/content/site.json +25 -0
  141. package/templates/starter/content/styles/custom.css +4 -0
  142. package/templates/starter/goodfellow.config.tsx +17 -0
  143. package/templates/starter/package.json +36 -0
  144. package/templates/starter/public/media/favicon.svg +1 -0
  145. package/templates/starter/public/media/logo.svg +1 -0
  146. package/templates/starter/public/media/placeholder.svg +1 -0
  147. package/templates/starter/src/styles.css +8 -0
  148. package/templates/starter/tsconfig.json +14 -0
  149. package/templates/starter/vercel.ts +65 -0
  150. package/templates/versions.json +13 -0
@@ -0,0 +1,54 @@
1
+ {
2
+ "version": 1,
3
+ "data": {
4
+ "content": [
5
+ {
6
+ "type": "Section",
7
+ "props": {
8
+ "id": "Section-header",
9
+ "background": "none",
10
+ "backgroundImage": "",
11
+ "className": "",
12
+ "content": [
13
+ {
14
+ "type": "Flex",
15
+ "props": {
16
+ "id": "Flex-header",
17
+ "align": "center",
18
+ "className": "",
19
+ "direction": "row",
20
+ "gap": "md",
21
+ "items": [
22
+ {
23
+ "type": "SiteBrand",
24
+ "props": {
25
+ "id": "SiteBrand-header",
26
+ "className": "",
27
+ "show": "both"
28
+ }
29
+ },
30
+ {
31
+ "type": "Menu",
32
+ "props": {
33
+ "id": "Menu-main",
34
+ "className": "",
35
+ "menu": "main",
36
+ "orientation": "horizontal"
37
+ }
38
+ }
39
+ ],
40
+ "justify": "between",
41
+ "wrap": true
42
+ }
43
+ }
44
+ ],
45
+ "padding": "sm",
46
+ "width": "wide"
47
+ }
48
+ }
49
+ ],
50
+ "root": {
51
+ "props": {}
52
+ }
53
+ }
54
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "version": 1,
3
+ "menus": {
4
+ "footer": [
5
+ {
6
+ "href": "/about",
7
+ "label": "About"
8
+ }
9
+ ],
10
+ "main": [
11
+ {
12
+ "href": "/",
13
+ "label": "Home"
14
+ },
15
+ {
16
+ "href": "/about",
17
+ "label": "About"
18
+ },
19
+ {
20
+ "href": "/news",
21
+ "label": "News"
22
+ }
23
+ ]
24
+ }
25
+ }
@@ -0,0 +1,73 @@
1
+ {
2
+ "version": 1,
3
+ "data": {
4
+ "content": [
5
+ {
6
+ "type": "Section",
7
+ "props": {
8
+ "id": "Section-not-found",
9
+ "background": "none",
10
+ "backgroundImage": "",
11
+ "className": "",
12
+ "content": [
13
+ {
14
+ "type": "Heading",
15
+ "props": {
16
+ "id": "Heading-not-found",
17
+ "align": "center",
18
+ "className": "",
19
+ "level": "h1",
20
+ "text": "Page not found"
21
+ }
22
+ },
23
+ {
24
+ "type": "Text",
25
+ "props": {
26
+ "id": "Text-not-found",
27
+ "align": "center",
28
+ "className": "mt-4 text-muted-foreground",
29
+ "content": "<p>The page you're looking for doesn't exist or has moved.</p>"
30
+ }
31
+ },
32
+ {
33
+ "type": "Flex",
34
+ "props": {
35
+ "id": "Flex-not-found",
36
+ "align": "center",
37
+ "className": "mt-8",
38
+ "direction": "row",
39
+ "gap": "md",
40
+ "items": [
41
+ {
42
+ "type": "Button",
43
+ "props": {
44
+ "id": "Button-home",
45
+ "className": "",
46
+ "href": "/",
47
+ "label": "Go to the home page",
48
+ "newTab": false,
49
+ "size": "md",
50
+ "variant": "primary"
51
+ }
52
+ }
53
+ ],
54
+ "justify": "center",
55
+ "wrap": true
56
+ }
57
+ }
58
+ ],
59
+ "padding": "lg",
60
+ "width": "narrow"
61
+ }
62
+ }
63
+ ],
64
+ "root": {
65
+ "props": {
66
+ "className": "",
67
+ "description": "",
68
+ "image": "",
69
+ "title": "Page not found"
70
+ }
71
+ }
72
+ }
73
+ }
@@ -0,0 +1,59 @@
1
+ {
2
+ "version": 1,
3
+ "data": {
4
+ "content": [
5
+ {
6
+ "type": "Section",
7
+ "props": {
8
+ "id": "Section-about",
9
+ "background": "none",
10
+ "backgroundImage": "",
11
+ "className": "",
12
+ "content": [
13
+ {
14
+ "type": "Heading",
15
+ "props": {
16
+ "id": "Heading-about",
17
+ "align": "left",
18
+ "className": "",
19
+ "level": "h1",
20
+ "text": "About us"
21
+ }
22
+ },
23
+ {
24
+ "type": "Text",
25
+ "props": {
26
+ "id": "Text-about",
27
+ "align": "left",
28
+ "className": "mt-6",
29
+ "content": "<p>Tell visitors who you are. This text supports <strong>bold</strong>, <em>italics</em>, lists and <a href=\"/\">links</a>.</p><ul><li>Founded in 2026</li><li>Based wherever you are</li></ul>"
30
+ }
31
+ },
32
+ {
33
+ "type": "Image",
34
+ "props": {
35
+ "id": "Image-about",
36
+ "alt": "",
37
+ "aspect": "wide",
38
+ "caption": "Replace this image with one of your own.",
39
+ "className": "mt-8",
40
+ "rounded": true,
41
+ "src": "/media/placeholder.svg"
42
+ }
43
+ }
44
+ ],
45
+ "padding": "lg",
46
+ "width": "narrow"
47
+ }
48
+ }
49
+ ],
50
+ "root": {
51
+ "props": {
52
+ "className": "",
53
+ "description": "Who we are and what we do.",
54
+ "image": "",
55
+ "title": "About us"
56
+ }
57
+ }
58
+ }
59
+ }
@@ -0,0 +1,215 @@
1
+ {
2
+ "version": 1,
3
+ "data": {
4
+ "content": [
5
+ {
6
+ "type": "Section",
7
+ "props": {
8
+ "id": "Section-hero",
9
+ "background": "primary",
10
+ "backgroundImage": "",
11
+ "className": "",
12
+ "content": [
13
+ {
14
+ "type": "Heading",
15
+ "props": {
16
+ "id": "Heading-hero",
17
+ "align": "left",
18
+ "className": "",
19
+ "level": "h1",
20
+ "text": "Welcome to my site"
21
+ }
22
+ },
23
+ {
24
+ "type": "Text",
25
+ "props": {
26
+ "id": "Text-hero",
27
+ "align": "left",
28
+ "className": "mt-4 max-w-2xl text-lg opacity-90",
29
+ "content": "<p>This page is stored as a file in your site's repository. Edit it in the admin panel, or change <code>content/pages/index.json</code> directly.</p>"
30
+ }
31
+ },
32
+ {
33
+ "type": "Flex",
34
+ "props": {
35
+ "id": "Flex-hero",
36
+ "align": "center",
37
+ "className": "mt-8",
38
+ "direction": "row",
39
+ "gap": "md",
40
+ "items": [
41
+ {
42
+ "type": "Button",
43
+ "props": {
44
+ "id": "Button-primary",
45
+ "className": "",
46
+ "href": "/about",
47
+ "label": "About us",
48
+ "newTab": false,
49
+ "size": "lg",
50
+ "variant": "secondary"
51
+ }
52
+ },
53
+ {
54
+ "type": "Button",
55
+ "props": {
56
+ "id": "Button-secondary",
57
+ "className": "",
58
+ "href": "/about",
59
+ "label": "Contact",
60
+ "newTab": false,
61
+ "size": "lg",
62
+ "variant": "outline"
63
+ }
64
+ }
65
+ ],
66
+ "justify": "start",
67
+ "wrap": true
68
+ }
69
+ }
70
+ ],
71
+ "padding": "lg",
72
+ "width": "normal"
73
+ }
74
+ },
75
+ {
76
+ "type": "Section",
77
+ "props": {
78
+ "id": "Section-features",
79
+ "background": "none",
80
+ "backgroundImage": "",
81
+ "className": "",
82
+ "content": [
83
+ {
84
+ "type": "Heading",
85
+ "props": {
86
+ "id": "Heading-features",
87
+ "align": "center",
88
+ "className": "mb-10",
89
+ "level": "h2",
90
+ "text": "Everything in one place"
91
+ }
92
+ },
93
+ {
94
+ "type": "Grid",
95
+ "props": {
96
+ "id": "Grid-features",
97
+ "className": "",
98
+ "columns": "3",
99
+ "gap": "md",
100
+ "items": [
101
+ {
102
+ "type": "Section",
103
+ "props": {
104
+ "id": "Section-feature-1",
105
+ "background": "none",
106
+ "backgroundImage": "",
107
+ "className": "rounded-lg border px-2 py-6",
108
+ "content": [
109
+ {
110
+ "type": "Heading",
111
+ "props": {
112
+ "id": "Heading-feature-1",
113
+ "align": "left",
114
+ "className": "",
115
+ "level": "h3",
116
+ "text": "Pages"
117
+ }
118
+ },
119
+ {
120
+ "type": "Text",
121
+ "props": {
122
+ "id": "Text-feature-1",
123
+ "align": "left",
124
+ "className": "mt-2 text-muted-foreground",
125
+ "content": "<p>Build pages from blocks: sections, columns, text, images and buttons.</p>"
126
+ }
127
+ }
128
+ ],
129
+ "padding": "none",
130
+ "width": "full"
131
+ }
132
+ },
133
+ {
134
+ "type": "Section",
135
+ "props": {
136
+ "id": "Section-feature-2",
137
+ "background": "none",
138
+ "backgroundImage": "",
139
+ "className": "rounded-lg border px-2 py-6",
140
+ "content": [
141
+ {
142
+ "type": "Heading",
143
+ "props": {
144
+ "id": "Heading-feature-2",
145
+ "align": "left",
146
+ "className": "",
147
+ "level": "h3",
148
+ "text": "Your design"
149
+ }
150
+ },
151
+ {
152
+ "type": "Text",
153
+ "props": {
154
+ "id": "Text-feature-2",
155
+ "align": "left",
156
+ "className": "mt-2 text-muted-foreground",
157
+ "content": "<p>Set colors and fonts once in the site settings, and every page follows.</p>"
158
+ }
159
+ }
160
+ ],
161
+ "padding": "none",
162
+ "width": "full"
163
+ }
164
+ },
165
+ {
166
+ "type": "Section",
167
+ "props": {
168
+ "id": "Section-feature-3",
169
+ "background": "none",
170
+ "backgroundImage": "",
171
+ "className": "rounded-lg border px-2 py-6",
172
+ "content": [
173
+ {
174
+ "type": "Heading",
175
+ "props": {
176
+ "id": "Heading-feature-3",
177
+ "align": "left",
178
+ "className": "",
179
+ "level": "h3",
180
+ "text": "No hosting bills"
181
+ }
182
+ },
183
+ {
184
+ "type": "Text",
185
+ "props": {
186
+ "id": "Text-feature-3",
187
+ "align": "left",
188
+ "className": "mt-2 text-muted-foreground",
189
+ "content": "<p>The whole site is static files, so free hosting is enough.</p>"
190
+ }
191
+ }
192
+ ],
193
+ "padding": "none",
194
+ "width": "full"
195
+ }
196
+ }
197
+ ]
198
+ }
199
+ }
200
+ ],
201
+ "padding": "lg",
202
+ "width": "normal"
203
+ }
204
+ }
205
+ ],
206
+ "root": {
207
+ "props": {
208
+ "className": "",
209
+ "description": "",
210
+ "image": "",
211
+ "title": "Welcome"
212
+ }
213
+ }
214
+ }
215
+ }
@@ -0,0 +1,55 @@
1
+ {
2
+ "version": 1,
3
+ "data": {
4
+ "content": [
5
+ {
6
+ "type": "Section",
7
+ "props": {
8
+ "id": "Section-news-list",
9
+ "background": "none",
10
+ "backgroundImage": "",
11
+ "className": "",
12
+ "content": [
13
+ {
14
+ "type": "Heading",
15
+ "props": {
16
+ "id": "Heading-news",
17
+ "align": "left",
18
+ "className": "",
19
+ "level": "h1",
20
+ "text": "News"
21
+ }
22
+ },
23
+ {
24
+ "type": "CollectionList",
25
+ "props": {
26
+ "id": "CollectionList-news",
27
+ "className": "mt-8",
28
+ "collection": "news",
29
+ "columns": "3",
30
+ "dateField": "date",
31
+ "emptyText": "No news yet.",
32
+ "imageField": "image",
33
+ "layout": "cards",
34
+ "limit": 0,
35
+ "order": "default",
36
+ "show": "all",
37
+ "summaryField": "summary"
38
+ }
39
+ }
40
+ ],
41
+ "padding": "lg",
42
+ "width": "normal"
43
+ }
44
+ }
45
+ ],
46
+ "root": {
47
+ "props": {
48
+ "className": "",
49
+ "description": "The latest news.",
50
+ "image": "",
51
+ "title": "News"
52
+ }
53
+ }
54
+ }
55
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "version": 1,
3
+ "description": "A website built with Goodfellow.",
4
+ "favicon": "/media/favicon.svg",
5
+ "language": "en",
6
+ "logo": {
7
+ "alt": "My site",
8
+ "src": "/media/logo.svg"
9
+ },
10
+ "theme": {
11
+ "colors": {
12
+ "border": "#e2e8f0",
13
+ "muted": "#f1f5f9",
14
+ "mutedForeground": "#64748b",
15
+ "primary": "#1e3a8a",
16
+ "primaryForeground": "#ffffff"
17
+ },
18
+ "fonts": {
19
+ "body": "Inter",
20
+ "heading": "Fraunces"
21
+ }
22
+ },
23
+ "title": "My site",
24
+ "titleTemplate": "%s | My site"
25
+ }
@@ -0,0 +1,4 @@
1
+ /* Site-wide CSS. Anything here overrides the blocks' own styles. */
2
+ .gf-header {
3
+ border-bottom: 1px solid var(--color-border);
4
+ }
@@ -0,0 +1,17 @@
1
+ import { blocks, categories } from "@goodfellow-cms/blocks";
2
+ import { defineConfig } from "@goodfellow-cms/core";
3
+ // import { github } from "@goodfellow-cms/github";
4
+ // import { gitlab } from "@goodfellow-cms/gitlab";
5
+ import { installedBlocks, installedCategories } from "./blocks/installed";
6
+
7
+ export default defineConfig({
8
+ // The built-in blocks, and those installed from the admin panel's Blocks screen.
9
+ // Add your own here too, e.g. `blocks: { ...blocks, ...installedBlocks, MassTimes }`.
10
+ blocks: { ...blocks, ...installedBlocks },
11
+ categories: { ...categories, ...installedCategories },
12
+
13
+ // Where the site is stored. Once this is set, builds include the admin panel at /admin.
14
+ // See README.md for how to set up sign-in.
15
+ // backend: github({ repo: "your-name/your-site" }),
16
+ // backend: gitlab({ project: "your-name/your-site", clientId: "your-application-id" }),
17
+ });
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@goodfellow-cms/template-starter",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "goodfellow dev",
8
+ "build": "goodfellow build",
9
+ "preview": "goodfellow preview",
10
+ "typecheck": "tsc -p tsconfig.json"
11
+ },
12
+ "dependencies": {
13
+ "@goodfellow-cms/admin": "workspace:*",
14
+ "@goodfellow-cms/blocks": "workspace:*",
15
+ "@goodfellow-cms/core": "workspace:*",
16
+ "@goodfellow-cms/github": "workspace:*",
17
+ "@goodfellow-cms/gitlab": "workspace:*",
18
+ "@goodfellow-cms/react": "workspace:*",
19
+ "@puckeditor/core": "0.23.0",
20
+ "class-variance-authority": "0.7.1",
21
+ "clsx": "2.1.1",
22
+ "cn": "0.4.0",
23
+ "embla-carousel-react": "8.6.0",
24
+ "lucide-react": "1.52.0",
25
+ "radix-ui": "1.7.0",
26
+ "react": "19.3.0",
27
+ "react-dom": "19.3.0",
28
+ "tailwind-merge": "3.7.0",
29
+ "tailwindcss": "4.3.3",
30
+ "tw-animate-css": "1.4.0"
31
+ },
32
+ "devDependencies": {
33
+ "goodfellow": "workspace:*",
34
+ "@types/react": "^19.2.0"
35
+ }
36
+ }
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40"><rect width="40" height="40" rx="10" fill="#1e3a8a"/><path d="M12 28V12h6.5a5 5 0 0 1 0 10H16v6zm4-9.5h2.4a1.75 1.75 0 0 0 0-3.5H16z" fill="#fff"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40"><rect width="40" height="40" rx="10" fill="#1e3a8a"/><path d="M12 28V12h6.5a5 5 0 0 1 0 10H16v6zm4-9.5h2.4a1.75 1.75 0 0 0 0-3.5H16z" fill="#fff"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 450"><rect width="800" height="450" fill="#e2e8f0"/><path d="M0 450 260 210l140 130 110-90 290 200z" fill="#cbd5e1"/><circle cx="600" cy="130" r="50" fill="#cbd5e1"/></svg>
@@ -0,0 +1,8 @@
1
+ @import "tailwindcss";
2
+ /* Animations for blocks installed from block registries, such as an FAQ's opening answers. */
3
+ @import "tw-animate-css";
4
+ @import "@goodfellow-cms/react/theme.css";
5
+ @import "@goodfellow-cms/blocks/styles.css";
6
+
7
+ /* Lets Tailwind find class names typed into the editor. */
8
+ @source "../content";
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "jsx": "react-jsx",
7
+ "strict": true,
8
+ "skipLibCheck": true,
9
+ "noEmit": true,
10
+ // As in shadcn/ui projects: blocks installed from block registries import from "@/components/ui/…".
11
+ "paths": { "@/*": ["./*"] }
12
+ },
13
+ "include": ["goodfellow.config.tsx", "blocks", "components", "lib", "hooks"]
14
+ }
@@ -0,0 +1,65 @@
1
+ // Vercel's settings for this site, which Vercel reads when it builds the site.
2
+ //
3
+ // Vercel also sends the redirects from Site Settings → Redirects (content/redirects.json) itself,
4
+ // so visitors go straight to the new address. Like the pages the build writes at old addresses
5
+ // for other hosts, a redirect from an address a page has is left out: pages always win.
6
+ // This file uses only Node's own modules, since Vercel may read it before installing packages.
7
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
8
+ import { join } from "node:path";
9
+
10
+ export const config = {
11
+ framework: null,
12
+ installCommand: "npm ci",
13
+ buildCommand: "npx goodfellow build",
14
+ outputDirectory: "dist",
15
+ redirects: redirects(),
16
+ };
17
+
18
+ interface Redirect {
19
+ from: string;
20
+ to: string;
21
+ }
22
+
23
+ function readJson(file: string): Record<string, unknown> | undefined {
24
+ return existsSync(file) ? JSON.parse(readFileSync(file, "utf8")) : undefined;
25
+ }
26
+
27
+ /** Every file in a folder and its folders, as paths inside it. */
28
+ function filesIn(dir: string): string[] {
29
+ if (!existsSync(dir)) return [];
30
+ return readdirSync(dir, { recursive: true, encoding: "utf8" }).map((file) => file.split("\\").join("/"));
31
+ }
32
+
33
+ /** The addresses that have pages: `content/pages/about.json` is /about, and each collection item has its own. */
34
+ function pageAddresses(): Set<string> {
35
+ const addresses = new Set<string>();
36
+ for (const file of filesIn("content/pages")) {
37
+ if (file.endsWith(".json")) addresses.add(`/${file.slice(0, -".json".length)}`.replace(/\/?index$/, "") || "/");
38
+ }
39
+ for (const id of filesIn("content/collections").filter((file) => file.endsWith("/_collection.json"))) {
40
+ const folder = id.slice(0, -"/_collection.json".length);
41
+ const pattern = readJson(join("content/collections", id))?.path;
42
+ if (typeof pattern !== "string") continue;
43
+ for (const file of readdirSync(join("content/collections", folder))) {
44
+ const slug = /^(.+)\.(json|md)$/.exec(file)?.[1];
45
+ if (slug && file !== "_collection.json") addresses.add(pattern.replace("{slug}", slug).replace(/\/+$/, ""));
46
+ }
47
+ }
48
+ return addresses;
49
+ }
50
+
51
+ /** An address as Vercel matches it, with the characters its patterns use escaped. */
52
+ function source(path: string): string {
53
+ return encodeURI(path).replace(/[()[\]{}+*?:!]/g, "\\$&");
54
+ }
55
+
56
+ function redirects() {
57
+ const list = (readJson("content/redirects.json")?.redirects ?? []) as Redirect[];
58
+ const pages = pageAddresses();
59
+ return list.flatMap(({ from, to }) => {
60
+ const path = from.replace(/\/+$/, "");
61
+ if (pages.has(path)) return [];
62
+ // With and without a trailing slash, as the other hosts serve the page there.
63
+ return [path, `${path}/`].map((address) => ({ source: source(address), destination: to, permanent: true }));
64
+ });
65
+ }