@stati/core 1.10.2 → 1.11.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 (38) hide show
  1. package/dist/core/build.d.ts.map +1 -1
  2. package/dist/core/build.js +52 -4
  3. package/dist/core/invalidate.d.ts.map +1 -1
  4. package/dist/core/invalidate.js +3 -110
  5. package/dist/core/utils/glob-patterns.d.ts +40 -0
  6. package/dist/core/utils/glob-patterns.d.ts.map +1 -0
  7. package/dist/core/utils/glob-patterns.js +127 -0
  8. package/dist/core/utils/index.d.ts +1 -0
  9. package/dist/core/utils/index.d.ts.map +1 -1
  10. package/dist/core/utils/index.js +2 -0
  11. package/dist/index.d.ts +3 -0
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +1 -0
  14. package/dist/rss/generator.d.ts +26 -0
  15. package/dist/rss/generator.d.ts.map +1 -0
  16. package/dist/rss/generator.js +331 -0
  17. package/dist/rss/index.d.ts +8 -0
  18. package/dist/rss/index.d.ts.map +1 -0
  19. package/dist/rss/index.js +6 -0
  20. package/dist/rss/utils/index.d.ts +6 -0
  21. package/dist/rss/utils/index.d.ts.map +1 -0
  22. package/dist/rss/utils/index.js +5 -0
  23. package/dist/rss/utils/pattern-matching.d.ts +33 -0
  24. package/dist/rss/utils/pattern-matching.d.ts.map +1 -0
  25. package/dist/rss/utils/pattern-matching.js +87 -0
  26. package/dist/rss/validation.d.ts +30 -0
  27. package/dist/rss/validation.d.ts.map +1 -0
  28. package/dist/rss/validation.js +124 -0
  29. package/dist/seo/sitemap.d.ts.map +1 -1
  30. package/dist/seo/sitemap.js +3 -52
  31. package/dist/types/config.d.ts +2 -0
  32. package/dist/types/config.d.ts.map +1 -1
  33. package/dist/types/index.d.ts +1 -0
  34. package/dist/types/index.d.ts.map +1 -1
  35. package/dist/types/rss.d.ts +247 -0
  36. package/dist/types/rss.d.ts.map +1 -0
  37. package/dist/types/rss.js +4 -0
  38. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/core/build.ts"],"names":[],"mappings":"AAyCA,OAAO,KAAK,EAEV,UAAU,EACV,MAAM,EAKP,MAAM,mBAAmB,CAAC;AAE3B;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,YAAY;IAC3B,iDAAiD;IACjD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,iDAAiD;IACjD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AA4FD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAsB,KAAK,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,CAW3E"}
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/core/build.ts"],"names":[],"mappings":"AA2CA,OAAO,KAAK,EAEV,UAAU,EACV,MAAM,EAKP,MAAM,mBAAmB,CAAC;AAE3B;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,YAAY;IAC3B,iDAAiD;IACjD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,iDAAiD;IACjD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AA4FD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAsB,KAAK,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,CAW3E"}
@@ -8,6 +8,8 @@ import { createTemplateEngine, renderPage } from './templates.js';
8
8
  import { buildNavigation } from './navigation.js';
9
9
  import { loadCacheManifest, saveCacheManifest, shouldRebuildPage, createCacheEntry, updateCacheEntry, withBuildLock, computeNavigationHash, } from './isg/index.js';
10
10
  import { generateSitemap, generateRobotsTxtFromConfig, autoInjectSEO, } from '../seo/index.js';
