create-goodfellow 0.1.0 → 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 (55) hide show
  1. package/README.md +2 -2
  2. package/dist/cli.js +36 -3
  3. package/dist/index.d.ts +6 -79
  4. package/dist/index.js +2 -2
  5. package/dist/scaffold-BGxzPdN1.js +150 -0
  6. package/package.json +6 -6
  7. package/templates/next/.github/workflows/deploy.yml +37 -2
  8. package/templates/next/.gitlab-ci.yml +12 -2
  9. package/templates/next/README.md +14 -7
  10. package/templates/next/app/admin/layout.tsx +2 -2
  11. package/templates/parish/.github/workflows/deploy.yml +37 -2
  12. package/templates/parish/.gitlab-ci.yml +12 -2
  13. package/templates/parish/README.md +14 -7
  14. package/templates/parish/content/collections/events/_collection.json +19 -20
  15. package/templates/parish/content/collections/events/advent-penance-service.json +5 -3
  16. package/templates/parish/content/collections/events/all-souls.json +5 -3
  17. package/templates/parish/content/collections/events/christmas-concert.json +5 -3
  18. package/templates/parish/content/collections/events/fall-festival.json +5 -3
  19. package/templates/parish/content/collections/events/youth-group.json +23 -0
  20. package/templates/parish/content/collections/news/_collection.json +24 -0
  21. package/templates/parish/content/collections/news/bell-tower-repairs.json +13 -0
  22. package/templates/parish/content/collections/news/choir-new-voices.json +14 -0
  23. package/templates/parish/content/collections/news/food-drive.json +4 -1
  24. package/templates/parish/content/collections/news/new-website.json +4 -1
  25. package/templates/parish/content/collections/news/winter-coats.json +13 -0
  26. package/templates/parish/content/collections/news/youth-group.json +4 -1
  27. package/templates/parish/content/collections/news/youth-service-day.json +14 -0
  28. package/templates/parish/content/menus.json +4 -0
  29. package/templates/parish/content/pages/about.json +48 -5
  30. package/templates/parish/content/pages/calendar.json +59 -0
  31. package/templates/parish/content/pages/events.json +27 -11
  32. package/templates/parish/content/pages/index.json +4 -37
  33. package/templates/parish/content/pages/news.json +14 -1
  34. package/templates/parish/content/site.json +1 -0
  35. package/templates/parish/vercel.ts +65 -0
  36. package/templates/registry/custom-html.json +1 -1
  37. package/templates/registry/registry.json +11 -11
  38. package/templates/registry/shadcn-call-to-action.json +1 -1
  39. package/templates/registry/shadcn-cards.json +1 -1
  40. package/templates/registry/shadcn-carousel.json +1 -1
  41. package/templates/registry/shadcn-faq.json +1 -1
  42. package/templates/registry/shadcn-hero.json +1 -1
  43. package/templates/registry/shadcn-notice.json +1 -1
  44. package/templates/registry/shadcn-pricing.json +1 -1
  45. package/templates/registry/shadcn-tabs.json +1 -1
  46. package/templates/registry/shadcn-testimonials.json +1 -1
  47. package/templates/starter/.github/workflows/deploy.yml +37 -2
  48. package/templates/starter/.gitlab-ci.yml +12 -2
  49. package/templates/starter/README.md +14 -7
  50. package/templates/starter/vercel.ts +65 -0
  51. package/templates/versions.json +11 -11
  52. package/dist/scaffold-4nOF_fOJ.js +0 -207
  53. package/templates/next/tsconfig.tsbuildinfo +0 -1
  54. package/templates/parish/vercel.json +0 -7
  55. package/templates/starter/vercel.json +0 -7
package/README.md CHANGED
@@ -7,9 +7,9 @@ npm create goodfellow@latest my-site
7
7
  npm create goodfellow@latest my-site -- --template parish --host github-pages
8
8
  ```
9
9
 
10
- [Goodfellow](https://github.com/chartung17/goodfellow-cms) is a website builder for people who aren't developers, with no server or database: pages are files in your own GitHub or GitLab repository, edited in a visual editor built on [Puck](https://puckeditor.com). To start a new site, run `npm create goodfellow@latest my-site`. See the [documentation](https://chartung17.github.io/goodfellow-cms/) for everything else.
10
+ [Goodfellow](https://github.com/goodfellow-cms/goodfellow-cms) is a website builder for people who aren't developers, with no server or database: pages are files in your own GitHub or GitLab repository, edited in a visual editor built on [Puck](https://puckeditor.com). To start a new site, run `npm create goodfellow@latest my-site`. See the [documentation](https://goodfellow-cms.github.io/goodfellow-cms/) for everything else.
11
11
 
12
- More in the docs: [Create a site](https://chartung17.github.io/goodfellow-cms/docs/create-a-site).
12
+ More in the docs: [Create a site](https://goodfellow-cms.github.io/goodfellow-cms/docs/create-a-site).
13
13
 
14
14
  ## License
15
15
 
package/dist/cli.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as TEMPLATES, c as hostsFor, d as parseRepo, f as scaffold, i as ScaffoldError, r as HOSTS, t as BLOCK_CHOICES } from "./scaffold-4nOF_fOJ.js";
1
+ import { a as TEMPLATES, d as installPackages, h as scaffold, i as ScaffoldError, l as hostsFor, o as commitAll, p as parseRepo, r as HOSTS, t as BLOCK_CHOICES, u as initGitRepository } from "./scaffold-BGxzPdN1.js";
2
2
  import { join, relative, resolve } from "node:path";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { stdin, stdout } from "node:process";
@@ -16,6 +16,8 @@ Options:
16
16
  --gitlab <project> The GitLab project the site will be stored in, such as your-group/your-site
17
17
  --host <host> github-pages, gitlab-pages or vercel. Keeps only that host's setup file.
18
18
  --blocks <choice> recommended (the default) to add the recommended blocks, or built-in for Goodfellow's own only
19
+ --no-install Don't run npm install
20
+ --no-git Don't make the site a git repository, or commit it
19
21
  -y, --yes Don't ask anything: use the options given, and defaults for the rest
20
22
  -h, --help Show this help
21
23
  `;
