@stati/core 1.13.0 → 1.15.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.
@@ -74,8 +74,12 @@ export declare const DEFAULT_LOCALE = "en-US";
74
74
  export declare const DEFAULT_TS_SRC_DIR = "src";
75
75
  /** Default TypeScript output directory (relative to build output) */
76
76
  export declare const DEFAULT_TS_OUT_DIR = "_assets";
77
- /** Default TypeScript entry point filename */
78
- export declare const DEFAULT_TS_ENTRY_POINT = "main.ts";
79
- /** Default TypeScript bundle name */
80
- export declare const DEFAULT_TS_BUNDLE_NAME = "bundle";
77
+ /**
78
+ * Default bundle configuration when `bundles` is not specified.
79
+ * Creates a single global bundle from 'main.ts' entry point.
80
+ */
81
+ export declare const DEFAULT_BUNDLES: readonly [{
82
+ readonly entryPoint: "main.ts";
83
+ readonly bundleName: "main";
84
+ }];
81
85
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,iDAAiD;AACjD,eAAO,MAAM,eAAe,SAAS,CAAC;AAEtC,+CAA+C;AAC/C,eAAO,MAAM,eAAe,SAAS,CAAC;AAEtC,sCAAsC;AACtC,eAAO,MAAM,kBAAkB,WAAW,CAAC;AAE3C,iCAAiC;AACjC,eAAO,MAAM,cAAc,WAAW,CAAC;AAEvC,4DAA4D;AAC5D,eAAO,MAAM,YAAY,UAAU,CAAC;AAEpC,kCAAkC;AAClC,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AAIjD,qEAAqE;AACrE,eAAO,MAAM,sBAAsB,iCAAkC,CAAC;AAEtE,mCAAmC;AACnC,eAAO,MAAM,gBAAgB,iBAAiB,CAAC;AAE/C,2CAA2C;AAC3C,eAAO,MAAM,oBAAoB,UAEhC,CAAC;AAIF,sCAAsC;AACtC,eAAO,MAAM,gBAAgB,OAAO,CAAC;AAErC,kCAAkC;AAClC,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC,sCAAsC;AACtC,eAAO,MAAM,gBAAgB,cAAc,CAAC;AAE5C,0CAA0C;AAC1C,eAAO,MAAM,oBAAoB,0BAAmD,CAAC;AAIrF,wDAAwD;AACxD,eAAO,MAAM,mBAAmB,QAAQ,CAAC;AAEzC,+DAA+D;AAC/D,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C,qEAAqE;AACrE,eAAO,MAAM,wBAAwB,QAAQ,CAAC;AAI9C,4BAA4B;AAC5B,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC,0BAA0B;AAC1B,eAAO,MAAM,gBAAgB,QAA0B,CAAC;AAExD,yBAAyB;AACzB,eAAO,MAAM,eAAe,QAAwB,CAAC;AAErD,0BAA0B;AAC1B,eAAO,MAAM,gBAAgB,QAAsB,CAAC;AAEpD,8BAA8B;AAC9B,eAAO,MAAM,oBAAoB,QAAyB,CAAC;AAI3D,oDAAoD;AACpD,eAAO,MAAM,aAAa,QAAyB,CAAC;AAEpD,iDAAiD;AACjD,eAAO,MAAM,cAAc,QAA0B,CAAC;AAEtD,6CAA6C;AAC7C,eAAO,MAAM,UAAU,QAAmB,CAAC;AAE3C,iDAAiD;AACjD,eAAO,MAAM,WAAW,QAAuB,CAAC;AAEhD,uCAAuC;AACvC,eAAO,MAAM,WAAW,QAAsB,CAAC;AAE/C,2CAA2C;AAC3C,eAAO,MAAM,SAAS,QAAkB,CAAC;AAEzC,6CAA6C;AAC7C,eAAO,MAAM,UAAU,QAAmB,CAAC;AAI3C,kCAAkC;AAClC,eAAO,MAAM,kBAAkB,SAAS,CAAC;AAEzC,8BAA8B;AAC9B,eAAO,MAAM,kBAAkB,QAAQ,CAAC;AAExC,+BAA+B;AAC/B,eAAO,MAAM,eAAe,eAAgC,CAAC;AAE7D,mEAAmE;AACnE,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAIvC,yBAAyB;AACzB,eAAO,MAAM,kBAAkB,kBAAkB,CAAC;AAElD,8CAA8C;AAC9C,eAAO,MAAM,cAAc,UAAU,CAAC;AAItC,0CAA0C;AAC1C,eAAO,MAAM,kBAAkB,QAAQ,CAAC;AAExC,qEAAqE;AACrE,eAAO,MAAM,kBAAkB,YAAY,CAAC;AAE5C,8CAA8C;AAC9C,eAAO,MAAM,sBAAsB,YAAY,CAAC;AAEhD,qCAAqC;AACrC,eAAO,MAAM,sBAAsB,WAAW,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,iDAAiD;AACjD,eAAO,MAAM,eAAe,SAAS,CAAC;AAEtC,+CAA+C;AAC/C,eAAO,MAAM,eAAe,SAAS,CAAC;AAEtC,sCAAsC;AACtC,eAAO,MAAM,kBAAkB,WAAW,CAAC;AAE3C,iCAAiC;AACjC,eAAO,MAAM,cAAc,WAAW,CAAC;AAEvC,4DAA4D;AAC5D,eAAO,MAAM,YAAY,UAAU,CAAC;AAEpC,kCAAkC;AAClC,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AAIjD,qEAAqE;AACrE,eAAO,MAAM,sBAAsB,iCAAkC,CAAC;AAEtE,mCAAmC;AACnC,eAAO,MAAM,gBAAgB,iBAAiB,CAAC;AAE/C,2CAA2C;AAC3C,eAAO,MAAM,oBAAoB,UAEhC,CAAC;AAIF,sCAAsC;AACtC,eAAO,MAAM,gBAAgB,OAAO,CAAC;AAErC,kCAAkC;AAClC,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC,sCAAsC;AACtC,eAAO,MAAM,gBAAgB,cAAc,CAAC;AAE5C,0CAA0C;AAC1C,eAAO,MAAM,oBAAoB,0BAAmD,CAAC;AAIrF,wDAAwD;AACxD,eAAO,MAAM,mBAAmB,QAAQ,CAAC;AAEzC,+DAA+D;AAC/D,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C,qEAAqE;AACrE,eAAO,MAAM,wBAAwB,QAAQ,CAAC;AAI9C,4BAA4B;AAC5B,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC,0BAA0B;AAC1B,eAAO,MAAM,gBAAgB,QAA0B,CAAC;AAExD,yBAAyB;AACzB,eAAO,MAAM,eAAe,QAAwB,CAAC;AAErD,0BAA0B;AAC1B,eAAO,MAAM,gBAAgB,QAAsB,CAAC;AAEpD,8BAA8B;AAC9B,eAAO,MAAM,oBAAoB,QAAyB,CAAC;AAI3D,oDAAoD;AACpD,eAAO,MAAM,aAAa,QAAyB,CAAC;AAEpD,iDAAiD;AACjD,eAAO,MAAM,cAAc,QAA0B,CAAC;AAEtD,6CAA6C;AAC7C,eAAO,MAAM,UAAU,QAAmB,CAAC;AAE3C,iDAAiD;AACjD,eAAO,MAAM,WAAW,QAAuB,CAAC;AAEhD,uCAAuC;AACvC,eAAO,MAAM,WAAW,QAAsB,CAAC;AAE/C,2CAA2C;AAC3C,eAAO,MAAM,SAAS,QAAkB,CAAC;AAEzC,6CAA6C;AAC7C,eAAO,MAAM,UAAU,QAAmB,CAAC;AAI3C,kCAAkC;AAClC,eAAO,MAAM,kBAAkB,SAAS,CAAC;AAEzC,8BAA8B;AAC9B,eAAO,MAAM,kBAAkB,QAAQ,CAAC;AAExC,+BAA+B;AAC/B,eAAO,MAAM,eAAe,eAAgC,CAAC;AAE7D,mEAAmE;AACnE,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAIvC,yBAAyB;AACzB,eAAO,MAAM,kBAAkB,kBAAkB,CAAC;AAElD,8CAA8C;AAC9C,eAAO,MAAM,cAAc,UAAU,CAAC;AAItC,0CAA0C;AAC1C,eAAO,MAAM,kBAAkB,QAAQ,CAAC;AAExC,qEAAqE;AACrE,eAAO,MAAM,kBAAkB,YAAY,CAAC;AAE5C;;;GAGG;AACH,eAAO,MAAM,eAAe;;;EAA2D,CAAC"}
package/dist/constants.js CHANGED
@@ -83,7 +83,8 @@ export const DEFAULT_LOCALE = 'en-US';
83
83
  export const DEFAULT_TS_SRC_DIR = 'src';
