@stacksjs/stx 0.2.10 → 0.2.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/README.md +16 -11
  2. package/dist/a11y.d.ts +2 -1
  3. package/dist/analyzer.d.ts +4 -0
  4. package/dist/app-shell.d.ts +72 -0
  5. package/dist/app.d.ts +1 -0
  6. package/dist/broadcasting.d.ts +77 -0
  7. package/dist/build-assets.d.ts +32 -0
  8. package/dist/build-mode-detector.d.ts +26 -0
  9. package/dist/build.d.ts +31 -0
  10. package/dist/builtins/icon.d.ts +7 -0
  11. package/dist/builtins/index.d.ts +21 -0
  12. package/dist/builtins/stx-image.d.ts +2 -0
  13. package/dist/builtins/stx-link.d.ts +2 -0
  14. package/dist/builtins/stx-loading-indicator.d.ts +2 -0
  15. package/dist/bundle-analyzer/index.d.ts +2 -0
  16. package/dist/bundle-analyzer.js +20 -20
  17. package/dist/caching.d.ts +27 -0
  18. package/dist/cli.js +3942 -2608
  19. package/dist/client-helpers.d.ts +7 -3
  20. package/dist/client-script-bundler.d.ts +27 -0
  21. package/dist/client-script.d.ts +5 -1
  22. package/dist/client.d.ts +1 -0
  23. package/dist/component-hmr.d.ts +1 -0
  24. package/dist/component-processing.d.ts +107 -0
  25. package/dist/component-registry.d.ts +54 -0
  26. package/dist/component-renderer.d.ts +18 -0
  27. package/dist/components/StxImage.stx +12 -0
  28. package/dist/components/StxLink.stx +18 -8
  29. package/dist/composables/index.d.ts +7 -0
  30. package/dist/composables/use-broadcast-channel.d.ts +1 -0
  31. package/dist/composables/use-channel.d.ts +67 -0
  32. package/dist/composables/use-intersection-observer.d.ts +2 -0
  33. package/dist/composables/use-keyboard.d.ts +1 -0
  34. package/dist/composables/use-mouse.d.ts +1 -0
  35. package/dist/composables/use-router.d.ts +4 -0
  36. package/dist/composables/use-websocket.d.ts +1 -0
  37. package/dist/composables.d.ts +2 -0
  38. package/dist/composition-api.d.ts +1 -0
  39. package/dist/computed.d.ts +1 -0
  40. package/dist/config.d.ts +12 -1
  41. package/dist/craft-bridge.d.ts +6 -3
  42. package/dist/craft-components.d.ts +1 -0
  43. package/dist/craft.js +39 -33
  44. package/dist/deploy/index.d.ts +1 -0
  45. package/dist/dev-server/crosswind.d.ts +2 -5
  46. package/dist/dev-server/native-window.d.ts +19 -1
  47. package/dist/dev-server/serve-app.d.ts +15 -0
  48. package/dist/dev-server/serve-file.d.ts +3 -0
  49. package/dist/dev-server/serve-markdown.d.ts +3 -0
  50. package/dist/dev-server/serve-multi.d.ts +5 -0
  51. package/dist/dev-server/types.d.ts +2 -0
  52. package/dist/dev-server.d.ts +6 -40
  53. package/dist/devtools.d.ts +1 -0
  54. package/dist/directive-api.d.ts +1 -1
  55. package/dist/document-shell.d.ts +33 -0
  56. package/dist/error-boundaries.d.ts +1 -0
  57. package/dist/events.d.ts +1 -0
  58. package/dist/expressions.d.ts +38 -0
  59. package/dist/forms-validation.d.ts +2 -0
  60. package/dist/head.d.ts +1 -0
  61. package/dist/hot-reload.d.ts +1 -0
  62. package/dist/hydration-runtime.d.ts +1 -0
  63. package/dist/hydration.d.ts +1 -0
  64. package/dist/i18n.d.ts +3 -1
  65. package/dist/image-optimization/build-plugin.d.ts +1 -0
  66. package/dist/index.d.ts +22 -1
  67. package/dist/index.js +5571 -4182
  68. package/dist/inline-assets.d.ts +28 -0
  69. package/dist/internal-markdown.d.ts +0 -4
  70. package/dist/jsx-runtime.d.ts +1 -0
  71. package/dist/keep-alive.d.ts +1 -0
  72. package/dist/manifest.d.ts +41 -0
  73. package/dist/markdown.d.ts +1 -0
  74. package/dist/media/client/blur-up.d.ts +1 -0
  75. package/dist/media/client/lazy-load.d.ts +1 -0
  76. package/dist/media/client/upload-handler.d.ts +1 -0
  77. package/dist/media/shared/cache.d.ts +2 -0
  78. package/dist/media/video/index.d.ts +1 -0
  79. package/dist/media/video/processor/thumbnail.d.ts +1 -0
  80. package/dist/misc-directives.d.ts +49 -0
  81. package/dist/parser/directive-parser.d.ts +13 -0
  82. package/dist/performance-utils.d.ts +2 -0
  83. package/dist/placeholder.d.ts +45 -0
  84. package/dist/plugin-system.d.ts +32 -2
  85. package/dist/process.d.ts +11 -1
  86. package/dist/production-builder.d.ts +42 -0
  87. package/dist/production-server.d.ts +19 -0
  88. package/dist/props.d.ts +1 -0
  89. package/dist/pwa.js +3883 -2679
  90. package/dist/reactive.d.ts +2 -16
  91. package/dist/reactivity.d.ts +1 -0
  92. package/dist/render.d.ts +2 -1
  93. package/dist/router.d.ts +1 -1
  94. package/dist/runtime-injection.d.ts +20 -0
  95. package/dist/runtime.d.ts +2 -2
  96. package/dist/script-classifier.d.ts +35 -0
  97. package/dist/script-validation.d.ts +16 -0
  98. package/dist/server-bindings.d.ts +7 -0
  99. package/dist/server-components.d.ts +4 -0
  100. package/dist/signal-processing.d.ts +109 -0
  101. package/dist/signals-api.d.ts +309 -0
  102. package/dist/signals.d.ts +2 -309
  103. package/dist/spa-shell.d.ts +1 -0
  104. package/dist/ssg.js +3862 -2655
  105. package/dist/ssr.d.ts +1 -0
  106. package/dist/state-management.d.ts +27 -0
  107. package/dist/store-imports.d.ts +24 -0
  108. package/dist/store-loader.d.ts +16 -0
  109. package/dist/story/bun-test.d.ts +1 -0
  110. package/dist/story/compiled-output.d.ts +1 -0
  111. package/dist/story/interactions.d.ts +1 -0
  112. package/dist/story/renderer.d.ts +2 -0
  113. package/dist/story/setup.d.ts +1 -0
  114. package/dist/story/ui/code-panel.d.ts +1 -0
  115. package/dist/stx.d.ts +437 -0
  116. package/dist/style-scoping.d.ts +23 -0
  117. package/dist/template-compiler.d.ts +31 -0
  118. package/dist/template-hydrator.d.ts +18 -0
  119. package/dist/transitions.d.ts +1 -0
  120. package/dist/types/config-types.d.ts +62 -0
  121. package/dist/validator.d.ts +1 -0
  122. package/dist/variable-extractor.d.ts +1 -1
  123. package/dist/view-composers.d.ts +1 -0
  124. package/dist/virtual-scrolling.d.ts +1 -0
  125. package/dist/visual-testing.js +2 -2
  126. package/dist/web-components.d.ts +1 -0
  127. package/dist/x-element.d.ts +1 -0
  128. package/package.json +19 -7
  129. package/stx.d.ts +437 -0
package/README.md CHANGED
@@ -728,25 +728,30 @@ Web components support several configuration options:
728
728
 
729
729
  ## TypeScript Support
730
730
 
731
- stx includes TypeScript declarations for importing .stx files. Make sure your `tsconfig.json` includes the necessary configuration:
731
+ stx ships its TypeScript declarations as part of the package. As soon as
732
+ `@stacksjs/stx` is installed, TypeScript automatically picks up:
733
+
734
+ - `*.stx` and `*.md` module declarations (so `.stx` imports typecheck)
735
+ - Ambient runtime globals injected into `<script client>` blocks: `state`,
736
+ `derived`, `effect`, `batch`, `onMount`, `onDestroy`, `defineStore`,
737
+ `useStore`, `useHead`, `useSeoMeta`, `navigate`, `useRoute`, etc.
738
+ - The `window.stx` registry interface
739
+
740
+ You do **not** need to create your own `stx.d.ts`. Just include `.stx`
741
+ files in your `tsconfig.json`:
732
742
 
