@stati/core 1.19.0 → 1.20.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 +51 -7
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +2 -0
- package/dist/core/markdown.d.ts +9 -0
- package/dist/core/markdown.d.ts.map +1 -1
- package/dist/core/markdown.js +12 -0
- package/dist/core/utils/html.utils.d.ts +35 -0
- package/dist/core/utils/html.utils.d.ts.map +1 -0
- package/dist/core/utils/html.utils.js +46 -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/metrics/recorder.d.ts.map +1 -1
- package/dist/metrics/types.d.ts +1 -0
- package/dist/metrics/types.d.ts.map +1 -1
- package/dist/search/auto-inject.d.ts +21 -0
- package/dist/search/auto-inject.d.ts.map +1 -0
- package/dist/search/auto-inject.js +33 -0
- package/dist/search/constants.d.ts +28 -0
- package/dist/search/constants.d.ts.map +1 -0
- package/dist/search/constants.js +27 -0
- package/dist/search/generator.d.ts +101 -0
- package/dist/search/generator.d.ts.map +1 -0
- package/dist/search/generator.js +278 -0
- package/dist/search/index.d.ts +24 -0
- package/dist/search/index.d.ts.map +1 -0
- package/dist/search/index.js +22 -0
- package/dist/seo/auto-inject.d.ts.map +1 -1
- package/dist/seo/auto-inject.js +5 -18
- package/dist/types/config.d.ts +3 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/content.d.ts +6 -0
- package/dist/types/content.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/search.d.ts +121 -0
- package/dist/types/search.d.ts.map +1 -0
- package/dist/types/search.js +5 -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":"AAsDA,OAAO,KAAK,EAEV,UAAU,EACV,MAAM,EAQP,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxE;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,gCAAgC;IAChC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,sCAAsC;IACtC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,YAAY;IAC3B,iDAAiD;IACjD,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC5B,iDAAiD;IACjD,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC5B,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,uCAAuC;IACvC,aAAa,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACpC,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,8BAA8B;IAC9B,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,+BAA+B;IAC/B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,iCAAiC;IACjC,OAAO,CAAC,EAAE,cAAc,GAAG,SAAS,CAAC;CACtC;AAED;;GAEG;AACH,MAAM,WAAW,WAAY,SAAQ,UAAU;IAC7C,wDAAwD;IACxD,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AA4FD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAsB,KAAK,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,WAAW,CAAC,CAW5E"}
|
package/dist/core/build.js
CHANGED
|
@@ -3,12 +3,13 @@ import { join, dirname, relative, posix } from 'node:path';
|
|
|
3
3
|
import { performance } from 'node:perf_hooks';
|
|
4
4
|
import { loadConfig } from '../config/loader.js';
|
|
5
5
|
import { loadContent } from './content.js';
|
|
6
|
-
import { createMarkdownProcessor, renderMarkdown } from './markdown.js';
|
|
6
|
+
import { createMarkdownProcessor, renderMarkdown, extractToc } from './markdown.js';
|
|
7
7
|
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
11
|
import { generateRSSFeeds, validateRSSConfig } from '../rss/index.js';
|
|
12
|
+
import { computeSearchIndexFilename, generateSearchIndex, writeSearchIndex, autoInjectSearchMeta, } from '../search/index.js';
|
|
12
13
|
import { getEnv } from '../env.js';
|
|
13
14
|
import { DEFAULT_OUT_DIR } from '../constants.js';
|
|
14
15
|
import { createMetricRecorder, noopMetricRecorder } from '../metrics/index.js';
|
|
@@ -190,9 +191,10 @@ async function loadContentAndBuildNavigation(config, options, logger) {
|
|
|
190
191
|
/**
|
|
191
192
|
* Processes pages with ISG caching logic.
|
|
192
193
|
*/
|
|
193
|
-
async function processPagesWithCache(pages, manifest, config, outDir, md, eta, navigation, buildTime, options, logger, compiledBundles, recorder = noopMetricRecorder) {
|
|
194
|
+
async function processPagesWithCache(pages, manifest, config, outDir, md, eta, navigation, buildTime, options, logger, compiledBundles, recorder = noopMetricRecorder, searchIndexFilename) {
|
|
194
195
|
let cacheHits = 0;
|
|
195
196
|
let cacheMisses = 0;
|
|
197
|
+
const searchablePages = [];
|
|
196
198
|
// Build context
|
|
197
199
|
const buildContext = { config, pages };
|
|
198
200
|
// Run beforeAll hook
|
|
@@ -249,6 +251,13 @@ async function processPagesWithCache(pages, manifest, config, outDir, md, eta, n
|
|
|
249
251
|
}
|
|
250
252
|
// Record page timing for cached pages (0ms render time)
|
|
251
253
|
recorder.recordPageTiming(page.url, 0, true);
|
|
254
|
+
// Collect searchable page data for cached pages if search is enabled
|
|
255
|
+
// Extract TOC without full HTML rendering for efficiency
|
|
256
|
+
if (config.search?.enabled === true) {
|
|
257
|
+
const tocEnabled = config.markdown?.toc !== false;
|
|
258
|
+
const toc = tocEnabled ? extractToc(page.content, md) : [];
|
|
259
|
+
searchablePages.push({ page, toc, markdownContent: page.content });
|
|
260
|
+
}
|
|
252
261
|
continue;
|
|
253
262
|
}
|
|
254
263
|
// Cache miss - need to rebuild
|
|
@@ -265,7 +274,14 @@ async function processPagesWithCache(pages, manifest, config, outDir, md, eta, n
|
|
|
265
274
|
const { html: htmlContent, toc } = renderMarkdown(page.content, md, tocEnabled);
|
|
266
275
|
// Compute matched bundle paths for this page
|
|
267
276
|
const bundlePaths = getBundlePathsForPage(page.url, compiledBundles);
|
|
268
|
-
|
|
277
|
+
// Build assets object with bundle paths and search index path
|
|
278
|
+
const assets = {
|
|
279
|
+
bundlePaths,
|
|
280
|
+
...(config.search?.enabled === true &&
|
|
281
|
+
searchIndexFilename && {
|
|
282
|
+
searchIndexPath: `/${searchIndexFilename}`,
|
|
283
|
+
}),
|
|
284
|
+
};
|
|
269
285
|
// Render with template
|
|
270
286
|
const renderResult = await renderPage(page, htmlContent, config, eta, navigation, pages, assets, toc, logger);
|
|
271
287
|
let finalHtml = renderResult.html;
|
|
@@ -284,9 +300,14 @@ async function processPagesWithCache(pages, manifest, config, outDir, md, eta, n
|
|
|
284
300
|
}
|
|
285
301
|
finalHtml = autoInjectSEO(finalHtml, injectOptions);
|
|
286
302
|
}
|
|
303
|
+
// Auto-inject search index meta tag if enabled (default: true)
|
|
304
|
+
if (config.search?.enabled === true &&
|
|
305
|
+
searchIndexFilename &&
|
|
306
|
+
config.search?.autoInjectMetaTag !== false) {
|
|
307
|
+
finalHtml = autoInjectSearchMeta(finalHtml, `/${searchIndexFilename}`);
|
|
308
|
+
}
|
|
287
309
|
// Auto-inject TypeScript bundle script tags if available and autoInject is enabled (default: true)
|
|
288
|
-
|
|
289
|
-
if (shouldAutoInject && assets && assets.bundlePaths.length > 0) {
|
|
310
|
+
if (config.typescript?.autoInject !== false && assets.bundlePaths.length > 0) {
|
|
290
311
|
finalHtml = autoInjectBundles(finalHtml, assets.bundlePaths);
|
|
291
312
|
}
|
|
292
313
|
const renderTime = Math.round(performance.now() - startTime);
|
|
@@ -308,6 +329,11 @@ async function processPagesWithCache(pages, manifest, config, outDir, md, eta, n
|
|
|
308
329
|
else {
|
|
309
330
|
manifest.entries[cacheKey] = await createCacheEntry(page, config, buildTime);
|
|
310
331
|
}
|
|
332
|
+
// Collect searchable page data if search is enabled
|
|
333
|
+
// Uses TOC entries and markdown content instead of parsing rendered HTML
|
|
334
|
+
if (config.search?.enabled === true) {
|
|
335
|
+
searchablePages.push({ page, toc, markdownContent: page.content });
|
|
336
|
+
}
|
|
311
337
|
// Run afterRender hook
|
|
312
338
|
if (config.hooks?.afterRender) {
|
|
313
339
|
const hookStart = performance.now();
|
|
@@ -322,7 +348,7 @@ async function processPagesWithCache(pages, manifest, config, outDir, md, eta, n
|
|
|
322
348
|
logger.clearRenderingTree();
|
|
323
349
|
}
|
|
324
350
|
}
|
|
325
|
-
return { cacheHits, cacheMisses };
|
|
351
|
+
return { cacheHits, cacheMisses, searchablePages };
|
|
326
352
|
}
|
|
327
353
|
/**
|
|
328
354
|
* Copies static assets and returns the count.
|
|
@@ -453,16 +479,34 @@ async function buildInternal(options = {}) {
|
|
|
453
479
|
});
|
|
454
480
|
endTsSpan();
|
|
455
481
|
}
|
|
482
|
+
// Pre-compute search index filename if search is enabled
|
|
483
|
+
let searchIndexFilename;
|
|
484
|
+
if (config.search?.enabled) {
|
|
485
|
+
searchIndexFilename = computeSearchIndexFilename(config.search, buildStartTime.toString());
|
|
486
|
+
}
|
|
456
487
|
// Process pages with ISG caching logic
|
|
457
488
|
if (logger.step) {
|
|
458
489
|
console.log(); // Add spacing before page processing
|
|
459
490
|
}
|
|
460
491
|
const endPageRenderSpan = recorder.startSpan('pageRenderingMs');
|
|
461
492
|
const buildTime = new Date();
|
|
462
|
-
const pageProcessingResult = await processPagesWithCache(pages, manifest, config, outDir, md, eta, navigation, buildTime, options, logger, compiledBundles, recorder);
|
|
493
|
+
const pageProcessingResult = await processPagesWithCache(pages, manifest, config, outDir, md, eta, navigation, buildTime, options, logger, compiledBundles, recorder, searchIndexFilename);
|
|
463
494
|
endPageRenderSpan();
|
|
464
495
|
cacheHits = pageProcessingResult.cacheHits;
|
|
465
496
|
cacheMisses = pageProcessingResult.cacheMisses;
|
|
497
|
+
const searchablePages = pageProcessingResult.searchablePages;
|
|
498
|
+
// Generate search index if enabled
|
|
499
|
+
// Uses markdown content and TOC entries
|
|
500
|
+
let searchIndexMetadata;
|
|
501
|
+
if (config.search?.enabled && searchablePages.length > 0 && searchIndexFilename) {
|
|
502
|
+
logger.info('');
|
|
503
|
+
logger.info(`Generating search index to ${searchIndexFilename}`);
|
|
504
|
+
const endSearchIndexSpan = recorder.startSpan('searchIndexGenerationMs');
|
|
505
|
+
const searchIndex = generateSearchIndex(searchablePages, config.search);
|
|
506
|
+
searchIndexMetadata = await writeSearchIndex(searchIndex, outDir, searchIndexFilename);
|
|
507
|
+
endSearchIndexSpan();
|
|
508
|
+
logger.success(`Generated search index with ${searchIndexMetadata.documentCount} documents`);
|
|
509
|
+
}
|
|
466
510
|
// Record page rendering counts
|
|
467
511
|
recorder.increment('renderedPages', cacheMisses);
|
|
468
512
|
recorder.increment('cachedPages', cacheHits);
|
package/dist/core/index.d.ts
CHANGED
|
@@ -10,4 +10,5 @@ export { createPreviewServer } from './preview.js';
|
|
|
10
10
|
export type { PreviewServerOptions } from './preview.js';
|
|
11
11
|
export { invalidate } from './invalidate.js';
|
|
12
12
|
export type { InvalidationResult } from './invalidate.js';
|
|
13
|
+
export { injectBeforeHeadClose, findHeadClosePosition } from './utils/index.js';
|
|
13
14
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/core/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG5E,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,YAAY,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAGjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,YAAY,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAGzD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG5E,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,YAAY,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAGjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,YAAY,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAGzD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAG1D,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC"}
|
package/dist/core/index.js
CHANGED
|
@@ -10,3 +10,5 @@ export { createDevServer } from './dev.js';
|
|
|
10
10
|
export { createPreviewServer } from './preview.js';
|
|
11
11
|
// Cache invalidation
|
|
12
12
|
export { invalidate } from './invalidate.js';
|
|
13
|
+
// HTML utilities
|
|
14
|
+
export { injectBeforeHeadClose, findHeadClosePosition } from './utils/index.js';
|
package/dist/core/markdown.d.ts
CHANGED
|
@@ -14,6 +14,15 @@ export interface MarkdownResult {
|
|
|
14
14
|
* Supports both plugin array format and configure function format.
|
|
15
15
|
*/
|
|
16
16
|
export declare function createMarkdownProcessor(config: StatiConfig): Promise<MarkdownIt>;
|
|
17
|
+
/**
|
|
18
|
+
* Extracts table of contents entries from markdown content without rendering HTML.
|
|
19
|
+
* This is more efficient than renderMarkdown when only TOC data is needed.
|
|
20
|
+
*
|
|
21
|
+
* @param content - The markdown content to extract TOC from
|
|
22
|
+
* @param md - The configured MarkdownIt instance
|
|
23
|
+
* @returns Array of TOC entries extracted from headings (levels 2-6)
|
|
24
|
+
*/
|
|
25
|
+
export declare function extractToc(content: string, md: MarkdownIt): TocEntry[];
|
|
17
26
|
/**
|
|
18
27
|
* Renders markdown content to HTML with optional TOC extraction.
|
|
19
28
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/core/markdown.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,aAAa,CAAC;AAKrC,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAG/D;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,GAAG,EAAE,QAAQ,EAAE,CAAC;CACjB;AAmBD;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAuCtF;AA6ED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,EAAE,EAAE,UAAU,EACd,UAAU,GAAE,OAAc,GACzB,cAAc,CAWhB"}
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/core/markdown.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,aAAa,CAAC;AAKrC,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAG/D;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,GAAG,EAAE,QAAQ,EAAE,CAAC;CACjB;AAmBD;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAuCtF;AA6ED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,GAAG,QAAQ,EAAE,CAGtE;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,EAAE,EAAE,UAAU,EACd,UAAU,GAAE,OAAc,GACzB,cAAc,CAWhB"}
|
package/dist/core/markdown.js
CHANGED
|
@@ -127,6 +127,18 @@ function extractAndInjectAnchors(tokens, tocEnabled) {
|
|
|
127
127
|
}
|
|
128
128
|
return toc;
|
|
129
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* Extracts table of contents entries from markdown content without rendering HTML.
|
|
132
|
+
* This is more efficient than renderMarkdown when only TOC data is needed.
|
|
133
|
+
*
|
|
134
|
+
* @param content - The markdown content to extract TOC from
|
|
135
|
+
* @param md - The configured MarkdownIt instance
|
|
136
|
+
* @returns Array of TOC entries extracted from headings (levels 2-6)
|
|
137
|
+
*/
|
|
138
|
+
export function extractToc(content, md) {
|
|
139
|
+
const tokens = md.parse(content, {});
|
|
140
|
+
return extractAndInjectAnchors(tokens, true);
|
|
141
|
+
}
|
|
130
142
|
/**
|
|
131
143
|
* Renders markdown content to HTML with optional TOC extraction.
|
|
132
144
|
*
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML manipulation utilities.
|
|
3
|
+
* @module core/utils/html
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Finds the position to inject content before </head>.
|
|
7
|
+
* Performs a case-insensitive search for the closing head tag.
|
|
8
|
+
*
|
|
9
|
+
* @param html - HTML content
|
|
10
|
+
* @returns Position index or -1 if not found
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const html = '<html><head><title>Test</title></head><body></body></html>';
|
|
15
|
+
* const pos = findHeadClosePosition(html);
|
|
16
|
+
* // pos is the index just before </head>
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare function findHeadClosePosition(html: string): number;
|
|
20
|
+
/**
|
|
21
|
+
* Injects content before the </head> tag with proper indentation.
|
|
22
|
+
*
|
|
23
|
+
* @param html - HTML content
|
|
24
|
+
* @param content - Content to inject
|
|
25
|
+
* @param indent - Indentation string (default: 4 spaces)
|
|
26
|
+
* @returns HTML with injected content, or original HTML if </head> not found
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* const html = '<html><head><title>Test</title></head><body></body></html>';
|
|
31
|
+
* const result = injectBeforeHeadClose(html, '<meta name="test" content="value">');
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare function injectBeforeHeadClose(html: string, content: string, indent?: string): string;
|
|
35
|
+
//# sourceMappingURL=html.utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/html.utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI1D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,MAAe,GACtB,MAAM,CAWR"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML manipulation utilities.
|
|
3
|
+
* @module core/utils/html
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Finds the position to inject content before </head>.
|
|
7
|
+
* Performs a case-insensitive search for the closing head tag.
|
|
8
|
+
*
|
|
9
|
+
* @param html - HTML content
|
|
10
|
+
* @returns Position index or -1 if not found
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const html = '<html><head><title>Test</title></head><body></body></html>';
|
|
15
|
+
* const pos = findHeadClosePosition(html);
|
|
16
|
+
* // pos is the index just before </head>
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export function findHeadClosePosition(html) {
|
|
20
|
+
// Case-insensitive search for </head>
|
|
21
|
+
const match = html.match(/<\/head>/i);
|
|
22
|
+
return match ? (match.index ?? -1) : -1;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Injects content before the </head> tag with proper indentation.
|
|
26
|
+
*
|
|
27
|
+
* @param html - HTML content
|
|
28
|
+
* @param content - Content to inject
|
|
29
|
+
* @param indent - Indentation string (default: 4 spaces)
|
|
30
|
+
* @returns HTML with injected content, or original HTML if </head> not found
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* const html = '<html><head><title>Test</title></head><body></body></html>';
|
|
35
|
+
* const result = injectBeforeHeadClose(html, '<meta name="test" content="value">');
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export function injectBeforeHeadClose(html, content, indent = ' ') {
|
|
39
|
+
const headClosePos = findHeadClosePosition(html);
|
|
40
|
+
if (headClosePos === -1) {
|
|
41
|
+
return html;
|
|
42
|
+
}
|
|
43
|
+
const before = html.substring(0, headClosePos);
|
|
44
|
+
const after = html.substring(headClosePos);
|
|
45
|
+
return `${before}${indent}${content}\n${after}`;
|
|
46
|
+
}
|
|
@@ -24,4 +24,5 @@ export type { CompiledBundleInfo } from './bundle-matching.utils.js';
|
|
|
24
24
|
export { createFallbackLogger } from './logger.utils.js';
|
|
25
25
|
export { compileTypeScript, createTypeScriptWatcher, compileStatiConfig, cleanupCompiledConfig, autoInjectBundles, } from './typescript.utils.js';
|
|
26
26
|
export type { CompileOptions, WatchOptions } from './typescript.utils.js';
|
|
27
|
+
export { findHeadClosePosition, injectBeforeHeadClose } from './html.utils.js';
|
|
27
28
|
//# 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,eAAe,CAAC;AAGvB,OAAO,EACL,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,qBAAqB,EACrB,cAAc,EACd,cAAc,EACd,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGhD,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAG7C,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,+BAA+B,CAAC;AAGvC,OAAO,EAAE,6BAA6B,EAAE,MAAM,+BAA+B,CAAC;AAG9E,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAC3F,YAAY,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAGpE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAG/F,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAGxE,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACzE,YAAY,EACV,eAAe,EACf,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACjF,YAAY,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAG7D,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAGrD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGpE,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,yBAAyB,EACzB,wBAAwB,GACzB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAGrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAGzD,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,uBAAuB,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,eAAe,CAAC;AAGvB,OAAO,EACL,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,qBAAqB,EACrB,cAAc,EACd,cAAc,EACd,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGhD,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAG7C,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,+BAA+B,CAAC;AAGvC,OAAO,EAAE,6BAA6B,EAAE,MAAM,+BAA+B,CAAC;AAG9E,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAC3F,YAAY,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAGpE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAG/F,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAGxE,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACzE,YAAY,EACV,eAAe,EACf,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACjF,YAAY,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAG7D,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAGrD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGpE,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,yBAAyB,EACzB,wBAAwB,GACzB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAGrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAGzD,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAG1E,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/core/utils/index.js
CHANGED
|
@@ -36,3 +36,5 @@ export { matchBundlesForPage, getBundlePathsForPage, validateUniqueBundleNames,
|
|
|
36
36
|
export { createFallbackLogger } from './logger.utils.js';
|
|
37
37
|
// TypeScript compilation utilities
|
|
38
38
|
export { compileTypeScript, createTypeScriptWatcher, compileStatiConfig, cleanupCompiledConfig, autoInjectBundles, } from './typescript.utils.js';
|
|
39
|
+
// HTML manipulation utilities
|
|
40
|
+
export { findHeadClosePosition, injectBeforeHeadClose } from './html.utils.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../../src/metrics/recorder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EACV,cAAc,EACd,qBAAqB,EAQtB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../../src/metrics/recorder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EACV,cAAc,EACd,qBAAqB,EAQtB,MAAM,YAAY,CAAC;AAqNpB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,qBAA0B,GAAG,cAAc,CAaxF"}
|
package/dist/metrics/types.d.ts
CHANGED
|
@@ -85,6 +85,7 @@ export interface MetricsPhases {
|
|
|
85
85
|
readonly cacheManifestLoadMs?: number;
|
|
86
86
|
readonly typescriptCompileMs?: number;
|
|
87
87
|
readonly pageRenderingMs?: number;
|
|
88
|
+
readonly searchIndexGenerationMs?: number;
|
|
88
89
|
readonly assetCopyMs?: number;
|
|
89
90
|
readonly cacheManifestSaveMs?: number;
|
|
90
91
|
readonly sitemapGenerationMs?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/metrics/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/metrics/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search index auto-injection utilities.
|
|
3
|
+
* @module search/auto-inject
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Injects a meta tag with the search index path into HTML.
|
|
7
|
+
* The meta tag allows client-side JavaScript to discover the search index location.
|
|
8
|
+
*
|
|
9
|
+
* @param html - Rendered HTML content
|
|
10
|
+
* @param searchIndexPath - Path to the search index file (e.g., '/search-index-a1b2c3d4.json')
|
|
11
|
+
* @returns HTML with injected search meta tag
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* const html = '<html><head><title>Page</title></head><body>Content</body></html>';
|
|
16
|
+
* const enhanced = autoInjectSearchMeta(html, '/search-index-abc123.json');
|
|
17
|
+
* // Returns HTML with <meta name="stati:search-index" content="/search-index-abc123.json">
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export declare function autoInjectSearchMeta(html: string, searchIndexPath: string): string;
|
|
21
|
+
//# sourceMappingURL=auto-inject.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-inject.d.ts","sourceRoot":"","sources":["../../src/search/auto-inject.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,MAAM,CAgBlF"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search index auto-injection utilities.
|
|
3
|
+
* @module search/auto-inject
|
|
4
|
+
*/
|
|
5
|
+
import { injectBeforeHeadClose } from '../core/index.js';
|
|
6
|
+
import { SEARCH_INDEX_META_NAME } from './constants.js';
|
|
7
|
+
/**
|
|
8
|
+
* Injects a meta tag with the search index path into HTML.
|
|
9
|
+
* The meta tag allows client-side JavaScript to discover the search index location.
|
|
10
|
+
*
|
|
11
|
+
* @param html - Rendered HTML content
|
|
12
|
+
* @param searchIndexPath - Path to the search index file (e.g., '/search-index-a1b2c3d4.json')
|
|
13
|
+
* @returns HTML with injected search meta tag
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* const html = '<html><head><title>Page</title></head><body>Content</body></html>';
|
|
18
|
+
* const enhanced = autoInjectSearchMeta(html, '/search-index-abc123.json');
|
|
19
|
+
* // Returns HTML with <meta name="stati:search-index" content="/search-index-abc123.json">
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function autoInjectSearchMeta(html, searchIndexPath) {
|
|
23
|
+
// Check if meta tag already exists using regex to handle attribute order variations
|
|
24
|
+
// Matches: <meta name="stati:search-index" ...> or <meta content="..." name="stati:search-index" ...>
|
|
25
|
+
const existingMetaPattern = new RegExp(`<meta[^>]*name=["']${SEARCH_INDEX_META_NAME}["'][^>]*>`, 'i');
|
|
26
|
+
if (existingMetaPattern.test(html)) {
|
|
27
|
+
return html;
|
|
28
|
+
}
|
|
29
|
+
// Create the meta tag
|
|
30
|
+
const metaTag = `<meta name="${SEARCH_INDEX_META_NAME}" content="${searchIndexPath}">`;
|
|
31
|
+
// Inject before </head>
|
|
32
|
+
return injectBeforeHeadClose(html, metaTag);
|
|
33
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search index constants.
|
|
3
|
+
* @module search/constants
|
|
4
|
+
*/
|
|
5
|
+
/** Meta tag name used to expose the search index path to client-side code */
|
|
6
|
+
export declare const SEARCH_INDEX_META_NAME = "stati:search-index";
|
|
7
|
+
/** Current schema version for the search index */
|
|
8
|
+
export declare const SEARCH_INDEX_VERSION = "1.0.0";
|
|
9
|
+
/** Default configuration values for search index generation */
|
|
10
|
+
export declare const SEARCH_DEFAULTS: {
|
|
11
|
+
/** Default base filename for the search index */
|
|
12
|
+
readonly indexName: "search-index";
|
|
13
|
+
/** Whether to include content hash in filename by default */
|
|
14
|
+
readonly hashFilename: true;
|
|
15
|
+
/** Default maximum content length per section (in characters) */
|
|
16
|
+
readonly maxContentLength: 1000;
|
|
17
|
+
/** Default maximum preview length for page-level entries (in characters) */
|
|
18
|
+
readonly maxPreviewLength: 500;
|
|
19
|
+
/** Default heading levels to include in the index */
|
|
20
|
+
readonly headingLevels: readonly number[];
|
|
21
|
+
/** Default exclude patterns */
|
|
22
|
+
readonly exclude: readonly string[];
|
|
23
|
+
/** Whether to include the home page by default */
|
|
24
|
+
readonly includeHomePage: false;
|
|
25
|
+
/** Whether to auto-inject search index meta tag by default */
|
|
26
|
+
readonly autoInjectMetaTag: true;
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/search/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,6EAA6E;AAC7E,eAAO,MAAM,sBAAsB,uBAAuB,CAAC;AAE3D,kDAAkD;AAClD,eAAO,MAAM,oBAAoB,UAAU,CAAC;AAE5C,+DAA+D;AAC/D,eAAO,MAAM,eAAe;IAC1B,iDAAiD;;IAEjD,6DAA6D;;IAE7D,iEAAiE;;IAEjE,4EAA4E;;IAE5E,qDAAqD;4BACnB,SAAS,MAAM,EAAE;IACnD,+BAA+B;sBAChB,SAAS,MAAM,EAAE;IAChC,kDAAkD;;IAElD,8DAA8D;;CAEtD,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search index constants.
|
|
3
|
+
* @module search/constants
|
|
4
|
+
*/
|
|
5
|
+
/** Meta tag name used to expose the search index path to client-side code */
|
|
6
|
+
export const SEARCH_INDEX_META_NAME = 'stati:search-index';
|
|
7
|
+
/** Current schema version for the search index */
|
|
8
|
+
export const SEARCH_INDEX_VERSION = '1.0.0';
|
|
9
|
+
/** Default configuration values for search index generation */
|
|
10
|
+
export const SEARCH_DEFAULTS = {
|
|
11
|
+
/** Default base filename for the search index */
|
|
12
|
+
indexName: 'search-index',
|
|
13
|
+
/** Whether to include content hash in filename by default */
|
|
14
|
+
hashFilename: true,
|
|
15
|
+
/** Default maximum content length per section (in characters) */
|
|
16
|
+
maxContentLength: 1000,
|
|
17
|
+
/** Default maximum preview length for page-level entries (in characters) */
|
|
18
|
+
maxPreviewLength: 500,
|
|
19
|
+
/** Default heading levels to include in the index */
|
|
20
|
+
headingLevels: [2, 3, 4, 5, 6],
|
|
21
|
+
/** Default exclude patterns */
|
|
22
|
+
exclude: [],
|
|
23
|
+
/** Whether to include the home page by default */
|
|
24
|
+
includeHomePage: false,
|
|
25
|
+
/** Whether to auto-inject search index meta tag by default */
|
|
26
|
+
autoInjectMetaTag: true,
|
|
27
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search index generator utilities.
|
|
3
|
+
* @module search/generator
|
|
4
|
+
*/
|
|
5
|
+
import type { PageModel, TocEntry } from '../types/content.js';
|
|
6
|
+
import type { SearchConfig, SearchDocument, SearchIndex, SearchIndexMetadata } from '../types/search.js';
|
|
7
|
+
/**
|
|
8
|
+
* Generates a short content hash for cache busting.
|
|
9
|
+
* Uses MD5 and returns first 8 characters.
|
|
10
|
+
*
|
|
11
|
+
* @param content - Content to hash
|
|
12
|
+
* @returns 8-character hash string
|
|
13
|
+
*/
|
|
14
|
+
export declare function generateContentHash(content: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Builds a breadcrumb string from a URL path.
|
|
17
|
+
*
|
|
18
|
+
* @param url - Page URL path (e.g., '/getting-started/installation')
|
|
19
|
+
* @param pageTitle - Page title to use as the last segment
|
|
20
|
+
* @returns Breadcrumb string (e.g., 'Getting Started > Installation')
|
|
21
|
+
*/
|
|
22
|
+
export declare function buildBreadcrumb(url: string, pageTitle: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Strips Markdown syntax to extract plain text for search indexing.
|
|
25
|
+
* Handles code blocks, links, images, emphasis, etc.
|
|
26
|
+
*
|
|
27
|
+
* @param markdown - Raw markdown content
|
|
28
|
+
* @returns Plain text content
|
|
29
|
+
*/
|
|
30
|
+
export declare function stripMarkdown(markdown: string): string;
|
|
31
|
+
/**
|
|
32
|
+
* Extracts searchable sections from page data using TOC entries and markdown content.
|
|
33
|
+
* This is more efficient than parsing rendered HTML as it uses structured data
|
|
34
|
+
* already available from the markdown processing pipeline.
|
|
35
|
+
*
|
|
36
|
+
* @param toc - Table of contents entries with heading IDs, text, and levels
|
|
37
|
+
* @param markdownContent - Raw markdown content of the page
|
|
38
|
+
* @param pageUrl - Page URL path
|
|
39
|
+
* @param pageTitle - Page title from frontmatter
|
|
40
|
+
* @param tags - Optional tags from frontmatter
|
|
41
|
+
* @param config - Search configuration
|
|
42
|
+
* @returns Array of SearchDocument objects
|
|
43
|
+
*/
|
|
44
|
+
export declare function extractSectionsFromMarkdown(toc: TocEntry[], markdownContent: string, pageUrl: string, pageTitle: string, tags: readonly string[] | undefined, config: SearchConfig): SearchDocument[];
|
|
45
|
+
/**
|
|
46
|
+
* Checks if a page should be excluded from the search index.
|
|
47
|
+
*
|
|
48
|
+
* @param page - Page model to check
|
|
49
|
+
* @param config - Search configuration
|
|
50
|
+
* @returns true if the page should be excluded
|
|
51
|
+
*/
|
|
52
|
+
export declare function shouldExcludePage(page: PageModel, config: SearchConfig): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Page data for search indexing using markdown and TOC entries.
|
|
55
|
+
* Uses structured data already available from the markdown processing pipeline.
|
|
56
|
+
*/
|
|
57
|
+
export interface SearchablePage {
|
|
58
|
+
/** The page model with metadata */
|
|
59
|
+
page: PageModel;
|
|
60
|
+
/** Table of contents entries extracted during markdown rendering */
|
|
61
|
+
toc: TocEntry[];
|
|
62
|
+
/** Raw markdown content for section extraction */
|
|
63
|
+
markdownContent: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Generates a search index from searchable pages using markdown and TOC data.
|
|
67
|
+
*
|
|
68
|
+
* @param searchablePages - Array of pages with TOC and markdown content
|
|
69
|
+
* @param config - Search configuration
|
|
70
|
+
* @returns SearchIndex object
|
|
71
|
+
*/
|
|
72
|
+
export declare function generateSearchIndex(searchablePages: SearchablePage[], config: SearchConfig): SearchIndex;
|
|
73
|
+
/**
|
|
74
|
+
* Writes the search index to a JSON file.
|
|
75
|
+
*
|
|
76
|
+
* @param searchIndex - The search index to write
|
|
77
|
+
* @param outDir - Output directory path
|
|
78
|
+
* @param filename - Pre-computed filename (from computeSearchIndexFilename)
|
|
79
|
+
* @returns Metadata about the written search index
|
|
80
|
+
*/
|
|
81
|
+
export declare function writeSearchIndex(searchIndex: SearchIndex, outDir: string, filename: string): Promise<SearchIndexMetadata>;
|
|
82
|
+
/**
|
|
83
|
+
* Computes the search index filename based on configuration.
|
|
84
|
+
* When hashFilename is true, generates a deterministic hash based on the build ID.
|
|
85
|
+
* This allows the filename to be known before the index is generated,
|
|
86
|
+
* enabling meta tag injection during initial template render.
|
|
87
|
+
*
|
|
88
|
+
* @param config - Search configuration
|
|
89
|
+
* @param buildId - Unique identifier for this build (e.g., timestamp or build ID)
|
|
90
|
+
* @returns The search index filename (without leading slash)
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```typescript
|
|
94
|
+
* // At build start, compute the filename
|
|
95
|
+
* const filename = computeSearchIndexFilename(config.search, Date.now().toString());
|
|
96
|
+
* // filename: 'search-index-a1b2c3d4.json' (when hashFilename is true)
|
|
97
|
+
* // filename: 'search-index.json' (when hashFilename is false)
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
export declare function computeSearchIndexFilename(config: SearchConfig, buildId?: string): string;
|
|
101
|
+
//# sourceMappingURL=generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/search/generator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,KAAK,EACV,YAAY,EACZ,cAAc,EACd,WAAW,EACX,mBAAmB,EACpB,MAAM,oBAAoB,CAAC;AAE5B;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAuBtE;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA0CtD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,QAAQ,EAAE,EACf,eAAe,EAAE,MAAM,EACvB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,EACnC,MAAM,EAAE,YAAY,GACnB,cAAc,EAAE,CAiGlB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAsBhF;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,mCAAmC;IACnC,IAAI,EAAE,SAAS,CAAC;IAChB,oEAAoE;IACpE,GAAG,EAAE,QAAQ,EAAE,CAAC;IAChB,kDAAkD;IAClD,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,eAAe,EAAE,cAAc,EAAE,EACjC,MAAM,EAAE,YAAY,GACnB,WAAW,CA6Bb;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,mBAAmB,CAAC,CAgB9B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAczF"}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search index generator utilities.
|
|
3
|
+
* @module search/generator
|
|
4
|
+
*/
|
|
5
|
+
import { createHash } from 'node:crypto';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { minimatch } from 'minimatch';
|
|
8
|
+
import { writeFile, ensureDir } from '../core/utils/fs.utils.js';
|
|
9
|
+
import { SEARCH_INDEX_VERSION, SEARCH_DEFAULTS } from './constants.js';
|
|
10
|
+
/**
|
|
11
|
+
* Generates a short content hash for cache busting.
|
|
12
|
+
* Uses MD5 and returns first 8 characters.
|
|
13
|
+
*
|
|
14
|
+
* @param content - Content to hash
|
|
15
|
+
* @returns 8-character hash string
|
|
16
|
+
*/
|
|
17
|
+
export function generateContentHash(content) {
|
|
18
|
+
return createHash('md5').update(content).digest('hex').substring(0, 8);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Builds a breadcrumb string from a URL path.
|
|
22
|
+
*
|
|
23
|
+
* @param url - Page URL path (e.g., '/getting-started/installation')
|
|
24
|
+
* @param pageTitle - Page title to use as the last segment
|
|
25
|
+
* @returns Breadcrumb string (e.g., 'Getting Started > Installation')
|
|
26
|
+
*/
|
|
27
|
+
export function buildBreadcrumb(url, pageTitle) {
|
|
28
|
+
if (url === '/' || url === '') {
|
|
29
|
+
return pageTitle;
|
|
30
|
+
}
|
|
31
|
+
const segments = url.split('/').filter(Boolean);
|
|
32
|
+
if (segments.length === 0) {
|
|
33
|
+
return pageTitle;
|
|
34
|
+
}
|
|
35
|
+
// Convert slug segments to title case
|
|
36
|
+
const crumbs = segments.slice(0, -1).map((segment) => segment
|
|
37
|
+
.split('-')
|
|
38
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
39
|
+
.join(' '));
|
|
40
|
+
// Add the page title as the last crumb
|
|
41
|
+
crumbs.push(pageTitle);
|
|
42
|
+
return crumbs.join(' > ');
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Strips Markdown syntax to extract plain text for search indexing.
|
|
46
|
+
* Handles code blocks, links, images, emphasis, etc.
|
|
47
|
+
*
|
|
48
|
+
* @param markdown - Raw markdown content
|
|
49
|
+
* @returns Plain text content
|
|
50
|
+
*/
|
|
51
|
+
export function stripMarkdown(markdown) {
|
|
52
|
+
let text = markdown;
|
|
53
|
+
// Remove code blocks (fenced and indented)
|
|
54
|
+
text = text.replace(/```[\s\S]*?```/g, ' ');
|
|
55
|
+
text = text.replace(/`[^`]+`/g, ' ');
|
|
56
|
+
// Remove images 
|
|
57
|
+
text = text.replace(/!\[[^\]]*\]\([^)]*\)/g, ' ');
|
|
58
|
+
// Convert links [text](url) to just text
|
|
59
|
+
text = text.replace(/\[([^\]]*)\]\([^)]*\)/g, '$1');
|
|
60
|
+
// Remove reference-style link definitions [id]: url
|
|
61
|
+
text = text.replace(/^\[[^\]]+\]:\s*\S+.*$/gm, '');
|
|
62
|
+
// Remove HTML tags
|
|
63
|
+
text = text.replace(/<[^>]+>/g, ' ');
|
|
64
|
+
// Remove emphasis markers (bold, italic)
|
|
65
|
+
text = text.replace(/[*_]{1,3}([^*_]+)[*_]{1,3}/g, '$1');
|
|
66
|
+
// Remove strikethrough
|
|
67
|
+
text = text.replace(/~~([^~]+)~~/g, '$1');
|
|
68
|
+
// Remove headings markers (but keep the text)
|
|
69
|
+
text = text.replace(/^#{1,6}\s+/gm, '');
|
|
70
|
+
// Remove blockquote markers
|
|
71
|
+
text = text.replace(/^>\s*/gm, '');
|
|
72
|
+
// Remove horizontal rules
|
|
73
|
+
text = text.replace(/^[-*_]{3,}\s*$/gm, '');
|
|
74
|
+
// Remove list markers
|
|
75
|
+
text = text.replace(/^[\s]*[-*+]\s+/gm, '');
|
|
76
|
+
text = text.replace(/^[\s]*\d+\.\s+/gm, '');
|
|
77
|
+
// Normalize whitespace
|
|
78
|
+
text = text.replace(/\s+/g, ' ').trim();
|
|
79
|
+
return text;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Extracts searchable sections from page data using TOC entries and markdown content.
|
|
83
|
+
* This is more efficient than parsing rendered HTML as it uses structured data
|
|
84
|
+
* already available from the markdown processing pipeline.
|
|
85
|
+
*
|
|
86
|
+
* @param toc - Table of contents entries with heading IDs, text, and levels
|
|
87
|
+
* @param markdownContent - Raw markdown content of the page
|
|
88
|
+
* @param pageUrl - Page URL path
|
|
89
|
+
* @param pageTitle - Page title from frontmatter
|
|
90
|
+
* @param tags - Optional tags from frontmatter
|
|
91
|
+
* @param config - Search configuration
|
|
92
|
+
* @returns Array of SearchDocument objects
|
|
93
|
+
*/
|
|
94
|
+
export function extractSectionsFromMarkdown(toc, markdownContent, pageUrl, pageTitle, tags, config) {
|
|
95
|
+
const documents = [];
|
|
96
|
+
const headingLevels = config.headingLevels ?? SEARCH_DEFAULTS.headingLevels;
|
|
97
|
+
const maxContentLength = config.maxContentLength ?? SEARCH_DEFAULTS.maxContentLength;
|
|
98
|
+
const maxPreviewLength = config.maxPreviewLength ?? SEARCH_DEFAULTS.maxPreviewLength;
|
|
99
|
+
const breadcrumb = buildBreadcrumb(pageUrl, pageTitle);
|
|
100
|
+
// Filter TOC entries to only include configured heading levels
|
|
101
|
+
const filteredToc = toc.filter((entry) => headingLevels.includes(entry.level));
|
|
102
|
+
// Find heading positions in markdown content
|
|
103
|
+
// Headings in markdown are lines starting with # symbols
|
|
104
|
+
const lines = markdownContent.split('\n');
|
|
105
|
+
const headingPositions = [];
|
|
106
|
+
// Build a map of heading text to TOC entries for matching
|
|
107
|
+
let charIndex = 0;
|
|
108
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
|
|
109
|
+
const line = lines[lineIndex] ?? '';
|
|
110
|
+
const headingMatch = line.match(/^(#{2,6})\s+(.+)$/);
|
|
111
|
+
if (headingMatch) {
|
|
112
|
+
const headingText = headingMatch[2]?.trim() ?? '';
|
|
113
|
+
// Find matching TOC entry by text (case-insensitive, normalized)
|
|
114
|
+
const normalizedText = headingText.toLowerCase().replace(/[*_`[\]]/g, '');
|
|
115
|
+
const matchingEntry = filteredToc.find((entry) => entry.text.toLowerCase() === normalizedText || entry.text === headingText);
|
|
116
|
+
if (matchingEntry) {
|
|
117
|
+
headingPositions.push({
|
|
118
|
+
entry: matchingEntry,
|
|
119
|
+
lineIndex,
|
|
120
|
+
charIndex,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
charIndex += line.length + 1; // +1 for newline
|
|
125
|
+
}
|
|
126
|
+
// Extract content before first heading for page-level document
|
|
127
|
+
const firstHeadingChar = headingPositions[0]?.charIndex ?? markdownContent.length;
|
|
128
|
+
const pageContentMarkdown = markdownContent.substring(0, firstHeadingChar);
|
|
129
|
+
const pageContent = stripMarkdown(pageContentMarkdown).substring(0, maxPreviewLength);
|
|
130
|
+
// Create page-level document (level 1 = page title)
|
|
131
|
+
documents.push({
|
|
132
|
+
id: `${pageUrl}#top`,
|
|
133
|
+
url: pageUrl,
|
|
134
|
+
anchor: '',
|
|
135
|
+
title: pageTitle,
|
|
136
|
+
heading: pageTitle,
|
|
137
|
+
level: 1,
|
|
138
|
+
content: pageContent,
|
|
139
|
+
breadcrumb,
|
|
140
|
+
...(tags && tags.length > 0 ? { tags: [...tags] } : {}),
|
|
141
|
+
});
|
|
142
|
+
// Create documents for each heading section
|
|
143
|
+
for (let i = 0; i < headingPositions.length; i++) {
|
|
144
|
+
const pos = headingPositions[i];
|
|
145
|
+
if (!pos)
|
|
146
|
+
continue;
|
|
147
|
+
const nextPos = headingPositions[i + 1];
|
|
148
|
+
const sectionStart = pos.charIndex;
|
|
149
|
+
const sectionEnd = nextPos ? nextPos.charIndex : markdownContent.length;
|
|
150
|
+
// Extract section content (skip the heading line itself)
|
|
151
|
+
const sectionLines = markdownContent.substring(sectionStart, sectionEnd).split('\n');
|
|
152
|
+
const contentLines = sectionLines.slice(1); // Skip heading line
|
|
153
|
+
const sectionMarkdown = contentLines.join('\n');
|
|
154
|
+
const sectionContent = stripMarkdown(sectionMarkdown).substring(0, maxContentLength);
|
|
155
|
+
// Skip empty sections
|
|
156
|
+
if (!sectionContent.trim()) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
documents.push({
|
|
160
|
+
id: `${pageUrl}#${pos.entry.id}`,
|
|
161
|
+
url: pageUrl,
|
|
162
|
+
anchor: pos.entry.id,
|
|
163
|
+
title: pageTitle,
|
|
164
|
+
heading: pos.entry.text,
|
|
165
|
+
level: pos.entry.level,
|
|
166
|
+
content: sectionContent,
|
|
167
|
+
breadcrumb: `${breadcrumb} > ${pos.entry.text}`,
|
|
168
|
+
...(tags && tags.length > 0 ? { tags: [...tags] } : {}),
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
return documents;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Checks if a page should be excluded from the search index.
|
|
175
|
+
*
|
|
176
|
+
* @param page - Page model to check
|
|
177
|
+
* @param config - Search configuration
|
|
178
|
+
* @returns true if the page should be excluded
|
|
179
|
+
*/
|
|
180
|
+
export function shouldExcludePage(page, config) {
|
|
181
|
+
// Exclude drafts
|
|
182
|
+
if (page.frontMatter.draft) {
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
// Check home page exclusion
|
|
186
|
+
const isHomePage = page.url === '/' || page.url === '';
|
|
187
|
+
const includeHomePage = config.includeHomePage ?? SEARCH_DEFAULTS.includeHomePage;
|
|
188
|
+
if (isHomePage && !includeHomePage) {
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
// Check exclude patterns
|
|
192
|
+
const excludePatterns = config.exclude ?? SEARCH_DEFAULTS.exclude;
|
|
193
|
+
for (const pattern of excludePatterns) {
|
|
194
|
+
if (minimatch(page.url, pattern)) {
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Generates a search index from searchable pages using markdown and TOC data.
|
|
202
|
+
*
|
|
203
|
+
* @param searchablePages - Array of pages with TOC and markdown content
|
|
204
|
+
* @param config - Search configuration
|
|
205
|
+
* @returns SearchIndex object
|
|
206
|
+
*/
|
|
207
|
+
export function generateSearchIndex(searchablePages, config) {
|
|
208
|
+
const documents = [];
|
|
209
|
+
for (const { page, toc, markdownContent } of searchablePages) {
|
|
210
|
+
// Skip excluded pages
|
|
211
|
+
if (shouldExcludePage(page, config)) {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
const pageTitle = page.frontMatter.title || page.slug;
|
|
215
|
+
const tags = page.frontMatter.tags;
|
|
216
|
+
const pageDocs = extractSectionsFromMarkdown(toc, markdownContent, page.url, pageTitle, tags, config);
|
|
217
|
+
documents.push(...pageDocs);
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
version: SEARCH_INDEX_VERSION,
|
|
221
|
+
generatedAt: new Date().toISOString(),
|
|
222
|
+
documentCount: documents.length,
|
|
223
|
+
documents,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Writes the search index to a JSON file.
|
|
228
|
+
*
|
|
229
|
+
* @param searchIndex - The search index to write
|
|
230
|
+
* @param outDir - Output directory path
|
|
231
|
+
* @param filename - Pre-computed filename (from computeSearchIndexFilename)
|
|
232
|
+
* @returns Metadata about the written search index
|
|
233
|
+
*/
|
|
234
|
+
export async function writeSearchIndex(searchIndex, outDir, filename) {
|
|
235
|
+
// Serialize index
|
|
236
|
+
const content = JSON.stringify(searchIndex, null, 0);
|
|
237
|
+
// Ensure output directory exists
|
|
238
|
+
await ensureDir(outDir);
|
|
239
|
+
// Write the file
|
|
240
|
+
const filePath = join(outDir, filename);
|
|
241
|
+
await writeFile(filePath, content, 'utf-8');
|
|
242
|
+
return {
|
|
243
|
+
enabled: true,
|
|
244
|
+
indexPath: `/${filename}`,
|
|
245
|
+
documentCount: searchIndex.documentCount,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Computes the search index filename based on configuration.
|
|
250
|
+
* When hashFilename is true, generates a deterministic hash based on the build ID.
|
|
251
|
+
* This allows the filename to be known before the index is generated,
|
|
252
|
+
* enabling meta tag injection during initial template render.
|
|
253
|
+
*
|
|
254
|
+
* @param config - Search configuration
|
|
255
|
+
* @param buildId - Unique identifier for this build (e.g., timestamp or build ID)
|
|
256
|
+
* @returns The search index filename (without leading slash)
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* ```typescript
|
|
260
|
+
* // At build start, compute the filename
|
|
261
|
+
* const filename = computeSearchIndexFilename(config.search, Date.now().toString());
|
|
262
|
+
* // filename: 'search-index-a1b2c3d4.json' (when hashFilename is true)
|
|
263
|
+
* // filename: 'search-index.json' (when hashFilename is false)
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
export function computeSearchIndexFilename(config, buildId) {
|
|
267
|
+
const indexName = config.indexName ?? SEARCH_DEFAULTS.indexName;
|
|
268
|
+
const hashFilename = config.hashFilename ?? SEARCH_DEFAULTS.hashFilename;
|
|
269
|
+
if (hashFilename) {
|
|
270
|
+
// Generate a deterministic hash based on build ID
|
|
271
|
+
// This allows the filename to be known before content is generated
|
|
272
|
+
const hashInput = buildId ?? Date.now().toString();
|
|
273
|
+
const hash = generateContentHash(hashInput);
|
|
274
|
+
return `${indexName}-${hash}.json`;
|
|
275
|
+
}
|
|
276
|
+
// Return base filename (without hash)
|
|
277
|
+
return `${indexName}.json`;
|
|
278
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search index generation module.
|
|
3
|
+
* Provides build-time search index generation for Stati sites.
|
|
4
|
+
*
|
|
5
|
+
* @module search
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { generateSearchIndex, writeSearchIndex, SEARCH_INDEX_META_NAME } from '@stati/core/search';
|
|
10
|
+
*
|
|
11
|
+
* // Generate search index from searchable pages
|
|
12
|
+
* const searchIndex = generateSearchIndex(searchablePages, config.search);
|
|
13
|
+
*
|
|
14
|
+
* // Write to output directory
|
|
15
|
+
* const metadata = await writeSearchIndex(searchIndex, outDir, config.search);
|
|
16
|
+
* console.log(`Generated search index at ${metadata.indexPath}`);
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export { generateSearchIndex, writeSearchIndex, extractSectionsFromMarkdown, stripMarkdown, shouldExcludePage, generateContentHash, buildBreadcrumb, computeSearchIndexFilename, } from './generator.js';
|
|
20
|
+
export type { SearchablePage } from './generator.js';
|
|
21
|
+
export { autoInjectSearchMeta } from './auto-inject.js';
|
|
22
|
+
export { SEARCH_INDEX_META_NAME, SEARCH_INDEX_VERSION, SEARCH_DEFAULTS } from './constants.js';
|
|
23
|
+
export type { SearchConfig, SearchDocument, SearchIndex, SearchIndexMetadata, } from '../types/search.js';
|
|
24
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/search/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,2BAA2B,EAC3B,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EACnB,eAAe,EACf,0BAA0B,GAC3B,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAE/F,YAAY,EACV,YAAY,EACZ,cAAc,EACd,WAAW,EACX,mBAAmB,GACpB,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search index generation module.
|
|
3
|
+
* Provides build-time search index generation for Stati sites.
|
|
4
|
+
*
|
|
5
|
+
* @module search
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { generateSearchIndex, writeSearchIndex, SEARCH_INDEX_META_NAME } from '@stati/core/search';
|
|
10
|
+
*
|
|
11
|
+
* // Generate search index from searchable pages
|
|
12
|
+
* const searchIndex = generateSearchIndex(searchablePages, config.search);
|
|
13
|
+
*
|
|
14
|
+
* // Write to output directory
|
|
15
|
+
* const metadata = await writeSearchIndex(searchIndex, outDir, config.search);
|
|
16
|
+
* console.log(`Generated search index at ${metadata.indexPath}`);
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export { generateSearchIndex, writeSearchIndex, extractSectionsFromMarkdown, stripMarkdown, shouldExcludePage, generateContentHash, buildBreadcrumb, computeSearchIndexFilename, } from './generator.js';
|
|
20
|
+
// Auto-injection
|
|
21
|
+
export { autoInjectSearchMeta } from './auto-inject.js';
|
|
22
|
+
export { SEARCH_INDEX_META_NAME, SEARCH_INDEX_VERSION, SEARCH_DEFAULTS } from './constants.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auto-inject.d.ts","sourceRoot":"","sources":["../../src/seo/auto-inject.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"auto-inject.d.ts","sourceRoot":"","sources":["../../src/seo/auto-inject.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAKlD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,+CAA+C;IAC/C,IAAI,EAAE,SAAS,CAAC;IAChB,yBAAyB;IACzB,MAAM,EAAE,WAAW,CAAC;IACpB,oBAAoB;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,8BAA8B;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,2BAA2B;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAmBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,MAAM,CA0D9E;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAK/E"}
|
package/dist/seo/auto-inject.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { detectExistingSEOTags } from './utils/index.js';
|
|
6
6
|
import { generateSEOMetadata } from './generator.js';
|
|
7
|
+
import { injectBeforeHeadClose } from '../core/index.js';
|
|
7
8
|
/**
|
|
8
9
|
* Helper function to conditionally log debug messages for SEO auto-injection.
|
|
9
10
|
* Checks both the explicit debug flag and the config-level debug setting.
|
|
@@ -17,16 +18,6 @@ function logDebug(message, options) {
|
|
|
17
18
|
options.logger.warning(logMessage);
|
|
18
19
|
}
|
|
19
20
|
}
|
|
20
|
-
/**
|
|
21
|
-
* Finds the position to inject SEO tags (before </head>)
|
|
22
|
-
* @param html - HTML content
|
|
23
|
-
* @returns Position index or -1 if not found
|
|
24
|
-
*/
|
|
25
|
-
function findHeadClosePosition(html) {
|
|
26
|
-
// Case-insensitive search for </head>
|
|
27
|
-
const match = html.match(/<\/head>/i);
|
|
28
|
-
return match ? (match.index ?? -1) : -1;
|
|
29
|
-
}
|
|
30
21
|
/**
|
|
31
22
|
* Automatically injects SEO metadata into HTML if not already present
|
|
32
23
|
* @param html - Rendered HTML content
|
|
@@ -77,17 +68,13 @@ export function autoInjectSEO(html, options) {
|
|
|
77
68
|
logDebug(`No tags to inject for ${page.url} (all exist)`, { debug, config, logger });
|
|
78
69
|
return html;
|
|
79
70
|
}
|
|
80
|
-
//
|
|
81
|
-
const
|
|
82
|
-
if (
|
|
71
|
+
// Inject SEO metadata before </head>
|
|
72
|
+
const injected = injectBeforeHeadClose(html, seoMetadata);
|
|
73
|
+
// Check if injection was successful (</head> was found)
|
|
74
|
+
if (injected === html) {
|
|
83
75
|
logDebug(`No </head> tag found in ${page.url}, skipping injection`, { debug, config, logger });
|
|
84
76
|
return html;
|
|
85
77
|
}
|
|
86
|
-
// Inject SEO metadata before </head>
|
|
87
|
-
const before = html.substring(0, headClosePos);
|
|
88
|
-
const after = html.substring(headClosePos);
|
|
89
|
-
// Add proper indentation (4 spaces) and newline
|
|
90
|
-
const injected = `${before} ${seoMetadata}\n${after}`;
|
|
91
78
|
logDebug(`Injected ${existingTags.size === 0 ? 'all' : 'missing'} SEO tags into ${page.url}`, {
|
|
92
79
|
debug,
|
|
93
80
|
config,
|
package/dist/types/config.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type MarkdownIt from 'markdown-it';
|
|
2
2
|
import type { SitemapConfig } from './sitemap.js';
|
|
3
3
|
import type { AuthorConfig } from './content.js';
|
|
4
|
+
import type { SearchConfig } from './search.js';
|
|
4
5
|
/**
|
|
5
6
|
* Configuration related type definitions
|
|
6
7
|
*/
|
|
@@ -78,6 +79,8 @@ export interface StatiConfig {
|
|
|
78
79
|
robots?: RobotsTxtConfig;
|
|
79
80
|
/** RSS feed generation configuration */
|
|
80
81
|
rss?: import('./rss.js').RSSConfig;
|
|
82
|
+
/** Search index generation configuration */
|
|
83
|
+
search?: SearchConfig;
|
|
81
84
|
/** TypeScript compilation settings */
|
|
82
85
|
typescript?: TypeScriptConfig;
|
|
83
86
|
/** Development server configuration */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"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"}
|
package/dist/types/content.d.ts
CHANGED
|
@@ -22,6 +22,12 @@ export interface StatiAssets {
|
|
|
22
22
|
* Always an array, empty [] if no TypeScript enabled or no bundles match.
|
|
23
23
|
*/
|
|
24
24
|
bundlePaths: string[];
|
|
25
|
+
/**
|
|
26
|
+
* Path to the search index file (e.g., '/search-index-a1b2c3d4.json').
|
|
27
|
+
* Available when search index generation is enabled.
|
|
28
|
+
* Use this path in client-side JavaScript to fetch the search index.
|
|
29
|
+
*/
|
|
30
|
+
searchIndexPath?: string;
|
|
25
31
|
}
|
|
26
32
|
/**
|
|
27
33
|
* Table of contents entry extracted from page headings.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/types/content.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,WAAW,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/types/content.ts"],"names":[],"mappings":"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"}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export type { SEOContext, SEOValidationResult } from './seo.js';
|
|
|
9
9
|
export { SEOTagType } from './seo.js';
|
|
10
10
|
export type { ChangeFrequency, SitemapEntry, SitemapConfig, SitemapGenerationResult, } from './sitemap.js';
|
|
11
11
|
export type { RSSConfig, RSSFeedConfig, RSSGenerationResult } from './rss.js';
|
|
12
|
+
export type { SearchConfig, SearchDocument, SearchIndex, SearchIndexMetadata } from './search.js';
|
|
12
13
|
export type { NavNode } from './navigation.js';
|
|
13
14
|
export type { Logger } from './logging.js';
|
|
14
15
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGhF,YAAY,EACV,UAAU,EACV,WAAW,EACX,YAAY,EACZ,WAAW,EACX,UAAU,EACV,UAAU,EACV,SAAS,EACT,eAAe,EACf,gBAAgB,EAChB,YAAY,GACb,MAAM,aAAa,CAAC;AAGrB,YAAY,EACV,WAAW,EACX,SAAS,EACT,cAAc,EACd,eAAe,EACf,WAAW,EACX,eAAe,EACf,YAAY,EACZ,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,QAAQ,GACT,MAAM,cAAc,CAAC;AAGtB,YAAY,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGtC,YAAY,EACV,eAAe,EACf,YAAY,EACZ,aAAa,EACb,uBAAuB,GACxB,MAAM,cAAc,CAAC;AAGtB,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAG9E,YAAY,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAG/C,YAAY,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGhF,YAAY,EACV,UAAU,EACV,WAAW,EACX,YAAY,EACZ,WAAW,EACX,UAAU,EACV,UAAU,EACV,SAAS,EACT,eAAe,EACf,gBAAgB,EAChB,YAAY,GACb,MAAM,aAAa,CAAC;AAGrB,YAAY,EACV,WAAW,EACX,SAAS,EACT,cAAc,EACd,eAAe,EACf,WAAW,EACX,eAAe,EACf,YAAY,EACZ,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,QAAQ,GACT,MAAM,cAAc,CAAC;AAGtB,YAAY,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGtC,YAAY,EACV,eAAe,EACf,YAAY,EACZ,aAAa,EACb,uBAAuB,GACxB,MAAM,cAAc,CAAC;AAGtB,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAG9E,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGlG,YAAY,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAG/C,YAAY,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search index generation configuration and type definitions.
|
|
3
|
+
* @module types/search
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Search index generation configuration.
|
|
7
|
+
* When enabled, Stati generates a JSON search index at build time.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const config: StatiConfig = {
|
|
12
|
+
* search: {
|
|
13
|
+
* enabled: true,
|
|
14
|
+
* indexName: 'search-index',
|
|
15
|
+
* hashFilename: true,
|
|
16
|
+
* maxContentLength: 1000,
|
|
17
|
+
* maxPreviewLength: 500,
|
|
18
|
+
* headingLevels: [2, 3, 4],
|
|
19
|
+
* exclude: ['/private/**'],
|
|
20
|
+
* includeHomePage: false,
|
|
21
|
+
* autoInjectMetaTag: true,
|
|
22
|
+
* }
|
|
23
|
+
* };
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export interface SearchConfig {
|
|
27
|
+
/** Enable search index generation. @default false */
|
|
28
|
+
enabled: boolean;
|
|
29
|
+
/** Base filename for the search index (without extension). @default 'search-index' */
|
|
30
|
+
indexName?: string;
|
|
31
|
+
/** Include content hash in filename for cache busting. @default true */
|
|
32
|
+
hashFilename?: boolean;
|
|
33
|
+
/** Maximum content length per section (in characters). @default 1000 */
|
|
34
|
+
maxContentLength?: number;
|
|
35
|
+
/** Maximum preview length for page-level entries (in characters). @default 500 */
|
|
36
|
+
maxPreviewLength?: number;
|
|
37
|
+
/** Heading levels to include in the index. @default [2, 3, 4, 5, 6] */
|
|
38
|
+
headingLevels?: number[];
|
|
39
|
+
/** Glob patterns for pages to exclude from the search index. @default [] */
|
|
40
|
+
exclude?: string[];
|
|
41
|
+
/** Include the home page (index) in the search index. @default false */
|
|
42
|
+
includeHomePage?: boolean;
|
|
43
|
+
/** Auto-inject search index meta tag into rendered HTML. @default true */
|
|
44
|
+
autoInjectMetaTag?: boolean;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* A searchable document representing a page section.
|
|
48
|
+
* Each document is a searchable unit in the search index.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```typescript
|
|
52
|
+
* const doc: SearchDocument = {
|
|
53
|
+
* id: '/getting-started/installation#prerequisites',
|
|
54
|
+
* url: '/getting-started/installation',
|
|
55
|
+
* anchor: 'prerequisites',
|
|
56
|
+
* title: 'Installation',
|
|
57
|
+
* heading: 'Prerequisites',
|
|
58
|
+
* level: 2,
|
|
59
|
+
* content: 'Before installing Stati, ensure you have Node.js 22 or later...',
|
|
60
|
+
* breadcrumb: 'Getting Started > Installation',
|
|
61
|
+
* tags: ['setup', 'installation']
|
|
62
|
+
* };
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export interface SearchDocument {
|
|
66
|
+
/** Unique identifier (page URL + section anchor) */
|
|
67
|
+
id: string;
|
|
68
|
+
/** Page URL path */
|
|
69
|
+
url: string;
|
|
70
|
+
/** Section anchor (heading ID), empty string for page-level entry */
|
|
71
|
+
anchor: string;
|
|
72
|
+
/** Page title from frontmatter */
|
|
73
|
+
title: string;
|
|
74
|
+
/** Section heading text */
|
|
75
|
+
heading: string;
|
|
76
|
+
/** Heading level (1 for page title, 2-6 for headings) */
|
|
77
|
+
level: number;
|
|
78
|
+
/** Text content of the section (stripped of HTML) */
|
|
79
|
+
content: string;
|
|
80
|
+
/** Breadcrumb path for display */
|
|
81
|
+
breadcrumb: string;
|
|
82
|
+
/** Optional tags from frontmatter */
|
|
83
|
+
tags?: string[];
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The complete search index structure written to JSON.
|
|
87
|
+
* This is the output format for the generated search index file.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```typescript
|
|
91
|
+
* const index: SearchIndex = {
|
|
92
|
+
* version: '1.0.0',
|
|
93
|
+
* generatedAt: '2025-01-15T10:30:00.000Z',
|
|
94
|
+
* documentCount: 150,
|
|
95
|
+
* documents: [...]
|
|
96
|
+
* };
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
export interface SearchIndex {
|
|
100
|
+
/** Schema version for forward compatibility */
|
|
101
|
+
version: string;
|
|
102
|
+
/** ISO timestamp when the index was generated */
|
|
103
|
+
generatedAt: string;
|
|
104
|
+
/** Total document count */
|
|
105
|
+
documentCount: number;
|
|
106
|
+
/** Array of searchable documents */
|
|
107
|
+
documents: SearchDocument[];
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Metadata about the generated search index.
|
|
111
|
+
* Used to pass search index information to templates and other parts of the build.
|
|
112
|
+
*/
|
|
113
|
+
export interface SearchIndexMetadata {
|
|
114
|
+
/** Whether search index was generated */
|
|
115
|
+
enabled: boolean;
|
|
116
|
+
/** Full path to the search index file (e.g., '/search-index-a1b2c3d4.json') */
|
|
117
|
+
indexPath: string;
|
|
118
|
+
/** Document count in the index */
|
|
119
|
+
documentCount: number;
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/types/search.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,YAAY;IAC3B,qDAAqD;IACrD,OAAO,EAAE,OAAO,CAAC;IAEjB,sFAAsF;IACtF,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,wEAAwE;IACxE,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,kFAAkF;IAClF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,uEAAuE;IACvE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IAEzB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB,wEAAwE;IACxE,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B,0EAA0E;IAC1E,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,cAAc;IAC7B,oDAAoD;IACpD,EAAE,EAAE,MAAM,CAAC;IACX,oBAAoB;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,2BAA2B;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,kCAAkC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,qCAAqC;IACrC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,WAAW;IAC1B,+CAA+C;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,WAAW,EAAE,MAAM,CAAC;IACpB,2BAA2B;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,oCAAoC;IACpC,SAAS,EAAE,cAAc,EAAE,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,yCAAyC;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,+EAA+E;IAC/E,SAAS,EAAE,MAAM,CAAC;IAClB,kCAAkC;IAClC,aAAa,EAAE,MAAM,CAAC;CACvB"}
|