@hkdigital/lib-core 0.5.12 → 0.5.14
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/dist/meta/README.md +386 -0
- package/dist/meta/robots/index.d.ts +37 -0
- package/dist/meta/robots/index.js +83 -0
- package/dist/meta/robots/typedef.d.ts +13 -0
- package/dist/meta/robots/typedef.js +10 -0
- package/dist/meta/robots.d.ts +1 -0
- package/dist/meta/robots.js +5 -0
- package/dist/meta/sitemap/index.d.ts +11 -0
- package/dist/meta/sitemap/index.js +63 -0
- package/dist/meta/sitemap/typedef.d.ts +20 -0
- package/dist/meta/sitemap/typedef.js +14 -0
- package/dist/meta/sitemap.d.ts +1 -0
- package/dist/meta/sitemap.js +5 -0
- package/package.json +1 -1
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
# Meta Utilities
|
|
2
|
+
|
|
3
|
+
SEO and meta tag utilities for robots.txt and sitemap.xml generation.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This module provides functions for generating robots.txt and sitemap.xml
|
|
8
|
+
files dynamically based on configuration. The utilities are used by the
|
|
9
|
+
`routes/(meta)` endpoints but can also be used independently in your own
|
|
10
|
+
server routes.
|
|
11
|
+
|
|
12
|
+
## Modules
|
|
13
|
+
|
|
14
|
+
### Robots
|
|
15
|
+
|
|
16
|
+
Generate robots.txt content with host filtering and sitemap references.
|
|
17
|
+
|
|
18
|
+
```javascript
|
|
19
|
+
import { generateRobotsTxt, isHostAllowed } from '$lib/meta/robots.js';
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Sitemap
|
|
23
|
+
|
|
24
|
+
Generate sitemap.xml content from route configurations.
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
|
+
import { generateSitemap } from '$lib/meta/sitemap.js';
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
### Robots.txt Generation
|
|
33
|
+
|
|
34
|
+
The `generateRobotsTxt()` function creates robots.txt content based on the
|
|
35
|
+
request URL and configuration.
|
|
36
|
+
|
|
37
|
+
**Basic usage:**
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
|
+
import { generateRobotsTxt } from '$lib/meta/robots.js';
|
|
41
|
+
|
|
42
|
+
export const GET = async ({ url }) => {
|
|
43
|
+
const config = {
|
|
44
|
+
allowedHosts: ['example.com', 'www.example.com'],
|
|
45
|
+
disallowedPaths: ['/admin', '/api']
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const robotsTxt = generateRobotsTxt(url, config);
|
|
49
|
+
return new Response(robotsTxt, {
|
|
50
|
+
headers: { 'Content-Type': 'text/plain' }
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Configuration options:**
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
/**
|
|
59
|
+
* @typedef {Object} RobotsConfig
|
|
60
|
+
* @property {string[] | '*'} [allowedHosts]
|
|
61
|
+
* Allowed host patterns. Use '*' or omit to allow all hosts.
|
|
62
|
+
* Supports wildcards (e.g., '*.example.com')
|
|
63
|
+
* @property {string[]} [disallowedPaths]
|
|
64
|
+
* Paths to block from indexing (e.g., '/admin', '/api/*')
|
|
65
|
+
*/
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Host filtering:**
|
|
69
|
+
|
|
70
|
+
```javascript
|
|
71
|
+
// Allow only production domain
|
|
72
|
+
const config = {
|
|
73
|
+
allowedHosts: ['example.com']
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// example.com → User-agent: *\nAllow: /\nSitemap: ...
|
|
77
|
+
// test.example.com → User-agent: *\nDisallow: /
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Wildcard patterns:**
|
|
81
|
+
|
|
82
|
+
```javascript
|
|
83
|
+
// Allow all subdomains
|
|
84
|
+
const config = {
|
|
85
|
+
allowedHosts: ['example.com', '*.example.com']
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// example.com → allowed
|
|
89
|
+
// test.example.com → allowed
|
|
90
|
+
// staging.example.com → allowed
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Path blocking:**
|
|
94
|
+
|
|
95
|
+
```javascript
|
|
96
|
+
// Block specific paths
|
|
97
|
+
const config = {
|
|
98
|
+
allowedHosts: ['example.com'],
|
|
99
|
+
disallowedPaths: ['/admin', '/api', '/private/*']
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// Generates:
|
|
103
|
+
// User-agent: *
|
|
104
|
+
// Allow: /
|
|
105
|
+
// Disallow: /admin
|
|
106
|
+
// Disallow: /api
|
|
107
|
+
// Disallow: /private/*
|
|
108
|
+
// Sitemap: https://example.com/sitemap.xml
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Sitemap reference:**
|
|
112
|
+
|
|
113
|
+
Sitemap reference is always included for allowed hosts:
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
User-agent: *
|
|
117
|
+
Allow: /
|
|
118
|
+
Sitemap: https://example.com/sitemap.xml
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Sitemap.xml Generation
|
|
122
|
+
|
|
123
|
+
The `generateSitemap()` function creates sitemap.xml content from route
|
|
124
|
+
configurations.
|
|
125
|
+
|
|
126
|
+
**Basic usage:**
|
|
127
|
+
|
|
128
|
+
```javascript
|
|
129
|
+
import { generateSitemap } from '$lib/meta/sitemap.js';
|
|
130
|
+
|
|
131
|
+
export const GET = async ({ url }) => {
|
|
132
|
+
const routes = ['/', '/about', '/contact'];
|
|
133
|
+
|
|
134
|
+
const sitemap = generateSitemap(url.origin, routes);
|
|
135
|
+
return new Response(sitemap, {
|
|
136
|
+
headers: { 'Content-Type': 'application/xml' }
|
|
137
|
+
});
|
|
138
|
+
};
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**Route configuration:**
|
|
142
|
+
|
|
143
|
+
```javascript
|
|
144
|
+
/**
|
|
145
|
+
* @typedef {string | SitemapRouteObject} SitemapRoute
|
|
146
|
+
* Route can be a simple string path or an object with details
|
|
147
|
+
*/
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* @typedef {Object} SitemapRouteObject
|
|
151
|
+
* @property {string} path - Route path (e.g., '/about')
|
|
152
|
+
* @property {number} [priority] - Priority (0.0 to 1.0)
|
|
153
|
+
* @property {'always'|'hourly'|'daily'|'weekly'|'monthly'|'yearly'|'never'}
|
|
154
|
+
* [changefreq] - Change frequency
|
|
155
|
+
*/
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Simple format (recommended):**
|
|
159
|
+
|
|
160
|
+
```javascript
|
|
161
|
+
const routes = [
|
|
162
|
+
'/', // priority: 1.0, changefreq: 'daily'
|
|
163
|
+
'/about', // priority: 0.8, changefreq: 'weekly'
|
|
164
|
+
'/contact' // priority: 0.8, changefreq: 'weekly'
|
|
165
|
+
];
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**Advanced format with custom settings:**
|
|
169
|
+
|
|
170
|
+
```javascript
|
|
171
|
+
const routes = [
|
|
172
|
+
'/',
|
|
173
|
+
'/about',
|
|
174
|
+
{ path: '/blog', priority: 0.9, changefreq: 'daily' },
|
|
175
|
+
{ path: '/legal', priority: 0.3, changefreq: 'yearly' }
|
|
176
|
+
];
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Mixed format:**
|
|
180
|
+
|
|
181
|
+
```javascript
|
|
182
|
+
const routes = [
|
|
183
|
+
'/',
|
|
184
|
+
'/about',
|
|
185
|
+
{ path: '/blog', priority: 0.9, changefreq: 'daily' },
|
|
186
|
+
'/contact'
|
|
187
|
+
];
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**Default values:**
|
|
191
|
+
|
|
192
|
+
- Root path (`/`): priority `1.0`, changefreq `daily`
|
|
193
|
+
- Other paths: priority `0.8`, changefreq `weekly`
|
|
194
|
+
- Root path is always included (added automatically if missing)
|
|
195
|
+
|
|
196
|
+
## Helper Functions
|
|
197
|
+
|
|
198
|
+
### isHostAllowed()
|
|
199
|
+
|
|
200
|
+
Check if a hostname matches the allowed hosts configuration.
|
|
201
|
+
|
|
202
|
+
```javascript
|
|
203
|
+
import { isHostAllowed } from '$lib/meta/robots.js';
|
|
204
|
+
|
|
205
|
+
// Exact match
|
|
206
|
+
isHostAllowed('example.com', ['example.com']); // true
|
|
207
|
+
isHostAllowed('test.example.com', ['example.com']); // false
|
|
208
|
+
|
|
209
|
+
// Wildcard match
|
|
210
|
+
isHostAllowed('test.example.com', ['*.example.com']); // true
|
|
211
|
+
isHostAllowed('example.com', ['*.example.com']); // false
|
|
212
|
+
|
|
213
|
+
// Allow all
|
|
214
|
+
isHostAllowed('anything.com', '*'); // true
|
|
215
|
+
isHostAllowed('anything.com', undefined); // true
|
|
216
|
+
|
|
217
|
+
// Multiple patterns
|
|
218
|
+
isHostAllowed('example.com', ['example.com', '*.staging.com']); // true
|
|
219
|
+
isHostAllowed('app.staging.com', ['example.com', '*.staging.com']); // true
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**Case insensitive:**
|
|
223
|
+
|
|
224
|
+
```javascript
|
|
225
|
+
isHostAllowed('Example.COM', ['example.com']); // true
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
**String or array:**
|
|
229
|
+
|
|
230
|
+
```javascript
|
|
231
|
+
// Single string
|
|
232
|
+
isHostAllowed('example.com', 'example.com'); // true
|
|
233
|
+
|
|
234
|
+
// Array
|
|
235
|
+
isHostAllowed('example.com', ['example.com']); // true
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## Real-World Examples
|
|
239
|
+
|
|
240
|
+
### Production-only indexing
|
|
241
|
+
|
|
242
|
+
```javascript
|
|
243
|
+
// Only allow production domain to be indexed
|
|
244
|
+
const robotsConfig = {
|
|
245
|
+
allowedHosts: ['mysite.com', 'www.mysite.com'],
|
|
246
|
+
disallowedPaths: ['/admin', '/api']
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
// Production: mysite.com → Allow + Sitemap
|
|
250
|
+
// Staging: staging.mysite.com → Disallow
|
|
251
|
+
// Development: localhost → Disallow
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Staging and production indexing
|
|
255
|
+
|
|
256
|
+
```javascript
|
|
257
|
+
// Allow both production and staging subdomains
|
|
258
|
+
const robotsConfig = {
|
|
259
|
+
allowedHosts: ['mysite.com', '*.mysite.com'],
|
|
260
|
+
disallowedPaths: ['/admin', '/api']
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
// Production: mysite.com → Allow + Sitemap
|
|
264
|
+
// Staging: staging.mysite.com → Allow + Sitemap
|
|
265
|
+
// Development: localhost → Disallow
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Allow all hosts (development)
|
|
269
|
+
|
|
270
|
+
```javascript
|
|
271
|
+
// Allow all hosts - useful during development
|
|
272
|
+
const robotsConfig = {
|
|
273
|
+
allowedHosts: '*',
|
|
274
|
+
disallowedPaths: ['/admin']
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
// All hosts → Allow + Sitemap
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Complex sitemap configuration
|
|
281
|
+
|
|
282
|
+
```javascript
|
|
283
|
+
const routes = [
|
|
284
|
+
'/',
|
|
285
|
+
'/about',
|
|
286
|
+
'/contact',
|
|
287
|
+
|
|
288
|
+
// Blog updated frequently
|
|
289
|
+
{ path: '/blog', priority: 0.9, changefreq: 'daily' },
|
|
290
|
+
|
|
291
|
+
// Legal pages rarely change
|
|
292
|
+
{ path: '/privacy', priority: 0.3, changefreq: 'yearly' },
|
|
293
|
+
{ path: '/terms', priority: 0.3, changefreq: 'yearly' },
|
|
294
|
+
|
|
295
|
+
// Documentation moderately important
|
|
296
|
+
{ path: '/docs', priority: 0.7, changefreq: 'monthly' }
|
|
297
|
+
];
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## Integration with Routes
|
|
301
|
+
|
|
302
|
+
These utilities are used by the `routes/(meta)` endpoints:
|
|
303
|
+
|
|
304
|
+
### robots.txt endpoint
|
|
305
|
+
|
|
306
|
+
```javascript
|
|
307
|
+
// routes/(meta)/robots.txt/+server.js
|
|
308
|
+
import { text } from '@sveltejs/kit';
|
|
309
|
+
import { generateRobotsTxt } from '$lib/meta/robots.js';
|
|
310
|
+
import { robotsConfig } from '../config.js';
|
|
311
|
+
|
|
312
|
+
export const GET = async ({ url }) => {
|
|
313
|
+
const robotsTxt = generateRobotsTxt(url, robotsConfig);
|
|
314
|
+
return text(robotsTxt);
|
|
315
|
+
};
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### sitemap.xml endpoint
|
|
319
|
+
|
|
320
|
+
```javascript
|
|
321
|
+
// routes/(meta)/sitemap.xml/+server.js
|
|
322
|
+
import { generateSitemap } from '$lib/meta/sitemap.js';
|
|
323
|
+
import { siteRoutes } from '../config.js';
|
|
324
|
+
|
|
325
|
+
export const GET = async ({ url }) => {
|
|
326
|
+
const sitemap = generateSitemap(url.origin, siteRoutes);
|
|
327
|
+
|
|
328
|
+
return new Response(sitemap, {
|
|
329
|
+
headers: {
|
|
330
|
+
'Content-Type': 'application/xml',
|
|
331
|
+
'Cache-Control': 'max-age=0, s-maxage=3600'
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
};
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
## Reverse Proxy Configuration
|
|
338
|
+
|
|
339
|
+
If your app is deployed behind a reverse proxy (nginx, Cloudflare, etc.),
|
|
340
|
+
ensure your SvelteKit adapter is configured to trust proxy headers for
|
|
341
|
+
correct origin detection:
|
|
342
|
+
|
|
343
|
+
```javascript
|
|
344
|
+
// svelte.config.js
|
|
345
|
+
import adapter from '@sveltejs/adapter-node';
|
|
346
|
+
|
|
347
|
+
export default {
|
|
348
|
+
kit: {
|
|
349
|
+
adapter: adapter({
|
|
350
|
+
// Trust X-Forwarded-* headers from proxy
|
|
351
|
+
trustProxy: true
|
|
352
|
+
})
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
Without this, `url.origin` may be `http://localhost` instead of your actual
|
|
358
|
+
domain, and the sitemap directive will point to the wrong URL.
|
|
359
|
+
|
|
360
|
+
## Testing
|
|
361
|
+
|
|
362
|
+
Unit tests are included for all functions:
|
|
363
|
+
|
|
364
|
+
```bash
|
|
365
|
+
# Run meta utility tests
|
|
366
|
+
pnpm test:file src/lib/meta/
|
|
367
|
+
|
|
368
|
+
# Test coverage includes:
|
|
369
|
+
# - Host pattern matching (exact, wildcard, multiple)
|
|
370
|
+
# - Robots.txt generation (allowed/blocked hosts, paths, sitemap)
|
|
371
|
+
# - Sitemap generation (simple/advanced routes, defaults, mixed formats)
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
## Type Definitions
|
|
375
|
+
|
|
376
|
+
TypeScript-style JSDoc type definitions are available:
|
|
377
|
+
|
|
378
|
+
```javascript
|
|
379
|
+
// robots.js
|
|
380
|
+
import './robots/typedef.js'; // RobotsConfig
|
|
381
|
+
|
|
382
|
+
// sitemap.js
|
|
383
|
+
import './sitemap/typedef.js'; // SitemapRoute, SitemapRouteObject
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
See `typedef.js` files in each subdirectory for complete type definitions.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** @typedef {import('./typedef.js').RobotsConfig} RobotsConfig */
|
|
2
|
+
/**
|
|
3
|
+
* Check if hostname matches allowed hosts pattern
|
|
4
|
+
*
|
|
5
|
+
* @param {string} hostname - Hostname to check (e.g., test.mysite.com)
|
|
6
|
+
* @param {string[] | '*' | undefined} allowedHosts - Allowed host patterns
|
|
7
|
+
*
|
|
8
|
+
* @returns {boolean} True if host is allowed
|
|
9
|
+
*/
|
|
10
|
+
export function isHostAllowed(hostname: string, allowedHosts: string[] | "*" | undefined): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Generate robots.txt with sitemap reference
|
|
13
|
+
*
|
|
14
|
+
* NOTE: If deployed behind a reverse proxy (nginx, Cloudflare, etc.),
|
|
15
|
+
* ensure your adapter is configured to trust proxy headers for correct
|
|
16
|
+
* origin detection:
|
|
17
|
+
*
|
|
18
|
+
* // svelte.config.js
|
|
19
|
+
* export default {
|
|
20
|
+
* kit: {
|
|
21
|
+
* adapter: adapter({
|
|
22
|
+
* // Trust X-Forwarded-* headers from proxy
|
|
23
|
+
* trustProxy: true
|
|
24
|
+
* })
|
|
25
|
+
* }
|
|
26
|
+
* };
|
|
27
|
+
*
|
|
28
|
+
* Without this, url.origin may be http://localhost instead of your
|
|
29
|
+
* actual domain, and the sitemap directive will be omitted.
|
|
30
|
+
*
|
|
31
|
+
* @param {URL} url - Request URL object
|
|
32
|
+
* @param {RobotsConfig} [config] - Robots configuration object
|
|
33
|
+
*
|
|
34
|
+
* @returns {string} robots.txt content
|
|
35
|
+
*/
|
|
36
|
+
export function generateRobotsTxt(url: URL, config?: RobotsConfig): string;
|
|
37
|
+
export type RobotsConfig = import("./typedef.js").RobotsConfig;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/** @typedef {import('./typedef.js').RobotsConfig} RobotsConfig */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Check if hostname matches allowed hosts pattern
|
|
5
|
+
*
|
|
6
|
+
* @param {string} hostname - Hostname to check (e.g., test.mysite.com)
|
|
7
|
+
* @param {string[] | '*' | undefined} allowedHosts - Allowed host patterns
|
|
8
|
+
*
|
|
9
|
+
* @returns {boolean} True if host is allowed
|
|
10
|
+
*/
|
|
11
|
+
export function isHostAllowed(hostname, allowedHosts) {
|
|
12
|
+
// If not configured or set to '*', allow all hosts
|
|
13
|
+
if (!allowedHosts || allowedHosts === '*') {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (typeof allowedHosts === 'string') {
|
|
18
|
+
allowedHosts = [allowedHosts];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Check if hostname matches any allowed pattern
|
|
22
|
+
return allowedHosts.some((pattern) => {
|
|
23
|
+
// Convert wildcard pattern to regex
|
|
24
|
+
// Example: *.mysite.com -> ^.*\.mysite\.com$
|
|
25
|
+
const regexPattern = pattern
|
|
26
|
+
.replace(/\./g, '\\.') // Escape dots
|
|
27
|
+
.replace(/\*/g, '.*'); // * becomes .*
|
|
28
|
+
|
|
29
|
+
const regex = new RegExp(`^${regexPattern}$`, 'i');
|
|
30
|
+
return regex.test(hostname);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Generate robots.txt with sitemap reference
|
|
36
|
+
*
|
|
37
|
+
* NOTE: If deployed behind a reverse proxy (nginx, Cloudflare, etc.),
|
|
38
|
+
* ensure your adapter is configured to trust proxy headers for correct
|
|
39
|
+
* origin detection:
|
|
40
|
+
*
|
|
41
|
+
* // svelte.config.js
|
|
42
|
+
* export default {
|
|
43
|
+
* kit: {
|
|
44
|
+
* adapter: adapter({
|
|
45
|
+
* // Trust X-Forwarded-* headers from proxy
|
|
46
|
+
* trustProxy: true
|
|
47
|
+
* })
|
|
48
|
+
* }
|
|
49
|
+
* };
|
|
50
|
+
*
|
|
51
|
+
* Without this, url.origin may be http://localhost instead of your
|
|
52
|
+
* actual domain, and the sitemap directive will be omitted.
|
|
53
|
+
*
|
|
54
|
+
* @param {URL} url - Request URL object
|
|
55
|
+
* @param {RobotsConfig} [config] - Robots configuration object
|
|
56
|
+
*
|
|
57
|
+
* @returns {string} robots.txt content
|
|
58
|
+
*/
|
|
59
|
+
export function generateRobotsTxt(url, config = {}) {
|
|
60
|
+
const hostAllowed = isHostAllowed(url.hostname, config.allowedHosts);
|
|
61
|
+
|
|
62
|
+
// Block entire site if host is not allowed
|
|
63
|
+
if (!hostAllowed) {
|
|
64
|
+
return 'User-agent: *\nDisallow: /';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Allow site, but add specific path blocks
|
|
68
|
+
let content = 'User-agent: *\nAllow: /';
|
|
69
|
+
|
|
70
|
+
// Add disallowed paths
|
|
71
|
+
if (config.disallowedPaths && config.disallowedPaths.length > 0) {
|
|
72
|
+
config.disallowedPaths.forEach((path) => {
|
|
73
|
+
content += `\nDisallow: ${path}`;
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Always add sitemap reference
|
|
78
|
+
if (url.origin) {
|
|
79
|
+
content += `\nSitemap: ${url.origin}/sitemap.xml`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return content;
|
|
83
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
declare const _default: {};
|
|
2
|
+
export default _default;
|
|
3
|
+
export type RobotsConfig = {
|
|
4
|
+
/**
|
|
5
|
+
* Allowed host patterns. Use '*' or omit to allow all hosts.
|
|
6
|
+
* Supports wildcards (e.g., '*.example.com')
|
|
7
|
+
*/
|
|
8
|
+
allowedHosts?: string[] | "*" | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* Paths to block from indexing (e.g., '/admin', '/api/*')
|
|
11
|
+
*/
|
|
12
|
+
disallowedPaths?: string[] | undefined;
|
|
13
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} RobotsConfig
|
|
3
|
+
* @property {string[] | '*'} [allowedHosts]
|
|
4
|
+
* Allowed host patterns. Use '*' or omit to allow all hosts.
|
|
5
|
+
* Supports wildcards (e.g., '*.example.com')
|
|
6
|
+
* @property {string[]} [disallowedPaths]
|
|
7
|
+
* Paths to block from indexing (e.g., '/admin', '/api/*')
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export default {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { generateRobotsTxt, isHostAllowed } from "./robots/index.js";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate sitemap XML
|
|
3
|
+
*
|
|
4
|
+
* @param {string} origin - Base URL (e.g., https://example.com)
|
|
5
|
+
* @param {SitemapRoute[]} [routes=[]] - Array of routes
|
|
6
|
+
*
|
|
7
|
+
* @returns {string} XML sitemap
|
|
8
|
+
*/
|
|
9
|
+
export function generateSitemap(origin: string, routes?: SitemapRoute[]): string;
|
|
10
|
+
export type SitemapRoute = import("./typedef.js").SitemapRoute;
|
|
11
|
+
export type SitemapRouteObject = import("./typedef.js").SitemapRouteObject;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// @see https://www.sitemaps.org/protocol.html
|
|
2
|
+
|
|
3
|
+
/** @typedef {import('./typedef.js').SitemapRoute} SitemapRoute */
|
|
4
|
+
/** @typedef {import('./typedef.js').SitemapRouteObject} SitemapRouteObject */
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Normalize route to full route object with defaults
|
|
8
|
+
*
|
|
9
|
+
* @param {import('./typedef.js').SitemapRoute} route - Route path string or route object
|
|
10
|
+
*
|
|
11
|
+
* @returns {SitemapRouteObject} Normalized route object
|
|
12
|
+
*/
|
|
13
|
+
function normalizeRoute(route) {
|
|
14
|
+
// Handle simple string format
|
|
15
|
+
if (typeof route === 'string') {
|
|
16
|
+
return {
|
|
17
|
+
path: route,
|
|
18
|
+
priority: route === '/' ? 1.0 : 0.8,
|
|
19
|
+
changefreq: route === '/' ? 'daily' : 'weekly'
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Handle object format with defaults
|
|
24
|
+
return {
|
|
25
|
+
priority: 0.8,
|
|
26
|
+
changefreq: 'weekly',
|
|
27
|
+
...route
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Generate sitemap XML
|
|
33
|
+
*
|
|
34
|
+
* @param {string} origin - Base URL (e.g., https://example.com)
|
|
35
|
+
* @param {SitemapRoute[]} [routes=[]] - Array of routes
|
|
36
|
+
*
|
|
37
|
+
* @returns {string} XML sitemap
|
|
38
|
+
*/
|
|
39
|
+
export function generateSitemap(origin, routes = []) {
|
|
40
|
+
// Ensure root path is always included (failsafe)
|
|
41
|
+
const hasRoot = routes.some((route) => {
|
|
42
|
+
const path = typeof route === 'string' ? route : route.path;
|
|
43
|
+
return path === '/';
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const normalizedRoutes = hasRoot ? routes : ['/', ...routes];
|
|
47
|
+
|
|
48
|
+
const urls = normalizedRoutes
|
|
49
|
+
.map(normalizeRoute)
|
|
50
|
+
.map(
|
|
51
|
+
(route) => `
|
|
52
|
+
<url>
|
|
53
|
+
<loc>${origin}${route.path}</loc>
|
|
54
|
+
<changefreq>${route.changefreq}</changefreq>
|
|
55
|
+
<priority>${route.priority}</priority>
|
|
56
|
+
</url>`
|
|
57
|
+
)
|
|
58
|
+
.join('');
|
|
59
|
+
|
|
60
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
61
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">${urls}
|
|
62
|
+
</urlset>`;
|
|
63
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
declare const _default: {};
|
|
2
|
+
export default _default;
|
|
3
|
+
export type SitemapRouteObject = {
|
|
4
|
+
/**
|
|
5
|
+
* - Route path (e.g., '/about')
|
|
6
|
+
*/
|
|
7
|
+
path: string;
|
|
8
|
+
/**
|
|
9
|
+
* - Priority (0.0 to 1.0)
|
|
10
|
+
*/
|
|
11
|
+
priority?: number | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* - Change frequency
|
|
14
|
+
*/
|
|
15
|
+
changefreq?: "hourly" | "daily" | "weekly" | "always" | "monthly" | "yearly" | "never" | undefined;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Route can be a simple string path or an object with details
|
|
19
|
+
*/
|
|
20
|
+
export type SitemapRoute = string | SitemapRouteObject;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} SitemapRouteObject
|
|
3
|
+
* @property {string} path - Route path (e.g., '/about')
|
|
4
|
+
* @property {number} [priority] - Priority (0.0 to 1.0)
|
|
5
|
+
* @property {'always'|'hourly'|'daily'|'weekly'|'monthly'|'yearly'|'never'}
|
|
6
|
+
* [changefreq] - Change frequency
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {string | SitemapRouteObject} SitemapRoute
|
|
11
|
+
* Route can be a simple string path or an object with details
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export default {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { generateSitemap } from "./sitemap/index.js";
|