11
+ import { generateRSSFeeds, validateRSSConfig } from '../rss/index.js';
12
+ import { getEnv } from '../env.js';
11
13
  /**
12
14
  * Recursively calculates the total size of a directory in bytes.
13
15
  * Used for build statistics.
@@ -413,8 +415,10 @@ async function buildInternal(options = {}) {
413
415
  // Copy static assets and count them
414
416
  let assetsCount = 0;
415
417
  assetsCount = await copyStaticAssets(config, outDir, logger);
416
- // Generate sitemap if enabled
417
- if (config.sitemap?.enabled) {
418
+ // Get current environment
419
+ const currentEnv = getEnv();
420
+ // Generate sitemap if enabled (only in production mode)
421
+ if (config.sitemap?.enabled && currentEnv === 'production') {
418
422
  console.log(); // Add spacing before sitemap generation
419
423
  logger.info('Generating sitemap...');
420
424
  const sitemapResult = generateSitemap(pages, config, config.sitemap);
@@ -430,14 +434,58 @@ async function buildInternal(options = {}) {
430
434
  logger.success(`Generated sitemap with ${sitemapResult.entryCount} entries`);
431
435
  }
432
436
  }
433
- // Generate robots.txt if enabled
434
- if (config.robots?.enabled) {
437
+ // Generate robots.txt if enabled (only in production mode)
438
+ if (config.robots?.enabled && currentEnv === 'production') {
435
439
  console.log(); // Add spacing before robots.txt generation
436
440
  logger.info('Generating robots.txt...');
437
441
  const robotsContent = generateRobotsTxtFromConfig(config.robots, config.site.baseUrl);
438
442
  await writeFile(join(outDir, 'robots.txt'), robotsContent);
439
443
  logger.success('Generated robots.txt');
440
444
  }
445
+ // Generate RSS feeds if enabled (only in production mode)
446
+ if (config.rss?.enabled && currentEnv === 'production') {
447
+ console.log(); // Add spacing before RSS generation
448
+ logger.info('Generating RSS feeds...');
449
+ try {
450
+ // Validate RSS configuration before generating
451
+ const validationResult = validateRSSConfig(config.rss);
452
+ if (!validationResult.valid) {
453
+ logger.error('RSS configuration validation failed:');
454
+ validationResult.errors.forEach((error) => logger.error(` - ${error}`));
455
+ if (validationResult.warnings.length > 0) {
456
+ validationResult.warnings.forEach((warning) => logger.warning(` - ${warning}`));
457
+ }
458
+ }
459
+ else {
460
+ // Log warnings if any
461
+ if (validationResult.warnings.length > 0) {
462
+ validationResult.warnings.forEach((warning) => logger.warning(warning));
463
+ }
464
+ // Validate pages array before generating feeds
465
+ if (pages.length === 0) {
466
+ logger.warning('No pages found to include in RSS feeds');
467
+ }
468
+ else {
469
+ const rssResults = generateRSSFeeds(pages, config, logger);
470
+ if (rssResults && rssResults.length > 0) {
471
+ for (const result of rssResults) {
472
+ await writeFile(join(outDir, result.filename), result.xml);
473
+ logger.success(`Generated ${result.filename} with ${result.itemCount} items (${(result.sizeInBytes / 1024).toFixed(2)} KB)`);
474
+ }
475
+ logger.success(`Generated ${rssResults.length} RSS feed${rssResults.length > 1 ? 's' : ''}`);
476
+ }
477
+ else {
478
+ logger.info('No RSS feeds generated (no matching pages found)');
479
+ }
480
+ }
481
+ }
482
+ }
483
+ catch (error) {
484
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred during RSS generation';
485
+ logger.error(`Failed to generate RSS feeds: ${errorMessage}`);
486
+ // Don't throw - allow build to continue even if RSS generation fails
487
+ }
488
+ }
441
489
  // Run afterAll hook
442
490
  if (config.hooks?.afterAll) {
443
491
  await config.hooks.afterAll({ config, pages });
@@ -1 +1 @@
1
- {"version":3,"file":"invalidate.d.ts","sourceRoot":"","sources":["../../src/core/invalidate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAGpD;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,0CAA0C;IAC1C,gBAAgB,EAAE,MAAM,CAAC;IACzB,iCAAiC;IACjC,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,2CAA2C;IAC3C,UAAU,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAkC9D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAkC9F;AAuLD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAsB,UAAU,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAoD5E"}
1
+ {"version":3,"file":"invalidate.d.ts","sourceRoot":"","sources":["../../src/core/invalidate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAGpD;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,0CAA0C;IAC1C,gBAAgB,EAAE,MAAM,CAAC;IACzB,iCAAiC;IACjC,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,2CAA2C;IAC3C,UAAU,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAkC9D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAkC9F;AAuED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAsB,UAAU,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAoD5E"}
@@ -1,5 +1,5 @@
1
1
  import { loadCacheManifest, saveCacheManifest } from './isg/index.js';
2
- import { resolveCacheDir } from './utils/index.js';
2
+ import { resolveCacheDir, matchesGlob as matchesGlobPattern } from './utils/index.js';
3
3
  /**
4
4
  * Parses an invalidation query string into individual query terms.
5
5
  * Supports space-separated values and quoted strings.
@@ -77,8 +77,8 @@ export function matchesInvalidationTerm(entry, path, term) {
77
77
  // Support both exact path match and prefix match
78
78
  return path === value || path.startsWith(value);
79
79
  case 'glob':
80
- // Simple glob pattern matching for paths
81
- return matchesGlob(path, value);
80
+ // Glob pattern matching for paths
81
+ return matchesGlobPattern(path, value);
82
82
  case 'age':
83
83
  // Time-based invalidation: age:3months, age:1week, age:30days
84
84
  return matchesAge(entry, value);
@@ -92,113 +92,6 @@ export function matchesInvalidationTerm(entry, path, term) {
92
92
  return entry.tags.some((tag) => tag.includes(term)) || path.includes(term);
93
93
  }
94
94
  }
95
- /**
96
- * Simple glob pattern matching for paths.
97
- * Supports * and ** wildcards.
98
- *
99
- * @param path - Path to test
100
- * @param pattern - Glob pattern
101
- * @returns True if path matches pattern
102
- */
103
- function matchesGlob(path, pattern) {
104
- try {
105
- // Convert glob pattern to regex by processing character by character
106
- // This avoids the magic string replacement issue
107
- const regex = globToRegex(pattern);
108
- return regex.test(path);
109
- }
110
- catch {
111
- console.warn(`Invalid glob pattern: ${pattern}`);
112
- return false;
113
- }
114
- }
115
- /**
116
- * Converts a glob pattern to a regular expression.
117
- * Processes the pattern character by character to avoid placeholder conflicts.
118
- *
119
- * @param pattern - Glob pattern to convert
120
- * @returns Regular expression that matches the glob pattern
121
- */
122
- function globToRegex(pattern) {
123
- let regexStr = '^';
124
- let i = 0;
125
- while (i < pattern.length) {
126
- const char = pattern[i];
127
- switch (char) {
128
- case '*':
129
- if (i + 1 < pattern.length && pattern[i + 1] === '*') {
130
- // Handle ** (matches any path including subdirectories)
131
- if (i + 2 < pattern.length && pattern[i + 2] === '/') {
132
- // **/ pattern - matches zero or more directories
133
- regexStr += '(?:.*/)?';
134
- i += 3;
135
- }
136
- else if (i + 2 === pattern.length) {
137
- // ** at end - matches everything
138
- regexStr += '.*';
139
- i += 2;
140
- }
141
- else {
142
- // ** not followed by / or end - treat as single *
143
- regexStr += '[^/]*';
144
- i += 1;
145
- }
146
- }
147
- else {
148
- // Single * matches any characters except path separator
149
- regexStr += '[^/]*';
150
- i += 1;
151
- }
152
- break;
153
- case '?':
154
- // ? matches any single character except path separator
155
- regexStr += '[^/]';
156
- i += 1;
157
- break;
158
- case '[': {
159
- // Handle character classes - find the closing bracket
160
- let closeIndex = i + 1;
161
- while (closeIndex < pattern.length && pattern[closeIndex] !== ']') {
162
- closeIndex++;
163
- }
164
- if (closeIndex >= pattern.length) {
165
- // No closing bracket found - this creates an invalid regex
166
- // Just add the character and let the regex constructor throw an error
167
- regexStr += char;
168
- i += 1;
169
- }
170
- else {
171
- // Valid character class - copy it as-is
172
- regexStr += pattern.slice(i, closeIndex + 1);
173
- i = closeIndex + 1;
174
- }
175
- break;
176
- }
177
- case '.':
178
- case '+':
179
- case '^':
180
- case '$':
181
- case '(':
182
- case ')':
183
- case ']':
184
- case '{':
185
- case '}':
186
- case '|':
187
- case '\\':
188
- // Escape regex special characters
189
- regexStr += '\\' + char;
190
- i += 1;
191
- break;
192
- default:
193
- // Regular character
194
- regexStr += char;
195
- i += 1;
196
- break;
197
- }
198
- }
199
- regexStr += '$';
200
- return new RegExp(regexStr);
201
- }
202
95
  /**
203
96
  * Checks if a cache entry matches an age-based invalidation term.
204
97
  * Supports various time units: days, weeks, months, years.
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Glob pattern matching utilities for Stati
3
+ * Provides shared glob-to-regex conversion functionality
4
+ * @module core/utils/glob-patterns
5
+ */
6
+ /**
7
+ * Converts a glob pattern to a regular expression.
8
+ * Combines features from both pattern-matching and invalidate implementations.
9
+ *
10
+ * Supports:
11
+ * - `**` - matches zero or more path segments
12
+ * - `*` - matches any characters except path separator
13
+ * - `?` - matches any single character except path separator
14
+ * - `[...]` - character classes
15
+ *
16
+ * @param pattern - Glob pattern to convert (backslashes are normalized to forward slashes)
17
+ * @returns Regular expression that matches the glob pattern
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * const regex = globToRegex('site/**\/*.md');
22
+ * regex.test('site/blog/post.md'); // true
23
+ * regex.test('site/blog/2024/post.md'); // true
24
+ * ```
25
+ */
26
+ export declare function globToRegex(pattern: string): RegExp;
27
+ /**
28
+ * Tests if a path matches a glob pattern
29
+ * @param path - Path to test
30
+ * @param pattern - Glob pattern
31
+ * @returns True if path matches pattern
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * matchesGlob('/blog/post', '/blog/**'); // true
36
+ * matchesGlob('/blog/post', '/news/**'); // false
37
+ * ```
38
+ */
39
+ export declare function matchesGlob(path: string, pattern: string): boolean;
40
+ //# sourceMappingURL=glob-patterns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glob-patterns.d.ts","sourceRoot":"","sources":["../../../src/core/utils/glob-patterns.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAoFnD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAQlE"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Glob pattern matching utilities for Stati
3
+ * Provides shared glob-to-regex conversion functionality
4
+ * @module core/utils/glob-patterns
5
+ */
6
+ /**
7
+ * Converts a glob pattern to a regular expression.
8
+ * Combines features from both pattern-matching and invalidate implementations.
9
+ *
10
+ * Supports:
11
+ * - `**` - matches zero or more path segments
12
+ * - `*` - matches any characters except path separator
13
+ * - `?` - matches any single character except path separator
14
+ * - `[...]` - character classes
15
+ *
16
+ * @param pattern - Glob pattern to convert (backslashes are normalized to forward slashes)
17
+ * @returns Regular expression that matches the glob pattern
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * const regex = globToRegex('site/**\/*.md');
22
+ * regex.test('site/blog/post.md'); // true
23
+ * regex.test('site/blog/2024/post.md'); // true
24
+ * ```
25
+ */
26
+ export function globToRegex(pattern) {
27
+ // Normalize pattern to use forward slashes for cross-platform compatibility
28
+ const normalizedPattern = pattern.replace(/\\/g, '/');
29
+ let regexStr = '^';
30
+ let i = 0;
31
+ while (i < normalizedPattern.length) {
32
+ const char = normalizedPattern[i];
33
+ switch (char) {
34
+ case '*':
35
+ if (i + 1 < normalizedPattern.length && normalizedPattern[i + 1] === '*') {
36
+ // Handle ** (matches zero or more path segments)
37
+ if (i + 2 < normalizedPattern.length && normalizedPattern[i + 2] === '/') {
38
+ // **/ pattern - matches zero or more directories
39
+ regexStr += '(?:.*/)?';
40
+ i += 3;
41
+ }
42
+ else if (i + 2 === normalizedPattern.length) {
43
+ // ** at end - matches everything
44
+ regexStr += '.*';
45
+ i += 2;
46
+ }
47
+ else {
48
+ // ** in middle not followed by / - match everything remaining
49
+ regexStr += '.*';
50
+ i += 2;
51
+ }
52
+ }
53
+ else {
54
+ // Single * matches any characters except path separator
55
+ regexStr += '[^/]*';
56
+ i += 1;
57
+ }
58
+ break;
59
+ case '?':
60
+ // ? matches any single character except path separator
61
+ regexStr += '[^/]';
62
+ i += 1;
63
+ break;
64
+ case '[': {
65
+ // Handle character classes - find the closing bracket
66
+ let closeIndex = i + 1;
67
+ while (closeIndex < normalizedPattern.length && normalizedPattern[closeIndex] !== ']') {
68
+ closeIndex++;
69
+ }
70
+ if (closeIndex >= normalizedPattern.length) {
71
+ // No closing bracket found - invalid pattern
72
+ throw new Error(`Unclosed character class at position ${i}`);
73
+ }
74
+ else {
75
+ // Valid character class - copy it as-is
76
+ regexStr += normalizedPattern.slice(i, closeIndex + 1);
77
+ i = closeIndex + 1;
78
+ }
79
+ break;
80
+ }
81
+ case '.':
82
+ case '+':
83
+ case '^':
84
+ case '$':
85
+ case '(':
86
+ case ')':
87
+ case ']':
88
+ case '{':
89
+ case '}':
90
+ case '|':
91
+ case '\\':
92
+ // Escape regex special characters
93
+ regexStr += '\\' + char;
94
+ i += 1;
95
+ break;
96
+ default:
97
+ // Regular character
98
+ regexStr += char;
99
+ i += 1;
100
+ break;
101
+ }
102
+ }
103
+ regexStr += '$';
104
+ return new RegExp(regexStr);
105
+ }
106
+ /**
107
+ * Tests if a path matches a glob pattern
108
+ * @param path - Path to test
109
+ * @param pattern - Glob pattern
110
+ * @returns True if path matches pattern
111
+ *
112
+ * @example
113
+ * ```typescript
114
+ * matchesGlob('/blog/post', '/blog/**'); // true
115
+ * matchesGlob('/blog/post', '/news/**'); // false
116
+ * ```
117
+ */
118
+ export function matchesGlob(path, pattern) {
119
+ try {
120
+ const regex = globToRegex(pattern);
121
+ return regex.test(path);
122
+ }
123
+ catch {
124
+ console.warn(`Invalid glob pattern: ${pattern}`);
125
+ return false;
126
+ }
127
+ }
@@ -17,4 +17,5 @@ export type { PrettyUrlResult } from './server.js';
17
17
  export { createErrorOverlay, parseErrorDetails } from './error-overlay.js';
