@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 +16 -2
- package/dist/fields.d.ts +23 -0
- package/dist/fields.d.ts.map +1 -0
- package/dist/fields.js +90 -0
- package/dist/fields.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/package.json +19 -2
package/README.md
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
# @zedtech/zedkit-payload-seo
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@zedtech/zedkit-payload-seo)
|
|
4
|
+
[](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
|
package/dist/fields.d.ts
ADDED
|
@@ -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":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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 @@
|
|
|
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.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public",
|
|
6
6
|
"registry": "https://registry.npmjs.org/"
|
|
7
7
|
},
|
|
8
|
-
"description": "
|
|
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",
|