@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.
- package/dist/core/build.d.ts.map +1 -1
- package/dist/core/build.js +52 -4
- package/dist/core/invalidate.d.ts.map +1 -1
- package/dist/core/invalidate.js +3 -110
- package/dist/core/utils/glob-patterns.d.ts +40 -0
- package/dist/core/utils/glob-patterns.d.ts.map +1 -0
- package/dist/core/utils/glob-patterns.js +127 -0
- package/dist/core/utils/index.d.ts +1 -0
- package/dist/core/utils/index.d.ts.map +1 -1
- package/dist/core/utils/index.js +2 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/rss/generator.d.ts +26 -0
- package/dist/rss/generator.d.ts.map +1 -0
- package/dist/rss/generator.js +331 -0
- package/dist/rss/index.d.ts +8 -0
- package/dist/rss/index.d.ts.map +1 -0
- package/dist/rss/index.js +6 -0
- package/dist/rss/utils/index.d.ts +6 -0
- package/dist/rss/utils/index.d.ts.map +1 -0
- package/dist/rss/utils/index.js +5 -0
- package/dist/rss/utils/pattern-matching.d.ts +33 -0
- package/dist/rss/utils/pattern-matching.d.ts.map +1 -0
- package/dist/rss/utils/pattern-matching.js +87 -0
- package/dist/rss/validation.d.ts +30 -0
- package/dist/rss/validation.d.ts.map +1 -0
- package/dist/rss/validation.js +124 -0
- package/dist/seo/sitemap.d.ts.map +1 -1
- package/dist/seo/sitemap.js +3 -52
- package/dist/types/config.d.ts +2 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/rss.d.ts +247 -0
- package/dist/types/rss.d.ts.map +1 -0
- package/dist/types/rss.js +4 -0
- package/package.json +1 -1
package/dist/core/build.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/core/build.ts"],"names":[],"mappings":"
|
|
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"}
|
package/dist/core/build.js
CHANGED
|
@@ -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
|
-
//
|
|
417
|
-
|
|
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;
|
|
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"}
|
package/dist/core/invalidate.js
CHANGED
|
@@ -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
|
-
//
|
|
81
|
-
return
|
|
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"}
|
package/dist/core/utils/index.js
CHANGED
|
@@ -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';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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"}
|