18
18
  export type { ErrorDetails } from './error-overlay.js';
19
19
  export { getStatiVersion } from './version.js';
20
+ export { globToRegex, matchesGlob } from './glob-patterns.js';
20
21
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/utils/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACL,QAAQ,EACR,SAAS,EACT,UAAU,EACV,SAAS,EACT,MAAM,EACN,QAAQ,EACR,OAAO,EACP,IAAI,GACL,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,qBAAqB,EACrB,cAAc,EACd,cAAc,EACd,iBAAiB,GAClB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGhD,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,wBAAwB,EACxB,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,2BAA2B,EAC3B,cAAc,EACd,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAE,6BAA6B,EAAE,MAAM,yBAAyB,CAAC;AAGxE,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AACrF,YAAY,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAG9D,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAGzF,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAGlE,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGnD,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC3E,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGvD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/utils/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACL,QAAQ,EACR,SAAS,EACT,UAAU,EACV,SAAS,EACT,MAAM,EACN,QAAQ,EACR,OAAO,EACP,IAAI,GACL,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,qBAAqB,EACrB,cAAc,EACd,cAAc,EACd,iBAAiB,GAClB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGhD,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,wBAAwB,EACxB,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,2BAA2B,EAC3B,cAAc,EACd,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAE,6BAA6B,EAAE,MAAM,yBAAyB,CAAC;AAGxE,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AACrF,YAAY,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAG9D,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAGzF,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAGlE,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGnD,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC3E,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGvD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAG/C,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC"}
@@ -26,3 +26,5 @@ export { resolvePrettyUrl } from './server.js';
26
26
  export { createErrorOverlay, parseErrorDetails } from './error-overlay.js';