733
743
  ```jsonc
734
744
  {
735
745
  "compilerOptions": {
736
- // ... your other options
737
- "types": ["bun"]
746
+ "types": ["bun"],
747
+ "moduleResolution": "bundler"
738
748
  },
739
- "files": ["src/stx.d.ts"],
740
- "include": ["**/*.ts", "**/*.d.ts", "*.stx", "./**/*.stx"]
749
+ "include": ["**/*.ts", "**/*.stx"]
741
750
  }
742
751
  ```
743
752
 
744
- Create a declaration file (`src/stx.d.ts`):
745
-
746
- ```ts
747
- // Allow importing .stx files
748
- declare module '*.stx';
749
- ```
753
+ If you have an old `stx.d.ts` from a previous version declaring `*.stx`
754
+ or runtime globals, it's now redundant and can be deleted.
750
755
 
751
756
  ## Example Server
752
757
 
package/dist/a11y.d.ts CHANGED
@@ -19,7 +19,8 @@ export declare function getScreenReaderOnlyStyle(): string;
19
19
  * ```typescript
20
20
  * if (hasDOMSupport()) {
21
21
  * console.log('Using full DOM-based a11y checking')
22
- * } else {
22
+ * }
23
+ else {
23
24
  * console.log('Using regex-based a11y checking (limited)')
24
25
  * }
25
26
  * ```
@@ -5,10 +5,14 @@ export declare function analyzeTemplate(filePath: string): Promise<AnalysisResul
5
5
  /**
6
6
  * Analyze multiple templates
7
7
  */
8
+ // eslint-disable-next-line pickier/no-unused-vars
8
9
  export declare function analyzeProject(patterns?: string[], cwd?: string): Promise<{
9
10
  results: AnalysisResult[]
10
11
  summary: ProjectSummary
11
12
  }>;
13
+ /**
14
+ * stx Template Analyzer - Analyze templates for performance, best practices, and potential issues
15
+ */
12
16
  export declare interface AnalysisResult {
13
17
  file: string
14
18
  metrics: TemplateMetrics
@@ -0,0 +1,72 @@
1
+ import type { StxOptions } from './types';
2
+ /**
3
+ * Detect an app shell file in the given directory.
4
+ *
5
+ * @deprecated Use `layouts/default.stx` instead. The framework now auto-generates
6
+ * the document shell from `stx.config.ts` `app.head` configuration.
7
+ * The app.stx shell pattern is kept for backwards compatibility.
8
+ *
9
+ * Checks for `app.stx` in the project root (or configured shell path).
10
+ * Returns the absolute path if found, null otherwise.
11
+ */
12
+ export declare function detectShell(appDir: string, shellConfig?: string | false): string | null;
13
+ /**
14
+ * Process the app shell template and split it at the `<slot />` injection point.
15
+ *
16
+ * The shell is processed through `processDirectives()` with a placeholder
17
+ * for the slot, then split into before/after parts for fast page composition.
18
+ */
19
+ export declare function processShell(shellPath: string, options: StxOptions): Promise<ProcessedShell | null>;
20
+ /**
21
+ * Compose a full page by injecting page content into the processed shell.
22
+ *
23
+ * Wraps the page content in a document structure with the shell's layout,
24
+ * shell scripts, shell styles, and the router.
25
+ */
26
+ export declare function composeShellWithPage(shell: ProcessedShell, pageContent: string, pageTitle?: string): string;
27
+ /**
28
+ * Strip document wrapper (DOCTYPE, html, head, body) from page content,
29
+ * returning just the body content as a fragment.
30
+ *
31
+ * If the content has no document wrapper, it's returned as-is.
32
+ *
33
+ * Preserves:
34
+ * - `<script>` tags from `<body>` (page scripts)
35
+ * - `<style>` tags from `<head>` (page styles, moved to fragment)
36
+ * - Body content
37
+ *
38
+ * Strips:
39
+ * - `<!DOCTYPE>`, `<html>`, `<head>`, `<body>` wrappers
40
+ * - `<meta>`, `<title>`, `<link>` tags from `<head>`
41
+ */
42
+ export declare function stripDocumentWrapper(html: string): string;
43
+ /**
44
+ * Check if a request is an SPA navigation request (from the stx router).
45
+ */
46
+ export declare function isSpaNavigation(request: Request): boolean;
47
+ /**
48
+ * Extract the inner content of the router container element from a full HTML
49
+ * document, preserving any `<style>` tags found in `<head>`.
50
+ *
51
+ * This is what fragment files should contain — NOT the whole body. The SPA
52
+ * router injects fragments directly into the container element via innerHTML,
53
+ * so the fragment must already match the shape of the container's content.
54
+ * If you pass it the whole body (nav + main + footer), the router will dump
55
+ * all of that inside `<main>`, producing a duplicated nav.
56
+ *
57
+ * Falls back to `stripDocumentWrapper` if the container cannot be located.
58
+ *
59
+ * @param html - Full HTML document (with or without doctype/html/head/body)
60
+ * @param containerSelector - Tag name or selector for the router container (e.g. `main`)
61
+ */
62
+ export declare function extractContainerContent(html: string, containerSelector?: string): string;
63
+ /**
64
+ * Processed shell ready for page injection
65
+ */
66
+ export declare interface ProcessedShell {
67
+ beforeSlot: string
68
+ afterSlot: string
69
+ filePath: string
70
+ shellScripts: string[]
71
+ shellStyles: string[]
72
+ }
package/dist/app.d.ts CHANGED
@@ -61,4 +61,5 @@ export declare interface StxAppConfig {
61
61
  }
62
62
  }
63
63
  /** Plugin can be an object with install() or a plain function */
64
+ // eslint-disable-next-line pickier/no-unused-vars
64
65
  export type StxPluginInput = StxAppPlugin | ((app: StxApp, ...options: any[]) => void);
