@stati/core 1.16.2 → 1.17.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 +41 -12
- package/dist/core/build.d.ts.map +1 -1
- package/dist/core/build.js +92 -11
- package/dist/core/dev.js +3 -3
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/isg/ttl.js +1 -1
- package/dist/core/templates.d.ts +10 -1
- package/dist/core/templates.d.ts.map +1 -1
- package/dist/core/templates.js +11 -14
- package/dist/core/utils/glob-patterns.utils.d.ts.map +1 -1
- package/dist/core/utils/glob-patterns.utils.js +1 -6
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/metrics/index.d.ts +38 -0
- package/dist/metrics/index.d.ts.map +1 -0
- package/dist/metrics/index.js +42 -0
- package/dist/metrics/noop.d.ts +12 -0
- package/dist/metrics/noop.d.ts.map +1 -0
- package/dist/metrics/noop.js +88 -0
- package/dist/metrics/recorder.d.ts +31 -0
- package/dist/metrics/recorder.d.ts.map +1 -0
- package/dist/metrics/recorder.js +176 -0
- package/dist/metrics/types.d.ts +236 -0
- package/dist/metrics/types.d.ts.map +1 -0
- package/dist/metrics/types.js +7 -0
- package/dist/metrics/utils/index.d.ts +9 -0
- package/dist/metrics/utils/index.d.ts.map +1 -0
- package/dist/metrics/utils/index.js +9 -0
- package/dist/metrics/utils/system.utils.d.ts +44 -0
- package/dist/metrics/utils/system.utils.d.ts.map +1 -0
- package/dist/metrics/utils/system.utils.js +95 -0
- package/dist/metrics/utils/writer.utils.d.ts +64 -0
- package/dist/metrics/utils/writer.utils.d.ts.map +1 -0
- package/dist/metrics/utils/writer.utils.js +145 -0
- package/package.json +1 -1
package/dist/core/build.d.ts
CHANGED
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
import type { BuildStats, Logger } from '../types/index.js';
|
|
2
|
+
import type { BuildMetrics } from '../metrics/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Options for metrics collection during builds.
|
|
5
|
+
*/
|
|
6
|
+
export interface MetricsOptions {
|
|
7
|
+
/** Enable metrics collection */
|
|
8
|
+
enabled?: boolean;
|
|
9
|
+
/** Include per-page timing details */
|
|
10
|
+
detailed?: boolean;
|
|
11
|
+
}
|
|
2
12
|
/**
|
|
3
13
|
* Options for customizing the build process.
|
|
4
14
|
*
|
|
15
|
+
* @remarks
|
|
16
|
+
* The `| undefined` on optional properties is intentional due to
|
|
17
|
+
* `exactOptionalPropertyTypes: true` in tsconfig.json, allowing callers
|
|
18
|
+
* to explicitly pass `undefined` values.
|
|
19
|
+
*
|
|
5
20
|
* @example
|
|
6
21
|
* ```typescript
|
|
7
22
|
* const options: BuildOptions = {
|
|
@@ -9,23 +24,33 @@ import type { BuildStats, Logger } from '../types/index.js';
|
|
|
9
24
|
* clean: true, // Clean output directory before build
|
|
10
25
|
* configPath: './custom.config.js', // Custom config file path
|
|
11
26
|
* includeDrafts: true, // Include draft pages in build
|
|
12
|
-
* version: '1.0.0'
|
|
27
|
+
* version: '1.0.0', // Version to display in build messages
|
|
28
|
+
* metrics: { enabled: true, detailed: true } // Enable metrics collection
|
|
13
29
|
* };
|
|
14
30
|
* ```
|
|
15
31
|
*/
|
|
16
32
|
export interface BuildOptions {
|
|
17
33
|
/** Force rebuild of all pages, ignoring cache */
|
|
18
|
-
force?: boolean;
|
|
34
|
+
force?: boolean | undefined;
|
|
19
35
|
/** Clean the output directory before building */
|
|
20
|
-
clean?: boolean;
|
|
36
|
+
clean?: boolean | undefined;
|
|
21
37
|
/** Path to a custom configuration file */
|
|
22
|
-
configPath?: string;
|
|
38
|
+
configPath?: string | undefined;
|
|
23
39
|
/** Include draft pages in the build */
|
|
24
|
-
includeDrafts?: boolean;
|
|
40
|
+
includeDrafts?: boolean | undefined;
|
|
25
41
|
/** Custom logger for build output */
|
|
26
|
-
logger?: Logger;
|
|
42
|
+
logger?: Logger | undefined;
|
|
27
43
|
/** Version information to display in build messages */
|
|
28
|
-
version?: string;
|
|
44
|
+
version?: string | undefined;
|
|
45
|
+
/** Metrics collection options */
|
|
46
|
+
metrics?: MetricsOptions | undefined;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Extended build result including optional metrics.
|
|
50
|
+
*/
|
|
51
|
+
export interface BuildResult extends BuildStats {
|
|
52
|
+
/** Build metrics (only present when metrics enabled) */
|
|
53
|
+
buildMetrics?: BuildMetrics;
|
|
29
54
|
}
|
|
30
55
|
/**
|
|
31
56
|
* Builds the Stati site with ISG support.
|
|
@@ -43,20 +68,24 @@ export interface BuildOptions {
|
|
|
43
68
|
* 5. Update cache manifest
|
|
44
69
|
*
|
|
45
70
|
* @param options - Build configuration options
|
|
46
|
-
* @returns Promise resolving to build statistics
|
|
71
|
+
* @returns Promise resolving to build result with statistics and optional metrics
|
|
47
72
|
* @throws {Error} When configuration is invalid
|
|
48
73
|
* @throws {Error} When template rendering fails
|
|
49
74
|
* @throws {Error} When build lock cannot be acquired
|
|
50
75
|
*
|
|
51
76
|
* @example
|
|
52
77
|
* ```typescript
|
|
53
|
-
* const
|
|
78
|
+
* const result = await build({
|
|
54
79
|
* force: true,
|
|
55
80
|
* clean: true,
|
|
56
|
-
* includeDrafts: false
|
|
81
|
+
* includeDrafts: false,
|
|
82
|
+
* metrics: { enabled: true }
|
|
57
83
|
* });
|
|
58
|
-
* console.log(`Built ${
|
|
84
|
+
* console.log(`Built ${result.totalPages} pages in ${result.buildTimeMs}ms`);
|
|
85
|
+
* if (result.buildMetrics) {
|
|
86
|
+
* console.log(`Cache hit rate: ${result.buildMetrics.isg.cacheHitRate}`);
|
|
87
|
+
* }
|
|
59
88
|
* ```
|
|
60
89
|
*/
|
|
61
|
-
export declare function build(options?: BuildOptions): Promise<
|
|
90
|
+
export declare function build(options?: BuildOptions): Promise<BuildResult>;
|
|
62
91
|
//# sourceMappingURL=build.d.ts.map
|
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":"AAgDA,OAAO,KAAK,EAEV,UAAU,EACV,MAAM,EAMP,MAAM,mBAAmB,CAAC;AAC3B,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,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ensureDir, writeFile, remove, pathExists, stat, readdir, copyFile, resolveOutDir, resolveStaticDir, resolveCacheDir, enableInventoryTracking, disableInventoryTracking, clearInventory, writeTailwindClassInventory, getInventorySize, isTailwindUsed, loadPreviousInventory, compileTypeScript, autoInjectBundles, getBundlePathsForPage, } from './utils/index.js';
|
|
2
2
|
import { join, dirname, relative, posix } from 'node:path';
|
|
3
|
+
import { performance } from 'node:perf_hooks';
|
|
3
4
|
import { loadConfig } from '../config/loader.js';
|
|
4
5
|
import { loadContent } from './content.js';
|
|
5
6
|
import { createMarkdownProcessor, renderMarkdown } from './markdown.js';
|
|
@@ -10,6 +11,7 @@ import { generateSitemap, generateRobotsTxtFromConfig, autoInjectSEO, } from '..
|
|
|
10
11
|
import { generateRSSFeeds, validateRSSConfig } from '../rss/index.js';
|
|
11
12
|
import { getEnv } from '../env.js';
|
|
12
13
|
import { DEFAULT_OUT_DIR } from '../constants.js';
|
|
14
|
+
import { createMetricRecorder, noopMetricRecorder } from '../metrics/index.js';
|
|
13
15
|
/**
|
|
14
16
|
* Recursively calculates the total size of a directory in bytes.
|
|
15
17
|
* Used for build statistics.
|
|
@@ -103,19 +105,23 @@ const defaultLogger = {
|
|
|
103
105
|
* 5. Update cache manifest
|
|
104
106
|
*
|
|
105
107
|
* @param options - Build configuration options
|
|
106
|
-
* @returns Promise resolving to build statistics
|
|
108
|
+
* @returns Promise resolving to build result with statistics and optional metrics
|
|
107
109
|
* @throws {Error} When configuration is invalid
|
|
108
110
|
* @throws {Error} When template rendering fails
|
|
109
111
|
* @throws {Error} When build lock cannot be acquired
|
|
110
112
|
*
|
|
111
113
|
* @example
|
|
112
114
|
* ```typescript
|
|
113
|
-
* const
|
|
115
|
+
* const result = await build({
|
|
114
116
|
* force: true,
|
|
115
117
|
* clean: true,
|
|
116
|
-
* includeDrafts: false
|
|
118
|
+
* includeDrafts: false,
|
|
119
|
+
* metrics: { enabled: true }
|
|
117
120
|
* });
|
|
118
|
-
* console.log(`Built ${
|
|
121
|
+
* console.log(`Built ${result.totalPages} pages in ${result.buildTimeMs}ms`);
|
|
122
|
+
* if (result.buildMetrics) {
|
|
123
|
+
* console.log(`Cache hit rate: ${result.buildMetrics.isg.cacheHitRate}`);
|
|
124
|
+
* }
|
|
119
125
|
* ```
|
|
120
126
|
*/
|
|
121
127
|
export async function build(options = {}) {
|
|
@@ -184,14 +190,16 @@ async function loadContentAndBuildNavigation(config, options, logger) {
|
|
|
184
190
|
/**
|
|
185
191
|
* Processes pages with ISG caching logic.
|
|
186
192
|
*/
|
|
187
|
-
async function processPagesWithCache(pages, manifest, config, outDir, md, eta, navigation, buildTime, options, logger, compiledBundles) {
|
|
193
|
+
async function processPagesWithCache(pages, manifest, config, outDir, md, eta, navigation, buildTime, options, logger, compiledBundles, recorder = noopMetricRecorder) {
|
|
188
194
|
let cacheHits = 0;
|
|
189
195
|
let cacheMisses = 0;
|
|
190
196
|
// Build context
|
|
191
197
|
const buildContext = { config, pages };
|
|
192
198
|
// Run beforeAll hook
|
|
193
199
|
if (config.hooks?.beforeAll) {
|
|
200
|
+
const endHookBeforeAll = recorder.startSpan('hookBeforeAllMs');
|
|
194
201
|
await config.hooks.beforeAll(buildContext);
|
|
202
|
+
endHookBeforeAll();
|
|
195
203
|
}
|
|
196
204
|
// Render each page with tree-based progress tracking and ISG
|
|
197
205
|
if (logger.step) {
|
|
@@ -239,14 +247,18 @@ async function processPagesWithCache(pages, manifest, config, outDir, md, eta, n
|
|
|
239
247
|
else {
|
|
240
248
|
logger.processing(`📋 Cached ${page.url}`);
|
|
241
249
|
}
|
|
250
|
+
// Record page timing for cached pages (0ms render time)
|
|
251
|
+
recorder.recordPageTiming(page.url, 0, true);
|
|
242
252
|
continue;
|
|
243
253
|
}
|
|
244
254
|
// Cache miss - need to rebuild
|
|
245
255
|
cacheMisses++;
|
|
246
|
-
const startTime =
|
|
256
|
+
const startTime = performance.now();
|
|
247
257
|
// Run beforeRender hook
|
|
248
258
|
if (config.hooks?.beforeRender) {
|
|
259
|
+
const hookStart = performance.now();
|
|
249
260
|
await config.hooks.beforeRender({ page, config });
|
|
261
|
+
recorder.addToPhase('hookBeforeRenderTotalMs', performance.now() - hookStart);
|
|
250
262
|
}
|
|
251
263
|
// Render markdown to HTML
|
|
252
264
|
const htmlContent = renderMarkdown(page.content, md);
|
|
@@ -254,7 +266,10 @@ async function processPagesWithCache(pages, manifest, config, outDir, md, eta, n
|
|
|
254
266
|
const bundlePaths = getBundlePathsForPage(page.url, compiledBundles);
|
|
255
267
|
const assets = bundlePaths.length > 0 ? { bundlePaths } : undefined;
|
|
256
268
|
// Render with template
|
|
257
|
-
|
|
269
|
+
const renderResult = await renderPage(page, htmlContent, config, eta, navigation, pages, assets);
|
|
270
|
+
let finalHtml = renderResult.html;
|
|
271
|
+
// Record templates loaded for this page
|
|
272
|
+
recorder.increment('templatesLoaded', renderResult.templatesLoaded);
|
|
258
273
|
// Auto-inject SEO tags if enabled
|
|
259
274
|
if (config.seo?.autoInject !== false) {
|
|
260
275
|
const injectOptions = {
|
|
@@ -273,7 +288,9 @@ async function processPagesWithCache(pages, manifest, config, outDir, md, eta, n
|
|
|
273
288
|
if (shouldAutoInject && assets && assets.bundlePaths.length > 0) {
|
|
274
289
|
finalHtml = autoInjectBundles(finalHtml, assets.bundlePaths);
|
|
275
290
|
}
|
|
276
|
-
const renderTime =
|
|
291
|
+
const renderTime = Math.round(performance.now() - startTime);
|
|
292
|
+
// Record page timing for rendered pages (includes template count)
|
|
293
|
+
recorder.recordPageTiming(page.url, renderTime, false, renderResult.templatesLoaded);
|
|
277
294
|
if (logger.updateTreeNode) {
|
|
278
295
|
logger.updateTreeNode(pageId, 'completed', {
|
|
279
296
|
timing: renderTime,
|
|
@@ -292,7 +309,9 @@ async function processPagesWithCache(pages, manifest, config, outDir, md, eta, n
|
|
|
292
309
|
}
|
|
293
310
|
// Run afterRender hook
|
|
294
311
|
if (config.hooks?.afterRender) {
|
|
312
|
+
const hookStart = performance.now();
|
|
295
313
|
await config.hooks.afterRender({ page, config });
|
|
314
|
+
recorder.addToPhase('hookAfterRenderTotalMs', performance.now() - hookStart);
|
|
296
315
|
}
|
|
297
316
|
}
|
|
298
317
|
// Display final rendering tree and clear it
|
|
@@ -347,11 +366,29 @@ async function generateBuildStats(pages, assetsCount, buildStartTime, outDir, ca
|
|
|
347
366
|
* Separated for cleaner error handling and testing.
|
|
348
367
|
*/
|
|
349
368
|
async function buildInternal(options = {}) {
|
|
369
|
+
// Date.now() is used for user-facing build duration display (wall-clock time, in milliseconds).
|
|
370
|
+
// Note: Date.now() can be affected by system clock changes and is not monotonic.
|
|
371
|
+
// Internal metrics use performance.now() via the MetricRecorder for higher precision and monotonic timing,
|
|
372
|
+
// which is not affected by system clock adjustments.
|
|
350
373
|
const buildStartTime = Date.now();
|
|
351
374
|
const logger = options.logger || defaultLogger;
|
|
375
|
+
// Create metric recorder (noop if disabled)
|
|
376
|
+
const recorder = createMetricRecorder({
|
|
377
|
+
enabled: options.metrics?.enabled,
|
|
378
|
+
detailed: options.metrics?.detailed,
|
|
379
|
+
command: 'build',
|
|
380
|
+
flags: {
|
|
381
|
+
force: options.force,
|
|
382
|
+
clean: options.clean,
|
|
383
|
+
includeDrafts: options.includeDrafts,
|
|
384
|
+
},
|
|
385
|
+
statiVersion: options.version,
|
|
386
|
+
});
|
|
352
387
|
logger.building('Building your site...');
|
|
353
|
-
// Load configuration
|
|
388
|
+
// Load configuration (instrumented)
|
|
389
|
+
const endConfigSpan = recorder.startSpan('configLoadMs');
|
|
354
390
|
const { config, outDir, cacheDir } = await loadAndValidateConfig(options);
|
|
391
|
+
endConfigSpan();
|
|
355
392
|
// Initialize cache stats
|
|
356
393
|
let cacheHits = 0;
|
|
357
394
|
let cacheMisses = 0;
|
|
@@ -384,17 +421,27 @@ async function buildInternal(options = {}) {
|
|
|
384
421
|
}
|
|
385
422
|
}
|
|
386
423
|
// Load cache manifest for ISG (after potential clean operation)
|
|
424
|
+
const endManifestLoadSpan = recorder.startSpan('cacheManifestLoadMs');
|
|
387
425
|
const { manifest } = await setupCacheAndManifest(cacheDir);
|
|
426
|
+
endManifestLoadSpan();
|
|
388
427
|
// Load content and build navigation
|
|
389
428
|
if (logger.step) {
|
|
390
429
|
console.log(); // Add spacing before content loading
|
|
391
430
|
}
|
|
431
|
+
const endContentSpan = recorder.startSpan('contentDiscoveryMs');
|
|
392
432
|
const { pages, navigation, md, eta, navigationHash } = await loadContentAndBuildNavigation(config, options, logger);
|
|
433
|
+
endContentSpan();
|
|
434
|
+
// Record content discovery counts.
|
|
435
|
+
// Both totalPages and markdownFilesProcessed are incremented by pages.length
|
|
436
|
+
// because loadContent() only processes *.md files, so all pages are markdown files.
|
|
437
|
+
recorder.increment('totalPages', pages.length);
|
|
438
|
+
recorder.increment('markdownFilesProcessed', pages.length);
|
|
393
439
|
// Store navigation hash in manifest for change detection in dev server
|
|
394
440
|
manifest.navigationHash = navigationHash;
|
|
395
441
|
// Compile TypeScript if enabled
|
|
396
442
|
let compiledBundles = [];
|
|
397
443
|
if (config.typescript?.enabled) {
|
|
444
|
+
const endTsSpan = recorder.startSpan('typescriptCompileMs');
|
|
398
445
|
compiledBundles = await compileTypeScript({
|
|
399
446
|
projectRoot: process.cwd(),
|
|
400
447
|
config: config.typescript,
|
|
@@ -402,15 +449,21 @@ async function buildInternal(options = {}) {
|
|
|
402
449
|
mode: getEnv() === 'production' ? 'production' : 'development',
|
|
403
450
|
logger,
|
|
404
451
|
});
|
|
452
|
+
endTsSpan();
|
|
405
453
|
}
|
|
406
454
|
// Process pages with ISG caching logic
|
|
407
455
|
if (logger.step) {
|
|
408
456
|
console.log(); // Add spacing before page processing
|
|
409
457
|
}
|
|
458
|
+
const endPageRenderSpan = recorder.startSpan('pageRenderingMs');
|
|
410
459
|
const buildTime = new Date();
|
|
411
|
-
const pageProcessingResult = await processPagesWithCache(pages, manifest, config, outDir, md, eta, navigation, buildTime, options, logger, compiledBundles);
|
|
460
|
+
const pageProcessingResult = await processPagesWithCache(pages, manifest, config, outDir, md, eta, navigation, buildTime, options, logger, compiledBundles, recorder);
|
|
461
|
+
endPageRenderSpan();
|
|
412
462
|
cacheHits = pageProcessingResult.cacheHits;
|
|
413
463
|
cacheMisses = pageProcessingResult.cacheMisses;
|
|
464
|
+
// Record page rendering counts
|
|
465
|
+
recorder.increment('renderedPages', cacheMisses);
|
|
466
|
+
recorder.increment('cachedPages', cacheHits);
|
|
414
467
|
// Write Tailwind class inventory after all templates have been rendered (if Tailwind is used)
|
|
415
468
|
if (hasTailwind) {
|
|
416
469
|
const inventorySize = getInventorySize();
|
|
@@ -423,10 +476,15 @@ async function buildInternal(options = {}) {
|
|
|
423
476
|
disableInventoryTracking();
|
|
424
477
|
}
|
|
425
478
|
// Save updated cache manifest
|
|
479
|
+
const endManifestSaveSpan = recorder.startSpan('cacheManifestSaveMs');
|
|
426
480
|
await saveCacheManifest(cacheDir, manifest);
|
|
481
|
+
endManifestSaveSpan();
|
|
427
482
|
// Copy static assets and count them
|
|
483
|
+
const endAssetSpan = recorder.startSpan('assetCopyMs');
|
|
428
484
|
let assetsCount = 0;
|
|
429
485
|
assetsCount = await copyStaticAssets(config, outDir, logger);
|
|
486
|
+
endAssetSpan();
|
|
487
|
+
recorder.increment('assetsCopied', assetsCount);
|
|
430
488
|
// Get current environment
|
|
431
489
|
const currentEnv = getEnv();
|
|
432
490
|
// Generate sitemap if enabled (only in production mode)
|
|
@@ -435,8 +493,10 @@ async function buildInternal(options = {}) {
|
|
|
435
493
|
console.log(); // Add spacing before sitemap generation
|
|
436
494
|
}
|
|
437
495
|
logger.info('Generating sitemap...');
|
|
496
|
+
const endSitemapSpan = recorder.startSpan('sitemapGenerationMs');
|
|
438
497
|
const sitemapResult = generateSitemap(pages, config, config.sitemap);
|
|
439
498
|
await writeFile(join(outDir, 'sitemap.xml'), sitemapResult.xml);
|
|
499
|
+
endSitemapSpan();
|
|
440
500
|
// Write additional sitemap files if split
|
|
441
501
|
if (sitemapResult.sitemaps) {
|
|
442
502
|
for (const { filename, xml } of sitemapResult.sitemaps) {
|
|
@@ -506,9 +566,30 @@ async function buildInternal(options = {}) {
|
|
|
506
566
|
}
|
|
507
567
|
// Run afterAll hook
|
|
508
568
|
if (config.hooks?.afterAll) {
|
|
569
|
+
const endHookAfterAll = recorder.startSpan('hookAfterAllMs');
|
|
509
570
|
await config.hooks.afterAll({ config, pages });
|
|
571
|
+
endHookAfterAll();
|
|
510
572
|
}
|
|
511
573
|
// Calculate build statistics
|
|
512
574
|
const buildStats = await generateBuildStats(pages, assetsCount, buildStartTime, outDir, cacheHits, cacheMisses, logger);
|
|
513
|
-
|
|
575
|
+
// Set ISG metrics
|
|
576
|
+
const totalPages = pages.length;
|
|
577
|
+
const cacheHitRate = totalPages > 0 ? cacheHits / totalPages : 0;
|
|
578
|
+
recorder.setISGMetrics({
|
|
579
|
+
enabled: config.isg?.enabled !== false,
|
|
580
|
+
cacheHitRate,
|
|
581
|
+
manifestEntries: Object.keys(manifest.entries).length,
|
|
582
|
+
invalidatedEntries: cacheMisses,
|
|
583
|
+
});
|
|
584
|
+
// Take final memory snapshot
|
|
585
|
+
recorder.snapshotMemory();
|
|
586
|
+
// Build result with optional metrics
|
|
587
|
+
const result = {
|
|
588
|
+
...buildStats,
|
|
589
|
+
};
|
|
590
|
+
// Add metrics if enabled
|
|
591
|
+
if (recorder.enabled) {
|
|
592
|
+
result.buildMetrics = recorder.finalize();
|
|
593
|
+
}
|
|
594
|
+
return result;
|
|
514
595
|
}
|
package/dist/core/dev.js
CHANGED
|
@@ -198,7 +198,7 @@ async function handleTemplateChange(templatePath, configPath, logger) {
|
|
|
198
198
|
return;
|
|
199
199
|
}
|
|
200
200
|
// Find pages that depend on this template
|
|
201
|
-
|
|
201
|
+
let affectedPagesCount = 0;
|
|
202
202
|
const normalizedTemplatePath = posix.normalize(templatePath.replace(/\\/g, '/'));
|
|
203
203
|
for (const [pagePath, entry] of Object.entries(cacheManifest.entries)) {
|
|
204
204
|
if (entry.deps.some((dep) => {
|
|
@@ -207,12 +207,12 @@ async function handleTemplateChange(templatePath, configPath, logger) {
|
|
|
207
207
|
return (normalizedDep === normalizedTemplatePath ||
|
|
208
208
|
normalizedDep.endsWith('/' + normalizedTemplatePath));
|
|
209
209
|
})) {
|
|
210
|
-
|
|
210
|
+
affectedPagesCount++;
|
|
211
211
|
// Remove from cache to force rebuild
|
|
212
212
|
delete cacheManifest.entries[pagePath];
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
|
-
if (
|
|
215
|
+
if (affectedPagesCount > 0) {
|
|
216
216
|
// Save updated cache manifest
|
|
217
217
|
await saveCacheManifest(cacheDir, cacheManifest);
|
|
218
218
|
// Perform incremental rebuild (only affected pages will be rebuilt)
|
package/dist/core/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Barrel file for all core Stati functionality including build, dev server, preview, and invalidation.
|
|
4
4
|
*/
|
|
5
5
|
export { build } from './build.js';
|
|
6
|
-
export type { BuildOptions } from './build.js';
|
|
6
|
+
export type { BuildOptions, MetricsOptions, BuildResult } from './build.js';
|
|
7
7
|
export { createDevServer } from './dev.js';
|
|
8
8
|
export type { DevServerOptions } from './dev.js';
|
|
9
9
|
export { createPreviewServer } from './preview.js';
|
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,MAAM,YAAY,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"}
|
package/dist/core/isg/ttl.js
CHANGED
|
@@ -209,7 +209,7 @@ function getPublishedDate(page) {
|
|
|
209
209
|
// Handle Date objects
|
|
210
210
|
if (value instanceof Date) {
|
|
211
211
|
const safeDate = getSafeCurrentTime(value);
|
|
212
|
-
if (
|
|
212
|
+
if (!isNaN(safeDate.getTime())) {
|
|
213
213
|
return safeDate;
|
|
214
214
|
}
|
|
215
215
|
}
|
package/dist/core/templates.d.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { Eta } from 'eta';
|
|
2
2
|
import type { StatiConfig, PageModel, NavNode } from '../types/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Result of rendering a page template.
|
|
5
|
+
*/
|
|
6
|
+
export interface RenderResult {
|
|
7
|
+
/** The rendered HTML content */
|
|
8
|
+
html: string;
|
|
9
|
+
/** Number of templates loaded (layout + partials) */
|
|
10
|
+
templatesLoaded: number;
|
|
11
|
+
}
|
|
3
12
|
export declare function createTemplateEngine(config: StatiConfig): Eta;
|
|
4
|
-
export declare function renderPage(page: PageModel, body: string, config: StatiConfig, eta: Eta, navigation?: NavNode[], allPages?: PageModel[], assets?: import('../types/index.js').StatiAssets): Promise<
|
|
13
|
+
export declare function renderPage(page: PageModel, body: string, config: StatiConfig, eta: Eta, navigation?: NavNode[], allPages?: PageModel[], assets?: import('../types/index.js').StatiAssets): Promise<RenderResult>;
|
|
5
14
|
//# sourceMappingURL=templates.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../src/core/templates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAG1B,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAkB,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../src/core/templates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAG1B,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAkB,MAAM,mBAAmB,CAAC;AAiBzF;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,eAAe,EAAE,MAAM,CAAC;CACzB;AAoMD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,WAAW,GAAG,GAAG,CAW7D;AAED,wBAAsB,UAAU,CAC9B,IAAI,EAAE,SAAS,EACf,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,WAAW,EACnB,GAAG,EAAE,GAAG,EACR,UAAU,CAAC,EAAE,OAAO,EAAE,EACtB,QAAQ,CAAC,EAAE,SAAS,EAAE,EACtB,MAAM,CAAC,EAAE,OAAO,mBAAmB,EAAE,WAAW,GAC/C,OAAO,CAAC,YAAY,CAAC,CAsLvB"}
|
package/dist/core/templates.js
CHANGED
|
@@ -187,6 +187,8 @@ export async function renderPage(page, body, config, eta, navigation, allPages,
|
|
|
187
187
|
const srcDir = resolveSrcDir(config);
|
|
188
188
|
const relativePath = relative(srcDir, page.sourcePath);
|
|
189
189
|
const partialPaths = await discoverPartials(relativePath, config);
|
|
190
|
+
// Count templates: partials + layout (if found)
|
|
191
|
+
const partialsCount = Object.keys(partialPaths).length;
|
|
190
192
|
// Build collection data based on page type
|
|
191
193
|
let collectionData;
|
|
192
194
|
const isIndexPage = allPages && isCollectionIndexPage(page, allPages);
|
|
@@ -203,6 +205,8 @@ export async function renderPage(page, body, config, eta, navigation, allPages,
|
|
|
203
205
|
// Discover the appropriate layout using hierarchical layout.eta convention
|
|
204
206
|
// Pass isIndexPage flag to enable index.eta lookup for aggregation pages
|
|
205
207
|
const layoutPath = await discoverLayout(relativePath, config, page.frontMatter.layout, isIndexPage);
|
|
208
|
+
// Calculate total templates loaded (partials + layout if present)
|
|
209
|
+
const templatesLoaded = partialsCount + (layoutPath ? 1 : 0);
|
|
206
210
|
// Create navigation helpers
|
|
207
211
|
const navTree = navigation || [];
|
|
208
212
|
const navHelpers = createNavigationHelpers(navTree, page);
|
|
@@ -235,17 +239,9 @@ export async function renderPage(page, body, config, eta, navigation, allPages,
|
|
|
235
239
|
const renderedPartials = {};
|
|
236
240
|
const maxPasses = 3; // Prevent infinite loops
|
|
237
241
|
for (let pass = 0; pass < maxPasses; pass++) {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
partialsToRender = { ...partialPaths };
|
|
242
|
-
}
|
|
243
|
-
else {
|
|
244
|
-
// Subsequent passes: re-render partials that might need updated dependencies
|
|
245
|
-
// For simplicity, re-render all partials to ensure they have access to all previously rendered ones
|
|
246
|
-
// TODO: Optimize by tracking which partials changed or have dependencies
|
|
247
|
-
partialsToRender = { ...partialPaths };
|
|
248
|
-
}
|
|
242
|
+
// Each pass renders all partials to ensure they have access to all previously rendered ones
|
|
243
|
+
// TODO: Optimize by tracking which partials changed or have dependencies
|
|
244
|
+
const partialsToRender = { ...partialPaths };
|
|
249
245
|
if (Object.keys(partialsToRender).length === 0) {
|
|
250
246
|
break;
|
|
251
247
|
}
|
|
@@ -311,9 +307,10 @@ export async function renderPage(page, body, config, eta, navigation, allPages,
|
|
|
311
307
|
try {
|
|
312
308
|
if (!layoutPath) {
|
|
313
309
|
console.warn('No layout template found, using fallback');
|
|
314
|
-
return createFallbackHtml(page, body);
|
|
310
|
+
return { html: createFallbackHtml(page, body), templatesLoaded };
|
|
315
311
|
}
|
|
316
|
-
|
|
312
|
+
const html = await eta.renderAsync(layoutPath, context);
|
|
313
|
+
return { html, templatesLoaded };
|
|
317
314
|
}
|
|
318
315
|
catch (error) {
|
|
319
316
|
console.error(`Error rendering layout ${layoutPath || 'unknown'}:`, error);
|
|
@@ -322,7 +319,7 @@ export async function renderPage(page, body, config, eta, navigation, allPages,
|
|
|
322
319
|
const templateError = createTemplateError(error instanceof Error ? error : new Error(String(error)), layoutPath || undefined);
|
|
323
320
|
throw templateError;
|
|
324
321
|
}
|
|
325
|
-
return createFallbackHtml(page, body);
|
|
322
|
+
return { html: createFallbackHtml(page, body), templatesLoaded };
|
|
326
323
|
}
|
|
327
324
|
}
|
|
328
325
|
function createFallbackHtml(page, body) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"glob-patterns.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/glob-patterns.utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"glob-patterns.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/glob-patterns.utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAgFnD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAQlE"}
|
|
@@ -39,13 +39,8 @@ export function globToRegex(pattern) {
|
|
|
39
39
|
regexStr += '(?:.*/)?';
|
|
40
40
|
i += 3;
|
|
41
41
|
}
|
|
42
|
-
else if (i + 2 === normalizedPattern.length) {
|
|
43
|
-
// ** at end - matches everything
|
|
44
|
-
regexStr += '.*';
|
|
45
|
-
i += 2;
|
|
46
|
-
}
|
|
47
42
|
else {
|
|
48
|
-
// ** in middle not followed by / - match everything remaining
|
|
43
|
+
// ** at end or in middle not followed by / - match everything remaining
|
|
49
44
|
regexStr += '.*';
|
|
50
45
|
i += 2;
|
|
51
46
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -23,8 +23,10 @@ export type { StatiConfig, PageModel, FrontMatter, BuildContext, PageContext, Bu
|
|
|
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
25
|
export type { RSSConfig, RSSFeedConfig, RSSGenerationResult } from './types/index.js';
|
|
26
|
-
export type { BuildOptions, DevServerOptions, PreviewServerOptions, InvalidationResult, } from './core/index.js';
|
|
26
|
+
export type { BuildOptions, MetricsOptions, BuildResult, DevServerOptions, PreviewServerOptions, InvalidationResult, } from './core/index.js';
|
|
27
27
|
export { build, createDevServer, createPreviewServer, invalidate } from './core/index.js';
|
|
28
|
+
export type { BuildMetrics, MetricsMeta, MetricsFlags, MetricsTotals, MetricsPhases, MetricsCounts, MetricsISG, PageTiming, IncrementalMetrics, MetricRecorder, MetricRecorderOptions, PhaseName, CounterName, } from './metrics/index.js';
|
|
29
|
+
export { createMetricRecorder, noopMetricRecorder, writeMetrics, formatMetricsSummary, } from './metrics/index.js';
|
|
28
30
|
export type { AutoInjectOptions } from './seo/index.js';
|
|
29
31
|
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
32
|
export type { RSSValidationResult } from './rss/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,EACV,YAAY,GACb,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"}
|
|
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,EACV,YAAY,GACb,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,cAAc,EACd,WAAW,EACX,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,EACV,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,aAAa,EACb,aAAa,EACb,aAAa,EACb,UAAU,EACV,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,qBAAqB,EACrB,SAAS,EACT,WAAW,GACZ,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,YAAY,EACZ,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAG5B,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
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
22
|
export { build, createDevServer, createPreviewServer, invalidate } from './core/index.js';
|
|
23
|
+
export { createMetricRecorder, noopMetricRecorder, writeMetrics, formatMetricsSummary, } from './metrics/index.js';
|
|
23
24
|
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
25
|
export { generateRSSFeed, generateRSSFeeds, validateRSSConfig, validateRSSFeedConfig, } from './rss/index.js';
|
|
25
26
|
// Re-export config and env utilities
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stati Build Metrics System
|
|
3
|
+
*
|
|
4
|
+
* Provides performance measurement and reporting for Stati builds.
|
|
5
|
+
* Uses Node.js built-ins only - no external dependencies.
|
|
6
|
+
*
|
|
7
|
+
* @example Basic Usage
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { createMetricRecorder, writeMetrics, formatMetricsSummary } from '@stati/core';
|
|
10
|
+
*
|
|
11
|
+
* // Create recorder (noop when disabled)
|
|
12
|
+
* const recorder = createMetricRecorder({ enabled: true });
|
|
13
|
+
*
|
|
14
|
+
* // Record phases with spans
|
|
15
|
+
* const endConfig = recorder.startSpan('configLoadMs');
|
|
16
|
+
* await loadConfig();
|
|
17
|
+
* endConfig();
|
|
18
|
+
*
|
|
19
|
+
* // Increment counters
|
|
20
|
+
* recorder.increment('renderedPages');
|
|
21
|
+
*
|
|
22
|
+
* // Finalize and write
|
|
23
|
+
* const metrics = recorder.finalize();
|
|
24
|
+
* await writeMetrics(metrics, { cacheDir: '.stati' });
|
|
25
|
+
*
|
|
26
|
+
* // Format for CLI
|
|
27
|
+
* const summary = formatMetricsSummary(metrics);
|
|
28
|
+
* summary.forEach(line => console.log(line));
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* @packageDocumentation
|
|
32
|
+
*/
|
|
33
|
+
export type { BuildMetrics, MetricsMeta, MetricsFlags, MetricsTotals, MetricsPhases, MetricsCounts, MetricsISG, PageTiming, IncrementalMetrics, MetricRecorder, MetricRecorderOptions, PhaseName, CounterName, GaugeName, } from './types.js';
|
|
34
|
+
export { createMetricRecorder } from './recorder.js';
|
|
35
|
+
export { noopMetricRecorder } from './noop.js';
|
|
36
|
+
export { writeMetrics, formatMetricsSummary, generateMetricsFilename, DEFAULT_METRICS_DIR, isCI, getGitCommit, getGitBranch, getCpuCount, getPlatform, getArch, getNodeVersion, getMemoryUsage, } from './utils/index.js';
|
|
37
|
+
export type { WriteMetricsOptions, WriteMetricsResult } from './utils/index.js';
|
|
38
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/metrics/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAGH,YAAY,EACV,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,aAAa,EACb,aAAa,EACb,aAAa,EACb,UAAU,EACV,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,qBAAqB,EACrB,SAAS,EACT,WAAW,EACX,SAAS,GACV,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAGrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAG/C,OAAO,EAEL,YAAY,EACZ,oBAAoB,EACpB,uBAAuB,EACvB,mBAAmB,EAEnB,IAAI,EACJ,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,WAAW,EACX,OAAO,EACP,cAAc,EACd,cAAc,GACf,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stati Build Metrics System
|
|
3
|
+
*
|
|
4
|
+
* Provides performance measurement and reporting for Stati builds.
|
|
5
|
+
* Uses Node.js built-ins only - no external dependencies.
|
|
6
|
+
*
|
|
7
|
+
* @example Basic Usage
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { createMetricRecorder, writeMetrics, formatMetricsSummary } from '@stati/core';
|
|
10
|
+
*
|
|
11
|
+
* // Create recorder (noop when disabled)
|
|
12
|
+
* const recorder = createMetricRecorder({ enabled: true });
|
|
13
|
+
*
|
|
14
|
+
* // Record phases with spans
|
|
15
|
+
* const endConfig = recorder.startSpan('configLoadMs');
|
|
16
|
+
* await loadConfig();
|
|
17
|
+
* endConfig();
|
|
18
|
+
*
|
|
19
|
+
* // Increment counters
|
|
20
|
+
* recorder.increment('renderedPages');
|
|
21
|
+
*
|
|
22
|
+
* // Finalize and write
|
|
23
|
+
* const metrics = recorder.finalize();
|
|
24
|
+
* await writeMetrics(metrics, { cacheDir: '.stati' });
|
|
25
|
+
*
|
|
26
|
+
* // Format for CLI
|
|
27
|
+
* const summary = formatMetricsSummary(metrics);
|
|
28
|
+
* summary.forEach(line => console.log(line));
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* @packageDocumentation
|
|
32
|
+
*/
|
|
33
|
+
// Recorder factory
|
|
34
|
+
export { createMetricRecorder } from './recorder.js';
|
|
35
|
+
// Noop recorder singleton
|
|
36
|
+
export { noopMetricRecorder } from './noop.js';
|
|
37
|
+
// Utilities (file writing, system info)
|
|
38
|
+
export {
|
|
39
|
+
// Writer utilities
|
|
40
|
+
writeMetrics, formatMetricsSummary, generateMetricsFilename, DEFAULT_METRICS_DIR,
|
|
41
|
+
// System utilities
|
|
42
|
+
isCI, getGitCommit, getGitBranch, getCpuCount, getPlatform, getArch, getNodeVersion, getMemoryUsage, } from './utils/index.js';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Noop MetricRecorder implementation.
|
|
3
|
+
* Used when metrics collection is disabled - has zero overhead.
|
|
4
|
+
* (Null Object Pattern)
|
|
5
|
+
*/
|
|
6
|
+
import type { MetricRecorder } from './types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Singleton noop recorder instance.
|
|
9
|
+
* Use this when metrics are disabled for zero-overhead operation.
|
|
10
|
+
*/
|
|
11
|
+
export declare const noopMetricRecorder: MetricRecorder;
|
|
12
|
+
//# sourceMappingURL=noop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"noop.d.ts","sourceRoot":"","sources":["../../src/metrics/noop.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,cAAc,EAOf,MAAM,YAAY,CAAC;AAiGpB;;;GAGG;AACH,eAAO,MAAM,kBAAkB,EAAE,cAAyC,CAAC"}
|