27
27
  // Version utilities
28
28
  export { getStatiVersion } from './version.js';
29
+ // Glob pattern utilities
30
+ export { globToRegex, matchesGlob } from './glob-patterns.js';
package/dist/index.d.ts CHANGED
@@ -22,10 +22,13 @@
22
22
  export type { StatiConfig, PageModel, FrontMatter, BuildContext, PageContext, BuildHooks, NavNode, ISGConfig, AgingRule, BuildStats, } from './types/index.js';
23
23
  export type { SEOMetadata, SEOConfig, SEOContext, SEOValidationResult, SEOTagType, RobotsConfig, OpenGraphConfig, OpenGraphImage, OpenGraphArticle, TwitterCardConfig, AuthorConfig, } from './types/index.js';
24
24
  export type { SitemapConfig, SitemapEntry, SitemapGenerationResult, ChangeFrequency, } from './types/index.js';
25
+ export type { RSSConfig, RSSFeedConfig, RSSGenerationResult } from './types/index.js';
25
26
  export type { BuildOptions, DevServerOptions, PreviewServerOptions, InvalidationResult, } from './core/index.js';
26
27
  export { build, createDevServer, createPreviewServer, invalidate } from './core/index.js';
27
28
  export type { AutoInjectOptions } from './seo/index.js';
