@lownoise-studio/rendershield 0.3.1 → 1.0.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 (66) hide show
  1. package/CHANGELOG.md +58 -32
  2. package/CONTRIBUTING.md +41 -0
  3. package/README.md +209 -144
  4. package/SECURITY.md +25 -0
  5. package/dist/cli.d.ts +3 -0
  6. package/dist/cli.d.ts.map +1 -0
  7. package/dist/cli.js +27 -19
  8. package/dist/cli.js.map +1 -1
  9. package/dist/commands/build.d.ts +2 -0
  10. package/dist/commands/build.d.ts.map +1 -0
  11. package/dist/commands/build.js +10 -9
  12. package/dist/commands/build.js.map +1 -1
  13. package/dist/commands/init.d.ts +2 -0
  14. package/dist/commands/init.d.ts.map +1 -0
  15. package/dist/commands/verify.d.ts +19 -0
  16. package/dist/commands/verify.d.ts.map +1 -0
  17. package/dist/commands/verify.js +57 -64
  18. package/dist/commands/verify.js.map +1 -1
  19. package/dist/core/generateRobots.d.ts +3 -0
  20. package/dist/core/generateRobots.d.ts.map +1 -0
  21. package/dist/core/generateSitemap.d.ts +3 -0
  22. package/dist/core/generateSitemap.d.ts.map +1 -0
  23. package/dist/core/generateWorker.d.ts +3 -0
  24. package/dist/core/generateWorker.d.ts.map +1 -0
  25. package/dist/core/generateWorker.js +75 -75
  26. package/dist/core/loadConfig.d.ts +3 -0
  27. package/dist/core/loadConfig.d.ts.map +1 -0
  28. package/dist/core/loadConfig.js +68 -25
  29. package/dist/core/loadConfig.js.map +1 -1
  30. package/dist/core/loadMarkdown.d.ts +3 -0
  31. package/dist/core/loadMarkdown.d.ts.map +1 -0
  32. package/dist/core/loadMarkdown.js +4 -3
  33. package/dist/core/loadMarkdown.js.map +1 -1
  34. package/dist/core/renderHtml.d.ts +3 -0
  35. package/dist/core/renderHtml.d.ts.map +1 -0
  36. package/dist/core/renderHtml.js +25 -10
  37. package/dist/core/renderHtml.js.map +1 -1
  38. package/dist/core/validateOutput.d.ts +28 -0
  39. package/dist/core/validateOutput.d.ts.map +1 -0
  40. package/dist/core/validateOutput.js +7 -1
  41. package/dist/core/validateOutput.js.map +1 -1
  42. package/dist/errors.d.ts +14 -0
  43. package/dist/errors.d.ts.map +1 -0
  44. package/dist/errors.js +24 -0
  45. package/dist/errors.js.map +1 -0
  46. package/dist/index.d.ts +20 -0
  47. package/dist/index.d.ts.map +1 -0
  48. package/dist/index.js +19 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/types.d.ts +53 -0
  51. package/dist/types.d.ts.map +1 -0
  52. package/dist/types.js +1 -1
  53. package/dist/types.js.map +1 -1
  54. package/docs/deploy-cloudflare.md +40 -14
  55. package/package.json +24 -2
  56. package/src/cli.ts +86 -75
  57. package/src/commands/build.ts +199 -185
  58. package/src/commands/verify.ts +266 -236
  59. package/src/core/generateWorker.ts +97 -97
  60. package/src/core/loadConfig.ts +261 -173
  61. package/src/core/loadMarkdown.ts +9 -3
  62. package/src/core/renderHtml.ts +36 -12
  63. package/src/core/validateOutput.ts +335 -328
  64. package/src/errors.ts +48 -0
  65. package/src/index.ts +40 -0
  66. package/src/types.ts +4 -1
@@ -4,6 +4,7 @@ import fs from "fs-extra";
4
4
  import matter from "gray-matter";
5
5
  import MarkdownIt from "markdown-it";
6
6
  import { MarkdownDoc, RenderShieldConfig } from "../types.js";
7
+ import { renderShieldError } from "../errors.js";
7
8
 
8
9
  const md = new MarkdownIt({ html: false, linkify: true, typographer: true });
9
10
 
@@ -11,7 +12,8 @@ const REQUIRED_FIELDS = "title, excerpt, datePublished, coverImage, slug";
11
12
 
