@coherent.js/seo 1.0.0-beta.3 → 1.0.0-beta.6
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 +681 -0
- package/package.json +3 -2
- package/types/index.d.ts +337 -18
package/README.md
ADDED
|
@@ -0,0 +1,681 @@
|
|
|
1
|
+
# @coherent.js/seo
|
|
2
|
+
|
|
3
|
+
SEO optimization tools for Coherent.js applications - Meta tags, structured data, and search engine optimization utilities.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @coherent.js/seo
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @coherent.js/seo
|
|
11
|
+
# or
|
|
12
|
+
yarn add @coherent.js/seo
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Overview
|
|
16
|
+
|
|
17
|
+
The `@coherent.js/seo` package provides comprehensive SEO tools for Coherent.js applications, including:
|
|
18
|
+
|
|
19
|
+
- Dynamic meta tag management
|
|
20
|
+
- Open Graph and Twitter Card support
|
|
21
|
+
- JSON-LD structured data generation
|
|
22
|
+
- Sitemap generation
|
|
23
|
+
- Robots.txt management
|
|
24
|
+
- SEO-friendly URL generation
|
|
25
|
+
- Canonical URL support
|
|
26
|
+
- Internationalization SEO
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
```javascript
|
|
31
|
+
import { createSEOHooks } from '@coherent.js/seo';
|
|
32
|
+
|
|
33
|
+
// Create SEO hooks
|
|
34
|
+
const seo = createSEOHooks();
|
|
35
|
+
|
|
36
|
+
// Add to your Coherent.js component
|
|
37
|
+
function BlogPost({ post }) {
|
|
38
|
+
// Set SEO metadata
|
|
39
|
+
seo.setMetadata({
|
|
40
|
+
title: `${post.title} - My Blog`,
|
|
41
|
+
description: post.excerpt,
|
|
42
|
+
keywords: post.tags,
|
|
43
|
+
author: post.author.name,
|
|
44
|
+
publishedTime: post.publishedAt,
|
|
45
|
+
image: post.featuredImage
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
div: {
|
|
50
|
+
children: [
|
|
51
|
+
{ h1: { text: post.title } },
|
|
52
|
+
{ div: {
|
|
53
|
+
innerHTML: post.content,
|
|
54
|
+
className: 'post-content'
|
|
55
|
+
}}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Features
|
|
63
|
+
|
|
64
|
+
### Dynamic Meta Tags
|
|
65
|
+
|
|
66
|
+
Automatically generate meta tags for search engines:
|
|
67
|
+
|
|
68
|
+
```javascript
|
|
69
|
+
import { createSEOHooks } from '@coherent.js/seo';
|
|
70
|
+
|
|
71
|
+
const seo = createSEOHooks();
|
|
72
|
+
|
|
73
|
+
function ProductPage({ product }) {
|
|
74
|
+
seo.setMetadata({
|
|
75
|
+
title: product.name,
|
|
76
|
+
description: product.description,
|
|
77
|
+
keywords: [product.category, product.brand, ...product.tags],
|
|
78
|
+
image: product.images[0],
|
|
79
|
+
locale: 'en_US',
|
|
80
|
+
type: 'product',
|
|
81
|
+
url: `https://example.com/products/${product.slug}`
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
div: {
|
|
86
|
+
children: [
|
|
87
|
+
{ h1: { text: product.name } },
|
|
88
|
+
{ p: { text: product.description } },
|
|
89
|
+
{ img: { src: product.images[0], alt: product.name } }
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Open Graph Support
|
|
97
|
+
|
|
98
|
+
Generate Open Graph meta tags for social sharing:
|
|
99
|
+
|
|
100
|
+
```javascript
|
|
101
|
+
seo.setMetadata({
|
|
102
|
+
title: 'Awesome Product',
|
|
103
|
+
description: 'This product will change your life',
|
|
104
|
+
image: 'https://example.com/product-image.jpg',
|
|
105
|
+
type: 'product.item',
|
|
106
|
+
siteName: 'My E-commerce Store',
|
|
107
|
+
locale: 'en_US',
|
|
108
|
+
|
|
109
|
+
// Open Graph specific
|
|
110
|
+
og: {
|
|
111
|
+
price: {
|
|
112
|
+
amount: '29.99',
|
|
113
|
+
currency: 'USD'
|
|
114
|
+
},
|
|
115
|
+
availability: 'instock'
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Twitter Card Support
|
|
121
|
+
|
|
122
|
+
Generate Twitter Card meta tags:
|
|
123
|
+
|
|
124
|
+
```javascript
|
|
125
|
+
seo.setMetadata({
|
|
126
|
+
title: 'Check out this awesome content!',
|
|
127
|
+
description: 'You won\'t believe what happens next',
|
|
128
|
+
image: 'https://example.com/twitter-card-image.jpg',
|
|
129
|
+
|
|
130
|
+
// Twitter specific
|
|
131
|
+
twitter: {
|
|
132
|
+
card: 'summary_large_image',
|
|
133
|
+
site: '@mywebsite',
|
|
134
|
+
creator: '@author'
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### JSON-LD Structured Data
|
|
140
|
+
|
|
141
|
+
Generate structured data for rich search results:
|
|
142
|
+
|
|
143
|
+
```javascript
|
|
144
|
+
import { createArticleSchema, createBreadcrumbSchema } from '@coherent.js/seo';
|
|
145
|
+
|
|
146
|
+
function BlogPost({ post, breadcrumbs }) {
|
|
147
|
+
// Add article structured data
|
|
148
|
+
seo.addSchema(createArticleSchema({
|
|
149
|
+
headline: post.title,
|
|
150
|
+
description: post.excerpt,
|
|
151
|
+
author: {
|
|
152
|
+
name: post.author.name,
|
|
153
|
+
url: `/authors/${post.author.slug}`
|
|
154
|
+
},
|
|
155
|
+
datePublished: post.publishedAt,
|
|
156
|
+
dateModified: post.updatedAt,
|
|
157
|
+
image: post.featuredImage,
|
|
158
|
+
keywords: post.tags,
|
|
159
|
+
articleBody: post.content
|
|
160
|
+
}));
|
|
161
|
+
|
|
162
|
+
// Add breadcrumb schema
|
|
163
|
+
seo.addSchema(createBreadcrumbSchema(breadcrumbs));
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
div: {
|
|
167
|
+
children: [
|
|
168
|
+
{ h1: { text: post.title } },
|
|
169
|
+
{ div: { innerHTML: post.content } }
|
|
170
|
+
]
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## SEO Hooks
|
|
177
|
+
|
|
178
|
+
### Basic Usage
|
|
179
|
+
|
|
180
|
+
```javascript
|
|
181
|
+
import { createSEOHooks } from '@coherent.js/seo';
|
|
182
|
+
|
|
183
|
+
const seo = createSEOHooks({
|
|
184
|
+
defaultTitle: 'My Website',
|
|
185
|
+
defaultDescription: 'Welcome to my awesome website',
|
|
186
|
+
defaultImage: '/default-social-image.jpg',
|
|
187
|
+
siteUrl: 'https://example.com'
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
function Page({ title, description }) {
|
|
191
|
+
seo.setMetadata({
|
|
192
|
+
title: title,
|
|
193
|
+
description: description
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
div: {
|
|
198
|
+
children: [
|
|
199
|
+
{ h1: { text: title } },
|
|
200
|
+
{ p: { text: description } }
|
|
201
|
+
]
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Advanced Configuration
|
|
208
|
+
|
|
209
|
+
```javascript
|
|
210
|
+
const seo = createSEOHooks({
|
|
211
|
+
// Default metadata
|
|
212
|
+
defaultTitle: 'My Website',
|
|
213
|
+
defaultDescription: 'Default description',
|
|
214
|
+
defaultImage: '/images/default.jpg',
|
|
215
|
+
siteUrl: 'https://example.com',
|
|
216
|
+
|
|
217
|
+
// SEO settings
|
|
218
|
+
titleTemplate: '%s | My Website', // "Page Title | My Website"
|
|
219
|
+
truncateTitle: 60, // Truncate titles to 60 chars
|
|
220
|
+
truncateDescription: 160, // Truncate descriptions to 160 chars
|
|
221
|
+
|
|
222
|
+
// Social media
|
|
223
|
+
social: {
|
|
224
|
+
twitter: '@mytwitterhandle',
|
|
225
|
+
facebook: 'myfacebookpage',
|
|
226
|
+
linkedin: 'mylinkedincompany'
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
// Default Open Graph
|
|
230
|
+
og: {
|
|
231
|
+
type: 'website',
|
|
232
|
+
siteName: 'My Website'
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Sitemap Generation
|
|
238
|
+
|
|
239
|
+
Generate dynamic sitemaps:
|
|
240
|
+
|
|
241
|
+
```javascript
|
|
242
|
+
import { createSitemap } from '@coherent.js/seo';
|
|
243
|
+
|
|
244
|
+
const sitemap = createSitemap({
|
|
245
|
+
siteUrl: 'https://example.com',
|
|
246
|
+
changefreq: 'daily',
|
|
247
|
+
priority: 0.7,
|
|
248
|
+
lastmod: new Date()
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// Add URLs to sitemap
|
|
252
|
+
sitemap.add({
|
|
253
|
+
url: '/',
|
|
254
|
+
changefreq: 'daily',
|
|
255
|
+
priority: 1.0
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
sitemap.add({
|
|
259
|
+
url: '/about',
|
|
260
|
+
changefreq: 'monthly',
|
|
261
|
+
priority: 0.8
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
sitemap.add({
|
|
265
|
+
url: '/blog',
|
|
266
|
+
changefreq: 'weekly',
|
|
267
|
+
priority: 0.9
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
// Generate sitemap XML
|
|
271
|
+
const sitemapXml = sitemap.toXML();
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Robots.txt Management
|
|
275
|
+
|
|
276
|
+
Generate robots.txt files:
|
|
277
|
+
|
|
278
|
+
```javascript
|
|
279
|
+
import { createRobotsTxt } from '@coherent.js/seo';
|
|
280
|
+
|
|
281
|
+
const robots = createRobotsTxt({
|
|
282
|
+
userAgent: '*', // Default user agent
|
|
283
|
+
allow: ['/'], // Allow all paths
|
|
284
|
+
disallow: ['/admin', '/private'], // Disallow specific paths
|
|
285
|
+
sitemap: 'https://example.com/sitemap.xml',
|
|
286
|
+
host: 'https://example.com'
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
// Add custom rules
|
|
290
|
+
robots.addRule({
|
|
291
|
+
userAgent: 'Googlebot',
|
|
292
|
+
allow: ['/'],
|
|
293
|
+
crawlDelay: 10
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// Generate robots.txt content
|
|
297
|
+
const robotsTxt = robots.toString();
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## Canonical URLs
|
|
301
|
+
|
|
302
|
+
Manage canonical URLs to prevent duplicate content issues:
|
|
303
|
+
|
|
304
|
+
```javascript
|
|
305
|
+
function ProductPage({ product, queryParams }) {
|
|
306
|
+
// Set canonical URL without tracking parameters
|
|
307
|
+
const canonicalUrl = `https://example.com/products/${product.slug}`;
|
|
308
|
+
|
|
309
|
+
seo.setCanonical(canonicalUrl);
|
|
310
|
+
|
|
311
|
+
// Add alternate URLs for internationalization
|
|
312
|
+
seo.addAlternate({
|
|
313
|
+
href: `https://example.com/en/products/${product.slug}`,
|
|
314
|
+
hreflang: 'en'
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
seo.addAlternate({
|
|
318
|
+
href: `https://example.com/es/products/${product.slug}`,
|
|
319
|
+
hreflang: 'es'
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
return {
|
|
323
|
+
div: {
|
|
324
|
+
children: [
|
|
325
|
+
{ h1: { text: product.name } },
|
|
326
|
+
{ p: { text: product.description } }
|
|
327
|
+
]
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
## Internationalization SEO
|
|
334
|
+
|
|
335
|
+
Support for multilingual SEO:
|
|
336
|
+
|
|
337
|
+
```javascript
|
|
338
|
+
function MultilingualPage({ content, language, translations }) {
|
|
339
|
+
// Set language
|
|
340
|
+
seo.setLanguage(language);
|
|
341
|
+
|
|
342
|
+
// Add alternate language versions
|
|
343
|
+
Object.entries(translations).forEach(([lang, url]) => {
|
|
344
|
+
seo.addAlternate({
|
|
345
|
+
href: url,
|
|
346
|
+
hreflang: lang
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
// Set direction for RTL languages
|
|
351
|
+
seo.setDirection(language === 'ar' || language === 'he' ? 'rtl' : 'ltr');
|
|
352
|
+
|
|
353
|
+
return {
|
|
354
|
+
div: {
|
|
355
|
+
lang: language,
|
|
356
|
+
children: [
|
|
357
|
+
{ h1: { text: content.title } },
|
|
358
|
+
{ div: { innerHTML: content.body } }
|
|
359
|
+
]
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
## API Reference
|
|
366
|
+
|
|
367
|
+
### createSEOHooks(options)
|
|
368
|
+
|
|
369
|
+
Create SEO hooks for metadata management.
|
|
370
|
+
|
|
371
|
+
**Parameters:**
|
|
372
|
+
- `options.defaultTitle` - Default page title
|
|
373
|
+
- `options.defaultDescription` - Default page description
|
|
374
|
+
- `options.defaultImage` - Default social image
|
|
375
|
+
- `options.siteUrl` - Base site URL
|
|
376
|
+
- `options.titleTemplate` - Title template string
|
|
377
|
+
- `options.truncateTitle` - Title character limit
|
|
378
|
+
- `options.truncateDescription` - Description character limit
|
|
379
|
+
|
|
380
|
+
**Returns:** SEO hooks object with methods
|
|
381
|
+
|
|
382
|
+
### SEO Hooks Methods
|
|
383
|
+
|
|
384
|
+
- `seo.setMetadata(metadata)` - Set page metadata
|
|
385
|
+
- `seo.setTitle(title)` - Set page title
|
|
386
|
+
- `seo.setDescription(description)` - Set page description
|
|
387
|
+
- `seo.setCanonical(url)` - Set canonical URL
|
|
388
|
+
- `seo.addAlternate(options)` - Add alternate language URL
|
|
389
|
+
- `seo.addSchema(schema)` - Add JSON-LD schema
|
|
390
|
+
- `seo.setLanguage(lang)` - Set page language
|
|
391
|
+
- `seo.setDirection(dir)` - Set text direction
|
|
392
|
+
|
|
393
|
+
### createSitemap(options)
|
|
394
|
+
|
|
395
|
+
Create a sitemap generator.
|
|
396
|
+
|
|
397
|
+
**Parameters:**
|
|
398
|
+
- `options.siteUrl` - Base site URL
|
|
399
|
+
- `options.changefreq` - Default change frequency
|
|
400
|
+
- `options.priority` - Default priority
|
|
401
|
+
- `options.lastmod` - Default last modified date
|
|
402
|
+
|
|
403
|
+
**Returns:** Sitemap generator object
|
|
404
|
+
|
|
405
|
+
### createRobotsTxt(options)
|
|
406
|
+
|
|
407
|
+
Create a robots.txt generator.
|
|
408
|
+
|
|
409
|
+
**Parameters:**
|
|
410
|
+
- `options.userAgent` - Default user agent
|
|
411
|
+
- `options.allow` - Allowed paths
|
|
412
|
+
- `options.disallow` - Disallowed paths
|
|
413
|
+
- `options.sitemap` - Sitemap URL
|
|
414
|
+
- `options.host` - Host URL
|
|
415
|
+
|
|
416
|
+
**Returns:** Robots.txt generator object
|
|
417
|
+
|
|
418
|
+
## Schema Generators
|
|
419
|
+
|
|
420
|
+
### createArticleSchema(options)
|
|
421
|
+
|
|
422
|
+
Generate Article JSON-LD schema.
|
|
423
|
+
|
|
424
|
+
**Parameters:**
|
|
425
|
+
- `options.headline` - Article headline
|
|
426
|
+
- `options.description` - Article description
|
|
427
|
+
- `options.author` - Author information
|
|
428
|
+
- `options.datePublished` - Publication date
|
|
429
|
+
- `options.dateModified` - Modification date
|
|
430
|
+
- `options.image` - Featured image
|
|
431
|
+
- `options.keywords` - Article keywords
|
|
432
|
+
- `options.articleBody` - Article content
|
|
433
|
+
|
|
434
|
+
### createBreadcrumbSchema(breadcrumbs)
|
|
435
|
+
|
|
436
|
+
Generate Breadcrumb JSON-LD schema.
|
|
437
|
+
|
|
438
|
+
**Parameters:**
|
|
439
|
+
- `breadcrumbs` - Array of breadcrumb objects with name and url
|
|
440
|
+
|
|
441
|
+
### createProductSchema(options)
|
|
442
|
+
|
|
443
|
+
Generate Product JSON-LD schema.
|
|
444
|
+
|
|
445
|
+
**Parameters:**
|
|
446
|
+
- `options.name` - Product name
|
|
447
|
+
- `options.description` - Product description
|
|
448
|
+
- `options.image` - Product images
|
|
449
|
+
- `options.offers` - Product offers/pricing
|
|
450
|
+
- `options.brand` - Product brand
|
|
451
|
+
- `options.category` - Product category
|
|
452
|
+
- `options.sku` - Product SKU
|
|
453
|
+
|
|
454
|
+
## Examples
|
|
455
|
+
|
|
456
|
+
### Blog with Full SEO
|
|
457
|
+
|
|
458
|
+
```javascript
|
|
459
|
+
import {
|
|
460
|
+
createSEOHooks,
|
|
461
|
+
createArticleSchema,
|
|
462
|
+
createBreadcrumbSchema
|
|
463
|
+
} from '@coherent.js/seo';
|
|
464
|
+
|
|
465
|
+
const seo = createSEOHooks({
|
|
466
|
+
siteUrl: 'https://myblog.com',
|
|
467
|
+
defaultTitle: 'My Blog',
|
|
468
|
+
titleTemplate: '%s | My Blog'
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
function BlogPost({ post }) {
|
|
472
|
+
// Set basic metadata
|
|
473
|
+
seo.setMetadata({
|
|
474
|
+
title: post.title,
|
|
475
|
+
description: post.excerpt,
|
|
476
|
+
image: post.featuredImage,
|
|
477
|
+
publishedTime: post.publishedAt,
|
|
478
|
+
modifiedTime: post.updatedAt,
|
|
479
|
+
type: 'article',
|
|
480
|
+
author: post.author.name,
|
|
481
|
+
tags: post.tags
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
// Add structured data
|
|
485
|
+
seo.addSchema(createArticleSchema({
|
|
486
|
+
headline: post.title,
|
|
487
|
+
description: post.excerpt,
|
|
488
|
+
author: {
|
|
489
|
+
name: post.author.name,
|
|
490
|
+
url: `/authors/${post.author.slug}`
|
|
491
|
+
},
|
|
492
|
+
datePublished: post.publishedAt,
|
|
493
|
+
dateModified: post.updatedAt,
|
|
494
|
+
image: post.featuredImage,
|
|
495
|
+
keywords: post.tags,
|
|
496
|
+
articleBody: post.content
|
|
497
|
+
}));
|
|
498
|
+
|
|
499
|
+
// Add breadcrumbs
|
|
500
|
+
seo.addSchema(createBreadcrumbSchema([
|
|
501
|
+
{ name: 'Home', url: '/' },
|
|
502
|
+
{ name: 'Blog', url: '/blog' },
|
|
503
|
+
{ name: post.title, url: `/blog/${post.slug}` }
|
|
504
|
+
]));
|
|
505
|
+
|
|
506
|
+
// Set canonical URL
|
|
507
|
+
seo.setCanonical(`https://myblog.com/blog/${post.slug}`);
|
|
508
|
+
|
|
509
|
+
return {
|
|
510
|
+
article: {
|
|
511
|
+
className: 'blog-post',
|
|
512
|
+
children: [
|
|
513
|
+
{ h1: { text: post.title } },
|
|
514
|
+
{
|
|
515
|
+
div: {
|
|
516
|
+
className: 'post-meta',
|
|
517
|
+
children: [
|
|
518
|
+
{ span: { text: `By ${post.author.name}` } },
|
|
519
|
+
{ time: {
|
|
520
|
+
text: formatDate(post.publishedAt),
|
|
521
|
+
datetime: post.publishedAt
|
|
522
|
+
}}
|
|
523
|
+
]
|
|
524
|
+
}
|
|
525
|
+
},
|
|
526
|
+
{ div: {
|
|
527
|
+
className: 'post-content',
|
|
528
|
+
innerHTML: post.content
|
|
529
|
+
}}
|
|
530
|
+
]
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### E-commerce Product Page
|
|
537
|
+
|
|
538
|
+
```javascript
|
|
539
|
+
import {
|
|
540
|
+
createSEOHooks,
|
|
541
|
+
createProductSchema
|
|
542
|
+
} from '@coherent.js/seo';
|
|
543
|
+
|
|
544
|
+
const seo = createSEOHooks({
|
|
545
|
+
siteUrl: 'https://mystore.com'
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
function ProductPage({ product, reviews }) {
|
|
549
|
+
// Basic SEO metadata
|
|
550
|
+
seo.setMetadata({
|
|
551
|
+
title: `${product.name} | My Store`,
|
|
552
|
+
description: product.description,
|
|
553
|
+
image: product.images[0],
|
|
554
|
+
type: 'product.item',
|
|
555
|
+
|
|
556
|
+
// Open Graph for social sharing
|
|
557
|
+
og: {
|
|
558
|
+
price: {
|
|
559
|
+
amount: product.price.toString(),
|
|
560
|
+
currency: product.currency
|
|
561
|
+
},
|
|
562
|
+
availability: product.inStock ? 'instock' : 'outofstock',
|
|
563
|
+
condition: 'new'
|
|
564
|
+
},
|
|
565
|
+
|
|
566
|
+
// Twitter Card
|
|
567
|
+
twitter: {
|
|
568
|
+
card: 'summary_large_image',
|
|
569
|
+
label1: 'Price',
|
|
570
|
+
data1: `${product.currency} ${product.price}`,
|
|
571
|
+
label2: 'Availability',
|
|
572
|
+
data2: product.inStock ? 'In Stock' : 'Out of Stock'
|
|
573
|
+
}
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
// Product structured data
|
|
577
|
+
seo.addSchema(createProductSchema({
|
|
578
|
+
name: product.name,
|
|
579
|
+
description: product.description,
|
|
580
|
+
image: product.images,
|
|
581
|
+
offers: {
|
|
582
|
+
price: product.price.toString(),
|
|
583
|
+
priceCurrency: product.currency,
|
|
584
|
+
availability: product.inStock ? 'InStock' : 'OutOfStock',
|
|
585
|
+
url: `https://mystore.com/products/${product.slug}`,
|
|
586
|
+
seller: {
|
|
587
|
+
name: 'My Store'
|
|
588
|
+
}
|
|
589
|
+
},
|
|
590
|
+
brand: product.brand,
|
|
591
|
+
category: product.category,
|
|
592
|
+
sku: product.sku,
|
|
593
|
+
gtin: product.gtin,
|
|
594
|
+
aggregateRating: {
|
|
595
|
+
ratingValue: reviews.averageRating,
|
|
596
|
+
reviewCount: reviews.count
|
|
597
|
+
}
|
|
598
|
+
}));
|
|
599
|
+
|
|
600
|
+
// Set canonical and alternates
|
|
601
|
+
seo.setCanonical(`https://mystore.com/products/${product.slug}`);
|
|
602
|
+
|
|
603
|
+
return {
|
|
604
|
+
div: {
|
|
605
|
+
className: 'product-page',
|
|
606
|
+
children: [
|
|
607
|
+
{ h1: { text: product.name } },
|
|
608
|
+
{
|
|
609
|
+
div: {
|
|
610
|
+
className: 'product-images',
|
|
611
|
+
children: product.images.map(img => ({
|
|
612
|
+
img: { src: img, alt: product.name }
|
|
613
|
+
}))
|
|
614
|
+
}
|
|
615
|
+
},
|
|
616
|
+
{ p: { text: product.description } },
|
|
617
|
+
{ p: { text: `${product.currency} ${product.price}` } },
|
|
618
|
+
{
|
|
619
|
+
button: {
|
|
620
|
+
text: product.inStock ? 'Add to Cart' : 'Out of Stock',
|
|
621
|
+
disabled: !product.inStock
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
]
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
## Best Practices
|
|
631
|
+
|
|
632
|
+
### 1. Unique Titles and Descriptions
|
|
633
|
+
|
|
634
|
+
```javascript
|
|
635
|
+
// Good: Unique for each page
|
|
636
|
+
seo.setMetadata({
|
|
637
|
+
title: `How to Master Coherent.js | Developer Guide`,
|
|
638
|
+
description: 'Learn advanced techniques for building high-performance Coherent.js applications with this comprehensive guide.'
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
// Avoid: Generic/duplicated content
|
|
642
|
+
seo.setMetadata({
|
|
643
|
+
title: 'Page Title',
|
|
644
|
+
description: 'This is a description'
|
|
645
|
+
});
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
### 2. Optimal Lengths
|
|
649
|
+
|
|
650
|
+
```javascript
|
|
651
|
+
// Title: 50-60 characters
|
|
652
|
+
// Description: 150-160 characters
|
|
653
|
+
seo.setMetadata({
|
|
654
|
+
title: 'Coherent.js Performance Optimization Guide', // 42 chars - good
|
|
655
|
+
description: 'Maximize your Coherent.js application performance with these proven optimization techniques and best practices.' // 117 chars - good
|
|
656
|
+
});
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
### 3. Image Optimization
|
|
660
|
+
|
|
661
|
+
```javascript
|
|
662
|
+
seo.setMetadata({
|
|
663
|
+
// Use appropriately sized images
|
|
664
|
+
image: {
|
|
665
|
+
url: 'https://example.com/social-image.jpg',
|
|
666
|
+
width: 1200,
|
|
667
|
+
height: 630,
|
|
668
|
+
alt: 'Description of image content'
|
|
669
|
+
}
|
|
670
|
+
});
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
## Related Packages
|
|
674
|
+
|
|
675
|
+
- [@coherent.js/core](../core/README.md) - Core framework
|
|
676
|
+
- [@coherent.js/i18n](../i18n/README.md) - Internationalization
|
|
677
|
+
- [@coherent.js/client](../client/README.md) - Client-side utilities
|
|
678
|
+
|
|
679
|
+
## License
|
|
680
|
+
|
|
681
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coherent.js/seo",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.6",
|
|
4
4
|
"description": "SEO utilities for Coherent.js applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"author": "Coherent.js Team",
|
|
21
21
|
"license": "MIT",
|
|
22
22
|
"peerDependencies": {
|
|
23
|
-
"@coherent.js/core": "1.0.0-beta.
|
|
23
|
+
"@coherent.js/core": "1.0.0-beta.6"
|
|
24
24
|
},
|
|
25
25
|
"repository": {
|
|
26
26
|
"type": "git",
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"README.md",
|
|
36
36
|
"types/"
|
|
37
37
|
],
|
|
38
|
+
"sideEffects": false,
|
|
38
39
|
"scripts": {
|
|
39
40
|
"build": "node build.mjs",
|
|
40
41
|
"clean": "rm -rf dist"
|
package/types/index.d.ts
CHANGED
|
@@ -3,8 +3,164 @@
|
|
|
3
3
|
* @module @coherent.js/seo
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
import type { CoherentNode } from '@coherent.js/core';
|
|
7
7
|
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Meta Tag Types
|
|
10
|
+
// ============================================================================
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Generic meta tag definition
|
|
14
|
+
*/
|
|
15
|
+
export interface MetaTag {
|
|
16
|
+
/** Meta name attribute (for standard meta tags) */
|
|
17
|
+
name?: string;
|
|
18
|
+
/** Meta property attribute (for Open Graph, etc.) */
|
|
19
|
+
property?: string;
|
|
20
|
+
/** Meta content value */
|
|
21
|
+
content: string;
|
|
22
|
+
/** HTTP-equiv attribute */
|
|
23
|
+
httpEquiv?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Open Graph image configuration
|
|
28
|
+
*/
|
|
29
|
+
export interface OpenGraphImage {
|
|
30
|
+
/** Image URL */
|
|
31
|
+
url: string;
|
|
32
|
+
/** Image width in pixels */
|
|
33
|
+
width?: number;
|
|
34
|
+
/** Image height in pixels */
|
|
35
|
+
height?: number;
|
|
36
|
+
/** Image alt text */
|
|
37
|
+
alt?: string;
|
|
38
|
+
/** Image MIME type */
|
|
39
|
+
type?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Open Graph meta configuration
|
|
44
|
+
*/
|
|
45
|
+
export interface OpenGraphMeta {
|
|
46
|
+
/** Page title */
|
|
47
|
+
title: string;
|
|
48
|
+
/** Page description */
|
|
49
|
+
description?: string;
|
|
50
|
+
/** Content type */
|
|
51
|
+
type?: 'website' | 'article' | 'book' | 'profile' | 'video.movie' | 'video.episode' | 'music.song' | 'music.album';
|
|
52
|
+
/** Page URL */
|
|
53
|
+
url?: string;
|
|
54
|
+
/** Image(s) for sharing */
|
|
55
|
+
image?: string | OpenGraphImage | (string | OpenGraphImage)[];
|
|
56
|
+
/** Site name */
|
|
57
|
+
siteName?: string;
|
|
58
|
+
/** Locale (e.g., 'en_US') */
|
|
59
|
+
locale?: string;
|
|
60
|
+
/** Alternate locales */
|
|
61
|
+
alternateLocales?: string[];
|
|
62
|
+
/** Audio URL */
|
|
63
|
+
audio?: string;
|
|
64
|
+
/** Video URL */
|
|
65
|
+
video?: string;
|
|
66
|
+
/** Determiner (a, an, the, auto, or empty) */
|
|
67
|
+
determiner?: 'a' | 'an' | 'the' | 'auto' | '';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Twitter Card meta configuration
|
|
72
|
+
*/
|
|
73
|
+
export interface TwitterMeta {
|
|
74
|
+
/** Card type */
|
|
75
|
+
card?: 'summary' | 'summary_large_image' | 'app' | 'player';
|
|
76
|
+
/** Twitter @username of website */
|
|
77
|
+
site?: string;
|
|
78
|
+
/** Twitter @username of content creator */
|
|
79
|
+
creator?: string;
|
|
80
|
+
/** Card title (defaults to og:title) */
|
|
81
|
+
title?: string;
|
|
82
|
+
/** Card description (defaults to og:description) */
|
|
83
|
+
description?: string;
|
|
84
|
+
/** Card image (defaults to og:image) */
|
|
85
|
+
image?: string;
|
|
86
|
+
/** Image alt text */
|
|
87
|
+
imageAlt?: string;
|
|
88
|
+
/** App ID for app cards */
|
|
89
|
+
appIdIphone?: string;
|
|
90
|
+
/** App ID for iPad */
|
|
91
|
+
appIdIpad?: string;
|
|
92
|
+
/** App ID for Google Play */
|
|
93
|
+
appIdGooglePlay?: string;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Comprehensive SEO configuration
|
|
98
|
+
*/
|
|
99
|
+
export interface SEOConfig {
|
|
100
|
+
/** Page title */
|
|
101
|
+
title: string;
|
|
102
|
+
/** Title template (e.g., '%s | My Site') */
|
|
103
|
+
titleTemplate?: string;
|
|
104
|
+
/** Page description */
|
|
105
|
+
description?: string;
|
|
106
|
+
/** Canonical URL */
|
|
107
|
+
canonical?: string;
|
|
108
|
+
/** Robots directives (e.g., 'index, follow') */
|
|
109
|
+
robots?: string;
|
|
110
|
+
/** Additional meta tags */
|
|
111
|
+
meta?: MetaTag[];
|
|
112
|
+
/** Open Graph configuration */
|
|
113
|
+
openGraph?: OpenGraphMeta;
|
|
114
|
+
/** Twitter Card configuration */
|
|
115
|
+
twitter?: TwitterMeta;
|
|
116
|
+
/** JSON-LD structured data */
|
|
117
|
+
jsonLd?: Record<string, unknown> | Record<string, unknown>[];
|
|
118
|
+
/** Keywords (comma-separated or array) */
|
|
119
|
+
keywords?: string | string[];
|
|
120
|
+
/** Author name */
|
|
121
|
+
author?: string;
|
|
122
|
+
/** Viewport settings */
|
|
123
|
+
viewport?: string;
|
|
124
|
+
/** Character set */
|
|
125
|
+
charset?: string;
|
|
126
|
+
/** Language code */
|
|
127
|
+
language?: string;
|
|
128
|
+
/** Theme color */
|
|
129
|
+
themeColor?: string;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ============================================================================
|
|
133
|
+
// SEO Generation Functions
|
|
134
|
+
// ============================================================================
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Generate meta tags from SEO configuration
|
|
138
|
+
* @returns Array of meta element nodes
|
|
139
|
+
*/
|
|
140
|
+
export function generateMeta(config: SEOConfig): CoherentNode[];
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Generate JSON-LD script tag from data
|
|
144
|
+
*/
|
|
145
|
+
export function generateJsonLd(data: Record<string, unknown>): CoherentNode;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Create a reusable SEO component from configuration
|
|
149
|
+
*/
|
|
150
|
+
export function createSEOComponent(config: SEOConfig): () => CoherentNode;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Merge multiple SEO configurations (later configs override earlier)
|
|
154
|
+
*/
|
|
155
|
+
export function mergeSEOConfig(...configs: Partial<SEOConfig>[]): SEOConfig;
|
|
156
|
+
|
|
157
|
+
// ============================================================================
|
|
158
|
+
// Meta Builder
|
|
159
|
+
// ============================================================================
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* All meta tags in a flat structure
|
|
163
|
+
*/
|
|
8
164
|
export interface MetaTags {
|
|
9
165
|
title?: string;
|
|
10
166
|
description?: string;
|
|
@@ -32,41 +188,88 @@ export interface MetaTags {
|
|
|
32
188
|
twitterDescription?: string;
|
|
33
189
|
twitterImage?: string;
|
|
34
190
|
// Custom meta tags
|
|
35
|
-
[key: string]:
|
|
191
|
+
[key: string]: unknown;
|
|
36
192
|
}
|
|
37
193
|
|
|
194
|
+
/**
|
|
195
|
+
* Fluent builder for meta tags
|
|
196
|
+
*/
|
|
38
197
|
export class MetaBuilder {
|
|
39
198
|
constructor(tags?: MetaTags);
|
|
199
|
+
|
|
200
|
+
/** Set page title */
|
|
40
201
|
setTitle(title: string): this;
|
|
202
|
+
|
|
203
|
+
/** Set page description */
|
|
41
204
|
setDescription(description: string): this;
|
|
205
|
+
|
|
206
|
+
/** Set keywords */
|
|
42
207
|
setKeywords(keywords: string | string[]): this;
|
|
208
|
+
|
|
209
|
+
/** Set author */
|
|
43
210
|
setAuthor(author: string): this;
|
|
211
|
+
|
|
212
|
+
/** Set robots directive */
|
|
44
213
|
setRobots(robots: string): this;
|
|
214
|
+
|
|
215
|
+
/** Set canonical URL */
|
|
45
216
|
setCanonical(url: string): this;
|
|
217
|
+
|
|
218
|
+
/** Set viewport */
|
|
46
219
|
setViewport(viewport: string): this;
|
|
47
|
-
|
|
48
|
-
|
|
220
|
+
|
|
221
|
+
/** Set Open Graph properties */
|
|
222
|
+
setOpenGraph(tags: Partial<OpenGraphMeta>): this;
|
|
223
|
+
|
|
224
|
+
/** Set Twitter Card properties */
|
|
225
|
+
setTwitterCard(tags: Partial<TwitterMeta>): this;
|
|
226
|
+
|
|
227
|
+
/** Add a custom meta tag */
|
|
49
228
|
addCustomTag(name: string, content: string, type?: 'name' | 'property'): this;
|
|
50
|
-
|
|
51
|
-
|
|
229
|
+
|
|
230
|
+
/** Build as CoherentNode */
|
|
231
|
+
build(): CoherentNode;
|
|
232
|
+
|
|
233
|
+
/** Alias for build */
|
|
234
|
+
render(): CoherentNode;
|
|
235
|
+
|
|
236
|
+
/** Convert to HTML string */
|
|
52
237
|
toHTML(): string;
|
|
53
238
|
}
|
|
54
239
|
|
|
240
|
+
/**
|
|
241
|
+
* Create a MetaBuilder instance
|
|
242
|
+
*/
|
|
55
243
|
export function createMetaBuilder(tags?: MetaTags): MetaBuilder;
|
|
56
|
-
export function generateMeta(tags: MetaTags): object;
|
|
57
244
|
|
|
58
|
-
|
|
245
|
+
/**
|
|
246
|
+
* Generate meta nodes from flat tags
|
|
247
|
+
*/
|
|
248
|
+
export function generateMeta(tags: MetaTags): CoherentNode;
|
|
59
249
|
|
|
250
|
+
// ============================================================================
|
|
251
|
+
// Sitemap Generator
|
|
252
|
+
// ============================================================================
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Sitemap entry configuration
|
|
256
|
+
*/
|
|
60
257
|
export interface SitemapEntry {
|
|
258
|
+
/** Page URL */
|
|
61
259
|
url: string;
|
|
260
|
+
/** Last modification date */
|
|
62
261
|
lastmod?: string | Date;
|
|
262
|
+
/** Change frequency */
|
|
63
263
|
changefreq?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never';
|
|
264
|
+
/** Priority (0.0 to 1.0) */
|
|
64
265
|
priority?: number;
|
|
266
|
+
/** Image entries */
|
|
65
267
|
images?: Array<{
|
|
66
268
|
loc: string;
|
|
67
269
|
title?: string;
|
|
68
270
|
caption?: string;
|
|
69
271
|
}>;
|
|
272
|
+
/** Video entries */
|
|
70
273
|
videos?: Array<{
|
|
71
274
|
thumbnail_loc: string;
|
|
72
275
|
title: string;
|
|
@@ -74,30 +277,69 @@ export interface SitemapEntry {
|
|
|
74
277
|
content_loc?: string;
|
|
75
278
|
player_loc?: string;
|
|
76
279
|
}>;
|
|
280
|
+
/** Alternate language versions */
|
|
281
|
+
alternates?: Array<{
|
|
282
|
+
hreflang: string;
|
|
283
|
+
href: string;
|
|
284
|
+
}>;
|
|
77
285
|
}
|
|
78
286
|
|
|
287
|
+
/**
|
|
288
|
+
* Sitemap generator options
|
|
289
|
+
*/
|
|
79
290
|
export interface SitemapOptions {
|
|
291
|
+
/** Site hostname (e.g., 'https://example.com') */
|
|
80
292
|
hostname: string;
|
|
293
|
+
/** Cache time in milliseconds */
|
|
81
294
|
cacheTime?: number;
|
|
295
|
+
/** XML namespaces */
|
|
82
296
|
xmlNs?: Record<string, string>;
|
|
297
|
+
/** XSL stylesheet URL */
|
|
83
298
|
xslUrl?: string;
|
|
84
299
|
}
|
|
85
300
|
|
|
301
|
+
/**
|
|
302
|
+
* Sitemap generator class
|
|
303
|
+
*/
|
|
86
304
|
export class SitemapGenerator {
|
|
87
305
|
constructor(options: SitemapOptions);
|
|
306
|
+
|
|
307
|
+
/** Add a URL entry */
|
|
88
308
|
addUrl(entry: SitemapEntry): this;
|
|
309
|
+
|
|
310
|
+
/** Add multiple URL entries */
|
|
89
311
|
addUrls(entries: SitemapEntry[]): this;
|
|
312
|
+
|
|
313
|
+
/** Remove a URL entry */
|
|
90
314
|
removeUrl(url: string): this;
|
|
315
|
+
|
|
316
|
+
/** Generate sitemap XML string */
|
|
91
317
|
generate(): string;
|
|
318
|
+
|
|
319
|
+
/** Alias for generate */
|
|
92
320
|
toXML(): string;
|
|
321
|
+
|
|
322
|
+
/** Export entries as JSON */
|
|
93
323
|
toJSON(): SitemapEntry[];
|
|
94
324
|
}
|
|
95
325
|
|
|
326
|
+
/**
|
|
327
|
+
* Create a sitemap generator
|
|
328
|
+
*/
|
|
96
329
|
export function createSitemapGenerator(options: SitemapOptions): SitemapGenerator;
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Generate sitemap XML from entries
|
|
333
|
+
*/
|
|
97
334
|
export function generateSitemap(entries: SitemapEntry[], options: SitemapOptions): string;
|
|
98
335
|
|
|
99
|
-
//
|
|
336
|
+
// ============================================================================
|
|
337
|
+
// Structured Data (JSON-LD)
|
|
338
|
+
// ============================================================================
|
|
100
339
|
|
|
340
|
+
/**
|
|
341
|
+
* Supported structured data types
|
|
342
|
+
*/
|
|
101
343
|
export type StructuredDataType =
|
|
102
344
|
| 'Article'
|
|
103
345
|
| 'BlogPosting'
|
|
@@ -115,30 +357,71 @@ export type StructuredDataType =
|
|
|
115
357
|
| 'FAQPage'
|
|
116
358
|
| 'HowTo'
|
|
117
359
|
| 'VideoObject'
|
|
118
|
-
| 'ImageObject'
|
|
360
|
+
| 'ImageObject'
|
|
361
|
+
| 'SoftwareApplication'
|
|
362
|
+
| 'Course'
|
|
363
|
+
| 'JobPosting';
|
|
119
364
|
|
|
365
|
+
/**
|
|
366
|
+
* Structured data object
|
|
367
|
+
*/
|
|
120
368
|
export interface StructuredData {
|
|
121
369
|
'@context': string;
|
|
122
370
|
'@type': StructuredDataType | StructuredDataType[];
|
|
123
|
-
[key: string]:
|
|
371
|
+
[key: string]: unknown;
|
|
124
372
|
}
|
|
125
373
|
|
|
374
|
+
/**
|
|
375
|
+
* Fluent builder for structured data
|
|
376
|
+
*/
|
|
126
377
|
export class StructuredDataBuilder {
|
|
127
378
|
constructor(type: StructuredDataType);
|
|
379
|
+
|
|
380
|
+
/** Change the schema type */
|
|
128
381
|
setType(type: StructuredDataType): this;
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
382
|
+
|
|
383
|
+
/** Set a single property */
|
|
384
|
+
setProperty(key: string, value: unknown): this;
|
|
385
|
+
|
|
386
|
+
/** Set multiple properties */
|
|
387
|
+
setProperties(properties: Record<string, unknown>): this;
|
|
388
|
+
|
|
389
|
+
/** Add value to an array property */
|
|
390
|
+
addToArray(key: string, value: unknown): this;
|
|
391
|
+
|
|
392
|
+
/** Build the structured data object */
|
|
132
393
|
build(): StructuredData;
|
|
394
|
+
|
|
395
|
+
/** Convert to JSON string */
|
|
133
396
|
toJSON(): string;
|
|
134
|
-
|
|
397
|
+
|
|
398
|
+
/** Build as JSON-LD script node */
|
|
399
|
+
toJSONLD(): CoherentNode;
|
|
135
400
|
}
|
|
136
401
|
|
|
137
|
-
|
|
138
|
-
|
|
402
|
+
/**
|
|
403
|
+
* Create a structured data builder
|
|
404
|
+
*/
|
|
405
|
+
export function createStructuredData(
|
|
406
|
+
type: StructuredDataType,
|
|
407
|
+
properties?: Record<string, unknown>
|
|
408
|
+
): StructuredDataBuilder;
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Generate structured data object
|
|
412
|
+
*/
|
|
413
|
+
export function generateStructuredData(
|
|
414
|
+
type: StructuredDataType,
|
|
415
|
+
properties: Record<string, unknown>
|
|
416
|
+
): StructuredData;
|
|
139
417
|
|
|
140
|
-
//
|
|
418
|
+
// ============================================================================
|
|
419
|
+
// Common Structured Data Types
|
|
420
|
+
// ============================================================================
|
|
141
421
|
|
|
422
|
+
/**
|
|
423
|
+
* Article structured data
|
|
424
|
+
*/
|
|
142
425
|
export interface ArticleData {
|
|
143
426
|
headline: string;
|
|
144
427
|
image: string | string[];
|
|
@@ -160,6 +443,9 @@ export interface ArticleData {
|
|
|
160
443
|
mainEntityOfPage?: string;
|
|
161
444
|
}
|
|
162
445
|
|
|
446
|
+
/**
|
|
447
|
+
* Product structured data
|
|
448
|
+
*/
|
|
163
449
|
export interface ProductData {
|
|
164
450
|
name: string;
|
|
165
451
|
image: string | string[];
|
|
@@ -182,6 +468,9 @@ export interface ProductData {
|
|
|
182
468
|
};
|
|
183
469
|
}
|
|
184
470
|
|
|
471
|
+
/**
|
|
472
|
+
* Breadcrumb structured data
|
|
473
|
+
*/
|
|
185
474
|
export interface BreadcrumbData {
|
|
186
475
|
itemListElement: Array<{
|
|
187
476
|
'@type': 'ListItem';
|
|
@@ -190,3 +479,33 @@ export interface BreadcrumbData {
|
|
|
190
479
|
item?: string;
|
|
191
480
|
}>;
|
|
192
481
|
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Organization structured data
|
|
485
|
+
*/
|
|
486
|
+
export interface OrganizationData {
|
|
487
|
+
name: string;
|
|
488
|
+
url?: string;
|
|
489
|
+
logo?: string;
|
|
490
|
+
description?: string;
|
|
491
|
+
sameAs?: string[];
|
|
492
|
+
contactPoint?: {
|
|
493
|
+
'@type': 'ContactPoint';
|
|
494
|
+
telephone: string;
|
|
495
|
+
contactType: string;
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* FAQ Page structured data
|
|
501
|
+
*/
|
|
502
|
+
export interface FAQPageData {
|
|
503
|
+
mainEntity: Array<{
|
|
504
|
+
'@type': 'Question';
|
|
505
|
+
name: string;
|
|
506
|
+
acceptedAnswer: {
|
|
507
|
+
'@type': 'Answer';
|
|
508
|
+
text: string;
|
|
509
|
+
};
|
|
510
|
+
}>;
|
|
511
|
+
}
|