@zedtech/zedkit-payload-seo 0.0.0 → 0.1.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 CHANGED
@@ -1,12 +1,20 @@
1
1
  # @zedtech/zedkit-payload-seo
2
2
 
3
- Localized Payload SEO group field with length validation.
4
- Original work by the same author.
3
+ [![npm version](https://img.shields.io/npm/v/@zedtech/zedkit-payload-seo.svg)](https://www.npmjs.com/package/@zedtech/zedkit-payload-seo)
4
+ [![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/zedtech-fr/zedkit/blob/main/LICENSE)
5
+
6
+ Drop-in localized SEO group field for Payload CMS: meta tags, JSON-LD
7
+ and length validation, ready for i18n sites. Original work by the same
8
+ author.
9
+
10
+ ## Install
5
11
 
6
12
  ```sh
7
13
  pnpm add @zedtech/zedkit-payload-seo
8
14
  ```
9
15
 
16
+ ## Usage
17
+
10
18
  ```ts
11
19
  import { seoGroupField } from "@zedtech/zedkit-payload-seo";
12
20
 
@@ -16,6 +24,12 @@ export const Page = {
16
24
  };
17
25
  ```
18
26
 
27
+ ## When to use
28
+
29
+ Reach for this package when your Payload collections need per-locale
30
+ SEO fields. To render the stored values (metadata, sitemaps, JSON-LD),
31
+ pair it with `@zedtech/zedkit-seo`.
32
+
19
33
  `seoFields()` returns the nine leaf fields (meta title/description,
20
34
  keywords, canonical URL, OG title/description/image, robots
21
35
  noindex/nofollow); `seoGroupField()` wraps them in a group (default
@@ -0,0 +1,23 @@
1
+ import type { Field } from "payload";
2
+ export declare const SEO_TITLE_MAX = 70;
3
+ export declare const SEO_DESCRIPTION_MAX = 160;
4
+ /** Length validator with a friendly message (empty and non-strings pass). */
5
+ export declare function maxLengthValidator(max: number): (value: unknown) => string | true;
6
+ export interface SeoFieldsOptions {
7
+ /** Upload collection for the OG image. Defaults to `"media"`. */
8
+ mediaSlug?: string;
9
+ /** Meta/OG title cap. Defaults to 70. */
10
+ titleMaxLength?: number;
11
+ /** Meta/OG description cap. Defaults to 160. */
12
+ descriptionMaxLength?: number;
13
+ /** Group field name. Defaults to `"seo"`. */
14
+ groupName?: string;
15
+ }
16
+ /**
17
+ * Localized SEO leaf fields: meta title/description, keywords,
18
+ * canonical URL, OG title/description/image, robots flags.
19
+ */
20
+ export declare function seoFields(options?: SeoFieldsOptions): Field[];
21
+ /** The leaf fields wrapped in a group (default name `"seo"`). */
22
+ export declare function seoGroupField(options?: SeoFieldsOptions): Field;
23
+ //# sourceMappingURL=fields.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../src/fields.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,eAAO,MAAM,aAAa,KAAK,CAAC;AAChC,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC,6EAA6E;AAC7E,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,GAAG,IAAI,CAKjF;AAED,MAAM,WAAW,gBAAgB;IAC/B,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gDAAgD;IAChD,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,OAAO,GAAE,gBAAqB,GAAG,KAAK,EAAE,CAkEjE;AAED,iEAAiE;AACjE,wBAAgB,aAAa,CAAC,OAAO,GAAE,gBAAqB,GAAG,KAAK,CAMnE"}
package/dist/fields.js ADDED
@@ -0,0 +1,90 @@
1
+ export const SEO_TITLE_MAX = 70;
2
+ export const SEO_DESCRIPTION_MAX = 160;
3
+ /** Length validator with a friendly message (empty and non-strings pass). */
4
+ export function maxLengthValidator(max) {
5
+ return (value) => {
6
+ if (typeof value !== "string" || value.length === 0)
7
+ return true;
8
+ return value.length <= max ? true : `Must be ${max} characters or fewer (now ${value.length}).`;
9
+ };
10
+ }
11
+ /**
12
+ * Localized SEO leaf fields: meta title/description, keywords,
13
+ * canonical URL, OG title/description/image, robots flags.
14
+ */
15
+ export function seoFields(options = {}) {
16
+ const mediaSlug = options.mediaSlug ?? "media";
17
+ const titleMax = options.titleMaxLength ?? SEO_TITLE_MAX;
18
+ const descriptionMax = options.descriptionMaxLength ?? SEO_DESCRIPTION_MAX;
19
+ return [
20
+ {
21
+ name: "metaTitle",
22
+ type: "text",
23
+ localized: true,
24
+ maxLength: titleMax,
25
+ validate: maxLengthValidator(titleMax),
26
+ admin: { description: `Search result title (${titleMax} chars max).` },
27
+ },
28
+ {
29
+ name: "metaDescription",
30
+ type: "textarea",
31
+ localized: true,
32
+ maxLength: descriptionMax,
33
+ validate: maxLengthValidator(descriptionMax),
34
+ admin: { description: `Search result snippet (${descriptionMax} chars max).` },
35
+ },
36
+ {
37
+ name: "keywords",
38
+ type: "array",
39
+ localized: true,
40
+ fields: [{ name: "value", type: "text" }],
41
+ },
42
+ {
43
+ name: "canonicalUrl",
44
+ type: "text",
45
+ admin: { description: "Absolute URL; leave empty to use the page URL." },
46
+ },
47
+ {
48
+ name: "ogTitle",
49
+ type: "text",
50
+ localized: true,
51
+ maxLength: titleMax,
52
+ validate: maxLengthValidator(titleMax),
53
+ admin: { description: "Social share title; falls back to the meta title." },
54
+ },
55
+ {
56
+ name: "ogDescription",
57
+ type: "textarea",
58
+ localized: true,
59
+ maxLength: descriptionMax,
60
+ validate: maxLengthValidator(descriptionMax),
61
+ admin: { description: "Social share text; falls back to the meta description." },
62
+ },
63
+ {
64
+ name: "ogImage",
65
+ type: "upload",
66
+ relationTo: mediaSlug,
67
+ },
68
+ {
69
+ name: "robotsNoIndex",
70
+ type: "checkbox",
71
+ defaultValue: false,
72
+ admin: { description: "Hide this page from search results (drafts)." },
73
+ },
74
+ {
75
+ name: "robotsNoFollow",
76
+ type: "checkbox",
77
+ defaultValue: false,
78
+ admin: { description: "Tell crawlers not to follow this page's links." },
79
+ },
80
+ ];
81
+ }
82
+ /** The leaf fields wrapped in a group (default name `"seo"`). */
83
+ export function seoGroupField(options = {}) {
84
+ return {
85
+ name: options.groupName ?? "seo",
86
+ type: "group",
87
+ fields: seoFields(options),
88
+ };
89
+ }
90
+ //# sourceMappingURL=fields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fields.js","sourceRoot":"","sources":["../src/fields.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,aAAa,GAAG,EAAE,CAAC;AAChC,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAEvC,6EAA6E;AAC7E,MAAM,UAAU,kBAAkB,CAAC,GAAW;IAC5C,OAAO,CAAC,KAAc,EAAE,EAAE;QACxB,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACjE,OAAO,KAAK,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,GAAG,6BAA6B,KAAK,CAAC,MAAM,IAAI,CAAC;IAClG,CAAC,CAAC;AACJ,CAAC;AAaD;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,UAA4B,EAAE;IACtD,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC;IAC/C,MAAM,QAAQ,GAAG,OAAO,CAAC,cAAc,IAAI,aAAa,CAAC;IACzD,MAAM,cAAc,GAAG,OAAO,CAAC,oBAAoB,IAAI,mBAAmB,CAAC;IAC3E,OAAO;QACL;YACE,IAAI,EAAE,WAAW;YACjB,IAAI,EAAE,MAAM;YACZ,SAAS,EAAE,IAAI;YACf,SAAS,EAAE,QAAQ;YACnB,QAAQ,EAAE,kBAAkB,CAAC,QAAQ,CAAC;YACtC,KAAK,EAAE,EAAE,WAAW,EAAE,wBAAwB,QAAQ,cAAc,EAAE;SACvE;QACD;YACE,IAAI,EAAE,iBAAiB;YACvB,IAAI,EAAE,UAAU;YAChB,SAAS,EAAE,IAAI;YACf,SAAS,EAAE,cAAc;YACzB,QAAQ,EAAE,kBAAkB,CAAC,cAAc,CAAC;YAC5C,KAAK,EAAE,EAAE,WAAW,EAAE,0BAA0B,cAAc,cAAc,EAAE;SAC/E;QACD;YACE,IAAI,EAAE,UAAU;YAChB,IAAI,EAAE,OAAO;YACb,SAAS,EAAE,IAAI;YACf,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;SAC1C;QACD;YACE,IAAI,EAAE,cAAc;YACpB,IAAI,EAAE,MAAM;YACZ,KAAK,EAAE,EAAE,WAAW,EAAE,gDAAgD,EAAE;SACzE;QACD;YACE,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,MAAM;YACZ,SAAS,EAAE,IAAI;YACf,SAAS,EAAE,QAAQ;YACnB,QAAQ,EAAE,kBAAkB,CAAC,QAAQ,CAAC;YACtC,KAAK,EAAE,EAAE,WAAW,EAAE,mDAAmD,EAAE;SAC5E;QACD;YACE,IAAI,EAAE,eAAe;YACrB,IAAI,EAAE,UAAU;YAChB,SAAS,EAAE,IAAI;YACf,SAAS,EAAE,cAAc;YACzB,QAAQ,EAAE,kBAAkB,CAAC,cAAc,CAAC;YAC5C,KAAK,EAAE,EAAE,WAAW,EAAE,wDAAwD,EAAE;SACjF;QACD;YACE,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE,SAAS;SACtB;QACD;YACE,IAAI,EAAE,eAAe;YACrB,IAAI,EAAE,UAAU;YAChB,YAAY,EAAE,KAAK;YACnB,KAAK,EAAE,EAAE,WAAW,EAAE,8CAA8C,EAAE;SACvE;QACD;YACE,IAAI,EAAE,gBAAgB;YACtB,IAAI,EAAE,UAAU;YAChB,YAAY,EAAE,KAAK;YACnB,KAAK,EAAE,EAAE,WAAW,EAAE,gDAAgD,EAAE;SACzE;KACF,CAAC;AACJ,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,aAAa,CAAC,UAA4B,EAAE;IAC1D,OAAO;QACL,IAAI,EAAE,OAAO,CAAC,SAAS,IAAI,KAAK;QAChC,IAAI,EAAE,OAAO;QACb,MAAM,EAAE,SAAS,CAAC,OAAO,CAAC;KAC3B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { SEO_DESCRIPTION_MAX, SEO_TITLE_MAX, maxLengthValidator, seoFields, seoGroupField, type SeoFieldsOptions, } from "./fields.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,aAAa,EACb,kBAAkB,EAClB,SAAS,EACT,aAAa,EACb,KAAK,gBAAgB,GACtB,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { SEO_DESCRIPTION_MAX, SEO_TITLE_MAX, maxLengthValidator, seoFields, seoGroupField, } from "./fields.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,aAAa,EACb,kBAAkB,EAClB,SAAS,EACT,aAAa,GAEd,MAAM,aAAa,CAAC"}
package/package.json CHANGED
@@ -1,11 +1,28 @@
1
1
  {
2
2
  "name": "@zedtech/zedkit-payload-seo",
3
- "version": "0.0.0",
3
+ "version": "0.1.1",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "registry": "https://registry.npmjs.org/"
7
7
  },
8
- "description": "Localized Payload SEO group field with length validation.",
8
+ "description": "Drop-in localized SEO group field: meta tags, JSON-LD and length validation, ready for i18n sites.",
9
+ "keywords": [
10
+ "payloadcms",
11
+ "payload",
12
+ "seo",
13
+ "cms",
14
+ "meta-tags",
15
+ "json-ld",
16
+ "open-graph",
17
+ "localization",
18
+ "i18n",
19
+ "nextjs",
20
+ "typescript"
21
+ ],
22
+ "homepage": "https://github.com/zedtech-fr/zedkit/tree/main/packages/payload-seo",
23
+ "bugs": {
24
+ "url": "https://github.com/zedtech-fr/zedkit/issues"
25
+ },
9
26
  "license": "MIT",
10
27
  "repository": {
11
28
  "type": "git",