@goodfellow-cms/next 0.9.0 → 0.10.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.
@@ -11,16 +11,26 @@
11
11
  // content is kept in a repository of its own.
12
12
  // `goodfellow-next import wordpress <file or address> [--dry-run]` imports a WordPress site, as `goodfellow import` does.
13
13
  // `goodfellow-next import markdown <folder> [--dry-run]` imports a folder of Markdown files, as `goodfellow import` does.
14
- // `goodfellow-next export wordpress [file]` writes the site as a WordPress export file, as `goodfellow export` does.
14
+ // `goodfellow-next export wordpress [file]` writes the site as a WordPress export file, and `goodfellow-next export
15
+ // markdown [folder] [--scheduled] [--links site|files]` as a folder of Markdown files, as `goodfellow export` does.
16
+ // `goodfellow-next export site [file]` and `goodfellow-next import site <zip or folder>` move content between
17
+ // Goodfellow sites, as `goodfellow export site` and `goodfellow import site` do.
15
18
  // `goodfellow-next blocks add <item> [--registry <folder>] [--native <app>]` makes a block of a shadcn item, as
16
19
  // `goodfellow blocks add` does, and with --native its version for an Expo app.
17
20
  // `goodfellow-next admin add <address>|check|remove` sets the admin address, and `goodfellow-next broker use <address>|remove`
18
- // the token broker, in goodfellow.config.tsx, as `goodfellow admin` and `goodfellow broker` do.
21
+ // the token broker, and `goodfellow-next sign-in service <address>|client-id <id>|remove` how editors sign in, in
22
+ // goodfellow.config.tsx, as `goodfellow admin`, `goodfellow broker` and `goodfellow sign-in` do.
19
23
  import { existsSync, readFileSync } from "node:fs";
20
24
  import { rm } from "node:fs/promises";
21
25
  import { join, relative, resolve } from "node:path";
22
26
  import { parseArgs } from "node:util";
23
- import { buildNotes, CLOUDFLARE_REDIRECTS_LIMIT, configAdmin, formatUpdateResult } from "@goodfellow-cms/core";
27
+ import {
28
+ buildNotes,
29
+ CLOUDFLARE_REDIRECTS_LIMIT,
30
+ configAdmin,
31
+ formatUpdateResult,
32
+ SiteImportError,
33
+ } from "@goodfellow-cms/core";
24
34
  import { reportSiteProblems } from "@goodfellow-cms/core/accessibility";