84
84
  /** Default TypeScript output directory (relative to build output) */
85
85
  export const DEFAULT_TS_OUT_DIR = '_assets';
86
- /** Default TypeScript entry point filename */
87
- export const DEFAULT_TS_ENTRY_POINT = 'main.ts';
88
- /** Default TypeScript bundle name */
89
- export const DEFAULT_TS_BUNDLE_NAME = 'bundle';
86
+ /**
87
+ * Default bundle configuration when `bundles` is not specified.
88
+ * Creates a single global bundle from 'main.ts' entry point.
89
+ */
90
+ export const DEFAULT_BUNDLES = [{ entryPoint: 'main.ts', bundleName: 'main' }];
@@ -1 +1 @@
1
- {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/core/build.ts"],"names":[],"mappings":"AA8CA,OAAO,KAAK,EAEV,UAAU,EACV,MAAM,EAMP,MAAM,mBAAmB,CAAC;AAE3B;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,YAAY;IAC3B,iDAAiD;IACjD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,iDAAiD;IACjD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AA4FD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAsB,KAAK,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,CAW3E"}
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/core/build.ts"],"names":[],"mappings":"AA+CA,OAAO,KAAK,EAEV,UAAU,EACV,MAAM,EAMP,MAAM,mBAAmB,CAAC;AAE3B;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,YAAY;IAC3B,iDAAiD;IACjD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,iDAAiD;IACjD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AA4FD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAsB,KAAK,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,CAW3E"}
@@ -1,4 +1,4 @@
1
- import { ensureDir, writeFile, remove, pathExists, stat, readdir, copyFile, resolveOutDir, resolveStaticDir, resolveCacheDir, enableInventoryTracking, disableInventoryTracking, clearInventory, writeTailwindClassInventory, getInventorySize, isTailwindUsed, loadPreviousInventory, compileTypeScript, autoInjectBundle, } from './utils/index.js';
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
3
  import { loadConfig } from '../config/loader.js';
4
4
  import { loadContent } from './content.js';
@@ -9,7 +9,7 @@ import { loadCacheManifest, saveCacheManifest, shouldRebuildPage, createCacheEnt
9
9
  import { generateSitemap, generateRobotsTxtFromConfig, autoInjectSEO, } from '../seo/index.js';
10
10
  import { generateRSSFeeds, validateRSSConfig } from '../rss/index.js';
11
11
  import { getEnv } from '../env.js';
12
- import { DEFAULT_TS_OUT_DIR } from '../constants.js';
12
+ import { DEFAULT_OUT_DIR } from '../constants.js';
13
13
  /**
14
14
  * Recursively calculates the total size of a directory in bytes.
15
15
  * Used for build statistics.
@@ -184,7 +184,7 @@ async function loadContentAndBuildNavigation(config, options, logger) {
184
184
  /**
185
185
  * Processes pages with ISG caching logic.
186
186
  */
187
- async function processPagesWithCache(pages, manifest, config, outDir, md, eta, navigation, buildTime, options, logger, assets) {
187
+ async function processPagesWithCache(pages, manifest, config, outDir, md, eta, navigation, buildTime, options, logger, compiledBundles) {
188
188
  let cacheHits = 0;
189
189
  let cacheMisses = 0;
190
190
  // Build context
@@ -244,23 +244,15 @@ async function processPagesWithCache(pages, manifest, config, outDir, md, eta, n
244
244
  // Cache miss - need to rebuild
245
245
  cacheMisses++;
246
246
  const startTime = Date.now();
247
- // Add rendering substeps to tree
248
- const markdownId = `${pageId}-markdown`;
249
- const templateId = `${pageId}-template`;
250
- if (logger.addTreeNode) {
251
- logger.addTreeNode(pageId, markdownId, 'Processing Markdown', 'running');
252
- logger.addTreeNode(pageId, templateId, 'Applying Template', 'pending');
253
- }
254
247
  // Run beforeRender hook
255
248
  if (config.hooks?.beforeRender) {
256
249
  await config.hooks.beforeRender({ page, config });
257
250
  }
258
251
  // Render markdown to HTML
259
252
  const htmlContent = renderMarkdown(page.content, md);
260
- if (logger.updateTreeNode) {
261
- logger.updateTreeNode(markdownId, 'completed');
262
- logger.updateTreeNode(templateId, 'running');
263
- }
253
+ // Compute matched bundle paths for this page
254
+ const bundlePaths = getBundlePathsForPage(page.url, compiledBundles);
255
+ const assets = bundlePaths.length > 0 ? { bundlePaths } : undefined;
264
256
  // Render with template
265
257
  let finalHtml = await renderPage(page, htmlContent, config, eta, navigation, pages, assets);
266
258
  // Auto-inject SEO tags if enabled
@@ -276,13 +268,13 @@ async function processPagesWithCache(pages, manifest, config, outDir, md, eta, n
276
268
  }
277
269
  finalHtml = autoInjectSEO(finalHtml, injectOptions);
278
270
  }
279
- // Auto-inject TypeScript bundle script tag if available
280
- if (assets?.bundlePath) {
281
- finalHtml = autoInjectBundle(finalHtml, assets.bundlePath);
271
+ // Auto-inject TypeScript bundle script tags if available and autoInject is enabled (default: true)
272
+ const shouldAutoInject = config.typescript?.autoInject !== false;
273
+ if (shouldAutoInject && assets && assets.bundlePaths.length > 0) {
274
+ finalHtml = autoInjectBundles(finalHtml, assets.bundlePaths);
282
275
  }
283
276
  const renderTime = Date.now() - startTime;
284
277
  if (logger.updateTreeNode) {
285
- logger.updateTreeNode(templateId, 'completed');
286
278
  logger.updateTreeNode(pageId, 'completed', {
287
279
  timing: renderTime,
288
280
  url: page.url,
@@ -401,30 +393,22 @@ async function buildInternal(options = {}) {
401
393
  // Store navigation hash in manifest for change detection in dev server
402
394
  manifest.navigationHash = navigationHash;
403
395
  // Compile TypeScript if enabled
404
- let tsResult;
405
- let assets;
396
+ let compiledBundles = [];
406
397
  if (config.typescript?.enabled) {
407
- tsResult = await compileTypeScript({
398
+ compiledBundles = await compileTypeScript({
408
399
  projectRoot: process.cwd(),
409
400
  config: config.typescript,
410
- outDir: config.outDir || 'dist',
401
+ outDir: config.outDir || DEFAULT_OUT_DIR,
411
402
  mode: getEnv() === 'production' ? 'production' : 'development',
412
403
  logger,
413
404
  });
414
- if (tsResult?.bundleFilename) {
415
- const assetsDir = config.typescript.outDir || DEFAULT_TS_OUT_DIR;
416
- assets = {
417
- bundleName: tsResult.bundleFilename,
418
- bundlePath: posix.join('/', assetsDir, tsResult.bundleFilename),
419
- };
420
- }
421
405
  }
422
406
  // Process pages with ISG caching logic
423
407
  if (logger.step) {
424
408
  console.log(); // Add spacing before page processing
425
409
  }
426
410
  const buildTime = new Date();
427
- const pageProcessingResult = await processPagesWithCache(pages, manifest, config, outDir, md, eta, navigation, buildTime, options, logger, assets);
411
+ const pageProcessingResult = await processPagesWithCache(pages, manifest, config, outDir, md, eta, navigation, buildTime, options, logger, compiledBundles);
428
412
  cacheHits = pageProcessingResult.cacheHits;
429
413
  cacheMisses = pageProcessingResult.cacheMisses;
430
414
  // Write Tailwind class inventory after all templates have been rendered (if Tailwind is used)
@@ -432,6 +416,7 @@ async function buildInternal(options = {}) {
432
416
  const inventorySize = getInventorySize();
433
417
  if (inventorySize > 0) {
434
418
  await writeTailwindClassInventory(cacheDir);
419
+ logger.info('');
435
420
  logger.info(`📝 Generated Tailwind class inventory (${inventorySize} classes tracked)`);
436
421
  }
437
422
  // Disable inventory tracking after build
@@ -1 +1 @@
1
- {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/core/dev.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAe,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAsB7D,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;CACb;AA4SD,wBAAsB,eAAe,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,SAAS,CAAC,CAwaxF"}
1
+ {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/core/dev.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAe,MAAM,EAAE,MAAM,mBAAmB,CAAC;AA2B7D,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;CACb;AA4SD,wBAAsB,eAAe,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,SAAS,CAAC,CA+axF"}
package/dist/core/dev.js CHANGED
@@ -11,7 +11,7 @@ import { loadContent } from './content.js';
11
11
  import { buildNavigation } from './navigation.js';
12
12
  import { resolveDevPaths, resolveCacheDir, resolvePrettyUrl, createErrorOverlay, parseErrorDetails, TemplateError, createFallbackLogger, mergeServerOptions, createTypeScriptWatcher, } from './utils/index.js';
13
13
  import { setEnv, getEnv } from '../env.js';
14
- import { DEFAULT_DEV_PORT, DEFAULT_DEV_HOST, TEMPLATE_EXTENSION } from '../constants.js';
14
+ import { DEFAULT_DEV_PORT, DEFAULT_DEV_HOST, TEMPLATE_EXTENSION, DEFAULT_OUT_DIR, } from '../constants.js';
15
15
  /**
16
16
  * Loads and validates configuration for the dev server.
17
17
  */
@@ -291,7 +291,7 @@ export async function createDevServer(options = {}) {
291
291
  lastBuildError = error;
292
292
  };
293
293
  let watcher = null;
294
- let tsWatcher = null;
294
+ let tsWatchers = [];
295
295
  const isBuildingRef = { value: false };
296
296
  let isStopping = false;
297
297
  /**
@@ -523,15 +523,14 @@ export async function createDevServer(options = {}) {
523
523
  // TypeScript watcher setup (initial compilation is handled by performInitialBuild)
524
524
  if (config.typescript?.enabled) {
525
525
  try {
526
- // Start TypeScript watcher for hot reload
527
- tsWatcher = await createTypeScriptWatcher({
526
+ // Start TypeScript watchers for hot reload (one per bundle)
527
+ tsWatchers = await createTypeScriptWatcher({
528
528
  projectRoot: process.cwd(),
529
529
  config: config.typescript,
530
- outDir: config.outDir || 'dist',
531
- mode: 'development',
530
+ outDir: config.outDir || DEFAULT_OUT_DIR,
532
531
  logger,
533
- onRebuild: () => {
534
- logger.info?.('TypeScript recompiled, triggering reload...');
532
+ onRebuild: (_results, compileTimeMs) => {
533
+ logger.info?.(`⚡ TypeScript recompiled in ${compileTimeMs}ms`);
535
534
  // Broadcast reload to WebSocket clients
536
535
  if (wsServer) {
537
536
  wsServer.clients.forEach((client) => {
@@ -599,10 +598,17 @@ export async function createDevServer(options = {}) {
599
598
  await watcher.close();
600
599
  watcher = null;
601
600
  }
602
- // Clean up TypeScript watcher
603
- if (tsWatcher) {
604
- await tsWatcher.dispose();
605
- tsWatcher = null;
601
+ // Clean up TypeScript watchers
602
+ if (tsWatchers.length > 0) {
603
+ await Promise.all(tsWatchers.map(async (w) => {
604
+ try {
605
+ await w.dispose();
606
+ }
607
+ catch (error) {
608
+ logger.warning(`Failed to dispose TypeScript watcher: ${error}`);
609
+ }
610
+ }));
611
+ tsWatchers = [];
606
612
  }
607
613
  if (wsServer) {
608
614
  wsServer.close();
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Bundle matching utilities for per-page TypeScript bundle targeting.
3
+ * Determines which bundles should be included on each page based on glob patterns.
4
+ * @module core/utils/bundle-matching
5
+ */
6
+ import type { BundleConfig } from '../../types/config.js';
7
+ /**
8
+ * Error thrown when bundle configuration contains duplicate bundleNames.
9
+ */
10
+ export declare class DuplicateBundleNameError extends Error {
11
+ constructor(duplicates: string[]);
12
+ }
13
+ /**
14
+ * Validates that all bundles have unique bundleNames.
15
+ * Throws DuplicateBundleNameError if duplicates are found.
16
+ *
17
+ * @param bundles - Array of bundle configurations to validate
18
+ * @throws {DuplicateBundleNameError} When duplicate bundleNames are detected
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * validateUniqueBundleNames([
23
+ * { entryPoint: 'core.ts', bundleName: 'core' },
24
+ * { entryPoint: 'docs.ts', bundleName: 'docs' }
25
+ * ]); // OK
26
+ *
27
+ * validateUniqueBundleNames([
28
+ * { entryPoint: 'a.ts', bundleName: 'main' },
29
+ * { entryPoint: 'b.ts', bundleName: 'main' }
30
+ * ]); // Throws DuplicateBundleNameError
31
+ * ```
32
+ */
33
+ export declare function validateUniqueBundleNames(bundles: BundleConfig[]): void;
34
+ /**
35
+ * Compiled bundle info with resolved paths for matched bundles.
36
+ * Used to track bundle metadata after compilation.
37
+ */
38
+ export interface CompiledBundleInfo {
39
+ /** The original bundle configuration */
40
+ config: BundleConfig;
41
+ /** The compiled bundle filename (e.g., 'core-a1b2c3d4.js') */
42
+ filename: string;
43
+ /** The full path to the bundle (e.g., '/_assets/core-a1b2c3d4.js') */
44
+ path: string;
45
+ }
46
+ /**
47
+ * Determines which bundles should be included on a specific page.
48
+ * Filters bundles based on their include/exclude glob patterns.
49
+ *
50
+ * Matching logic:
51
+ * 1. If no `include` patterns: bundle is global (matches all pages)
52
+ * 2. If `include` patterns exist: page must match at least one pattern
53
+ * 3. If page matches any `exclude` pattern: bundle is excluded (exclude takes precedence)
54
+ * 4. Bundle order from config is preserved
55
+ *
56
+ * @param pageOutputPath - The page's output URL path (e.g., '/docs/api/hooks.html')
57
+ * @param bundles - Array of bundle configurations to filter
58
+ * @returns Array of bundles that match this page, in config order
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * const bundles: BundleConfig[] = [
63
+ * { entryPoint: 'core.ts', bundleName: 'core' }, // global
64
+ * { entryPoint: 'docs.ts', bundleName: 'docs', include: ['/docs/**'] },
65
+ * { entryPoint: 'playground.ts', bundleName: 'playground', include: ['/examples/**'], exclude: ['/examples/simple/**'] }
66
+ * ];
67
+ *
68
+ * matchBundlesForPage('/docs/api/hooks.html', bundles);
69
+ * // Returns: [{ entryPoint: 'core.ts', ... }, { entryPoint: 'docs.ts', ... }]
70
+ *
71
+ * matchBundlesForPage('/examples/advanced/demo.html', bundles);
72
+ * // Returns: [{ entryPoint: 'core.ts', ... }, { entryPoint: 'playground.ts', ... }]
73
+ *
74
+ * matchBundlesForPage('/examples/simple/basic.html', bundles);
75
+ * // Returns: [{ entryPoint: 'core.ts', ... }] - playground excluded
76
+ * ```
77
+ */
78
+ export declare function matchBundlesForPage(pageOutputPath: string, bundles: BundleConfig[]): BundleConfig[];
79
+ /**
80
+ * Gets bundle paths for a page from compiled bundle info.
81
+ * Filters compiled bundles based on page matching and returns only the paths.
82
+ *
83
+ * @param pageOutputPath - The page's output URL path (e.g., '/docs/api/hooks.html')
84
+ * @param compiledBundles - Array of compiled bundle info
85
+ * @returns Array of bundle paths matched for this page, in config order
86
+ *
87
+ * @example
88
+ * ```typescript
89
+ * const compiled: CompiledBundleInfo[] = [
90
+ * { config: { entryPoint: 'core.ts', bundleName: 'core' }, filename: 'core-abc.js', path: '/_assets/core-abc.js' },
91
+ * { config: { entryPoint: 'docs.ts', bundleName: 'docs', include: ['/docs/**'] }, filename: 'docs-def.js', path: '/_assets/docs-def.js' }
92
+ * ];
93
+ *
94
+ * getBundlePathsForPage('/docs/api/hooks.html', compiled);
95
+ * // Returns: ['/_assets/core-abc.js', '/_assets/docs-def.js']
96
+ * ```
97
+ */
98
+ export declare function getBundlePathsForPage(pageOutputPath: string, compiledBundles: CompiledBundleInfo[]): string[];
99
+ //# sourceMappingURL=bundle-matching.utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle-matching.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/bundle-matching.utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAG1D;;GAEG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;gBACrC,UAAU,EAAE,MAAM,EAAE;CAOjC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,IAAI,CAqBvE;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,wCAAwC;IACxC,MAAM,EAAE,YAAY,CAAC;IACrB,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,mBAAmB,CACjC,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,YAAY,EAAE,GACtB,YAAY,EAAE,CAyBhB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qBAAqB,CACnC,cAAc,EAAE,MAAM,EACtB,eAAe,EAAE,kBAAkB,EAAE,GACpC,MAAM,EAAE,CAaV"}
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Bundle matching utilities for per-page TypeScript bundle targeting.
3
+ * Determines which bundles should be included on each page based on glob patterns.
4
+ * @module core/utils/bundle-matching
5
+ */
6
+ import { matchesGlob } from './glob-patterns.utils.js';
7
+ /**
8
+ * Error thrown when bundle configuration contains duplicate bundleNames.
9
+ */
10
+ export class DuplicateBundleNameError extends Error {
11
+ constructor(duplicates) {
12
+ super(`Duplicate bundleName(s) found in configuration: ${duplicates.join(', ')}. ` +
13
+ 'Each bundle must have a unique bundleName.');
14
+ this.name = 'DuplicateBundleNameError';
15
+ }
16
+ }
17
+ /**
18
+ * Validates that all bundles have unique bundleNames.
19
+ * Throws DuplicateBundleNameError if duplicates are found.
20
+ *
21
+ * @param bundles - Array of bundle configurations to validate
22
+ * @throws {DuplicateBundleNameError} When duplicate bundleNames are detected
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * validateUniqueBundleNames([
27
+ * { entryPoint: 'core.ts', bundleName: 'core' },
28
+ * { entryPoint: 'docs.ts', bundleName: 'docs' }
29
+ * ]); // OK
30
+ *
31
+ * validateUniqueBundleNames([
32
+ * { entryPoint: 'a.ts', bundleName: 'main' },
33
+ * { entryPoint: 'b.ts', bundleName: 'main' }
34
+ * ]); // Throws DuplicateBundleNameError
35
+ * ```
36
+ */
37
+ export function validateUniqueBundleNames(bundles) {
38
+ if (!bundles || bundles.length === 0) {
39
+ return;
40
+ }
41
+ const seen = new Set();
42
+ const duplicates = [];
43
+ for (const bundle of bundles) {
44
+ if (seen.has(bundle.bundleName)) {
45
+ if (!duplicates.includes(bundle.bundleName)) {
46
+ duplicates.push(bundle.bundleName);
47
+ }
48
+ }
49
+ else {
50
+ seen.add(bundle.bundleName);
51
+ }
52
+ }
53
+ if (duplicates.length > 0) {
54
+ throw new DuplicateBundleNameError(duplicates);
55
+ }
56
+ }
57
+ /**
58
+ * Determines which bundles should be included on a specific page.
59
+ * Filters bundles based on their include/exclude glob patterns.
60
+ *
61
+ * Matching logic:
62
+ * 1. If no `include` patterns: bundle is global (matches all pages)
63
+ * 2. If `include` patterns exist: page must match at least one pattern
64
+ * 3. If page matches any `exclude` pattern: bundle is excluded (exclude takes precedence)
65
+ * 4. Bundle order from config is preserved
66
+ *
67
+ * @param pageOutputPath - The page's output URL path (e.g., '/docs/api/hooks.html')
68
+ * @param bundles - Array of bundle configurations to filter
69
+ * @returns Array of bundles that match this page, in config order
70
+ *
71
+ * @example
72
+ * ```typescript
73
+ * const bundles: BundleConfig[] = [
74
+ * { entryPoint: 'core.ts', bundleName: 'core' }, // global
75
+ * { entryPoint: 'docs.ts', bundleName: 'docs', include: ['/docs/**'] },
76
+ * { entryPoint: 'playground.ts', bundleName: 'playground', include: ['/examples/**'], exclude: ['/examples/simple/**'] }
77
+ * ];
78
+ *
79
+ * matchBundlesForPage('/docs/api/hooks.html', bundles);
80
+ * // Returns: [{ entryPoint: 'core.ts', ... }, { entryPoint: 'docs.ts', ... }]
81
+ *
82
+ * matchBundlesForPage('/examples/advanced/demo.html', bundles);
83
+ * // Returns: [{ entryPoint: 'core.ts', ... }, { entryPoint: 'playground.ts', ... }]
84
+ *
85
+ * matchBundlesForPage('/examples/simple/basic.html', bundles);
86
+ * // Returns: [{ entryPoint: 'core.ts', ... }] - playground excluded
87
+ * ```
88
+ */
89
+ export function matchBundlesForPage(pageOutputPath, bundles) {
90
+ if (!bundles || bundles.length === 0) {
91
+ return [];
92
+ }
93
+ // Normalize path to use forward slashes
94
+ const normalizedPath = pageOutputPath.replace(/\\/g, '/');
95
+ return bundles.filter((bundle) => {
96
+ // Check exclude patterns first (they take precedence)
97
+ if (bundle.exclude && bundle.exclude.length > 0) {
98
+ const matchesExclude = bundle.exclude.some((pattern) => matchesGlob(normalizedPath, pattern));
99
+ if (matchesExclude) {
100
+ return false;
101
+ }
102
+ }
103
+ // If no include patterns, bundle is global (matches all pages)
104
+ if (!bundle.include || bundle.include.length === 0) {
105
+ return true;
106
+ }
107
+ // Check if page matches any include pattern
108
+ return bundle.include.some((pattern) => matchesGlob(normalizedPath, pattern));
109
+ });
110
+ }
111
+ /**
112
+ * Gets bundle paths for a page from compiled bundle info.
113
+ * Filters compiled bundles based on page matching and returns only the paths.
114
+ *
115
+ * @param pageOutputPath - The page's output URL path (e.g., '/docs/api/hooks.html')
116
+ * @param compiledBundles - Array of compiled bundle info
117
+ * @returns Array of bundle paths matched for this page, in config order
118
+ *
119
+ * @example
120
+ * ```typescript
121
+ * const compiled: CompiledBundleInfo[] = [
122
+ * { config: { entryPoint: 'core.ts', bundleName: 'core' }, filename: 'core-abc.js', path: '/_assets/core-abc.js' },
123
+ * { config: { entryPoint: 'docs.ts', bundleName: 'docs', include: ['/docs/**'] }, filename: 'docs-def.js', path: '/_assets/docs-def.js' }
124
+ * ];
125
+ *
126
+ * getBundlePathsForPage('/docs/api/hooks.html', compiled);
127
+ * // Returns: ['/_assets/core-abc.js', '/_assets/docs-def.js']
128
+ * ```
129
+ */
130
+ export function getBundlePathsForPage(pageOutputPath, compiledBundles) {
131
+ if (!compiledBundles || compiledBundles.length === 0) {
132
+ return [];
133
+ }
134
+ const matchedConfigs = matchBundlesForPage(pageOutputPath, compiledBundles.map((b) => b.config));
135
+ // Map matched configs back to their paths, preserving order
136
+ const matchedNames = new Set(matchedConfigs.map((c) => c.bundleName));
137
+ return compiledBundles.filter((b) => matchedNames.has(b.config.bundleName)).map((b) => b.path);
138
+ }
@@ -18,7 +18,9 @@ export { createErrorOverlay, parseErrorDetails } from './error-overlay.utils.js'
18
18
  export type { ErrorDetails } from './error-overlay.utils.js';
19
19
  export { getStatiVersion } from './version.utils.js';
20
20
  export { globToRegex, matchesGlob } from './glob-patterns.utils.js';
21
+ export { matchBundlesForPage, getBundlePathsForPage, validateUniqueBundleNames, DuplicateBundleNameError, } from './bundle-matching.utils.js';
22
+ export type { CompiledBundleInfo } from './bundle-matching.utils.js';
21
23
  export { createFallbackLogger } from './logger.utils.js';
22
- export { compileTypeScript, createTypeScriptWatcher, compileStatiConfig, cleanupCompiledConfig, autoInjectBundle, } from './typescript.utils.js';
23
- export type { CompileOptions, CompileResult, WatchOptions } from './typescript.utils.js';
24
+ export { compileTypeScript, createTypeScriptWatcher, compileStatiConfig, cleanupCompiledConfig, autoInjectBundles, } from './typescript.utils.js';
25
+ export type { CompileOptions, WatchOptions } from './typescript.utils.js';
24
26
  //# 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,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,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAGzD,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,kBAAkB,EAClB,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,cAAc,EAAE,aAAa,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,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"}
@@ -28,7 +28,9 @@ export { createErrorOverlay, parseErrorDetails } from './error-overlay.utils.js'
28
28
  export { getStatiVersion } from './version.utils.js';
29
29
  // Glob pattern utilities
30
30
  export { globToRegex, matchesGlob } from './glob-patterns.utils.js';
31
+ // Bundle matching utilities
32
+ export { matchBundlesForPage, getBundlePathsForPage, validateUniqueBundleNames, DuplicateBundleNameError, } from './bundle-matching.utils.js';
31
33
  // Logger utilities
32
34
  export { createFallbackLogger } from './logger.utils.js';
33
35
  // TypeScript compilation utilities
34
- export { compileTypeScript, createTypeScriptWatcher, compileStatiConfig, cleanupCompiledConfig, autoInjectBundle, } from './typescript.utils.js';
36
+ export { compileTypeScript, createTypeScriptWatcher, compileStatiConfig, cleanupCompiledConfig, autoInjectBundles, } from './typescript.utils.js';