@@ -72,6 +74,8 @@ async function main() {
72
74
  gitlab: { type: "string" },
73
75
  host: { type: "string" },
74
76
  blocks: { type: "string" },
77
+ "no-git": { type: "boolean" },
78
+ "no-install": { type: "boolean" },
75
79
  yes: {
76
80
  type: "boolean",
77
81
  short: "y"
@@ -150,14 +154,24 @@ async function main() {
150
154
  ...host && { host },
151
155
  ...(blocks ?? "recommended") === "recommended" && { registryDir: join(templatesDir, "registry") }
152
156
  });
157
+ const git = values["no-git"] ? void 0 : initGitRepository(target, backend);
158
+ let installed = false;
159
+ if (!values["no-install"]) {
160
+ stdout.write("\nInstalling the site's packages with npm...\n\n");
161
+ installed = installPackages(target);
162
+ }
163
+ const commit = git?.created && (installed || values["no-install"]) ? commitAll(target, "Create the site") : void 0;
153
164
  const where = relative(process.cwd(), target) || ".";
165
+ const cd = where === "." ? [] : [` cd ${/\s/.test(where) ? JSON.stringify(where) : where}`];
154
166
  const lines = [
155
167
  "",
156
168
  `Created a new site in ${where}.`,
169
+ ...git ? gitLines(git, commit, installed) : [],
170
+ ...!installed && !values["no-install"] ? ["npm install didn't finish, so run it again in the site's folder."] : [],
157
171
  "",
158
172
  "Next:",
159
- ...where === "." ? [] : [` cd ${/\s/.test(where) ? JSON.stringify(where) : where}`],
160
- " npm install",
173
+ ...cd,
174
+ ...installed ? [] : [" npm install"],
161
175
  " npm run dev",
162
176
  "",
163
177
  "Then open http://localhost:4321/admin to start editing. README.md explains how to put the site online."
@@ -165,6 +179,25 @@ async function main() {
165
179
  if (!backend) lines.push("Before you do, say where the site is stored in goodfellow.config.tsx; README.md shows how.");
166
180
  stdout.write(`${lines.join("\n")}\n`);
167
181
  }
182
+ /** What happened with git, and what's left to do. */
183
+ function gitLines(git, commit, installed) {
184
+ if (!git.created) return [git.reason === "inside-repository" ? "It's inside another git repository, so it isn't one of its own." : "Git isn't installed, so it isn't a git repository yet: once it is, run git init -b main in it, then commit."];
185
+ const repository = "It's a git repository on the branch main";
186
+ const origin = git.remote ? [`Its origin is ${git.remote}.`] : [];
187
+ if (commit === "committed") return [
188
+ `${repository}, with everything in its first commit.`,
189
+ ...origin,
190
+ ...installed ? [] : ["Commit package-lock.json after npm install: the deploy setups need it."]
191
+ ];
192
+ if (commit === "no-identity") return [
193
+ `${repository}, but git doesn't know your name and email yet, so nothing is committed. Tell it, then commit:`,
194
+ " git config --global user.name \"Your Name\"",
195
+ " git config --global user.email you@example.com",
196
+ " git add -A && git commit -m \"Create the site\"",
197
+ ...origin
198
+ ];
199
+ return [`${repository}. Nothing is committed yet: commit everything once npm install has run.`, ...origin];
200
+ }
168
201
  async function chooseHost(rl, storage) {
169
202
  stdout.write("\nEach host's free plan has its own rules about business use. Check them if the site is for a business,");
170
203
  stdout.write("\nsells anything, shows ads, or anyone is paid to build or look after it.\n");
package/dist/index.d.ts CHANGED
@@ -1,56 +1,9 @@
1
+ import { BLOCK_CHOICES, BUNDLED_GITIGNORE, Backend, Backend as Backend$1, BlockChoice, HOSTS, Host, Host as Host$1, SiteFiles, TEMPLATES, TemplateName, configureBackend, hostsFor, parseRepo, sitePackageJson, withoutRepositoryNotes } from "@goodfellow-cms/core";
1
2
  //#region src/scaffold.d.ts
2
- /** The sites a new site can start from. The keys are what `--template` takes. */
3
- export declare const TEMPLATES: {
4
- readonly starter: {
5
- readonly label: "Starter";
6
- readonly description: "A simple site with a few pages and a news section";
7
- };
8
- readonly parish: {
9
- readonly label: "Parish example";
10
- readonly description: "A made-up parish with Mass times, events, news, bulletins and staff, and blocks of its own";
11
- };
12
- readonly next: {
13
- readonly label: "Starter for Next.js";
14
- readonly description: "The starter as a Next.js site, for developers who want their own Next.js pages beside it";
15
- };
16
- };
17
- type TemplateName = keyof typeof TEMPLATES;
18
- /** Where the site's repository is stored, which the admin panel publishes to. */
19
- interface Backend {
20
- host: "github" | "gitlab";
21
- /** `owner/name` on GitHub, or the project's path on GitLab, such as `group/subgroup/site`. */
22
- repo: string;
23
- }
24
- /** Where the built site is served from. Each has its own setup file in the template. */
25
- export declare const HOSTS: {
26
- readonly "github-pages": {
27
- readonly label: "GitHub Pages";
28
- readonly files: readonly [".github/workflows/deploy.yml"];
29
- readonly note: "Free for public repositories. Not allowed for online businesses or shops.";
30
- };
31
- readonly "gitlab-pages": {
32
- readonly label: "GitLab Pages";
33
- readonly files: readonly [".gitlab-ci.yml"];
34
- readonly note: "Free, including for private projects. No rule against business sites that we know of.";
35
- };
36
- readonly vercel: {
37
- readonly label: "Vercel";
38
- readonly files: readonly ["vercel.json"];
39
- readonly note: "Works with GitHub and GitLab. The free plan is for non-commercial sites only.";
40
- };
41
- };
42
- type Host = keyof typeof HOSTS;
43
- /** The hosts that can serve a site stored with `backend`: GitHub Pages only builds GitHub repositories, and so on. */
44
- export declare function hostsFor(backend: Backend["host"] | undefined): Host[];
45
3
  /** A problem the person creating the site can fix, described in plain words. */
46
4
  export declare class ScaffoldError extends Error {
47
5
  name: string;
48
6
  }
49
- /**
50
- * npm leaves `.gitignore` files out of published packages, so templates are
51
- * bundled with `_gitignore` instead, and it's renamed back in the new site.
52
- */
53
- export declare const BUNDLED_GITIGNORE = "_gitignore";
54
7
  /**
55
8
  * Copies a template's files, leaving out installed packages and build output.
56
9
  * `bundling` renames `.gitignore` files for publishing; otherwise they're renamed back.
@@ -60,34 +13,8 @@ export declare function copyTemplate(from: string, to: string, { bundling }?: {
60
13
  }): Promise<void>;
61
14
  /** A valid npm package name from a folder name, such as `My Parish` → `my-parish`. */
62
15
  export declare function packageName(folder: string): string;
63
- /**
64
- * Reads a repository from what someone typed: `owner/name`, or its address on
65
- * GitHub or GitLab. Returns `undefined` if it isn't one.
66
- */
67
- export declare function parseRepo(host: Backend["host"], input: string): string | undefined;
68
- /** The template's `package.json`, named for the new site and with workspace links replaced by published versions. */
69
- export declare function sitePackageJson(source: string, name: string, versions: Record<string, string>): string;
70
- /** Turns on one of the commented-out `backend` lines in a template's `goodfellow.config.tsx`. */
71
- export declare function configureBackend(config: string, backend: Backend): string;
72
- /** A site's README without the notes for people reading it in the Goodfellow repository. */
73
- export declare function withoutRepositoryNotes(readme: string): string;
74
- /** Which blocks a new site starts with, besides the built-in ones. */
75
- export declare const BLOCK_CHOICES: {
76
- readonly recommended: {
77
- readonly label: "Recommended";
78
- readonly description: "Adds the recommended blocks, such as Hero, Cards and FAQ. Others can be added in the admin panel.";
79
- };
80
- readonly "built-in": {
81
- readonly label: "Built-in blocks only";
82
- readonly description: "Starts with Goodfellow's own blocks. More can be added in the admin panel.";
83
- };
84
- };
85
- type BlockChoice = keyof typeof BLOCK_CHOICES;
86
- /**
87
- * Installs the registry's recommended blocks into a new site, as the admin
88
- * panel's Blocks screen would, so the site can add and remove them there later.
89
- */
90
- export declare function installRecommendedBlocks(target: string, registryDir: string): Promise<string[]>;
16
+ /** Reads a template's files, leaving out installed packages and build output, as `copyTemplate()` does. */
17
+ export declare function readTemplate(dir: string): Promise<SiteFiles>;
91
18
  interface ScaffoldOptions {
92
19
  /** The template's folder. */
93
20
  template: string;
@@ -95,13 +22,13 @@ interface ScaffoldOptions {
95
22
  target: string;
96
23
  /** Versions of Goodfellow's packages, by name, to replace the template's workspace links. */
97
24
  versions: Record<string, string>;
98
- backend?: Backend;
25
+ backend?: Backend$1;
99
26
  /** Keeps only this host's setup file. All are kept if it isn't set. */
100
- host?: Host;
27
+ host?: Host$1;
101
28
  /** The built block registry to install recommended blocks from. Without it, the site has the built-in blocks only. */
102
29
  registryDir?: string;
103
30
  }
104
31
  /** Creates a new site in `target` from a template. */
105
32
  export declare function scaffold({ template, target, versions, backend, host, registryDir }: ScaffoldOptions): Promise<void>;
106
33
  //#endregion
107
- export type { Backend, BlockChoice, Host, ScaffoldOptions, TemplateName };
34
+ export { BLOCK_CHOICES, BUNDLED_GITIGNORE, type Backend, type BlockChoice, HOSTS, type Host, type ScaffoldOptions, TEMPLATES, type TemplateName, configureBackend, hostsFor, parseRepo, sitePackageJson, withoutRepositoryNotes };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { a as TEMPLATES, c as hostsFor, d as parseRepo, f as scaffold, i as ScaffoldError, l as installRecommendedBlocks, m as withoutRepositoryNotes, n as BUNDLED_GITIGNORE, o as configureBackend, p as sitePackageJson, r as HOSTS, s as copyTemplate, t as BLOCK_CHOICES, u as packageName } from "./scaffold-4nOF_fOJ.js";
2
- export { BLOCK_CHOICES, BUNDLED_GITIGNORE, HOSTS, ScaffoldError, TEMPLATES, configureBackend, copyTemplate, hostsFor, installRecommendedBlocks, packageName, parseRepo, scaffold, sitePackageJson, withoutRepositoryNotes };
1
+ import { _ as withoutRepositoryNotes, a as TEMPLATES, c as copyTemplate, f as packageName, g as sitePackageJson, h as scaffold, i as ScaffoldError, l as hostsFor, m as readTemplate, n as BUNDLED_GITIGNORE, p as parseRepo, r as HOSTS, s as configureBackend, t as BLOCK_CHOICES } from "./scaffold-BGxzPdN1.js";
2
+ export { BLOCK_CHOICES, BUNDLED_GITIGNORE, HOSTS, ScaffoldError, TEMPLATES, configureBackend, copyTemplate, hostsFor, packageName, parseRepo, readTemplate, scaffold, sitePackageJson, withoutRepositoryNotes };
@@ -0,0 +1,150 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { cp, mkdir, readFile, readdir, rename, writeFile } from "node:fs/promises";
3
+ import { basename, dirname, join, relative, resolve, sep } from "node:path";
4
+ import { BLOCK_CHOICES, BUNDLED_GITIGNORE, BUNDLED_GITIGNORE as BUNDLED_GITIGNORE$1, GOODFELLOW_REGISTRY, HOSTS, TEMPLATES, configureBackend, hostsFor, parseRepo, planSite, sitePackageJson, sitePackageName, withoutRepositoryNotes } from "@goodfellow-cms/core";
5
+ //#region src/scaffold.ts
6
+ /** A problem the person creating the site can fix, described in plain words. */
7
+ var ScaffoldError = class extends Error {
8
+ name = "ScaffoldError";
9
+ };
10
+ /** Never copied, wherever they are: installed packages and build caches. */
11
+ const SKIPPED = /* @__PURE__ */ new Set(["node_modules", ".turbo"]);
12
+ /** Never copied from the template's own folder: build output, and files only the Goodfellow repository uses. */
13
+ const SKIPPED_AT_ROOT = /* @__PURE__ */ new Set([
14
+ "dist",
15
+ "out",
16
+ ".next",
17
+ "next-env.d.ts",
18
+ "turbo.json"
19
+ ]);
20
+ /**
21
+ * Copies a template's files, leaving out installed packages and build output.
22
+ * `bundling` renames `.gitignore` files for publishing; otherwise they're renamed back.
23
+ */
24
+ async function copyTemplate(from, to, { bundling = false } = {}) {
25
+ await cp(from, to, {
26
+ recursive: true,
27
+ filter: (source) => {
28
+ const path = relative(from, source).split(sep);
29
+ return !path.some((part) => SKIPPED.has(part)) && !SKIPPED_AT_ROOT.has(path[0] ?? "");
30
+ }
31
+ });
32
+ const [find, replace] = bundling ? [".gitignore", BUNDLED_GITIGNORE] : [BUNDLED_GITIGNORE, ".gitignore"];
33
+ for (const file of await readdir(to, { recursive: true })) if (basename(file) === find) await rename(join(to, file), join(to, file.slice(0, -find.length) + replace));
34
+ }
35
+ /** A valid npm package name from a folder name, such as `My Parish` → `my-parish`. */
36
+ function packageName(folder) {
37
+ return sitePackageName(basename(resolve(folder)));
38
+ }
39
+ /** Reads a built registry from disk, as the admin panel would read it from its address. */
40
+ function localRegistry(dir) {
41
+ const base = "https://registry.invalid/r/";
42
+ return {
43
+ registries: { [GOODFELLOW_REGISTRY]: `${base}{name}.json` },
44
+ fetchJson: async (url) => JSON.parse(await readFile(join(dir, url.slice(27)), "utf8"))
45
+ };
46
+ }
47
+ /** Reads a template's files, leaving out installed packages and build output, as `copyTemplate()` does. */
48
+ async function readTemplate(dir) {
49
+ const files = /* @__PURE__ */ new Map();
50
+ for (const entry of await readdir(dir, {
51
+ recursive: true,
52
+ withFileTypes: true
53
+ })) {
54
+ if (!entry.isFile()) continue;
55
+ const path = relative(dir, join(entry.parentPath, entry.name)).split(sep);
56
+ if (path.some((part) => SKIPPED.has(part)) || SKIPPED_AT_ROOT.has(path[0] ?? "")) continue;
57
+ files.set(path.join("/"), new Uint8Array(await readFile(join(dir, ...path))));
58
+ }
59
+ return files;
60
+ }
61
+ async function isEmptyFolder(path) {
62
+ try {
63
+ return (await readdir(path)).length === 0;
64
+ } catch (error) {
65
+ if (error.code === "ENOENT") return true;
66
+ throw error;
67
+ }
68
+ }
69
+ /** Where a backend's repository is pushed to, over HTTPS. */
70
+ function remoteUrl(backend) {
71
+ return `https://${backend.host === "github" ? "github.com" : "gitlab.com"}/${backend.repo}.git`;
72
+ }
73
+ /**
74
+ * Makes the new site a git repository on the branch `main`, which the deploy
75
+ * setups build from, with `origin` set when the site's repository is known.
76
+ * A folder inside another repository, such as a monorepo, is left as it is.
77
+ */
78
+ function initGitRepository(target, backend) {
79
+ const git = (...args) => spawnSync("git", args, {
80
+ cwd: target,
81
+ encoding: "utf8"
82
+ });
83
+ const inside = git("rev-parse", "--is-inside-work-tree");
84
+ if (inside.error) return {
85
+ created: false,
86
+ reason: "no-git"
87
+ };
88
+ if (inside.status === 0) return {
89
+ created: false,
90
+ reason: "inside-repository"
91
+ };
92
+ if (git("init", "-q", "-b", "main").status !== 0) {
93
+ if (git("init", "-q").status !== 0) return {
94
+ created: false,
95
+ reason: "no-git"
96
+ };
97
+ git("symbolic-ref", "HEAD", "refs/heads/main");
98
+ }
99
+ if (!backend) return { created: true };
100
+ const remote = remoteUrl(backend);
101
+ git("remote", "add", "origin", remote);
102
+ return {
103
+ created: true,
104
+ remote
105
+ };
106
+ }
107
+ /**
108
+ * Runs `npm install` in the new site, showing its output. Its `package-lock.json`
109
+ * goes in the first commit, since the deploy setups install with `npm ci`.
110
+ */
111
+ function installPackages(target) {
112
+ return (process.platform === "win32" ? spawnSync("npm install", {
113
+ cwd: target,
114
+ stdio: "inherit",
115
+ shell: true
116
+ }) : spawnSync("npm", ["install"], {
117
+ cwd: target,
118
+ stdio: "inherit"
119
+ })).status === 0;
120
+ }
121
+ /** Commits everything in the new site, which `.gitignore` doesn't leave out. */
122
+ function commitAll(target, message) {
123
+ const git = (...args) => spawnSync("git", args, {
124
+ cwd: target,
125
+ encoding: "utf8"
126
+ });
127
+ if (git("add", "-A").status !== 0) return "failed";
128
+ const commit = git("commit", "-q", "-m", message);
129
+ if (commit.status === 0) return "committed";
130
+ return /user\.(name|email)|tell me who you are|identity/i.test(commit.stderr) ? "no-identity" : "failed";
131
+ }
132
+ /** Creates a new site in `target` from a template. */
133
+ async function scaffold({ template, target, versions, backend, host, registryDir }) {
134
+ if (!await isEmptyFolder(target)) throw new ScaffoldError(`The folder ${target} already has files in it. Choose a new folder for the site.`);
135
+ const files = await planSite({
136
+ template: await readTemplate(template),
137
+ name: packageName(target),
138
+ versions,
139
+ backend,
140
+ host,
141
+ registry: registryDir ? localRegistry(registryDir) : void 0
142
+ });
143
+ await mkdir(target, { recursive: true });
144
+ for (const [path, file] of files) {
145
+ await mkdir(dirname(join(target, path)), { recursive: true });
146
+ await writeFile(join(target, path), file);
147
+ }
148
+ }
149
+ //#endregion
150
+ export { withoutRepositoryNotes as _, TEMPLATES as a, copyTemplate as c, installPackages as d, packageName as f, sitePackageJson as g, scaffold as h, ScaffoldError as i, hostsFor as l, readTemplate as m, BUNDLED_GITIGNORE$1 as n, commitAll as o, parseRepo as p, HOSTS as r, configureBackend as s, BLOCK_CHOICES as t, initGitRepository as u };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-goodfellow",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Creates a new Goodfellow site: npm create goodfellow@latest my-site",
5
5
  "keywords": [
6
6
  "goodfellow",
@@ -11,12 +11,12 @@
11
11
  "starter",
12
12
  "scaffold"
13
13
  ],
14
- "homepage": "https://chartung17.github.io/goodfellow-cms/docs/create-a-site",
15
- "bugs": "https://github.com/chartung17/goodfellow-cms/issues",
14
+ "homepage": "https://goodfellow-cms.github.io/goodfellow-cms/docs/create-a-site",
15
+ "bugs": "https://github.com/goodfellow-cms/goodfellow-cms/issues",
16
16
  "license": "MIT",
17
17
  "repository": {
18
18
  "type": "git",
19
- "url": "git+https://github.com/chartung17/goodfellow-cms.git",
19
+ "url": "git+https://github.com/goodfellow-cms/goodfellow-cms.git",
20
20
  "directory": "packages/create-goodfellow"
21
21
  },
22
22
  "type": "module",
@@ -39,11 +39,11 @@
39
39
  "node": ">=22"
40
40
  },
41
41
  "dependencies": {
42
- "@goodfellow-cms/core": "0.1.0"
42
+ "@goodfellow-cms/core": "0.2.0"
43
43
  },
44
44
  "devDependencies": {
45
+ "@goodfellow-cms/registry": "0.2.0",
45
46
  "@goodfellow-cms/example-parish": "0.0.0",
46
- "@goodfellow-cms/registry": "0.1.0",
47
47
  "@goodfellow-cms/template-starter": "0.0.0",
48
48
  "@goodfellow-cms/template-next": "0.0.0"
49
49
  },
@@ -1,7 +1,10 @@
1
1
  # Builds the site and publishes it with GitHub Pages whenever the main branch
2
2
  # changes (including every Publish in the admin panel), and once a night so
3
- # date-based content stays current. In the repository's Settings → Pages, set
4
- # "Source" to "GitHub Actions". See README.md.
3
+ # date-based content stays current. Each night it first installs Goodfellow's
4
+ # newest fixes, and publishes them only if the site still builds with them,
5
+ # unless content/updates.json turns automatic updates off.
6
+ # In the repository's Settings → Pages, set "Source" to "GitHub Actions".
7
+ # See README.md.
5
8
  name: Deploy to GitHub Pages
6
9
 
7
10
  on:
@@ -10,6 +13,11 @@ on:
10
13
  schedule:
11
14
  - cron: "17 4 * * *"
12
15
  workflow_dispatch:
16
+ inputs:
17
+ update:
18
+ description: "Update Goodfellow first: fixes, latest, or a release such as 0.5.0 (an older one goes back to it). Leave empty to only build."
19
+ required: false
20
+ default: ""
13
21
 
14
22
  permissions:
15
23
  contents: read
@@ -21,10 +29,36 @@ concurrency:
21
29
  cancel-in-progress: false
22
30
 
23
31
  jobs:
32
+ update:
33
+ # Each night, and when the admin panel's Updates screen asks.
34
+ if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.update != '')
35
+ runs-on: ubuntu-latest
36
+ permissions:
37
+ contents: write
38
+ outputs:
39
+ sha: ${{ steps.update.outputs.sha }}
40
+ steps:
41
+ - uses: actions/checkout@v5
42
+ - uses: actions/setup-node@v5
43
+ with:
44
+ node-version: 22
45
+ cache: npm
46
+ - run: npm ci
47
+ - id: update
48
+ env:
49
+ TO: ${{ inputs.update || 'automatic' }}
50
+ run: npx goodfellow-next update --publish --to "$TO"
51
+
24
52
  build:
53
+ needs: update
54
+ # Also when there was no update to run, or it didn't work: the site is built either way.
55
+ if: ${{ !cancelled() }}
25
56
  runs-on: ubuntu-latest
26
57
  steps:
27
58
  - uses: actions/checkout@v5
59
+ with:
60
+ # The update's commit, when it published one.
61
+ ref: ${{ needs.update.outputs.sha || github.sha }}
28
62
  - uses: actions/setup-node@v5
29
63
  with:
30
64
  node-version: 22
@@ -42,6 +76,7 @@ jobs:
42
76
 
43
77
  deploy:
44
78
  needs: build
79
+ if: ${{ !cancelled() && needs.build.result == 'success' }}
45
80
  runs-on: ubuntu-latest
46
81
  environment:
47
82
  name: github-pages
@@ -1,6 +1,11 @@
1
1
  # Builds the site and publishes it with GitLab Pages whenever the default
2
- # branch changes (including every Publish in the admin panel). For nightly
3
- # rebuilds, add a schedule under Build → Pipeline schedules. See README.md.
2
+ # branch changes (including every Publish in the admin panel). On scheduled
3
+ # pipelines, which the admin panel's Updates screen sets up to run each night,
4
+ # it first installs Goodfellow's newest fixes, unless content/updates.json turns
5
+ # automatic updates off (or the release GOODFELLOW_UPDATE names), and publishes
6
+ # them only if the site still builds with them. That
7
+ # needs "Allow Git push requests to the repository" under Settings → CI/CD →
8
+ # Job token permissions, which the Updates screen turns on. See README.md.
4
9
  pages:
5
10
  image: node:22
6
11
  cache:
@@ -9,6 +14,11 @@ pages:
9
14
  paths: [.npm/, .next/cache/]
10
15
  script:
11
16
  - npm ci --cache .npm --prefer-offline
17
+ - |
18
+ if [ "$CI_PIPELINE_SOURCE" = "schedule" ] || [ -n "$GOODFELLOW_UPDATE" ]; then
19
+ git remote set-url origin "https://gitlab-ci-token:${CI_JOB_TOKEN}@${CI_SERVER_FQDN:-$CI_SERVER_HOST}/${CI_PROJECT_PATH}.git"
20
+ npx goodfellow-next update --publish --to "${GOODFELLOW_UPDATE:-automatic}" || echo "The update wasn't published; building the site as it is."
21
+ fi
12
22
  # Pages may serve the site from a subfolder, such as https://group.gitlab.io/site/.
13
23
  - export GOODFELLOW_BASE="$(node -e 'console.log(new URL(process.env.CI_PAGES_URL).pathname)')"
14
24
  - npx next build
@@ -1,6 +1,6 @@
1
1
  # My site
2
2
 
3
- A website built with [Goodfellow](https://github.com/chartung17/goodfellow-cms) and [Next.js](https://nextjs.org). Pages are files in `content/`, edited in the admin panel at `/admin`, and built into static files that a free host serves.
3
+ A website built with [Goodfellow](https://github.com/goodfellow-cms/goodfellow-cms) and [Next.js](https://nextjs.org). Pages are files in `content/`, edited in the admin panel at `/admin`, and built into static files that a free host serves.
4
4
 
5
5
  ## Working on the site
6
6
 
@@ -27,14 +27,14 @@ The News page and its stories are an example of a collection: a group of similar
27
27
  | `app/(site)/[[...path]]/page.tsx` | Every page in `content/`, rendered as Server Components. Links between pages use `next/link`, and images `next/image`. |
28
28
  | `app/not-found.tsx` | The site's "Page not found" page, `content/pages/404.json` |
29
29
  | `app/admin/page.tsx` | The admin panel |
30
- | `app/admin/demo-content.json/route.ts` | For a [demo](https://github.com/chartung17/goodfellow-cms#demo-mode) (`demo: true` in the config), the copy of the content its admin panel starts from |
30
+ | `app/admin/demo-content.json/route.ts` | For a [demo](https://goodfellow-cms.github.io/goodfellow-cms/docs/demo-mode) (`demo: true` in the config), the copy of the content its admin panel starts from |
31
31
  | `app/site.css` | The site's styles: Tailwind, the theme, and the custom CSS from the admin panel |
32
32
  | `app/layout.tsx`, `app/sitemap.ts`, `app/robots.ts` | The page around everything, and files for search engines |
33
33
  | `next.config.ts` | `withGoodfellow()` builds static files, serves the site from a subfolder when the host needs it, and runs the admin panel's local backend in `next dev` |
34
34
 
35
35
  Your own Next.js pages can go beside these, in `app/`, and import from the project root as `@/`, such as `@/lib/site`.
36
36
 
37
- More blocks, such as an FAQ, tabs and a pricing table, can be added on the admin panel's **Blocks** screen, which publishes their code into `blocks/installed/` and the shadcn components they use into `components/ui/`. See [Blocks from block registries](https://github.com/chartung17/goodfellow-cms#blocks-from-block-registries).
37
+ More blocks, such as an FAQ, tabs and a pricing table, can be added on the admin panel's **Blocks** screen, which publishes their code into `blocks/installed/` and the shadcn components they use into `components/ui/`. See [Add blocks](https://goodfellow-cms.github.io/goodfellow-cms/docs/add-blocks).
38
38
 
39
39
  Blocks are React components in `blocks/`, added to the editor in `goodfellow.config.tsx`. For links and images, use `SiteLink` and `SiteImage` from `@goodfellow-cms/react` rather than `<a>` and `<img>`, so they follow the site's address and use `next/link` and `next/image`. Read the site's settings, menus and collections with `useSite()`.
40
40
 
@@ -48,7 +48,14 @@ Setup takes four steps. It's done once, usually by whoever set up the site.
48
48
 
49
49
  ### 1. Store the site on GitHub or GitLab
50
50
 
51
- Create a repository and push this folder to it, including `package-lock.json`.
51
+ Create an empty repository on GitHub or GitLab, without a README, and push this folder to it:
52
+
53
+ ```sh
54
+ git remote add origin https://github.com/your-name/your-site.git # unless origin is already set
55
+ git push -u origin main
56
+ ```
57
+
58
+ `create-goodfellow` makes the new site a git repository on the branch `main`, with everything, `package-lock.json` included, in its first commit, and sets `origin` when it's given the repository. Commit any changes since (`git add -A` and `git commit`) before pushing.
52
59
 
53
60
  ### 2. Tell Goodfellow where the site is stored
54
61
 
@@ -70,7 +77,7 @@ Also set the site's address under **Site settings → General → Site address**
70
77
 
71
78
  ### 3. Choose a host
72
79
 
73
- Each host's free plan has its own rules about business use. If the site is for a business, sells anything, shows ads, or is built or looked after by someone who's paid for it, check those rules first. As of October 2026, GitHub Pages and Vercel's free plan don't allow most of these, and GitLab Pages has no rule against them that we know of. See [Commercial sites](https://github.com/chartung17/goodfellow-cms#requirements-and-limits) in Goodfellow's README.
80
+ Each host's free plan has its own rules about business use. If the site is for a business, sells anything, shows ads, or is built or looked after by someone who's paid for it, check those rules first. As of October 2026, GitHub Pages and Vercel's free plan don't allow most of these, and GitLab Pages has no rule against them that we know of. See [Hosts and business sites](https://goodfellow-cms.github.io/goodfellow-cms/docs/hosts) in Goodfellow's documentation.
74
81
 
75
82
  **GitHub Pages** (free for public repositories; not for online businesses or shops)
76
83
 
@@ -85,7 +92,7 @@ Each host's free plan has its own rules about business use. If the site is for a
85
92
 
86
93
  **Vercel** (works with GitHub and GitLab; the free plan is for non-commercial sites only)
87
94
 
88
- 1. Import the repository in Vercel, which recognizes it as a Next.js site.
95
+ 1. Import the repository in Vercel, which recognizes it as a Next.js site, and sends the site's redirects itself.
89
96
  2. For nightly rebuilds, create a deploy hook under **Settings → Git → Deploy Hooks**, and call it from a scheduled GitHub Action or GitLab pipeline schedule.
90
97
 
91
98
  Delete the setup files for the hosts you don't use.
@@ -113,7 +120,7 @@ Upload images and files on the admin panel's **Media** screen, or with the **Cho
113
120
 
114
121
  ## Writing with AI
115
122
 
116
- The admin panel's **AI** tab can draft pages, rewrite blocks and fill in news stories. Each editor picks an AI service there. Claude and some others need the editor's own API key and charge for use; there are free options, including copying the request into a chat app such as Claude.ai. To offer only some services, or to turn the assistant off, see `ai` in [Goodfellow's README](https://github.com/chartung17/goodfellow-cms#ai-services).
123
+ The admin panel's **AI** tab can draft pages, rewrite blocks and fill in news stories. Each editor picks an AI service there. Claude and some others need the editor's own API key and charge for use; there are free options, including copying the request into a chat app such as Claude.ai. To offer only some services, or to turn the assistant off, see `ai` in [Configuration](https://goodfellow-cms.github.io/goodfellow-cms/docs/configuration).
117
124
 
118
125
  ## Publishing
119
126
 
@@ -1,7 +1,7 @@
1
- import type { Metadata } from "next";
2
1
  import type { ReactNode } from "react";
2
+ import { site } from "@/lib/site";
3
3
 
4
- export const metadata: Metadata = { title: "Site admin", robots: { index: false } };
4
+ export const generateMetadata = site.adminMetadata;
5
5
 
6
6
  export default function AdminLayout({ children }: { children: ReactNode }) {
7
7
  return children;
@@ -1,7 +1,10 @@
1
1
  # Builds the site and publishes it with GitHub Pages whenever the main branch
2
2
  # changes (including every Publish in the admin panel), and once a night so
3
- # date-based content stays current. In the repository's Settings → Pages, set
4
- # "Source" to "GitHub Actions". See README.md.
3
+ # date-based content stays current. Each night it first installs Goodfellow's
4
+ # newest fixes, and publishes them only if the site still builds with them,
5
+ # unless content/updates.json turns automatic updates off.
6
+ # In the repository's Settings → Pages, set "Source" to "GitHub Actions".
7
+ # See README.md.
5
8
  name: Deploy to GitHub Pages
6
9
 
7
10
  on:
@@ -10,6 +13,11 @@ on:
10
13
  schedule:
11
14
  - cron: "17 4 * * *"
12
15
  workflow_dispatch:
16
+ inputs:
17
+ update:
18
+ description: "Update Goodfellow first: fixes, latest, or a release such as 0.5.0 (an older one goes back to it). Leave empty to only build."
19
+ required: false
20
+ default: ""
13
21
 
14
22
  permissions:
15
23
  contents: read
@@ -21,10 +29,36 @@ concurrency:
21
29
  cancel-in-progress: false
22
30
 
23
31
  jobs:
32
+ update:
33
+ # Each night, and when the admin panel's Updates screen asks.
34
+ if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.update != '')
35
+ runs-on: ubuntu-latest
36
+ permissions:
37
+ contents: write
38
+ outputs:
39
+ sha: ${{ steps.update.outputs.sha }}
40
+ steps:
41
+ - uses: actions/checkout@v5
42
+ - uses: actions/setup-node@v5
43
+ with:
44
+ node-version: 22
45
+ cache: npm
46
+ - run: npm ci
47
+ - id: update
48
+ env:
49
+ TO: ${{ inputs.update || 'automatic' }}
50
+ run: npx goodfellow update --publish --to "$TO"
51
+
24
52
  build:
53
+ needs: update
54
+ # Also when there was no update to run, or it didn't work: the site is built either way.
55
+ if: ${{ !cancelled() }}
25
56
  runs-on: ubuntu-latest
26
57
  steps:
27
58
  - uses: actions/checkout@v5
59
+ with:
60
+ # The update's commit, when it published one.
61
+ ref: ${{ needs.update.outputs.sha || github.sha }}
28
62
  - uses: actions/setup-node@v5
29
63
  with:
30
64
  node-version: 22
@@ -40,6 +74,7 @@ jobs:
40
74
 
41
75
  deploy:
42
76
  needs: build
77
+ if: ${{ !cancelled() && needs.build.result == 'success' }}
43
78
  runs-on: ubuntu-latest
44
79
  environment:
45
80
  name: github-pages