12
13
  function requireString(value: unknown, field: string, file: string): string {
13
14
  if (typeof value !== "string" || value.trim().length === 0) {
14
- throw new Error(
15
+ throw renderShieldError(
16
+ "CONTENT_INVALID",
15
17
  `Missing required frontmatter field "${field}" in ${file}. Required fields: ${REQUIRED_FIELDS}`
16
18
  );
17
19
  }
@@ -25,7 +27,10 @@ function normalizeDate(value: unknown, file: string): string {
25
27
  if (value instanceof Date) {
26
28
  return value.toISOString().slice(0, 10);
27
29
  }
28
- throw new Error(`Invalid datePublished in ${file}. Use format YYYY-MM-DD.`);
30
+ throw renderShieldError(
31
+ "CONTENT_INVALID",
32
+ `Invalid datePublished in ${file}. Use format YYYY-MM-DD.`
33
+ );
29
34
  }
30
35
 
31
36
  export async function loadAllMarkdownDocs(cfg: RenderShieldConfig, cwd = process.cwd()): Promise<MarkdownDoc[]> {
@@ -47,7 +52,8 @@ export async function loadAllMarkdownDocs(cfg: RenderShieldConfig, cwd = process
47
52
 
48
53
  const datePublishedRaw = parsed.data?.datePublished;
49
54
  if (datePublishedRaw === undefined || datePublishedRaw === null) {
50
- throw new Error(
55
+ throw renderShieldError(
56
+ "CONTENT_INVALID",
51
57
  `Missing required frontmatter field "datePublished" in ${abs}. Required fields: ${REQUIRED_FIELDS}`
52
58
  );
53
59
  }
@@ -1,4 +1,4 @@
1
- import { MarkdownDoc, RenderShieldConfig } from "../types.js";
1
+ import { MarkdownDoc, RenderShieldConfig, SchemaType } from "../types.js";
2
2
 
3
3
  function escapeHtml(s: string): string {
4
4
  return s
@@ -15,6 +15,37 @@ function joinUrl(base: string, pathname: string): string {
15
15
  return b + p;
16
16
  }
17
17
 
18
+ function resolveSchemaType(cfg: RenderShieldConfig, doc: MarkdownDoc): SchemaType {
19
+ const collection = cfg.content.markdown.collections.find((c) => c.name === doc.collection);
20
+ return collection?.schemaType ?? "Article";
21
+ }
22
+
23
+ function buildJsonLd(
24
+ schemaType: SchemaType,
25
+ cfg: RenderShieldConfig,
26
+ doc: MarkdownDoc,
27
+ canonicalUrl: string,
28
+ ogImageUrl: string
29
+ ): Record<string, unknown> {
30
+ const base = {
31
+ "@context": "https://schema.org",
32
+ "@type": schemaType,
33
+ mainEntityOfPage: canonicalUrl,
34
+ image: ogImageUrl,
35
+ };
36
+
37
+ if (schemaType === "WebPage") {
38
+ return { ...base, name: doc.title };
39
+ }
40
+
41
+ return {
42
+ ...base,
43
+ headline: doc.title,
44
+ author: { "@type": "Person", name: cfg.site.authorName },
45
+ datePublished: doc.datePublished,
46
+ };
47
+ }
48
+
18
49
  export function renderPageHtml(cfg: RenderShieldConfig, doc: MarkdownDoc): string {
19
50
  const canonicalUrl = joinUrl(cfg.site.canonicalBase, doc.routePath);
20
51
  const ogImageUrl = doc.coverImage.startsWith("http")
@@ -23,16 +54,9 @@ export function renderPageHtml(cfg: RenderShieldConfig, doc: MarkdownDoc): strin
23
54
 
24
55
  const title = `${doc.title} - ${cfg.site.siteName}`;
25
56
  const description = doc.excerpt;
26
-
27
- const jsonLd = {
28
- "@context": "https://schema.org",
29
- "@type": "Article",
30
- headline: doc.title,
31
- author: { "@type": "Person", name: cfg.site.authorName },
32
- datePublished: doc.datePublished,
33
- image: ogImageUrl,
34
- mainEntityOfPage: canonicalUrl,
35
- };
57
+ const schemaType = resolveSchemaType(cfg, doc);
58
+ const ogType = schemaType === "WebPage" ? "website" : "article";
59
+ const jsonLd = buildJsonLd(schemaType, cfg, doc, canonicalUrl, ogImageUrl);
36
60
 
37
61
  // NOTE: doc.htmlContent is already HTML from markdown-it.
38
62
  // We trust it as generated output, not user-injected raw HTML (markdown-it html:false).
@@ -46,7 +70,7 @@ export function renderPageHtml(cfg: RenderShieldConfig, doc: MarkdownDoc): strin
46
70
  <meta name="description" content="${escapeHtml(description)}">
47
71
  <link rel="canonical" href="${escapeHtml(canonicalUrl)}">
48
72
 
49
- <meta property="og:type" content="article">
73
+ <meta property="og:type" content="${ogType}">
50
74
  <meta property="og:title" content="${escapeHtml(doc.title)}">
51
75
  <meta property="og:description" content="${escapeHtml(description)}">
52
76
  <meta property="og:image" content="${escapeHtml(ogImageUrl)}">