28
29
  export { generateSEOMetadata, generateSEO, generateOpenGraphTags, generateTwitterCardTags, generateSitemap, generateSitemapEntry, generateSitemapXml, generateSitemapIndexXml, generateRobotsTxt, generateRobotsTxtFromConfig, escapeHtml, generateRobotsContent, validateSEOMetadata, detectExistingSEOTags, normalizeUrlPath, resolveAbsoluteUrl, isValidUrl, autoInjectSEO, shouldAutoInject, } from './seo/index.js';
30
+ export type { RSSValidationResult } from './rss/index.js';
31
+ export { generateRSSFeed, generateRSSFeeds, validateRSSConfig, validateRSSFeedConfig, } from './rss/index.js';
29
32
  export { loadConfig } from './config/loader.js';
30
33
  export { setEnv, getEnv } from './env.js';
31
34
  import type { StatiConfig } from './types/index.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,YAAY,EACV,WAAW,EACX,SAAS,EACT,WAAW,EACX,YAAY,EACZ,WAAW,EACX,UAAU,EACV,OAAO,EACP,SAAS,EACT,SAAS,EACT,UAAU,GACX,MAAM,kBAAkB,CAAC;AAG1B,YAAY,EACV,WAAW,EACX,SAAS,EACT,UAAU,EACV,mBAAmB,EACnB,UAAU,EACV,YAAY,EACZ,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,GACb,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EACV,aAAa,EACb,YAAY,EACZ,uBAAuB,EACvB,eAAe,GAChB,MAAM,kBAAkB,CAAC;AAG1B,YAAY,EACV,YAAY,EACZ,gBAAgB,EAChB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,KAAK,EAAE,eAAe,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG1F,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EACL,mBAAmB,EACnB,WAAW,EACX,qBAAqB,EACrB,uBAAuB,EACvB,eAAe,EACf,oBAAoB,EACpB,kBAAkB,EAClB,uBAAuB,EACvB,iBAAiB,EACjB,2BAA2B,EAC3B,UAAU,EACV,qBAAqB,EACrB,mBAAmB,EACnB,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,EAClB,UAAU,EACV,aAAa,EACb,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAG1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAE7D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,YAAY,EACV,WAAW,EACX,SAAS,EACT,WAAW,EACX,YAAY,EACZ,WAAW,EACX,UAAU,EACV,OAAO,EACP,SAAS,EACT,SAAS,EACT,UAAU,GACX,MAAM,kBAAkB,CAAC;AAG1B,YAAY,EACV,WAAW,EACX,SAAS,EACT,UAAU,EACV,mBAAmB,EACnB,UAAU,EACV,YAAY,EACZ,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,GACb,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EACV,aAAa,EACb,YAAY,EACZ,uBAAuB,EACvB,eAAe,GAChB,MAAM,kBAAkB,CAAC;AAG1B,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAGtF,YAAY,EACV,YAAY,EACZ,gBAAgB,EAChB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,KAAK,EAAE,eAAe,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG1F,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EACL,mBAAmB,EACnB,WAAW,EACX,qBAAqB,EACrB,uBAAuB,EACvB,eAAe,EACf,oBAAoB,EACpB,kBAAkB,EAClB,uBAAuB,EACvB,iBAAiB,EACjB,2BAA2B,EAC3B,UAAU,EACV,qBAAqB,EACrB,mBAAmB,EACnB,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,EAClB,UAAU,EACV,aAAa,EACb,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AAGxB,YAAY,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAG1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAE7D"}