@@ -0,0 +1,77 @@
1
+ import { Broadcast, BroadcastServer, type ServerConfig } from 'ts-broadcasting';
2
+ import type { BroadcastingConfig } from './types/config-types';
3
+ export type {
4
+ ServerConfig,
5
+ } from 'ts-broadcasting';
6
+ /**
7
+ * Get the active BroadcastServer instance (or null if not started).
8
+ */
9
+ export declare function getBroadcastServer(): BroadcastServer | null;
10
+ /**
11
+ * Start the broadcasting WebSocket server.
12
+ *
13
+ * Called automatically by the stx dev-server when `broadcasting.enabled`
14
+ * is true in stx.config.ts. You normally don't need to call this yourself.
15
+ */
16
+ export declare function startBroadcasting(config: BroadcastingConfig): Promise<BroadcastServer>;
17
+ /**
18
+ * Stop the broadcasting server and clean up resources.
19
+ */
20
+ export declare function stopBroadcasting(): Promise<void>;
21
+ /**
22
+ * Load and execute a `routes/channels.ts` file, wiring up every
23
+ * channel definition to the running broadcasting server.
24
+ *
25
+ * Called automatically by the stx dev-server after `startBroadcasting()`.
26
+ */
27
+ export declare function loadChannels(channelsPath: string): Promise<number>;
28
+ /**
29
+ * Broadcast an event to a channel (or multiple channels).
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * broadcast('chat', 'message.sent', { text: 'Hello!' })
34
+ * broadcast(['chat', 'admin'], 'alert', { level: 'warn' })
35
+ * ```
36
+ */
37
+ export declare function broadcast<T = unknown>(channels: string | string[], event: string, data: T): void;
38
+ /**
39
+ * Broadcast to a specific user's private channel.
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * toUser(42, 'notification', { title: 'You have mail' })
44
+ * ```
45
+ */
46
+ export declare function toUser<T = unknown>(userId: string | number, event: string, data: T): void;
47
+ /**
48
+ * Broadcast to multiple users.
49
+ */
50
+ export declare function toUsers<T = unknown>(userIds: Array<string | number>, event: string, data: T): void;
51
+ /**
52
+ * Broadcast to a private channel.
53
+ */
54
+ export declare function broadcastPrivate<T = unknown>(channelName: string, event: string, data: T): void;
55
+ /**
56
+ * Broadcast to a presence channel.
57
+ */
58
+ export declare function broadcastPresence<T = unknown>(channelName: string, event: string, data: T): void;
59
+ // ── Types ──────────────────────────────────────────────────────
60
+ export declare interface ChannelDefinition<T = unknown> {
61
+ event?: string
62
+ interval?: number
63
+ data?: () => T | Promise<T>
64
+ authorize?: (socket: any, params: Record<string, string>) => boolean | object | Promise<boolean | object>
65
+ }
66
+ export declare interface ChannelRegistrar {
67
+ channel: <T = unknown>(name: string, definition: ChannelDefinition<T>) => void
68
+ }
69
+ /**
70
+ * Register channel authorization (ad-hoc, outside routes/channels.ts).
71
+ */
72
+ export { channel } from 'ts-broadcasting';
73
+ // ── Re-exports for advanced usage ──────────────────────────────
74
+ export {
75
+ Broadcast,
76
+ BroadcastServer,
77
+ } from 'ts-broadcasting';
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Generate a fingerprint hash from content.
3
+ * Uses SHA-256 truncated to 8 hex chars.
4
+ */
5
+ export declare function fingerprint(content: string): string;
6
+ /**
7
+ * Build the signals runtime as an external asset.
8
+ * This is identical for every page — no need to inline 20-40KB per page.
9
+ */
10
+ export declare function buildRuntimeAsset(debug?: boolean): BuiltAsset;
11
+ /**
12
+ * Build the SPA router script as an external asset.
13
+ * Identical for every page — ~5-8KB.
14
+ */
15
+ export declare function buildRouterAsset(): BuiltAsset;
16
+ /**
17
+ * Generates `<script src>` and `<link href>` tags for externalized assets.
18
+ * Used in build mode to replace inline scripts with references.
19
+ */
20
+ export declare function assetTags(assets: { runtime?: BuiltAsset, router?: BuiltAsset, css?: BuiltAsset }): {
21
+ runtimeTag: string
22
+ routerTag: string
23
+ cssTag: string
24
+ };
25
+ /**
26
+ * A built asset with content and fingerprinted filename.
27
+ */
28
+ export declare interface BuiltAsset {
29
+ content: string
30
+ filename: string
31
+ hash: string
32
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Scan all `.stx` files in the project and determine the build mode.
3
+ *
4
+ * - Zero `<script server>` tags → `'ssg'` (static site, flat HTML in `dist/`)
5
+ * - Any `<script server>` tag → `'ssr'` (server bundle in `.output/`)
6
+ */
7
+ export declare function detectBuildMode(root?: string): Promise<BuildModeDetectionResult>;
8
+ /**
9
+ * Validate that components don't mix `<script server>` and `<script client>`.
10
+ *
11
+ * Components should be server OR client — compose them via props/slots.
12
+ * Pages are allowed to have both (e.g. `<script server>` for SEO +
13
+ * `<script client>` for interactivity).
14
+ */
15
+ export declare function validateComponentScripts(root?: string): Promise<ComponentValidationError[]>;
16
+ export declare interface BuildModeDetectionResult {
17
+ mode: BuildMode
18
+ serverScriptFiles: string[]
19
+ totalFilesScanned: number
20
+ scanDurationMs: number
21
+ }
22
+ export declare interface ComponentValidationError {
23
+ file: string
24
+ message: string
25
+ }
26
+ export type BuildMode = 'ssg' | 'ssr';
@@ -0,0 +1,31 @@
1
+ import type { BuildMode } from './build-mode-detector';
2
+ import type { ProductionBuildOptions, ProductionBuildResult } from './production-builder';
3
+ import type { SSGConfig, SSGResult } from './ssg';
4
+ /**
5
+ * Unified build entry point for stx applications.
6
+ *
7
+ * Build mode is determined by the `ssr` field in `stx.config.ts`:
8
+ *
9
+ * - **`ssr: false`** (default) → SSG → `dist/` (flat HTML files for S3 + CloudFront).
10
+ * `<script server>` blocks are executed at build time and their output is
11
+ * baked into the static HTML. The server block is stripped from the output.
12
+ *
13
+ * - **`ssr: true`** → SSR → `.output/` (server bundle for EC2 behind CloudFront).
14
+ * `<script server>` blocks run per-request on the Bun server.
15
+ *
16
+ * `<script server>` works in both modes — the `ssr` flag controls *when* it
17
+ * runs (build time vs request time), not whether it's allowed.
18
+ */
19
+ export declare function buildApp(options?: UnifiedBuildOptions): Promise<UnifiedBuildResult>;
20
+ export declare interface UnifiedBuildOptions {
21
+ root?: string
22
+ debug?: boolean
23
+ ssg?: Partial<SSGConfig>
24
+ ssr?: Partial<ProductionBuildOptions>
25
+ }
26
+ export declare interface UnifiedBuildResult {
27
+ mode: BuildMode
28
+ ssg?: SSGResult
29
+ ssr?: ProductionBuildResult
30
+ duration: number
31
+ }
@@ -0,0 +1,7 @@
1
+ import type { BuiltinComponentDef } from '../component-registry';
2
+ /**
3
+ * Pre-load icon collections so they're available synchronously during render.
4
+ * Call this before template processing starts.
5
+ */
6
+ export declare function preloadIconCollection(prefix?: string): Promise<void>;
7
+ export declare const IconBuiltin: BuiltinComponentDef;
@@ -0,0 +1,21 @@
1
+ import { IconBuiltin } from './icon';
2
+ import { StxImageBuiltin } from './stx-image';
3
+ import { StxLinkBuiltin } from './stx-link';
4
+ import { StxLoadingIndicatorBuiltin } from './stx-loading-indicator';
5
+ /**
6
+ * Register all builtin components with the singleton registry.
7
+ * Call this once during framework initialization.
8
+ */
9
+ export declare function registerBuiltins(): void;
10
+ /**
11
+ * Builtin Components Barrel
12
+ *
13
+ * Re-exports all builtin component definitions and provides a
14
+ * convenience function to register them with the component registry.
15
+ *
16
+ * @module builtins
17
+ */
18
+ export { StxLinkBuiltin } from './stx-link';
19
+ export { StxImageBuiltin } from './stx-image';
20
+ export { StxLoadingIndicatorBuiltin } from './stx-loading-indicator';
21
+ export { IconBuiltin, preloadIconCollection } from './icon';
@@ -0,0 +1,2 @@
1
+ import type { BuiltinComponentDef } from '../component-registry';
2
+ export declare const StxImageBuiltin: BuiltinComponentDef;
@@ -0,0 +1,2 @@
1
+ import type { BuiltinComponentDef } from '../component-registry';
2
+ export declare const StxLinkBuiltin: BuiltinComponentDef;
@@ -0,0 +1,2 @@
1
+ import type { BuiltinComponentDef } from '../component-registry';
2
+ export declare const StxLoadingIndicatorBuiltin: BuiltinComponentDef;
@@ -4,6 +4,7 @@ import { generateTreemap, openInBrowser, type TreemapOptions } from './treemap';
4
4
  /**
5
5
  * Analyze a bundle directory and generate a report
6
6
  */
7
+ // eslint-disable-next-line pickier/no-unused-vars
7
8
  export declare function analyze(options?: AnalyzeOptions): Promise<{
8
9
  stats: ReturnType<typeof collectBundleStats> extends Promise<infer T> ? T : never
9
10
  report: string
@@ -13,6 +14,7 @@ export declare function analyze(options?: AnalyzeOptions): Promise<{
13
14
  /**
14
15
  * Quick summary of bundle size
15
16
  */
17
+ // eslint-disable-next-line pickier/no-unused-vars
16
18
  export declare function getBundleSummary(directory: string): Promise<{
17
19
  totalSize: number
18
20
  totalSizeFormatted: string
@@ -1,12 +1,12 @@
1
1
  // @bun
2
- import v from"fs";import H from"path";import{gzipSync as z}from"zlib";import{createHash as g}from"crypto";var D={".js":"js",".mjs":"js",".cjs":"js",".ts":"js",".jsx":"js",".tsx":"js",".css":"css",".scss":"css",".sass":"css",".less":"css",".html":"html",".htm":"html",".png":"image",".jpg":"image",".jpeg":"image",".gif":"image",".svg":"image",".webp":"image",".avif":"image",".ico":"image",".woff":"font",".woff2":"font",".ttf":"font",".eot":"font",".otf":"font"},b=[/node_modules/,/vendor/,/bower_components/,/\.min\./],B=[/index\.(js|ts|html)$/,/main\.(js|ts)$/,/app\.(js|ts)$/,/entry\.(js|ts)$/];async function L(Z,$={}){let{includeSourceMaps:X=!1,parseImports:j=!0,detectDuplicates:U=!0,topModulesCount:Q=20}=$;if(!v.existsSync(Z))throw Error(`Build directory not found: ${Z}`);if(!v.statSync(Z).isDirectory())throw Error(`Not a directory: ${Z}`);let K=await k(Z,X),J=[],F=[];for(let A of K)try{let O=await f(A,Z,j);J.push(O)}catch(O){F.push(`Failed to process ${A}: ${O}`)}let Y=c(J,Z),V=J.reduce((A,O)=>A+O.size,0),_=J.reduce((A,O)=>A+O.gzipSize,0),w=m(J),G=U?p(J):[],C=[...J].sort((A,O)=>O.size-A.size).slice(0,Q);return{timestamp:Date.now(),buildDir:H.resolve(Z),chunks:Y,totalSize:V,totalGzipSize:_,moduleCount:J.length,duplicates:G,byType:w,largestModules:C,warnings:F}}async function k(Z,$){let X=[];async function j(U){let Q=await v.promises.readdir(U,{withFileTypes:!0});for(let q of Q){let K=H.join(U,q.name);if(q.isDirectory()){if(!q.name.startsWith(".")&&q.name!=="node_modules")await j(K)}else if(q.isFile()){if(!$&&q.name.endsWith(".map"))continue;X.push(K)}}}return await j(Z),X}async function f(Z,$,X){let j=await v.promises.readFile(Z),U=H.relative($,Z),Q=H.extname(Z).toLowerCase(),q=j.length,K=y(j),J=h(j,Q),F=g("md5").update(j).digest("hex").slice(0,8),Y=D[Q]||"other",V=[];if(X&&Y==="js")V.push(...d(j.toString("utf-8")));return{id:U,path:U,size:q,gzipSize:K,parsedSize:J,extension:Q,type:Y,imports:V,importedBy:[],hash:F}}function y(Z){try{let $=z(Z,{level:9}).length;return Math.min($,Z.length)}catch{return Z.length}}function h(Z,$){if($.includes(".min.")||Z.length<1000)return Z.length;let j=D[$];if(j==="js")return Math.round(Z.length*0.6);if(j==="css")return Math.round(Z.length*0.7);return Z.length}function d(Z){let $=[],X=/import\s+(?:[\w*{}\s,]+\s+from\s+)?['"]([^'"]+)['"]/g,j;while((j=X.exec(Z))!==null)$.push(j[1]);let U=/import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;while((j=U.exec(Z))!==null)$.push(j[1]);let Q=/require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;while((j=Q.exec(Z))!==null)$.push(j[1]);return[...new Set($)]}function c(Z,$){let X=new Map;for(let U of Z){let Q=U.path.split(H.sep),q;if(Q.length===1)q=H.basename(U.path,U.extension);else q=Q[0];let K=X.get(q)||[];K.push(U),X.set(q,K)}let j=[];for(let[U,Q]of X){let q=Q.reduce((Y,V)=>Y+V.size,0),K=Q.reduce((Y,V)=>Y+V.gzipSize,0),J=Q.some((Y)=>B.some((V)=>V.test(Y.path))),F=Q.some((Y)=>b.some((V)=>V.test(Y.path)));j.push({name:U,files:Q.map((Y)=>Y.path),modules:Q,size:q,gzipSize:K,isEntry:J,isVendor:F})}return j.sort((U,Q)=>Q.size-U.size)}function m(Z){let $={js:{count:0,size:0,gzipSize:0},css:{count:0,size:0,gzipSize:0},html:{count:0,size:0,gzipSize:0},image:{count:0,size:0,gzipSize:0},font:{count:0,size:0,gzipSize:0},other:{count:0,size:0,gzipSize:0}};for(let X of Z)$[X.type].count++,$[X.type].size+=X.size,$[X.type].gzipSize+=X.gzipSize;return $}function p(Z){let $=new Map;for(let j of Z){let U=$.get(j.hash)||[];U.push(j),$.set(j.hash,U)}let X=[];for(let[,j]of $)if(j.length>1){let U=j[0];X.push({path:U.path,count:j.length,wastedBytes:U.size*(j.length-1),locations:j.map((Q)=>Q.path)})}return X.sort((j,U)=>U.wastedBytes-j.wastedBytes)}function W(Z){if(Z<1024)return`${Z} B`;if(Z<1048576)return`${(Z/1024).toFixed(2)} KB`;return`${(Z/1048576).toFixed(2)} MB`}function I(Z){let $=Z.match(/^([\d.]+)\s*(B|KB|MB|GB)?$/i);if(!$)return 0;let X=Number.parseFloat($[1]);switch(($[2]||"B").toUpperCase()){case"KB":return X*1024;case"MB":return X*1024*1024;case"GB":return X*1024*1024*1024;default:return X}}function E(Z,$){if($===0)return"0%";return`${(Z/$*100).toFixed(1)}%`}function x(Z,$){switch($.format){case"json":return u(Z,$);case"markdown":return l(Z,$);case"text":default:return r(Z,$)}}function u(Z,$){let X=N(Z,$);return JSON.stringify({timestamp:Z.timestamp,buildDir:Z.buildDir,summary:{totalSize:Z.totalSize,totalSizeFormatted:W(Z.totalSize),totalGzipSize:Z.totalGzipSize,totalGzipSizeFormatted:W(Z.totalGzipSize),moduleCount:Z.moduleCount,chunkCount:Z.chunks.length,duplicateCount:Z.duplicates.length},byType:Object.fromEntries(Object.entries(Z.byType).map(([j,U])=>[j,{...U,sizeFormatted:W(U.size),gzipSizeFormatted:W(U.gzipSize)}])),chunks:Z.chunks.map((j)=>({name:j.name,size:j.size,sizeFormatted:W(j.size),gzipSize:j.gzipSize,gzipSizeFormatted:W(j.gzipSize),moduleCount:j.modules.length,isEntry:j.isEntry,isVendor:j.isVendor})),largestModules:Z.largestModules.slice(0,$.topModules||10).map((j)=>({path:j.path,size:j.size,sizeFormatted:W(j.size),gzipSize:j.gzipSize,gzipSizeFormatted:W(j.gzipSize),type:j.type})),duplicates:Z.duplicates,analysis:X,warnings:Z.warnings},null,2)}function r(Z,$){let X=[],j=$.showGzip!==!1;if(X.push(""),X.push("Bundle Analysis Report"),X.push("\u2550".repeat(50)),X.push(""),X.push("Summary"),X.push("\u2500".repeat(50)),j)X.push(`Total Size: ${W(Z.totalSize)} (${W(Z.totalGzipSize)} gzip)`);else X.push(`Total Size: ${W(Z.totalSize)}`);if(X.push(`Modules: ${Z.moduleCount}`),X.push(`Chunks: ${Z.chunks.length}`),Z.duplicates.length>0)X.push(`Duplicates: ${Z.duplicates.length}`);if(X.push(""),$.threshold&&Z.totalSize>$.threshold)X.push(`\u26A0\uFE0F Bundle exceeds threshold of ${W($.threshold)}!`),X.push("");X.push("Size by Type"),X.push("\u2500".repeat(50));let U=["js","css","html","image","font","other"];for(let q of U){let K=Z.byType[q];if(K.count>0){let J=E(K.size,Z.totalSize),F=j?` (${W(K.gzipSize)} gzip)`:"";X.push(` ${q.padEnd(8)} ${W(K.size).padStart(12)}${F} ${J.padStart(6)} (${K.count} files)`)}}X.push(""),X.push("Chunks"),X.push("\u2500".repeat(50));for(let q of Z.chunks.slice(0,10)){let K=[];if(q.isEntry)K.push("entry");if(q.isVendor)K.push("vendor");let J=K.length>0?` [${K.join(", ")}]`:"",F=j?` (${W(q.gzipSize)} gzip)`:"";X.push(` ${q.name.padEnd(20)} ${W(q.size).padStart(12)}${F}${J}`)}if(Z.chunks.length>10)X.push(` ... and ${Z.chunks.length-10} more chunks`);X.push("");let Q=$.topModules||10;X.push(`Largest Modules (Top ${Q})`),X.push("\u2500".repeat(50));for(let q=0;q<Math.min(Q,Z.largestModules.length);q++){let K=Z.largestModules[q],J=j?` (${W(K.gzipSize)} gzip)`:"";X.push(` ${(q+1).toString().padStart(2)}. ${K.path}`),X.push(` ${W(K.size)}${J}`)}if(X.push(""),Z.duplicates.length>0){X.push("Duplicate Modules"),X.push("\u2500".repeat(50));for(let q of Z.duplicates.slice(0,5))X.push(` ${q.path}`),X.push(` ${q.count} copies, wasting ${W(q.wastedBytes)}`);if(Z.duplicates.length>5)X.push(` ... and ${Z.duplicates.length-5} more duplicates`);X.push("")}if($.recommendations!==!1){let q=N(Z,$);if(q.recommendations.length>0){X.push("Recommendations"),X.push("\u2500".repeat(50));for(let K of q.recommendations.slice(0,5)){let J=K.impact==="high"?"\u26A1":K.impact==="medium"?"\uD83D\uDCCA":"\uD83D\uDCA1";X.push(` ${J} ${K.title}`),X.push(` ${K.description}`)}X.push("")}}if(Z.warnings.length>0){X.push("Warnings"),X.push("\u2500".repeat(50));for(let q of Z.warnings)X.push(` \u26A0\uFE0F ${q}`);X.push("")}return X.join(`
3
- `)}function l(Z,$){let X=[],j=$.showGzip!==!1;if(X.push("# Bundle Analysis Report"),X.push(""),X.push(`Generated: ${new Date(Z.timestamp).toISOString()}`),X.push(""),X.push("## Summary"),X.push(""),X.push("| Metric | Value |"),X.push("|--------|-------|"),X.push(`| Total Size | ${W(Z.totalSize)} |`),j)X.push(`| Gzip Size | ${W(Z.totalGzipSize)} |`);X.push(`| Modules | ${Z.moduleCount} |`),X.push(`| Chunks | ${Z.chunks.length} |`),X.push(`| Duplicates | ${Z.duplicates.length} |`),X.push(""),X.push("## Size by Type"),X.push(""),X.push("| Type | Size | Gzip | % | Files |"),X.push("|------|------|------|---|-------|");let U=["js","css","html","image","font","other"];for(let q of U){let K=Z.byType[q];if(K.count>0)X.push(`| ${q} | ${W(K.size)} | ${W(K.gzipSize)} | ${E(K.size,Z.totalSize)} | ${K.count} |`)}X.push(""),X.push("## Chunks"),X.push(""),X.push("| Name | Size | Gzip | Modules | Type |"),X.push("|------|------|------|---------|------|");for(let q of Z.chunks.slice(0,15)){let K=q.isEntry?"Entry":q.isVendor?"Vendor":"-";X.push(`| ${q.name} | ${W(q.size)} | ${W(q.gzipSize)} | ${q.modules.length} | ${K} |`)}X.push("");let Q=$.topModules||10;X.push(`## Largest Modules (Top ${Q})`),X.push(""),X.push("| # | Path | Size | Gzip |"),X.push("|---|------|------|------|");for(let q=0;q<Math.min(Q,Z.largestModules.length);q++){let K=Z.largestModules[q];X.push(`| ${q+1} | \`${K.path}\` | ${W(K.size)} | ${W(K.gzipSize)} |`)}if(X.push(""),Z.duplicates.length>0){X.push("## Duplicates"),X.push(""),X.push("| Path | Copies | Wasted |"),X.push("|------|--------|--------|");for(let q of Z.duplicates.slice(0,10))X.push(`| \`${q.path}\` | ${q.count} | ${W(q.wastedBytes)} |`);X.push("")}if($.recommendations!==!1){let q=N(Z,$);if(q.recommendations.length>0){X.push("## Recommendations"),X.push("");for(let K of q.recommendations)X.push(`### ${K.title}`),X.push(""),X.push(K.description),X.push(""),X.push(`- **Impact:** ${K.impact}`),X.push(`- **Effort:** ${K.effort}`),X.push("")}}return X.join(`
4
- `)}function N(Z,$){let X=[],j=[];if($.threshold&&Z.totalSize>$.threshold)X.push({severity:"error",message:`Bundle size (${W(Z.totalSize)}) exceeds threshold (${W($.threshold)})`});if(Z.totalSize>1048576)X.push({severity:"warning",message:`Bundle is over 1MB (${W(Z.totalSize)})`,details:"Large bundles can significantly impact load time"});if(Z.duplicates.length>0){let K=Z.duplicates.reduce((J,F)=>J+F.wastedBytes,0);X.push({severity:"warning",message:`${Z.duplicates.length} duplicate modules found, wasting ${W(K)}`}),j.push({title:"Remove Duplicate Modules",description:"Consider using import aliases or ensuring consistent package versions to eliminate duplicates.",impact:"medium",effort:"low"})}if(Z.byType.js.size/Z.totalSize>0.8&&Z.totalSize>512000)j.push({title:"Code Splitting",description:"Over 80% of your bundle is JavaScript. Consider splitting code by route or feature.",impact:"high",effort:"medium"});let Q=Z.largestModules.filter((K)=>K.size>102400);if(Q.length>0){X.push({severity:"info",message:`${Q.length} modules are over 100KB`});for(let K of Q){if(K.path.includes("lodash")&&!K.path.includes("lodash-es"))j.push({title:"Use lodash-es instead of lodash",description:"lodash-es supports tree-shaking and can significantly reduce bundle size.",impact:"high",effort:"low"});if(K.path.includes("moment"))j.push({title:"Replace moment.js",description:"Consider using date-fns or dayjs for smaller date handling libraries.",impact:"high",effort:"medium"})}}if(Z.byType.image.size>512000)j.push({title:"Optimize Images",description:`Images account for ${W(Z.byType.image.size)}. Consider using WebP/AVIF formats and responsive images.`,impact:"high",effort:"low"});let q=100;if(Z.totalSize>1048576)q-=20;else if(Z.totalSize>512000)q-=10;if(q-=Math.min(20,Z.duplicates.length*2),q-=Math.min(15,Q.length*3),$.threshold&&Z.totalSize>$.threshold)q-=25;return{score:Math.max(0,q),issues:X,recommendations:j}}var i={lodash:{alternative:"lodash-es or individual imports",savings:"~70%"},moment:{alternative:"date-fns or dayjs",savings:"~90%"},"chart.js":{alternative:"chart.js with tree-shaking",savings:"~50%"},"highlight.js":{alternative:"Selective language imports",savings:"~80%"},fontawesome:{alternative:"Individual icon imports",savings:"~90%"},"material-ui":{alternative:"Named imports from @mui/*",savings:"~60%"}};function n(Z){for(let[$,X]of Object.entries(i))if(Z.includes($))return{title:`Optimize ${$}`,description:`Consider using ${X.alternative}. Potential savings: ${X.savings}`,impact:"high",effort:"low"};return null}var o={default:{js:"#f7df1e",css:"#264de4",html:"#e34c26",image:"#4caf50",font:"#9c27b0",other:"#607d8b",vendor:"#ff5722"},pastel:{js:"#fff3b0",css:"#b0d4ff",html:"#ffb0b0",image:"#b0ffb0",font:"#e0b0ff",other:"#d0d0d0",vendor:"#ffcfb0"},monochrome:{js:"#333333",css:"#555555",html:"#777777",image:"#999999",font:"#bbbbbb",other:"#dddddd",vendor:"#444444"}};function R(Z,$={}){let{title:X="Bundle Analysis",gzip:j=!0,colorScheme:U="default"}=$,Q=o[U],q=a(Z),K=JSON.stringify(q);return`<!DOCTYPE html>
2
+ import v from"fs";import H from"path";import{gzipSync as z}from"zlib";import{createHash as g}from"crypto";var D={".js":"js",".mjs":"js",".cjs":"js",".ts":"js",".jsx":"js",".tsx":"js",".css":"css",".scss":"css",".sass":"css",".less":"css",".html":"html",".htm":"html",".png":"image",".jpg":"image",".jpeg":"image",".gif":"image",".svg":"image",".webp":"image",".avif":"image",".ico":"image",".woff":"font",".woff2":"font",".ttf":"font",".eot":"font",".otf":"font"},b=[/node_modules/,/vendor/,/bower_components/,/\.min\./],B=[/index\.(js|ts|html)$/,/main\.(js|ts)$/,/app\.(js|ts)$/,/entry\.(js|ts)$/];async function L(X,Z={}){let{includeSourceMaps:Q=!1,parseImports:$=!0,detectDuplicates:U=!0,topModulesCount:K=20}=Z;if(!v.existsSync(X))throw Error(`Build directory not found: ${X}`);if(!v.statSync(X).isDirectory())throw Error(`Not a directory: ${X}`);let q=await k(X,Q),J=[],F=[];for(let A of q)try{let O=await f(A,X,$);J.push(O)}catch(O){F.push(`Failed to process ${A}: ${O}`)}let Y=c(J,X),V=J.reduce((A,O)=>A+O.size,0),_=J.reduce((A,O)=>A+O.gzipSize,0),w=m(J),G=U?p(J):[],C=[...J].sort((A,O)=>O.size-A.size).slice(0,K);return{timestamp:Date.now(),buildDir:H.resolve(X),chunks:Y,totalSize:V,totalGzipSize:_,moduleCount:J.length,duplicates:G,byType:w,largestModules:C,warnings:F}}async function k(X,Z){let Q=[];async function $(U){let K=await v.promises.readdir(U,{withFileTypes:!0});for(let j of K){let q=H.join(U,j.name);if(j.isDirectory()){if(!j.name.startsWith(".")&&j.name!=="node_modules")await $(q)}else if(j.isFile()){if(!Z&&j.name.endsWith(".map"))continue;Q.push(q)}}}return await $(X),Q}async function f(X,Z,Q){let $=await v.promises.readFile(X),U=H.relative(Z,X),K=H.extname(X).toLowerCase(),j=$.length,q=y($),J=h($,K),F=g("md5").update($).digest("hex").slice(0,8),Y=D[K]||"other",V=[];if(Q&&Y==="js")V.push(...d($.toString("utf-8")));return{id:U,path:U,size:j,gzipSize:q,parsedSize:J,extension:K,type:Y,imports:V,importedBy:[],hash:F}}function y(X){try{let Z=z(X,{level:9}).length;return Math.min(Z,X.length)}catch{return X.length}}function h(X,Z){if(Z.includes(".min.")||X.length<1000)return X.length;let $=D[Z];if($==="js")return Math.round(X.length*0.6);if($==="css")return Math.round(X.length*0.7);return X.length}function d(X){let Z=[],Q=/import\s+(?:[\w*{}\s,]+\s+from\s+)?['"]([^'"]+)['"]/g,$;while(($=Q.exec(X))!==null)Z.push($[1]);let U=/import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;while(($=U.exec(X))!==null)Z.push($[1]);let K=/require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;while(($=K.exec(X))!==null)Z.push($[1]);return[...new Set(Z)]}function c(X,Z){let Q=new Map;for(let U of X){let K=U.path.split(H.sep),j;if(K.length===1)j=H.basename(U.path,U.extension);else j=K[0];let q=Q.get(j)||[];q.push(U),Q.set(j,q)}let $=[];for(let[U,K]of Q){let j=K.reduce((Y,V)=>Y+V.size,0),q=K.reduce((Y,V)=>Y+V.gzipSize,0),J=K.some((Y)=>B.some((V)=>V.test(Y.path))),F=K.some((Y)=>b.some((V)=>V.test(Y.path)));$.push({name:U,files:K.map((Y)=>Y.path),modules:K,size:j,gzipSize:q,isEntry:J,isVendor:F})}return $.sort((U,K)=>K.size-U.size)}function m(X){let Z={js:{count:0,size:0,gzipSize:0},css:{count:0,size:0,gzipSize:0},html:{count:0,size:0,gzipSize:0},image:{count:0,size:0,gzipSize:0},font:{count:0,size:0,gzipSize:0},other:{count:0,size:0,gzipSize:0}};for(let Q of X)Z[Q.type].count++,Z[Q.type].size+=Q.size,Z[Q.type].gzipSize+=Q.gzipSize;return Z}function p(X){let Z=new Map;for(let $ of X){let U=Z.get($.hash)||[];U.push($),Z.set($.hash,U)}let Q=[];for(let[,$]of Z)if($.length>1){let U=$[0];Q.push({path:U.path,count:$.length,wastedBytes:U.size*($.length-1),locations:$.map((K)=>K.path)})}return Q.sort(($,U)=>U.wastedBytes-$.wastedBytes)}function W(X){if(X<1024)return`${X} B`;if(X<1048576)return`${(X/1024).toFixed(2)} KB`;return`${(X/1048576).toFixed(2)} MB`}function I(X){let Z=X.match(/^([\d.]+)\s*(B|KB|MB|GB)?$/i);if(!Z)return 0;let Q=Number.parseFloat(Z[1]);switch((Z[2]||"B").toUpperCase()){case"KB":return Q*1024;case"MB":return Q*1024*1024;case"GB":return Q*1024*1024*1024;default:return Q}}function E(X,Z){if(Z===0)return"0%";return`${(X/Z*100).toFixed(1)}%`}function R(X,Z){switch(Z.format){case"json":return u(X,Z);case"markdown":return l(X,Z);case"text":default:return r(X,Z)}}function u(X,Z){let Q=N(X,Z);return JSON.stringify({timestamp:X.timestamp,buildDir:X.buildDir,summary:{totalSize:X.totalSize,totalSizeFormatted:W(X.totalSize),totalGzipSize:X.totalGzipSize,totalGzipSizeFormatted:W(X.totalGzipSize),moduleCount:X.moduleCount,chunkCount:X.chunks.length,duplicateCount:X.duplicates.length},byType:Object.fromEntries(Object.entries(X.byType).map(([$,U])=>[$,{...U,sizeFormatted:W(U.size),gzipSizeFormatted:W(U.gzipSize)}])),chunks:X.chunks.map(($)=>({name:$.name,size:$.size,sizeFormatted:W($.size),gzipSize:$.gzipSize,gzipSizeFormatted:W($.gzipSize),moduleCount:$.modules.length,isEntry:$.isEntry,isVendor:$.isVendor})),largestModules:X.largestModules.slice(0,Z.topModules||10).map(($)=>({path:$.path,size:$.size,sizeFormatted:W($.size),gzipSize:$.gzipSize,gzipSizeFormatted:W($.gzipSize),type:$.type})),duplicates:X.duplicates,analysis:Q,warnings:X.warnings},null,2)}function r(X,Z){let Q=[],$=Z.showGzip!==!1;if(Q.push(""),Q.push("Bundle Analysis Report"),Q.push("\u2550".repeat(50)),Q.push(""),Q.push("Summary"),Q.push("\u2500".repeat(50)),$)Q.push(`Total Size: ${W(X.totalSize)} (${W(X.totalGzipSize)} gzip)`);else Q.push(`Total Size: ${W(X.totalSize)}`);if(Q.push(`Modules: ${X.moduleCount}`),Q.push(`Chunks: ${X.chunks.length}`),X.duplicates.length>0)Q.push(`Duplicates: ${X.duplicates.length}`);if(Q.push(""),Z.threshold&&X.totalSize>Z.threshold)Q.push(`\u26A0\uFE0F Bundle exceeds threshold of ${W(Z.threshold)}!`),Q.push("");Q.push("Size by Type"),Q.push("\u2500".repeat(50));let U=["js","css","html","image","font","other"];for(let j of U){let q=X.byType[j];if(q.count>0){let J=E(q.size,X.totalSize),F=$?` (${W(q.gzipSize)} gzip)`:"";Q.push(` ${j.padEnd(8)} ${W(q.size).padStart(12)}${F} ${J.padStart(6)} (${q.count} files)`)}}Q.push(""),Q.push("Chunks"),Q.push("\u2500".repeat(50));for(let j of X.chunks.slice(0,10)){let q=[];if(j.isEntry)q.push("entry");if(j.isVendor)q.push("vendor");let J=q.length>0?` [${q.join(", ")}]`:"",F=$?` (${W(j.gzipSize)} gzip)`:"";Q.push(` ${j.name.padEnd(20)} ${W(j.size).padStart(12)}${F}${J}`)}if(X.chunks.length>10)Q.push(` ... and ${X.chunks.length-10} more chunks`);Q.push("");let K=Z.topModules||10;Q.push(`Largest Modules (Top ${K})`),Q.push("\u2500".repeat(50));for(let j=0;j<Math.min(K,X.largestModules.length);j++){let q=X.largestModules[j],J=$?` (${W(q.gzipSize)} gzip)`:"";Q.push(` ${(j+1).toString().padStart(2)}. ${q.path}`),Q.push(` ${W(q.size)}${J}`)}if(Q.push(""),X.duplicates.length>0){Q.push("Duplicate Modules"),Q.push("\u2500".repeat(50));for(let j of X.duplicates.slice(0,5))Q.push(` ${j.path}`),Q.push(` ${j.count} copies, wasting ${W(j.wastedBytes)}`);if(X.duplicates.length>5)Q.push(` ... and ${X.duplicates.length-5} more duplicates`);Q.push("")}if(Z.recommendations!==!1){let j=N(X,Z);if(j.recommendations.length>0){Q.push("Recommendations"),Q.push("\u2500".repeat(50));for(let q of j.recommendations.slice(0,5)){let J=q.impact==="high"?"\u26A1":q.impact==="medium"?"\uD83D\uDCCA":"\uD83D\uDCA1";Q.push(` ${J} ${q.title}`),Q.push(` ${q.description}`)}Q.push("")}}if(X.warnings.length>0){Q.push("Warnings"),Q.push("\u2500".repeat(50));for(let j of X.warnings)Q.push(` \u26A0\uFE0F ${j}`);Q.push("")}return Q.join(`
3
+ `)}function l(X,Z){let Q=[],$=Z.showGzip!==!1;if(Q.push("# Bundle Analysis Report"),Q.push(""),Q.push(`Generated: ${new Date(X.timestamp).toISOString()}`),Q.push(""),Q.push("## Summary"),Q.push(""),Q.push("| Metric | Value |"),Q.push("|--------|-------|"),Q.push(`| Total Size | ${W(X.totalSize)} |`),$)Q.push(`| Gzip Size | ${W(X.totalGzipSize)} |`);Q.push(`| Modules | ${X.moduleCount} |`),Q.push(`| Chunks | ${X.chunks.length} |`),Q.push(`| Duplicates | ${X.duplicates.length} |`),Q.push(""),Q.push("## Size by Type"),Q.push(""),Q.push("| Type | Size | Gzip | % | Files |"),Q.push("|------|------|------|---|-------|");let U=["js","css","html","image","font","other"];for(let j of U){let q=X.byType[j];if(q.count>0)Q.push(`| ${j} | ${W(q.size)} | ${W(q.gzipSize)} | ${E(q.size,X.totalSize)} | ${q.count} |`)}Q.push(""),Q.push("## Chunks"),Q.push(""),Q.push("| Name | Size | Gzip | Modules | Type |"),Q.push("|------|------|------|---------|------|");for(let j of X.chunks.slice(0,15)){let q=j.isEntry?"Entry":j.isVendor?"Vendor":"-";Q.push(`| ${j.name} | ${W(j.size)} | ${W(j.gzipSize)} | ${j.modules.length} | ${q} |`)}Q.push("");let K=Z.topModules||10;Q.push(`## Largest Modules (Top ${K})`),Q.push(""),Q.push("| # | Path | Size | Gzip |"),Q.push("|---|------|------|------|");for(let j=0;j<Math.min(K,X.largestModules.length);j++){let q=X.largestModules[j];Q.push(`| ${j+1} | \`${q.path}\` | ${W(q.size)} | ${W(q.gzipSize)} |`)}if(Q.push(""),X.duplicates.length>0){Q.push("## Duplicates"),Q.push(""),Q.push("| Path | Copies | Wasted |"),Q.push("|------|--------|--------|");for(let j of X.duplicates.slice(0,10))Q.push(`| \`${j.path}\` | ${j.count} | ${W(j.wastedBytes)} |`);Q.push("")}if(Z.recommendations!==!1){let j=N(X,Z);if(j.recommendations.length>0){Q.push("## Recommendations"),Q.push("");for(let q of j.recommendations)Q.push(`### ${q.title}`),Q.push(""),Q.push(q.description),Q.push(""),Q.push(`- **Impact:** ${q.impact}`),Q.push(`- **Effort:** ${q.effort}`),Q.push("")}}return Q.join(`
4
+ `)}function N(X,Z){let Q=[],$=[];if(Z.threshold&&X.totalSize>Z.threshold)Q.push({severity:"error",message:`Bundle size (${W(X.totalSize)}) exceeds threshold (${W(Z.threshold)})`});if(X.totalSize>1048576)Q.push({severity:"warning",message:`Bundle is over 1MB (${W(X.totalSize)})`,details:"Large bundles can significantly impact load time"});if(X.duplicates.length>0){let q=X.duplicates.reduce((J,F)=>J+F.wastedBytes,0);Q.push({severity:"warning",message:`${X.duplicates.length} duplicate modules found, wasting ${W(q)}`}),$.push({title:"Remove Duplicate Modules",description:"Consider using import aliases or ensuring consistent package versions to eliminate duplicates.",impact:"medium",effort:"low"})}if(X.byType.js.size/X.totalSize>0.8&&X.totalSize>512000)$.push({title:"Code Splitting",description:"Over 80% of your bundle is JavaScript. Consider splitting code by route or feature.",impact:"high",effort:"medium"});let K=X.largestModules.filter((q)=>q.size>102400);if(K.length>0){Q.push({severity:"info",message:`${K.length} modules are over 100KB`});for(let q of K){if(q.path.includes("lodash")&&!q.path.includes("lodash-es"))$.push({title:"Use lodash-es instead of lodash",description:"lodash-es supports tree-shaking and can significantly reduce bundle size.",impact:"high",effort:"low"});if(q.path.includes("moment"))$.push({title:"Replace moment.js",description:"Consider using date-fns or dayjs for smaller date handling libraries.",impact:"high",effort:"medium"})}}if(X.byType.image.size>512000)$.push({title:"Optimize Images",description:`Images account for ${W(X.byType.image.size)}. Consider using WebP/AVIF formats and responsive images.`,impact:"high",effort:"low"});let j=100;if(X.totalSize>1048576)j-=20;else if(X.totalSize>512000)j-=10;if(j-=Math.min(20,X.duplicates.length*2),j-=Math.min(15,K.length*3),Z.threshold&&X.totalSize>Z.threshold)j-=25;return{score:Math.max(0,j),issues:Q,recommendations:$}}var i={lodash:{alternative:"lodash-es or individual imports",savings:"~70%"},moment:{alternative:"date-fns or dayjs",savings:"~90%"},"chart.js":{alternative:"chart.js with tree-shaking",savings:"~50%"},"highlight.js":{alternative:"Selective language imports",savings:"~80%"},fontawesome:{alternative:"Individual icon imports",savings:"~90%"},"material-ui":{alternative:"Named imports from @mui/*",savings:"~60%"}};function n(X){for(let[Z,Q]of Object.entries(i))if(X.includes(Z))return{title:`Optimize ${Z}`,description:`Consider using ${Q.alternative}. Potential savings: ${Q.savings}`,impact:"high",effort:"low"};return null}var o={default:{js:"#f7df1e",css:"#264de4",html:"#e34c26",image:"#4caf50",font:"#9c27b0",other:"#607d8b",vendor:"#ff5722"},pastel:{js:"#fff3b0",css:"#b0d4ff",html:"#ffb0b0",image:"#b0ffb0",font:"#e0b0ff",other:"#d0d0d0",vendor:"#ffcfb0"},monochrome:{js:"#333333",css:"#555555",html:"#777777",image:"#999999",font:"#bbbbbb",other:"#dddddd",vendor:"#444444"}};function x(X,Z={}){let{title:Q="Bundle Analysis",gzip:$=!0,colorScheme:U="default"}=Z,K=o[U],j=a(X),q=JSON.stringify(j);return`<!DOCTYPE html>
5
5
  <html lang="en">
6
6
  <head>
7
7
  <meta charset="UTF-8">
8
8
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
9
- <title>${M(X)}</title>
9
+ <title>${M(Q)}</title>
10
10
  <style>
11
11
  * {
12
12
  box-sizing: border-box;
@@ -212,22 +212,22 @@ import v from"fs";import H from"path";import{gzipSync as z}from"zlib";import{cre
212
212
  </head>
213
213
  <body>
214
214
  <div class="header">
215
- <h1>${M(X)}</h1>
215
+ <h1>${M(Q)}</h1>
216
216
  <div class="stats">
217
217
  <div class="stat">
218
- <div class="stat-value" id="total-size">${W(Z.totalSize)}</div>
218
+ <div class="stat-value" id="total-size">${W(X.totalSize)}</div>
219
219
  <div class="stat-label">Total Size</div>
220
220
  </div>
221
221
  <div class="stat">
222
- <div class="stat-value" id="gzip-size">${W(Z.totalGzipSize)}</div>
222
+ <div class="stat-value" id="gzip-size">${W(X.totalGzipSize)}</div>
223
223
  <div class="stat-label">Gzip Size</div>
224
224
  </div>
225
225
  <div class="stat">
226
- <div class="stat-value">${Z.moduleCount}</div>
226
+ <div class="stat-value">${X.moduleCount}</div>
227
227
  <div class="stat-label">Modules</div>
228
228
  </div>
229
229
  <div class="stat">
230
- <div class="stat-value">${Z.chunks.length}</div>
230
+ <div class="stat-value">${X.chunks.length}</div>
231
231
  <div class="stat-label">Chunks</div>
232
232
  </div>
233
233
  </div>
@@ -238,7 +238,7 @@ import v from"fs";import H from"path";import{gzipSync as z}from"zlib";import{cre
238
238
  <input type="text" id="search" placeholder="Search modules...">
239
239
  </div>
240
240
  <label>
241
- <input type="checkbox" id="show-gzip" ${j?"checked":""}>
241
+ <input type="checkbox" id="show-gzip" ${$?"checked":""}>
242
242
  Show Gzip Size
243
243
  </label>
244
244
  </div>
@@ -254,37 +254,37 @@ import v from"fs";import H from"path";import{gzipSync as z}from"zlib";import{cre
254
254
  <div class="legend">
255
255
  <div class="legend-title">File Types</div>
256
256
  <div class="legend-item">
257
- <div class="legend-color" style="background: ${Q.js}"></div>
257
+ <div class="legend-color" style="background: ${K.js}"></div>
258
258
  <span>JavaScript</span>
259
259
  </div>
260
260
  <div class="legend-item">
261
- <div class="legend-color" style="background: ${Q.css}"></div>
261
+ <div class="legend-color" style="background: ${K.css}"></div>
262
262
  <span>CSS</span>
263
263
  </div>
264
264
  <div class="legend-item">
265
- <div class="legend-color" style="background: ${Q.html}"></div>
265
+ <div class="legend-color" style="background: ${K.html}"></div>
266
266
  <span>HTML</span>
267
267
  </div>
268
268
  <div class="legend-item">
269
- <div class="legend-color" style="background: ${Q.image}"></div>
269
+ <div class="legend-color" style="background: ${K.image}"></div>
270
270
  <span>Images</span>
271
271
  </div>
272
272
  <div class="legend-item">
273
- <div class="legend-color" style="background: ${Q.font}"></div>
273
+ <div class="legend-color" style="background: ${K.font}"></div>
274
274
  <span>Fonts</span>
275
275
  </div>
276
276
  <div class="legend-item">
277
- <div class="legend-color" style="background: ${Q.other}"></div>
277
+ <div class="legend-color" style="background: ${K.other}"></div>
278
278
  <span>Other</span>
279
279
  </div>
280
280
  </div>
281
281
 
282
282
  <script src="https://d3js.org/d3.v7.min.js"></script>
283
283
  <script>
284
- const data = ${K};
285
- const colors = ${JSON.stringify(Q)};
284
+ const data = ${q};
285
+ const colors = ${JSON.stringify(K)};
286
286
 
287
- let showGzip = ${j};
287
+ let showGzip = ${$};
288
288
  let currentRoot = data;
289
289
  let searchTerm = '';
290
290
 
@@ -496,4 +496,4 @@ import v from"fs";import H from"path";import{gzipSync as z}from"zlib";import{cre
496
496
  render();
497
497
  </script>
498
498
  </body>
499
- </html>`}function a(Z){let $={name:"root",size:Z.totalSize,gzipSize:Z.totalGzipSize,children:[]};for(let X of Z.chunks){let j={name:X.name,size:X.size,gzipSize:X.gzipSize,children:[]};for(let U of X.modules)e(j,U);$.children.push(j)}return $}function e(Z,$){let X=$.path.split("/"),j=Z;for(let Q=0;Q<X.length-1;Q++){let q=X[Q],K=j.children?.find((J)=>J.name===q);if(!K){if(K={name:q,size:0,gzipSize:0,children:[]},!j.children)j.children=[];j.children.push(K)}j=K}let U=X[X.length-1];if(!j.children)j.children=[];j.children.push({name:U,path:$.path,size:$.size,gzipSize:$.gzipSize,type:$.type}),s(Z,$.size,$.gzipSize)}function s(Z,$,X){}function M(Z){return Z.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}function T(Z){let $=process.platform,X=$==="darwin"?"open":$==="win32"?"start":"xdg-open";Bun.spawn([X,Z],{stdio:["ignore","ignore","ignore"]})}import P from"fs";import S from"path";async function CX(Z={}){let{directory:$="dist",output:X,format:j="html",open:U=!0,gzip:Q=!0,threshold:q,recommendations:K=!0,topModules:J=20,parseImports:F=!0}=Z,V=await L($,{parseImports:F,detectDuplicates:!0,topModulesCount:J}),_=q?typeof q==="string"?I(q):q:void 0,w=_?V.totalSize>_:!1,G,C;if(j==="html"){let A={title:`Bundle Analysis - ${S.basename(S.resolve($))}`,gzip:Q};if(G=R(V,A),C=X||S.join($,"bundle-report.html"),await P.promises.writeFile(C,G),U)T(C)}else if(G=x(V,{format:j,threshold:_,recommendations:K,topModules:J,showGzip:Q}),X)C=X,await P.promises.writeFile(C,G);return{stats:V,report:G,outputPath:C,exceedsThreshold:w}}async function GX(Z){let $=await L(Z,{parseImports:!1,detectDuplicates:!1,topModulesCount:0});return{totalSize:$.totalSize,totalSizeFormatted:W($.totalSize),gzipSize:$.totalGzipSize,gzipSizeFormatted:W($.totalGzipSize),moduleCount:$.moduleCount,chunkCount:$.chunks.length}}export{E as percentage,I as parseSize,T as openInBrowser,n as getModuleRecommendation,GX as getBundleSummary,R as generateTreemap,x as generateReport,W as formatBytes,L as collectBundleStats,N as analyzeBundle,CX as analyze};
499
+ </html>`}function a(X){let Z={name:"root",size:X.totalSize,gzipSize:X.totalGzipSize,children:[]};for(let Q of X.chunks){let $={name:Q.name,size:Q.size,gzipSize:Q.gzipSize,children:[]};for(let U of Q.modules)e($,U);Z.children.push($)}return Z}function e(X,Z){let Q=Z.path.split("/"),$=X;for(let K=0;K<Q.length-1;K++){let j=Q[K],q=$.children?.find((J)=>J.name===j);if(!q){if(q={name:j,size:0,gzipSize:0,children:[]},!$.children)$.children=[];$.children.push(q)}$=q}let U=Q[Q.length-1];if(!$.children)$.children=[];$.children.push({name:U,path:Z.path,size:Z.size,gzipSize:Z.gzipSize,type:Z.type}),s(X,Z.size,Z.gzipSize)}function s(X,Z,Q){}function M(X){return X.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}function T(X){let Z=process.platform,Q=Z==="darwin"?"open":Z==="win32"?"start":"xdg-open";Bun.spawn([Q,X],{stdio:["ignore","ignore","ignore"]})}import P from"fs";import S from"path";async function CQ(X={}){let{directory:Z="dist",output:Q,format:$="html",open:U=!0,gzip:K=!0,threshold:j,recommendations:q=!0,topModules:J=20,parseImports:F=!0}=X,V=await L(Z,{parseImports:F,detectDuplicates:!0,topModulesCount:J}),_=j?typeof j==="string"?I(j):j:void 0,w=_?V.totalSize>_:!1,G,C;if($==="html"){let A={title:`Bundle Analysis - ${S.basename(S.resolve(Z))}`,gzip:K};if(G=x(V,A),C=Q||S.join(Z,"bundle-report.html"),await P.promises.writeFile(C,G),U)T(C)}else if(G=R(V,{format:$,threshold:_,recommendations:q,topModules:J,showGzip:K}),Q)C=Q,await P.promises.writeFile(C,G);return{stats:V,report:G,outputPath:C,exceedsThreshold:w}}async function GQ(X){let Z=await L(X,{parseImports:!1,detectDuplicates:!1,topModulesCount:0});return{totalSize:Z.totalSize,totalSizeFormatted:W(Z.totalSize),gzipSize:Z.totalGzipSize,gzipSizeFormatted:W(Z.totalGzipSize),moduleCount:Z.moduleCount,chunkCount:Z.chunks.length}}export{E as percentage,I as parseSize,T as openInBrowser,n as getModuleRecommendation,GQ as getBundleSummary,x as generateTreemap,R as generateReport,W as formatBytes,L as collectBundleStats,N as analyzeBundle,CQ as analyze};
package/dist/caching.d.ts CHANGED
@@ -1,4 +1,31 @@
1
1
  import type { StxOptions } from './types';
2
+ /**
3
+ * Read a file with mtime-based caching.
4
+ * Returns cached content if file hasn't been modified since last read.
5
+ */
6
+ export declare function readFileCached(filePath: string): Promise<string>;
7
+ /**
8
+ * Invalidate a specific file from the content cache.
9
+ * Called by HMR when a file changes.
10
+ */
11
+ export declare function invalidateFileCache(filePath: string): void;
12
+ /**
13
+ * Invalidate all files matching a pattern from the content cache.
14
+ */
15
+ export declare function invalidateFileCacheByDir(dirPath: string): void;
16
+ /**
17
+ * Get the signals runtime with module-level caching.
18
+ * The runtime is identical for every page and never changes during a dev session.
19
+ */
20
+ export declare function getCachedSignalsRuntime(debug?: boolean): Promise<string>;
21
+ /**
22
+ * Get the router script with module-level caching.
23
+ */
24
+ export declare function getCachedRouterScript(): Promise<string>;
25
+ /**
26
+ * Clear all dev caches. Called during full rebuild or HMR reset.
27
+ */
28
+ export declare function clearDevCaches(): void;
2
29
  /**
3
30
  * Check if a cached version of the template is available and valid
4
31
  */