25
35
  import {
26
36
  AdminSetupError,
@@ -29,22 +39,32 @@ import {
29
39
  checkEarlyTheme,
30
40
  checkSiteAdminAddress,
31
41
  contentRepositoryProblem,
42
+ exportMarkdown,
43
+ exportSite,
32
44
  exportWordPress,
33
45
  formatAdminResult,
34
46
  formatAdminText,
47
+ formatMarkdownExport,
35
48
  formatMarkdownImport,
49
+ formatSiteExport,
50
+ formatSiteImport,
36
51
  formatWordPressExport,
37
52
  formatWordPressImport,
38
53
  importMarkdown,
54
+ importSite,
39
55
  importWordPress,
56
+ MarkdownExportError,
40
57
  MarkdownFolderError,
41
58
  markdownFolderChoices,
42
59
  nightlyCheck,
43
60
  reportDue,
44
61
  reportNightly,
62
+ SignInSetupError,
63
+ SiteTransferError,
45
64
  scheduleDue,
46
65
  setAdminAddress,
47
66
  setBrokerAddress,
67
+ setSignIn,
48
68
  setUpContentRepository,
49
69
  update,
50
70
  writeAdminText,
@@ -62,6 +82,7 @@ import {
62
82
  exportTrailingSlash,
63
83
  fixSegmentFiles,
64
84
  guardExport,
85
+ loadTaskConfig,
65
86
  NEXT_SITE_FILES,
66
87
  nextDistDir,
67
88
  writeBuildRecordFile,
@@ -212,9 +233,24 @@ if (command === "content") {
212
233
  }
213
234
  }
214
235
  }
215
- if (command === "admin" || command === "broker") {
236
+ if (command === "admin" || command === "broker" || command === "sign-in") {
216
237
  const [action, argument] = process.argv.slice(3);
217
238
  try {
239
+ if (
240
+ command === "sign-in" &&
241
+ (((action === "service" || action === "client-id") && argument) || action === "remove")
242
+ ) {
243
+ const { host, value } = await setSignIn(".", action === "remove" ? undefined : argument);
244
+ const name = host === "github" ? "GitHub" : host === "gitlab" ? "GitLab" : "Bitbucket";
245
+ console.log(
246
+ !value
247
+ ? "Editors sign in with a token once the site has rebuilt with goodfellow.config.tsx."
248
+ : host === "github"
249
+ ? `Editors can choose Sign in with GitHub, through ${value}, once the site has rebuilt with goodfellow.config.tsx.`
250
+ : `Editors can choose Sign in with ${name} once the site has rebuilt with goodfellow.config.tsx.`,
251
+ );
252
+ process.exit(0);
253
+ }
218
254
  if (command === "admin" && ((action === "add" && argument) || action === "check" || action === "remove")) {
219
255
  const result =
220
256
  action === "check"
@@ -234,7 +270,9 @@ if (command === "admin" || command === "broker") {
234
270
  process.exit(0);
235
271
  }
236
272
  } catch (error) {
237
- if (!(error instanceof AdminSetupError || error instanceof BrokerSetupError)) throw error;
273
+ if (!(error instanceof AdminSetupError || error instanceof BrokerSetupError || error instanceof SignInSetupError)) {
274
+ throw error;
275
+ }
238
276
  console.error(error.message);
239
277
  process.exit(1);
240
278
  }
@@ -252,10 +290,44 @@ if (command === "import") {
252
290
  drafts: { type: "boolean" },
253
291
  menu: { type: "boolean" },
254
292
  "keep-fields": { type: "boolean" },
293
+ take: { type: "string" },
294
+ replace: { type: "boolean" },
295
+ menus: { type: "boolean" },
296
+ settings: { type: "boolean" },
297
+ layout: { type: "boolean" },
298
+ redirects: { type: "boolean" },
299
+ "without-missing-blocks": { type: "boolean" },
255
300
  },
256
301
  });
257
302
  const [kind, from] = positionals;
258
303
  const problem = contentRepositoryProblem(".");
304
+ if (kind === "site" && from && !problem) {
305
+ if (values.take !== undefined && values.take !== "site" && values.take !== "export") {
306
+ console.error('Say "--take site" or "--take export".');
307
+ process.exit(1);
308
+ }
309
+ try {
310
+ const config = await loadTaskConfig(resolve("."));
311
+ const result = await importSite(".", from, {
312
+ blocks: Object.keys(config.blocks),
313
+ replace: values.replace,
314
+ menus: values.menus,
315
+ settings: values.settings,
316
+ layout: values.layout,
317
+ redirects: values.redirects,
318
+ withoutMissingBlocks: values["without-missing-blocks"],
319
+ ...(values.take && { take: values.take }),
320
+ dryRun: values["dry-run"],
321
+ log: (line) => console.log(line),
322
+ });
323
+ console.log(formatSiteImport(result));
324
+ process.exit(0);
325
+ } catch (error) {
326
+ if (!(error instanceof SiteTransferError || error instanceof SiteImportError)) throw error;
327
+ console.error(error.message);
328
+ process.exit(1);
329
+ }
330
+ }
259
331
  if (kind === "markdown" && from && !problem) {
260
332
  try {
261
333
  const result = await importMarkdown(".", from, {
@@ -293,7 +365,12 @@ if (command === "import") {
293
365
  }
294
366
  }
295
367
  if (command === "export") {
296
- const [kind, file] = process.argv.slice(3);
368
+ const { positionals, values } = parseArgs({
369
+ args: process.argv.slice(3),
370
+ allowPositionals: true,
371
+ options: { scheduled: { type: "boolean" }, links: { type: "string" } },
372
+ });
373
+ const [kind, file] = positionals;
297
374
  const problem = contentRepositoryProblem(".");
298
375
  if (problem) {
299
376
  console.error(problem);
@@ -303,6 +380,34 @@ if (command === "export") {
303
380
  console.log(formatWordPressExport(await exportWordPress(".", file)));
304
381
  process.exit(0);
305
382
  }
383
+ if (kind === "site") {
384
+ try {
385
+ console.log(formatSiteExport(await exportSite(".", file)));
386
+ process.exit(0);
387
+ } catch (error) {
388
+ if (!(error instanceof SiteTransferError)) throw error;
389
+ console.error(error.message);
390
+ process.exit(1);
391
+ }
392
+ }
393
+ if (kind === "markdown") {
394
+ if (values.links !== undefined && values.links !== "site" && values.links !== "files") {
395
+ console.error('Say "--links site" or "--links files".');
396
+ process.exit(1);
397
+ }
398
+ try {
399
+ const result = await exportMarkdown(".", file, {
400
+ scheduled: values.scheduled === true,
401
+ ...(values.links && { links: values.links }),
402
+ });
403
+ console.log(formatMarkdownExport(result));
404
+ process.exit(0);
405
+ } catch (error) {
406
+ if (!(error instanceof MarkdownExportError)) throw error;
407
+ console.error(error.message);
408
+ process.exit(1);
409
+ }
410
+ }
306
411
  }
307
412
  if (command === "blocks") {
308
413
  const { positionals, values } = parseArgs({
@@ -343,9 +448,14 @@ if (command !== "finish" && command !== "index") {
343
448
  " goodfellow-next import markdown <folder> [--dry-run] [--pages <folder>] [--collection <folder>]",
344
449
  " [--replace-home] [--replace-pages] [--drafts] [--menu] [--keep-fields]",
345
450
  " goodfellow-next export wordpress [file]",
451
+ " goodfellow-next export markdown [folder] [--scheduled] [--links site|files]",
452
+ " goodfellow-next export site [file]",
453
+ " goodfellow-next import site <zip or folder> [--dry-run] [--take site|export] [--replace]",
454
+ " [--menus] [--settings] [--layout] [--redirects] [--without-missing-blocks]",
346
455
  " goodfellow-next blocks add <item> [--registry <folder>] [--native <app>]",
347
456
  " goodfellow-next admin add <address> | check | remove",
348
457
  " goodfellow-next broker use <address> | remove",
458
+ " goodfellow-next sign-in service <address> | client-id <id> | remove",
349
459
  "",
350
460
  "finish Gets the exported site in folder (default: out) ready to publish: puts the files",
351
461
  " Next.js uses to load pages ahead where browsers look for them, writes calendar files",
@@ -373,9 +483,15 @@ if (command !== "finish" && command !== "index") {
373
483
  " this site, from its export file (Tools → Export) or its address. With --dry-run,",
374
484
  " says what it would bring across and changes nothing. import markdown brings a folder",
375
485
  " of Markdown files in: the files at the top as pages, each folder as a collection,",
376
- " unless --pages or --collection names it (. for the top).",
486
+ " unless --pages or --collection names it (. for the top). import site brings another",
487
+ " Goodfellow site's export in: its pages, items, synced blocks and media, keeping this",
488
+ " site's version of what both have unless --take export; --menus, --settings, --layout",
489
+ " and --redirects bring those too, and --replace makes this site's content the export's.",
377
490
  "export Writes this site as a WordPress export file (default: named after the site),",
378
- " which WordPress imports with Tools → Import → WordPress.",
491
+ " which WordPress imports with Tools → Import → WordPress, or as a folder of Markdown",
492
+ " files, for Hugo, Jekyll, Eleventy, Astro and notes apps (--scheduled also exports what",
493
+ " isn't shown yet, as drafts; --links files links to the files rather than the addresses).",
494
+ " export site writes this site's content and media as a zip for another Goodfellow site.",
379
495
  "blocks Installs a shadcn registry item (button, @acme/hero, or an item's address) with",
380
496
  " shadcn's own CLI, makes a block of its component, with fields from its props, and",
381
497
  " says what it couldn't do. With --registry, writes the block into the block registry",
@@ -386,6 +502,9 @@ if (command !== "finish" && command !== "index") {
386
502
  " it, then starts the admin panel only there; remove stops using it.",
387
503
  "broker The site's token broker in goodfellow.config.tsx: use signs editors in there once",
388
504
  " it's online; remove stops using it.",
505
+ "sign-in How editors sign in, in goodfellow.config.tsx: service names a GitHub site's sign-in",
506
+ " service, client-id a GitLab site's OAuth application's ID or a Bitbucket site's OAuth",
507
+ " consumer's Key; remove takes either out.",
389
508
  ].join("\n"),
390
509
  );
391
510
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodfellow-cms/next",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Goodfellow CMS pages and the admin panel in a Next.js site.",
5
5
  "keywords": [
6
6
  "goodfellow",
@@ -67,24 +67,24 @@
67
67
  "dist"
68
68
  ],
69
69
  "dependencies": {
70
- "@goodfellow-cms/ai": "0.9.0",
71
- "@goodfellow-cms/core": "0.9.0",
72
- "@goodfellow-cms/react": "0.9.0",
70
+ "@goodfellow-cms/ai": "0.10.0",
71
+ "@goodfellow-cms/core": "0.10.0",
72
+ "@goodfellow-cms/react": "0.10.0",
73
73
  "rolldown": "1.2.12",
74
74
  "sharp": "0.35.5"
75
75
  },
76
76
  "peerDependencies": {
77
- "@goodfellow-cms/admin": "0.9.0",
77
+ "@goodfellow-cms/admin": "0.10.0",
78
78
  "@puckeditor/core": "0.23.0",
79
79
  "next": "^16.0.0",
80
80
  "react": "^19.0.0",
81
81
  "react-dom": "^19.0.0"
82
82
  },
83
83
  "devDependencies": {
84
- "@goodfellow-cms/admin": "0.9.0",
85
- "@goodfellow-cms/bitbucket": "0.9.0",
86
- "@goodfellow-cms/github": "0.9.0",
87
- "@goodfellow-cms/gitlab": "0.9.0",
84
+ "@goodfellow-cms/admin": "0.10.0",
85
+ "@goodfellow-cms/bitbucket": "0.10.0",
86
+ "@goodfellow-cms/github": "0.10.0",
87
+ "@goodfellow-cms/gitlab": "0.10.0",
88
88
  "@puckeditor/core": "0.23.0",
89
89
  "@tailwindcss/browser": "4.3.3",
90
90
  "@types/react": "^19.2.0",