package/dist/index.js CHANGED
@@ -21,6 +21,7 @@
21
21
  */
22
22
  export { build, createDevServer, createPreviewServer, invalidate } from './core/index.js';
23
23
  export { generateSEOMetadata, generateSEO, generateOpenGraphTags, generateTwitterCardTags, generateSitemap, generateSitemapEntry, generateSitemapXml, generateSitemapIndexXml, generateRobotsTxt, generateRobotsTxtFromConfig, escapeHtml, generateRobotsContent, validateSEOMetadata, detectExistingSEOTags, normalizeUrlPath, resolveAbsoluteUrl, isValidUrl, autoInjectSEO, shouldAutoInject, } from './seo/index.js';
24
+ export { generateRSSFeed, generateRSSFeeds, validateRSSConfig, validateRSSFeedConfig, } from './rss/index.js';
24
25
  // Re-export config and env utilities
25
26
  export { loadConfig } from './config/loader.js';
26
27
  export { setEnv, getEnv } from './env.js';
@@ -0,0 +1,26 @@
1
+ /**
2
+ * RSS feed generation utilities for Stati
3
+ * @module rss/generator
4
+ */
5
+ import type { PageModel } from '../types/content.js';
6
+ import type { StatiConfig } from '../types/config.js';
7
+ import type { RSSFeedConfig, RSSGenerationResult } from '../types/rss.js';
8
+ import type { Logger } from '../types/logging.js';
9
+ /**
10
+ * Generates RSS XML for a single feed
11
+ * @param pages - All pages in the site
12
+ * @param config - Stati configuration
13
+ * @param feedConfig - Feed configuration
14
+ * @param logger - Logger instance for warnings (optional)
15
+ * @returns RSS generation result
16
+ */
17
+ export declare function generateRSSFeed(pages: PageModel[], config: StatiConfig, feedConfig: RSSFeedConfig, logger?: Logger): RSSGenerationResult;
18
+ /**
19
+ * Generates all RSS feeds configured in the site
20
+ * @param pages - All pages in the site
21
+ * @param config - Stati configuration
22
+ * @param logger - Logger instance for warnings (optional)
23
+ * @returns Array of RSS generation results
24
+ */
25
+ export declare function generateRSSFeeds(pages: PageModel[], config: StatiConfig, logger?: Logger): RSSGenerationResult[];
26
+ //# sourceMappingURL=generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/rss/generator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC1E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AA2KlD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,SAAS,EAAE,EAClB,MAAM,EAAE,WAAW,EACnB,UAAU,EAAE,aAAa,EACzB,MAAM,CAAC,EAAE,MAAM,GACd,mBAAmB,CA4LrB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,SAAS,EAAE,EAClB,MAAM,EAAE,WAAW,EACnB,MAAM,CAAC,EAAE,MAAM,GACd,mBAAmB,EAAE,CAcvB"}